curvedraw

annotate src/app.cc @ 16:7f795f7fecd6

readme and COPYING
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 20 Dec 2015 10:55:57 +0200
parents 37ab3a4c02f8
children
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 }