curvedraw

diff src/app.cc @ 3:bf78387a9925

pan/zoom, grid snapping
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 17 Dec 2015 07:10:10 +0200
parents ce7aa9a0594c
children 9f75208b81cd
line diff
     1.1 --- a/src/app.cc	Thu Dec 17 05:13:25 2015 +0200
     1.2 +++ b/src/app.cc	Thu Dec 17 07:10:10 2015 +0200
     1.3 @@ -8,6 +8,12 @@
     1.4  #include "curve.h"
     1.5  #include "widgets.h"
     1.6  
     1.7 +enum SnapMode {
     1.8 +	SNAP_NONE,
     1.9 +	SNAP_GRID,
    1.10 +	SNAP_POINT
    1.11 +};
    1.12 +
    1.13  int win_width, win_height;
    1.14  float win_aspect;
    1.15  
    1.16 @@ -17,7 +23,11 @@
    1.17  
    1.18  // viewport control
    1.19  static Vector2 view_pan;
    1.20 -static float view_scale = 1.0f;
    1.21 +static float view_scale = 0.2f;
    1.22 +static Matrix4x4 view_matrix;
    1.23 +
    1.24 +static float grid_size = 1.0;
    1.25 +static SnapMode snap_mode;
    1.26  
    1.27  static std::vector<Curve*> curves;
    1.28  static Curve *sel_curve;	// selected curve being edited
    1.29 @@ -27,6 +37,10 @@
    1.30  
    1.31  static Label *weight_label;	// floating label for the cp weight
    1.32  
    1.33 +#ifdef DRAW_MOUSE_POINTER
    1.34 +static Vector2 mouse_pointer;
    1.35 +#endif
    1.36 +
    1.37  
    1.38  bool app_init(int argc, char **argv)
    1.39  {
    1.40 @@ -53,10 +67,11 @@
    1.41  
    1.42  	glMatrixMode(GL_MODELVIEW);
    1.43  	glLoadIdentity();
    1.44 -	glTranslatef(-view_pan.x, -view_pan.y, 0);
    1.45 +	glTranslatef(view_pan.x * view_scale, view_pan.y * view_scale, 0);
    1.46  	glScalef(view_scale, view_scale, view_scale);
    1.47  
    1.48 -	draw_grid(std::max(win_aspect, 1.0f / win_aspect), 0.1);
    1.49 +	float max_aspect = std::max(win_aspect, 1.0f / win_aspect);
    1.50 +	draw_grid(max_aspect, grid_size);
    1.51  
    1.52  	for(size_t i=0; i<curves.size(); i++) {
    1.53  		draw_curve(curves[i]);
    1.54 @@ -67,33 +82,61 @@
    1.55  	if(weight_label) {
    1.56  		weight_label->draw();
    1.57  	}
    1.58 +
    1.59 +#ifdef DRAW_MOUSE_POINTER
    1.60 +	glPointSize(6.0);
    1.61 +	glBegin(GL_POINTS);
    1.62 +	glColor3f(0, 0, 1);
    1.63 +	glVertex2f(mouse_pointer.x, mouse_pointer.y);
    1.64 +	glEnd();
    1.65 +#endif
    1.66  }
    1.67  
    1.68  static void draw_grid(float sz, float sep, float alpha)
    1.69  {
    1.70  	float x = 0.0f;
    1.71 +	float s = 1.0 / view_scale;
    1.72 +
    1.73 +	sz *= s;
    1.74 +	sz += sep;	// one more step for when we have non-zero fractional pan
    1.75 +	float end = std::min(sz, 100.0f * sep);
    1.76 +
    1.77 +	// fractional pan
    1.78 +	Vector2 pan = view_pan;
    1.79 +	Vector2 fpan = Vector2(fmod(pan.x, sep), fmod(pan.y, sep));
    1.80 +	Vector2 offset = fpan - pan;
    1.81 +
    1.82 +	glMatrixMode(GL_MODELVIEW);
    1.83 +	glPushMatrix();
    1.84 +	glTranslatef(offset.x, offset.y, 0);
    1.85 +
    1.86 +	glBegin(GL_LINES);
    1.87 +	glColor4f(0.35, 0.35, 0.35, alpha);
    1.88 +	while(x <= end) {
    1.89 +		glVertex2f(-end, x);
    1.90 +		glVertex2f(end, x);
    1.91 +		glVertex2f(-end, -x);
    1.92 +		glVertex2f(end, -x);
    1.93 +		glVertex2f(x, -end);
    1.94 +		glVertex2f(x, end);
    1.95 +		glVertex2f(-x, -end);
    1.96 +		glVertex2f(-x, end);
    1.97 +		x += sep;
    1.98 +	}
    1.99 +	glEnd();
   1.100 +	glPopMatrix();
   1.101 +
   1.102  
   1.103  	glLineWidth(1.0);
   1.104  	glBegin(GL_LINES);
   1.105  	glColor4f(0.6, 0.3, 0.2, alpha);
   1.106 -	glVertex2f(-sz, 0);
   1.107 -	glVertex2f(sz, 0);
   1.108 +	glVertex2f(-sz + offset.x, 0);
   1.109 +	glVertex2f(sz + offset.x, 0);
   1.110  	glColor4f(0.2, 0.3, 0.6, alpha);
   1.111 -	glVertex2f(0, -sz);
   1.112 -	glVertex2f(0, sz);
   1.113 -	glColor4f(0.35, 0.35, 0.35, alpha);
   1.114 -	while(x < sz) {
   1.115 -		x += sep;
   1.116 -		glVertex2f(-sz, x);
   1.117 -		glVertex2f(sz, x);
   1.118 -		glVertex2f(-sz, -x);
   1.119 -		glVertex2f(sz, -x);
   1.120 -		glVertex2f(x, -sz);
   1.121 -		glVertex2f(x, sz);
   1.122 -		glVertex2f(-x, -sz);
   1.123 -		glVertex2f(-x, sz);
   1.124 -	}
   1.125 +	glVertex2f(0, -sz + offset.y);
   1.126 +	glVertex2f(0, sz + offset.y);
   1.127  	glEnd();
   1.128 +
   1.129  }
   1.130  
   1.131  static void draw_curve(const Curve *curve)
   1.132 @@ -101,20 +144,6 @@
   1.133  	int numpt = curve->size();
   1.134  	int segm = numpt * 16;
   1.135  
   1.136 -	/*if(curve == hover_curve) {
   1.137 -		glLineWidth(3.0);
   1.138 -		glColor4f(0.8, 0.8, 0.0, 1.0);
   1.139 -
   1.140 -		glBegin(GL_LINE_STRIP);
   1.141 -		for(int i=0; i<segm; i++) {
   1.142 -			float t = (float)i / (float)(segm - 1);
   1.143 -			Vector2 v = curve->interpolate(t);
   1.144 -			glVertex2f(v.x, v.y);
   1.145 -		}
   1.146 -		glEnd();
   1.147 -	}
   1.148 -	*/
   1.149 -
   1.150  	glLineWidth(curve == hover_curve ? 4.0 : 2.0);
   1.151  	if(curve == sel_curve) {
   1.152  		glColor3f(0.3, 0.4, 1.0);
   1.153 @@ -219,13 +248,43 @@
   1.154  			break;
   1.155  		}
   1.156  	}
   1.157 +
   1.158 +
   1.159 +	switch(key) {
   1.160 +	case 's':
   1.161 +		snap_mode = pressed ? SNAP_GRID : SNAP_NONE;
   1.162 +		break;
   1.163 +
   1.164 +	case 'S':
   1.165 +		snap_mode = pressed ? SNAP_POINT : SNAP_NONE;
   1.166 +		break;
   1.167 +
   1.168 +	default:
   1.169 +		break;
   1.170 +	}
   1.171 +}
   1.172 +
   1.173 +static void calc_view_matrix()
   1.174 +{
   1.175 +	view_matrix.reset_identity();
   1.176 +	view_matrix.scale(Vector3(view_scale, view_scale, view_scale));
   1.177 +	view_matrix.translate(Vector3(view_pan.x, view_pan.y, 0.0));
   1.178  }
   1.179  
   1.180  static Vector2 pixel_to_uv(int x, int y)
   1.181  {
   1.182  	float u = win_aspect * (2.0 * (float)x / (float)win_width - 1.0);
   1.183  	float v = 1.0 - 2.0 * (float)y / (float)win_height;
   1.184 +
   1.185 +	u = u / view_scale - view_pan.x;
   1.186 +	v = v / view_scale - view_pan.y;
   1.187  	return Vector2(u, v);
   1.188 +	/*
   1.189 +	Matrix4x4 inv_view_matrix = view_matrix.inverse();
   1.190 +	Vector4 res = Vector4(u, v, 0.0, 1.0).transformed(inv_view_matrix);
   1.191 +
   1.192 +	return Vector2(res.x, res.y);
   1.193 +	*/
   1.194  }
   1.195  
   1.196  static int prev_x, prev_y;
   1.197 @@ -266,7 +325,7 @@
   1.198  
   1.199  static bool point_hit_test(const Vector2 &pos, Curve **curveret, int *pidxret)
   1.200  {
   1.201 -	float thres = 0.02;
   1.202 +	float thres = 0.02 / view_scale;
   1.203  
   1.204  	for(size_t i=0; i<curves.size(); i++) {
   1.205  		int pidx = curves[i]->nearest_point(pos);
   1.206 @@ -284,8 +343,23 @@
   1.207  	return false;
   1.208  }
   1.209  
   1.210 +static Vector2 snap(const Vector2 &p)
   1.211 +{
   1.212 +	switch(snap_mode) {
   1.213 +	case SNAP_GRID:
   1.214 +		return Vector2(round(p.x / grid_size) * grid_size, round(p.y / grid_size) * grid_size);
   1.215 +	case SNAP_POINT:
   1.216 +		// TODO
   1.217 +	default:
   1.218 +		break;
   1.219 +	}
   1.220 +	return p;
   1.221 +}
   1.222 +
   1.223  void app_mouse_motion(int x, int y)
   1.224  {
   1.225 +	Vector2 prev_uv = pixel_to_uv(prev_x, prev_y);
   1.226 +
   1.227  	int dx = x - prev_x;
   1.228  	int dy = y - prev_y;
   1.229  	prev_x = x;
   1.230 @@ -294,39 +368,70 @@
   1.231  	if(!dx && !dy) return;
   1.232  
   1.233  	Vector2 uv = pixel_to_uv(x, y);
   1.234 +#ifdef DRAW_MOUSE_POINTER
   1.235 +	mouse_pointer = uv;
   1.236 +	post_redisplay();
   1.237 +#endif
   1.238  
   1.239  	/* when entering a new curve, have the last (extra) point following
   1.240  	 * the mouse until it's entered by a click (see on_click).
   1.241  	 */
   1.242 -	if(new_curve && !new_curve->empty()) {
   1.243 -		new_curve->move_point(new_curve->size() - 1, uv);
   1.244 +	if(new_curve) {
   1.245 +		new_curve->move_point(new_curve->size() - 1, snap(uv));
   1.246  		post_redisplay();
   1.247 +		return;
   1.248  	}
   1.249 +	// from this point on we're definitely not in new-curve mode
   1.250  
   1.251 -	if(!new_curve && !bnstate) {
   1.252 +	if(!bnstate) {
   1.253 +		// not dragging, highlight curve under mouse
   1.254  		point_hit_test(uv, &hover_curve, &sel_pidx);
   1.255  		post_redisplay();
   1.256 -	}
   1.257  
   1.258 -	if(sel_curve && sel_pidx != -1) {
   1.259 -		if(bnstate & BNBIT(0)) {
   1.260 -			float w = sel_curve->get_weight(sel_pidx);
   1.261 -			sel_curve->set_point(sel_pidx, uv, w);
   1.262 -			post_redisplay();
   1.263 -		}
   1.264 +	} else {
   1.265 +		// we're dragging with one or more buttons held down
   1.266  
   1.267 -		if(bnstate & BNBIT(2)) {
   1.268 -			float w = sel_curve->get_weight(sel_pidx);
   1.269 -			w -= dy * 0.01;
   1.270 -			if(w < FLT_MIN) w = FLT_MIN;
   1.271 -			sel_curve->set_weight(sel_pidx, w);
   1.272 +		if(sel_curve && sel_pidx != -1) {
   1.273 +			// we have a curve and a point of the curve selected
   1.274  
   1.275 -			if(!weight_label) {
   1.276 -				weight_label = new Label;
   1.277 +			if(bnstate & BNBIT(0)) {
   1.278 +				// dragging point with left button: move it
   1.279 +				sel_curve->move_point(sel_pidx, snap(uv));
   1.280 +				post_redisplay();
   1.281  			}
   1.282 -			weight_label->set_position(uv);
   1.283 -			weight_label->set_textf("w=%g", w);
   1.284 -			post_redisplay();
   1.285 +
   1.286 +			if(bnstate & BNBIT(2)) {
   1.287 +				// dragging point with right button: change weight
   1.288 +				float w = sel_curve->get_weight(sel_pidx);
   1.289 +				w -= dy * 0.01;
   1.290 +				if(w < FLT_MIN) w = FLT_MIN;
   1.291 +				sel_curve->set_weight(sel_pidx, w);
   1.292 +
   1.293 +				// popup floating weight label if not already there
   1.294 +				if(!weight_label) {
   1.295 +					weight_label = new Label;
   1.296 +				}
   1.297 +				weight_label->set_position(uv);
   1.298 +				weight_label->set_textf("w=%g", w);
   1.299 +				post_redisplay();
   1.300 +			}
   1.301 +		} else {
   1.302 +			// no selection, we're dragging in empty space: manipulate viewport
   1.303 +			Vector2 dir = uv - prev_uv;
   1.304 +
   1.305 +			if(bnstate & (BNBIT(0) | BNBIT(1))) {
   1.306 +				// panning
   1.307 +				view_pan += dir;
   1.308 +				calc_view_matrix();
   1.309 +				post_redisplay();
   1.310 +			}
   1.311 +			if(bnstate & BNBIT(2)) {
   1.312 +				// zooming
   1.313 +				view_scale -= ((float)dy / (float)win_height) * view_scale * 5.0;
   1.314 +				if(view_scale < 1e-4) view_scale = 1e-4;
   1.315 +				calc_view_matrix();
   1.316 +				post_redisplay();
   1.317 +			}
   1.318  		}
   1.319  	}
   1.320  }