00001 // $Id: pathutil.cpp 1282 2006-06-09 09:46:49Z alex $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 // Implementation file for functions which operate on sub path elements 00099 // ie lines and curves 00100 00101 /* 00102 */ 00103 00104 #include "camtypes.h" 00105 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 #include "pathutil.h" 00107 #include <math.h> 00108 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 #include "macros.h" 00110 00111 #define EPSILON (ldexp(1.0,-MAXDEPTH-1)) // Flatness control value 00112 #define DEGREE 3 // Cubic Bezier curve 00113 #define W_DEGREE 5 // Degree of eqn to find roots of 00114 00115 const INT32 MAXDEPTH = 64; // Maximum depth for recursion 00116 00117 00118 00119 /******************************************************************************************** 00120 00121 > DocCoord RampItem::PointOnSemiCircle(const DocCoord& centre, const DocCoord& radialp, double t) 00122 00123 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00124 Created: 05/03/97 00125 Inputs: centre = the centre point of the circle 00126 radialp = another point (anywhere, but assumed to be somewhere on the radius of 00127 the circle. 00128 t = a parameter (0..1) defining the parametric locus about the centre from 00129 0 to pi radians. 00130 Outputs: - 00131 Returns: A new doc coord which is a point on the circles perimeter which corresponds to 00132 the parameter t. 00133 At t==1 the function will evaluate to radialp 00134 At t==0 the function will evaluate to centre-(radialp-centre) 00135 Purpose: Find a point on the circumference of a semicircle. 00136 The semicircle is specified by two points, it's centre and a point on the 00137 circumference. If c=0,0 and p=(1,0) then the semicircle exists in the y positive 00138 half of the plane from sweeping from (-1,0) at t==0 to (1.0) at t==1.0 00139 Errors: None. 00140 00141 ********************************************************************************************/ 00142 00143 DocCoord PathUtil::PointOnSemiCircle(const DocCoord& centre, const DocCoord& radialp, double t) 00144 { 00145 double X = (double)(radialp.x - centre.x); 00146 double Y = (double)(radialp.y - centre.y); 00147 double p = t * XS_PI; 00148 00149 // spin clockwise. 00150 double s = sin(p); 00151 double c = -cos(p); 00152 00153 DocCoord r; 00154 00155 // circular rounding maybe? 00156 r.x = (INT32)(X*c - Y*s) + centre.x; 00157 r.y = (INT32)(X*s + Y*c) + centre.y; 00158 00159 return r; 00160 } 00161 00162 00163 00164 /******************************************************************************************** 00165 00166 > double PathUtil::SqrDistanceToSemiCircle(const DocCoord* plist, 00167 const DocCoord& p1, 00168 double* param ) 00169 00170 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00171 Created: 22/08/94 00172 Inputs: plist = a pointer to two doc coordinates describing a half circle 00173 p1 = the point to find the squared distance to 00174 Outputs: param = the parameter at which the closest point exists 00175 Returns: a double, the distance a given point is away from a line element 00176 Purpose: Calculates the distance p1 is away from a semi circle segment. 00177 plist[0] describes the centre of the semi circle 00178 plist[1] describes a point on the circumference of the circle. 00179 For instance say c the centre is (0,0) and cp the circumference point is 00180 (1,0) then the semicircle exists in the positive y half of the place and 00181 sweeps from (-1,0) to (1,0). Parameter space is 00182 param==0 at (-1,0) 00183 param==1 at ( 1,0) 00184 param==0.5 at ( 0,1) 00185 00186 ********************************************************************************************/ 00187 00188 double PathUtil::SqrDistanceToSemiCircle(const DocCoord* plist, 00189 const DocCoord& p, 00190 double *param) 00191 { 00192 double ex,ey,px,py,Px,Py,l,t; 00193 00194 DocCoord c = plist[0]; // the centre point 00195 DocCoord e = plist[1]; // the end point 00196 DocCoord q; 00197 00198 // translate to origin 00199 ex = e.x-c.x; 00200 ey = e.y-c.y; 00201 px = p.x-c.x; 00202 py = p.y-c.y; 00203 00204 // build a (ex,ey) based transform 00205 l = sqrt(ex*ex+ey*ey); 00206 if (l!=0) 00207 { 00208 ex=ex/l; 00209 ey=ex/l; 00210 } 00211 00212 // transform the click point to local canonical 00213 Px = px*ex+py*ey; 00214 Py = py*ex-px*ey; 00215 00216 // are we below the half circle 00217 if (Py>0) 00218 { 00219 // no then calculate the dot product angle 00220 l = sqrt(Px*Px+Py*Py); 00221 if (l!=0) 00222 Px=Px/l; 00223 t = (XS_PI - acos(Px))/XS_PI; 00224 q = PathUtil::PointOnSemiCircle(c,p,t); 00225 } 00226 else 00227 { 00228 // yes then decide start point or endpoint 00229 if (Px>0) 00230 { 00231 t=1.0; 00232 q=e; 00233 } 00234 else 00235 { 00236 t=0.0; 00237 q=e-(c-e); 00238 } 00239 } 00240 00241 // set the output parameter 00242 (*param)=t; 00243 // calculate the squared distance 00244 return PathUtil::SqrDistance(p,q); 00245 } 00246 00247 00248 00249 00250 /******************************************************************************************** 00251 00252 > double PathUtil::SqrDistance(const Coord& Coord) 00253 00254 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00255 Created: 20/8/94 00256 Inputs: two coordinates 00257 Outputs: None 00258 Returns: the squared distance between the two coordinates 00259 Purpose: Accurate squared distance function. 00260 Errors: None. 00261 00262 ********************************************************************************************/ 00263 00264 double PathUtil::SqrDistance(const DocCoord& p1, const DocCoord& p2) 00265 { 00266 double dx = (double) (p2.x - p1.x); 00267 double dy = (double) (p2.y - p1.y); 00268 00269 return ((dx*dx)+(dy*dy)); 00270 00271 } 00272 00273 00274 /******************************************************************************************** 00275 00276 > BOOL PathUtil::SplitLine(const double t, 00277 const DocCoord* plist, 00278 UINT32* NumElements, 00279 PathVerb* OutVerbs, 00280 DocCoord* OutCoords) 00281 00282 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00283 Created: 22/08/94 00284 Inputs: t = parameter at which to split the line 00285 plist = pointer to 2 doc coords describing the line 00286 Outputs: NumElements = number of elements generated after split 00287 OutVerbs = pointer to verbs list to output data to 00288 OutCoords = pointer to coords list to output data to 00289 Returns: True if the line can be split, False if not. 00290 Purpose: Splits a line element into two lines, returning the lists of new 00291 coord points and verbs. 00292 00293 ********************************************************************************************/ 00294 /* 00295 Technical notes: 00296 00297 Two lineto coordinates will be returned as follows 00298 00299 Verbs Coords 00300 Lineto OA 00301 Lineto OB = IA 00302 00303 and can be inserted as follows 00304 00305 Input Output 00306 Verbs Coords Verbs Coords 00307 .... IA .... IA 00308 Lineto IB Lineto OA 00309 ... XX Lineto OB 00310 ... XX 00311 00312 OB is returned in the output list in order to allow removal of the split element and 00313 complete replacement with the generated elements 00314 00315 ********************************************************************************************/ 00316 00317 BOOL PathUtil::SplitLine(const double t, 00318 const DocCoord* plist, 00319 UINT32* NumElements, 00320 PathVerb* Verbs, 00321 DocCoord* Coords) 00322 { 00323 // check t is in range for this split. 00324 00325 if (t < SPLIT_EPSILON) return FALSE; 00326 if (t > (1.0 - SPLIT_EPSILON)) return FALSE; 00327 00328 // read the lines start and end points 00329 00330 INT32 x0,y0,x1,y1; 00331 00332 x0 = plist[0].x; 00333 y0 = plist[0].y; 00334 x1 = plist[1].x; 00335 y1 = plist[1].y; 00336 00337 // fill in the output block details 00338 00339 Verbs[0] = PT_LINETO; 00340 Coords[0].x = x0 + (INT32)(t*(x1-x0)+0.5); 00341 Coords[0].y = y0 + (INT32)(t*(y1-y0)+0.5); 00342 00343 Verbs[1] = PT_LINETO; 00344 Coords[1].x = x1; 00345 Coords[1].y = y1; 00346 00347 *NumElements = 2; 00348 00349 return TRUE; 00350 00351 } 00352 00353 00354 /******************************************************************************************** 00355 00356 > BOOL PathUtil::SplitCurve(const double t, 00357 const DocCoord* plist, 00358 UINT32* NumElements, 00359 PathVerb* OutVerbs, 00360 DocCoord* OutCoords) 00361 00362 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00363 Created: 22/08/94 00364 Inputs: t = parameter at which to split the curve 00365 plist = pointer to 4 curve control points to split 00366 Outputs: NumElements = number of elements generated 00367 OutVerbs = output verbs list 00368 OutCoords = output coords list 00369 Returns: True if the curve could be split, False if not. 00370 Purpose: Splits a curve element into two curves, returning the lists of new 00371 control points and verbs. 00372 00373 ********************************************************************************************/ 00374 /* 00375 00376 Technical notes: 00377 00378 Six bezier control points will be returned as follows: 00379 00380 Input Output 00381 IA OA 00382 IB OB 00383 IC OC 00384 ID OD 00385 OE 00386 OF = ID 00387 00388 The resultant curve control lists should be encorperated by the caller 00389 as follows: 00390 00391 Input Output 00392 Verbs Coords Verbs Coords 00393 .... IA .... IA 00394 Bez IB Bez OA 00395 Bez IC Bez OB 00396 Bez ID Bez OC 00397 ... XX Bez OD 00398 ... XX Bez OE 00399 Bez OF 00400 ... XX 00401 ... XX 00402 00403 ********************************************************************************************/ 00404 00405 00406 BOOL PathUtil::SplitCurve(const double t, 00407 const DocCoord* plist, 00408 UINT32* NumElements, 00409 PathVerb* OutVerbs, 00410 DocCoord* OutCoords) 00411 { 00412 // check t is in range for this split. 00413 00414 if (t < SPLIT_EPSILON) return FALSE; 00415 if (t > (1.0 - SPLIT_EPSILON)) return FALSE; 00416 00417 // fill in the path verb array, these are all curveto's 00418 00419 for (INT32 i=0; i<6; i++) 00420 OutVerbs[i] = PT_BEZIERTO; 00421 00422 // read the four curve control points 00423 00424 INT32 x0,y0,x1,y1,x2,y2,x3,y3; 00425 00426 x0 = plist[0].x; // note this should be move,line,curve 00427 y0 = plist[0].y; 00428 x1 = plist[1].x; // these should all be curveto's 00429 y1 = plist[1].y; 00430 x2 = plist[2].x; 00431 y2 = plist[2].y; 00432 x3 = plist[3].x; 00433 y3 = plist[3].y; 00434 00435 // now calculate six new coordinates from the given 4 00436 00437 double tx,ty; 00438 00439 tx = x1+t*(x2-x1); 00440 ty = y1+t*(y2-y1); 00441 00442 double Lx1,Ly1,Lx2,Ly2,Rx0,Ry0,Rx1,Ry1,Rx2,Ry2; 00443 00444 Lx1 = x0+t*(x1-x0); 00445 Ly1 = y0+t*(y1-y0); 00446 Rx2 = x2+t*(x3-x2); 00447 Ry2 = y2+t*(y3-y2); 00448 Lx2 = Lx1+t*(tx-Lx1); 00449 Ly2 = Ly1+t*(ty-Ly1); 00450 Rx1 = tx+t*(Rx2-tx); 00451 Ry1 = ty+t*(Ry2-ty); 00452 Rx0 = Lx2+t*(Rx1-Lx2); 00453 Ry0 = Ly2+t*(Ry1-Ly2); 00454 00455 // set the return values 00456 00457 OutCoords[0].x = (INT32)(Lx1+0.5); 00458 OutCoords[0].y = (INT32)(Ly1+0.5); 00459 OutCoords[1].x = (INT32)(Lx2+0.5); 00460 OutCoords[1].y = (INT32)(Ly2+0.5); 00461 OutCoords[2].x = (INT32)(Rx0+0.5); 00462 OutCoords[2].y = (INT32)(Ry0+0.5); 00463 OutCoords[3].x = (INT32)(Rx1+0.5); 00464 OutCoords[3].y = (INT32)(Ry1+0.5); 00465 OutCoords[4].x = (INT32)(Rx2+0.5); 00466 OutCoords[4].y = (INT32)(Ry2+0.5); 00467 OutCoords[5].x = x3; 00468 OutCoords[5].y = y3; 00469 00470 // Set the output number of elements. 00471 00472 *NumElements = 6; 00473 00474 return TRUE; 00475 } 00476 00477 00478 00479 /******************************************************************************************** 00480 00481 > void PathUtil::GetCurveCoefs(const DocCoord* coords, PtCoefs* coefs ) 00482 00483 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00484 Created: 22/08/94 00485 Inputs: coords = a pointer to 4 control point doccoords defining a curve 00486 Outputs: coefs = a set of curve coefficients. 00487 Returns: None 00488 Purpose: Converts a curve from Bezier form to Canonical form 00489 00490 ********************************************************************************************/ 00491 00492 void PathUtil::GetCurveCoefs(const DocCoord* coords, PtCoefs* coefs ) 00493 { 00494 // Read the curve coordinates. 00495 00496 INT32 X0,Y0,X1,Y1,X2,Y2,X3,Y3; 00497 00498 X0 = coords->x; 00499 Y0 = coords->y; 00500 coords++; 00501 X1 = coords->x; 00502 Y1 = coords->y; 00503 coords++; 00504 X2 = coords->x; 00505 Y2 = coords->y; 00506 coords++; 00507 X3 = coords->x; 00508 Y3 = coords->y; 00509 00510 // Calculate the curve coefficients. 00511 00512 coefs->ax = X3-X0+3*(X1-X2); 00513 coefs->ay = Y3-Y0+3*(Y1-Y2); 00514 coefs->bx = 3*(X2-2*X1+X0); 00515 coefs->by = 3*(Y2-2*Y1+Y0); 00516 coefs->cx = 3*(X1-X0); 00517 coefs->cy = 3*(Y1-Y0); 00518 00519 } 00520 00521 00522 /******************************************************************************************** 00523 00524 > double PathUtil::SqrDistanceToLine(const DocCoord* plist, 00525 const DocCoord& p1, 00526 double* t ) 00527 00528 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00529 Created: 22/08/94 00530 Inputs: plist = a pointer to two doc coordinates describing a line 00531 p1 = the point to find the squared distance to 00532 Outputs: t = the parameter at which the closest point exists 00533 Returns: a double, the distance a given point is away from a line element 00534 Purpose: Calculates the distance p1 is away from a line segment. The perpendicular 00535 distance is returned only when p1 is within the volume created by sweeping 00536 the line in the orthoganal direction. Otherwise the distance to the 00537 closest end point is returned. 00538 00539 ********************************************************************************************/ 00540 00541 double PathUtil::SqrDistanceToLine(const DocCoord* plist, 00542 const DocCoord& p1, 00543 double* t ) 00544 00545 { 00546 00547 // get hold of the lines end point coordinates 00548 00549 INT32 x0,y0,x1,y1; 00550 00551 x0 = plist[0].x; 00552 y0 = plist[0].y; 00553 x1 = plist[1].x; 00554 y1 = plist[1].y; 00555 00556 INT32 pdx = (x1-x0); 00557 INT32 pdy = (y1-y0); 00558 00559 // calculate the parameter at which the closest point exists 00560 // on an infinite line passing through (x0,y0),(x1,y1) 00561 // t=numdot/dendot. 00562 00563 double numdot,dendot; 00564 00565 numdot = (double)(p1.x - plist[0].x)*pdx + (double)(p1.y-plist[0].y)*pdy; 00566 dendot = (double)pdx*pdx + (double)pdy*pdy; 00567 00568 // if t is less then zero then the closest point lies behind 00569 // the start point of the vector, hence return the squared 00570 // distance to the startpoint 00571 00572 if (numdot<=0 || dendot<=0) 00573 { 00574 *t = 0.0; 00575 return (SqrDistance(p1, plist[0])); 00576 } 00577 00578 // if t is greater than or equal to 1 then the closest point 00579 // lies beyond the line segment, ie on the projected line 00580 // beyond (x1,y1). So return the squared distance to that end 00581 // point. 00582 00583 if (dendot<=numdot) 00584 { 00585 *t = 1.0; 00586 return (SqrDistance(p1,plist[1])); 00587 } 00588 00589 // ok the closest point lies somewhere inside the line segment 00590 // (x0,y0),(x1,y1) so return its parameter and the squared 00591 // distance to it. 00592 00593 *t = numdot/dendot; 00594 00595 double c = (double)pdy*p1.x - (double)pdx*p1.y; 00596 double d = (double)y1*x0 - (double)y0*x1; 00597 double e = (c-d)*(c-d); 00598 00599 return fabs(e/dendot); 00600 00601 } 00602 00603 00604 00605 /******************************************************************************************** 00606 00607 > DocCoord PathUtil::PointOnLine(const double t, const DocCoord* startpt) 00608 00609 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00610 Created: 22/08/94 00611 Inputs: t = parameter at which to evaluate point 00612 ptlist = pointer to two doc coords describing start and end of the line 00613 Outputs: 00614 Returns: DocCoord, an evaluation of a point on the specified line 00615 Purpose: Given a parameter t go and evaluate the point on the described line segment 00616 If t is out of range, ie t>1 or t<0 the end points of the line will be 00617 returned. ie the routine will not evaluate unbounded points. 00618 00619 ********************************************************************************************/ 00620 00621 DocCoord PathUtil::PointOnLine(const double t, const DocCoord* ptlist) 00622 { 00623 INT32 x0,y0,x1,y1; 00624 00625 x0 = ptlist[0].x; 00626 y0 = ptlist[0].y; 00627 x1 = ptlist[1].x; 00628 y1 = ptlist[1].y; 00629 00630 DocCoord d; 00631 00632 if (t<=0.0) 00633 { 00634 d.x = x0; 00635 d.y = y0; 00636 return d; 00637 } 00638 00639 if (t>=1.0) 00640 { 00641 d.x = x1; 00642 d.y = y1; 00643 return d; 00644 } 00645 00646 d.x = x0 + (INT32)(t*(x1-x0)+0.5); 00647 d.y = y0 + (INT32)(t*(y1-y0)+0.5); 00648 00649 return d; 00650 } 00651 00652 00653 00654 /******************************************************************************************** 00655 00656 > double PathUtil::SqrDistanceToCurve(const UINT32 step, 00657 const DocCoord* plist, 00658 const DocCoord& p1, 00659 double* t) 00660 00661 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00662 Created: 22/08/94 00663 Inputs: step = value between 2 and 256 dictates the corseness of stepping 00664 internally across the curve. 00665 plist = pointer to 4 doccoord control points describing the curve 00666 p1 = coordinate reference used to find closest coordinate to. 00667 Outputs: t holds the parameter value of the closest point on the curve, 0<=t<=1 00668 Returns: a double being the distance of the closest point on the curve. 00669 Purpose: This function returns the distance to the closest point on the curve and 00670 the parameter of this point 00671 00672 00673 ********************************************************************************************/ 00674 /* 00675 00676 Technical notes: 00677 00678 If Qx(t) = X0*(1-t)^3 + 3*X1*t*(1-t)^2 + 3*X2*t^2*(1-t) + X3*t^3 00679 and Qy(t) is similar 00680 00681 Then Qx(t) = (X0 - 3*X0*t + 3*X0*t^2 - X0*t^3) + 00682 (3*X1*t - 6*X1*t^2 + 3*X1*t^3 ) + 00683 (3*X2*t^2 - 3*X2*t^3) + 00684 X3*t^3 00685 00686 Collecting terms in t 00687 00688 Qx(t) = t^3*(X3-X0+3*(X1-X2)) + 00689 t^2*(3*X0-6*X1+3*X2) + 00690 t *(3*X1-3*X0) + 00691 X0 00692 00693 For forward differencing 00694 00695 Let Ax = X3-X0+3*(X1-X2) 00696 Bx = 3*(X0-2*X1+X2) 00697 Cx = 3*(X1-X0) 00698 Dx = X0 00699 00700 Then Q(t) = A*t^3 + B*t^2 + C*t + D 00701 = t*(C+t*(B+t*A)) + D 00702 00703 Ok, having got this calculate the terms involved in 00704 00705 D(t) = Q(t+delta)-Q(t) 00706 00707 The terms involved in D(t) will give forward differences which can be 00708 used to walk the curve Q(t) 00709 00710 ********************************************************************************************/ 00711 00712 /* double PathUtil::SqrDistanceToCurve(const UINT32 step, 00713 const DocCoord* plist, 00714 const DocCoord& p1, 00715 double* t ) 00716 00717 { 00718 00719 00720 ENSURE(step>1 && step<257,"curve step is out of range"); 00721 00722 // Calculate the curve coefficients for this curve element 00723 00724 PtCoefs CurveC; 00725 PathUtil::GetCurveCoefs(plist, &CurveC); 00726 00727 double t0; 00728 double t1; 00729 double dist = PathUtil::CurveClosestRange(step, &CurveC, p1, plist[0].x, plist[0].y, t, &t0, &t1); 00730 00731 // TRACE( _T("T0 = %f\n"),t0); 00732 // TRACE( _T("T = %f\n"),*t); 00733 // TRACE( _T("T1 = %f\n"),t1); 00734 00735 if (t0 >= t1) return dist; 00736 if ((*t==t0) || (*t==t1)) return dist; 00737 if ((*t==0.0) || (*t==1.0)) return dist; 00738 00739 INT32 x0,y0,x1,y1,x2,y2,x3,y3; 00740 00741 x0 = plist[0].x; // note this should be move,line,curve 00742 y0 = plist[0].y; 00743 x1 = plist[1].x; // these should all be curveto's 00744 y1 = plist[1].y; 00745 x2 = plist[2].x; 00746 y2 = plist[2].y; 00747 x3 = plist[3].x; 00748 y3 = plist[3].y; 00749 00750 // now calculate the coordinates of a bezier curve 00751 // lying between t0 and t1 00752 00753 double tx,ty; 00754 00755 tx = x1+t0*(x2-x1); 00756 ty = y1+t0*(y2-y1); 00757 00758 double Qx0,Qy0,Qx1,Qy1; 00759 double Rx1,Ry1,Rx2,Ry2,Lx1,Ly1,Lx2,Ly2; 00760 00761 Lx1 = x0+t0*(x1-x0); 00762 Ly1 = y0+t0*(y1-y0); 00763 Rx2 = x2+t0*(x3-x2); 00764 Ry2 = y2+t0*(y3-y2); 00765 Lx2 = Lx1+t0*(tx-Lx1); 00766 Ly2 = Ly1+t0*(ty-Ly1); 00767 Qx1 = tx+t0*(Rx2-tx); 00768 Qy1 = ty+t0*(Ry2-ty); 00769 Qx0 = Lx2+t0*(Qx1-Lx2); 00770 Qy0 = Ly2+t0*(Qy1-Ly2); 00771 00772 tx = x1+t1*(x2-x1); 00773 ty = y1+t1*(y2-y1); 00774 00775 double Qx2,Qy2,Qx3,Qy3; 00776 00777 Lx1 = x0+t1*(x1-x0); 00778 Ly1 = y0+t1*(y1-y0); 00779 Rx1 = x2+t1*(x3-x2); 00780 Ry1 = y2+t1*(y3-y2); 00781 Rx2 = tx+t1*(Rx1-tx); 00782 Ry2 = ty+t1*(Ry1-ty); 00783 Qx2 = Lx1+t1*(tx-Lx1); 00784 Qy2 = Ly1+t1*(ty-Ly1); 00785 Qx3 = Qx2+t1*(Rx2-Qx2); 00786 Qy3 = Qy2+t1*(Ry2-Qy2); 00787 00788 // now home in a little bit closer 00789 00790 CurveC.ax = Qx3-Qx0+3*(Qx1-Qx2); 00791 CurveC.ay = Qy3-Qy0+3*(Qy1-Qy2); 00792 CurveC.bx = 3*(Qx2-2*Qx1+Qx0); 00793 CurveC.by = 3*(Qy2-2*Qy1+Qy0); 00794 CurveC.cx = 3*(Qx1-Qx0); 00795 CurveC.cy = 3*(Qy1-Qy0); 00796 00797 double t2; 00798 double t3; 00799 dist = PathUtil::CurveClosestRange(step, &CurveC, p1, Qx0, Qy0, t, &t2, &t3); 00800 00801 *t = (1.0-(*t))*t0 + (*t)*t1; 00802 00803 // TRACE( _T("T' = %f\n\n"),*t); 00804 00805 return dist; 00806 } 00807 00808 00809 double PathUtil::CurveClosestRange(const UINT32 step, 00810 PtCoefs* CurveC, 00811 const DocCoord& p1, 00812 const double dx, 00813 const double dy, 00814 double* t, 00815 double* t0, 00816 double* t1) 00817 00818 { 00819 00820 // Calculate the step rate across the curve given the users 00821 // step value 00822 00823 double Delta=(double) 1/step; 00824 00825 // Calculate the forward difference factors for curve stepping 00826 00827 double X0,Y0,X1,Y1,X2,Y2,X3,Y3; 00828 00829 X3 = 6*Delta*Delta*Delta*CurveC->ax; 00830 Y3 = 6*Delta*Delta*Delta*CurveC->ay; 00831 X2 = 2*Delta*Delta*CurveC->bx+X3; 00832 Y2 = 2*Delta*Delta*CurveC->by+Y3; 00833 00834 // Evaluate the first differenced point on the curve 00835 00836 X1 = Delta*(CurveC->cx+Delta*(CurveC->bx+Delta*CurveC->ax)); 00837 Y1 = Delta*(CurveC->cy+Delta*(CurveC->by+Delta*CurveC->ay)); 00838 X0 = dx; 00839 Y0 = dy; 00840 00841 // find the distance from the first point on the curve 00842 00843 double Cdist=(p1.x - dx)*(p1.x - dx) + (p1.y - dy)*(p1.y - dy); 00844 double Fdist; 00845 INT32 Cpt=0; 00846 00847 for (UINT32 i=1; i<=step; i++) 00848 { 00849 X0+=X1; 00850 Y0+=Y1; 00851 X1+=X2; 00852 Y1+=Y2; 00853 X2+=X3; 00854 Y2+=Y3; 00855 Fdist = (X0-p1.x)*(X0-p1.x); // Is this point closer? 00856 if (Cdist>Fdist) 00857 { 00858 Fdist += (Y0-p1.y)*(Y0-p1.y); 00859 if (Cdist>Fdist) 00860 { 00861 Cdist=Fdist; 00862 Cpt=i; 00863 } 00864 } 00865 } 00866 00867 INT32 tl = Cpt-2; 00868 UINT32 tr = Cpt+2; 00869 if (tl<0) tl=0; 00870 if (tr>step) tr=step; 00871 00872 *t1 = (double) tr/step; 00873 *t0 = (double) tl/step; 00874 *t = (double) Cpt/step; // set the point parameter 00875 00876 return Cdist; // return the point distance 00877 } 00878 00879 */ 00880 00881 00882 /******************************************************************************************** 00883 00884 > DocCoord PathUtil::PointOnCurve(double t, const DocCoord* plist) 00885 00886 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00887 Created: 22/08/94 00888 Inputs: t = parameter to evaluate curve at 00889 plist = pointer to list of 4 curve control points. 00890 Outputs: 00891 Returns: A DocCoord describing the point on the curve 00892 Purpose: Evaluate a bezier curve, given a pointer to a set of control points and 00893 a parameter value. 00894 00895 ********************************************************************************************/ 00896 00897 DocCoord PathUtil::PointOnCurve(double t, const DocCoord* plist) 00898 { 00899 PtCoefs p; 00900 DocCoord c; 00901 00902 PathUtil::GetCurveCoefs(plist, &p); // calculate coefs block 00903 00904 if (t<0) t=0; // clamp range of parameter 00905 if (t>1) t=1; 00906 00907 // Eval bezier at specified parameter 00908 00909 double dx = t*(p.cx+t*(p.bx+t*p.ax)); 00910 double dy = t*(p.cy+t*(p.by+t*p.ay)); 00911 00912 c.x = plist->x + (INT32)(dx+0.5); 00913 c.y = plist->y + (INT32)(dy+0.5); 00914 00915 return c; 00916 } 00917 00918 00919 /******************************************************************************************** 00920 Quick local vector functions to calculate various properties of vectors 00921 ********************************************************************************************/ 00922 00923 Point2 V2ScaleII( Point2 *v, double s) 00924 { 00925 Point2 result; 00926 00927 result.x = v->x * s; 00928 result.y = v->y * s; 00929 00930 return (result); 00931 } 00932 00933 00934 double V2SquaredLength(Point2* a) 00935 { 00936 return( a->x*a->x + a->y*a->y ); 00937 } 00938 00939 00940 Point2 *V2Sub(Point2* a, Point2* b, Point2* c) 00941 { 00942 c->x = a->x - b->x; 00943 c->y = a->y - b->y; 00944 return(c); 00945 } 00946 00947 00948 double V2Dot(Point2* a, Point2* b) 00949 { 00950 return (a->x*b->x + a->y*b->y); 00951 } 00952 00953 00954 00955 /******************************************************************************************** 00956 00957 Point2 Bezier( Point2 *V, INT32 degree, double t, Point2 *Left, Point2 *Right) 00958 00959 Author: Unattributed (Xara Group Ltd) <camelotdev@xara.com> 00960 Created: 22/08/94 00961 Inputs: V = Control points of cubic Bezier 00962 degree = The degree of the polynomial 00963 t = Parameter value 00964 Outputs: Left = RETURN left half ctl pts (if NULL return none) 00965 Right = RETURN right half ctl pts (if NULL return none) 00966 Returns: Q(t), a point on the curve 00967 Purpose: Evaluate a Bezier curve at a particular parameter value 00968 Fill in control points for resulting sub-curves if "Left" and 00969 "Right" are non-null. 00970 00971 ********************************************************************************************/ 00972 00973 Point2 Bezier( Point2 *V, INT32 degree, double t, Point2 *Left, Point2 *Right) 00974 { 00975 INT32 i, j; // Index variables 00976 Point2 Vtemp[W_DEGREE+1][W_DEGREE+1]; 00977 00978 // Copy control points 00979 00980 for (j =0; j <= degree; j++) 00981 { 00982 Vtemp[0][j] = V[j]; 00983 } 00984 00985 // Triangle computation 00986 00987 for (i = 1; i <= degree; i++) 00988 { 00989 for (j =0 ; j <= degree - i; j++) 00990 { 00991 Vtemp[i][j].x = (1.0 - t) * Vtemp[i-1][j].x + t * Vtemp[i-1][j+1].x; 00992 Vtemp[i][j].y = (1.0 - t) * Vtemp[i-1][j].y + t * Vtemp[i-1][j+1].y; 00993 } 00994 } 00995 00996 if (Left != NULL) 00997 { 00998 for (j = 0; j <= degree; j++) 00999 { 01000 Left[j] = Vtemp[j][0]; 01001 } 01002 } 01003 if (Right != NULL) 01004 { 01005 for (j = 0; j <= degree; j++) 01006 { 01007 Right[j] = Vtemp[degree-j][j]; 01008 } 01009 } 01010 01011 return (Vtemp[degree][0]); 01012 } 01013 01014 01015 01016 /******************************************************************************************** 01017 01018 double ComputeXIntercept(Point2 *V, INT32 degree) 01019 01020 Author: 01021 Created: 22/08/94 01022 Inputs: V = Control points of cubic Bezier 01023 degree = The degree of the polynomial 01024 Outputs: 01025 Returns: 01026 Purpose: Compute intersection of chord from first control point to last 01027 with 0-axis. 01028 01029 ********************************************************************************************/ 01030 01031 double ComputeXIntercept(Point2 *V, INT32 degree) 01032 { 01033 double XLK, YLK, XNM, YNM, XMK, YMK; 01034 double det, detInv; 01035 double S, T; 01036 double X, Y; 01037 01038 XLK = 1.0 - 0.0; 01039 YLK = 0.0 - 0.0; 01040 XNM = V[degree].x - V[0].x; 01041 YNM = V[degree].y - V[0].y; 01042 XMK = V[0].x - 0.0; 01043 YMK = V[0].y - 0.0; 01044 01045 det = XNM*YLK - YNM*XLK; 01046 detInv = 1.0/det; 01047 01048 S = (XNM*YMK - YNM*XMK) * detInv; 01049 T = (XLK*YMK - YLK*XMK) * detInv; 01050 01051 X = 0.0 + XLK * S; 01052 Y = 0.0 + YLK * S; 01053 01054 return X; 01055 } 01056 01057 01058 01059 01060 01061 /******************************************************************************************** 01062 01063 INT32 CrossingCount( Point2 *V, INT32 degree) 01064 01065 Author: 01066 Created: 22/08/94 01067 Inputs: V = Control points of cubic Bezier 01068 degree = The degree of the polynomial 01069 Outputs: 01070 Returns: number of crossings 01071 Purpose: Count the number of times a Bezier control polygon 01072 crosses the 0-axis. This number is >= the number of roots. 01073 01074 01075 *********************************************************************************************/ 01076 01077 01078 INT32 CrossingCount( Point2 *V, INT32 degree) 01079 { 01080 INT32 i; 01081 INT32 n_crossings = 0; // Number of zero-crossings 01082 INT32 sign, old_sign; // Sign of coefficients 01083 01084 sign = old_sign = SGN(V[0].y); 01085 for (i = 1; i <= degree; i++) 01086 { 01087 sign = SGN(V[i].y); 01088 if (sign != old_sign) n_crossings++; 01089 old_sign = sign; 01090 } 01091 return n_crossings; 01092 } 01093 01094 01095 01096 01097 /******************************************************************************************** 01098 01099 INT32 ControlPolygonFlatEnough(Point2 *V, INT32 degree) 01100 01101 Author: 01102 Created: 22/08/94 01103 Inputs: V = Control points of cubic Bezier 01104 degree = The degree of the polynomial 01105 Outputs: 01106 Returns: 01107 Purpose: Check if the control polygon of a Bezier curve is flat enough 01108 for recursive subdivision to bottom out. 01109 01110 ********************************************************************************************/ 01111 01112 01113 INT32 ControlPolygonFlatEnough(Point2 *V, INT32 degree) 01114 { 01115 INT32 i; // Index variable 01116 double *distance; // Distances from pts to line 01117 double max_distance_above; // maximum of these 01118 double max_distance_below; 01119 double error; // Precision of root 01120 double intercept_1, 01121 intercept_2, 01122 left_intercept, 01123 right_intercept; 01124 double a, b, c; // Coefficients of implicit 01125 // eqn for line from V[0]-V[deg] 01126 01127 /* Find the perpendicular distance 01128 from each interior control point to 01129 line connecting V[0] and V[degree] */ 01130 01131 distance = (double *)CCMalloc((unsigned)(degree + 1) * sizeof(double)); 01132 { 01133 double abSquared; 01134 01135 // Derive the implicit equation for line connecting first 01136 // and last control points 01137 01138 a = V[0].y - V[degree].y; 01139 b = V[degree].x - V[0].x; 01140 c = V[0].x * V[degree].y - V[degree].x * V[0].y; 01141 01142 abSquared = (a * a) + (b * b); 01143 01144 for (i = 1; i < degree; i++) 01145 { 01146 // Compute distance from each of the points to that line 01147 distance[i] = a * V[i].x + b * V[i].y + c; 01148 if (distance[i] > 0.0) 01149 { 01150 distance[i] = (distance[i] * distance[i]) / abSquared; 01151 } 01152 if (distance[i] < 0.0) 01153 { 01154 distance[i] = -((distance[i] * distance[i]) / abSquared); 01155 } 01156 } 01157 } 01158 01159 01160 // Find the largest distance 01161 01162 max_distance_above = 0.0; 01163 max_distance_below = 0.0; 01164 for (i = 1; i < degree; i++) 01165 { 01166 if (distance[i] < 0.0) 01167 { 01168 max_distance_below = MIN(max_distance_below, distance[i]); 01169 }; 01170 if (distance[i] > 0.0) 01171 { 01172 max_distance_above = MAX(max_distance_above, distance[i]); 01173 } 01174 } 01175 CCFree((char *)distance); 01176 01177 { 01178 double det, dInv; 01179 double a1, b1, c1, a2, b2, c2; 01180 01181 // Implicit equation for zero line 01182 01183 a1 = 0.0; 01184 b1 = 1.0; 01185 c1 = 0.0; 01186 01187 // Implicit equation for "above" line 01188 01189 a2 = a; 01190 b2 = b; 01191 c2 = c + max_distance_above; 01192 01193 det = a1 * b2 - a2 * b1; 01194 dInv = 1.0/det; 01195 01196 intercept_1 = (b1 * c2 - b2 * c1) * dInv; 01197 01198 // Implicit equation for "below" line 01199 01200 a2 = a; 01201 b2 = b; 01202 c2 = c + max_distance_below; 01203 01204 det = a1 * b2 - a2 * b1; 01205 dInv = 1.0/det; 01206 01207 intercept_2 = (b1 * c2 - b2 * c1) * dInv; 01208 } 01209 01210 // Compute intercepts of bounding box 01211 01212 left_intercept = MIN(intercept_1, intercept_2); 01213 right_intercept = MAX(intercept_1, intercept_2); 01214 01215 error = 0.5 * (right_intercept-left_intercept); 01216 if (error < EPSILON) 01217 { 01218 return 1; 01219 } 01220 else 01221 { 01222 return 0; 01223 } 01224 } 01225 01226 01227 01228 /******************************************************************************************** 01229 01230 INT32 FindRoots( Point2* w, INT32 degree, double* t, INT32 depth) 01231 01232 Author: 01233 Created: 22/08/94 01234 Inputs: w = Control points of cubic Bezier 01235 degree = The degree of the polynomial 01236 depth = The depth of the recursion 01237 Outputs: t = a list of candidate t-values 01238 Returns: 01239 Purpose: Given a 5th-degree equation in Bernstein-Bezier form, find 01240 all of the roots in the interval [0, 1]. Return the number 01241 of roots found. 01242 01243 ********************************************************************************************/ 01244 01245 INT32 FindRoots( Point2* w, INT32 degree, double* t, INT32 depth) 01246 { 01247 INT32 i; 01248 Point2 Left[W_DEGREE+1], // New left and right 01249 Right[W_DEGREE+1]; // control polygons 01250 INT32 left_count, // Solution count from children 01251 right_count; 01252 double left_t[W_DEGREE+1], // Solutions from kids 01253 right_t[W_DEGREE+1]; 01254 01255 switch (CrossingCount(w, degree)) 01256 { 01257 case 0 : 01258 { // No solutions here 01259 return 0; 01260 break; 01261 } 01262 case 1 : 01263 { // Unique solution 01264 // Stop recursion when the tree is deep enough 01265 // if deep enough, return 1 solution at midpoint 01266 if (depth >= MAXDEPTH) 01267 { 01268 t[0] = (w[0].x + w[W_DEGREE].x) / 2.0; 01269 return 1; 01270 } 01271 if (ControlPolygonFlatEnough(w, degree)) 01272 { 01273 t[0] = ComputeXIntercept(w, degree); 01274 return 1; 01275 } 01276 break; 01277 } 01278 } 01279 01280 // Otherwise, solve recursively after 01281 // subdividing control polygon 01282 01283 Bezier(w, degree, 0.5, Left, Right); 01284 left_count = FindRoots(Left, degree, left_t, depth+1); 01285 right_count = FindRoots(Right, degree, right_t, depth+1); 01286 01287 01288 /* Gather solutions together */ 01289 for (i = 0; i < left_count; i++) 01290 { 01291 t[i] = left_t[i]; 01292 } 01293 for (i = 0; i < right_count; i++) 01294 { 01295 t[i+left_count] = right_t[i]; 01296 } 01297 01298 /* Send back total number of solutions */ 01299 return (left_count+right_count); 01300 } 01301 01302 01303 01304 01305 /******************************************************************************************** 01306 01307 Point2 *ConvertToBezierForm( Point2 P, Point2* V ) 01308 01309 Author: 01310 Created: 22/08/94 01311 Inputs: P = The point to find t for 01312 V = Control points of cubic Bezier 01313 Outputs: 01314 Returns: 01315 Purpose: Given a point and a Bezier curve, generate a 5th-degree 01316 Bezier-format equation whose solution finds the point on the 01317 curve nearest the user-defined point. 01318 01319 *******************************************************************************************/ 01320 01321 Point2 *ConvertToBezierForm( Point2 P, Point2* V ) 01322 { 01323 INT32 i, j, k, m, n, ub, lb; 01324 INT32 row, column; // Table indices 01325 Vector2 c[DEGREE+1]; // V(i)'s - P 01326 Vector2 d[DEGREE]; // V(i+1) - V(i) 01327 Point2 *w; // Ctrl pts of 5th-degree curve 01328 double cdTable[3][4]; // Dot product of c, d 01329 static double z[3][4] = { // Precomputed "z" for cubics 01330 {1.0, 0.6, 0.3, 0.1}, 01331 {0.4, 0.6, 0.6, 0.4}, 01332 {0.1, 0.3, 0.6, 1.0}, 01333 }; 01334 01335 01336 // Determine the c's -- these are vectors created by subtracting 01337 // point P from each of the control points 01338 01339 for (i = 0; i <= DEGREE; i++) 01340 { 01341 V2Sub(&V[i], &P, &c[i]); 01342 } 01343 01344 // Determine the d's -- these are vectors created by subtracting 01345 // each control point from the next 01346 01347 for (i = 0; i <= DEGREE - 1; i++) 01348 { 01349 d[i] = V2ScaleII(V2Sub(&V[i+1], &V[i], &d[i]), 3.0); 01350 } 01351 01352 // Create the c,d table -- this is a table of dot products of the 01353 // c's and d's 01354 01355 for (row = 0; row <= DEGREE - 1; row++) 01356 { 01357 for (column = 0; column <= DEGREE; column++) 01358 { 01359 cdTable[row][column] = V2Dot(&d[row], &c[column]); 01360 } 01361 } 01362 01363 // Now, apply the z's to the dot products, on the skew diagonal 01364 // Also, set up the x-values, making these "points" 01365 01366 w = (Point2 *)CCMalloc((unsigned)(W_DEGREE+1) * sizeof(Point2)); 01367 for (i = 0; i <= W_DEGREE; i++) 01368 { 01369 w[i].y = 0.0; 01370 w[i].x = (double)(i) / W_DEGREE; 01371 } 01372 01373 n = DEGREE; 01374 m = DEGREE-1; 01375 for (k = 0; k <= n + m; k++) 01376 { 01377 lb = MAX(0, k - m); 01378 ub = MIN(k, n); 01379 for (i = lb; i <= ub; i++) 01380 { 01381 j = k - i; 01382 w[i+j].y += cdTable[j][i] * z[j][i]; 01383 } 01384 } 01385 01386 return (w); 01387 } 01388 01389 01390 /******************************************************************************************* 01391 01392 > double PathUtil::SqrDistanceToCurve(DocCoord* V, DocCoord& P, double* mu ) 01393 01394 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01395 Created: 22/08/94 01396 Inputs: split = max level at which to split the curve (usually 64) 01397 P = The user-supplied point 01398 V = Control points of cubic Bezier 01399 Outputs: mu holds the parameter value of the closest point on the curve, 0<=mu<=1 01400 Returns: The squared distance to the curve 01401 Purpose: Compute the parameter value of the point on a Bezier 01402 curve segment closest to some arbtitrary, user-input point. 01403 Return the squared distance to the curve. 01404 01405 *******************************************************************************************/ 01406 01407 double PathUtil::SqrDistanceToCurve(const UINT32 split, 01408 const DocCoord* V, 01409 const DocCoord& P, 01410 double* mu ) 01411 { 01412 Point2 *w; // Ctrl pts for 5th-degree equ 01413 Point2 lcurve[DEGREE+1]; // Local double version of control polygon 01414 double t_candidate[W_DEGREE]; // Possible roots 01415 INT32 n_solutions; // Number of roots found 01416 double t; // Parameter value of closest pt 01417 double dist; // Closest squared distance to curve 01418 01419 // Convert control polygon to double type 01420 01421 for (INT32 i=0; i < DEGREE+1; i++) 01422 { 01423 lcurve[i].x = (double) V[i].x; 01424 lcurve[i].y = (double) V[i].y; 01425 } 01426 01427 Point2 p; 01428 p.x = (double) P.x; 01429 p.y = (double) P.y; 01430 01431 // Convert problem to 5th-degree Bezier form 01432 01433 w = ConvertToBezierForm(p, lcurve); 01434 01435 // Find all possible roots of 5th-degree equation 01436 01437 n_solutions = FindRoots(w, W_DEGREE, t_candidate, 0); 01438 CCFree((char *)w); 01439 01440 // Compare distances of P to all candidates, and to t=0, and t=1 01441 01442 { double new_dist; 01443 Point2 q; 01444 Vector2 v; 01445 01446 // Check distance to beginning of curve, where t = 0 01447 01448 dist = V2SquaredLength(V2Sub(&p, lcurve, &v)); 01449 t = 0.0; 01450 01451 // Find distances for candidate points 01452 01453 for (INT32 i = 0; i < n_solutions; i++) 01454 { 01455 q = Bezier(lcurve, DEGREE, t_candidate[i], NULL, NULL); 01456 new_dist = V2SquaredLength(V2Sub(&p, &q, &v)); 01457 if (new_dist < dist) 01458 { 01459 dist = new_dist; 01460 t = t_candidate[i]; 01461 } 01462 } 01463 01464 // Finally, look at distance to end point, where t = 1 01465 01466 new_dist = V2SquaredLength(V2Sub(&p, &(lcurve[DEGREE]), &v)); 01467 if (new_dist < dist) 01468 { 01469 dist = new_dist; 01470 t = 1.0; 01471 } 01472 } 01473 01474 *mu = t; 01475 return dist; 01476 01477 }