curvedraw

changeset 2:ce7aa9a0594c

improved curve editing
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 17 Dec 2015 05:13:25 +0200
parents 7dcd0f6113e5
children bf78387a9925
files src/app.cc src/curve.cc src/curve.h
diffstat 3 files changed, 140 insertions(+), 45 deletions(-) [+]
line diff
     1.1 --- a/src/app.cc	Wed Dec 16 04:49:16 2015 +0200
     1.2 +++ b/src/app.cc	Thu Dec 17 05:13:25 2015 +0200
     1.3 @@ -1,5 +1,6 @@
     1.4  #include <stdlib.h>
     1.5  #include <float.h>
     1.6 +#include <assert.h>
     1.7  #include <vector>
     1.8  #include <algorithm>
     1.9  #include "opengl.h"
    1.10 @@ -14,20 +15,26 @@
    1.11  static void draw_curve(const Curve *curve);
    1.12  static void on_click(int bn, float u, float v);
    1.13  
    1.14 -static float view_pan_x, view_pan_y;
    1.15 +// viewport control
    1.16 +static Vector2 view_pan;
    1.17  static float view_scale = 1.0f;
    1.18  
    1.19  static std::vector<Curve*> curves;
    1.20 -static Curve *sel_curve, *new_curve;
    1.21 -static int sel_pidx = -1;
    1.22 +static Curve *sel_curve;	// selected curve being edited
    1.23 +static Curve *new_curve;	// new curve being entered
    1.24 +static Curve *hover_curve;	// curve the mouse is hovering over (click to select)
    1.25 +static int sel_pidx = -1;	// selected point of the selected or hovered-over curve
    1.26  
    1.27 -static Label *weight_label;
    1.28 +static Label *weight_label;	// floating label for the cp weight
    1.29  
    1.30  
    1.31  bool app_init(int argc, char **argv)
    1.32  {
    1.33  	glEnable(GL_MULTISAMPLE);
    1.34  	glEnable(GL_CULL_FACE);
    1.35 +
    1.36 +	glEnable(GL_BLEND);
    1.37 +	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    1.38  	return true;
    1.39  }
    1.40  
    1.41 @@ -46,7 +53,7 @@
    1.42  
    1.43  	glMatrixMode(GL_MODELVIEW);
    1.44  	glLoadIdentity();
    1.45 -	glTranslatef(-view_pan_x, -view_pan_y, 0);
    1.46 +	glTranslatef(-view_pan.x, -view_pan.y, 0);
    1.47  	glScalef(view_scale, view_scale, view_scale);
    1.48  
    1.49  	draw_grid(std::max(win_aspect, 1.0f / win_aspect), 0.1);
    1.50 @@ -55,7 +62,6 @@
    1.51  		draw_curve(curves[i]);
    1.52  	}
    1.53  	if(new_curve) {
    1.54 -		// TODO special drawing with more feedback
    1.55  		draw_curve(new_curve);
    1.56  	}
    1.57  	if(weight_label) {
    1.58 @@ -92,16 +98,30 @@
    1.59  
    1.60  static void draw_curve(const Curve *curve)
    1.61  {
    1.62 -	int numpt = curve->get_point_count();
    1.63 -	int segm = numpt * 16.0f;
    1.64 +	int numpt = curve->size();
    1.65 +	int segm = numpt * 16;
    1.66  
    1.67 -	glLineWidth(2.0);
    1.68 +	/*if(curve == hover_curve) {
    1.69 +		glLineWidth(3.0);
    1.70 +		glColor4f(0.8, 0.8, 0.0, 1.0);
    1.71 +
    1.72 +		glBegin(GL_LINE_STRIP);
    1.73 +		for(int i=0; i<segm; i++) {
    1.74 +			float t = (float)i / (float)(segm - 1);
    1.75 +			Vector2 v = curve->interpolate(t);
    1.76 +			glVertex2f(v.x, v.y);
    1.77 +		}
    1.78 +		glEnd();
    1.79 +	}
    1.80 +	*/
    1.81 +
    1.82 +	glLineWidth(curve == hover_curve ? 4.0 : 2.0);
    1.83  	if(curve == sel_curve) {
    1.84  		glColor3f(0.3, 0.4, 1.0);
    1.85  	} else if(curve == new_curve) {
    1.86  		glColor3f(1.0, 0.75, 0.3);
    1.87  	} else {
    1.88 -		glColor3f(0.75, 0.75, 0.75);
    1.89 +		glColor3f(0.6, 0.6, 0.6);
    1.90  	}
    1.91  	glBegin(GL_LINE_STRIP);
    1.92  	for(int i=0; i<segm; i++) {
    1.93 @@ -112,7 +132,7 @@
    1.94  	glEnd();
    1.95  	glLineWidth(1.0);
    1.96  
    1.97 -	glPointSize(7.0);
    1.98 +	glPointSize(curve == hover_curve ? 10.0 : 7.0);
    1.99  	glBegin(GL_POINTS);
   1.100  	if(curve == new_curve) {
   1.101  		glColor3f(1.0, 0.0, 0.0);
   1.102 @@ -244,25 +264,24 @@
   1.103  	}
   1.104  }
   1.105  
   1.106 -static void hover(int x, int y)
   1.107 +static bool point_hit_test(const Vector2 &pos, Curve **curveret, int *pidxret)
   1.108  {
   1.109  	float thres = 0.02;
   1.110  
   1.111 -	Vector2 uv = pixel_to_uv(x, y);
   1.112  	for(size_t i=0; i<curves.size(); i++) {
   1.113 -		int pidx = curves[i]->nearest_point(uv);
   1.114 +		int pidx = curves[i]->nearest_point(pos);
   1.115  		if(pidx == -1) continue;
   1.116  
   1.117  		Vector2 cp = curves[i]->get_point(pidx);
   1.118 -		if((cp - uv).length_sq() < thres * thres) {
   1.119 -			sel_curve = curves[i];
   1.120 -			sel_pidx = pidx;
   1.121 -			return;
   1.122 +		if((cp - pos).length_sq() < thres * thres) {
   1.123 +			*curveret = curves[i];
   1.124 +			*pidxret = pidx;
   1.125 +			return true;
   1.126  		}
   1.127  	}
   1.128 -
   1.129 -	sel_curve = 0;
   1.130 -	sel_pidx = -1;
   1.131 +	*curveret = 0;
   1.132 +	*pidxret = -1;
   1.133 +	return false;
   1.134  }
   1.135  
   1.136  void app_mouse_motion(int x, int y)
   1.137 @@ -274,15 +293,25 @@
   1.138  
   1.139  	if(!dx && !dy) return;
   1.140  
   1.141 +	Vector2 uv = pixel_to_uv(x, y);
   1.142 +
   1.143 +	/* when entering a new curve, have the last (extra) point following
   1.144 +	 * the mouse until it's entered by a click (see on_click).
   1.145 +	 */
   1.146 +	if(new_curve && !new_curve->empty()) {
   1.147 +		new_curve->move_point(new_curve->size() - 1, uv);
   1.148 +		post_redisplay();
   1.149 +	}
   1.150 +
   1.151  	if(!new_curve && !bnstate) {
   1.152 -		hover(x, y);
   1.153 +		point_hit_test(uv, &hover_curve, &sel_pidx);
   1.154  		post_redisplay();
   1.155  	}
   1.156  
   1.157  	if(sel_curve && sel_pidx != -1) {
   1.158  		if(bnstate & BNBIT(0)) {
   1.159  			float w = sel_curve->get_weight(sel_pidx);
   1.160 -			sel_curve->set_point(sel_pidx, pixel_to_uv(x, y), w);
   1.161 +			sel_curve->set_point(sel_pidx, uv, w);
   1.162  			post_redisplay();
   1.163  		}
   1.164  
   1.165 @@ -295,7 +324,7 @@
   1.166  			if(!weight_label) {
   1.167  				weight_label = new Label;
   1.168  			}
   1.169 -			weight_label->set_position(pixel_to_uv(x, y));
   1.170 +			weight_label->set_position(uv);
   1.171  			weight_label->set_textf("w=%g", w);
   1.172  			post_redisplay();
   1.173  		}
   1.174 @@ -304,18 +333,59 @@
   1.175  
   1.176  static void on_click(int bn, float u, float v)
   1.177  {
   1.178 +	Vector2 uv = Vector2(u, v);
   1.179 +
   1.180  	switch(bn) {
   1.181 -	case 0:
   1.182 -		if(!new_curve) {
   1.183 -			new_curve = new Curve;
   1.184 +	case 0:	// ------- LEFT CLICK ------
   1.185 +		if(hover_curve) {
   1.186 +			// if we're hovering: click selects
   1.187 +			sel_curve = hover_curve;
   1.188 +			hover_curve = 0;
   1.189 +		} else if(sel_curve) {
   1.190 +			// if we have a selected curve: click adds point (enter new_curve mode)
   1.191 +			std::vector<Curve*>::iterator it = std::find(curves.begin(), curves.end(), sel_curve);
   1.192 +			assert(it != curves.end());
   1.193 +			curves.erase(it, it + 1);
   1.194 +
   1.195 +			new_curve = sel_curve;
   1.196 +			sel_curve = 0;
   1.197 +			sel_pidx = -1;
   1.198 +
   1.199 +			new_curve->add_point(uv);
   1.200 +		} else {
   1.201 +			// otherwise, click starts a new curve
   1.202 +			if(!new_curve) {
   1.203 +				new_curve = new Curve;
   1.204 +				new_curve->add_point(uv);
   1.205 +			}
   1.206 +			new_curve->add_point(uv);
   1.207  		}
   1.208 -		new_curve->add_point(Vector2(u, v));
   1.209  		post_redisplay();
   1.210  		break;
   1.211  
   1.212 -	case 2:
   1.213 -		curves.push_back(new_curve);
   1.214 -		new_curve = 0;
   1.215 +	case 2:	// ------- RIGHT CLICK ------
   1.216 +		if(new_curve) {
   1.217 +			// in new-curve mode: finish curve (cancels last floating segment)
   1.218 +			new_curve->remove_point(new_curve->size() - 1);
   1.219 +			if(new_curve->empty()) {
   1.220 +				delete new_curve;
   1.221 +			} else {
   1.222 +				curves.push_back(new_curve);
   1.223 +			}
   1.224 +			new_curve = 0;
   1.225 +
   1.226 +		} else if(sel_curve) {
   1.227 +			// in selected curve mode: delete control point or unselect
   1.228 +			Curve *hit_curve;
   1.229 +			int hit_pidx;
   1.230 +			if(point_hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) {
   1.231 +				hit_curve->remove_point(hit_pidx);
   1.232 +				sel_pidx = -1;
   1.233 +			} else {
   1.234 +				sel_curve = 0;
   1.235 +				sel_pidx = -1;
   1.236 +			}
   1.237 +		}
   1.238  		post_redisplay();
   1.239  		break;
   1.240  
     2.1 --- a/src/curve.cc	Wed Dec 16 04:49:16 2015 +0200
     2.2 +++ b/src/curve.cc	Thu Dec 17 05:13:25 2015 +0200
     2.3 @@ -46,11 +46,26 @@
     2.4  	return res;
     2.5  }
     2.6  
     2.7 -int Curve::get_point_count() const
     2.8 +bool Curve::empty() const
     2.9 +{
    2.10 +	return cp.empty();
    2.11 +}
    2.12 +
    2.13 +int Curve::size() const
    2.14  {
    2.15  	return (int)cp.size();
    2.16  }
    2.17  
    2.18 +Vector3 &Curve::operator [](int idx)
    2.19 +{
    2.20 +	return cp[idx];
    2.21 +}
    2.22 +
    2.23 +const Vector3 &Curve::operator [](int idx) const
    2.24 +{
    2.25 +	return cp[idx];
    2.26 +}
    2.27 +
    2.28  const Vector3 &Curve::get_homo_point(int idx) const
    2.29  {
    2.30  	return cp[idx];
    2.31 @@ -84,18 +99,28 @@
    2.32  	return true;
    2.33  }
    2.34  
    2.35 +bool Curve::move_point(int idx, const Vector2 &p)
    2.36 +{
    2.37 +	if(idx < 0 || idx >= (int)cp.size()) {
    2.38 +		return false;
    2.39 +	}
    2.40 +	cp[idx] = Vector3(p.x, p.y, cp[idx].z);
    2.41 +	return true;
    2.42 +}
    2.43 +
    2.44  Vector2 Curve::interpolate(float t, CurveType type) const
    2.45  {
    2.46 -	int num_cp = (int)cp.size();
    2.47 -	if(!num_cp) {
    2.48 +	if(cp.empty()) {
    2.49  		return Vector2(0, 0);
    2.50  	}
    2.51 +
    2.52 +	int num_cp = (int)cp.size();
    2.53  	if(num_cp == 1) {
    2.54  		return Vector2(cp[0].x, cp[0].y);
    2.55  	}
    2.56  
    2.57  	Vector3 res;
    2.58 -	int idx0 = (int)floor(t * (num_cp - 1));
    2.59 +	int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2);
    2.60  	int idx1 = idx0 + 1;
    2.61  
    2.62  	float dt = 1.0 / (float)(num_cp - 1);
    2.63 @@ -135,10 +160,3 @@
    2.64  {
    2.65  	return interpolate(t);
    2.66  }
    2.67 -
    2.68 -float Curve::map_param(float x) const
    2.69 -{
    2.70 -	if(x < 0.0f) x = 0.0f;
    2.71 -	if(x >= 1.0f) x = 1.0f;
    2.72 -	return x * ((float)cp.size() - 1);
    2.73 -}
     3.1 --- a/src/curve.h	Wed Dec 16 04:49:16 2015 +0200
     3.2 +++ b/src/curve.h	Thu Dec 17 05:13:25 2015 +0200
     3.3 @@ -15,8 +15,6 @@
     3.4  	std::vector<Vector3> cp;
     3.5  	CurveType type;
     3.6  
     3.7 -	float map_param(float x) const;
     3.8 -
     3.9  public:
    3.10  	Curve(CurveType type = CURVE_HERMITE);
    3.11  
    3.12 @@ -28,17 +26,26 @@
    3.13  
    3.14  	int nearest_point(const Vector2 &p);
    3.15  
    3.16 -	int get_point_count() const;
    3.17 +	bool empty() const;
    3.18 +	int size() const;
    3.19 +	Vector3 &operator [](int idx);
    3.20 +	const Vector3 &operator [](int idx) const;
    3.21 +
    3.22  	const Vector3 &get_homo_point(int idx) const;	// homogeneous point
    3.23  	Vector2 get_point(int idx) const;
    3.24  	float get_weight(int idx) const;
    3.25  
    3.26  	bool set_point(int idx, const Vector2 &p, float weight = 1.0f);
    3.27  	bool set_weight(int idx, float weight);
    3.28 +	// move point without changing its weight
    3.29 +	bool move_point(int idx, const Vector2 &p);
    3.30  
    3.31  	Vector2 interpolate(float t, CurveType type) const;
    3.32  	Vector2 interpolate(float t) const;
    3.33  	Vector2 operator ()(float t) const;
    3.34 +
    3.35 +	void draw(int res = -1) const;
    3.36 +	void draw_cp(float sz = -1.0f) const;
    3.37  };
    3.38  
    3.39  #endif	// CURVE_H_