rev |
line source |
nuclear@16
|
1 /*
|
nuclear@16
|
2 curvedraw - a simple program to draw curves
|
nuclear@16
|
3 Copyright (C) 2015 John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@16
|
4
|
nuclear@16
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@16
|
6 it under the terms of the GNU General Public License as published by
|
nuclear@16
|
7 the Free Software Foundation, either version 3 of the License, or
|
nuclear@16
|
8 (at your option) any later version.
|
nuclear@16
|
9
|
nuclear@16
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@16
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@16
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@16
|
13 GNU General Public License for more details.
|
nuclear@16
|
14
|
nuclear@16
|
15 You should have received a copy of the GNU General Public License
|
nuclear@16
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@16
|
17 */
|
nuclear@0
|
18 #include <stdlib.h>
|
nuclear@0
|
19 #include <float.h>
|
nuclear@2
|
20 #include <assert.h>
|
nuclear@0
|
21 #include <vector>
|
nuclear@0
|
22 #include <algorithm>
|
nuclear@0
|
23 #include "opengl.h"
|
nuclear@0
|
24 #include "app.h"
|
nuclear@0
|
25 #include "curve.h"
|
nuclear@0
|
26 #include "widgets.h"
|
nuclear@6
|
27 #include "curvefile.h"
|
nuclear@0
|
28
|
nuclear@3
|
29 enum SnapMode {
|
nuclear@3
|
30 SNAP_NONE,
|
nuclear@3
|
31 SNAP_GRID,
|
nuclear@3
|
32 SNAP_POINT
|
nuclear@3
|
33 };
|
nuclear@3
|
34
|
nuclear@1
|
35 int win_width, win_height;
|
nuclear@1
|
36 float win_aspect;
|
nuclear@1
|
37
|
nuclear@0
|
38 static void draw_grid(float sz, float sep, float alpha = 1.0f);
|
nuclear@0
|
39 static void draw_curve(const Curve *curve);
|
nuclear@0
|
40 static void on_click(int bn, float u, float v);
|
nuclear@0
|
41
|
nuclear@2
|
42 // viewport control
|
nuclear@2
|
43 static Vector2 view_pan;
|
nuclear@3
|
44 static float view_scale = 0.2f;
|
nuclear@3
|
45 static Matrix4x4 view_matrix;
|
nuclear@3
|
46
|
nuclear@3
|
47 static float grid_size = 1.0;
|
nuclear@3
|
48 static SnapMode snap_mode;
|
nuclear@0
|
49
|
nuclear@12
|
50 static bool show_bounds;
|
nuclear@12
|
51
|
nuclear@0
|
52 static std::vector<Curve*> curves;
|
nuclear@2
|
53 static Curve *sel_curve; // selected curve being edited
|
nuclear@2
|
54 static Curve *new_curve; // new curve being entered
|
nuclear@2
|
55 static Curve *hover_curve; // curve the mouse is hovering over (click to select)
|
nuclear@13
|
56 static int sel_pidx = -1; // selected point of the selected curve
|
nuclear@13
|
57 static int hover_pidx = -1; // hovered over point
|
nuclear@0
|
58
|
nuclear@2
|
59 static Label *weight_label; // floating label for the cp weight
|
nuclear@0
|
60
|
nuclear@3
|
61 static Vector2 mouse_pointer;
|
nuclear@3
|
62
|
nuclear@0
|
63
|
nuclear@0
|
64 bool app_init(int argc, char **argv)
|
nuclear@0
|
65 {
|
nuclear@5
|
66 glewInit();
|
nuclear@5
|
67
|
nuclear@0
|
68 glEnable(GL_MULTISAMPLE);
|
nuclear@0
|
69 glEnable(GL_CULL_FACE);
|
nuclear@2
|
70
|
nuclear@2
|
71 glEnable(GL_BLEND);
|
nuclear@2
|
72 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
nuclear@0
|
73 return true;
|
nuclear@0
|
74 }
|
nuclear@0
|
75
|
nuclear@0
|
76 void app_cleanup()
|
nuclear@0
|
77 {
|
nuclear@0
|
78 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@0
|
79 delete curves[i];
|
nuclear@0
|
80 }
|
nuclear@0
|
81 curves.clear();
|
nuclear@0
|
82 }
|
nuclear@0
|
83
|
nuclear@0
|
84 void app_draw()
|
nuclear@0
|
85 {
|
nuclear@1
|
86 glClearColor(0.1, 0.1, 0.1, 1);
|
nuclear@0
|
87 glClear(GL_COLOR_BUFFER_BIT);
|
nuclear@0
|
88
|
nuclear@0
|
89 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
90 glLoadIdentity();
|
nuclear@3
|
91 glTranslatef(view_pan.x * view_scale, view_pan.y * view_scale, 0);
|
nuclear@0
|
92 glScalef(view_scale, view_scale, view_scale);
|
nuclear@0
|
93
|
nuclear@3
|
94 float max_aspect = std::max(win_aspect, 1.0f / win_aspect);
|
nuclear@3
|
95 draw_grid(max_aspect, grid_size);
|
nuclear@0
|
96
|
nuclear@0
|
97 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@0
|
98 draw_curve(curves[i]);
|
nuclear@0
|
99 }
|
nuclear@0
|
100 if(new_curve) {
|
nuclear@0
|
101 draw_curve(new_curve);
|
nuclear@0
|
102 }
|
nuclear@3
|
103
|
nuclear@3
|
104 #ifdef DRAW_MOUSE_POINTER
|
nuclear@3
|
105 glPointSize(6.0);
|
nuclear@3
|
106 glBegin(GL_POINTS);
|
nuclear@3
|
107 glColor3f(0, 0, 1);
|
nuclear@3
|
108 glVertex2f(mouse_pointer.x, mouse_pointer.y);
|
nuclear@3
|
109 glEnd();
|
nuclear@3
|
110 #endif
|
nuclear@4
|
111
|
nuclear@4
|
112 glMatrixMode(GL_MODELVIEW);
|
nuclear@4
|
113 glLoadIdentity();
|
nuclear@4
|
114
|
nuclear@4
|
115 if(weight_label) {
|
nuclear@4
|
116 weight_label->draw();
|
nuclear@4
|
117 }
|
nuclear@0
|
118 }
|
nuclear@0
|
119
|
nuclear@0
|
120 static void draw_grid(float sz, float sep, float alpha)
|
nuclear@0
|
121 {
|
nuclear@0
|
122 float x = 0.0f;
|
nuclear@3
|
123 float s = 1.0 / view_scale;
|
nuclear@3
|
124
|
nuclear@3
|
125 sz *= s;
|
nuclear@3
|
126 sz += sep; // one more step for when we have non-zero fractional pan
|
nuclear@3
|
127 float end = std::min(sz, 100.0f * sep);
|
nuclear@3
|
128
|
nuclear@3
|
129 // fractional pan
|
nuclear@3
|
130 Vector2 pan = view_pan;
|
nuclear@3
|
131 Vector2 fpan = Vector2(fmod(pan.x, sep), fmod(pan.y, sep));
|
nuclear@3
|
132 Vector2 offset = fpan - pan;
|
nuclear@3
|
133
|
nuclear@3
|
134 glMatrixMode(GL_MODELVIEW);
|
nuclear@3
|
135 glPushMatrix();
|
nuclear@3
|
136 glTranslatef(offset.x, offset.y, 0);
|
nuclear@3
|
137
|
nuclear@3
|
138 glBegin(GL_LINES);
|
nuclear@3
|
139 glColor4f(0.35, 0.35, 0.35, alpha);
|
nuclear@3
|
140 while(x <= end) {
|
nuclear@3
|
141 glVertex2f(-end, x);
|
nuclear@3
|
142 glVertex2f(end, x);
|
nuclear@3
|
143 glVertex2f(-end, -x);
|
nuclear@3
|
144 glVertex2f(end, -x);
|
nuclear@3
|
145 glVertex2f(x, -end);
|
nuclear@3
|
146 glVertex2f(x, end);
|
nuclear@3
|
147 glVertex2f(-x, -end);
|
nuclear@3
|
148 glVertex2f(-x, end);
|
nuclear@3
|
149 x += sep;
|
nuclear@3
|
150 }
|
nuclear@3
|
151 glEnd();
|
nuclear@3
|
152 glPopMatrix();
|
nuclear@3
|
153
|
nuclear@0
|
154
|
nuclear@0
|
155 glLineWidth(1.0);
|
nuclear@0
|
156 glBegin(GL_LINES);
|
nuclear@0
|
157 glColor4f(0.6, 0.3, 0.2, alpha);
|
nuclear@3
|
158 glVertex2f(-sz + offset.x, 0);
|
nuclear@3
|
159 glVertex2f(sz + offset.x, 0);
|
nuclear@0
|
160 glColor4f(0.2, 0.3, 0.6, alpha);
|
nuclear@3
|
161 glVertex2f(0, -sz + offset.y);
|
nuclear@3
|
162 glVertex2f(0, sz + offset.y);
|
nuclear@0
|
163 glEnd();
|
nuclear@3
|
164
|
nuclear@0
|
165 }
|
nuclear@0
|
166
|
nuclear@0
|
167 static void draw_curve(const Curve *curve)
|
nuclear@0
|
168 {
|
nuclear@2
|
169 int numpt = curve->size();
|
nuclear@2
|
170 int segm = numpt * 16;
|
nuclear@0
|
171
|
nuclear@12
|
172 if(show_bounds) {
|
nuclear@12
|
173 Vector3 bmin, bmax;
|
nuclear@12
|
174 curve->get_bbox(&bmin, &bmax);
|
nuclear@12
|
175
|
nuclear@12
|
176 glLineWidth(1.0);
|
nuclear@12
|
177 glColor3f(0, 1, 0);
|
nuclear@12
|
178 glBegin(GL_LINE_LOOP);
|
nuclear@12
|
179 glVertex2f(bmin.x, bmin.y);
|
nuclear@12
|
180 glVertex2f(bmax.x, bmin.y);
|
nuclear@12
|
181 glVertex2f(bmax.x, bmax.y);
|
nuclear@12
|
182 glVertex2f(bmin.x, bmax.y);
|
nuclear@12
|
183 glEnd();
|
nuclear@12
|
184 }
|
nuclear@12
|
185
|
nuclear@2
|
186 glLineWidth(curve == hover_curve ? 4.0 : 2.0);
|
nuclear@1
|
187 if(curve == sel_curve) {
|
nuclear@1
|
188 glColor3f(0.3, 0.4, 1.0);
|
nuclear@1
|
189 } else if(curve == new_curve) {
|
nuclear@1
|
190 glColor3f(1.0, 0.75, 0.3);
|
nuclear@1
|
191 } else {
|
nuclear@2
|
192 glColor3f(0.6, 0.6, 0.6);
|
nuclear@1
|
193 }
|
nuclear@0
|
194 glBegin(GL_LINE_STRIP);
|
nuclear@0
|
195 for(int i=0; i<segm; i++) {
|
nuclear@0
|
196 float t = (float)i / (float)(segm - 1);
|
nuclear@12
|
197 Vector3 v = curve->interpolate(t);
|
nuclear@0
|
198 glVertex2f(v.x, v.y);
|
nuclear@0
|
199 }
|
nuclear@0
|
200 glEnd();
|
nuclear@0
|
201 glLineWidth(1.0);
|
nuclear@0
|
202
|
nuclear@2
|
203 glPointSize(curve == hover_curve ? 10.0 : 7.0);
|
nuclear@0
|
204 glBegin(GL_POINTS);
|
nuclear@1
|
205 if(curve == new_curve) {
|
nuclear@1
|
206 glColor3f(1.0, 0.0, 0.0);
|
nuclear@1
|
207 } else {
|
nuclear@1
|
208 glColor3f(0.6, 0.3, 0.2);
|
nuclear@1
|
209 }
|
nuclear@0
|
210 for(int i=0; i<numpt; i++) {
|
nuclear@1
|
211 if(curve == sel_curve) {
|
nuclear@1
|
212 if(i == sel_pidx) {
|
nuclear@1
|
213 glColor3f(1.0, 0.2, 0.1);
|
nuclear@1
|
214 } else {
|
nuclear@1
|
215 glColor3f(0.2, 1.0, 0.2);
|
nuclear@1
|
216 }
|
nuclear@1
|
217 }
|
nuclear@12
|
218 Vector2 pt = curve->get_point2(i);
|
nuclear@0
|
219 glVertex2f(pt.x, pt.y);
|
nuclear@0
|
220 }
|
nuclear@0
|
221 glEnd();
|
nuclear@12
|
222
|
nuclear@12
|
223 // draw the projected mouse point on the selected curve
|
nuclear@12
|
224 /*
|
nuclear@12
|
225 if(curve == sel_curve) {
|
nuclear@12
|
226 Vector3 pp = curve->proj_point(Vector3(mouse_pointer.x, mouse_pointer.y, 0.0));
|
nuclear@12
|
227
|
nuclear@12
|
228 glPointSize(5.0);
|
nuclear@12
|
229 glBegin(GL_POINTS);
|
nuclear@12
|
230 glColor3f(1, 0.8, 0.2);
|
nuclear@12
|
231 glVertex2f(pp.x, pp.y);
|
nuclear@12
|
232 glEnd();
|
nuclear@12
|
233 }
|
nuclear@12
|
234 */
|
nuclear@0
|
235 glPointSize(1.0);
|
nuclear@0
|
236 }
|
nuclear@0
|
237
|
nuclear@0
|
238 void app_reshape(int x, int y)
|
nuclear@0
|
239 {
|
nuclear@0
|
240 win_width = x;
|
nuclear@0
|
241 win_height = y;
|
nuclear@0
|
242 win_aspect = (float)x / (float)y;
|
nuclear@0
|
243
|
nuclear@0
|
244 glViewport(0, 0, x, y);
|
nuclear@0
|
245 glMatrixMode(GL_PROJECTION);
|
nuclear@0
|
246 glLoadIdentity();
|
nuclear@0
|
247 glOrtho(-win_aspect, win_aspect, -1, 1, -1, 1);
|
nuclear@0
|
248 }
|
nuclear@0
|
249
|
nuclear@0
|
250 void app_keyboard(int key, bool pressed)
|
nuclear@0
|
251 {
|
nuclear@0
|
252 if(pressed) {
|
nuclear@0
|
253 switch(key) {
|
nuclear@1
|
254 case 'q':
|
nuclear@1
|
255 case 'Q':
|
nuclear@1
|
256 exit(0);
|
nuclear@1
|
257
|
nuclear@0
|
258 case 27:
|
nuclear@1
|
259 if(new_curve) {
|
nuclear@1
|
260 delete new_curve;
|
nuclear@1
|
261 new_curve = 0;
|
nuclear@1
|
262 post_redisplay();
|
nuclear@1
|
263 }
|
nuclear@1
|
264 break;
|
nuclear@0
|
265
|
nuclear@12
|
266 case '1':
|
nuclear@12
|
267 case '2':
|
nuclear@12
|
268 case '3':
|
nuclear@0
|
269 if(sel_curve) {
|
nuclear@12
|
270 sel_curve->set_type((CurveType)((int)CURVE_LINEAR + key - '1'));
|
nuclear@0
|
271 post_redisplay();
|
nuclear@0
|
272 }
|
nuclear@1
|
273 if(new_curve) {
|
nuclear@12
|
274 new_curve->set_type((CurveType)((int)CURVE_LINEAR + key - '1'));
|
nuclear@1
|
275 post_redisplay();
|
nuclear@1
|
276 }
|
nuclear@0
|
277 break;
|
nuclear@0
|
278
|
nuclear@0
|
279 case 'b':
|
nuclear@0
|
280 case 'B':
|
nuclear@12
|
281 show_bounds = !show_bounds;
|
nuclear@12
|
282 post_redisplay();
|
nuclear@0
|
283 break;
|
nuclear@0
|
284
|
nuclear@12
|
285 case 'n':
|
nuclear@12
|
286 case 'N':
|
nuclear@0
|
287 if(sel_curve) {
|
nuclear@12
|
288 sel_curve->normalize();
|
nuclear@1
|
289 post_redisplay();
|
nuclear@1
|
290 }
|
nuclear@0
|
291 break;
|
nuclear@6
|
292
|
nuclear@6
|
293 case 'e':
|
nuclear@6
|
294 case 'E':
|
nuclear@6
|
295 // TODO: GUI for filename at least
|
nuclear@10
|
296 if(!save_curves("test.curves", &curves[0], (int)curves.size())) {
|
nuclear@6
|
297 fprintf(stderr, "failed to export curves\n");
|
nuclear@6
|
298 }
|
nuclear@6
|
299 printf("exported %d curves\n", (int)curves.size());
|
nuclear@6
|
300 break;
|
nuclear@10
|
301
|
nuclear@12
|
302 case 'l':
|
nuclear@12
|
303 case 'L':
|
nuclear@10
|
304 {
|
nuclear@10
|
305 std::list<Curve*> clist = load_curves("test.curves");
|
nuclear@10
|
306 if(clist.empty()) {
|
nuclear@10
|
307 fprintf(stderr, "failed to import curves\n");
|
nuclear@10
|
308 }
|
nuclear@10
|
309
|
nuclear@12
|
310 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@12
|
311 delete curves[i];
|
nuclear@12
|
312 }
|
nuclear@12
|
313 curves.clear();
|
nuclear@12
|
314
|
nuclear@10
|
315 int num = 0;
|
nuclear@10
|
316 std::list<Curve*>::iterator it = clist.begin();
|
nuclear@10
|
317 while(it != clist.end()) {
|
nuclear@10
|
318 curves.push_back(*it++);
|
nuclear@10
|
319 ++num;
|
nuclear@10
|
320 }
|
nuclear@10
|
321 printf("imported %d curves\n", num);
|
nuclear@10
|
322 }
|
nuclear@12
|
323 post_redisplay();
|
nuclear@10
|
324 break;
|
nuclear@0
|
325 }
|
nuclear@0
|
326 }
|
nuclear@3
|
327
|
nuclear@3
|
328
|
nuclear@3
|
329 switch(key) {
|
nuclear@3
|
330 case 's':
|
nuclear@3
|
331 snap_mode = pressed ? SNAP_GRID : SNAP_NONE;
|
nuclear@3
|
332 break;
|
nuclear@3
|
333
|
nuclear@3
|
334 case 'S':
|
nuclear@3
|
335 snap_mode = pressed ? SNAP_POINT : SNAP_NONE;
|
nuclear@3
|
336 break;
|
nuclear@3
|
337
|
nuclear@3
|
338 default:
|
nuclear@3
|
339 break;
|
nuclear@3
|
340 }
|
nuclear@3
|
341 }
|
nuclear@3
|
342
|
nuclear@3
|
343 static void calc_view_matrix()
|
nuclear@3
|
344 {
|
nuclear@3
|
345 view_matrix.reset_identity();
|
nuclear@3
|
346 view_matrix.scale(Vector3(view_scale, view_scale, view_scale));
|
nuclear@3
|
347 view_matrix.translate(Vector3(view_pan.x, view_pan.y, 0.0));
|
nuclear@0
|
348 }
|
nuclear@0
|
349
|
nuclear@0
|
350 static Vector2 pixel_to_uv(int x, int y)
|
nuclear@0
|
351 {
|
nuclear@0
|
352 float u = win_aspect * (2.0 * (float)x / (float)win_width - 1.0);
|
nuclear@0
|
353 float v = 1.0 - 2.0 * (float)y / (float)win_height;
|
nuclear@3
|
354
|
nuclear@3
|
355 u = u / view_scale - view_pan.x;
|
nuclear@3
|
356 v = v / view_scale - view_pan.y;
|
nuclear@0
|
357 return Vector2(u, v);
|
nuclear@3
|
358 /*
|
nuclear@3
|
359 Matrix4x4 inv_view_matrix = view_matrix.inverse();
|
nuclear@3
|
360 Vector4 res = Vector4(u, v, 0.0, 1.0).transformed(inv_view_matrix);
|
nuclear@3
|
361
|
nuclear@3
|
362 return Vector2(res.x, res.y);
|
nuclear@3
|
363 */
|
nuclear@0
|
364 }
|
nuclear@0
|
365
|
nuclear@0
|
366 static int prev_x, prev_y;
|
nuclear@0
|
367 static int click_pos[8][2];
|
nuclear@0
|
368 static unsigned int bnstate;
|
nuclear@0
|
369
|
nuclear@0
|
370 #define BNBIT(x) (1 << (x))
|
nuclear@0
|
371
|
nuclear@0
|
372 void app_mouse_button(int bn, bool pressed, int x, int y)
|
nuclear@0
|
373 {
|
nuclear@0
|
374 prev_x = x;
|
nuclear@0
|
375 prev_y = y;
|
nuclear@0
|
376 if(pressed) {
|
nuclear@0
|
377 bnstate |= BNBIT(bn);
|
nuclear@0
|
378 } else {
|
nuclear@0
|
379 bnstate &= ~BNBIT(bn);
|
nuclear@0
|
380 }
|
nuclear@0
|
381
|
nuclear@0
|
382 if(pressed) {
|
nuclear@0
|
383 click_pos[bn][0] = x;
|
nuclear@0
|
384 click_pos[bn][1] = y;
|
nuclear@0
|
385 } else {
|
nuclear@0
|
386 int dx = x - click_pos[bn][0];
|
nuclear@0
|
387 int dy = y - click_pos[bn][1];
|
nuclear@0
|
388
|
nuclear@0
|
389 if(abs(dx) + abs(dy) < 3) {
|
nuclear@0
|
390 Vector2 uv = pixel_to_uv(x, y);
|
nuclear@0
|
391 on_click(bn, uv.x, uv.y);
|
nuclear@0
|
392 }
|
nuclear@0
|
393
|
nuclear@0
|
394 if(!(bnstate & BNBIT(2))) {
|
nuclear@0
|
395 delete weight_label;
|
nuclear@0
|
396 weight_label = 0;
|
nuclear@0
|
397 post_redisplay();
|
nuclear@0
|
398 }
|
nuclear@0
|
399 }
|
nuclear@0
|
400 }
|
nuclear@0
|
401
|
nuclear@2
|
402 static bool point_hit_test(const Vector2 &pos, Curve **curveret, int *pidxret)
|
nuclear@0
|
403 {
|
nuclear@3
|
404 float thres = 0.02 / view_scale;
|
nuclear@0
|
405
|
nuclear@0
|
406 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@2
|
407 int pidx = curves[i]->nearest_point(pos);
|
nuclear@0
|
408 if(pidx == -1) continue;
|
nuclear@0
|
409
|
nuclear@13
|
410 Vector2 cp = curves[i]->get_point2(pidx);
|
nuclear@2
|
411 if((cp - pos).length_sq() < thres * thres) {
|
nuclear@2
|
412 *curveret = curves[i];
|
nuclear@2
|
413 *pidxret = pidx;
|
nuclear@2
|
414 return true;
|
nuclear@0
|
415 }
|
nuclear@0
|
416 }
|
nuclear@2
|
417 *curveret = 0;
|
nuclear@2
|
418 *pidxret = -1;
|
nuclear@2
|
419 return false;
|
nuclear@0
|
420 }
|
nuclear@0
|
421
|
nuclear@13
|
422 static bool hit_test(const Vector2 &pos, Curve **curveret, int *pidxret)
|
nuclear@13
|
423 {
|
nuclear@13
|
424 float thres = 0.02 / view_scale;
|
nuclear@13
|
425
|
nuclear@13
|
426 if(point_hit_test(pos, curveret, pidxret)) {
|
nuclear@13
|
427 return true;
|
nuclear@13
|
428 }
|
nuclear@13
|
429
|
nuclear@13
|
430 Vector3 pos3 = Vector3(pos.x, pos.y, 0.0f);
|
nuclear@13
|
431 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@13
|
432 float x;
|
nuclear@13
|
433 if((x = curves[i]->distance_sq(pos3)) < thres * thres) {
|
nuclear@13
|
434 *curveret = curves[i];
|
nuclear@13
|
435 *pidxret = -1;
|
nuclear@13
|
436 return true;
|
nuclear@13
|
437 }
|
nuclear@13
|
438 }
|
nuclear@13
|
439 *curveret = 0;
|
nuclear@13
|
440 *pidxret = -1;
|
nuclear@13
|
441 return false;
|
nuclear@13
|
442 }
|
nuclear@13
|
443
|
nuclear@3
|
444 static Vector2 snap(const Vector2 &p)
|
nuclear@3
|
445 {
|
nuclear@3
|
446 switch(snap_mode) {
|
nuclear@3
|
447 case SNAP_GRID:
|
nuclear@3
|
448 return Vector2(round(p.x / grid_size) * grid_size, round(p.y / grid_size) * grid_size);
|
nuclear@3
|
449 case SNAP_POINT:
|
nuclear@14
|
450 {
|
nuclear@14
|
451 Curve *nearest_curve = 0;
|
nuclear@14
|
452 int nearest_curve_pidx = -1;
|
nuclear@14
|
453 float nearest_dist_sq = FLT_MAX;
|
nuclear@14
|
454
|
nuclear@14
|
455 if(new_curve) {
|
nuclear@14
|
456 // find the closest point, ignoring the last
|
nuclear@14
|
457 for(int i=0; i<new_curve->size() - 1; i++) {
|
nuclear@14
|
458 Vector2 cp = new_curve->get_point(i);
|
nuclear@14
|
459 float distsq = (cp - p).length_sq();
|
nuclear@14
|
460 if(distsq < nearest_dist_sq) {
|
nuclear@14
|
461 nearest_curve = new_curve;
|
nuclear@14
|
462 nearest_dist_sq = distsq;
|
nuclear@14
|
463 nearest_curve_pidx = i;
|
nuclear@14
|
464 }
|
nuclear@14
|
465 }
|
nuclear@14
|
466 }
|
nuclear@14
|
467
|
nuclear@14
|
468
|
nuclear@14
|
469 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@14
|
470 int pidx = curves[i]->nearest_point(p);
|
nuclear@14
|
471 Vector2 cp = curves[i]->get_point(pidx);
|
nuclear@14
|
472 float dist_sq = (cp - p).length_sq();
|
nuclear@14
|
473 if(dist_sq < nearest_dist_sq) {
|
nuclear@14
|
474 nearest_curve = curves[i];
|
nuclear@14
|
475 nearest_curve_pidx = pidx;
|
nuclear@14
|
476 nearest_dist_sq = dist_sq;
|
nuclear@14
|
477 }
|
nuclear@14
|
478 }
|
nuclear@14
|
479
|
nuclear@14
|
480 if(nearest_curve) {
|
nuclear@14
|
481 return nearest_curve->get_point(nearest_curve_pidx);
|
nuclear@14
|
482 }
|
nuclear@14
|
483 }
|
nuclear@14
|
484 break;
|
nuclear@14
|
485
|
nuclear@3
|
486 default:
|
nuclear@3
|
487 break;
|
nuclear@3
|
488 }
|
nuclear@3
|
489 return p;
|
nuclear@3
|
490 }
|
nuclear@3
|
491
|
nuclear@0
|
492 void app_mouse_motion(int x, int y)
|
nuclear@0
|
493 {
|
nuclear@3
|
494 Vector2 prev_uv = pixel_to_uv(prev_x, prev_y);
|
nuclear@3
|
495
|
nuclear@0
|
496 int dx = x - prev_x;
|
nuclear@0
|
497 int dy = y - prev_y;
|
nuclear@0
|
498 prev_x = x;
|
nuclear@0
|
499 prev_y = y;
|
nuclear@0
|
500
|
nuclear@0
|
501 if(!dx && !dy) return;
|
nuclear@0
|
502
|
nuclear@2
|
503 Vector2 uv = pixel_to_uv(x, y);
|
nuclear@3
|
504 mouse_pointer = uv;
|
nuclear@13
|
505 //post_redisplay();
|
nuclear@2
|
506
|
nuclear@2
|
507 /* when entering a new curve, have the last (extra) point following
|
nuclear@2
|
508 * the mouse until it's entered by a click (see on_click).
|
nuclear@2
|
509 */
|
nuclear@3
|
510 if(new_curve) {
|
nuclear@3
|
511 new_curve->move_point(new_curve->size() - 1, snap(uv));
|
nuclear@2
|
512 post_redisplay();
|
nuclear@2
|
513 }
|
nuclear@2
|
514
|
nuclear@4
|
515 if(!new_curve && !bnstate) {
|
nuclear@3
|
516 // not dragging, highlight curve under mouse
|
nuclear@13
|
517 hit_test(uv, &hover_curve, &hover_pidx);
|
nuclear@13
|
518 if(hover_curve == sel_curve) {
|
nuclear@13
|
519 sel_pidx = hover_pidx;
|
nuclear@13
|
520 }
|
nuclear@0
|
521 post_redisplay();
|
nuclear@0
|
522
|
nuclear@3
|
523 } else {
|
nuclear@3
|
524 // we're dragging with one or more buttons held down
|
nuclear@0
|
525
|
nuclear@3
|
526 if(sel_curve && sel_pidx != -1) {
|
nuclear@3
|
527 // we have a curve and a point of the curve selected
|
nuclear@0
|
528
|
nuclear@3
|
529 if(bnstate & BNBIT(0)) {
|
nuclear@3
|
530 // dragging point with left button: move it
|
nuclear@3
|
531 sel_curve->move_point(sel_pidx, snap(uv));
|
nuclear@3
|
532 post_redisplay();
|
nuclear@0
|
533 }
|
nuclear@3
|
534
|
nuclear@3
|
535 if(bnstate & BNBIT(2)) {
|
nuclear@3
|
536 // dragging point with right button: change weight
|
nuclear@3
|
537 float w = sel_curve->get_weight(sel_pidx);
|
nuclear@3
|
538 w -= dy * 0.01;
|
nuclear@3
|
539 if(w < FLT_MIN) w = FLT_MIN;
|
nuclear@3
|
540 sel_curve->set_weight(sel_pidx, w);
|
nuclear@3
|
541
|
nuclear@3
|
542 // popup floating weight label if not already there
|
nuclear@3
|
543 if(!weight_label) {
|
nuclear@3
|
544 weight_label = new Label;
|
nuclear@3
|
545 }
|
nuclear@3
|
546 weight_label->set_position(uv);
|
nuclear@3
|
547 weight_label->set_textf("w=%g", w);
|
nuclear@3
|
548 post_redisplay();
|
nuclear@3
|
549 }
|
nuclear@3
|
550 } else {
|
nuclear@3
|
551 // no selection, we're dragging in empty space: manipulate viewport
|
nuclear@3
|
552 Vector2 dir = uv - prev_uv;
|
nuclear@3
|
553
|
nuclear@3
|
554 if(bnstate & (BNBIT(0) | BNBIT(1))) {
|
nuclear@3
|
555 // panning
|
nuclear@3
|
556 view_pan += dir;
|
nuclear@3
|
557 calc_view_matrix();
|
nuclear@3
|
558 post_redisplay();
|
nuclear@3
|
559 }
|
nuclear@3
|
560 if(bnstate & BNBIT(2)) {
|
nuclear@3
|
561 // zooming
|
nuclear@3
|
562 view_scale -= ((float)dy / (float)win_height) * view_scale * 5.0;
|
nuclear@3
|
563 if(view_scale < 1e-4) view_scale = 1e-4;
|
nuclear@3
|
564 calc_view_matrix();
|
nuclear@3
|
565 post_redisplay();
|
nuclear@3
|
566 }
|
nuclear@0
|
567 }
|
nuclear@0
|
568 }
|
nuclear@0
|
569 }
|
nuclear@0
|
570
|
nuclear@0
|
571 static void on_click(int bn, float u, float v)
|
nuclear@0
|
572 {
|
nuclear@2
|
573 Vector2 uv = Vector2(u, v);
|
nuclear@2
|
574
|
nuclear@0
|
575 switch(bn) {
|
nuclear@2
|
576 case 0: // ------- LEFT CLICK ------
|
nuclear@2
|
577 if(hover_curve) {
|
nuclear@2
|
578 // if we're hovering: click selects
|
nuclear@2
|
579 sel_curve = hover_curve;
|
nuclear@13
|
580 sel_pidx = hover_pidx;
|
nuclear@2
|
581 hover_curve = 0;
|
nuclear@2
|
582 } else if(sel_curve) {
|
nuclear@2
|
583 // if we have a selected curve: click adds point (enter new_curve mode)
|
nuclear@2
|
584 std::vector<Curve*>::iterator it = std::find(curves.begin(), curves.end(), sel_curve);
|
nuclear@2
|
585 assert(it != curves.end());
|
nuclear@2
|
586 curves.erase(it, it + 1);
|
nuclear@2
|
587
|
nuclear@2
|
588 new_curve = sel_curve;
|
nuclear@2
|
589 sel_curve = 0;
|
nuclear@2
|
590 sel_pidx = -1;
|
nuclear@2
|
591
|
nuclear@2
|
592 new_curve->add_point(uv);
|
nuclear@2
|
593 } else {
|
nuclear@2
|
594 // otherwise, click starts a new curve
|
nuclear@2
|
595 if(!new_curve) {
|
nuclear@2
|
596 new_curve = new Curve;
|
nuclear@2
|
597 new_curve->add_point(uv);
|
nuclear@2
|
598 }
|
nuclear@2
|
599 new_curve->add_point(uv);
|
nuclear@0
|
600 }
|
nuclear@0
|
601 post_redisplay();
|
nuclear@0
|
602 break;
|
nuclear@0
|
603
|
nuclear@2
|
604 case 2: // ------- RIGHT CLICK ------
|
nuclear@2
|
605 if(new_curve) {
|
nuclear@2
|
606 // in new-curve mode: finish curve (cancels last floating segment)
|
nuclear@2
|
607 new_curve->remove_point(new_curve->size() - 1);
|
nuclear@2
|
608 if(new_curve->empty()) {
|
nuclear@2
|
609 delete new_curve;
|
nuclear@2
|
610 } else {
|
nuclear@2
|
611 curves.push_back(new_curve);
|
nuclear@2
|
612 }
|
nuclear@2
|
613 new_curve = 0;
|
nuclear@2
|
614
|
nuclear@2
|
615 } else if(sel_curve) {
|
nuclear@2
|
616 // in selected curve mode: delete control point or unselect
|
nuclear@2
|
617 Curve *hit_curve;
|
nuclear@2
|
618 int hit_pidx;
|
nuclear@13
|
619 if(hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) {
|
nuclear@13
|
620 if(hit_pidx != -1) {
|
nuclear@13
|
621 hit_curve->remove_point(hit_pidx);
|
nuclear@13
|
622 sel_pidx = -1;
|
nuclear@13
|
623 }
|
nuclear@2
|
624 } else {
|
nuclear@2
|
625 sel_curve = 0;
|
nuclear@2
|
626 sel_pidx = -1;
|
nuclear@2
|
627 }
|
nuclear@2
|
628 }
|
nuclear@0
|
629 post_redisplay();
|
nuclear@0
|
630 break;
|
nuclear@0
|
631
|
nuclear@0
|
632 default:
|
nuclear@0
|
633 break;
|
nuclear@0
|
634 }
|
nuclear@0
|
635 }
|