| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 Solving the Nearest Point-on-Curve Problem | |
| 3 and | |
| 4 A Bezier Curve-Based Root-Finder | |
| 5 by Philip J. Schneider | |
| 6 from "Graphics Gems", Academic Press, 1990 | |
| 7 */ | |
| 8 | |
| 9 /* point_on_curve.c */ | |
| 10 | |
| 11 #include <stdio.h> | |
| 12 #include <malloc.h> | |
| 13 #include <math.h> | |
| 14 #include "GraphicsGems.h" | |
| 15 | |
| 16 #define TESTMODE | |
| 17 | |
| 18 /* | |
| 19 * Forward declarations | |
| 20 */ | |
| 21 Point2 NearestPointOnCurve(); | |
| 22 static int FindRoots(); | |
| 23 static Point2 *ConvertToBezierForm(); | |
| 24 static double ComputeXIntercept(); | |
| 25 static int ControlPolygonFlatEnough(); | |
| 26 static int CrossingCount(); | |
| 27 static Point2 Bezier(); | |
| 28 static Vector2 V2ScaleII(); | |
| 29 | |
| 30 int MAXDEPTH = 64; /* Maximum depth for recursion */ | |
| 31 | |
| 32 #define EPSILON (ldexp(1.0,-MAXDEPTH-1)) /*Flatness control value */ | |
| 33 #define DEGREE 3 /* Cubic Bezier curve */ | |
| 34 #define W_DEGREE 5 /* Degree of eqn to find roots of */ | |
| 35 | |
| 36 #ifdef TESTMODE | |
| 37 /* | |
| 38 * main : | |
| 39 * Given a cubic Bezier curve (i.e., its control points), and some | |
| 40 * arbitrary point in the plane, find the point on the curve | |
| 41 * closest to that arbitrary point. | |
| 42 */ | |
| 43 main() | |
| 44 { | |
| 45 | |
| 46 static Point2 bezCurve[4] = { /* A cubic Bezier curve */ | |
| 47 { 0.0, 0.0 }, | |
| 48 { 1.0, 2.0 }, | |
| 49 { 3.0, 3.0 }, | |
| 50 { 4.0, 2.0 }, | |
| 51 }; | |
| 52 static Point2 arbPoint = { 3.5, 2.0 }; /*Some arbitrary point*/ | |
| 53 Point2 pointOnCurve; /* Nearest point on the curve */ | |
| 54 | |
| 55 /* Find the closest point */ | |
| 56 pointOnCurve = NearestPointOnCurve(arbPoint, bezCurve); | |
| 57 printf("pointOnCurve : (%4.4f, %4.4f)\n", pointOnCurve.x, | |
| 58 pointOnCurve.y); | |
| 59 } | |
| 60 #endif /* TESTMODE */ | |
| 61 | |
| 62 | |
| 63 /* | |
| 64 * NearestPointOnCurve : | |
| 65 * Compute the parameter value of the point on a Bezier | |
| 66 * curve segment closest to some arbtitrary, user-input point. | |
| 67 * Return the point on the curve at that parameter value. | |
| 68 * | |
| 69 */ | |
| 70 Point2 NearestPointOnCurve(P, V) | |
| 71 Point2 P; /* The user-supplied point */ | |
| 72 Point2 *V; /* Control points of cubic Bezier */ | |
| 73 { | |
| 74 Point2 *w; /* Ctl pts for 5th-degree eqn */ | |
| 75 double t_candidate[W_DEGREE]; /* Possible roots */ | |
| 76 int n_solutions; /* Number of roots found */ | |
| 77 double t; /* Parameter value of closest pt*/ | |
| 78 | |
| 79 /* Convert problem to 5th-degree Bezier form */ | |
| 80 w = ConvertToBezierForm(P, V); | |
| 81 | |
| 82 /* Find all possible roots of 5th-degree equation */ | |
| 83 n_solutions = FindRoots(w, W_DEGREE, t_candidate, 0); | |
| 84 free((char *)w); | |
| 85 | |
| 86 /* Compare distances of P to all candidates, and to t=0, and t=1 */ | |
| 87 { | |
| 88 double dist, new_dist; | |
| 89 Point2 p; | |
| 90 Vector2 v; | |
| 91 int i; | |
| 92 | |
| 93 | |
| 94 /* Check distance to beginning of curve, where t = 0 */ | |
| 95 dist = V2SquaredLength(V2Sub(&P, &V[0], &v)); | |
| 96 t = 0.0; | |
| 97 | |
| 98 /* Find distances for candidate points */ | |
| 99 for (i = 0; i < n_solutions; i++) { | |
| 100 p = Bezier(V, DEGREE, t_candidate[i], | |
| 101 (Point2 *)NULL, (Point2 *)NULL); | |
| 102 new_dist = V2SquaredLength(V2Sub(&P, &p, &v)); | |
| 103 if (new_dist < dist) { | |
| 104 dist = new_dist; | |
| 105 t = t_candidate[i]; | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 /* Finally, look at distance to end point, where t = 1.0 */ | |
| 110 new_dist = V2SquaredLength(V2Sub(&P, &V[DEGREE], &v)); | |
| 111 if (new_dist < dist) { | |
| 112 dist = new_dist; | |
| 113 t = 1.0; | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 /* Return the point on the curve at parameter value t */ | |
| 118 printf("t : %4.12f\n", t); | |
| 119 return (Bezier(V, DEGREE, t, (Point2 *)NULL, (Point2 *)NULL)); | |
| 120 } | |
| 121 | |
| 122 | |
| 123 /* | |
| 124 * ConvertToBezierForm : | |
| 125 * Given a point and a Bezier curve, generate a 5th-degree | |
| 126 * Bezier-format equation whose solution finds the point on the | |
| 127 * curve nearest the user-defined point. | |
| 128 */ | |
| 129 static Point2 *ConvertToBezierForm(P, V) | |
| 130 Point2 P; /* The point to find t for */ | |
| 131 Point2 *V; /* The control points */ | |
| 132 { | |
| 133 int i, j, k, m, n, ub, lb; | |
| 134 int row, column; /* Table indices */ | |
| 135 Vector2 c[DEGREE+1]; /* V(i)'s - P */ | |
| 136 Vector2 d[DEGREE]; /* V(i+1) - V(i) */ | |
| 137 Point2 *w; /* Ctl pts of 5th-degree curve */ | |
| 138 double cdTable[3][4]; /* Dot product of c, d */ | |
| 139 static double z[3][4] = { /* Precomputed "z" for cubics */ | |
| 140 {1.0, 0.6, 0.3, 0.1}, | |
| 141 {0.4, 0.6, 0.6, 0.4}, | |
| 142 {0.1, 0.3, 0.6, 1.0}, | |
| 143 }; | |
| 144 | |
| 145 | |
| 146 /*Determine the c's -- these are vectors created by subtracting*/ | |
| 147 /* point P from each of the control points */ | |
| 148 for (i = 0; i <= DEGREE; i++) { | |
| 149 V2Sub(&V[i], &P, &c[i]); | |
| 150 } | |
| 151 /* Determine the d's -- these are vectors created by subtracting*/ | |
| 152 /* each control point from the next */ | |
| 153 for (i = 0; i <= DEGREE - 1; i++) { | |
| 154 d[i] = V2ScaleII(V2Sub(&V[i+1], &V[i], &d[i]), 3.0); | |
| 155 } | |
| 156 | |
| 157 /* Create the c,d table -- this is a table of dot products of the */ | |
| 158 /* c's and d's */ | |
| 159 for (row = 0; row <= DEGREE - 1; row++) { | |
| 160 for (column = 0; column <= DEGREE; column++) { | |
| 161 cdTable[row][column] = V2Dot(&d[row], &c[column]); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 /* Now, apply the z's to the dot products, on the skew diagonal*/ | |
| 166 /* Also, set up the x-values, making these "points" */ | |
| 167 w = (Point2 *)malloc((unsigned)(W_DEGREE+1) * sizeof(Point2)); | |
| 168 for (i = 0; i <= W_DEGREE; i++) { | |
| 169 w[i].y = 0.0; | |
| 170 w[i].x = (double)(i) / W_DEGREE; | |
| 171 } | |
| 172 | |
| 173 n = DEGREE; | |
| 174 m = DEGREE-1; | |
| 175 for (k = 0; k <= n + m; k++) { | |
| 176 lb = MAX(0, k - m); | |
| 177 ub = MIN(k, n); | |
| 178 for (i = lb; i <= ub; i++) { | |
| 179 j = k - i; | |
| 180 w[i+j].y += cdTable[j][i] * z[j][i]; | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 return (w); | |
| 185 } | |
| 186 | |
| 187 | |
| 188 /* | |
| 189 * FindRoots : | |
| 190 * Given a 5th-degree equation in Bernstein-Bezier form, find | |
| 191 * all of the roots in the interval [0, 1]. Return the number | |
| 192 * of roots found. | |
| 193 */ | |
| 194 static int FindRoots(w, degree, t, depth) | |
| 195 Point2 *w; /* The control points */ | |
| 196 int degree; /* The degree of the polynomial */ | |
| 197 double *t; /* RETURN candidate t-values */ | |
| 198 int depth; /* The depth of the recursion */ | |
| 199 { | |
| 200 int i; | |
| 201 Point2 Left[W_DEGREE+1], /* New left and right */ | |
| 202 Right[W_DEGREE+1]; /* control polygons */ | |
| 203 int left_count, /* Solution count from */ | |
| 204 right_count; /* children */ | |
| 205 double left_t[W_DEGREE+1], /* Solutions from kids */ | |
| 206 right_t[W_DEGREE+1]; | |
| 207 | |
| 208 switch (CrossingCount(w, degree)) { | |
| 209 case 0 : { /* No solutions here */ | |
| 210 return 0; | |
| 211 } | |
| 212 case 1 : { /* Unique solution */ | |
| 213 /* Stop recursion when the tree is deep enough */ | |
| 214 /* if deep enough, return 1 solution at midpoint */ | |
| 215 if (depth >= MAXDEPTH) { | |
| 216 t[0] = (w[0].x + w[W_DEGREE].x) / 2.0; | |
| 217 return 1; | |
| 218 } | |
| 219 if (ControlPolygonFlatEnough(w, degree)) { | |
| 220 t[0] = ComputeXIntercept(w, degree); | |
| 221 return 1; | |
| 222 } | |
| 223 break; | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 /* Otherwise, solve recursively after */ | |
| 228 /* subdividing control polygon */ | |
| 229 Bezier(w, degree, 0.5, Left, Right); | |
| 230 left_count = FindRoots(Left, degree, left_t, depth+1); | |
| 231 right_count = FindRoots(Right, degree, right_t, depth+1); | |
| 232 | |
| 233 | |
| 234 /* Gather solutions together */ | |
| 235 for (i = 0; i < left_count; i++) { | |
| 236 t[i] = left_t[i]; | |
| 237 } | |
| 238 for (i = 0; i < right_count; i++) { | |
| 239 t[i+left_count] = right_t[i]; | |
| 240 } | |
| 241 | |
| 242 /* Send back total number of solutions */ | |
| 243 return (left_count+right_count); | |
| 244 } | |
| 245 | |
| 246 | |
| 247 /* | |
| 248 * CrossingCount : | |
| 249 * Count the number of times a Bezier control polygon | |
| 250 * crosses the 0-axis. This number is >= the number of roots. | |
| 251 * | |
| 252 */ | |
| 253 static int CrossingCount(V, degree) | |
| 254 Point2 *V; /* Control pts of Bezier curve */ | |
| 255 int degree; /* Degreee of Bezier curve */ | |
| 256 { | |
| 257 int i; | |
| 258 int n_crossings = 0; /* Number of zero-crossings */ | |
| 259 int sign, old_sign; /* Sign of coefficients */ | |
| 260 | |
| 261 sign = old_sign = SGN(V[0].y); | |
| 262 for (i = 1; i <= degree; i++) { | |
| 263 sign = SGN(V[i].y); | |
| 264 if (sign != old_sign) n_crossings++; | |
| 265 old_sign = sign; | |
| 266 } | |
| 267 return n_crossings; | |
| 268 } | |
| 269 | |
| 270 | |
| 271 | |
| 272 /* | |
| 273 * ControlPolygonFlatEnough : | |
| 274 * Check if the control polygon of a Bezier curve is flat enough | |
| 275 * for recursive subdivision to bottom out. | |
| 276 * | |
| 277 */ | |
| 278 static int ControlPolygonFlatEnough(V, degree) | |
| 279 Point2 *V; /* Control points */ | |
| 280 int degree; /* Degree of polynomial */ | |
| 281 { | |
| 282 int i; /* Index variable */ | |
| 283 double *distance; /* Distances from pts to line */ | |
| 284 double max_distance_above; /* maximum of these */ | |
| 285 double max_distance_below; | |
| 286 double error; /* Precision of root */ | |
| 287 double intercept_1, | |
| 288 intercept_2, | |
| 289 left_intercept, | |
| 290 right_intercept; | |
| 291 double a, b, c; /* Coefficients of implicit */ | |
| 292 /* eqn for line from V[0]-V[deg]*/ | |
| 293 | |
| 294 /* Find the perpendicular distance */ | |
| 295 /* from each interior control point to */ | |
| 296 /* line connecting V[0] and V[degree] */ | |
| 297 distance = (double *)malloc((unsigned)(degree + 1) * siz
eof(double)); | |
| 298 { | |
| 299 double abSquared; | |
| 300 | |
| 301 /* Derive the implicit equation for line connecting first *' | |
| 302 /* and last control points */ | |
| 303 a = V[0].y - V[degree].y; | |
| 304 b = V[degree].x - V[0].x; | |
| 305 c = V[0].x * V[degree].y - V[degree].x * V[0].y; | |
| 306 | |
| 307 abSquared = (a * a) + (b * b); | |
| 308 | |
| 309 for (i = 1; i < degree; i++) { | |
| 310 /* Compute distance from each of the points to that line */ | |
| 311 distance[i] = a * V[i].x + b * V[i].y + c; | |
| 312 if (distance[i] > 0.0) { | |
| 313 distance[i] = (distance[i] * distance[i]) / abSquared; | |
| 314 } | |
| 315 if (distance[i] < 0.0) { | |
| 316 distance[i] = -((distance[i] * distance[i]) /
abSquared); | |
| 317 } | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 | |
| 322 /* Find the largest distance */ | |
| 323 max_distance_above = 0.0; | |
| 324 max_distance_below = 0.0; | |
| 325 for (i = 1; i < degree; i++) { | |
| 326 if (distance[i] < 0.0) { | |
| 327 max_distance_below = MIN(max_distance_below, distance[i]); | |
| 328 }; | |
| 329 if (distance[i] > 0.0) { | |
| 330 max_distance_above = MAX(max_distance_above, distance[i]); | |
| 331 } | |
| 332 } | |
| 333 free((char *)distance); | |
| 334 | |
| 335 { | |
| 336 double det, dInv; | |
| 337 double a1, b1, c1, a2, b2, c2; | |
| 338 | |
| 339 /* Implicit equation for zero line */ | |
| 340 a1 = 0.0; | |
| 341 b1 = 1.0; | |
| 342 c1 = 0.0; | |
| 343 | |
| 344 /* Implicit equation for "above" line */ | |
| 345 a2 = a; | |
| 346 b2 = b; | |
| 347 c2 = c + max_distance_above; | |
| 348 | |
| 349 det = a1 * b2 - a2 * b1; | |
| 350 dInv = 1.0/det; | |
| 351 | |
| 352 intercept_1 = (b1 * c2 - b2 * c1) * dInv; | |
| 353 | |
| 354 /* Implicit equation for "below" line */ | |
| 355 a2 = a; | |
| 356 b2 = b; | |
| 357 c2 = c + max_distance_below; | |
| 358 | |
| 359 det = a1 * b2 - a2 * b1; | |
| 360 dInv = 1.0/det; | |
| 361 | |
| 362 intercept_2 = (b1 * c2 - b2 * c1) * dInv; | |
| 363 } | |
| 364 | |
| 365 /* Compute intercepts of bounding box */ | |
| 366 left_intercept = MIN(intercept_1, intercept_2); | |
| 367 right_intercept = MAX(intercept_1, intercept_2); | |
| 368 | |
| 369 error = 0.5 * (right_intercept-left_intercept); | |
| 370 if (error < EPSILON) { | |
| 371 return 1; | |
| 372 } | |
| 373 else { | |
| 374 return 0; | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 | |
| 379 | |
| 380 /* | |
| 381 * ComputeXIntercept : | |
| 382 * Compute intersection of chord from first control point to last | |
| 383 * with 0-axis. | |
| 384 * | |
| 385 */ | |
| 386 /* NOTE: "T" and "Y" do not have to be computed, and there are many useless | |
| 387 * operations in the following (e.g. "0.0 - 0.0"). | |
| 388 */ | |
| 389 static double ComputeXIntercept(V, degree) | |
| 390 Point2 *V; /* Control points */ | |
| 391 int degree; /* Degree of curve */ | |
| 392 { | |
| 393 double XLK, YLK, XNM, YNM, XMK, YMK; | |
| 394 double det, detInv; | |
| 395 double S, T; | |
| 396 double X, Y; | |
| 397 | |
| 398 XLK = 1.0 - 0.0; | |
| 399 YLK = 0.0 - 0.0; | |
| 400 XNM = V[degree].x - V[0].x; | |
| 401 YNM = V[degree].y - V[0].y; | |
| 402 XMK = V[0].x - 0.0; | |
| 403 YMK = V[0].y - 0.0; | |
| 404 | |
| 405 det = XNM*YLK - YNM*XLK; | |
| 406 detInv = 1.0/det; | |
| 407 | |
| 408 S = (XNM*YMK - YNM*XMK) * detInv; | |
| 409 /* T = (XLK*YMK - YLK*XMK) * detInv; */ | |
| 410 | |
| 411 X = 0.0 + XLK * S; | |
| 412 /* Y = 0.0 + YLK * S; */ | |
| 413 | |
| 414 return X; | |
| 415 } | |
| 416 | |
| 417 | |
| 418 /* | |
| 419 * Bezier : | |
| 420 * Evaluate a Bezier curve at a particular parameter value | |
| 421 * Fill in control points for resulting sub-curves if "Left" and | |
| 422 * "Right" are non-null. | |
| 423 * | |
| 424 */ | |
| 425 static Point2 Bezier(V, degree, t, Left, Right) | |
| 426 int degree; /* Degree of bezier curve */ | |
| 427 Point2 *V; /* Control pts */ | |
| 428 double t; /* Parameter value */ | |
| 429 Point2 *Left; /* RETURN left half ctl pts */ | |
| 430 Point2 *Right; /* RETURN right half ctl pts */ | |
| 431 { | |
| 432 int i, j; /* Index variables */ | |
| 433 Point2 Vtemp[W_DEGREE+1][W_DEGREE+1]; | |
| 434 | |
| 435 | |
| 436 /* Copy control points */ | |
| 437 for (j =0; j <= degree; j++) { | |
| 438 Vtemp[0][j] = V[j]; | |
| 439 } | |
| 440 | |
| 441 /* Triangle computation */ | |
| 442 for (i = 1; i <= degree; i++) { | |
| 443 for (j =0 ; j <= degree - i; j++) { | |
| 444 Vtemp[i][j].x = | |
| 445 (1.0 - t) * Vtemp[i-1][j].x + t * Vtemp[i-1][j+1].x; | |
| 446 Vtemp[i][j].y = | |
| 447 (1.0 - t) * Vtemp[i-1][j].y + t * Vtemp[i-1][j+1].y; | |
| 448 } | |
| 449 } | |
| 450 | |
| 451 if (Left != NULL) { | |
| 452 for (j = 0; j <= degree; j++) { | |
| 453 Left[j] = Vtemp[j][0]; | |
| 454 } | |
| 455 } | |
| 456 if (Right != NULL) { | |
| 457 for (j = 0; j <= degree; j++) { | |
| 458 Right[j] = Vtemp[degree-j][j]; | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 return (Vtemp[degree][0]); | |
| 463 } | |
| 464 | |
| 465 static Vector2 V2ScaleII(v, s) | |
| 466 Vector2 *v; | |
| 467 double s; | |
| 468 { | |
| 469 Vector2 result; | |
| 470 | |
| 471 result.x = v->x * s; result.y = v->y * s; | |
| 472 return (result); | |
| 473 } | |
| OLD | NEW |