# HG changeset patch # User John Tsiombikas # Date 1450592544 -7200 # Node ID 4da693339d99d606b08332506f262d6df8ee3acf # Parent 84a647283237d7c9e13a2f515cc28569bb162bdf - distance from curve - hover/selection of curves directly on the curve diff -r 84a647283237 -r 4da693339d99 src/app.cc --- a/src/app.cc Sun Dec 20 07:21:32 2015 +0200 +++ b/src/app.cc Sun Dec 20 08:22:24 2015 +0200 @@ -36,7 +36,8 @@ 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 int sel_pidx = -1; // selected point of the selected curve +static int hover_pidx = -1; // hovered over point static Label *weight_label; // floating label for the cp weight @@ -389,7 +390,7 @@ int pidx = curves[i]->nearest_point(pos); if(pidx == -1) continue; - Vector2 cp = curves[i]->get_point(pidx); + Vector2 cp = curves[i]->get_point2(pidx); if((cp - pos).length_sq() < thres * thres) { *curveret = curves[i]; *pidxret = pidx; @@ -401,6 +402,28 @@ return false; } +static bool hit_test(const Vector2 &pos, Curve **curveret, int *pidxret) +{ + float thres = 0.02 / view_scale; + + if(point_hit_test(pos, curveret, pidxret)) { + return true; + } + + Vector3 pos3 = Vector3(pos.x, pos.y, 0.0f); + for(size_t i=0; idistance_sq(pos3)) < thres * thres) { + *curveret = curves[i]; + *pidxret = -1; + return true; + } + } + *curveret = 0; + *pidxret = -1; + return false; +} + static Vector2 snap(const Vector2 &p) { switch(snap_mode) { @@ -427,7 +450,7 @@ Vector2 uv = pixel_to_uv(x, y); mouse_pointer = uv; - post_redisplay(); + //post_redisplay(); /* when entering a new curve, have the last (extra) point following * the mouse until it's entered by a click (see on_click). @@ -439,7 +462,10 @@ if(!new_curve && !bnstate) { // not dragging, highlight curve under mouse - point_hit_test(uv, &hover_curve, &sel_pidx); + hit_test(uv, &hover_curve, &hover_pidx); + if(hover_curve == sel_curve) { + sel_pidx = hover_pidx; + } post_redisplay(); } else { @@ -499,6 +525,7 @@ if(hover_curve) { // if we're hovering: click selects sel_curve = hover_curve; + sel_pidx = hover_pidx; hover_curve = 0; } else if(sel_curve) { // if we have a selected curve: click adds point (enter new_curve mode) @@ -537,9 +564,11 @@ // 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; + if(hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) { + if(hit_pidx != -1) { + hit_curve->remove_point(hit_pidx); + sel_pidx = -1; + } } else { sel_curve = 0; sel_pidx = -1; diff -r 84a647283237 -r 4da693339d99 src/curve.cc --- a/src/curve.cc Sun Dec 20 07:21:32 2015 +0200 +++ b/src/curve.cc Sun Dec 20 08:22:24 2015 +0200 @@ -262,69 +262,69 @@ inval_bounds(); } -/* Projection to the curve is not correct, but should be good enough for - * most purposes. - * - * First we find the nearest segment (pair of control points), and then - * we subdivide between them to find the nearest interpolated point in that - * segment. - * The incorrect assumption here is that the nearest segment as defined by - * the distance of its control points to p, will contain the nearest point - * on the curve. This only holds for polylines, and *possibly* bsplines, but - * certainly not hermite splines. - */ -Vector3 Curve::proj_point(const Vector3 &p) const +Vector3 Curve::proj_point(const Vector3 &p, float refine_thres) const { - // TODO fix: select nearest segment based on point-line distance, not distance from the CPs + // first step through the curve a few times and find the nearest of them int num_cp = size(); - if(num_cp <= 0) return p; - if(num_cp == 1) return cp[0]; + int num_steps = num_cp * 5; // arbitrary number; sounds ok + float dt = 1.0f / (float)(num_steps - 1); - int idx0 = nearest_point(p); - int next_idx = idx0 + 1; - int prev_idx = idx0 - 1; + float best_distsq = FLT_MAX; + float best_t = 0.0f; + Vector3 best_pp; - float next_distsq = next_idx >= num_cp ? FLT_MAX : (get_point3(next_idx) - p).length_sq(); - float prev_distsq = prev_idx < 0 ? FLT_MAX : (get_point3(prev_idx) - p).length_sq(); - int idx1 = next_distsq < prev_distsq ? next_idx : prev_idx; - assert(idx1 >= 0 && idx1 < num_cp - 1); - if(idx0 > idx1) std::swap(idx0, idx1); + float t = 0.0f; + for(int i=0; i dist0 && dist > dist1) { - pp = dist0 < dist1 ? pp0 : pp1; + if(fabs(dn - dp) < refine_thres * refine_thres) { break; } - if(dist0 < dist1) { - t1 = t; - dist1 = dist; - pp1 = pp; + if(dn < dist) { + t = tn; + dist = dn; + } else if(dp < dist) { + t = tp; + dist = dp; } else { - t0 = t; - dist0 = dist; - pp0 = pp; - } - - if(fabs(dist0 - dist1) < 1e-4) { - break; + break; // found the minimum } } - return pp; + + return interpolate(t); } +float Curve::distance(const Vector3 &p) const +{ + return (proj_point(p) - p).length(); +} + +float Curve::distance_sq(const Vector3 &p) const +{ + return fabs((proj_point(p) - p).length_sq()); +} + + Vector3 Curve::interpolate_segment(int a, int b, float t) const { int num_cp = size(); @@ -346,6 +346,7 @@ if(res.w != 0.0f) { res.x /= res.w; res.y /= res.w; + res.z /= res.w; } } } @@ -364,6 +365,9 @@ return Vector3(cp[0].x, cp[0].y, cp[0].z); } + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2); int idx1 = idx0 + 1; diff -r 84a647283237 -r 4da693339d99 src/curve.h --- a/src/curve.h Sun Dec 20 07:21:32 2015 +0200 +++ b/src/curve.h Sun Dec 20 08:22:24 2015 +0200 @@ -70,9 +70,11 @@ void normalize(); // project a point to the curve (nearest point on the curve) - Vector3 proj_point(const Vector3 &p) const; - // equivalent to (proj_point(p) - p).length(); + Vector3 proj_point(const Vector3 &p, float refine_thres = 0.01) const; + // equivalent to (proj_point(p) - p).length() float distance(const Vector3 &p) const; + // equivalent to fabs((proj_point(p) - p).length_sq()) + float distance_sq(const Vector3 &p) const; Vector3 interpolate_segment(int a, int b, float t) const; Vector3 interpolate(float t) const;