rev |
line source |
nuclear@0
|
1 #include <stdlib.h>
|
nuclear@0
|
2 #include <float.h>
|
nuclear@2
|
3 #include <assert.h>
|
nuclear@0
|
4 #include <vector>
|
nuclear@0
|
5 #include <algorithm>
|
nuclear@0
|
6 #include "opengl.h"
|
nuclear@0
|
7 #include "app.h"
|
nuclear@0
|
8 #include "curve.h"
|
nuclear@0
|
9 #include "widgets.h"
|
nuclear@0
|
10
|
nuclear@1
|
11 int win_width, win_height;
|
nuclear@1
|
12 float win_aspect;
|
nuclear@1
|
13
|
nuclear@0
|
14 static void draw_grid(float sz, float sep, float alpha = 1.0f);
|
nuclear@0
|
15 static void draw_curve(const Curve *curve);
|
nuclear@0
|
16 static void on_click(int bn, float u, float v);
|
nuclear@0
|
17
|
nuclear@2
|
18 // viewport control
|
nuclear@2
|
19 static Vector2 view_pan;
|
nuclear@0
|
20 static float view_scale = 1.0f;
|
nuclear@0
|
21
|
nuclear@0
|
22 static std::vector<Curve*> curves;
|
nuclear@2
|
23 static Curve *sel_curve; // selected curve being edited
|
nuclear@2
|
24 static Curve *new_curve; // new curve being entered
|
nuclear@2
|
25 static Curve *hover_curve; // curve the mouse is hovering over (click to select)
|
nuclear@2
|
26 static int sel_pidx = -1; // selected point of the selected or hovered-over curve
|
nuclear@0
|
27
|
nuclear@2
|
28 static Label *weight_label; // floating label for the cp weight
|
nuclear@0
|
29
|
nuclear@0
|
30
|
nuclear@0
|
31 bool app_init(int argc, char **argv)
|
nuclear@0
|
32 {
|
nuclear@0
|
33 glEnable(GL_MULTISAMPLE);
|
nuclear@0
|
34 glEnable(GL_CULL_FACE);
|
nuclear@2
|
35
|
nuclear@2
|
36 glEnable(GL_BLEND);
|
nuclear@2
|
37 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
nuclear@0
|
38 return true;
|
nuclear@0
|
39 }
|
nuclear@0
|
40
|
nuclear@0
|
41 void app_cleanup()
|
nuclear@0
|
42 {
|
nuclear@0
|
43 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@0
|
44 delete curves[i];
|
nuclear@0
|
45 }
|
nuclear@0
|
46 curves.clear();
|
nuclear@0
|
47 }
|
nuclear@0
|
48
|
nuclear@0
|
49 void app_draw()
|
nuclear@0
|
50 {
|
nuclear@1
|
51 glClearColor(0.1, 0.1, 0.1, 1);
|
nuclear@0
|
52 glClear(GL_COLOR_BUFFER_BIT);
|
nuclear@0
|
53
|
nuclear@0
|
54 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
55 glLoadIdentity();
|
nuclear@2
|
56 glTranslatef(-view_pan.x, -view_pan.y, 0);
|
nuclear@0
|
57 glScalef(view_scale, view_scale, view_scale);
|
nuclear@0
|
58
|
nuclear@0
|
59 draw_grid(std::max(win_aspect, 1.0f / win_aspect), 0.1);
|
nuclear@0
|
60
|
nuclear@0
|
61 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@0
|
62 draw_curve(curves[i]);
|
nuclear@0
|
63 }
|
nuclear@0
|
64 if(new_curve) {
|
nuclear@0
|
65 draw_curve(new_curve);
|
nuclear@0
|
66 }
|
nuclear@0
|
67 if(weight_label) {
|
nuclear@0
|
68 weight_label->draw();
|
nuclear@0
|
69 }
|
nuclear@0
|
70 }
|
nuclear@0
|
71
|
nuclear@0
|
72 static void draw_grid(float sz, float sep, float alpha)
|
nuclear@0
|
73 {
|
nuclear@0
|
74 float x = 0.0f;
|
nuclear@0
|
75
|
nuclear@0
|
76 glLineWidth(1.0);
|
nuclear@0
|
77 glBegin(GL_LINES);
|
nuclear@0
|
78 glColor4f(0.6, 0.3, 0.2, alpha);
|
nuclear@0
|
79 glVertex2f(-sz, 0);
|
nuclear@0
|
80 glVertex2f(sz, 0);
|
nuclear@0
|
81 glColor4f(0.2, 0.3, 0.6, alpha);
|
nuclear@0
|
82 glVertex2f(0, -sz);
|
nuclear@0
|
83 glVertex2f(0, sz);
|
nuclear@1
|
84 glColor4f(0.35, 0.35, 0.35, alpha);
|
nuclear@0
|
85 while(x < sz) {
|
nuclear@0
|
86 x += sep;
|
nuclear@0
|
87 glVertex2f(-sz, x);
|
nuclear@0
|
88 glVertex2f(sz, x);
|
nuclear@0
|
89 glVertex2f(-sz, -x);
|
nuclear@0
|
90 glVertex2f(sz, -x);
|
nuclear@0
|
91 glVertex2f(x, -sz);
|
nuclear@0
|
92 glVertex2f(x, sz);
|
nuclear@0
|
93 glVertex2f(-x, -sz);
|
nuclear@0
|
94 glVertex2f(-x, sz);
|
nuclear@0
|
95 }
|
nuclear@0
|
96 glEnd();
|
nuclear@0
|
97 }
|
nuclear@0
|
98
|
nuclear@0
|
99 static void draw_curve(const Curve *curve)
|
nuclear@0
|
100 {
|
nuclear@2
|
101 int numpt = curve->size();
|
nuclear@2
|
102 int segm = numpt * 16;
|
nuclear@0
|
103
|
nuclear@2
|
104 /*if(curve == hover_curve) {
|
nuclear@2
|
105 glLineWidth(3.0);
|
nuclear@2
|
106 glColor4f(0.8, 0.8, 0.0, 1.0);
|
nuclear@2
|
107
|
nuclear@2
|
108 glBegin(GL_LINE_STRIP);
|
nuclear@2
|
109 for(int i=0; i<segm; i++) {
|
nuclear@2
|
110 float t = (float)i / (float)(segm - 1);
|
nuclear@2
|
111 Vector2 v = curve->interpolate(t);
|
nuclear@2
|
112 glVertex2f(v.x, v.y);
|
nuclear@2
|
113 }
|
nuclear@2
|
114 glEnd();
|
nuclear@2
|
115 }
|
nuclear@2
|
116 */
|
nuclear@2
|
117
|
nuclear@2
|
118 glLineWidth(curve == hover_curve ? 4.0 : 2.0);
|
nuclear@1
|
119 if(curve == sel_curve) {
|
nuclear@1
|
120 glColor3f(0.3, 0.4, 1.0);
|
nuclear@1
|
121 } else if(curve == new_curve) {
|
nuclear@1
|
122 glColor3f(1.0, 0.75, 0.3);
|
nuclear@1
|
123 } else {
|
nuclear@2
|
124 glColor3f(0.6, 0.6, 0.6);
|
nuclear@1
|
125 }
|
nuclear@0
|
126 glBegin(GL_LINE_STRIP);
|
nuclear@0
|
127 for(int i=0; i<segm; i++) {
|
nuclear@0
|
128 float t = (float)i / (float)(segm - 1);
|
nuclear@0
|
129 Vector2 v = curve->interpolate(t);
|
nuclear@0
|
130 glVertex2f(v.x, v.y);
|
nuclear@0
|
131 }
|
nuclear@0
|
132 glEnd();
|
nuclear@0
|
133 glLineWidth(1.0);
|
nuclear@0
|
134
|
nuclear@2
|
135 glPointSize(curve == hover_curve ? 10.0 : 7.0);
|
nuclear@0
|
136 glBegin(GL_POINTS);
|
nuclear@1
|
137 if(curve == new_curve) {
|
nuclear@1
|
138 glColor3f(1.0, 0.0, 0.0);
|
nuclear@1
|
139 } else {
|
nuclear@1
|
140 glColor3f(0.6, 0.3, 0.2);
|
nuclear@1
|
141 }
|
nuclear@0
|
142 for(int i=0; i<numpt; i++) {
|
nuclear@1
|
143 if(curve == sel_curve) {
|
nuclear@1
|
144 if(i == sel_pidx) {
|
nuclear@1
|
145 glColor3f(1.0, 0.2, 0.1);
|
nuclear@1
|
146 } else {
|
nuclear@1
|
147 glColor3f(0.2, 1.0, 0.2);
|
nuclear@1
|
148 }
|
nuclear@1
|
149 }
|
nuclear@0
|
150 Vector2 pt = curve->get_point(i);
|
nuclear@0
|
151 glVertex2f(pt.x, pt.y);
|
nuclear@0
|
152 }
|
nuclear@0
|
153 glEnd();
|
nuclear@0
|
154 glPointSize(1.0);
|
nuclear@0
|
155 }
|
nuclear@0
|
156
|
nuclear@0
|
157 void app_reshape(int x, int y)
|
nuclear@0
|
158 {
|
nuclear@0
|
159 win_width = x;
|
nuclear@0
|
160 win_height = y;
|
nuclear@0
|
161 win_aspect = (float)x / (float)y;
|
nuclear@0
|
162
|
nuclear@0
|
163 glViewport(0, 0, x, y);
|
nuclear@0
|
164 glMatrixMode(GL_PROJECTION);
|
nuclear@0
|
165 glLoadIdentity();
|
nuclear@0
|
166 glOrtho(-win_aspect, win_aspect, -1, 1, -1, 1);
|
nuclear@0
|
167 }
|
nuclear@0
|
168
|
nuclear@0
|
169 void app_keyboard(int key, bool pressed)
|
nuclear@0
|
170 {
|
nuclear@0
|
171 if(pressed) {
|
nuclear@0
|
172 switch(key) {
|
nuclear@1
|
173 case 'q':
|
nuclear@1
|
174 case 'Q':
|
nuclear@1
|
175 exit(0);
|
nuclear@1
|
176
|
nuclear@0
|
177 case 27:
|
nuclear@1
|
178 if(new_curve) {
|
nuclear@1
|
179 delete new_curve;
|
nuclear@1
|
180 new_curve = 0;
|
nuclear@1
|
181 post_redisplay();
|
nuclear@1
|
182 }
|
nuclear@1
|
183 break;
|
nuclear@0
|
184
|
nuclear@0
|
185 case 'l':
|
nuclear@0
|
186 case 'L':
|
nuclear@0
|
187 if(sel_curve) {
|
nuclear@0
|
188 sel_curve->set_type(CURVE_LINEAR);
|
nuclear@0
|
189 post_redisplay();
|
nuclear@0
|
190 }
|
nuclear@1
|
191 if(new_curve) {
|
nuclear@1
|
192 new_curve->set_type(CURVE_LINEAR);
|
nuclear@1
|
193 post_redisplay();
|
nuclear@1
|
194 }
|
nuclear@0
|
195 break;
|
nuclear@0
|
196
|
nuclear@0
|
197 case 'b':
|
nuclear@0
|
198 case 'B':
|
nuclear@0
|
199 if(sel_curve) {
|
nuclear@0
|
200 sel_curve->set_type(CURVE_BSPLINE);
|
nuclear@0
|
201 post_redisplay();
|
nuclear@0
|
202 }
|
nuclear@1
|
203 if(new_curve) {
|
nuclear@1
|
204 new_curve->set_type(CURVE_BSPLINE);
|
nuclear@1
|
205 post_redisplay();
|
nuclear@1
|
206 }
|
nuclear@0
|
207 break;
|
nuclear@0
|
208
|
nuclear@0
|
209 case 'h':
|
nuclear@0
|
210 case 'H':
|
nuclear@0
|
211 if(sel_curve) {
|
nuclear@0
|
212 sel_curve->set_type(CURVE_HERMITE);
|
nuclear@0
|
213 post_redisplay();
|
nuclear@0
|
214 }
|
nuclear@1
|
215 if(new_curve) {
|
nuclear@1
|
216 new_curve->set_type(CURVE_HERMITE);
|
nuclear@1
|
217 post_redisplay();
|
nuclear@1
|
218 }
|
nuclear@0
|
219 break;
|
nuclear@0
|
220 }
|
nuclear@0
|
221 }
|
nuclear@0
|
222 }
|
nuclear@0
|
223
|
nuclear@0
|
224 static Vector2 pixel_to_uv(int x, int y)
|
nuclear@0
|
225 {
|
nuclear@0
|
226 float u = win_aspect * (2.0 * (float)x / (float)win_width - 1.0);
|
nuclear@0
|
227 float v = 1.0 - 2.0 * (float)y / (float)win_height;
|
nuclear@0
|
228 return Vector2(u, v);
|
nuclear@0
|
229 }
|
nuclear@0
|
230
|
nuclear@0
|
231 static int prev_x, prev_y;
|
nuclear@0
|
232 static int click_pos[8][2];
|
nuclear@0
|
233 static unsigned int bnstate;
|
nuclear@0
|
234
|
nuclear@0
|
235 #define BNBIT(x) (1 << (x))
|
nuclear@0
|
236
|
nuclear@0
|
237 void app_mouse_button(int bn, bool pressed, int x, int y)
|
nuclear@0
|
238 {
|
nuclear@0
|
239 prev_x = x;
|
nuclear@0
|
240 prev_y = y;
|
nuclear@0
|
241 if(pressed) {
|
nuclear@0
|
242 bnstate |= BNBIT(bn);
|
nuclear@0
|
243 } else {
|
nuclear@0
|
244 bnstate &= ~BNBIT(bn);
|
nuclear@0
|
245 }
|
nuclear@0
|
246
|
nuclear@0
|
247 if(pressed) {
|
nuclear@0
|
248 click_pos[bn][0] = x;
|
nuclear@0
|
249 click_pos[bn][1] = y;
|
nuclear@0
|
250 } else {
|
nuclear@0
|
251 int dx = x - click_pos[bn][0];
|
nuclear@0
|
252 int dy = y - click_pos[bn][1];
|
nuclear@0
|
253
|
nuclear@0
|
254 if(abs(dx) + abs(dy) < 3) {
|
nuclear@0
|
255 Vector2 uv = pixel_to_uv(x, y);
|
nuclear@0
|
256 on_click(bn, uv.x, uv.y);
|
nuclear@0
|
257 }
|
nuclear@0
|
258
|
nuclear@0
|
259 if(!(bnstate & BNBIT(2))) {
|
nuclear@0
|
260 delete weight_label;
|
nuclear@0
|
261 weight_label = 0;
|
nuclear@0
|
262 post_redisplay();
|
nuclear@0
|
263 }
|
nuclear@0
|
264 }
|
nuclear@0
|
265 }
|
nuclear@0
|
266
|
nuclear@2
|
267 static bool point_hit_test(const Vector2 &pos, Curve **curveret, int *pidxret)
|
nuclear@0
|
268 {
|
nuclear@1
|
269 float thres = 0.02;
|
nuclear@0
|
270
|
nuclear@0
|
271 for(size_t i=0; i<curves.size(); i++) {
|
nuclear@2
|
272 int pidx = curves[i]->nearest_point(pos);
|
nuclear@0
|
273 if(pidx == -1) continue;
|
nuclear@0
|
274
|
nuclear@0
|
275 Vector2 cp = curves[i]->get_point(pidx);
|
nuclear@2
|
276 if((cp - pos).length_sq() < thres * thres) {
|
nuclear@2
|
277 *curveret = curves[i];
|
nuclear@2
|
278 *pidxret = pidx;
|
nuclear@2
|
279 return true;
|
nuclear@0
|
280 }
|
nuclear@0
|
281 }
|
nuclear@2
|
282 *curveret = 0;
|
nuclear@2
|
283 *pidxret = -1;
|
nuclear@2
|
284 return false;
|
nuclear@0
|
285 }
|
nuclear@0
|
286
|
nuclear@0
|
287 void app_mouse_motion(int x, int y)
|
nuclear@0
|
288 {
|
nuclear@0
|
289 int dx = x - prev_x;
|
nuclear@0
|
290 int dy = y - prev_y;
|
nuclear@0
|
291 prev_x = x;
|
nuclear@0
|
292 prev_y = y;
|
nuclear@0
|
293
|
nuclear@0
|
294 if(!dx && !dy) return;
|
nuclear@0
|
295
|
nuclear@2
|
296 Vector2 uv = pixel_to_uv(x, y);
|
nuclear@2
|
297
|
nuclear@2
|
298 /* when entering a new curve, have the last (extra) point following
|
nuclear@2
|
299 * the mouse until it's entered by a click (see on_click).
|
nuclear@2
|
300 */
|
nuclear@2
|
301 if(new_curve && !new_curve->empty()) {
|
nuclear@2
|
302 new_curve->move_point(new_curve->size() - 1, uv);
|
nuclear@2
|
303 post_redisplay();
|
nuclear@2
|
304 }
|
nuclear@2
|
305
|
nuclear@0
|
306 if(!new_curve && !bnstate) {
|
nuclear@2
|
307 point_hit_test(uv, &hover_curve, &sel_pidx);
|
nuclear@0
|
308 post_redisplay();
|
nuclear@0
|
309 }
|
nuclear@0
|
310
|
nuclear@0
|
311 if(sel_curve && sel_pidx != -1) {
|
nuclear@0
|
312 if(bnstate & BNBIT(0)) {
|
nuclear@0
|
313 float w = sel_curve->get_weight(sel_pidx);
|
nuclear@2
|
314 sel_curve->set_point(sel_pidx, uv, w);
|
nuclear@0
|
315 post_redisplay();
|
nuclear@0
|
316 }
|
nuclear@0
|
317
|
nuclear@0
|
318 if(bnstate & BNBIT(2)) {
|
nuclear@0
|
319 float w = sel_curve->get_weight(sel_pidx);
|
nuclear@1
|
320 w -= dy * 0.01;
|
nuclear@0
|
321 if(w < FLT_MIN) w = FLT_MIN;
|
nuclear@0
|
322 sel_curve->set_weight(sel_pidx, w);
|
nuclear@0
|
323
|
nuclear@0
|
324 if(!weight_label) {
|
nuclear@0
|
325 weight_label = new Label;
|
nuclear@0
|
326 }
|
nuclear@2
|
327 weight_label->set_position(uv);
|
nuclear@0
|
328 weight_label->set_textf("w=%g", w);
|
nuclear@0
|
329 post_redisplay();
|
nuclear@0
|
330 }
|
nuclear@0
|
331 }
|
nuclear@0
|
332 }
|
nuclear@0
|
333
|
nuclear@0
|
334 static void on_click(int bn, float u, float v)
|
nuclear@0
|
335 {
|
nuclear@2
|
336 Vector2 uv = Vector2(u, v);
|
nuclear@2
|
337
|
nuclear@0
|
338 switch(bn) {
|
nuclear@2
|
339 case 0: // ------- LEFT CLICK ------
|
nuclear@2
|
340 if(hover_curve) {
|
nuclear@2
|
341 // if we're hovering: click selects
|
nuclear@2
|
342 sel_curve = hover_curve;
|
nuclear@2
|
343 hover_curve = 0;
|
nuclear@2
|
344 } else if(sel_curve) {
|
nuclear@2
|
345 // if we have a selected curve: click adds point (enter new_curve mode)
|
nuclear@2
|
346 std::vector<Curve*>::iterator it = std::find(curves.begin(), curves.end(), sel_curve);
|
nuclear@2
|
347 assert(it != curves.end());
|
nuclear@2
|
348 curves.erase(it, it + 1);
|
nuclear@2
|
349
|
nuclear@2
|
350 new_curve = sel_curve;
|
nuclear@2
|
351 sel_curve = 0;
|
nuclear@2
|
352 sel_pidx = -1;
|
nuclear@2
|
353
|
nuclear@2
|
354 new_curve->add_point(uv);
|
nuclear@2
|
355 } else {
|
nuclear@2
|
356 // otherwise, click starts a new curve
|
nuclear@2
|
357 if(!new_curve) {
|
nuclear@2
|
358 new_curve = new Curve;
|
nuclear@2
|
359 new_curve->add_point(uv);
|
nuclear@2
|
360 }
|
nuclear@2
|
361 new_curve->add_point(uv);
|
nuclear@0
|
362 }
|
nuclear@0
|
363 post_redisplay();
|
nuclear@0
|
364 break;
|
nuclear@0
|
365
|
nuclear@2
|
366 case 2: // ------- RIGHT CLICK ------
|
nuclear@2
|
367 if(new_curve) {
|
nuclear@2
|
368 // in new-curve mode: finish curve (cancels last floating segment)
|
nuclear@2
|
369 new_curve->remove_point(new_curve->size() - 1);
|
nuclear@2
|
370 if(new_curve->empty()) {
|
nuclear@2
|
371 delete new_curve;
|
nuclear@2
|
372 } else {
|
nuclear@2
|
373 curves.push_back(new_curve);
|
nuclear@2
|
374 }
|
nuclear@2
|
375 new_curve = 0;
|
nuclear@2
|
376
|
nuclear@2
|
377 } else if(sel_curve) {
|
nuclear@2
|
378 // in selected curve mode: delete control point or unselect
|
nuclear@2
|
379 Curve *hit_curve;
|
nuclear@2
|
380 int hit_pidx;
|
nuclear@2
|
381 if(point_hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) {
|
nuclear@2
|
382 hit_curve->remove_point(hit_pidx);
|
nuclear@2
|
383 sel_pidx = -1;
|
nuclear@2
|
384 } else {
|
nuclear@2
|
385 sel_curve = 0;
|
nuclear@2
|
386 sel_pidx = -1;
|
nuclear@2
|
387 }
|
nuclear@2
|
388 }
|
nuclear@0
|
389 post_redisplay();
|
nuclear@0
|
390 break;
|
nuclear@0
|
391
|
nuclear@0
|
392 default:
|
nuclear@0
|
393 break;
|
nuclear@0
|
394 }
|
nuclear@0
|
395 }
|