nuclear@0: #include nuclear@0: #include nuclear@2: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "opengl.h" nuclear@0: #include "app.h" nuclear@0: #include "curve.h" nuclear@0: #include "widgets.h" nuclear@0: nuclear@1: int win_width, win_height; nuclear@1: float win_aspect; nuclear@1: nuclear@0: static void draw_grid(float sz, float sep, float alpha = 1.0f); nuclear@0: static void draw_curve(const Curve *curve); nuclear@0: static void on_click(int bn, float u, float v); nuclear@0: nuclear@2: // viewport control nuclear@2: static Vector2 view_pan; nuclear@0: static float view_scale = 1.0f; nuclear@0: nuclear@0: static std::vector curves; nuclear@2: static Curve *sel_curve; // selected curve being edited nuclear@2: static Curve *new_curve; // new curve being entered nuclear@2: static Curve *hover_curve; // curve the mouse is hovering over (click to select) nuclear@2: static int sel_pidx = -1; // selected point of the selected or hovered-over curve nuclear@0: nuclear@2: static Label *weight_label; // floating label for the cp weight nuclear@0: nuclear@0: nuclear@0: bool app_init(int argc, char **argv) nuclear@0: { nuclear@0: glEnable(GL_MULTISAMPLE); nuclear@0: glEnable(GL_CULL_FACE); nuclear@2: nuclear@2: glEnable(GL_BLEND); nuclear@2: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: void app_cleanup() nuclear@0: { nuclear@0: for(size_t i=0; idraw(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static void draw_grid(float sz, float sep, float alpha) nuclear@0: { nuclear@0: float x = 0.0f; nuclear@0: nuclear@0: glLineWidth(1.0); nuclear@0: glBegin(GL_LINES); nuclear@0: glColor4f(0.6, 0.3, 0.2, alpha); nuclear@0: glVertex2f(-sz, 0); nuclear@0: glVertex2f(sz, 0); nuclear@0: glColor4f(0.2, 0.3, 0.6, alpha); nuclear@0: glVertex2f(0, -sz); nuclear@0: glVertex2f(0, sz); nuclear@1: glColor4f(0.35, 0.35, 0.35, alpha); nuclear@0: while(x < sz) { nuclear@0: x += sep; nuclear@0: glVertex2f(-sz, x); nuclear@0: glVertex2f(sz, x); nuclear@0: glVertex2f(-sz, -x); nuclear@0: glVertex2f(sz, -x); nuclear@0: glVertex2f(x, -sz); nuclear@0: glVertex2f(x, sz); nuclear@0: glVertex2f(-x, -sz); nuclear@0: glVertex2f(-x, sz); nuclear@0: } nuclear@0: glEnd(); nuclear@0: } nuclear@0: nuclear@0: static void draw_curve(const Curve *curve) nuclear@0: { nuclear@2: int numpt = curve->size(); nuclear@2: int segm = numpt * 16; nuclear@0: nuclear@2: /*if(curve == hover_curve) { nuclear@2: glLineWidth(3.0); nuclear@2: glColor4f(0.8, 0.8, 0.0, 1.0); nuclear@2: nuclear@2: glBegin(GL_LINE_STRIP); nuclear@2: for(int i=0; iinterpolate(t); nuclear@2: glVertex2f(v.x, v.y); nuclear@2: } nuclear@2: glEnd(); nuclear@2: } nuclear@2: */ nuclear@2: nuclear@2: glLineWidth(curve == hover_curve ? 4.0 : 2.0); nuclear@1: if(curve == sel_curve) { nuclear@1: glColor3f(0.3, 0.4, 1.0); nuclear@1: } else if(curve == new_curve) { nuclear@1: glColor3f(1.0, 0.75, 0.3); nuclear@1: } else { nuclear@2: glColor3f(0.6, 0.6, 0.6); nuclear@1: } nuclear@0: glBegin(GL_LINE_STRIP); nuclear@0: for(int i=0; iinterpolate(t); nuclear@0: glVertex2f(v.x, v.y); nuclear@0: } nuclear@0: glEnd(); nuclear@0: glLineWidth(1.0); nuclear@0: nuclear@2: glPointSize(curve == hover_curve ? 10.0 : 7.0); nuclear@0: glBegin(GL_POINTS); nuclear@1: if(curve == new_curve) { nuclear@1: glColor3f(1.0, 0.0, 0.0); nuclear@1: } else { nuclear@1: glColor3f(0.6, 0.3, 0.2); nuclear@1: } nuclear@0: for(int i=0; iget_point(i); nuclear@0: glVertex2f(pt.x, pt.y); nuclear@0: } nuclear@0: glEnd(); nuclear@0: glPointSize(1.0); nuclear@0: } nuclear@0: nuclear@0: void app_reshape(int x, int y) nuclear@0: { nuclear@0: win_width = x; nuclear@0: win_height = y; nuclear@0: win_aspect = (float)x / (float)y; nuclear@0: nuclear@0: glViewport(0, 0, x, y); nuclear@0: glMatrixMode(GL_PROJECTION); nuclear@0: glLoadIdentity(); nuclear@0: glOrtho(-win_aspect, win_aspect, -1, 1, -1, 1); nuclear@0: } nuclear@0: nuclear@0: void app_keyboard(int key, bool pressed) nuclear@0: { nuclear@0: if(pressed) { nuclear@0: switch(key) { nuclear@1: case 'q': nuclear@1: case 'Q': nuclear@1: exit(0); nuclear@1: nuclear@0: case 27: nuclear@1: if(new_curve) { nuclear@1: delete new_curve; nuclear@1: new_curve = 0; nuclear@1: post_redisplay(); nuclear@1: } nuclear@1: break; nuclear@0: nuclear@0: case 'l': nuclear@0: case 'L': nuclear@0: if(sel_curve) { nuclear@0: sel_curve->set_type(CURVE_LINEAR); nuclear@0: post_redisplay(); nuclear@0: } nuclear@1: if(new_curve) { nuclear@1: new_curve->set_type(CURVE_LINEAR); nuclear@1: post_redisplay(); nuclear@1: } nuclear@0: break; nuclear@0: nuclear@0: case 'b': nuclear@0: case 'B': nuclear@0: if(sel_curve) { nuclear@0: sel_curve->set_type(CURVE_BSPLINE); nuclear@0: post_redisplay(); nuclear@0: } nuclear@1: if(new_curve) { nuclear@1: new_curve->set_type(CURVE_BSPLINE); nuclear@1: post_redisplay(); nuclear@1: } nuclear@0: break; nuclear@0: nuclear@0: case 'h': nuclear@0: case 'H': nuclear@0: if(sel_curve) { nuclear@0: sel_curve->set_type(CURVE_HERMITE); nuclear@0: post_redisplay(); nuclear@0: } nuclear@1: if(new_curve) { nuclear@1: new_curve->set_type(CURVE_HERMITE); nuclear@1: post_redisplay(); nuclear@1: } nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static Vector2 pixel_to_uv(int x, int y) nuclear@0: { nuclear@0: float u = win_aspect * (2.0 * (float)x / (float)win_width - 1.0); nuclear@0: float v = 1.0 - 2.0 * (float)y / (float)win_height; nuclear@0: return Vector2(u, v); nuclear@0: } nuclear@0: nuclear@0: static int prev_x, prev_y; nuclear@0: static int click_pos[8][2]; nuclear@0: static unsigned int bnstate; nuclear@0: nuclear@0: #define BNBIT(x) (1 << (x)) nuclear@0: nuclear@0: void app_mouse_button(int bn, bool pressed, int x, int y) nuclear@0: { nuclear@0: prev_x = x; nuclear@0: prev_y = y; nuclear@0: if(pressed) { nuclear@0: bnstate |= BNBIT(bn); nuclear@0: } else { nuclear@0: bnstate &= ~BNBIT(bn); nuclear@0: } nuclear@0: nuclear@0: if(pressed) { nuclear@0: click_pos[bn][0] = x; nuclear@0: click_pos[bn][1] = y; nuclear@0: } else { nuclear@0: int dx = x - click_pos[bn][0]; nuclear@0: int dy = y - click_pos[bn][1]; nuclear@0: nuclear@0: if(abs(dx) + abs(dy) < 3) { nuclear@0: Vector2 uv = pixel_to_uv(x, y); nuclear@0: on_click(bn, uv.x, uv.y); nuclear@0: } nuclear@0: nuclear@0: if(!(bnstate & BNBIT(2))) { nuclear@0: delete weight_label; nuclear@0: weight_label = 0; nuclear@0: post_redisplay(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@2: static bool point_hit_test(const Vector2 &pos, Curve **curveret, int *pidxret) nuclear@0: { nuclear@1: float thres = 0.02; nuclear@0: nuclear@0: for(size_t i=0; inearest_point(pos); nuclear@0: if(pidx == -1) continue; nuclear@0: nuclear@0: Vector2 cp = curves[i]->get_point(pidx); nuclear@2: if((cp - pos).length_sq() < thres * thres) { nuclear@2: *curveret = curves[i]; nuclear@2: *pidxret = pidx; nuclear@2: return true; nuclear@0: } nuclear@0: } nuclear@2: *curveret = 0; nuclear@2: *pidxret = -1; nuclear@2: return false; nuclear@0: } nuclear@0: nuclear@0: void app_mouse_motion(int x, int y) nuclear@0: { nuclear@0: int dx = x - prev_x; nuclear@0: int dy = y - prev_y; nuclear@0: prev_x = x; nuclear@0: prev_y = y; nuclear@0: nuclear@0: if(!dx && !dy) return; nuclear@0: nuclear@2: Vector2 uv = pixel_to_uv(x, y); nuclear@2: nuclear@2: /* when entering a new curve, have the last (extra) point following nuclear@2: * the mouse until it's entered by a click (see on_click). nuclear@2: */ nuclear@2: if(new_curve && !new_curve->empty()) { nuclear@2: new_curve->move_point(new_curve->size() - 1, uv); nuclear@2: post_redisplay(); nuclear@2: } nuclear@2: nuclear@0: if(!new_curve && !bnstate) { nuclear@2: point_hit_test(uv, &hover_curve, &sel_pidx); nuclear@0: post_redisplay(); nuclear@0: } nuclear@0: nuclear@0: if(sel_curve && sel_pidx != -1) { nuclear@0: if(bnstate & BNBIT(0)) { nuclear@0: float w = sel_curve->get_weight(sel_pidx); nuclear@2: sel_curve->set_point(sel_pidx, uv, w); nuclear@0: post_redisplay(); nuclear@0: } nuclear@0: nuclear@0: if(bnstate & BNBIT(2)) { nuclear@0: float w = sel_curve->get_weight(sel_pidx); nuclear@1: w -= dy * 0.01; nuclear@0: if(w < FLT_MIN) w = FLT_MIN; nuclear@0: sel_curve->set_weight(sel_pidx, w); nuclear@0: nuclear@0: if(!weight_label) { nuclear@0: weight_label = new Label; nuclear@0: } nuclear@2: weight_label->set_position(uv); nuclear@0: weight_label->set_textf("w=%g", w); nuclear@0: post_redisplay(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static void on_click(int bn, float u, float v) nuclear@0: { nuclear@2: Vector2 uv = Vector2(u, v); nuclear@2: nuclear@0: switch(bn) { nuclear@2: case 0: // ------- LEFT CLICK ------ nuclear@2: if(hover_curve) { nuclear@2: // if we're hovering: click selects nuclear@2: sel_curve = hover_curve; nuclear@2: hover_curve = 0; nuclear@2: } else if(sel_curve) { nuclear@2: // if we have a selected curve: click adds point (enter new_curve mode) nuclear@2: std::vector::iterator it = std::find(curves.begin(), curves.end(), sel_curve); nuclear@2: assert(it != curves.end()); nuclear@2: curves.erase(it, it + 1); nuclear@2: nuclear@2: new_curve = sel_curve; nuclear@2: sel_curve = 0; nuclear@2: sel_pidx = -1; nuclear@2: nuclear@2: new_curve->add_point(uv); nuclear@2: } else { nuclear@2: // otherwise, click starts a new curve nuclear@2: if(!new_curve) { nuclear@2: new_curve = new Curve; nuclear@2: new_curve->add_point(uv); nuclear@2: } nuclear@2: new_curve->add_point(uv); nuclear@0: } nuclear@0: post_redisplay(); nuclear@0: break; nuclear@0: nuclear@2: case 2: // ------- RIGHT CLICK ------ nuclear@2: if(new_curve) { nuclear@2: // in new-curve mode: finish curve (cancels last floating segment) nuclear@2: new_curve->remove_point(new_curve->size() - 1); nuclear@2: if(new_curve->empty()) { nuclear@2: delete new_curve; nuclear@2: } else { nuclear@2: curves.push_back(new_curve); nuclear@2: } nuclear@2: new_curve = 0; nuclear@2: nuclear@2: } else if(sel_curve) { nuclear@2: // in selected curve mode: delete control point or unselect nuclear@2: Curve *hit_curve; nuclear@2: int hit_pidx; nuclear@2: if(point_hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) { nuclear@2: hit_curve->remove_point(hit_pidx); nuclear@2: sel_pidx = -1; nuclear@2: } else { nuclear@2: sel_curve = 0; nuclear@2: sel_pidx = -1; nuclear@2: } nuclear@2: } nuclear@0: post_redisplay(); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: }