curvedraw
changeset 2:ce7aa9a0594c
improved curve editing
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 17 Dec 2015 05:13:25 +0200 |
parents | 7dcd0f6113e5 |
children | bf78387a9925 |
files | src/app.cc src/curve.cc src/curve.h |
diffstat | 3 files changed, 140 insertions(+), 45 deletions(-) [+] |
line diff
1.1 --- a/src/app.cc Wed Dec 16 04:49:16 2015 +0200 1.2 +++ b/src/app.cc Thu Dec 17 05:13:25 2015 +0200 1.3 @@ -1,5 +1,6 @@ 1.4 #include <stdlib.h> 1.5 #include <float.h> 1.6 +#include <assert.h> 1.7 #include <vector> 1.8 #include <algorithm> 1.9 #include "opengl.h" 1.10 @@ -14,20 +15,26 @@ 1.11 static void draw_curve(const Curve *curve); 1.12 static void on_click(int bn, float u, float v); 1.13 1.14 -static float view_pan_x, view_pan_y; 1.15 +// viewport control 1.16 +static Vector2 view_pan; 1.17 static float view_scale = 1.0f; 1.18 1.19 static std::vector<Curve*> curves; 1.20 -static Curve *sel_curve, *new_curve; 1.21 -static int sel_pidx = -1; 1.22 +static Curve *sel_curve; // selected curve being edited 1.23 +static Curve *new_curve; // new curve being entered 1.24 +static Curve *hover_curve; // curve the mouse is hovering over (click to select) 1.25 +static int sel_pidx = -1; // selected point of the selected or hovered-over curve 1.26 1.27 -static Label *weight_label; 1.28 +static Label *weight_label; // floating label for the cp weight 1.29 1.30 1.31 bool app_init(int argc, char **argv) 1.32 { 1.33 glEnable(GL_MULTISAMPLE); 1.34 glEnable(GL_CULL_FACE); 1.35 + 1.36 + glEnable(GL_BLEND); 1.37 + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1.38 return true; 1.39 } 1.40 1.41 @@ -46,7 +53,7 @@ 1.42 1.43 glMatrixMode(GL_MODELVIEW); 1.44 glLoadIdentity(); 1.45 - glTranslatef(-view_pan_x, -view_pan_y, 0); 1.46 + glTranslatef(-view_pan.x, -view_pan.y, 0); 1.47 glScalef(view_scale, view_scale, view_scale); 1.48 1.49 draw_grid(std::max(win_aspect, 1.0f / win_aspect), 0.1); 1.50 @@ -55,7 +62,6 @@ 1.51 draw_curve(curves[i]); 1.52 } 1.53 if(new_curve) { 1.54 - // TODO special drawing with more feedback 1.55 draw_curve(new_curve); 1.56 } 1.57 if(weight_label) { 1.58 @@ -92,16 +98,30 @@ 1.59 1.60 static void draw_curve(const Curve *curve) 1.61 { 1.62 - int numpt = curve->get_point_count(); 1.63 - int segm = numpt * 16.0f; 1.64 + int numpt = curve->size(); 1.65 + int segm = numpt * 16; 1.66 1.67 - glLineWidth(2.0); 1.68 + /*if(curve == hover_curve) { 1.69 + glLineWidth(3.0); 1.70 + glColor4f(0.8, 0.8, 0.0, 1.0); 1.71 + 1.72 + glBegin(GL_LINE_STRIP); 1.73 + for(int i=0; i<segm; i++) { 1.74 + float t = (float)i / (float)(segm - 1); 1.75 + Vector2 v = curve->interpolate(t); 1.76 + glVertex2f(v.x, v.y); 1.77 + } 1.78 + glEnd(); 1.79 + } 1.80 + */ 1.81 + 1.82 + glLineWidth(curve == hover_curve ? 4.0 : 2.0); 1.83 if(curve == sel_curve) { 1.84 glColor3f(0.3, 0.4, 1.0); 1.85 } else if(curve == new_curve) { 1.86 glColor3f(1.0, 0.75, 0.3); 1.87 } else { 1.88 - glColor3f(0.75, 0.75, 0.75); 1.89 + glColor3f(0.6, 0.6, 0.6); 1.90 } 1.91 glBegin(GL_LINE_STRIP); 1.92 for(int i=0; i<segm; i++) { 1.93 @@ -112,7 +132,7 @@ 1.94 glEnd(); 1.95 glLineWidth(1.0); 1.96 1.97 - glPointSize(7.0); 1.98 + glPointSize(curve == hover_curve ? 10.0 : 7.0); 1.99 glBegin(GL_POINTS); 1.100 if(curve == new_curve) { 1.101 glColor3f(1.0, 0.0, 0.0); 1.102 @@ -244,25 +264,24 @@ 1.103 } 1.104 } 1.105 1.106 -static void hover(int x, int y) 1.107 +static bool point_hit_test(const Vector2 &pos, Curve **curveret, int *pidxret) 1.108 { 1.109 float thres = 0.02; 1.110 1.111 - Vector2 uv = pixel_to_uv(x, y); 1.112 for(size_t i=0; i<curves.size(); i++) { 1.113 - int pidx = curves[i]->nearest_point(uv); 1.114 + int pidx = curves[i]->nearest_point(pos); 1.115 if(pidx == -1) continue; 1.116 1.117 Vector2 cp = curves[i]->get_point(pidx); 1.118 - if((cp - uv).length_sq() < thres * thres) { 1.119 - sel_curve = curves[i]; 1.120 - sel_pidx = pidx; 1.121 - return; 1.122 + if((cp - pos).length_sq() < thres * thres) { 1.123 + *curveret = curves[i]; 1.124 + *pidxret = pidx; 1.125 + return true; 1.126 } 1.127 } 1.128 - 1.129 - sel_curve = 0; 1.130 - sel_pidx = -1; 1.131 + *curveret = 0; 1.132 + *pidxret = -1; 1.133 + return false; 1.134 } 1.135 1.136 void app_mouse_motion(int x, int y) 1.137 @@ -274,15 +293,25 @@ 1.138 1.139 if(!dx && !dy) return; 1.140 1.141 + Vector2 uv = pixel_to_uv(x, y); 1.142 + 1.143 + /* when entering a new curve, have the last (extra) point following 1.144 + * the mouse until it's entered by a click (see on_click). 1.145 + */ 1.146 + if(new_curve && !new_curve->empty()) { 1.147 + new_curve->move_point(new_curve->size() - 1, uv); 1.148 + post_redisplay(); 1.149 + } 1.150 + 1.151 if(!new_curve && !bnstate) { 1.152 - hover(x, y); 1.153 + point_hit_test(uv, &hover_curve, &sel_pidx); 1.154 post_redisplay(); 1.155 } 1.156 1.157 if(sel_curve && sel_pidx != -1) { 1.158 if(bnstate & BNBIT(0)) { 1.159 float w = sel_curve->get_weight(sel_pidx); 1.160 - sel_curve->set_point(sel_pidx, pixel_to_uv(x, y), w); 1.161 + sel_curve->set_point(sel_pidx, uv, w); 1.162 post_redisplay(); 1.163 } 1.164 1.165 @@ -295,7 +324,7 @@ 1.166 if(!weight_label) { 1.167 weight_label = new Label; 1.168 } 1.169 - weight_label->set_position(pixel_to_uv(x, y)); 1.170 + weight_label->set_position(uv); 1.171 weight_label->set_textf("w=%g", w); 1.172 post_redisplay(); 1.173 } 1.174 @@ -304,18 +333,59 @@ 1.175 1.176 static void on_click(int bn, float u, float v) 1.177 { 1.178 + Vector2 uv = Vector2(u, v); 1.179 + 1.180 switch(bn) { 1.181 - case 0: 1.182 - if(!new_curve) { 1.183 - new_curve = new Curve; 1.184 + case 0: // ------- LEFT CLICK ------ 1.185 + if(hover_curve) { 1.186 + // if we're hovering: click selects 1.187 + sel_curve = hover_curve; 1.188 + hover_curve = 0; 1.189 + } else if(sel_curve) { 1.190 + // if we have a selected curve: click adds point (enter new_curve mode) 1.191 + std::vector<Curve*>::iterator it = std::find(curves.begin(), curves.end(), sel_curve); 1.192 + assert(it != curves.end()); 1.193 + curves.erase(it, it + 1); 1.194 + 1.195 + new_curve = sel_curve; 1.196 + sel_curve = 0; 1.197 + sel_pidx = -1; 1.198 + 1.199 + new_curve->add_point(uv); 1.200 + } else { 1.201 + // otherwise, click starts a new curve 1.202 + if(!new_curve) { 1.203 + new_curve = new Curve; 1.204 + new_curve->add_point(uv); 1.205 + } 1.206 + new_curve->add_point(uv); 1.207 } 1.208 - new_curve->add_point(Vector2(u, v)); 1.209 post_redisplay(); 1.210 break; 1.211 1.212 - case 2: 1.213 - curves.push_back(new_curve); 1.214 - new_curve = 0; 1.215 + case 2: // ------- RIGHT CLICK ------ 1.216 + if(new_curve) { 1.217 + // in new-curve mode: finish curve (cancels last floating segment) 1.218 + new_curve->remove_point(new_curve->size() - 1); 1.219 + if(new_curve->empty()) { 1.220 + delete new_curve; 1.221 + } else { 1.222 + curves.push_back(new_curve); 1.223 + } 1.224 + new_curve = 0; 1.225 + 1.226 + } else if(sel_curve) { 1.227 + // in selected curve mode: delete control point or unselect 1.228 + Curve *hit_curve; 1.229 + int hit_pidx; 1.230 + if(point_hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) { 1.231 + hit_curve->remove_point(hit_pidx); 1.232 + sel_pidx = -1; 1.233 + } else { 1.234 + sel_curve = 0; 1.235 + sel_pidx = -1; 1.236 + } 1.237 + } 1.238 post_redisplay(); 1.239 break; 1.240
2.1 --- a/src/curve.cc Wed Dec 16 04:49:16 2015 +0200 2.2 +++ b/src/curve.cc Thu Dec 17 05:13:25 2015 +0200 2.3 @@ -46,11 +46,26 @@ 2.4 return res; 2.5 } 2.6 2.7 -int Curve::get_point_count() const 2.8 +bool Curve::empty() const 2.9 +{ 2.10 + return cp.empty(); 2.11 +} 2.12 + 2.13 +int Curve::size() const 2.14 { 2.15 return (int)cp.size(); 2.16 } 2.17 2.18 +Vector3 &Curve::operator [](int idx) 2.19 +{ 2.20 + return cp[idx]; 2.21 +} 2.22 + 2.23 +const Vector3 &Curve::operator [](int idx) const 2.24 +{ 2.25 + return cp[idx]; 2.26 +} 2.27 + 2.28 const Vector3 &Curve::get_homo_point(int idx) const 2.29 { 2.30 return cp[idx]; 2.31 @@ -84,18 +99,28 @@ 2.32 return true; 2.33 } 2.34 2.35 +bool Curve::move_point(int idx, const Vector2 &p) 2.36 +{ 2.37 + if(idx < 0 || idx >= (int)cp.size()) { 2.38 + return false; 2.39 + } 2.40 + cp[idx] = Vector3(p.x, p.y, cp[idx].z); 2.41 + return true; 2.42 +} 2.43 + 2.44 Vector2 Curve::interpolate(float t, CurveType type) const 2.45 { 2.46 - int num_cp = (int)cp.size(); 2.47 - if(!num_cp) { 2.48 + if(cp.empty()) { 2.49 return Vector2(0, 0); 2.50 } 2.51 + 2.52 + int num_cp = (int)cp.size(); 2.53 if(num_cp == 1) { 2.54 return Vector2(cp[0].x, cp[0].y); 2.55 } 2.56 2.57 Vector3 res; 2.58 - int idx0 = (int)floor(t * (num_cp - 1)); 2.59 + int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2); 2.60 int idx1 = idx0 + 1; 2.61 2.62 float dt = 1.0 / (float)(num_cp - 1); 2.63 @@ -135,10 +160,3 @@ 2.64 { 2.65 return interpolate(t); 2.66 } 2.67 - 2.68 -float Curve::map_param(float x) const 2.69 -{ 2.70 - if(x < 0.0f) x = 0.0f; 2.71 - if(x >= 1.0f) x = 1.0f; 2.72 - return x * ((float)cp.size() - 1); 2.73 -}
3.1 --- a/src/curve.h Wed Dec 16 04:49:16 2015 +0200 3.2 +++ b/src/curve.h Thu Dec 17 05:13:25 2015 +0200 3.3 @@ -15,8 +15,6 @@ 3.4 std::vector<Vector3> cp; 3.5 CurveType type; 3.6 3.7 - float map_param(float x) const; 3.8 - 3.9 public: 3.10 Curve(CurveType type = CURVE_HERMITE); 3.11 3.12 @@ -28,17 +26,26 @@ 3.13 3.14 int nearest_point(const Vector2 &p); 3.15 3.16 - int get_point_count() const; 3.17 + bool empty() const; 3.18 + int size() const; 3.19 + Vector3 &operator [](int idx); 3.20 + const Vector3 &operator [](int idx) const; 3.21 + 3.22 const Vector3 &get_homo_point(int idx) const; // homogeneous point 3.23 Vector2 get_point(int idx) const; 3.24 float get_weight(int idx) const; 3.25 3.26 bool set_point(int idx, const Vector2 &p, float weight = 1.0f); 3.27 bool set_weight(int idx, float weight); 3.28 + // move point without changing its weight 3.29 + bool move_point(int idx, const Vector2 &p); 3.30 3.31 Vector2 interpolate(float t, CurveType type) const; 3.32 Vector2 interpolate(float t) const; 3.33 Vector2 operator ()(float t) const; 3.34 + 3.35 + void draw(int res = -1) const; 3.36 + void draw_cp(float sz = -1.0f) const; 3.37 }; 3.38 3.39 #endif // CURVE_H_