fitcurve.cpp

Go to the documentation of this file.
00001 // $Id: fitcurve.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 // Curve Fitting Functions
00099 
00100 /*
00101 */
00102 
00103 
00104 #include "camtypes.h"
00105 #include <stdio.h>
00106 #include <stdlib.h>
00107 #include <math.h>
00108 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "fitcurve.h"
00110 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "pathtrap.h"
00112 
00113 
00114 // Set things up so that the tool will be listed in the Dialog box
00115 DECLARE_SOURCE("$Revision: 1282 $");
00116 
00117 
00118 #define STEP_SIZE   6
00119 #define ERROR_STEP  6
00120 
00121 // Give my name in memory dumps
00122 CC_IMPLEMENT_MEMDUMP(CurveFitObject, CC_CLASS_MEMDUMP)
00123 CC_IMPLEMENT_MEMDUMP(FitPoint, CC_CLASS_MEMDUMP)
00124 
00125 #define new CAM_DEBUG_NEW
00126 
00127 
00128 /********************************************************************************************
00129 
00130 >   FitPoint FitPoint::operator - ()
00131 
00132     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00133     Created:    02/03/94
00134     Returns:    A FitPoint that is the negative of this
00135     Purpose:    Unary Minus for a FitPoint (Vector)
00136 
00137 ********************************************************************************************/
00138 
00139 FitPoint FitPoint::operator - ()
00140 {
00141     FitPoint Result;
00142 
00143     // negate the vector
00144     Result.x = -x;
00145     Result.y = -y;
00146 
00147     // and return it
00148     return Result;
00149 }
00150 
00151 
00152 
00153 /********************************************************************************************
00154 
00155 >   FitPoint FitPoint::operator * (double Factor)
00156 
00157     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00158     Created:    02/03/94
00159     Inputs:     Factor - the amount to scale the vector by
00160     Returns:    FitPoint - this vector multiplied by the Factor
00161     Purpose:    FitPoint Multiply function. This will multiply the vector by a constant
00162                 Factor. The result is returned
00163 
00164 ********************************************************************************************/
00165 
00166 FitPoint FitPoint::operator * (double Factor)
00167 {
00168     FitPoint Result;
00169 
00170     // Scale the vector by the factor
00171     Result.x = x*Factor;
00172     Result.y = y*Factor;
00173 
00174     // and return it
00175     return Result;
00176 }
00177 
00178 
00179 /********************************************************************************************
00180 
00181 >   FitPoint FitPoint::SetLength( double NewLen )
00182 
00183     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00184     Created:    02/03/94
00185     Inputs:     NewLen - The length you want the vector to be
00186     Returns:    FitPoint - the new vector that points in the same direction as this vector,
00187                 only of magnitude NewLen
00188     Purpose:    Scales the FitPoint vector to by the specified length
00189 
00190 ********************************************************************************************/
00191 
00192 FitPoint FitPoint::SetLength( double NewLen )
00193 {
00194     FitPoint Result(x, y);
00195 
00196     double Len = Length();
00197     if (Len != 0.0)
00198     {
00199         Len = NewLen/Len ;
00200         Result.x *= Len;
00201         Result.y *= Len;
00202     }
00203 
00204     return Result;
00205 }
00206 
00207 
00208 /********************************************************************************************
00209 
00210 >   FitPoint operator + (const FitPoint& Point1, const FitPoint& Point2)
00211 
00212     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00213     Created:    02/03/94
00214     Inputs:     Point1 - The first FitPoint Vector
00215                 Point2 - The Second FitPoint vector
00216     Returns:    FitPoint - the FitPoint vector that is a combination of Point1 and Point2
00217     Purpose:    Adds two FitPoint vectors together. This function is a Friend of the FitPoint
00218                 class
00219 
00220 ********************************************************************************************/
00221 
00222 FitPoint operator + (const FitPoint& Point1, const FitPoint& Point2)
00223 {
00224     FitPoint Result;
00225 
00226     // Add the two vector together
00227     Result.x = Point1.x + Point2.x;
00228     Result.y = Point1.y + Point2.y;
00229 
00230     // return the result
00231     return Result;
00232 }
00233 
00234 
00235 /********************************************************************************************
00236 
00237 >   FitPoint operator - (const FitPoint& Point1, const FitPoint& Point2)
00238 
00239     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00240     Created:    02/03/94
00241     Inputs:     Point1 - The first FitPoint Vector
00242                 Point2 - The Second FitPoint vector
00243     Returns:    FitPoint - the FitPoint vector that is the first vector minus the second
00244                 vector
00245     Purpose:    Subtracts the two FitPoint vectors. This function is a Friend of the FitPoint
00246                 class
00247 
00248 ********************************************************************************************/
00249 
00250 FitPoint operator - (const FitPoint& Point1, const FitPoint& Point2)
00251 {
00252     FitPoint Result;
00253 
00254     // Subtract the two vector from each other
00255     Result.x = Point1.x - Point2.x;
00256     Result.y = Point1.y - Point2.y;
00257 
00258     // return the result
00259     return Result;
00260 }
00261 
00262 
00263 
00264 /********************************************************************************************
00265 
00266 >   void FitPoint::Dump()
00267 
00268     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00269     Created:    02/03/94
00270     Purpose:    Dumps the values of the FitPoint class to the TRACE output
00271 
00272 ********************************************************************************************/
00273 
00274 void FitPoint::Dump()
00275 {
00276     TRACEALL( _T("FitPoint Object (%7.2f, %7.2f)\n"), x, y );
00277 }
00278 
00279 
00280 
00281 
00282 
00283 
00284 
00285 
00286 
00287 
00288 
00289 
00290 /********************************************************************************************
00291 
00292 >   CurveFitObject::CurveFitObject( Path* ThePath, double MaxError )
00293 
00294     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00295     Created:    7/3/94
00296     Inputs:     ThePath - A Pointer to a path object that we are to simplify
00297                 MaxError - The Square of the distance that we are prepared to let the fitted
00298                 path stray from the original data
00299     Purpose:    Constructs a Curve Fitting Object and gives it some data that it will need
00300                 during the fit
00301     SeeAlso:    CurveFitObject::Initialise
00302 
00303 ********************************************************************************************/
00304 
00305 CurveFitObject::CurveFitObject( Path* ThePath, double MaxError )
00306 {
00307     // Make a note of info we have got
00308     LongPath = ThePath;
00309     Error = MaxError;
00310 
00311     // And set defaults to the rest
00312     Distances = NULL;
00313     PathArray = NULL;
00314     LineArray = NULL;
00315 
00316     TotalStraightLines = 0;
00317     TotalCoords = 0;
00318 }
00319 
00320 
00321 /********************************************************************************************
00322 
00323 >   CurveFitObject::~CurveFitObject()
00324 
00325     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00326     Created:    7/3/94
00327     Purpose:    Destroys the curve fitter when it is done, freeing any memory that was
00328                 allocated in the Initialise() function
00329     SeeAlso:    CurveFitObject::Initialise
00330 
00331 ********************************************************************************************/
00332 
00333 CurveFitObject::~CurveFitObject()
00334 {
00335     if (Distances!=NULL)
00336         delete Distances;
00337 
00338     if (PathArray!=NULL)
00339         delete PathArray;
00340 
00341     if (LineArray!=NULL)
00342         delete LineArray;
00343 }
00344 
00345 
00346 
00347 /********************************************************************************************
00348 
00349 >   BOOL CurveFitObject::Initialise(Path* CopyPath, INT32 NumPoints)
00350 
00351     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00352     Created:    7/3/94
00353     Inputs:     CopyPath - The path to copy the MoveTo Data from
00354                 NumPoints - the number of points in the path object
00355     Returns:    TRUE if the CurveFitter was properly initialised or FALSE if it failed to
00356                 get the memory that it needs
00357     Purpose:    Gets memory to store all the coords in and starts going through the path
00358                 extracting the relavent points. It also calculates the distance along the path
00359                 of each of the points it stores, which are needed extensivly when checking
00360                 the accuracy of the fit. This function also empties the path object ready for
00361                 the fitted version to be added.
00362 
00363 ********************************************************************************************/
00364 
00365 BOOL CurveFitObject::Initialise(Path* CopyPath, INT32 NumPoints)
00366 {
00367     // Here we must try and get some memory for the path array
00368     PathArray = new DocCoord[NumPoints];
00369     if (PathArray==NULL)
00370         return FALSE;
00371 
00372     // copy the data out of the path and into the array. Only copy points of interest
00373     CopyPath->FindStartOfPath();
00374     DocCoord* Coords = CopyPath->GetCoordArray();
00375 
00376     // Deal with the flags - We have to look for Spare1 being true, meaning that the previous
00377     // Lineto was meant to stay as a straight line
00378     PathFlags* Flags = CopyPath->GetFlagArray();
00379     TotalStraightLines = 0;
00380     for (INT32 i=0; i<NumPoints; i++)
00381     {
00382         if (Flags[i].Spare1==TRUE)
00383             TotalStraightLines++;
00384     }
00385 
00386     // Get some memory to store the positions of the line breaks in
00387     if (TotalStraightLines>0)
00388     {
00389         LineArray = new INT32[TotalStraightLines];
00390         if (LineArray==NULL)
00391         {
00392             delete PathArray;
00393             PathArray = NULL;
00394             return FALSE;
00395         }
00396     }
00397 
00398     // copy the MoveTo out of the path and into our array
00399     PathArray[0].x = Coords[0].x;
00400     PathArray[0].y = Coords[0].y;
00401 
00402     INT32 IncludePoint = 1;
00403     INT32 StraightLinePos = 0;
00404     for (INT32 i=1; i<NumPoints; i++)
00405     {
00406         // If this is one of the straight line bits, then make a note of its position
00407         if (Flags[i].Spare1==TRUE)
00408         {
00409             LineArray[StraightLinePos] = IncludePoint;
00410             StraightLinePos++;
00411         }
00412 
00413         // Check to see if this coordinate is really needed (last point is always needed)
00414         if ((Coords[i].x != PathArray[IncludePoint-1].x) || (Coords[i].y != PathArray[IncludePoint-1].y) &&
00415             (i!=NumPoints-1))
00416         {
00417             // This point is not the same as the one before, so add it into the array
00418             PathArray[IncludePoint].x = Coords[i].x;
00419             PathArray[IncludePoint].y = Coords[i].y;
00420 
00421             IncludePoint++;
00422         }
00423     }
00424 
00425     // Add the last point in the track data to the path array if it is not already there
00426     if ((PathArray[IncludePoint-1].x != Coords[NumPoints-1].x) ||
00427         (PathArray[IncludePoint-1].y != Coords[NumPoints-1].y))
00428     {
00429         PathArray[IncludePoint].x = Coords[NumPoints-1].x;
00430         PathArray[IncludePoint].y = Coords[NumPoints-1].y;
00431         IncludePoint++;
00432     }
00433 
00434     // increment the Include count (as we have just added a point to the array) and then
00435     // set the true number of points properly.
00436     NumPoints = IncludePoint;
00437     if (NumPoints<2)
00438     {
00439         delete PathArray;
00440         delete LineArray;
00441         PathArray = NULL;
00442         LineArray = NULL;
00443         return FALSE;
00444     }
00445 
00446     // Build an array of the distances along the path
00447     Distances = new INT32[NumPoints];
00448     if (Distances==NULL)
00449     {
00450         delete PathArray;
00451         delete LineArray;
00452         PathArray = NULL;
00453         LineArray = NULL;
00454         return FALSE;
00455     }
00456 
00457     Distances[0] = 0;
00458     INT32 dx, dy, min;
00459     for (INT32 i=1; i<NumPoints; i++)
00460     {
00461         // This is doing an approximation to a Square Root
00462         // It is about 250 times faster on a machine without FPU
00463 
00464         // find the difference between the last 2 points
00465         dx = abs(PathArray[i].x - PathArray[i-1].x);
00466         dy = abs(PathArray[i].y - PathArray[i-1].y);
00467 
00468         // Find out half the smallest of dx and dy
00469         if (dx>dy)
00470             min = dy>>1;
00471         else
00472             min = dx>>1;
00473 
00474         Distances[i] = Distances[i-1] + dx + dy - min;
00475     }
00476 
00477     // Now we can delete the Path Data in the path we are to put the smoothed path in
00478     LongPath->ClearPath();
00479     LongPath->FindStartOfPath();
00480     LongPath->InsertMoveTo(PathArray[0]);
00481 
00482     // Store the total number of coords in the array for future reference
00483     TotalCoords = NumPoints;
00484 
00485     return TRUE;
00486 }
00487 
00488 
00489 /********************************************************************************************
00490 
00491 >   void CurveFitObject::FitCurve()
00492 
00493     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00494     Created:    7/3/94
00495     Purpose:    Actually fits the curve and places the results back in the path object
00496                 declared in the constructor. This function walks through all the data points
00497                 trying to find cusps in the path and fits curves between them by calling the
00498                 FitCubic member of the CurveFitObject
00499     SeeAlso:    CurveFitObject::FitCubic
00500 
00501 ********************************************************************************************/
00502 
00503 void CurveFitObject::FitCurve()
00504 {
00505     FitPoint Tangent1, Tangent2;
00506     double Angle1, Angle2;
00507     INT32 Start = 0;
00508     INT32 StraightLinePos = 0;
00509 
00510     for (INT32 i=1; i<TotalCoords-1; i++)
00511     {
00512         if ((TotalStraightLines>0) && (i==LineArray[StraightLinePos]))
00513         {
00514             // if we have a section to the path that needs fitting before we do this then do it
00515             if (Start != (i-1))
00516             {
00517                 // calculate the tangents off the end of the path
00518                 Tangent1 = LeftTangent(Start);
00519                 Tangent2 = RightTangent(i-1);
00520 
00521                 // Fit a curve up to the straight line section
00522                 FitCubic(Start, i-1, Tangent1, Tangent2);
00523             }
00524             
00525             // And draw in the straight line section
00526             InsertStraightLine(PathArray[i]);
00527             StraightLinePos++;
00528             Start = i;
00529         }
00530         else
00531         {
00532             // Go find the angle between a group of points
00533             Angle1 = atan2((double)PathArray[i].y-PathArray[i-1].y, (double)PathArray[i].x-PathArray[i-1].x);
00534             Angle2 = atan2((double)PathArray[i+1].y-PathArray[i].y, (double)PathArray[i+1].x-PathArray[i].x);
00535         
00536             // Get them in a sensible range
00537             if (Angle1 < -PI)   Angle1 += 2*PI;
00538             if (Angle1 > PI)    Angle1 -= 2*PI;
00539             if (Angle2 < -PI)   Angle2 += 2*PI;
00540             if (Angle2 > PI)    Angle2 -= 2*PI;
00541         
00542             // See if this point is a cusp in the curve
00543             if ((fabs(Angle2-Angle1) > (PI/2)) && (fabs(Angle2-Angle1) <= PI))
00544             {
00545                 // calculate the tangents off the end of the path
00546                 Tangent1 = LeftTangent(Start);
00547                 Tangent2 = RightTangent(i);
00548 
00549                 // and do a load of maths that will hopefully fit a nice curve on it
00550                 FitCubic(Start, i, Tangent1, Tangent2);
00551                 Start = i;
00552             }
00553         }
00554     }
00555 
00556 
00557     INT32 End = TotalCoords-1;
00558     if ((TotalStraightLines>0) && (End==LineArray[StraightLinePos]))
00559     {
00560         // if we have a section to the path that needs fitting before we do this then do it
00561         if (Start != (End-1))
00562         {
00563             // calculate the tangents off the end of the path
00564             Tangent1 = LeftTangent(Start);
00565             Tangent2 = RightTangent(End-1);
00566 
00567             // Fit a curve up to the straight line section
00568             FitCubic(Start, End-1, Tangent1, Tangent2);
00569         }
00570         
00571         // And draw in the straight line section
00572         InsertStraightLine(PathArray[End]);
00573     }
00574     else
00575     {
00576         // Just have to fit a curve from the last cusp to the end of the path
00577         Tangent1 = LeftTangent(Start);
00578         Tangent2 = RightTangent(End);
00579     
00580         // and do a load of maths that will hopefully fit a nice curve on it
00581         FitCubic(Start, End, Tangent1, Tangent2);
00582     }
00583 }
00584 
00585 
00586 
00587 /********************************************************************************************
00588 
00589 >   void CurveFitObject::FitCubic( INT32 FirstPoint, INT32 LastPoint,
00590                                    FitPoint Tangent1, FitPoint Tangent2,
00591                                    BOOL IsStartCusp = TRUE, BOOL IsEndCusp = TRUE);
00592 
00593     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00594     Created:    7/3/94
00595     Inputs:     FirstPoint - the index of the coordinate in the path to start fitting from
00596                 LastPoint - the index of the coordinate in the path to stop fitting at
00597                 Tangent1 - The tangent of the curve at the first point
00598                 Tangent2 - the tangent of the curve at the last point
00599                 IsStartCusp - TRUE if FirstPoint is next to a cusp
00600                 IsEndCusp - TRUE of EndPoint is next to a cusp
00601     Purpose:    This function is recursive. It tries to fit a cubic function to the
00602                 coordinates in the path between FirstPoint and LastPoint. It then compares
00603                 this function with the actual coordinates to determine how good a fit it
00604                 has found. If the fit is good it is added to the path object. If the fit
00605                 is bad then it is split at the point where the fit is at its worst and
00606                 FitCubic is called again for the left and right halves.
00607     Scope:      private
00608     SeeAlso:    CurveFitObject::GenerateBezier; CurveFitObject::CalcMaxError
00609 
00610 ********************************************************************************************/
00611 
00612 void CurveFitObject::FitCubic(INT32 FirstPoint, INT32 LastPoint, FitPoint Tangent1, FitPoint Tangent2,
00613                               BOOL IsStartCusp, BOOL IsEndCusp)
00614 {
00615     // Will need space for a Bezier curve
00616     FitPoint Bezier[4];
00617     INT32 NumPoints = LastPoint - FirstPoint + 1;
00618 
00619     // if this segment only has 2 points in it then do the special case
00620     if ( NumPoints == 2 )
00621     {
00622         InsertLine(PathArray[FirstPoint], PathArray[LastPoint], Tangent1, Tangent2, IsStartCusp, IsEndCusp);
00623         return;
00624     }
00625     
00626     // Due to a bug in the algorithm we also have to consider 3 points as a special case
00627     if ( NumPoints == 3 )
00628     {
00629         INT32 Distance = (Distances[LastPoint] - Distances[FirstPoint]) / 3;
00630         
00631         // store the end points
00632         Bezier[0] = PathArray[FirstPoint];
00633         Bezier[3] = PathArray[LastPoint];
00634         
00635         // calc the control points
00636         Bezier[1] = Bezier[0] + Tangent1.SetLength(Distance);
00637         Bezier[2] = Bezier[3] + Tangent2.SetLength(Distance);
00638 
00639         // add it to the path
00640         InsertBezier(Bezier, IsStartCusp, IsEndCusp);
00641         return;
00642     }
00643 
00644     // Create a Bezier curve from the segemnt and see if it is a good fit
00645     INT32 SplitPoint;
00646     GenerateBezier(FirstPoint, LastPoint, Tangent1, Tangent2, Bezier);
00647     double MaxError = CalcMaxError(FirstPoint, LastPoint, Bezier, &SplitPoint);
00648     
00649     if (MaxError < Error)
00650     {
00651         // The mapping was good, so output the curve segment
00652         InsertBezier(Bezier, IsStartCusp, IsEndCusp);
00653         return;
00654     }
00655     
00656     // fitting failed -- split at max error point and fit recursively
00657     FitPoint CentTangent = CentreTangent(SplitPoint);
00658     FitCubic(FirstPoint, SplitPoint, Tangent1, CentTangent, IsStartCusp, FALSE);
00659 
00660     CentTangent = -CentTangent;
00661     FitCubic(SplitPoint, LastPoint, CentTangent, Tangent2, FALSE, IsEndCusp);
00662 }
00663 
00664 
00665 
00666 
00667 /********************************************************************************************
00668 
00669 >   void CurveFitObject::GenerateBezier(INT32 FirstPoint, INT32 LastPoint, FitPoint Tangent1, 
00670                                         FitPoint Tangent2, FitPoint* Bezier)
00671 
00672     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00673     Created:    7/3/94
00674     Inputs:     FirstPoint - the index of the coordinate in the path to start fitting from
00675                 LastPoint - the index of the coordinate in the path to stop fitting at
00676                 Tangent1 - The tangent of the curve at the first point
00677                 Tangent2 - the tangent of the curve at the last point
00678     Outputs:    Bezier - A pointer to a Bezier Curve
00679     Purpose:    This function supplies the maths to try and fit a cubic function to a 
00680                 set of points. It spends its time trying to come up with good lengths for
00681                 the two tangents passed in to maximise the closeness of the fit. If it
00682                 fails to come up with realistic results it simply sets the tangent lengths
00683                 to be 1/3 of the distance between the start point and the end point.
00684     Scope:      private
00685 
00686 ********************************************************************************************/
00687 
00688 void CurveFitObject::GenerateBezier(INT32 FirstPoint, INT32 LastPoint, FitPoint Tangent1, 
00689                                     FitPoint Tangent2, FitPoint* Bezier)
00690 {
00691     INT32 NumPoints = LastPoint - FirstPoint + 1;
00692     
00693     // Build a matrix type of thing that contains the tangents scaled by the offsets
00694     FitPoint A[STEP_SIZE+1][2];         //  Vector2 (*A)[2] = new Vector2[NumPoints+1][2];
00695     double   Offsets[STEP_SIZE+1];
00696 
00697     INT32 step = (NumPoints+STEP_SIZE) / STEP_SIZE;
00698     INT32 i, pos = 0;
00699 
00700     // Chord length parameterisation
00701     const INT32 DistToEnd = Distances[LastPoint] - Distances[FirstPoint];
00702     for (i=FirstPoint; i<LastPoint+1; i+=step)
00703     {
00704         Offsets[pos] = Distances[i] - Distances[FirstPoint];
00705         Offsets[pos] /= DistToEnd;
00706 
00707         // Fill the matrix A
00708         A[pos][0] = Tangent1.SetLength( Bezier1(Offsets[pos]) );
00709         A[pos][1] = Tangent2.SetLength( Bezier2(Offsets[pos]) );
00710 
00711         // Move to the next element in the path
00712         pos++;
00713     }
00714 
00715     // For a detailed description of the maths used here, please see Graphics Gems I
00716     // I have made some changes to the basic algorithm used there - the main one is
00717     // that this block of maths is only performed on a small selection of the points
00718     // in the data set, where-as the one in the book uses all the points
00719     double  C[2][2];
00720     double  X[2];
00721     
00722     C[0][0] = 0.0;
00723     C[0][1] = 0.0;
00724     C[1][0] = 0.0;
00725     C[1][1] = 0.0;
00726     X[0]    = 0.0;
00727     X[1]    = 0.0;
00728     
00729     FitPoint FirstCoord = PathArray[FirstPoint];
00730     FitPoint LastCoord  = PathArray[LastPoint];
00731     FitPoint ThisCoord, Combo;
00732 
00733     pos = 0;
00734     for (i=0; i<NumPoints; i+=step)
00735     {
00736         C[0][0] += A[pos][0].SquaredLength();
00737         C[0][1] += A[pos][0].Dot(A[pos][1]);
00738         // Point C[1][0] is the same as C[0][1] and is set outside the loop below
00739         C[1][1] += A[pos][1].SquaredLength();
00740         
00741         // Go ahead and build a vector based on the bezier functions
00742         ThisCoord = PathArray[FirstPoint+i];
00743         Combo = ThisCoord - ((FirstCoord * Bezier0(Offsets[pos]))
00744                             + (FirstCoord * Bezier1(Offsets[pos]))
00745                             + (LastCoord  * Bezier2(Offsets[pos]))
00746                             + (LastCoord  * Bezier3(Offsets[pos])));
00747 
00748         // Combine it with the other points
00749         X[0] += A[pos][0].Dot( Combo );
00750         X[1] += A[pos][1].Dot( Combo );
00751 
00752         pos++;
00753     }
00754 
00755     // This point in the matrix is the same, so we do not need to do it in the loop
00756     C[1][0] = C[0][1];
00757     
00758     // calc the determinants of C and X
00759     double det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
00760     double det_C0_X  = C[0][0] * X[1]    - C[0][1] * X[0];
00761     double det_X_C1  = X[0]    * C[1][1] - X[1]    * C[0][1];
00762     
00763     // finally, derive the length of the ideal tangents
00764     if (det_C0_C1 == 0.0)
00765         det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12;   // oh err, whats it up to here then!
00766     
00767     double AlphaLeft  = det_X_C1 / det_C0_C1;
00768     double AlphaRight = det_C0_X / det_C0_C1;
00769     
00770     Bezier[0] = PathArray[FirstPoint];
00771     Bezier[3] = PathArray[LastPoint];
00772 
00773     // if alpha negative, the set the tangent length to a third of the distance between
00774     // the start and the end points of the curve segment    
00775     if ( AlphaLeft < 0.0 || AlphaRight < 0.0)
00776     {
00777         INT32 Distance = (Distances[LastPoint] - Distances[FirstPoint]) / 3;
00778         
00779         Bezier[1] = Bezier[0] + Tangent1.SetLength(Distance);
00780         Bezier[2] = Bezier[3] + Tangent2.SetLength(Distance);
00781     }
00782     else
00783     {   
00784         Bezier[1] = Bezier[0] + Tangent1.SetLength(AlphaLeft);
00785         Bezier[2] = Bezier[3] + Tangent2.SetLength(AlphaRight);
00786     }
00787 }
00788 
00789 
00790 /********************************************************************************************
00791 
00792 >   FitPoint CurveFitObject::BezierPoint( FitPoint* Bez, double u)
00793 
00794     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00795     Created:    7/3/94
00796     Inputs:     Bez - The control points of a bezier curve
00797                 u - the normalised distance along the bezier that we are interested in
00798     Returns:    The coord of the point that is a distance u into the bezier. for example,
00799                 if u = 0.5 then the coord of the point half way along the bezier will be
00800                 returned
00801     Purpose:    This function simply evaluates the bezier function for a given position and
00802                 is used to help when determining how good a fit we have obtained
00803     Scope:      private
00804     SeeAlso:    CurveFitObject::CalcMaxError
00805 
00806 ********************************************************************************************/
00807 
00808 FitPoint CurveFitObject::BezierPoint( FitPoint* Bez, double u)
00809 {
00810     double OneMinus = 1.0-u;
00811     double uSquared = u*u;
00812     double OneMinusSquared = OneMinus*OneMinus;
00813 
00814     FitPoint Coord;
00815     Coord = Bez[0]*(OneMinusSquared*OneMinus);
00816     Coord = Coord + Bez[1]*(3.0*u*OneMinusSquared);
00817     Coord = Coord + Bez[2]*(3.0*uSquared*OneMinus);
00818     Coord = Coord + Bez[3]*(uSquared*u);
00819 
00820     return Coord;
00821 }
00822 
00823 
00824 
00825 /********************************************************************************************
00826 
00827 >   double CurveFitObject::CalcMaxError(INT32 FirstPoint, INT32 LastPoint,
00828                                         FitPoint* Bezier, INT32* SplitPoint)
00829 
00830     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00831     Created:    7/3/94
00832     Inputs:     FirstPoint, LastPoint - The index at the start and end of the curve section
00833                 we have just fit
00834                 Bezier - the control points of the bezier we have fitted to the points
00835     Outputs:    SplitPoint - the point to split the curve at if the error is too great
00836     Returns:    The maximum distance between the bezier curve and the original data points
00837     Purpose:    Finds out how good a fit the bezier curve we have created is when compared
00838                 with the data points
00839     Scope:      private
00840 
00841 ********************************************************************************************/
00842 
00843 double CurveFitObject::CalcMaxError(INT32 FirstPoint, INT32 LastPoint, FitPoint* Bezier, INT32* SplitPoint)
00844 {
00845     double      Distance;
00846     double      MaxDist = 0.0;
00847     double      RTotalLength = 1.0/(Distances[LastPoint] - Distances[FirstPoint]);
00848     FitPoint    Point;
00849 
00850     // Start out by putting the split point in the middle of the curve segment
00851     INT32 NumPoints = LastPoint - FirstPoint + 1;
00852     *SplitPoint = NumPoints / 2;
00853     INT32 step = (NumPoints+ERROR_STEP) / ERROR_STEP;
00854     
00855     // Loop though the points, visiting a fixed number of them
00856     for (INT32 i=FirstPoint+1; i<LastPoint; i+=step)
00857     {
00858         // Calculate the offset at this point
00859         double Offset = Distances[i] - Distances[FirstPoint];
00860         Offset *= RTotalLength;
00861 
00862         // Calculate where the curve actually is and find the distance from where we want it
00863         FitPoint Coord = PathArray[i];
00864         Point = BezierPoint(Bezier, Offset);
00865         Distance = (Point - Coord).SquaredLength();
00866         if ( Distance >= MaxDist)
00867         {
00868             MaxDist = Distance;
00869             *SplitPoint = i;
00870         }
00871     }
00872     
00873     return MaxDist;
00874 }
00875 
00876 
00877 /********************************************************************************************
00878 
00879 >   FitPoint CurveFitObject::LeftTangent(INT32 Start)
00880 
00881     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00882     Created:    7/3/94
00883     Inputs:     Start - the index of the point at the start of the segment to fit
00884     Returns:    The tangent at the point Start
00885     Purpose:    Finds the Left tangent at the given point in the path
00886     Scope:      private
00887 
00888 ********************************************************************************************/
00889 
00890 FitPoint CurveFitObject::LeftTangent(INT32 Start)
00891 {
00892     FitPoint Tangent;
00893 
00894     // check for empty point array
00895     if (TotalCoords == 0)
00896     {
00897         Tangent.x = 1;
00898         Tangent.y = 0;
00899 
00900         return Tangent;
00901     }
00902     
00903     // check for start outside the array
00904     if ((Start >= TotalCoords) || (Start < 0))
00905         Start = TotalCoords / 2;
00906 
00907     // Find out which point to look to
00908     INT32 Forward = Start+2;
00909     if (Forward > TotalCoords-1)
00910         Forward = TotalCoords-1;
00911 
00912     // Calc the tangent from the left of the curve segment
00913     Tangent.x = PathArray[Forward].x - PathArray[Start].x;
00914     Tangent.y = PathArray[Forward].y - PathArray[Start].y;
00915 
00916     // Make sure that is not of zero length
00917     if ((Tangent.x==0) && (Tangent.y==0))
00918     {
00919         TRACEALL( _T("Tangent was a zero length vector in the curve fitter (left)\n"));
00920         Tangent.x = 1;
00921     }
00922     
00923     return Tangent;
00924 }
00925 
00926 
00927 /********************************************************************************************
00928 
00929 >   FitPoint CurveFitObject::RightTangent(INT32 End)
00930 
00931     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00932     Created:    7/3/94
00933     Inputs:     End - the index of the point at the end of the segment to fit
00934     Returns:    The tangent at the point End
00935     Purpose:    Finds the Right tangent at the given point in the path
00936     Scope:      private
00937 
00938 ********************************************************************************************/
00939 
00940 FitPoint CurveFitObject::RightTangent(INT32 End)
00941 {
00942     FitPoint Tangent;
00943 
00944     // Find out which point to look to
00945     INT32 Backward = End-2;
00946     if (Backward<0)
00947         Backward = 0;
00948 
00949     Tangent.x = PathArray[Backward].x - PathArray[End].x;
00950     Tangent.y = PathArray[Backward].y - PathArray[End].y;
00951 
00952     // Make sure that is not of zero length
00953     if ((Tangent.x==0) && (Tangent.y==0))
00954     {
00955         TRACEALL( _T("Tangent was a zero length vector in the curve fitter (Right)\n"));
00956         Tangent.x = -1;
00957     }
00958 
00959     return Tangent;
00960 }
00961 
00962 
00963 
00964 /********************************************************************************************
00965 
00966 >   FitPoint CurveFitObject::CentreTangent(INT32 Centre)
00967 
00968     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00969     Created:    7/3/94
00970     Inputs:     Centre - the index of the split point in the path
00971     Returns:    The tangent at the point Centre
00972     Purpose:    Finds the tangent at the centre of the path
00973     Scope:      private
00974 
00975 ********************************************************************************************/
00976 
00977 FitPoint CurveFitObject::CentreTangent(INT32 Centre)
00978 {
00979     DocCoord Left, Right;
00980     FitPoint CentreTang;
00981 
00982     // check for empty point array
00983     if (TotalCoords == 0)
00984     {
00985         CentreTang.x = 1;
00986         CentreTang.y = 0;
00987 
00988         return CentreTang;
00989     }
00990 
00991     // check for centre outside the array
00992     if ((Centre >= TotalCoords) || (Centre < 0))
00993         Centre = TotalCoords / 2;
00994 
00995     // Find out which point to look to
00996     INT32 Forward = Centre+2;
00997     if (Forward > TotalCoords-1)
00998         Forward = TotalCoords-1;
00999 
01000     // Find out which point to look to
01001     INT32 Backward = Centre-2;
01002     if (Backward < 0)
01003         Backward = 0;
01004 
01005     // Calc right tangent
01006     Left.x = PathArray[Backward].x - PathArray[Centre].x;
01007     Left.y = PathArray[Backward].y - PathArray[Centre].y;
01008 
01009     // Calc left tangent
01010     Right.x = PathArray[Centre].x - PathArray[Forward].x;
01011     Right.y = PathArray[Centre].y - PathArray[Forward].y;
01012 
01013     // Average to get the centre tangent
01014     CentreTang.x = (Left.x + Right.x) / 2.0;
01015     CentreTang.y = (Left.y + Right.y) / 2.0;
01016 
01017     // Make sure that is not of zero length
01018     if ((CentreTang.x==0) && (CentreTang.y==0))
01019     {
01020         TRACEALL( _T("Tangent was a zero length vector in the curve fitter (Cent)\n"));
01021         CentreTang.x = 1;
01022     }
01023 
01024     // return it
01025     return CentreTang;
01026 }
01027 
01028 
01029 
01030 /********************************************************************************************
01031 
01032 >   void CurveFitObject::InsertBezier(FitPoint* Bezier, BOOL IsStartCusp, BOOL IsEndCusp)
01033 
01034     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01035     Created:    7/3/94
01036     Inputs:     Bezier - the control points of the bezier curve to add to the path
01037                 IsStartCusp - TRUE if the start of this bezier is at a cusp
01038                 IsEndCusp - TRUE if the End of this bezier is at a cusp
01039     Purpose:    Adds the bezier curve to the path. If it is that last curve in the fitting
01040                 (ie Depth is 0) then the rotate flag will not be set
01041     Scope:      private
01042 
01043 ********************************************************************************************/
01044 
01045 void CurveFitObject::InsertBezier(FitPoint* Bezier, BOOL IsStartCusp, BOOL IsEndCusp)
01046 {
01047     // Prepare some flags
01048     PathFlags Flags;
01049     Flags.IsSelected = FALSE;
01050     Flags.IsSmooth = FALSE;
01051     Flags.IsRotate = TRUE;
01052 
01053     // Add this Bezier curve into the path
01054     LongPath->InsertCurveTo( DocCoord( (INT32)Bezier[1].x, (INT32)Bezier[1].y),
01055                              DocCoord( (INT32)Bezier[2].x, (INT32)Bezier[2].y),
01056                              DocCoord( (INT32)Bezier[3].x, (INT32)Bezier[3].y), &Flags);
01057 
01058     // Deal with cusps
01059     if (IsStartCusp || IsEndCusp)
01060     {
01061         // Go find out about the path
01062         PathFlags* AllFlags = LongPath->GetFlagArray();
01063         INT32 NumCoords = LongPath->GetNumCoords();
01064 
01065         if (IsStartCusp)
01066         {
01067             // Patch up the flags of the bits near that start
01068             AllFlags[NumCoords-3].IsRotate = FALSE;
01069         }
01070     
01071         if (IsEndCusp)
01072         {
01073             // Patch up the flags of the bits near that end
01074             AllFlags[NumCoords-2].IsRotate = FALSE;
01075             AllFlags[NumCoords-1].IsRotate = FALSE;
01076         }
01077     }
01078 }
01079 
01080 
01081 
01082 /********************************************************************************************
01083 
01084 >   void CurveFitObject::InsertLine(const DocCoord& Start, const DocCoord& End, 
01085                                     FitPoint Tangent1, FitPoint Tangent2, BOOL IsStartCusp, BOOL IsEndCusp)
01086 
01087     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01088     Created:    7/3/94
01089     Inputs:     Start - the coord of the start point of the line
01090                 End - the coord of the end point of the line
01091                 Tangent1, Tangent2 - The tangents to the curve at the start and end points
01092                 IsStartCusp - TRUE if the start of this bezier is at a cusp
01093                 IsEndCusp - TRUE if the End of this bezier is at a cusp
01094     Purpose:    Inserts a straight curve into the path. It keeps it as a curve so that the
01095                 path will continue to look smooth after it is edited. If this is the last
01096                 Path element in the fit, (ie Depth is zero) then the rotate flag will not
01097                 be set.
01098     Scope:      private
01099 
01100 ********************************************************************************************/
01101 
01102 void CurveFitObject::InsertLine(const DocCoord& Start, const DocCoord& End,
01103                                 FitPoint Tangent1, FitPoint Tangent2, BOOL IsStartCusp, BOOL IsEndCusp)
01104 {
01105     // Prepare some flags
01106     PathFlags Flags;
01107     Flags.IsSelected = FALSE;
01108     Flags.IsSmooth = FALSE;
01109     Flags.IsRotate = TRUE;
01110 
01111     // Find out a third of the distance between the two points
01112     FitPoint StartPos(Start);
01113     FitPoint EndPos(End);
01114     FitPoint DistanceVect = EndPos - StartPos;
01115     INT32 Length = (INT32)DistanceVect.Length() / 3;
01116 
01117     // Make the tangents the right length
01118     Tangent1 = Tangent1.SetLength(Length);
01119     Tangent2 = Tangent2.SetLength(Length);
01120 
01121     // Work out the position of the control points
01122     StartPos = StartPos + Tangent1;
01123     EndPos = EndPos + Tangent2;
01124 
01125     // Add the line to the curve
01126     LongPath->InsertCurveTo( DocCoord( (INT32)StartPos.x, (INT32)StartPos.y ), 
01127                              DocCoord( (INT32)EndPos.x, (INT32)EndPos.y ),
01128                              End, &Flags);  
01129 
01130     // Deal with cusps
01131     if (IsStartCusp || IsEndCusp)
01132     {
01133         // Go find out about the path
01134         PathFlags* AllFlags = LongPath->GetFlagArray();
01135         INT32 NumCoords = LongPath->GetNumCoords();
01136 
01137         if (IsStartCusp)
01138         {
01139             // Patch up the flags of the bits near that start
01140             AllFlags[NumCoords-3].IsRotate = FALSE;
01141         }
01142     
01143         if (IsEndCusp)
01144         {
01145             // Patch up the flags of the bits near that end
01146             AllFlags[NumCoords-2].IsRotate = FALSE;
01147             AllFlags[NumCoords-1].IsRotate = FALSE;
01148         }
01149     }
01150 }
01151 
01152 
01153 
01154 
01155 /********************************************************************************************
01156 
01157 >   void CurveFitObject::InsertStraightLine(const DocCoord& End)
01158 
01159     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01160     Created:    25/4/94
01161     Inputs:     End - the coord of the end point of the line
01162     Purpose:    Inserts a straight Line into the path. This is used in cases where the
01163                 Line segment must be kept as a straight line (ie by using Straight Line
01164                 Mode of the FreeHand tool)
01165     Scope:      private
01166 
01167 ********************************************************************************************/
01168 
01169 void CurveFitObject::InsertStraightLine(const DocCoord& End)
01170 {
01171     // Prepare some flags
01172     PathFlags Flags;
01173     Flags.IsSelected = FALSE;
01174     Flags.IsRotate = FALSE;
01175     Flags.IsSmooth = FALSE;
01176 
01177     // Insert the line
01178     LongPath->InsertLineTo(End, &Flags);    
01179 }
01180 
01182 // FitCurveNoChangeGeometry implementation
01183 /********************************************************************************************
01184 
01185 >   void FitCurveNoChangeGeometry::SmoothPath(Path * pPath, double Error)
01186 
01187     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01188     Created:    12/1/2000
01189     Inputs:     pPath   -   The path to smooth
01190                 Error   -   The error to pass into Path::Smooth()
01191     Purpose:    Smooths the path, eliminating all unnecessary points
01192     Notes :     For example of use, see NodeCntr.cpp, GenerateContour() function
01193 
01194 ********************************************************************************************/
01195 void FitCurveNoChangeGeometry::SmoothPath(Path * pPath, double Error)
01196 {
01197     Path SubPath;
01198     SubPath.Initialise();
01199 
01200     Path QuantPath;
01201     QuantPath.Initialise();
01202 
01203     Path RetnPath;
01204     RetnPath.Initialise();
01205     
01206     // split the path into sub-paths
01207     INT32 NumSubPaths = pPath->GetNumSubpaths();
01208     for (INT32 i = 0; i < NumSubPaths ; i++)
01209     {
01210         // seperate out all sub-paths and smooth them individually
01211         SubPath.ClearPath();
01212         pPath->MakePathFromSubPath(i, &SubPath);
01213 
01214         // quantise this path
01215         QuantPath.ClearPath();
01216         SubPath.Quantise(20, &QuantPath);
01217 
01218         SmoothPathNoChangeGeometry(&QuantPath, Error);
01219         EliminateColinearPointsFromPath(&QuantPath);
01220         
01221         RetnPath.MergeTwoPaths(QuantPath);
01222     }
01223 
01224     pPath->ClearPath();
01225     pPath->CloneFrom(RetnPath);
01226 }
01227 
01228 /********************************************************************************************
01229 
01230 >   void FitCurveNoChangeGeometry::SmoothPathNoChangeGeometry(Path * pPath, double Error)
01231 
01232     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01233     Created:    12/1/2000
01234     Inputs:     pPath   -   The path to smooth (must have NO sub-paths)
01235                 Error   -   The error to pass into Path::Smooth()
01236     Purpose:    Smooths the path, eliminating all unnecessary points but maintaining
01237                 geometry as much as possible - doesn't eliminate colinear points
01238     Notes:      Assumes the path is flattened
01239 
01240 ********************************************************************************************/
01241 void FitCurveNoChangeGeometry::SmoothPathNoChangeGeometry(Path * pPath, double Error)
01242 {
01243     INT32 StartIndex = 0;
01244     INT32 EndIndex = 0;
01245 
01246     NormCoord Vec1;
01247     NormCoord Vec2;
01248 
01249     double dot = 0;
01250 
01251     // threshold for 30 degrees for the dot product
01252     const double Thres = cos(30.0 * 3.142 / 180.0);
01253     
01254     for (INT32 i = 0 ; i < pPath->GetNumCoords(); i++)
01255     {
01256         if (i < pPath->GetNumCoords() - 2)
01257         {
01258             Vec1.x = pPath->GetCoordArray()[i+1].x - pPath->GetCoordArray()[i].x;
01259             Vec1.y = pPath->GetCoordArray()[i+1].y - pPath->GetCoordArray()[i].y;
01260 
01261             Vec2.x = pPath->GetCoordArray()[i+2].x - pPath->GetCoordArray()[i+1].x;
01262             Vec2.y = pPath->GetCoordArray()[i+2].y - pPath->GetCoordArray()[i+1].y;
01263 
01264             Vec1.Normalise();
01265             Vec2.Normalise();
01266 
01267             dot = (Vec1.x * Vec2.x) + (Vec1.y * Vec2.y);
01268 
01269             if (dot < Thres)
01270             {
01271                 // don't smooth this !
01272             }
01273             else
01274             {
01275                 StartIndex = i;
01276                 
01277                 // find the region to smooth
01278                 while (dot > Thres && i < (pPath->GetNumCoords() - 2))
01279                 {
01280                     i++;
01281                     Vec1 = Vec2;
01282                     Vec2.x = pPath->GetCoordArray()[i+2].x - pPath->GetCoordArray()[i+1].x;
01283                     Vec2.y = pPath->GetCoordArray()[i+2].y - pPath->GetCoordArray()[i+1].y;
01284                     Vec2.Normalise();
01285 
01286                     dot = (Vec1.x * Vec2.x) + (Vec1.y * Vec2.y);
01287                 }
01288                  
01289                 i--;
01290 
01291                 EndIndex = i+1;
01292 
01293                 // ok, we've found the region to smooth so do it !
01294                 if (EndIndex > StartIndex && EndIndex < pPath->GetNumCoords())
01295                 {
01296                     pPath->SmoothSection(StartIndex, &EndIndex, Error, 0);
01297                 }
01298 
01299                 i = EndIndex+1;
01300             }
01301         }
01302     }
01303 }
01304 
01305 /********************************************************************************************
01306 
01307 >   void FitCurveNoChangeGeometry::EliminateColinearPointsFromPath(Path * pPath)
01308 
01309     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01310     Created:    12/1/2000
01311     Inputs:     pPath   -   The path to smooth (must have NO sub-paths)
01312     Purpose:    Eliminates
01313     Notes:      Do after SmoothPathNoChangeGeometry
01314 
01315 ********************************************************************************************/
01316 void FitCurveNoChangeGeometry::EliminateColinearPointsFromPath(Path * pPath)
01317 {
01318     // make sure the flags are initialised on the path
01319     pPath->InitialiseFlags();
01320     
01321     NormCoord Vec1, Vec2;
01322     double dot = 0;
01323 //  double OriginalDot = 0;
01324 
01325     BOOL bClose = FALSE;
01326 
01327     if ((pPath->GetVerbArray()[pPath->GetNumCoords() - 1] & PT_CLOSEFIGURE) != 0)
01328     {
01329         pPath->GetVerbArray()[pPath->GetNumCoords() - 1] -= PT_CLOSEFIGURE;
01330         bClose = TRUE;
01331     }
01332 
01333     INT32 StartIndex = 0;
01334     INT32 MidIndex = 0;
01335     INT32 EndIndex = 0;
01336 
01337     // threshold is one degree
01338     const double Thres = cos (1.0 * 3.142 / 180.0);
01339 
01340     BOOL bDelete = FALSE;
01341 
01342     for (INT32 i = 0 ; i < pPath->GetNumCoords() - 2; 
01343                 i ++)
01344     {
01345         // set up the start, mid & end points of the section to test
01346         StartIndex = -1;
01347         MidIndex = -1;
01348         EndIndex = -1;
01349 
01350         if (pPath->GetVerbArray()[i] == PT_MOVETO ||
01351             pPath->GetVerbArray()[i] == PT_LINETO)
01352         {
01353             StartIndex = i;
01354             MidIndex = i+1;
01355         }
01356         else if (pPath->GetVerbArray()[i] == PT_BEZIERTO)
01357         {
01358             if (pPath->GetFlagArray()[i].IsEndPoint)
01359             {
01360                 StartIndex = i;
01361                 MidIndex = i + 1;
01362             }
01363             else if (i < pPath->GetNumCoords()-1 && pPath->GetFlagArray()[i+1].IsEndPoint)
01364             {
01365                 StartIndex = i + 1;
01366                 MidIndex = i + 2;
01367             }
01368             else if (i < pPath->GetNumCoords()-2 && pPath->GetFlagArray()[i+2].IsEndPoint)
01369             {
01370                 StartIndex = i + 2;
01371                 MidIndex = i + 3;
01372             }
01373             else
01374             {
01375                 ERROR3("Path Flags not initialised");
01376             }
01377         }
01378         else
01379         {
01380             ERROR3("Unrecognised path element");
01381         }
01382 
01383         // so we now have the start & mid points, let's get the end point the same way
01384         if (StartIndex >= 0 && MidIndex >= 0)
01385         {
01386             if (pPath->GetVerbArray()[MidIndex] == PT_LINETO ||
01387                 pPath->GetVerbArray()[MidIndex] == PT_MOVETO)
01388             {
01389                 EndIndex = MidIndex + 1;
01390             }
01391             else if (pPath->GetVerbArray()[MidIndex] == PT_BEZIERTO)
01392             {
01393                 EndIndex = MidIndex + 3;
01394             }
01395             else
01396             {
01397                 ERROR3("Unrecognised path element");
01398             }
01399         }
01400 
01401         // if we have valid points, check all vectors imbetween
01402         if (StartIndex >= 0 && MidIndex >= 0 && EndIndex >= 0)
01403         {
01404             bDelete = TRUE;
01405 
01406             if (EndIndex < StartIndex + 2)
01407                 bDelete = FALSE;
01408 
01409             for (INT32 j = StartIndex; j <= EndIndex-2; j++)
01410             {
01411                 if (j == StartIndex)
01412                 {
01413                     Vec1.x = pPath->GetCoordArray()[j+1].x - pPath->GetCoordArray()[j].x;
01414                     Vec1.y = pPath->GetCoordArray()[j+1].y - pPath->GetCoordArray()[j].y;
01415                     Vec1.Normalise();
01416                 }
01417                 else
01418                 {
01419                     Vec1 = Vec2;
01420                 }
01421 
01422                 Vec2.x = pPath->GetCoordArray()[j+2].x - pPath->GetCoordArray()[j+1].x;
01423                 Vec2.y = pPath->GetCoordArray()[j+2].y - pPath->GetCoordArray()[j+1].y;
01424 
01425                 Vec2.Normalise();
01426 
01427                 dot = (Vec1.x * Vec2.x) + (Vec1.y * Vec2.y);
01428 
01429                 if (dot < Thres)
01430                 {
01431                     // angle is greater than the threshold, so don't delete this section
01432                     bDelete = FALSE;
01433                     break;
01434                 }
01435                 else
01436                 {
01437                     bDelete = TRUE;
01438                 }
01439             }
01440 
01441             if (bDelete)
01442             {
01443                 if (pPath->GetVerbArray()[MidIndex] == PT_LINETO)
01444                 {
01445                     // delete this point
01446                     pPath->DeleteSection(MidIndex, 1);
01447                 }
01448                 else if (pPath->GetVerbArray()[MidIndex] == PT_BEZIERTO)
01449                 {
01450                     pPath->DeleteSection(MidIndex, 3);
01451                 }
01452 
01453                 // start again at this point
01454                 i --;
01455             }
01456         }
01457     }   
01458 
01459     if (bClose)
01460     {
01461         pPath->GetVerbArray()[pPath->GetNumCoords() - 1] |= PT_CLOSEFIGURE;
01462     }
01463 }

Generated on Sat Nov 10 03:45:20 2007 for Camelot by  doxygen 1.4.4