# HG changeset patch # User John Tsiombikas # Date 1450322005 -7200 # Node ID ce7aa9a0594c4e27b554abb976c6da15c075e09e # Parent 7dcd0f6113e5411de8a9535e74d6940811e7b4a3 improved curve editing diff -r 7dcd0f6113e5 -r ce7aa9a0594c src/app.cc --- a/src/app.cc Wed Dec 16 04:49:16 2015 +0200 +++ b/src/app.cc Thu Dec 17 05:13:25 2015 +0200 @@ -1,5 +1,6 @@ #include #include +#include #include #include #include "opengl.h" @@ -14,20 +15,26 @@ static void draw_curve(const Curve *curve); static void on_click(int bn, float u, float v); -static float view_pan_x, view_pan_y; +// viewport control +static Vector2 view_pan; static float view_scale = 1.0f; static std::vector curves; -static Curve *sel_curve, *new_curve; -static int sel_pidx = -1; +static Curve *sel_curve; // selected curve being edited +static Curve *new_curve; // new curve being entered +static Curve *hover_curve; // curve the mouse is hovering over (click to select) +static int sel_pidx = -1; // selected point of the selected or hovered-over curve -static Label *weight_label; +static Label *weight_label; // floating label for the cp weight bool app_init(int argc, char **argv) { glEnable(GL_MULTISAMPLE); glEnable(GL_CULL_FACE); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); return true; } @@ -46,7 +53,7 @@ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glTranslatef(-view_pan_x, -view_pan_y, 0); + glTranslatef(-view_pan.x, -view_pan.y, 0); glScalef(view_scale, view_scale, view_scale); draw_grid(std::max(win_aspect, 1.0f / win_aspect), 0.1); @@ -55,7 +62,6 @@ draw_curve(curves[i]); } if(new_curve) { - // TODO special drawing with more feedback draw_curve(new_curve); } if(weight_label) { @@ -92,16 +98,30 @@ static void draw_curve(const Curve *curve) { - int numpt = curve->get_point_count(); - int segm = numpt * 16.0f; + int numpt = curve->size(); + int segm = numpt * 16; - glLineWidth(2.0); + /*if(curve == hover_curve) { + glLineWidth(3.0); + glColor4f(0.8, 0.8, 0.0, 1.0); + + glBegin(GL_LINE_STRIP); + for(int i=0; iinterpolate(t); + glVertex2f(v.x, v.y); + } + glEnd(); + } + */ + + glLineWidth(curve == hover_curve ? 4.0 : 2.0); if(curve == sel_curve) { glColor3f(0.3, 0.4, 1.0); } else if(curve == new_curve) { glColor3f(1.0, 0.75, 0.3); } else { - glColor3f(0.75, 0.75, 0.75); + glColor3f(0.6, 0.6, 0.6); } glBegin(GL_LINE_STRIP); for(int i=0; inearest_point(uv); + int pidx = curves[i]->nearest_point(pos); if(pidx == -1) continue; Vector2 cp = curves[i]->get_point(pidx); - if((cp - uv).length_sq() < thres * thres) { - sel_curve = curves[i]; - sel_pidx = pidx; - return; + if((cp - pos).length_sq() < thres * thres) { + *curveret = curves[i]; + *pidxret = pidx; + return true; } } - - sel_curve = 0; - sel_pidx = -1; + *curveret = 0; + *pidxret = -1; + return false; } void app_mouse_motion(int x, int y) @@ -274,15 +293,25 @@ if(!dx && !dy) return; + Vector2 uv = pixel_to_uv(x, y); + + /* when entering a new curve, have the last (extra) point following + * the mouse until it's entered by a click (see on_click). + */ + if(new_curve && !new_curve->empty()) { + new_curve->move_point(new_curve->size() - 1, uv); + post_redisplay(); + } + if(!new_curve && !bnstate) { - hover(x, y); + point_hit_test(uv, &hover_curve, &sel_pidx); post_redisplay(); } if(sel_curve && sel_pidx != -1) { if(bnstate & BNBIT(0)) { float w = sel_curve->get_weight(sel_pidx); - sel_curve->set_point(sel_pidx, pixel_to_uv(x, y), w); + sel_curve->set_point(sel_pidx, uv, w); post_redisplay(); } @@ -295,7 +324,7 @@ if(!weight_label) { weight_label = new Label; } - weight_label->set_position(pixel_to_uv(x, y)); + weight_label->set_position(uv); weight_label->set_textf("w=%g", w); post_redisplay(); } @@ -304,18 +333,59 @@ static void on_click(int bn, float u, float v) { + Vector2 uv = Vector2(u, v); + switch(bn) { - case 0: - if(!new_curve) { - new_curve = new Curve; + case 0: // ------- LEFT CLICK ------ + if(hover_curve) { + // if we're hovering: click selects + sel_curve = hover_curve; + hover_curve = 0; + } else if(sel_curve) { + // if we have a selected curve: click adds point (enter new_curve mode) + std::vector::iterator it = std::find(curves.begin(), curves.end(), sel_curve); + assert(it != curves.end()); + curves.erase(it, it + 1); + + new_curve = sel_curve; + sel_curve = 0; + sel_pidx = -1; + + new_curve->add_point(uv); + } else { + // otherwise, click starts a new curve + if(!new_curve) { + new_curve = new Curve; + new_curve->add_point(uv); + } + new_curve->add_point(uv); } - new_curve->add_point(Vector2(u, v)); post_redisplay(); break; - case 2: - curves.push_back(new_curve); - new_curve = 0; + case 2: // ------- RIGHT CLICK ------ + if(new_curve) { + // in new-curve mode: finish curve (cancels last floating segment) + new_curve->remove_point(new_curve->size() - 1); + if(new_curve->empty()) { + delete new_curve; + } else { + curves.push_back(new_curve); + } + new_curve = 0; + + } else if(sel_curve) { + // in selected curve mode: delete control point or unselect + Curve *hit_curve; + int hit_pidx; + if(point_hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) { + hit_curve->remove_point(hit_pidx); + sel_pidx = -1; + } else { + sel_curve = 0; + sel_pidx = -1; + } + } post_redisplay(); break; diff -r 7dcd0f6113e5 -r ce7aa9a0594c src/curve.cc --- a/src/curve.cc Wed Dec 16 04:49:16 2015 +0200 +++ b/src/curve.cc Thu Dec 17 05:13:25 2015 +0200 @@ -46,11 +46,26 @@ return res; } -int Curve::get_point_count() const +bool Curve::empty() const +{ + return cp.empty(); +} + +int Curve::size() const { return (int)cp.size(); } +Vector3 &Curve::operator [](int idx) +{ + return cp[idx]; +} + +const Vector3 &Curve::operator [](int idx) const +{ + return cp[idx]; +} + const Vector3 &Curve::get_homo_point(int idx) const { return cp[idx]; @@ -84,18 +99,28 @@ return true; } +bool Curve::move_point(int idx, const Vector2 &p) +{ + if(idx < 0 || idx >= (int)cp.size()) { + return false; + } + cp[idx] = Vector3(p.x, p.y, cp[idx].z); + return true; +} + Vector2 Curve::interpolate(float t, CurveType type) const { - int num_cp = (int)cp.size(); - if(!num_cp) { + if(cp.empty()) { return Vector2(0, 0); } + + int num_cp = (int)cp.size(); if(num_cp == 1) { return Vector2(cp[0].x, cp[0].y); } Vector3 res; - int idx0 = (int)floor(t * (num_cp - 1)); + int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2); int idx1 = idx0 + 1; float dt = 1.0 / (float)(num_cp - 1); @@ -135,10 +160,3 @@ { return interpolate(t); } - -float Curve::map_param(float x) const -{ - if(x < 0.0f) x = 0.0f; - if(x >= 1.0f) x = 1.0f; - return x * ((float)cp.size() - 1); -} diff -r 7dcd0f6113e5 -r ce7aa9a0594c src/curve.h --- a/src/curve.h Wed Dec 16 04:49:16 2015 +0200 +++ b/src/curve.h Thu Dec 17 05:13:25 2015 +0200 @@ -15,8 +15,6 @@ std::vector cp; CurveType type; - float map_param(float x) const; - public: Curve(CurveType type = CURVE_HERMITE); @@ -28,17 +26,26 @@ int nearest_point(const Vector2 &p); - int get_point_count() const; + bool empty() const; + int size() const; + Vector3 &operator [](int idx); + const Vector3 &operator [](int idx) const; + const Vector3 &get_homo_point(int idx) const; // homogeneous point Vector2 get_point(int idx) const; float get_weight(int idx) const; bool set_point(int idx, const Vector2 &p, float weight = 1.0f); bool set_weight(int idx, float weight); + // move point without changing its weight + bool move_point(int idx, const Vector2 &p); Vector2 interpolate(float t, CurveType type) const; Vector2 interpolate(float t) const; Vector2 operator ()(float t) const; + + void draw(int res = -1) const; + void draw_cp(float sz = -1.0f) const; }; #endif // CURVE_H_