paths.cpp

Go to the documentation of this file.
00001 // $Id: paths.cpp 1492 2006-07-20 19:19:48Z 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 // The functions that control paths
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 #if !defined(EXCLUDE_FROM_XARLIB)
00105 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 #endif
00107 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include <math.h>
00109 
00110 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "pathutil.h"
00112 //#include "handles.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "pathproc.h"
00114 #include "vector.h"
00115 
00116 #if !defined(EXCLUDE_FROM_XARLIB)
00117 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 #include "osrndrgn.h"
00119 #include "pen.h"
00120 //  #include "pathutil.h"
00121 #include "blobs.h"
00122 //  #include "fixmem.h"
00123 #include "fitcurve.h"
00124 #include "layermgr.h"
00125 #include "gclip.h"
00126 #include "grndrgn.h"
00127 #include "XaDraw.h"
00128 #include "cstroke.h"
00129 #include "lineattr.h"
00130 #include "attrmap.h"
00131 //  #include "handles.h"
00132 #endif
00133 
00134 // Declare smart memory handling in Debug builds
00135 #define new CAM_DEBUG_NEW
00136 
00137 // Put my version number into the about box
00138 DECLARE_SOURCE( "$Revision: 1492 $" );
00139 
00140 /********************************************************************************************
00141 
00142 >   PathFlags::PathFlags()
00143 
00144     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00145     Created:    02/03/94
00146     Purpose:    Path Flags Constructor. This sets all the path flags to FALSE ready for
00147                 use.
00148 
00149 ********************************************************************************************/
00150 
00151 PathFlags::PathFlags()
00152 {
00153     IsSelected = FALSE;
00154 
00155     // Flags to aid with the smoothing of curves
00156     IsSmooth = FALSE;
00157     IsRotate = FALSE;
00158 
00159     // Flags that help with the EORed rendering as the path is edited
00160     NeedToRender = FALSE;
00161 
00162     // All Endpoints in the path have this flag set
00163     IsEndPoint = FALSE;
00164 
00165     // May as well use up the spare bits in this byte for the future
00166     Spare1 = FALSE;
00167     Spare2 = FALSE;
00168     Spare3 = FALSE;
00169 }
00170 
00171 
00172 /********************************************************************************************
00173 
00174 >   Path::Path()
00175 
00176     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00177     Created:    28/01/94
00178     Purpose:    constructor - just sets a few things to sensible values
00179 
00180 ********************************************************************************************/
00181 
00182 Path::Path()
00183 {
00184     // We do not have any memory yet
00185     UnUsedSlots = 0;
00186     UsedSlots = 0;
00187     SlotInitSize = 0;
00188     SlotAllocSize = 0;
00189 
00190     // set all the handles to bad in case they are used
00191     VerbHandle = BAD_MHANDLE;
00192     CoordHandle = BAD_MHANDLE;
00193     FlagsHandle = BAD_MHANDLE;
00194 
00195     // Set the path flags
00196     IsFilled = FALSE;
00197     IsStroked = TRUE;
00198 
00199     // Set the current path position
00200     CurrentPos = 0;
00201 
00202     // Setup the Extra Info Channels
00203     ExtraInfo = NULL;
00204 
00205 #if !defined(EXCLUDE_FROM_XARLIB)
00206     // Set up the contouring variables
00207 //  m_pGPC = NULL;
00208 //  m_pGSC = NULL;
00209     m_ContourLength = 0;
00210     m_IsAnOuterContour = true;
00211     m_DoClosePath = false;
00212 
00213     // MRH New!
00214     m_ContourWidth = 100;
00215     m_ContourJoinS = JOIN_MITER;
00216     m_ContourCapS = CAPS_BUTT;
00217     m_ContourFlatness = 200.0;
00218 
00219     // Set the mitre limit to 10 shifted 16. It`s done in two parts as it can cause overflow
00220     // problems if done as one call.
00221     m_ContourMitreLimit = 10;
00222     m_ContourMitreLimit <<= 16;
00223     m_UseContourMode = TRUE;
00224 #endif
00225 }
00226 
00227 /********************************************************************************************
00228 
00229 >   Path::~Path()
00230 
00231     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00232     Created:    28/01/94
00233     Purpose:    Destructor - releases the memory that the path has been using to store coords
00234                 in.
00235 
00236 ********************************************************************************************/
00237 
00238 Path::~Path()
00239 {
00240     // Free the memory that the path is using to store its data in
00241     if (VerbHandle != BAD_MHANDLE)
00242         ReleaseBlock(VerbHandle);
00243     
00244     if (CoordHandle != BAD_MHANDLE)
00245         ReleaseBlock(CoordHandle);
00246 
00247     if (FlagsHandle != BAD_MHANDLE)
00248         ReleaseBlock(FlagsHandle);
00249 
00250     if (ExtraInfo != NULL)
00251         delete ExtraInfo;
00252 /*  
00253     if(m_pGPC != NULL)
00254     {
00255         delete m_pGPC;
00256         m_pGPC = NULL;
00257     }
00258 
00259     if(m_pGSC != NULL)
00260     {
00261         delete m_pGSC;
00262         m_pGSC = NULL;
00263     }
00264 */
00265 }
00266 
00267 
00268 
00269 /********************************************************************************************
00270 
00271 >   BOOL Path::Initialise(INT32 InitialSize, INT32 BlockSize)
00272 
00273     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00274     Created:    31/01/94
00275     Inputs:     InitialSize - The number of coords the path can hold initially
00276                 BlockSize - The number of coords to allocate for each time we run out of space
00277     Returns:    TRUE if we got all the memory we needed, FALSE if not
00278     Purpose:    Allocates memory in the path for the three arrays - The Verbs, Coordinates
00279                 and the Flags.
00280     Errors:     Can fail if it runs out of memory
00281 
00282 ********************************************************************************************/
00283 
00284 BOOL Path::Initialise(INT32 InitialSize, INT32 BlockSize)
00285 {
00286     // CGS (19/6/2000)  A few of camelots programmers seem to be assuming that calling initialise
00287     // on an already initialised path will clear out all previous pointers.  This is NOT the case
00288     // (or at least wasn't).  It is not unreasonable for the programmer to expect this to happen
00289     // (being viewed as a reinitialise), so I have added code to handle deletion automatically.
00290     // This should fix a considerable amount of camelots memory leaks!
00291 
00292     if (VerbHandle != BAD_MHANDLE)
00293         ReleaseBlock(VerbHandle);
00294     
00295     if (CoordHandle != BAD_MHANDLE)
00296         ReleaseBlock(CoordHandle);
00297 
00298     if (FlagsHandle != BAD_MHANDLE)
00299         ReleaseBlock(FlagsHandle);
00300     
00301     // The memory manager will not allocate blocks of less that 12 bytes and have to be multiples of 4!
00302     if (InitialSize<12)
00303     {
00304         InitialSize = 12;
00305     }
00306     else
00307     {
00308         // Make sure it is a multiple of 4, by shifting left 2 and then shifting it back again!
00309         InitialSize = (InitialSize+3) >> 2;
00310         InitialSize = InitialSize << 2;
00311     }
00312 
00313     if (BlockSize<12)
00314     {
00315         BlockSize = 12;
00316     }
00317     else
00318     {
00319         // Make sure it is a multiple of 4, by shifting left 2 and then shifting it back again!
00320         BlockSize = (BlockSize+3) >> 2;
00321         BlockSize = BlockSize << 2;
00322     }
00323 
00324     // try and get memory for the verbs
00325     VerbHandle = ClaimBlock(sizeof(PathVerb)*InitialSize);
00326     if (VerbHandle!=BAD_MHANDLE)
00327     {
00328         // try and get memory for the coords
00329         CoordHandle = ClaimBlock(sizeof(DocCoord)*InitialSize);
00330         if (CoordHandle != BAD_MHANDLE)
00331         {
00332             // try and get memory for the flags
00333             FlagsHandle = ClaimBlock(sizeof(PathFlags)*InitialSize);
00334             if (FlagsHandle != BAD_MHANDLE)
00335             {
00336                 // everything worked
00337                 UsedSlots = 0;
00338                 UnUsedSlots = InitialSize;
00339                 SlotInitSize = InitialSize;
00340                 SlotAllocSize = BlockSize;
00341             }
00342             else
00343             {
00344                 // failed to get the flag block, so release the other blocks and fail
00345                 ReleaseBlock( CoordHandle );
00346                 ReleaseBlock( VerbHandle );
00347                 return FALSE;
00348             }
00349         }
00350         else
00351         {
00352             // failed to get the coords block, so release the block we already have and fail
00353             ReleaseBlock( VerbHandle );
00354             return FALSE;
00355         }
00356     }
00357     else
00358     {
00359         // failed to get any memory, so fail
00360         return FALSE;
00361     }
00362 
00363     // a total success, so say so.
00364     return TRUE;
00365 }
00366 
00367 
00368 /********************************************************************************************
00369 
00370 >   BOOL Path::CopyPathDataFrom( Path* SrcPath )
00371 
00372     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00373     Created:    28/01/94
00374     Inputs:     SrcPath - The path that you want to copy data from
00375     Returns:    TRUE if it worked, FALSE otherwise. Can fail if there is not enough space
00376                 in the path to copy SrcPaths data into.             
00377     Purpose:    Copy the coords, verbs and flags from the SrcPath to this path. You must 
00378                 already have initialised this path by calling Path::Initialise(). Pass it
00379                 an Initial size big enough the hold the path you want to copy.
00380 
00381 ********************************************************************************************/
00382 
00383 BOOL Path::CopyPathDataFrom( Path* SrcPath )
00384 {
00385     // Ensure that a non-null path is being used.
00386     if ( SrcPath == NULL )
00387         return FALSE;
00388 
00389     // Find out how much space we need to alloc in this new path
00390     INT32 NumCoords = SrcPath->GetNumCoords();
00391 
00392     if (UsedSlots+UnUsedSlots < NumCoords)
00393     {
00394         // There is not enough space in this path to copy the SrcPath to
00395         //if (IsUserName("Rik")) TRACE( _T("Not enough space in path to copy data to\n") );
00396         ERROR3 ("Not enough space in path to copy data to!");
00397         return FALSE;
00398     }
00399 
00400     // copy the data across
00401     memcpy((void*)DescribeHandle(VerbHandle), (void*)DescribeHandle(SrcPath->VerbHandle), 
00402            (size_t)NumCoords*sizeof(PathVerb));
00403 
00404     memcpy((void*)DescribeHandle(CoordHandle), (void*)DescribeHandle(SrcPath->CoordHandle), 
00405            (size_t)NumCoords*sizeof(DocCoord));
00406 
00407     memcpy((void*)DescribeHandle(FlagsHandle), (void*)DescribeHandle(SrcPath->FlagsHandle), 
00408            (size_t)NumCoords*sizeof(PathFlags));
00409 
00410     // If the source path has extra info in it then we need to copy that too
00411     if (SrcPath->ExtraInfo != NULL) 
00412         ExtraInfo->CopyExtraInfo(SrcPath->ExtraInfo);
00413 
00414     // Set the vars that tell us how much mem has been used
00415     UnUsedSlots = (UsedSlots+UnUsedSlots) - NumCoords;
00416     UsedSlots = NumCoords;
00417 
00418     // copy any other flags of value
00419     IsFilled = SrcPath->IsFilled;
00420     IsStroked = SrcPath->IsStroked;
00421 
00422     // Keep track of the current path position
00423     CurrentPos = 0;
00424 
00425     // Well, as far as I can tell its worked
00426     return TRUE;
00427 }
00428 
00429 
00430 /********************************************************************************************
00431 
00432     BOOL Path::CopyPathDataFrom( DocCoord* Coords, PathVerb* Verbs, INT32 NumCoords, 
00433                                  BOOL IsFilled = FALSE, BOOL IsStroked = TRUE)
00434 
00435     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00436     Created:    03/02/94
00437     Inputs:     Coords - The path coords you want to copy
00438                 Verbs - The verbs you want to copy
00439                 NumCoords - The number of coords to copy
00440                 IsFilled - Is the path filled ?
00441                 IsStroked - Is the path stroked ?
00442 
00443     Returns:    TRUE if it worked, FALSE otherwise. Can fail if there is not enough space
00444                 in the path to copy the data into.          
00445     Purpose:    Copy the specified coords and verbs to this path. The path's flags will 
00446                 be the defaults as specified in the constructors. You must 
00447                 already have initialised this path by calling Path::Initialise(). Pass it
00448                 an Initial size big enough to hold the path you want to copy.
00449 
00450 ********************************************************************************************/
00451 
00452 BOOL Path::CopyPathDataFrom( DocCoord* Coords, PathVerb* Verbs, INT32 NumCoords, 
00453                              BOOL Filled, BOOL Stroked)
00454 {
00455 
00456     // Check that this path has enough slots
00457     if (UsedSlots+UnUsedSlots < NumCoords)
00458     {
00459         // There is not enough space in this path to copy the SrcPath to
00460         TRACEUSER( "Simon", _T("Not enough space in path to copy data to\n") );
00461         return FALSE;
00462     }
00463 
00464     // copy the data across
00465     memcpy((void*)DescribeHandle(VerbHandle), (void*)Verbs, 
00466            (size_t)NumCoords*sizeof(PathVerb));
00467 
00468     memcpy((void*)DescribeHandle(CoordHandle), (void*)Coords, 
00469            (size_t)NumCoords*sizeof(DocCoord));
00470 
00471     // Every point on the path has the default constructor flags set
00472     PathFlags f;
00473     PathFlags* pFlags = (PathFlags*) DescribeHandle(FlagsHandle); 
00474 
00475     for (INT32 i = 0; i< NumCoords; i++)
00476     {
00477         pFlags[i] = f;      
00478     }
00479 
00480     // Set the vars that tell us how much mem has been used
00481     UnUsedSlots = (UsedSlots+UnUsedSlots) - NumCoords;
00482     UsedSlots = NumCoords;
00483 
00484     // Keep track of the current path position
00485     CurrentPos = 0; 
00486 
00487     IsFilled = Filled; 
00488     IsStroked = Stroked;
00489 
00490     // Well, as far as I can tell its worked
00491     return TRUE;
00492 
00493 } 
00494 
00495 /********************************************************************************************
00496 
00497 >   BOOL Path::CreatePathFromDocRect(DocRect* pRect)
00498 
00499     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
00500     Created:    20/7/99
00501     Outputs:    BOOL to say whether or not it was successful
00502     Purpose:    Creates a filled path from a given DocRect
00503 
00504 ********************************************************************************************/
00505 
00506 BOOL Path::CreatePathFromDocRect(DocRect* pRect)
00507 {
00508     DocCoord TopLeft(pRect->lo.x,pRect->hi.y);
00509     DocCoord TopRight(pRect->hi.x,pRect->hi.y);
00510     DocCoord BottomLeft(pRect->lo.x,pRect->lo.y);
00511     DocCoord BottomRight(pRect->hi.x,pRect->lo.y);
00512 
00513     InsertMoveTo(TopLeft);
00514     InsertLineTo(TopRight);
00515     InsertLineTo(BottomRight);
00516     InsertLineTo(BottomLeft);
00517     InsertLineTo(TopLeft);
00518 
00519     IsFilled = TRUE;
00520 
00521     return TRUE;
00522 }
00523 
00524 
00525 /********************************************************************************************
00526 
00527 >   void Path::FindStartOfPath( PathPosition* Pos )
00528 
00529     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00530     Created:    31/01/94
00531     Outputs:    Pos - Holds the position of the start of the path
00532     Purpose:    Sets Pos to the first element in the path
00533 
00534 ********************************************************************************************/
00535 
00536 BOOL Path::FindStartOfPath()
00537 {
00538     // set the position to the start of the path
00539     CurrentPos = 0;
00540 
00541     // see if this a valid path position
00542     if (UsedSlots==0)
00543         return FALSE;
00544     else
00545         return TRUE;
00546 }
00547 
00548 
00549 /********************************************************************************************
00550 
00551 >   void Path::FindStartOfSubPath()
00552 
00553     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00554     Created:    31/01/94
00555     Inputs:     -
00556     Outputs:    -
00557     Purpose:    Searches backwards from the current position until it finds the
00558                 start of the sub path that the current position is in.
00559     Errors:     ENSUREs if the start of the path does not seem to be a MoveTo
00560 
00561 ********************************************************************************************/
00562 
00563 void Path::FindStartOfSubPath()
00564 {
00565     ENSURE( CurrentPos<UsedSlots, "Path position was not valid in FindStartOfSubPath" );
00566 
00567     // I am already in a path, so start working backwards until I find the moveto
00568     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00569 
00570     while ((CurrentPos>0) && (Verbs[CurrentPos]!=PT_MOVETO))
00571         CurrentPos--;
00572 
00573     // The index will either be pointing at a moveto or the begining of the path
00574     // Either way, it should be a moveto
00575     ENSURE( (Verbs[CurrentPos]==PT_MOVETO), "Did not find a start to this sub path" );
00576 }
00577 
00578 /********************************************************************************************
00579 
00580 >   void Path::FindStartOfSubPath( INT32* Index )
00581 
00582     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00583     Created:    7/7/94
00584     Inputs:     Index - the index of the current element (pointer!)
00585     Outputs:    Index - Holds the position of the start of the sub path
00586     Purpose:    Searches backwards from the current position from Index until it find the 
00587                 start of the subpath (always a moveto).
00588     Errors:     ENSUREs if the start of the path does not seem to be a MoveTo
00589 
00590 ********************************************************************************************/
00591 
00592 void Path::FindStartOfSubPath(INT32* Index) const
00593 {
00594     ENSURE( ((*Index)<UsedSlots && (Index)>=0), "Path position was not valid in FindStartOfSubPath" );
00595 
00596     // I am already in a path, so start working backwards until I find the moveto
00597     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00598 
00599     while (((*Index)>0) && (Verbs[*Index]!=PT_MOVETO))
00600         (*Index)--;
00601 
00602     // The index will either be pointing at a moveto or the begining of the path
00603     // Either way, it should be a moveto
00604     ENSURE( (Verbs[(*Index)]==PT_MOVETO), "Did not find a start to this sub path" );
00605 }
00606 
00607 
00608 /********************************************************************************************
00609 
00610 >   void Path::FindEndOfSubPath()
00611 
00612     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00613     Created:    31/01/94
00614     Inputs:     Pos - The position in the path to start looking from
00615     Outputs:    Pos - the position of the end of the subpath
00616     Purpose:    Searches forwards until it finds the end of the current sub-path. ie pos will
00617                 point at the last element in the sub path
00618 
00619 ********************************************************************************************/
00620 
00621 void Path::FindEndOfSubPath()
00622 {
00623     ENSURE( CurrentPos<UsedSlots, "Path position was not valid in FindEndOfSubPath" );
00624 
00625     // I am already in a path, so start working backwards until I find the moveto
00626     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00627     INT32 PrevIndex;
00628 
00629     do
00630     {
00631         PrevIndex = CurrentPos;
00632         FindNext();
00633     } while ((CurrentPos<UsedSlots) && (Verbs[CurrentPos]!=PT_MOVETO));
00634 
00635     // So we have either fallen off the end of the world, or we are now on a MoveTo
00636     // either way we need the info from the previous position
00637     CurrentPos = PrevIndex;
00638 }
00639 
00640 /********************************************************************************************
00641 
00642 >   void Path::FindEndOfSubPath(INT32* Index)
00643 
00644     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00645     Created:    31/01/94
00646     Inputs:     Index - The position in the path to start looking from
00647     Outputs:    Index - the position of the end of the subpath
00648     Purpose:    Searches forwards until it finds the end of the current sub-path. ie Index will
00649                 point at the last element in the sub path
00650 
00651 ********************************************************************************************/
00652 
00653 void Path::FindEndOfSubPath(INT32* Index) const
00654 {
00655     ENSURE( (*Index)<UsedSlots, "Path position was not valid in FindEndOfSubPath" );
00656 
00657     // I am already in a path, so start working backwards until I find the moveto
00658     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00659     INT32 PrevIndex;
00660 
00661     do
00662     {
00663         PrevIndex = (*Index);
00664         FindNext(Index);
00665     } while (((*Index)<UsedSlots) && (Verbs[(*Index)]!=PT_MOVETO));
00666 
00667     // So we have either fallen off the end of the world, or we are now on a MoveTo
00668     // either way we need the info from the previous position
00669     (*Index) = PrevIndex;
00670 }
00671 
00672 /********************************************************************************************
00673 
00674 >   BOOL Path::GetSubPathEnds(DocCoord* start, DocCoord* end)
00675 
00676     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00677     Created:    15/3/94
00678     Inputs:     -
00679     Outputs:    start, end are the coords of the start and end (if function returned TRUE)
00680     Returns:    TRUE if subpath is open, FALSE otherwise
00681                 CurrentPos in the path is set to the end of the subpath.
00682     Purpose:    This function returns whether the path is open or closed. This is independent
00683                 of the IsFilled flag, because some programs allow open paths to be filled.
00684                 Basically, if there's no gap in the subpath, it's closed, otherwise it's open.
00685                 These are the same rules as ArtWorks followed.
00686                 Initial implementation just scans the subpath and checks the last element 
00687                 in the subpath.
00688     Errors:     -
00689     SeeAlso:    -
00690 
00691 ********************************************************************************************/
00692 
00693 BOOL Path::GetSubPathEnds(DocCoord* start, DocCoord* end)
00694 {
00695     DocCoord temp1;
00696 
00697     FindStartOfSubPath();
00698     temp1 = GetCoord();
00699     FindEndOfSubPath();
00700 
00701 // Find out if this path is really closed. If the PT_CLOSEFIGURE flag is set in the end
00702 // verb, the path is closed, so return FALSE, otherwise return TRUE, with *start and *end 
00703 // set up accordingly.
00704 
00705     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00706     if (Verbs[CurrentPos] & PT_CLOSEFIGURE)
00707     {
00708         return FALSE;
00709     }
00710     else
00711     {
00712         *end = GetEndPoint();
00713         *start = temp1;
00714         return TRUE;
00715     }
00716 }
00717 
00718 
00719 
00720 /********************************************************************************************
00721 
00722 >   BOOL Path::FindNext()
00723 
00724     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00725     Created:    31/01/94
00726     Purpose:    Finds the next path element in this path. The position will become invalid
00727                 if you try to search past the start of the path. This can be checked by
00728                 using Pos->IsValid.
00729     SeeAlso:    Path::FindPrev
00730 
00731 ********************************************************************************************/
00732 
00733 BOOL Path::FindNext()
00734 {
00735     ENSURE( CurrentPos<=UsedSlots, "Path position was not valid in FindNext" );
00736     ENSURE( CurrentPos>=0, "Path Position less than zero!" );
00737 
00738     // de-reference the verb array
00739     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00740 
00741     // Move to the next path element
00742     if ((Verbs[CurrentPos] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
00743         CurrentPos += 3;
00744     else
00745         CurrentPos ++;
00746 
00747     // Check to see if we fell off the end of the path
00748     if (CurrentPos>=UsedSlots)
00749     {
00750         CurrentPos = UsedSlots;
00751         return FALSE;
00752     }
00753 
00754     // we did it, so return happy
00755     return TRUE;
00756 }
00757 
00758 
00759 /********************************************************************************************
00760 
00761 >   BOOL Path::FindNextEndPoint(INT32* index) const
00762 
00763     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00764     Created:    3/11/94
00765     Inputs:     Index   = pointer to a INT32 indicating where to start from
00766     Outpus:     Index   = points to an index which is the next element
00767     Returns:    TRUE    if the next end point has been found
00768                 FALSE   if there are no more end points in this path
00769     Purpose:    Finds the next path endpoint in this path
00770     SeeAlso:    Path::FindPrevEndPoint
00771 
00772 ********************************************************************************************/
00773 
00774 BOOL Path::FindNextEndPoint(INT32* index) const
00775 {
00776     ENSURE( (*index)<=UsedSlots, "Path position was not valid in FindNextEndPoint" );
00777     ENSURE( (*index)>=0, "Path Position less than zero in FindNextEndPoint" );
00778 
00779     // de-reference the verb array
00780     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00781 
00782     (*index)++;
00783 
00784     if ((*index) >= UsedSlots)
00785     {
00786         ((*index) = UsedSlots-1);
00787         return FALSE;
00788     }
00789 
00790     if ((Verbs[(*index)] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
00791         (*index) += 2;
00792 
00793     return TRUE;
00794 }
00795 
00796 
00797 
00798 /********************************************************************************************
00799 
00800 >   BOOL Path::FindPrevEndPoint(INT32* index)
00801 
00802     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00803     Created:    5/1/95
00804     Inputs:     Index   = pointer to a INT32 indicating where to start from
00805     Outpus:     Index   = points to an index which is the previous endpoint
00806     Returns:    TRUE    if the previous end point has been found
00807                 FALSE   if there are no more end points in this path
00808     Purpose:    Finds the previous path endpoint in this path
00809     Errors:     ERROR2's if Index is beyond the end of the path, OR before the start
00810     SeeAlso:    Path::FindNextEndPoint
00811 
00812 ********************************************************************************************/
00813 
00814 BOOL Path::FindPrevEndPoint(INT32* index) const
00815 {
00816     ERROR2IF(*index > UsedSlots, FALSE, "Path position was not valid in FindPrevEndPoint" );
00817     ERROR2IF(*index < 0, FALSE, "Path Position less than zero in FindPrevEndPoint" );
00818 
00819     // de-reference the verb array
00820     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00821 
00822     if ((Verbs[*index] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
00823         *index -= 3;
00824     else
00825         *index -= 1;
00826 
00827     if (*index < 0)
00828     {
00829         *index = 0;
00830         return FALSE;
00831     }
00832 
00833     return TRUE;
00834 }
00835 
00836 
00837 
00838 /********************************************************************************************
00839 
00840 >   BOOL Path::FindNext(INT32* Index) const
00841 
00842     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00843     Created:    31/01/94
00844     Inputs:     Index is a pointer to an index into the path to start from
00845     Outputs:    Index will point at the next element in the path
00846     Returns:    TRUE if valid next element found
00847                 FALSE if there is no next element, Index is set to NumCoords
00848     Purpose:    Finds the next path element in this path. The position will become invalid
00849                 if you try to search past the start of the path. This can be checked by
00850                 using Pos->IsValid.
00851     SeeAlso:    Path::FindPrev
00852 
00853 ********************************************************************************************/
00854 
00855 BOOL Path::FindNext(INT32* Index) const
00856 {
00857     ENSURE( (*Index)<=UsedSlots, "Path position was not valid in FindNext" );
00858     ENSURE( (*Index)>=0, "Path Position less than zero!" );
00859 
00860     // de-reference the verb array
00861     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00862 
00863     // Move to the next path element
00864     if ((Verbs[(*Index)] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
00865         (*Index) += 3;
00866     else
00867         (*Index) ++;
00868 
00869     // Check to see if we fell off the end of the path
00870     if ((*Index)>=UsedSlots)
00871     {
00872         (*Index) = UsedSlots;
00873         return FALSE;
00874     }
00875 
00876     // we did it, so return happy
00877     return TRUE;
00878 }
00879 
00880 
00881 /********************************************************************************************
00882 
00883 >   void Path::FindPrev()
00884 
00885     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00886     Created:    31/01/94
00887     Inputs:     -
00888     Outputs:    -
00889     Returns:    TRUE if valid previous element found
00890                 FALSE if there is no previous element
00891     Purpose:    Finds the prev path element in this path starting the search from CurrentPos 
00892     SeeAlso:    Path::FindNext
00893 
00894 ********************************************************************************************/
00895 
00896 BOOL Path::FindPrev()
00897 {
00898     ENSURE( CurrentPos<=UsedSlots, "Path position was not valid in FindPrev" );
00899     ENSURE( CurrentPos>=0, "Path Position less than zero!" );
00900 
00901     // de-reference the verb array
00902     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00903 
00904     // Move to the prev path element
00905     if (CurrentPos==UsedSlots)
00906     {
00907         // The current pos is at the end of the path, so move to the last element in the path
00908         CurrentPos --;
00909     }
00910     else
00911     {
00912         // see if we were now in the middle of a curve
00913         CurrentPos--;
00914 
00915         if ((Verbs[CurrentPos] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
00916             CurrentPos -= 2;
00917     }
00918 
00919     // Make sure we are still in bounds
00920     if (CurrentPos<0)
00921     {
00922         // we are not, so set the current pos back to the start and fail
00923         CurrentPos = 0;
00924         return FALSE;
00925     }
00926 
00927     // we are in the path, so succeed
00928     return TRUE;
00929 }
00930 
00931 
00932 
00933 /********************************************************************************************
00934 
00935 >   void Path::FindPrev(INT32 *index) const
00936 
00937     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00938     Created:    3/11/94
00939     Inputs:     Index is a pointer to an index into the path to start from
00940     Outputs:    Index will point at the previous element in the path
00941     Returns:    TRUE if valid previous element found
00942                 FALSE if there is no previous element
00943     Purpose:    Finds the previous path element in this path starting from the value (index) 
00944     SeeAlso:    Path::FindNext
00945 
00946 ********************************************************************************************/
00947 
00948 BOOL Path::FindPrev(INT32* index) const
00949 {
00950     ENSURE( (*index)<=UsedSlots, "Path position was not valid in FindPrev" );
00951     ENSURE( (*index)>=0, "Path Position less than zero!" );
00952 
00953     // de-reference the verb array
00954     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
00955 
00956     // Move to the prev path element
00957     if ((*index)==UsedSlots)
00958     {
00959         // The current pos is at the end of the path, so move to the last element in the path
00960         (*index) --;
00961     }
00962     else
00963     {
00964         // see if we were now in the middle of a curve
00965         (*index)--;
00966 
00967         if ((Verbs[(*index)] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
00968             (*index) -= 2;
00969     }
00970 
00971     // Make sure we are still in bounds
00972     if ((*index)<0)
00973     {
00974         // we are not, so set the current pos back to the start and fail
00975         (*index) = 0;
00976         return FALSE;
00977     }
00978 
00979     // we are in the path, so succeed
00980     return TRUE;
00981 }
00982 
00983 
00984 
00985 
00986 
00987 
00988 /********************************************************************************************
00989 
00990 >   INT32 Path::GetPathPosition()
00991 
00992     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00993     Created:    21/02/94
00994     Returns:    The Coord number in the path that the Current Path Position is at
00995     Purpose:    Find out where in the path we are
00996 
00997 ********************************************************************************************/
00998 
00999 INT32 Path::GetPathPosition()
01000 {
01001     return CurrentPos;
01002 }
01003 
01004 
01005 
01006 /********************************************************************************************
01007 
01008 >   void Path::SetPathPosition(INT32 NewPos)
01009 
01010     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01011     Created:    21/02/94
01012     Inputs:     NewPos - the Position you want to go to
01013     Purpose:    Sets the current path position to the value supplied. This represents the
01014                 Coord number in the path
01015 
01016 ********************************************************************************************/
01017 
01018 void Path::SetPathPosition(INT32 NewPos)
01019 {
01020     // Set the pos to the position supplied
01021     CurrentPos = NewPos;
01022 
01023     // make sure it was in bounds
01024     if (CurrentPos>=UsedSlots)
01025         CurrentPos = UsedSlots-1;
01026 
01027     if (CurrentPos<0)
01028         CurrentPos = 0;
01029 }
01030 
01031 
01032 
01033 
01034 /********************************************************************************************
01035 
01036 >   BOOL Path::IsInPath()
01037 
01038     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01039     Created:    11/02/94
01040     Returns:    TRUE if the current position is still in the path
01041                 FALSE if it has gone past the last element in the path
01042     Purpose:    Determine if the path current position is still inside the path
01043 
01044 ********************************************************************************************/
01045 BOOL Path::IsInPath()
01046 {
01047     // We are still in the path if the current position has not gone past the last valid
01048     // item in the path
01049     return ((CurrentPos>=0) && (CurrentPos<UsedSlots));
01050 }
01051 
01052 /********************************************************************************************
01053 
01054 >   INT32 Path::ComparePathToPath(Path* pComparePath, BOOL QuickCheck = TRUE)
01055 
01056     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> howitt
01057     Created:    05/12/00
01058     Inputs:     A path to compare against.
01059                 A flag to say wether to return as soon as it finds a difference (Default TRUE)
01060     Returns:    The difference in number of coords + or -.
01061     Purpose:    Compares athis path to a passed in path. It firswt does a quick check on the
01062                 number of coords and if they are equal does a more thoughour check on the 
01063                 verb array and coord array.
01064 
01065 ********************************************************************************************/
01066 INT32 Path::ComparePathToPath(Path* pComparePath, BOOL QuickCheck)
01067 {
01068     // Setup a default return value. This is basically the negative value of this paths
01069     // number of coords!
01070     INT32 ThisPathLength = (INT32)GetNumCoords();
01071     INT32 Difference = -ThisPathLength;
01072 
01073     // Only do any comparing if we`ve got valid pointers
01074     if(pComparePath)
01075     {
01076         // quick check first!
01077         if(ThisPathLength == pComparePath->GetNumCoords())
01078         {
01079             // Get the Coord array and Verb array for both paths.
01080             DocCoord* SPoint = GetCoordArray();
01081             DocCoord* CPoint = pComparePath->GetCoordArray();
01082             PBYTE SVerb = GetVerbArray();
01083             PBYTE CVerb = pComparePath->GetVerbArray();
01084 
01085             // Reset the Difference Value
01086             Difference = 0;
01087             
01088             // Now go throught each element of the arrays calculating the differences
01089             for( INT32 i = 0; i < ThisPathLength; i++)
01090             {
01091                 // If either the points OR verbs are different then increment the Diff counter
01092                 if(SPoint[i] != CPoint[i] || SVerb[i] != CVerb[i])
01093                 {
01094                     Difference++;
01095                     if(QuickCheck)
01096                         break;
01097                 }
01098             }
01099         }
01100         else
01101         {
01102             // Ok! there`s different so calcualte the difference
01103             Difference = ThisPathLength - (INT32)pComparePath->GetNumCoords();
01104         }
01105     }
01106 
01107     // return the Differance
01108     return Difference;
01109 }
01110 
01111 /********************************************************************************************
01112 
01113 >   PathVerb Path::GetVerb() const
01114 
01115     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01116     Created:    31/01/94
01117     Returns:    The verb at the current path position
01118     Purpose:    finds the verb associated with the path position
01119     Errors:     Ensures if the position if not valid or out of bounds
01120 
01121 ********************************************************************************************/
01122 PathVerb Path::GetVerb() const
01123 {
01124     ENSURE( CurrentPos<UsedSlots, "Index out of bounds in GetVerb" );
01125 
01126     // de-reference the verb array
01127     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
01128 
01129     // return the verb with the close path bit removed
01130     return ((Verbs[CurrentPos]) & (~PT_CLOSEFIGURE));
01131 }
01132 
01133 
01134 /********************************************************************************************
01135 
01136 >   DocCoord Path::GetCoord() const
01137 
01138     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01139     Created:    31/01/94
01140     Returns:    The DocCoord at the Current Path position
01141     Purpose:    finds the DocCoord associated with the path position
01142     Errors:     Ensures if the position if not valid or out of bounds
01143 
01144 ********************************************************************************************/
01145 
01146 DocCoord Path::GetCoord() const
01147 {
01148     ENSURE( CurrentPos<UsedSlots, "Index out of bounds in Path::GetCoord" );
01149 
01150     // de-reference the coord array
01151     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01152 
01153     return Coords[CurrentPos];
01154 }
01155 
01156 /********************************************************************************************
01157 
01158 >   PathFlag Path::GetFlags() const
01159 
01160     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01161     Created:    31/01/94
01162     Returns:    The flag at the current path position
01163     Purpose:    finds the flags associated with the path position
01164     Errors:     Ensures if the position if not valid or out of bounds
01165 
01166 ********************************************************************************************/
01167 
01168 PathFlags Path::GetFlags() const
01169 {
01170     ENSURE( CurrentPos<UsedSlots, "Index out of bounds in GetFlags" );
01171 
01172     // de-reference the flag array
01173     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
01174 
01175     return Flags[CurrentPos];   
01176 }
01177 
01178 
01179 
01180 
01181 /********************************************************************************************
01182 
01183 >   PathTypeEnum Path::GetPathType()
01184 
01185     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01186     Created:    31/1/95
01187     Inputs:     -
01188     Outputs:    -
01189     Returns:    Path type
01190     Purpose:    This determines whether the path is a line or a shape.
01191                 It does this by seeing if it has a PT_CLOSEFIGURE bit set in its last verb.
01192                 
01193 ********************************************************************************************/
01194 
01195 PathTypeEnum Path::GetPathType() const
01196 {
01197     PathVerb* pVerbs = GetVerbArray();
01198 
01199     if (pVerbs[UsedSlots-1] & PT_CLOSEFIGURE)
01200         return (PATHTYPE_SHAPE);
01201     else
01202         return (PATHTYPE_LINE);
01203 }
01204 
01205 
01206 /********************************************************************************************
01207 
01208 >   INT32 Path::FindOppositeControlPoint(INT32 ThisIndex)
01209 
01210     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01211     Created:    18/8/94
01212     Returns:    The index of the control point opposite the indexed one (or -1)
01213     Purpose:    Given a bezier control point, this function returns the index of
01214                 the control point that is opposite this one (with an endpoint between).
01215                 This routine takes closed paths into account.
01216 
01217 ********************************************************************************************/
01218 
01219 INT32 Path::FindOppositeControlPoint(INT32 ThisIndex)
01220 {
01221     // Get arrays of flags and verbs
01222     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
01223     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
01224 
01225     // If this isn't a control point, ensure!
01226     ENSURE(!Flags[ThisIndex].IsEndPoint,"Point is not a control point in FindOppositeControlPoint");
01227 
01228     // First see if this is the first or second control point in a curve element. If the 
01229     // previous point is an endpoint, then this is the first control point
01230     if (Flags[ThisIndex-1].IsEndPoint)
01231     {
01232         // if the previous endpoint is a moveto, we might have to wrap
01233         if (Verbs[ThisIndex-1] == PT_MOVETO)
01234         {
01235             INT32 i;
01236             for (i = ThisIndex;i<UsedSlots && Verbs[i]!=PT_MOVETO;i++); // ; deliberate
01237             i--;
01238             // Now i gives us the endpoint of the subpath
01239             // If the path is closed and the last point is a curve, return the opposite point
01240             // otherwise return -1
01241             if ((Verbs[i] & PT_CLOSEFIGURE) && ((Verbs[i] & ~PT_CLOSEFIGURE)==PT_BEZIERTO))
01242                 return i-1;
01243             else
01244                 return -1;
01245         }
01246         else
01247         {
01248             if ((Verbs[ThisIndex-1] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
01249                 return ThisIndex-2;
01250             else
01251                 return -1;
01252         }
01253     }
01254     else
01255     {
01256         // This is the second control point. If the next point has the CloseFigure flag
01257         // set we'll have to scan back. Otherwise, return ThisIndex+2 unless the next element 
01258         // isn't a curve, or there is no next element.
01259         if (Verbs[ThisIndex+1] & PT_CLOSEFIGURE)
01260         {
01261             // The path is closed - scan back for the moveto
01262             INT32 i;
01263             for (i=ThisIndex;Verbs[i] != PT_MOVETO;i--);    // ; deliberate
01264 
01265             // Move to the next point - if it's a BezierTo we return it, otherwise return -1
01266             if (Verbs[i+1] == PT_BEZIERTO)
01267                 return i+1;
01268             else
01269                 return -1;
01270         }
01271         else
01272         {
01273             // The path isn't closed here, so see if there's another element
01274             if (ThisIndex+2 >= UsedSlots || Verbs[ThisIndex+2] != PT_BEZIERTO)
01275             {
01276                 // No next element so return -1
01277                 return -1;
01278             }
01279             else
01280             {
01281                 // Next element is also a curve so return index of control point
01282                 return ThisIndex+2;
01283             }
01284         }
01285     }
01286 }
01287 
01288 /********************************************************************************************
01289 
01290 >   INT32 Path::GetPathByteLength()
01291 
01292     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01293     Created:    31/01/94
01294     Returns:    The Length of the path in bytes
01295     Purpose:    Calculates the length of the path in bytes. This figures includes the size of
01296                 the 3 arrays (Coordinates, Verbs and Flags) only.
01297 
01298 ********************************************************************************************/
01299 
01300 INT32 Path::GetPathByteLength() const
01301 {
01302     INT32 Total;
01303 
01304     // Add up the size of each of the Arrays
01305     Total  = UsedSlots * sizeof(DocCoord);
01306     Total += UsedSlots * sizeof(PathVerb);
01307     Total += UsedSlots * sizeof(PathFlags);
01308 
01309     return Total;
01310 }
01311 
01312 
01313 /********************************************************************************************
01314 
01315 >   INT32 Path::GetNumElements() const
01316 
01317     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01318     Created:    30/11/94
01319     Returns:    The number of path elements in all sub paths.
01320     Purpose:    This function counts the number of elements in the path.
01321                 This ignores MoveTos, hence giving you the total number of visible path elements
01322                 (i.e. all the lines and curves).
01323 
01324 ********************************************************************************************/
01325 
01326 INT32 Path::GetNumElements() const
01327 {
01328     // The routine relies on the path beginning with a MoveTo
01329     PathVerb* pVerbs = (PathVerb*) DescribeHandle(VerbHandle);
01330     INT32 Index=0,Count=0;
01331     while (FindNextEndPoint(&Index))
01332     {
01333         if ((pVerbs[Index] & ~PT_CLOSEFIGURE) != PT_MOVETO)
01334             Count++;
01335     }
01336 
01337     return Count;
01338 }
01339 
01340 /********************************************************************************************
01341 
01342 >   INT32 Path::GetNumSubpaths() const
01343 
01344     Author:     Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com>
01345     Created:    22/4/97
01346     Returns:    The number of subpaths in this path
01347     Purpose:    Gets the number of subpaths in this path
01348 
01349 ********************************************************************************************/
01350 
01351 INT32 Path::GetNumSubpaths() const
01352 {
01353     //This variable will tell us our current position in the path
01354     INT32 lCurPos=0;
01355 
01356     //And this will keep a count of the subpaths we find
01357     INT32 lSubpaths=0;
01358 
01359     //Now, while we are still in the path
01360     while (lCurPos<GetNumCoords())
01361     {
01362         //Set lCurPos to the end of the subpath we are in
01363         FindEndElOfSubPath(&lCurPos);
01364 
01365         //Add one to the number of subpaths found
01366         lSubpaths++;
01367 
01368         //And move the current position on by one place, to move it
01369         //either into the next subpath or off the end of the path
01370         lCurPos++;
01371     }
01372 
01373     //And return the number of subpaths we found
01374     return lSubpaths;
01375     
01376 }
01377 
01378 
01379 
01380 /********************************************************************************************
01381 
01382 >   INT32 Path::GetNumCoords()
01383 
01384     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01385     Created:    31/01/94
01386     Returns:    The number of coordinates in the path
01387     Purpose:    This returns the number of coordinates that make the path up
01388 
01389 ********************************************************************************************/
01390 
01391 INT32 Path::GetNumCoords() const
01392 {
01393     return UsedSlots;
01394 }
01395 
01396 
01397 
01398 /********************************************************************************************
01399 
01400 >   INT32 Path::GetNumSelEndPoints() const
01401 
01402     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01403     Created:    25/10/94
01404     Returns:    Number of selected endpoints within this path
01405     Purpose:    determine how many selected endpoints there are within this path. Useful
01406                 indirectly for finding whether there's a subselection.
01407     SeeAlso:    IsSubSelected()
01408                 
01409 ********************************************************************************************/
01410 
01411 INT32 Path::GetNumSelEndPoints() const
01412 {
01413     // We need to find all the selected control points in the path
01414     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
01415     INT32 count = 0;
01416 
01417     // look for selected coords
01418     for (INT32 i=0; i<UsedSlots; i++)
01419     {
01420         if ((Flags[i].IsSelected) && (Flags[i].IsEndPoint))
01421             count++;
01422     }
01423     return count;
01424 }
01425 
01426 
01427 
01428 /********************************************************************************************
01429 
01430 >   INT32 Path::GetNumEndPoints()
01431 
01432     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01433     Created:    12/4/94
01434     Returns:    The number of open ends to the path.
01435     Purpose:    Goes through the path and counts all open ends in the path. For example,
01436                 a straight line has 2 open ends, a closed shape has no open ends and a 
01437                 complex path could have any number of open ends. You should use this function
01438                 before calling GetAllOpenEnds that will fill an array for their coords
01439                 for you.
01440     SeeAlso:    Path::GetAllOpenEnds
01441 
01442 ********************************************************************************************/
01443 
01444 INT32 Path::GetNumEndPoints() const
01445 {
01446     // de-reference the verb array
01447     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
01448     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
01449 
01450     INT32 Count = 0;
01451     for (INT32 i=0; i<UsedSlots; i++)
01452     {
01453         if (Flags[i].IsEndPoint)
01454             Count++;
01455 
01456         if ((i>0) && (Verbs[i]==PT_MOVETO) && (Verbs[i-1] & PT_CLOSEFIGURE))
01457             Count--;
01458     }
01459 
01460     // return the number of 'on the path' end points
01461     return Count;
01462 }
01463 
01464 /********************************************************************************************
01465 
01466 >   void Path::GetAllOpenEnds()
01467 
01468     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01469     Created:    12/4/94
01470     Inputs:     MaxElements - The maximum number of coords that can be written to the
01471                 array
01472     Outputs:    Coords - A array of DocCoords
01473     Returns:    The number of coords in the array
01474     Purpose:    Goes through the path and copies the coordinates of all the Open ends
01475                 of the path into the array, until MaxElements elements have been added
01476                 to the array. Call GetNumEndPoints to find out how big to make your
01477                 array in the first place.
01478     SeeAlso:    Path::GetNumEndPoints
01479 
01480 ********************************************************************************************/
01481 
01482 INT32 Path::GetAllOpenEnds(INT32 MaxElements, DocCoord* EndCoords) const
01483 {
01484     // de-reference the verb array
01485     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
01486     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01487 
01488     INT32 Count = 0;
01489     for (INT32 i=0; i<UsedSlots; i++)
01490     {
01491         if (Verbs[i]==PT_MOVETO)
01492         {
01493             // Count this moveto
01494             EndCoords[Count] = Coords[i];
01495             Count++;
01496 
01497             // Check to see we have hit the maximum allowed entries
01498             if (Count==MaxElements)
01499                 return Count;
01500             
01501             if ((i>0) && (Verbs[i-1]&PT_CLOSEFIGURE))
01502             {
01503                 // The previous element was a close figure, so it corresponding MoveTo
01504                 // should not count
01505                 Count--;
01506             }
01507             else
01508             {
01509                 // The previous elment was not a close figure, so it was an Open End
01510                 EndCoords[Count] = Coords[i-1];
01511                 Count++;
01512 
01513                 // Check to see we have hit the maximum allowed entries
01514                 if (Count==MaxElements)
01515                     return Count;
01516 
01517             }
01518         }
01519     }
01520 
01521     // Check to see if the very last element in the path was a close figure or not
01522     if (Verbs[UsedSlots-1] & PT_CLOSEFIGURE)
01523         Count--;
01524     else
01525     {
01526         EndCoords[Count] = Coords[UsedSlots-1];
01527         Count++;
01528     }
01529 
01530     // return the number of coords we are stored
01531     return Count;
01532 }
01533 
01534 
01535 
01536 
01537 /********************************************************************************************
01538 
01539 >   DocRect Path::GetBoundingRect()
01540 
01541     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01542     Created:    31/01/94
01543     Returns:    The bounding box of the path
01544     Purpose:    Calculates the bounding rectangle of the paths coordinates. This is calculated
01545                 on the fly, so don't over use it
01546 
01547 ********************************************************************************************/
01548 
01549 DocRect Path::GetBoundingRect() const
01550 {
01551     // If the path is empty, then return an empty rectangle
01552     if (UsedSlots==0)
01553         return DocRect(0,0,0,0);
01554 
01555     // de-reference the coord array
01556     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01557     DocRect BoundingRect(Coords[0], Coords[0]);
01558 
01559     // include each point in the path to the bounding rectangle
01560     for (INT32 i=1; i<UsedSlots; i++)
01561         BoundingRect.IncludePoint(Coords[i]);
01562             
01563     // return the completed rectangle
01564     return BoundingRect;
01565 }
01566 
01567 
01568 /********************************************************************************************
01569 
01570 >   BOOL Path::GetTrueBoundingRect( DocRect* pRect, MILLIPOINT LineWidth = 0,
01571                                                     CCAttrMap* pAttrMap = NULL )
01572     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01573     Created:    19 May 2000
01574     Inputs:     pRect       pointer to a container rect for us to put the bounds in.
01575                 
01576                 *** Added by Diccon 22/5/2000  
01577                 LineWidth - the width of the path, note that this defaults to zero
01578                 so you don't have to supply anything unless you want to.
01579 
01580                 pAttrMap    ptr to an attrmap, from which to take modification info.
01581                             note that if we can extract a LineWidth value from it, then
01582                             the attrmap's value will override that passed in directly.
01583 
01584     Outputs:    if successful, pRect will contain the bounds of the path,
01585                 otherwise pRect will be made empty.
01586 
01587     Returns:    TRUE if successful,
01588                 FALSE otherwise.
01589 
01590     Purpose:    Calculate the bounding rectangle of a path using GDraw.
01591 
01592     Notes:      This gives the true bounds,  including all the curvy bits - not just
01593                 the bounds of the control points, which is what GetBoundingRect() returns.
01594 
01595                 By using one or other of the extra parameters to this method, you can find
01596                 out the bounds for thick lines, or lines with arrow-heads/tails.
01597 
01598     Errors:     ERROR3's with FALSE if pRect is NULL.
01599     SeeAlso:    GDrawContext::CalcStrokeBBox.
01600 
01601 ********************************************************************************************/
01602 #if !defined(EXCLUDE_FROM_XARLIB)
01603 BOOL Path::GetTrueBoundingRect(DocRect* pRect, MILLIPOINT LineWidth, CCAttrMap* pAttrMap)
01604 {
01605     // validate inputs.
01606     if (pRect == NULL)
01607     {
01608         ERROR3("Path::GetTrueBoundingRect; NULL input rectangle!");
01609         return FALSE;
01610     }
01611     if (LineWidth < 0)
01612     {
01613         //ERROR3("Negative line width supplied to Path::GetTrueBoundingRect");
01614         LineWidth = 0;  // we can recover from this
01615     }
01616 
01617     // check the attr map for any attrs which we think may alter
01618     // the bounds of the path, eg line width, arrow heads.
01619     ArrowRec*   pArrow1 = NULL;
01620     ArrowRec*   pArrow2 = NULL;
01621     JoinStyles  JoinStyle = JOIN_ROUND;
01622     if (pAttrMap != NULL)
01623     {
01624         AttrLineWidth*      pLineWidth  = NULL;
01625         AttrStrokeColour*   pLineColour = NULL;
01626         AttrStartArrow*     pStartArrow = NULL;
01627         AttrEndArrow*       pEndArrow   = NULL;
01628         AttrJoinType*       pJoinType   = NULL;
01629 
01630         // note that we only use the path's LineWidth if its outline is not transparent.
01631         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrLineWidth), (void*&)pLineWidth);
01632         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeColour), (void*&)pLineColour);
01633 
01634         if (pLineColour != NULL && pLineWidth != NULL)
01635         {
01636             LineWidth = 0;
01637             DocColour* pCol = pLineColour->GetStartColour();
01638             if (pCol != NULL && !pCol->IsTransparent())
01639                 LineWidth = pLineWidth->Value.LineWidth;
01640         }
01641 
01642         // ok, if we have a line width then we'll need to look for stuff like
01643         // arrowheads and join-style (esp. Mitre joins).
01644         if (LineWidth > 0)
01645         {
01646             pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartArrow), (void*&)pStartArrow);
01647             if (pStartArrow != NULL)
01648                 pArrow1 = &(pStartArrow->Value.StartArrow);
01649 
01650             pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrEndArrow), (void*&)pEndArrow);
01651             if (pEndArrow != NULL)
01652                 pArrow2 = &(pEndArrow->Value.EndArrow);
01653 
01654             pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrJoinType), (void*&)pJoinType);
01655             if (pJoinType != NULL)
01656             {
01657                 JointType jt = pJoinType->Value.JoinType;
01658 
01659                 JoinStyle = (jt == MitreJoin) ? JOIN_MITER :
01660                             (jt == RoundJoin) ? JOIN_ROUND :
01661                                                 JOIN_BEVEL;
01662             }
01663         }
01664     }
01665 
01666     // ok, let's get the path's bounds!
01667     pRect->MakeEmpty();
01668     BOOL fSuccess = FALSE;
01669     DocRect drBounds;
01670     GDrawContext *GD = GRenderRegion::GetStaticDrawContext();
01671 
01672     if (GD == NULL)
01673         return FALSE;
01674 
01675     fSuccess = GD->CalcStrokeBBox(  (POINT*)GetCoordArray(), GetVerbArray(), GetNumCoords(),
01676                                     (RECT *)(&drBounds), IsFilled, (DWORD)LineWidth,
01677                                     CAPS_ROUND, JoinStyle, NULL ) != -1;
01678 
01679     // if the bounds are invalid then make sure that we flag a failure.
01680     if (!drBounds.IsValid())
01681         fSuccess = FALSE;
01682 
01683     // ok so far - if we need to account for any arrowheads, do so now.
01684     if (fSuccess)
01685     {
01686         if (pArrow1 != NULL)
01687             drBounds = drBounds.Union(pArrow1->GetArrowBoundingRect(this, LineWidth, TRUE));
01688 
01689         if (pArrow2 != NULL)
01690             drBounds = drBounds.Union(pArrow2->GetArrowBoundingRect(this, LineWidth, FALSE));
01691     }
01692 
01693     // ok, if we're a success then record the bounds.
01694     if (fSuccess)
01695         *pRect = drBounds;
01696 
01697     return fSuccess;
01698 }
01699 
01700 
01701 
01702 /********************************************************************************************
01703 
01704 >   DocRect Path::GetBlobRect()
01705 
01706     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01707     Created:    20/12/94
01708     Returns:    The bounding box of the path and blobs
01709     Purpose:    Calculates the bounding rectangle of the paths coordinates expanded by the
01710                 current blob size.
01711                 This is calculated on the fly, so don't over use it
01712 
01713 ********************************************************************************************/
01714 
01715 DocRect Path::GetBlobRect() const
01716 {
01717     // If the path is empty, then return an empty rectangle
01718     if (UsedSlots==0)
01719         return DocRect(0,0,0,0);
01720 
01721     // de-reference the coord array
01722     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01723     DocRect BlobRect;
01724 
01725     for (INT32 i=0; i<UsedSlots; i++)
01726     {
01727         DocRect TempRect;
01728         GetApplication()->GetBlobManager()->GetBlobRect(Coords[i],&TempRect);
01729 
01730         if (i==0)
01731             BlobRect = TempRect;                    // For first coord, set BlobRect = rect of blob
01732         else
01733             BlobRect = BlobRect.Union(TempRect);    // for other coords, find the union
01734     }
01735 
01736     // return the completed rectangle
01737     return BlobRect;
01738 }
01739 #endif
01740 
01741 
01742 /********************************************************************************************
01743 
01744 >   DocCoord Path::GetEndPoint()
01745 
01746     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01747     Created:    31/01/94
01748     Returns:    The Endpoint of the Path Element pointed at by Current Path position
01749     Purpose:    Finds the endpoint of a given path element and returns it. For MoveTos and
01750                 LineTos, the point returned will be its associated coord, but for a CurveTo
01751                 the last coord will be returned
01752     Errors:     Ensures if the position is not valid or out of bounds
01753 
01754 ********************************************************************************************/
01755 
01756 DocCoord Path::GetEndPoint()
01757 {
01758     ENSURE( CurrentPos<UsedSlots, "Index out of bounds in GetEndPoint" );
01759 
01760     // de-reference the verb array
01761     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
01762 
01763     // de-reference the coord array
01764     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01765     
01766     if ((Verbs[CurrentPos] & (~PT_CLOSEFIGURE))==PT_BEZIERTO)
01767         return Coords[CurrentPos+2];
01768     else
01769         return Coords[CurrentPos];
01770 }
01771 
01772 
01773 
01774 /********************************************************************************************
01775 
01776 >   DocCoord Path::GetControl1()
01777 
01778     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01779     Created:    22/02/94
01780     Returns:    DocCoord - the Coord of the first control point
01781     Purpose:    Finds the first control point of a bezier
01782 
01783 ********************************************************************************************/
01784 
01785 DocCoord Path::GetControl1()
01786 {
01787     ENSURE( CurrentPos<UsedSlots, "Index out of bounds in GetEndPoint" );
01788 
01789     // de-reference the coord array
01790     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01791     
01792     ENSURE((((PathVerb*) DescribeHandle(VerbHandle)) [CurrentPos] & (~PT_CLOSEFIGURE))==PT_BEZIERTO, "Not a Bezier in Path::GetControl1()");
01793 
01794     return Coords[CurrentPos];
01795 }
01796 
01797 
01798 /********************************************************************************************
01799 
01800 >   DocCoord Path::GetControl2()
01801 
01802     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01803     Created:    22/02/94
01804     Returns:    DocCoord - The coord of the seconds control point
01805     Purpose:    Finds the second control point of a Bezier
01806 
01807 ********************************************************************************************/
01808 
01809 DocCoord Path::GetControl2()
01810 {
01811     ENSURE( CurrentPos<UsedSlots, "Index out of bounds in GetEndPoint" );
01812 
01813     // de-reference the arrays
01814     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
01815     
01816     ENSURE((((PathVerb*) DescribeHandle(VerbHandle))[CurrentPos+1] & (~PT_CLOSEFIGURE))==PT_BEZIERTO, "Not a Bezier in Path::GetControl2()");
01817 
01818     return Coords[CurrentPos+1];
01819 }
01820 
01821 
01822 
01823 /********************************************************************************************
01824 
01825 >   DocCoord* Path::GetCoordArray()
01826 
01827     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01828     Created:    31/01/94
01829     Returns:    Pointer to the Array of coords held by the path.
01830     Purpose:    Gives you access to the coordinate array. This array is simply a list of all
01831                 the coordinates in the path, in order. This should be very useful if you need
01832                 to perform transformations or other actions that effect all the coordinates.
01833                 The coordinates are in the corect order for the NT PolyDraw() function handle.
01834 
01835 ********************************************************************************************/
01836 
01837 DocCoord* Path::GetCoordArray() const
01838 {
01839     // de-reference the coord array
01840     return (DocCoord*) DescribeHandle(CoordHandle);
01841 }
01842 
01843 
01844 /********************************************************************************************
01845 
01846 >   PathVerb* Path::GetVerbArray()
01847 
01848     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01849     Created:    31/01/94
01850     Returns:    A pointer to the verb array held by the path. 
01851     Purpose:    This array holds a list of all the path elements used in the path. 
01852                 The structure used is the same as the NT structure and can in fact be passed
01853                 striaght on to the NT PolyDraw function.
01854                 The verbs must be one of the following PC_MoveTo, PC_LineTo or PC_CurveTo.
01855                 LineTo and CurveTo verbs can be combined with a ClosePath Flag
01856 
01857 ********************************************************************************************/
01858 
01859 PathVerb* Path::GetVerbArray() const
01860 {
01861     // de-reference the Verb array
01862     return (PathVerb*) DescribeHandle(VerbHandle);
01863 }
01864 
01865 
01866 /********************************************************************************************
01867 
01868 >   PathFlag* Path::GetFlagArray()
01869 
01870     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01871     Created:    31/01/94
01872     Returns:    A pointer to an array of Path Flags
01873     Purpose:    This array holds a set of flags for each and every coordinate in the path.
01874                 These flags are used to record information specific to each coordinate, such
01875                 as its selection state, to determine if it should smooth this part of the
01876                 path etc
01877     SeeAlso:    PathFlag
01878 
01879 ********************************************************************************************/
01880 
01881 PathFlags* Path::GetFlagArray() const
01882 {
01883     // de-reference the Flag array
01884     return (PathFlags*) DescribeHandle(FlagsHandle);
01885 }
01886 
01887 
01888 
01889 /********************************************************************************************
01890 
01891 >   void Path::GetPathArrays(PathVerb** VerbArray, DocCoord** CoordArray = NULL, PathFlags** FlagsArray = NULL)
01892 
01893     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01894     Created:    28/4/95
01895     Inputs:     VerbArray - pointer to a pointer to a PathVerb object
01896                 CoordArray - pointer to a pointer to a DocCoord
01897                 FlagsArray - pointer to a pointer to a PathFlags object
01898     Outputs:    VerbArray points to the verbs array
01899                 CoordArray points to the coordinates array
01900                 FlagsArray points to the flags array
01901     Returns:    -
01902     Purpose:    Easy way of getting the path pointers into variables.  Combines three function
01903                 calls into one.
01904     Errors:     -
01905 
01906 ********************************************************************************************/
01907 void Path::GetPathArrays(PathVerb** VerbArray, DocCoord** CoordArray, PathFlags** FlagsArray)
01908 {
01909     if (VerbArray != NULL)
01910         *VerbArray = (PathVerb*) DescribeHandle(VerbHandle);
01911     if (CoordArray != NULL)
01912         *CoordArray = (DocCoord*) DescribeHandle(CoordHandle);
01913     if (FlagsArray != NULL)
01914         *FlagsArray = (PathFlags*) DescribeHandle(FlagsHandle);
01915 }
01916 
01917 
01918 
01919 /********************************************************************************************
01920 
01921 >   BOOL Path::AddMoveTo(DocCoord p1, PathFlags* NewFlags=NULL)
01922 
01923     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01924     Created:    2/11/94
01925     Inputs:     p1 - the coord to draw the line to
01926                 NewFlags - Flags associtaed with the coord
01927     Returns:    TRUE if it worked, FALSE if it failed to get more memory
01928     Purpose:    Adds a new MoveTo to the end of the path
01929     SeeAlso:    Path::AddLineTo; Path::AddCurveTo
01930 
01931 ********************************************************************************************/
01932 
01933 BOOL Path::AddMoveTo(DocCoord p1, PathFlags* NewFlags)
01934 {
01935     // Sadly there is no way for the outside world to set the insert position
01936     // to the end of the path!! SetPathPosition() for instance sets the currentpos to
01937     // usedslots-1, when passed usedslots which means you can never ever call InsertLineTo
01938     // and expect a line to be added to the pathend, it will be inserted before the element
01939     // which is the last in the path. Ok so this function does the job.
01940         
01941     CurrentPos = UsedSlots;
01942     return InsertMoveTo(p1, NewFlags);
01943 }
01944 
01945 
01946 
01947 /********************************************************************************************
01948 
01949 >   BOOL Path::AddLineTo(DocCoord p1, PathFlags* NewFlags=NULL)
01950 
01951     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01952     Created:    2/11/94
01953     Inputs:     p1 - the coord to draw the line to
01954                 NewFlags - Flags associtaed with the coord
01955     Returns:    TRUE if it worked, FALSE if it failed to get more memory
01956     Purpose:    Adds a new LineTo to the end of the path
01957     SeeAlso:    Path::AddMoveTo; Path::AddCurveTo
01958 
01959 ********************************************************************************************/
01960 
01961 BOOL Path::AddLineTo(DocCoord p1, PathFlags* NewFlags)
01962 {
01963     CurrentPos = UsedSlots;
01964     return InsertLineTo(p1, NewFlags);
01965 }
01966 
01967 
01968 
01969 /********************************************************************************************
01970 
01971 >   void Path::AddCurveTo(DocCoord p1, DocCoord p2, DocCoord p3, PathFlags* NewFlags=NULL)
01972 
01973     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01974     Created:    2/11/94
01975     Inputs:     p1 - The first control point
01976                 p2 - The second control point
01977                 p3 - The endpoint
01978                 NewFlags - The flags to set the new coords to
01979     Returns:    FALSE if there was an error, TRUE otherwise
01980     Purpose:    Adds a Curve to the end of the path
01981     Errors:     Can fail if it can not get enough memory to add the curve
01982     SeeAlso:    Path::AddLineTo; Path::AddMoveTo
01983 
01984 ********************************************************************************************/
01985 
01986 BOOL Path::AddCurveTo(DocCoord p1, DocCoord p2, DocCoord p3, PathFlags* NewFlags)
01987 {
01988     CurrentPos = UsedSlots;
01989     return InsertCurveTo(p1,p2,p3,NewFlags);
01990 }
01991 
01992 /********************************************************************************************
01993 
01994 >   void Path::AddCurveTo(DocCoord p3, PathFlags* NewFlags=NULL)
01995 
01996     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01997     Created:    11/12/94
01998     Inputs:     p3 - The endpoint
01999                 NewFlags - The flags to set the new coords to
02000     Returns:    FALSE if there was an error, TRUE otherwise
02001     Purpose:    Adds a straight line version of a curve to the end of the path. The control
02002                 points are positioned 1/3 and 2/3 along the line between its end points
02003     Errors:     Can fail if it can not get enough memory to add the curve
02004     SeeAlso:    Path::AddLineTo; Path::AddMoveTo
02005 
02006 ********************************************************************************************/
02007 
02008 BOOL Path::AddCurveTo(DocCoord p3, PathFlags* NewFlags)
02009 {
02010     ERROR1IF( UsedSlots==0,FALSE,"No previous path elements when calling Path::AddCurveTo()" );
02011     // set the current pos.
02012     CurrentPos = UsedSlots;
02013 
02014     // Find the coord of the last point in the path
02015     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
02016     DocCoord prev = Coords[CurrentPos-1];
02017 
02018     // Create some temp coords
02019     DocCoord p1,p2;
02020 
02021     // calculate the intermediary control points
02022     p1.x = (2*prev.x + p3.x)/3;
02023     p1.y = (2*prev.y + p3.y)/3;
02024     p2.x = (prev.x + 2*p3.x)/3;
02025     p2.y = (prev.y + 2*p3.y)/3;
02026 
02027     return InsertCurveTo(p1,p2,p3,NewFlags);
02028 }
02029 
02030 
02031 
02032 /********************************************************************************************
02033 
02034 >   BOOL Path::InsertMoveTo(DocCoord p1, PathFlags* NewFlags = NULL)
02035 
02036     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02037     Created:    31/01/94
02038     Inputs:     p1 - The coordinate to move to
02039                 NewFlags - The flags associated with the coord
02040     Returns:    TRUE if it worked, FALSE otherwise
02041     Purpose:    Inserts a MoveTo element into the middle of the Path and asks for extra
02042                 memory if we need it. It will return FALSE if we fail to get the memory.
02043 
02044 ********************************************************************************************/
02045 
02046 BOOL Path::InsertMoveTo(DocCoord p1, PathFlags* NewFlags)
02047 {
02048     // We need only 1 empty slot to add a MoveTo
02049     if (!MakeSpaceInPath(1))
02050     {
02051         ERROR( _R(IDS_PATH_MEM_ERROR), FALSE );
02052     }
02053 
02054     // get all the pointers
02055     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02056     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
02057     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02058 
02059     // Prepare the flags
02060     PathFlags UseFlags;
02061     if (NewFlags!=NULL)
02062         UseFlags = *NewFlags;
02063 
02064     UseFlags.IsEndPoint = TRUE;
02065 
02066     // There is now a hole in the middle of the path. We can fill in the appropriate details
02067     // and call it a day
02068     Verbs[CurrentPos] = PT_MOVETO;
02069     Coords[CurrentPos] = p1;
02070     Flags[CurrentPos] = UseFlags;
02071 
02072     // Update the memory usage vars
02073     UsedSlots++;
02074     UnUsedSlots--;
02075 
02076     // move the position indicator to point to the thing after our new item
02077     SyncExtraInfo();
02078     CurrentPos++;
02079 
02080     return TRUE;
02081 }
02082 
02083 
02084 
02085 /********************************************************************************************
02086 
02087 >   BOOL Path::InsertLineTo(DocCoord p1, PathFlags* NewFlags=NULL)
02088 
02089     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02090     Created:    31/01/94
02091     Inputs:     p1 - the coord to draw the line to
02092                 NewFlags - Flags associtaed with the coord
02093     Returns:    TRUE if it worked, FALSE if it failed to get more memory
02094     Purpose:    Inserts a LineTo into the path at the Current Path Position
02095 
02096 ********************************************************************************************/
02097 
02098 BOOL Path::InsertLineTo(DocCoord p1, PathFlags* NewFlags)
02099 {
02100     // We need only 1 empty slot to add a LineTo
02101     if (!MakeSpaceInPath(1))
02102     {
02103         ERROR( _R(IDS_PATH_MEM_ERROR), FALSE );
02104     }
02105 
02106     // get all the pointers
02107     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02108     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
02109     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02110 
02111     // Prepare the flags
02112     PathFlags UseFlags;
02113     if (NewFlags!=NULL)
02114         UseFlags = *NewFlags;
02115 
02116     UseFlags.IsEndPoint = TRUE;
02117 
02118     // There is now a hole in the middle of the path. We can fill in the appropriate details
02119     // and call it a day
02120     Verbs[CurrentPos] = PT_LINETO;
02121     Coords[CurrentPos] = p1;
02122     Flags[CurrentPos] = UseFlags;
02123 
02124     // Update the memory usage vars
02125     UsedSlots++;
02126     UnUsedSlots--;
02127 
02128     // move the position indicator to point to the thing after our new item
02129     SyncExtraInfo();
02130     CurrentPos++;
02131 
02132     return TRUE;
02133 }
02134 
02135 
02136 
02137 /********************************************************************************************
02138 
02139 >   void Path::InsertCurveTo(DocCoord p1, DocCoord p2, DocCoord p3, PathFlags* NewFlags=NULL)
02140 
02141     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02142     Created:    31/01/94
02143     Inputs:     p1 - The first control point
02144                 p2 - The second control point
02145                 p3 - The endpoint
02146                 NewFlags - The flags to set the new coords to
02147     Returns:    FALSE if there was an error, TRUE otherwise
02148     Purpose:    Inserts a Curve into the path at the Current Path Position
02149     Errors:     Can fail if it can not get enough memory to add the curve
02150     SeeAlso:    Path::InsertLineTo; Path::InsertMoveTo
02151 
02152 ********************************************************************************************/
02153 
02154 BOOL Path::InsertCurveTo(DocCoord p1, DocCoord p2, DocCoord p3, PathFlags* NewFlags)
02155 {
02156     // We need 3 empty slot to add a CurveTo
02157     if (!MakeSpaceInPath(3))
02158     {
02159         ERROR( _R(IDS_PATH_MEM_ERROR), FALSE );
02160     }
02161 
02162     // get all the pointers
02163     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02164     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
02165     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02166 
02167     // Prepare the flags
02168     PathFlags UseFlags;
02169     if (NewFlags!=NULL)
02170         UseFlags = *NewFlags;
02171 
02172     UseFlags.IsEndPoint = FALSE;
02173 
02174     // There is now a hole in the middle of the path.
02175     // We can fill in the appropriate details for the first control point
02176     Verbs[CurrentPos] = PT_BEZIERTO;
02177     Coords[CurrentPos] = p1;
02178     Flags[CurrentPos] = UseFlags;
02179 
02180     SyncExtraInfo();
02181     CurrentPos++;
02182 
02183     // and the second control point
02184     Verbs[CurrentPos] = PT_BEZIERTO;
02185     Coords[CurrentPos] = p2;
02186     Flags[CurrentPos] = UseFlags;
02187     CurrentPos++;
02188 
02189     // and the endpoint
02190     UseFlags.IsEndPoint = TRUE;
02191     Verbs[CurrentPos] = PT_BEZIERTO;
02192     Coords[CurrentPos] = p3;
02193     Flags[CurrentPos] = UseFlags;
02194     CurrentPos++;
02195 
02196     // Update the memory usage vars
02197     UsedSlots += 3;
02198     UnUsedSlots -= 3;
02199 
02200     // The Current Path Position has already been updated and now points to the item
02201     // after the curveto (or off the end of the path, if this is the last element in the path
02202 
02203     return TRUE;
02204 }
02205 
02206 
02207 
02208 /********************************************************************************************
02209 
02210 >   BOOL Path::CloseSubPath()
02211 
02212     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02213     Created:    31/01/94
02214     Returns:    TRUE if it worked, FALSE if it fails (ie you tried to close a path that only
02215                 had a PT_MOVETO element in it.
02216     Purpose:    To indicate that a path is closed with a line, you should call CloseSubPath
02217                 with the Current Path Position indicator somewhere inside the sub-path that
02218                 you want closed. This function will adjust the verb of the last line segment
02219                 in the sub-path by adding the PT_CLOSEFIGURE element into as described in the
02220                 NT Prog Reference guides for the PolyDraw() function (page 304 of book 4 at
02221                 last count)
02222                 The Current Path Position is left on the element at the end of the subpath
02223 
02224 ********************************************************************************************/
02225 
02226 BOOL Path::CloseSubPath()
02227 {
02228     // need to search for either a MoveTo or the end of the path and insert a close
02229     // path just before it. We start from the position supplied
02230     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02231 
02232     // We are inside the path somewhere
02233     while (CurrentPos+1<UsedSlots)
02234     {
02235         if (Verbs[CurrentPos+1]==PT_MOVETO)
02236         {
02237             // go back one and close the path
02238             ENSURE( Verbs[CurrentPos]!=PT_MOVETO, "You can not close a path at a moveto" );
02239             if (Verbs[CurrentPos]==PT_MOVETO)
02240                 return FALSE;
02241 
02242             Verbs[CurrentPos] = Verbs[CurrentPos] | PT_CLOSEFIGURE;
02243             return TRUE;
02244         }
02245 
02246         CurrentPos++;
02247     }
02248 
02249     // Make sure we are in the path
02250     if (CurrentPos>=UsedSlots)
02251         CurrentPos = UsedSlots-1;
02252 
02253     // We are right at the end of the path, so change the last path element
02254     ENSURE( Verbs[CurrentPos]!=PT_MOVETO, "You can not close a path at a moveto" );
02255     if (Verbs[CurrentPos]==PT_MOVETO)
02256         return FALSE;
02257 
02258     Verbs[CurrentPos] = Verbs[CurrentPos] | PT_CLOSEFIGURE;
02259     CurrentPos++;
02260     return TRUE;
02261 }
02262 
02263 /********************************************************************************************
02264 
02265 >   BOOL Path::DeleteSection(INT32 StartSlot, INT32 NumSlots)
02266 
02267     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02268     Created:    27/4/94
02269     Inputs:     StartSlot - The slot number to start deleting from
02270                 NumSlots - The number of slots to delete
02271     Returns:    TRUE if it worked, FALSE if it failed
02272     Purpose:    Deletes sections out of the middle of the path. Used by DeleteElement and
02273                 for Deleting sub paths etc.
02274 
02275 ********************************************************************************************/
02276 
02277 BOOL Path::DeleteSection(INT32 StartSlot, INT32 NumSlots)
02278 {
02279     // Saftey Checks
02280     ENSURE( StartSlot<UsedSlots, "PathPosition is off end of path in Path::DeleteElement" );
02281     ENSURE( StartSlot>=0, "PathPosition is off beginning of path in Path::DeleteElement" );
02282     ENSURE( NumSlots>0, "Trying to delete Zero slots from the path!" );
02283 
02284     if (NumSlots==0)
02285         return TRUE;
02286 
02287     // Find out how much memory to move about and adjust the element counters
02288     INT32 SlotsToMove = UsedSlots-StartSlot-NumSlots;
02289 
02290     // and move it all about if we need to
02291     if (SlotsToMove>0)
02292     {
02293         // Dereference the pointers
02294         PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
02295         DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
02296         PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
02297 
02298         memmove((void*)(&Verbs[StartSlot]), (void*)(&Verbs[StartSlot+NumSlots]), SlotsToMove*sizeof(PathVerb));
02299         memmove((void*)(&Flags[StartSlot]), (void*)(&Flags[StartSlot+NumSlots]), SlotsToMove*sizeof(PathFlags));
02300         memmove((void*)(&Coords[StartSlot]), (void*)(&Coords[StartSlot+NumSlots]), SlotsToMove*sizeof(DocCoord));
02301         if (ExtraInfo != NULL)
02302             ExtraInfo->ShiftDownExtraInfo(StartSlot, NumSlots, SlotsToMove);
02303     }
02304 
02305     // Update the path details
02306     UsedSlots   -= NumSlots;
02307     UnUsedSlots += NumSlots;
02308 
02309     return Compact();
02310 }
02311 
02312 
02313 /********************************************************************************************
02314 
02315 >   void Path::DeleteElement()
02316 
02317     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02318     Created:    31/01/94
02319     Purpose:    Deletes the element at the Current Path Position. May free some memory if
02320                 the path gets to have too much unused space in the path. The Current Path
02321                 Position points at the slot before the one being deleted
02322 
02323 ********************************************************************************************/
02324 
02325 BOOL Path::DeleteElement()
02326 {
02327     // Dereference the pointers
02328     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
02329 //  DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
02330 
02331     // Saftey Checks
02332     ENSURE( CurrentPos<UsedSlots, "PathPosition is off end of path in Path::DeleteElement" );
02333     ENSURE( CurrentPos>=0, "PathPosition is off beginning of path in Path::DeleteElement" );
02334     ENSURE( ((PathFlags*) DescribeHandle(FlagsHandle))[CurrentPos].IsEndPoint==TRUE, "Delete Element called when Path Position was not on an endpoint");
02335 
02336     // Find the start of the element
02337     INT32 StartSlot = CurrentPos;
02338 
02339     // Work out how many slots we need to delete
02340     INT32 NumSlots;
02341     if ((Verbs[CurrentPos] & (~PT_CLOSEFIGURE)) == PT_BEZIERTO)
02342     {
02343         NumSlots  = 3;
02344         StartSlot -= 2;
02345     }
02346     else
02347     {
02348         NumSlots = 1;
02349     }
02350 
02351     // Delete the element
02352     BOOL RetVal = DeleteSection(StartSlot, NumSlots);
02353 
02354     // tidy up the Current Path Position
02355     CurrentPos = StartSlot-1;
02356     if (CurrentPos<0)
02357         CurrentPos = 0;
02358 
02359     return RetVal;
02360 }
02361 
02362 
02363 
02364 
02365 /********************************************************************************************
02366 
02367 >   BOOL Path::DeleteFromElement(INT32 ElementNum)
02368 
02369     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02370     Created:    13/4/94
02371     Inputs:     ElementNum - the number of the element to start deleting from.
02372     Returns:    TRUE if the deletion was a success
02373     Purpose:    Deletes all the elements in the path from ElementNum to the end
02374                 of the path
02375 
02376 ********************************************************************************************/
02377 
02378 BOOL Path::DeleteFromElement(INT32 ElementNum)
02379 {
02380     // Find out how many slots there are from ElementNum to the end
02381     INT32 SlotsToDelete = UsedSlots-ElementNum;
02382 
02383     // go and delete the end of the path
02384     BOOL RetVal = DeleteSection(ElementNum, SlotsToDelete);
02385 
02386     // Set the current Path position to just after the path, ready for further additions
02387     CurrentPos = UsedSlots;
02388 
02389     return RetVal;
02390 }
02391 
02392 
02393 /********************************************************************************************
02394 
02395 >   BOOL Path::ClearPath()
02396 
02397     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02398     Created:    16/02/94
02399     Returns:    TRUE if it was able to free up the unused parts of the path, FALSE if there
02400                 was a memory de-alloc error
02401     Purpose:    Empties the path of all its entries and tries to free up excessive amounts of
02402                 unused memory left over at the end. It does keep some memory though, ready
02403                 for a new path to be put into it.
02404                 It also now resets CurrentPos back to zero.
02405 
02406 ********************************************************************************************/
02407 
02408 BOOL Path::ClearPath()
02409 {
02410     if (UsedSlots==0)
02411         return TRUE;
02412 
02413     BOOL ok = DeleteSection(0,UsedSlots);
02414     if (ok)
02415         CurrentPos=0;
02416     return ok;
02417 }
02418 
02419 
02420 /********************************************************************************************
02421 
02422 >   BOOL Path::ClearPath(BOOL compress)
02423 
02424     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02425     Created:    16/11/94
02426     Inputs:     compress - a boolean, to describe whether to compress the paths memory
02427                            or not.
02428     Returns:    TRUE if it was able to free up the unused parts of the path, FALSE if there
02429                 was a memory de-alloc error
02430     Purpose:    Empties the path of all its entries.
02431                 If compress is TRUE the routine will try to free up excessive amounts of
02432                 unused memory left over at the end. 
02433                 It also now resets CurrentPos back to zero.
02434     SeeAlso:    Path::Compact();
02435 
02436 ********************************************************************************************/
02437 
02438 BOOL Path::ClearPath(BOOL compress)
02439 {
02440     if (UsedSlots>0)
02441     {
02442         // reset the path definitions
02443         UnUsedSlots+=UsedSlots;
02444         UsedSlots=0;
02445         CurrentPos=0;
02446         // make sure we compress memory if necessary
02447         if (compress)
02448             Compact();                      // ignore any error coming from here
02449     }   
02450     return TRUE;
02451 } 
02452 
02453 
02454 /********************************************************************************************
02455 
02456 >   BOOL Path::IsSelected()
02457 
02458     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02459     Created:    31/01/94
02460     Returns:    TRUE if the point is selected, FALSE if not
02461     Purpose:    return the Selected flag for the coordinate
02462 
02463 ********************************************************************************************/
02464 
02465 BOOL Path::IsSelected()
02466 {
02467     ENSURE( CurrentPos<UsedSlots, "PathPosition is out of Bounds in Path::IsSelected" );
02468 
02469     // Get the flags array
02470     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02471 
02472     // and return the selected flag
02473     return Flags[CurrentPos].IsSelected;
02474 }
02475 
02476 
02477 /********************************************************************************************
02478 
02479 >   BOOL Path::IsEndPoint()
02480 
02481     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02482     Created:    21/02/94
02483     Returns:    TRUE if this point is an EndPoint (ie Not a Bezier control point)
02484     Purpose:    return the IsEndPoint flag for the coordinate
02485 
02486 ********************************************************************************************/
02487 
02488 BOOL Path::IsEndPoint()
02489 {
02490     ENSURE( CurrentPos<UsedSlots, "PathPosition is out of Bounds in Path::IsEndPoint" );
02491 
02492     // Get the flags array
02493     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02494 
02495     // and return the selected flag
02496     return Flags[CurrentPos].IsEndPoint;
02497 }
02498 
02499 
02500 
02501 /********************************************************************************************
02502 
02503 >   BOOL Path::IsSmooth()
02504 
02505     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02506     Created:    31/01/94
02507     Returns:    TRUE if the point is Smooth, FALSE if not
02508     Purpose:    returns the Smooth flag for the Current Path Position
02509 
02510 ********************************************************************************************/
02511 
02512 BOOL Path::IsSmooth()
02513 {
02514     ENSURE( CurrentPos<UsedSlots, "PathPosition is out of Bounds in Path::IsSmooth" );
02515 
02516     // Get the flags array
02517     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02518 
02519     // and return the selected flag
02520     return Flags[CurrentPos].IsSmooth;
02521 }
02522 
02523 
02524 
02525 /********************************************************************************************
02526 
02527 >   BOOL Path::IsRotate()
02528 
02529     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02530     Created:    31/01/94
02531     Returns:    TRUE if the point has the rotate flag set, FALSE if not
02532     Purpose:    returns the Rotate flag for the coordinate
02533 
02534 ********************************************************************************************/
02535 
02536 BOOL Path::IsRotate()
02537 {
02538     ENSURE( CurrentPos<UsedSlots, "PathPosition is out of Bounds in Path::IsRotate" );
02539 
02540     // Get the flags array
02541     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02542 
02543     // and return the selected flag
02544     return Flags[CurrentPos].IsRotate;
02545 }
02546 
02547 
02548 
02549 /********************************************************************************************
02550 
02551 >   BOOL Path::IsNearOpenEnd(const DocRect& BlobRect)
02552 
02553     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02554     Created:    26/4/94
02555     Inputs:     BlobRect - The rect to see if any of the endpoints are in
02556     Returns:    TRUE if the TestCoord is close to one of the endpoints
02557     Purpose:    Determines if the Coordinate is near any of the path open ends. This
02558                 function looks through all the open ends in complex paths and returns
02559                 TRUE if any of them are near the TestCoord
02560 
02561 ********************************************************************************************/
02562 
02563 BOOL Path::IsNearOpenEnd(const DocRect& BlobRect, INT32* SlotNum)
02564 {
02565 #if !defined(EXCLUDE_FROM_RALPH)
02566     // Dereference a few pointers
02567     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
02568     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02569 
02570     // Start searching all the coords for EndPoints
02571     INT32 EndSlot = UsedSlots-1;
02572     for (INT32 i=UsedSlots-1; i>=0; i--)
02573     {
02574         // Are we at the start of a sub-path yet
02575         if (Verbs[i] == PT_MOVETO)
02576         {
02577             // Is this sub path open?
02578             if ((Verbs[EndSlot] & PT_CLOSEFIGURE) == 0)
02579             {
02580                 // This is an open sub path, so test if they are close to the TestCoord
02581                 if (BlobRect.ContainsCoord(Coords[i]))
02582                 {
02583                     *SlotNum = i;
02584                     return TRUE;
02585                 }
02586 
02587                 if (BlobRect.ContainsCoord(Coords[EndSlot]))
02588                 {
02589                     *SlotNum = EndSlot;
02590                     return TRUE;
02591                 }
02592             }
02593 
02594             // That sub path did not intersect the rect, so carry on
02595             EndSlot = i-1;
02596         }
02597     }
02598 #endif
02599     // No luck, so fail
02600     return FALSE;
02601 }
02602 
02603 
02604 /********************************************************************************************
02605 
02606 >   BOOL Path::IsOpenEnd(const INT32 index)
02607 
02608     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02609     Created:    20/2/95
02610     Inputs:     index = index of element to check
02611     Returns:    TRUE if this index is set to either end of an open subpath
02612     Purpose:    Tests to see if the index provided specifies either the first or last
02613                 element in an open subpath. ie the index is a true end point.
02614 
02615 ********************************************************************************************/
02616 
02617 BOOL Path::IsOpenEnd(const INT32 index)
02618 {
02619     // Do a quick sanity check
02620     if (index<0 || index>=UsedSlots || UsedSlots==0)
02621         return FALSE;
02622 
02623     // 0<=index<=UsedSlots-1
02624     // Dereference the verbs
02625     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02626 
02627     // Check its not the end index
02628     INT32 end = index;
02629     FindEndElOfSubPath(&end);
02630     if (Verbs[end] & PT_CLOSEFIGURE)
02631         return FALSE;
02632 
02633     if (end==index)
02634         return TRUE;
02635 
02636     // Check for a start point
02637     return (Verbs[index]==PT_MOVETO);
02638 
02639 }
02640 
02641 
02642 
02643 /********************************************************************************************
02644 
02645 >   BOOL Path::IsComplexPath()
02646 
02647     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02648     Created:    26/4/94
02649     Returns:    TRUE if the Path is Complex
02650     Purpose:    Tests to see if the path is a complex path (ie it has multiple sub-paths)
02651 
02652 ********************************************************************************************/
02653 
02654 BOOL Path::IsComplexPath()
02655 {
02656     // Get the path verbs
02657     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02658 
02659     INT32 SubPaths = 0;
02660     for (INT32 i=0; i<UsedSlots; i++)
02661     {
02662         // Count all the MoveTos in the path
02663         if (Verbs[i] == PT_MOVETO)
02664             SubPaths++;
02665     }
02666 
02667     // Make sure that there was a moveto in this path. This should not happen
02668     ENSURE( SubPaths>0, "Path::IsComplexPath() - Found a Path with no MoveTo in it!" );
02669 
02670     // If there was only one SubPath then it is simple, otherwise it is complex
02671     if (SubPaths==1)
02672         return FALSE;
02673     else
02674         return TRUE;
02675 }
02676 
02677 
02678 /********************************************************************************************
02679 
02680 >   void Path::GetFlags( PathFlags* NewFlags)
02681 
02682     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02683     Created:    21/02/94
02684     Outputs:    NewFlags - Holds the flags for the current path position
02685     Purpose:    returns all the flags for the current path position
02686 
02687 ********************************************************************************************/
02688 
02689 void Path::GetFlags( PathFlags* NewFlags)
02690 {
02691     ENSURE( CurrentPos<UsedSlots, "PathPosition is out of Bounds in Path::IsEndPoint" );
02692 
02693     // Get the flags array
02694     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02695 
02696     // and return the selected flag
02697     *NewFlags = Flags[CurrentPos];
02698 }
02699 
02700 
02701 /********************************************************************************************
02702 
02703 >   void Path::SetPathFlag( const PathFlags &NewFlags )
02704 
02705     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02706     Created:    21/02/94
02707     Inputs:     NewFlags - The flags you want to set
02708     Purpose:    Sets the flags at the current path position to the flags provided
02709 
02710 ********************************************************************************************/
02711 
02712 void Path::SetFlags( const PathFlags &NewFlags )
02713 {
02714     ENSURE( CurrentPos<UsedSlots, "PathPosition is out of Bounds in Path::IsEndPoint" );
02715 
02716     // Get the flags array
02717     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02718 
02719     // and set the flags
02720     Flags[CurrentPos] = NewFlags;
02721 }
02722 
02723 
02724 /********************************************************************************************
02725 
02726     void Path::InitialiseFlags()
02727 
02728     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02729     Created:    16/1/95
02730     Inputs:     -
02731     Outputs:    The path's flags array contains valid flags for each of its verbs
02732     Returns:    -
02733     Purpose:    This generates a legal path flags array based on the all path verbs. 
02734                 It sets all flags to default values and then scans all
02735                 end points setting their end point flag to TRUE
02736     SeeAlso:    -
02737 
02738 ********************************************************************************************/
02739 
02740 void Path::InitialiseFlags()
02741 {
02742     InitialiseFlags(0,UsedSlots);
02743 }
02744 
02745 
02746 /********************************************************************************************
02747 
02748     void Path::InitialiseFlags( const UINT32 Startindex, const INT32 Len)
02749 
02750     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02751     Created:    8/11/94
02752     Inputs:     Startindex  = index of element at which to start
02753                 Len         = num verbs to set corresponding flags for
02754     Outputs:    The path's flags array contains valid flags for each of its verbs
02755     Returns:    -
02756     Purpose:    This generates a legal path flags array based on the path verbs within the
02757                 specified region. It sets all flags to default values and then scans all
02758                 end points setting their end point flag to TRUE
02759     SeeAlso:    -
02760 
02761 ********************************************************************************************/
02762 
02763 void Path::InitialiseFlags(const INT32 Startindex,
02764                            const INT32 Len)
02765 {
02766     ERROR3IF(Startindex<0,"startindex off start of path in Path::SetPathFlags()");
02767     ERROR3IF(Startindex+Len>UsedSlots,"(startindex+len) of end of path in Path::SetPathFlags()"); 
02768 
02769     // Get the flags array
02770     PathFlags* pFlags = (PathFlags*) DescribeHandle(FlagsHandle);
02771     PathVerb* pVerbs = (PathVerb*)   DescribeHandle(VerbHandle);
02772 
02773     InitialiseFlags(pVerbs+Startindex,pFlags+Startindex,Len);
02774 }
02775 
02776 
02777 
02778 /********************************************************************************************
02779 
02780     void Path::InitialiseFlags(PathVerb* pVerbs, PathFlags* pFlags, const INT32 Len)
02781 
02782     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02783     Created:    16/1/95
02784     Inputs:     pVerbs = ptr to a path verbs array
02785                 pFlags = ptr to buffer to place the initialised flags
02786                 Len    = num verbs in pVerbs, and num flags in pFlags
02787     Outputs:    pFlags array contains valid flags for each of the verbs in pVerbs
02788     Returns:    -
02789     Purpose:    This generates a legal path flags array based on the path verbs in pVerbs within the
02790                 specified region. It sets all flags in pFlags to default values and then scans all
02791                 end points setting their end point flag to TRUE
02792     SeeAlso:    -
02793 
02794 ********************************************************************************************/
02795 
02796 void Path::InitialiseFlags(PathVerb* pVerbs, PathFlags* pFlags, const INT32 Len)
02797 {
02798     INT32 n;
02799 
02800     // Reset all the path flags to default values
02801     PathFlags DefaultPathFlags;
02802     for (n=0; n < Len; n++)
02803         pFlags[n] = DefaultPathFlags;
02804 
02805     // Scan the verbs, so that the end point flags can be set correctly
02806     for (n=0; n < Len; n++)
02807     {
02808         PathVerb Verb = pVerbs[n] & ~PT_CLOSEFIGURE;
02809         switch (Verb)
02810         {
02811             case PT_LINETO:
02812             case PT_MOVETO:
02813                 pFlags[n].IsEndPoint = TRUE;
02814                 break;
02815 
02816             case PT_BEZIERTO:
02817                 n += 2;
02818                 ERROR3IF(n>=Len,"Found a PT_BEZIERTO, but third pt is off the end of the array");
02819                 pFlags[n].IsEndPoint = TRUE;
02820                 break;
02821 
02822             default:
02823                 ERROR3_PF(("Illegal path verb found : %c",Verb));
02824                 break;
02825         }
02826     }
02827 }
02828 
02829 
02830 
02831 /********************************************************************************************
02832 
02833 >   void Path::DumpPath()
02834 
02835     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02836     Created:    01/02/94
02837     Purpose:    Dumps the whole path out to the debug terminal
02838 
02839 ********************************************************************************************/
02840 
02841 void Path::DumpPath()
02842 {
02843 #ifdef _DEBUG
02844     TRACE( _T("\nStarting Path Dump\n") );
02845     TRACE( _T("[%d=PT_MOVETO, %d=PT_LINETO, %d=PT_BEZIERTO, %d=PT_CLOSEFIGURE]\n"), 
02846            PT_MOVETO, PT_LINETO, PT_BEZIERTO, PT_CLOSEFIGURE );
02847     TRACE( _T("UsedSlots=%ld, UnUsedSlots=%ld, CurrentPos=%ld\n"), UsedSlots, UnUsedSlots, CurrentPos );
02848     TRACE( _T("Path Size in Bytes=%ld\n"), GetPathByteLength() );
02849     TRACE( _T("\tVerb\tCoord\n") );
02850 
02851     // de-reference the pointers
02852     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
02853     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
02854     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
02855 
02856     // loop through all the coords, displaying their type and position
02857     for (INT32 i=0; i<UsedSlots; i++)
02858     {
02859         TRACE( _T("%d\t%d\t(%ld, %ld)"), i, Verbs[i], Coords[i].x, Coords[i].y );
02860         if (Flags[i].IsSmooth)
02861             TRACE( _T("\tS"));
02862         else
02863             TRACE( _T("\t "));
02864 
02865         if (Flags[i].IsRotate)
02866             TRACE( _T("R"));
02867         else
02868             TRACE( _T(" "));
02869 
02870         if (Flags[i].IsEndPoint)
02871             TRACE( _T("E"));
02872         else
02873             TRACE( _T(" "));
02874 
02875         if (Flags[i].IsSelected)
02876             TRACE( _T("(sel)"));
02877 
02878         TRACE( _T("\n"));
02879     }
02880 #endif
02881 }
02882 
02883 
02884 
02885 /********************************************************************************************
02886 
02887 >   void Path::GetDebugDetails()
02888 
02889     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>; Karim 26/05/2000
02890     Created:    20/12/94
02891     Purpose:    Dumps the whole path out during tree display debugging.
02892 
02893                 (Karim)
02894                 I'm updating this method to give a little bit more info, so's it isn't
02895                 necessary to manually dump path data every time you need to. It doesn't
02896                 currently seem to be called from anywhere, so my changes should have zero
02897                 effect on existing code. Please feel free to use this method and alter it
02898                 to display more info if required.
02899 
02900                 Info returned:
02901                     *   Number of handles and bytes used by the path data.
02902                     *   Whether the path is filled or not.
02903                     *   Each handle's verbs, coords and flags
02904                             Verbs - L - LineTo
02905                                     C - CurveTo
02906                                     M - MoveTo
02907                                     S - ClosePath
02908 
02909     Notes:      For example of use, see NodeClipViewController::GetDebugDetails().
02910 
02911 ********************************************************************************************/
02912 void Path::GetDebugDetails(StringBase* Str)
02913 {
02914 #ifdef _DEBUG
02915     String_256 TempStr;
02916 
02917     // output number of handles and space used by the path.
02918     INT32 nSlots = GetNumCoords();
02919     TempStr._MakeMsg( TEXT("#1%ld handles, #2%ld bytes used\r\n"),  nSlots,
02920                                                                     GetPathByteLength() );
02921     (*Str) += TempStr;
02922 
02923     // output whether or not the path is filled.
02924     TempStr._MakeMsg( TEXT("Path is#1%s Filled and is#2%s Stroked\r\n"),
02925                                                                 IsFilled    ? "" : " not",
02926                                                                 IsStroked   ? "" : " not" );
02927     (*Str) += TempStr;
02928 
02929     // output the path's handle information.
02930     (*Str) += TEXT( "Num\tType\tX Coord\tY Coord\r\n" );
02931     PathVerb *  Verbs   = GetVerbArray();
02932     DocCoord *  Coords  = GetCoordArray();
02933     PathFlags * Flags   = GetFlagArray();
02934     for (INT32 i=0; i < nSlots; i ++)
02935     {
02936         String_32 VerbStr;
02937         VerbStr._MakeMsg(TEXT("#1%d ("), Verbs[i]);
02938         if (Verbs[i] & PT_CLOSEFIGURE)  VerbStr += TEXT("S");
02939         if (Verbs[i] & PT_LINETO)       VerbStr += TEXT("L");
02940         if (Verbs[i] & PT_BEZIERTO)     VerbStr += TEXT("C");
02941         if (Verbs[i] & PT_MOVETO)       VerbStr += TEXT("M");
02942         VerbStr += TEXT(")");
02943 
02944         TempStr._MakeMsg( TEXT("#1%d.\t#2%S\t#3%ld,\t#4%ld\t"),
02945                           i, &VerbStr, Coords[i].x, Coords[i].y );
02946 
02947         if (Flags[i].IsSmooth)      TempStr += TEXT("S");
02948         if (Flags[i].IsRotate)      TempStr += TEXT("R");
02949         if (Flags[i].IsEndPoint)    TempStr += TEXT("E");
02950         if (Flags[i].IsSelected)    TempStr += TEXT("(sel)");
02951 
02952         TempStr += TEXT("\r\n");
02953         (*Str) += TempStr;
02954     }
02955 #endif
02956 }
02957 
02958 
02959 #if !defined(EXCLUDE_FROM_XARLIB)
02960 
02961 /********************************************************************************************
02962 
02963 >   void Path::RenderPathBlobs(Spread* pSpread)
02964 
02965     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02966     Created:    24/02/94
02967     Inputs:     pSpread - spread to render into
02968     Purpose:    Draws all the selection blobs for a path. Useful for putting the blobs on or
02969                 taking them back off again.
02970                 This goes into its own RenderOnTop loop and calls RenderPathBlobs(RenderRegion* pRender).
02971 
02972 ********************************************************************************************/
02973 
02974 void Path::RenderPathBlobs(Spread* pSpread)
02975 {
02976 #if !defined(EXCLUDE_FROM_RALPH)
02977     // Go into a RenderOnTop loop
02978     RenderRegion* pRender = DocView::RenderOnTop(NULL, pSpread, ClippedEOR);
02979     while (pRender!=NULL)
02980     {
02981         RenderPathBlobs(pRender);
02982         pRender = DocView::GetNextOnTop(NULL);
02983     }
02984 #endif
02985 }
02986 
02987 
02988 
02989 /********************************************************************************************
02990 
02991 >   void Path::RenderPathBlobs(RenderRegion* pRender)
02992 
02993     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> (split from pSpread varient by Markn)
02994     Created:    9/11/94
02995     Inputs:     pRender - The render region to draw into
02996     Purpose:    Draws all the selection blobs for a path. Useful for putting the blobs on or
02997                 taking them back off again
02998 
02999 ********************************************************************************************/
03000 
03001 void Path::RenderPathBlobs(RenderRegion* pRender)
03002 {
03003 #if !defined(EXCLUDE_FROM_RALPH)
03004     // Dereference the pointers
03005     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
03006     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
03007     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
03008 
03009     BOOL PrevIsEndPoint;
03010     DocCoord StartCoord = Coords[0];
03011 
03012     // loop through the path and render each of the blobs
03013     INT32 endcount = 0;     // Count the number of selected endpoints
03014     INT32 EndPtIndex = 0;   // Index of only selected endpoint
03015     for (INT32 i=0; i<UsedSlots; i++)
03016     {
03017         if (Flags[i].IsEndPoint)
03018         {
03019             // Render a blob
03020             if (!((Verbs[i] & PT_CLOSEFIGURE) && Coords[i]==StartCoord))
03021             {
03022                 DrawBlob(pRender, Coords[i], Flags[i].IsSelected);
03023                 // count the number of selected endpoints
03024                 if (Flags[i].IsSelected)
03025                 {
03026                     EndPtIndex = i;
03027                     endcount++;
03028                 }
03029             }
03030             //GdiFlush();
03031             PrevIsEndPoint = TRUE;
03032             if (Verbs[i] == PT_MOVETO)
03033                 StartCoord = Coords[i];
03034         }
03035     }
03036     // Now render the control point blobs if there was just one selected endpoint
03037     if (endcount == 1)
03038     {
03039         RenderPathControlBlobs(pRender, EndPtIndex);
03040     }
03041 #endif
03042 }
03043 
03044 
03045 
03046 /********************************************************************************************
03047 
03048 >   void Path::RenderPathSelectedControlBlobs(Spread* pSpread, BOOL Removing = TRUE)
03049 
03050     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03051     Created:    23/1/95
03052     Inputs:     pSpread - pointer to the spread containing the path
03053                 Removing - TRUE if the blobs are being removed (ie rendered off)
03054     Purpose:    Draws all the control points selection blobs for an entire path. If you are
03055                 to change the selection flags on a path then call this to remove all selection
03056                 blobs, do your change, then call this again to draw the new blobs.
03057                 This goes into its own RenderOnTop loop.
03058 
03059 ********************************************************************************************/
03060 
03061 void Path::RenderPathSelectedControlBlobs(Spread* pSpread, BOOL Removing)
03062 {
03063 #if !defined(EXCLUDE_FROM_RALPH)
03064     // If removing then render on into pending render regions first
03065 //  if (Removing)
03066 //      RenderSelectedControlBlobsToPendingRegions(pSpread);
03067 
03068     // Render the blobs on the path
03069     RenderRegion* pRender = DocView::RenderOnTop(NULL, pSpread, ClippedEOR);
03070     while (pRender!=NULL)
03071     {
03072         RenderPathSelectedControlBlobs(pSpread, pRender);
03073         pRender = DocView::GetNextOnTop(NULL);
03074     }   
03075 
03076     // If putting back on then render back off in pender render regions
03077 //  if (!Removing)
03078 //      RenderSelectedControlBlobsToPendingRegions(pSpread);
03079 #endif
03080 }
03081 
03082 
03083 
03084 /********************************************************************************************
03085 
03086 >   void Path::RenderPathSelectedControlBlobs(Spread* pSpread, RenderRegion* pRender)
03087 
03088     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03089     Created:    28/5/95
03090     Inputs:     pSpread - pointer to the spread containing the path
03091                 pRender - pointer to region to render into
03092     Purpose:    Draws all the control points selection blobs for an entire path into a 
03093                 specified render region.  Called by the above function
03094     SeeAlso:    Path::RenderPathSelectedControlBlobs
03095 
03096 ********************************************************************************************/
03097 void Path::RenderPathSelectedControlBlobs(Spread* pSpread, RenderRegion* pRender)
03098 {
03099 #if !defined(EXCLUDE_FROM_RALPH)
03100     ERROR3IF(pSpread == NULL, "pSpread was NULL");
03101     ERROR3IF(pRender == NULL, "pRender was NULL");
03102     if ((pRender == NULL) || (pSpread == NULL))
03103         return ;
03104 
03105     // loop through the path and count the selected blobs
03106     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03107     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03108     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
03109     const INT32 NumCoords = GetNumCoords();
03110     INT32 endcount = 0;     // Count the number of selected endpoints
03111     INT32 EndPtIndex = 0;   // Index of only selected endpoint
03112     for (INT32 i=0; i<NumCoords; i++)
03113     {
03114         if (Flags[i].IsEndPoint && Flags[i].IsSelected)
03115         {
03116             if (!(Verbs[i] & PT_CLOSEFIGURE))
03117             {
03118                 EndPtIndex = i;
03119                 endcount++;
03120                 DrawBlob(pRender, Coords[i], TRUE);
03121                 DrawBlob(pRender, Coords[i], FALSE);
03122             }
03123         }
03124     }
03125     if (endcount == 1)
03126     {
03127         // Remove the control points and lines
03128         RenderPathControlBlobs(pRender, EndPtIndex);        
03129     }
03130 #endif
03131 }
03132 
03133 
03134 
03135 /********************************************************************************************
03136 
03137 >   void Path::RenderSelectedControlBlobsToPendingRegions(Spread* pSpread)
03138 
03139     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03140     Created:    28/5/95
03141     Inputs:     pSpread - pointer to the spread containing the path
03142     Purpose:    Draws all the control points selection blobs for an entire path into 
03143                 all the pending render regions.
03144     SeeAlso:    Path::RenderPathSelectedControlBlobs
03145 
03146 ********************************************************************************************/
03147 void Path::RenderSelectedControlBlobsToPendingRegions(Spread* pSpread)
03148 {
03149 #if !defined(EXCLUDE_FROM_RALPH)
03150     RenderRegionList* pRegionList = GetApplication()->GetRegionList();
03151     if (!pRegionList->IsEmpty())
03152     {
03153         RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead();  
03154 
03155         while (pRegion != NULL)
03156         {
03157             // Check the RenderRegion is for the same spread.
03158             if (pRegion->GetRenderSpread() == pSpread)
03159             {
03160                 // Render the blobs into this Render Region.
03161                 RenderPathSelectedControlBlobs(pSpread, pRegion);
03162             }
03163 
03164             // Get the Next render region
03165             pRegion = (RenderRegion*)pRegionList->GetNext(pRegion);
03166         }
03167     }
03168 #endif
03169 }
03170 
03171 
03172 /********************************************************************************************
03173 
03174 >   void Path::RenderPathControlBlobs(Spread* pSpread, INT32 EndPtIndex)
03175 
03176     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> (from Rik Code)
03177     Created:    19/10/94
03178     Inputs:     pSpread - pointer to the spread containing the path
03179                 EndPtIndex - the index of the endpoint around which the control points
03180                 are to be rendered.
03181     Purpose:    Draws the control points selection blobs for an endpoint on a path. Used
03182                 by RenderPathBlobs and also the Bezier tool.
03183                 This goes into its own RenderOnTop loop.
03184 
03185 ********************************************************************************************/
03186 
03187 void Path::RenderPathControlBlobs(Spread* pSpread, INT32 EndPtIndex)
03188 {
03189 #if !defined(EXCLUDE_FROM_RALPH)
03190     // Go into a RenderOnTop loop
03191     RenderRegion* pRender = DocView::RenderOnTop(NULL, pSpread, ClippedEOR);
03192     while (pRender!=NULL)
03193     {
03194         RenderPathControlBlobs(pRender,EndPtIndex);     
03195         pRender = DocView::GetNextOnTop(NULL);
03196     }
03197 #endif
03198 }
03199 
03200 
03201 
03202 /********************************************************************************************
03203 
03204 >   void Path::RenderPathControlBlobs(RenderRegion* pRender, INT32 EndPtIndex)
03205 
03206     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> (from Rik Code - split from pSpread varient by Markn)
03207     Created:    9/11/94
03208     Inputs:     pRender = ptr to render region to render into
03209                 EndPtIndex - the index of the endpoint around which the control points
03210                 are to be rendered.
03211     Purpose:    Draws all the control points selection blobs for a path. Used
03212                 by RenderPathBlobs and also the Bezier tool.
03213 
03214 ********************************************************************************************/
03215 
03216 void Path::RenderPathControlBlobs(RenderRegion* pRender, INT32 EndPtIndex)
03217 {
03218 #if !defined(EXCLUDE_FROM_RALPH)
03219     // Dereference the pointers
03220     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
03221 //  PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03222 
03223     // Render the appropriate control points
03224     // We render the two control points adjacent to the only
03225     // selected endpoint, but we also render the two control points beyond 
03226     // those, because their positions might affect the curve.
03227     // A special case is when we have a closed path containing 
03228     // only a moveto and a curveto (it could happen) in which case
03229     // we'll have to spot that, and only render two controls instead
03230     // of four
03231 
03232     // find the index of the previous ctrl point of this endpoint
03233     INT32 i = FindPrevControlPoint(EndPtIndex);
03234     if (i>=0)
03235     {
03236         DrawControlBlob(pRender, Coords[i]);
03237         DrawControlLine(pRender, Coords[i], Coords[EndPtIndex]);
03238         DrawControlBlob(pRender, Coords[i-1]);
03239         DrawControlLine(pRender, Coords[i-1], Coords[i-2]);
03240     }
03241 
03242     // now do the next control point. Don't render if the next
03243     // is still part of the same path
03244     INT32 j = FindNextControlPoint(EndPtIndex);
03245     if (j>=0 && j!=i-1)
03246     {
03247         DrawControlBlob(pRender, Coords[j]);
03248         DrawControlLine(pRender, Coords[j], Coords[EndPtIndex]);
03249 
03250         DrawControlBlob(pRender, Coords[j+1]);
03251         DrawControlLine(pRender, Coords[j+1], Coords[j+2]);
03252     }
03253 #endif
03254 }
03255 
03256 
03257 
03258 
03259 
03260 /********************************************************************************************
03261 
03262 >   void Path::RenderPathPenBlobs(Spread* pSpread)
03263 
03264     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03265     Created:    28/09/94
03266     Inputs:     pSpread - The render spread to draw into
03267     Purpose:    Draws all the selection blobs and pen blobs for a path. Useful for putting 
03268                 the blobs on or taking them back off again
03269 
03270 ********************************************************************************************/
03271 
03272 void Path::RenderPathPenBlobs(Spread* pSpread)
03273 {
03274 #if !defined(EXCLUDE_FROM_RALPH)
03275     // Go into a RenderOnTop loop
03276     RenderRegion* pRender = DocView::RenderOnTop(NULL, pSpread, ClippedEOR);
03277     while (pRender!=NULL)
03278     {
03279         RenderPathPenBlobs(pRender);
03280         pRender = DocView::GetNextOnTop(NULL);
03281     }
03282 #endif
03283 }
03284 
03285 
03286 /********************************************************************************************
03287 
03288 >   void Path::RenderPathPenBlobs(RenderRegion* pRegion)
03289 
03290     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> (via Rik)
03291     Created:    28/09/94
03292     Inputs:     pRegion - The render region to draw into
03293     Purpose:    Draws all the selection blobs and pen blobs for a path. Useful for putting 
03294                 the blobs on or taking them back off again
03295 
03296 ********************************************************************************************/
03297 
03298 void Path::RenderPathPenBlobs(RenderRegion* pRegion)
03299 {
03300 #if !defined(EXCLUDE_FROM_RALPH)
03301     // Dereference the pointers
03302     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
03303     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03304     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03305 
03306     DocCoord StartCoord = Coords[0];
03307     INT32 SelEndPts = 0;
03308 
03309     // loop through the path and render each of the blobs
03310     INT32 EndPtIndex = 0;   // Index of only selected endpoint
03311     INT32 i;
03312     for (i=0; i<UsedSlots; i++)
03313     {
03314         if (Flags[i].IsEndPoint)
03315         {
03316             // Render a blob
03317             if (!((Verbs[i] & PT_CLOSEFIGURE) && Coords[i]==StartCoord))
03318             {
03319                 DrawBlob(pRegion, Coords[i], Flags[i].IsSelected);
03320                 // count the number of selected endpoints
03321                 if (Flags[i].IsSelected)
03322                 {
03323                     EndPtIndex = i;
03324                     SelEndPts++;
03325                 }
03326             }
03327             if (Verbs[i] == PT_MOVETO)
03328                 StartCoord = Coords[i];
03329         }
03330     }
03331 
03332     // now make sure the correct control handles are drawn
03333     if (SelEndPts == 1)
03334     {
03335         if (Flags[EndPtIndex].IsRotate && IsOpenEnd(EndPtIndex))
03336         { 
03337             if (Verbs[EndPtIndex] == PT_MOVETO)
03338                 i = FindNextControlPoint(EndPtIndex);
03339             else
03340                 i = FindPrevControlPoint(EndPtIndex);
03341 
03342             if (i>=0)
03343             {
03344                 DocCoord GhostPt;
03345                 GhostPt.x = Coords[EndPtIndex].x - (Coords[i].x - Coords[EndPtIndex].x);
03346                 GhostPt.y = Coords[EndPtIndex].y - (Coords[i].y - Coords[EndPtIndex].y);
03347 
03348                 pRegion -> SetLineColour(COLOUR_BEZIERLINE);
03349                 pRegion -> SetFillColour(COLOUR_TRANS);
03350                 pRegion -> DrawLine(GhostPt, Coords[i]);
03351 
03352                 pRegion -> SetLineColour(COLOUR_TRANS);
03353                 pRegion -> SetFillColour(COLOUR_UNSELECTEDBLOB);
03354                 pRegion -> DrawBlob(GhostPt, BT_UNSELECTED);
03355                 pRegion -> DrawBlob(Coords[i], BT_UNSELECTED);
03356             }
03357         }
03358     }
03359 #endif
03360 }
03361 
03362 #endif  // EXCLUDE_FROM_XARLIB
03363 
03364 
03365 /********************************************************************************************
03366 
03367 >   INT32 Path::FindPrevControlPoint(INT32 Index)
03368 
03369     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
03370     Created:    23/8/94
03371     Inputs:     Index gives the endpoint whose previous control point I want
03372     Outputs:    -
03373     Returns:    Index to the control point (or -1 if none)
03374     Purpose:    This routine will scan back from an endpoint to find the attached bezier 
03375                 control point. It takes account of closed and open paths and complex
03376                 paths, and will wrap around the path if necessary.
03377     Errors:     -
03378     SeeAlso:    -
03379 
03380 ********************************************************************************************/
03381 
03382 INT32 Path::FindPrevControlPoint(INT32 Index)
03383 {
03384 //  DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
03385 //  PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03386     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03387     INT32 i;
03388     if (Verbs[Index] == PT_MOVETO)
03389     {
03390         // This is the start of a path - if the path is open, return -1
03391         // otherwise return the index of the last control point. If the last 
03392         // element is a line, return -1
03393         i = Index;
03394         FindEndOfSubPath(&i);
03395         // If the last segment isn't a bezier, return -1
03396         if (Verbs[i] != PT_BEZIERTO)
03397             return -1;
03398 
03399         // if the CLOSEFIGURE flag is set in the final element, return i+1
03400         // otherwise return -1
03401         if (Verbs[i+2] & PT_CLOSEFIGURE)
03402             return i+1;
03403         return -1;
03404     }
03405     else
03406     {
03407         // It isn't the start of a path, so see what kind of endpoint this is
03408         // and if it is a bezier, return the control point, otherwise return -1
03409         if ((Verbs[Index] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
03410             return Index-1;
03411         return -1;
03412     }
03413 }
03414 
03415 /********************************************************************************************
03416 
03417 >   INT32 Path::FindNextControlPoint(INT32 Index)
03418 
03419     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
03420     Created:    23/8/94
03421     Inputs:     Index gives the endpoint whose next control point I want
03422     Outputs:    -
03423     Returns:    Index to the control point (or -1 if none)
03424     Purpose:    This routine will scan forwards from an endpoint to find the attached 
03425                 bezier control point. It takes account of closed and open paths and 
03426                 complex paths, and will wrap around the path if necessary.
03427     Errors:     -
03428     SeeAlso:    -
03429 
03430 ********************************************************************************************/
03431 
03432 INT32 Path::FindNextControlPoint(INT32 Index)
03433 {
03434 //  DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
03435 //  PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03436     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03437     
03438     // See if this is the end of a path (no more elements or next point is moveto)
03439     if (Index+1 == UsedSlots || Verbs[Index+1] == PT_MOVETO)
03440     {
03441         // This is the last element, so see if we should wrap around
03442         if (Verbs[Index] & PT_CLOSEFIGURE)
03443         {
03444             // The path is closed, so we have to scan backwards to find the start
03445             INT32 i=Index;
03446             FindStartOfSubPath(&i);     // i points at start of subpath
03447             
03448             // return next control point if next element is a bezier, -1 otherwise
03449             if (Verbs[i+1] == PT_BEZIERTO)
03450                 return i+1;
03451             return -1;
03452         }
03453         else
03454         {
03455             // Path is open, so no next control point
03456             return -1;
03457         }
03458     }
03459     else
03460     {
03461         // This element isn't the last in this subpath, so look at next element
03462         // if not bezier, return -1, otherwise return next element
03463         if (Verbs[Index+1] == PT_BEZIERTO)
03464             return Index+1;
03465         return -1;
03466     }
03467     return -1;
03468 }
03469 
03470 
03471 #if !defined(EXCLUDE_FROM_XARLIB)
03472 
03473 /********************************************************************************************
03474 
03475 >   void Path::DrawBlob(RenderRegion* pRender, const DocCoord& Coord, BOOL DrawSelected)
03476 
03477     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03478     Created:    21/02/94
03479     Inputs:     pRender - Render region to draw to
03480                 Coord - the coord that the blob should be centred on
03481                 DrawSelected - Draw a selected blob or an unselected blob
03482     Purpose:    Draws a single blob, either selected or un-selected
03483 
03484 ********************************************************************************************/
03485 
03486 void Path::DrawBlob(RenderRegion* pRender, const DocCoord& Coord, BOOL DrawSelected)
03487 {
03488 #if !defined(EXCLUDE_FROM_RALPH)
03489     if (DrawSelected)
03490     {
03491         // Set the colours to draw the selected blob colours
03492         pRender->SetLineColour(COLOUR_BEZIERBLOB);
03493         pRender->SetFillColour(COLOUR_TRANS);
03494 
03495         // Draw a Selected blob to the render region
03496         pRender->DrawBlob(Coord, BT_SELECTED);
03497     }
03498     else
03499     {
03500         // Draw an UnSelected Blob to the render region
03501         // Set the colours back
03502 //      pRender->SetLineColour(COLOUR_UNSELECTEDBLOB);
03503         pRender->SetLineColour(COLOUR_NONE);
03504         pRender->SetFillColour(COLOUR_UNSELECTEDBLOB);
03505         pRender->DrawBlob(Coord, BT_UNSELECTED);
03506     }
03507 #endif
03508 }
03509 
03510 
03511 /********************************************************************************************
03512 
03513 >   void Path::DrawControlBlob(RenderRegion* pRender, const DocCoord& Coord)
03514 
03515     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03516     Created:    21/02/94
03517     Inputs:     pRender - Render region to draw to
03518                 Coord - the coord that the blob should be centred on
03519     Purpose:    Draws a blob for a Bezier control point
03520 
03521 ********************************************************************************************/
03522 
03523 void Path::DrawControlBlob(RenderRegion* pRender, const DocCoord& Coord)
03524 {
03525 #if !defined(EXCLUDE_FROM_RALPH)
03526     // Draw a Control blob to the render region
03527 //  pRender->SetLineColour(COLOUR_BEZIERBLOB);
03528     pRender->SetLineColour(COLOUR_NONE);        // A line colour leads to bad rendering probs
03529                                                 // on some video drivers. Best solution is not
03530                                                 // to use one.
03531     pRender->SetFillColour(COLOUR_BEZIERBLOB);
03532     pRender->DrawBlob(Coord, BT_UNSELECTED);
03533 #endif
03534 }
03535 
03536 
03537 /********************************************************************************************
03538 
03539 >   void Path::DrawControlLine(RenderRegion* pRender, const DocCoord& Start, const DocCoord& End)
03540 
03541     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03542     Created:    21/02/94
03543     Inputs:     pRender - Render region to draw to
03544                 Coord - the coord that the blob should be centred on
03545     Purpose:    Draws a line from the Bezier control point to its endpoint
03546 
03547 ********************************************************************************************/
03548 
03549 void Path::DrawControlLine(RenderRegion* pRender, const DocCoord& Start, const DocCoord& End)
03550 {
03551 #if !defined(EXCLUDE_FROM_RALPH)
03552     // Draw a line to the render region
03553     pRender->SetLineColour(COLOUR_BEZIERLINE);
03554     pRender->DrawLine( Start, End );
03555 #endif
03556 }
03557 
03558 #endif
03559 
03560 
03561 /********************************************************************************************
03562 
03563 >   void Path::ClearSubSelection()
03564 
03565     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03566     Created:    20/4/94
03567     Purpose:    Goes through the path and sets all the IsSelected flags to FALSE, clearing
03568                 any selected control points in the path
03569 
03570 ********************************************************************************************/
03571 
03572 void Path::ClearSubSelection()
03573 {
03574     // We need to find all the selected control points in the path and de-select them
03575     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
03576 
03577     for (INT32 i=0; i<UsedSlots; i++)
03578         Flags[i].IsSelected = FALSE;
03579 } 
03580 
03581 
03582 
03583 /********************************************************************************************
03584 
03585 >   void Path::EnsureSelection(BOOL UseStarts)
03586 
03587     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03588     Created:    5/1/95
03589     Inputs:     UseStarts - TRUE if the selection state of the start of subpaths should be used
03590                 the set the ends.  FALSE if the end sel state should be used to set the starts
03591     Purpose:    Goes through the path ensuring that the selection status of the ends/starts
03592                 of subpaths equal the selection state of the start/ends of that subpath
03593                 Also selects/deselectes control points as appropiate
03594 
03595 ********************************************************************************************/
03596 
03597 void Path::EnsureSelection(BOOL UseStarts)
03598 {
03599 #if !defined(EXCLUDE_FROM_RALPH)
03600     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03601     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03602 
03603     // Ensure the selection state of the sub paths
03604     const INT32 NumCoords = GetNumCoords();
03605     if (UseStarts)
03606     {
03607         INT32 Start = 0;
03608         INT32 End = 0;
03609         while (End < NumCoords-1)
03610         {
03611             FindEndElOfSubPath(&End);
03612             if (Verbs[End] & PT_CLOSEFIGURE)
03613                 Flags[End].IsSelected = Flags[Start].IsSelected;
03614             Start = ++End;
03615         }
03616     }
03617     else
03618     {
03619         INT32 Close = NumCoords-1;
03620         INT32 Offset = NumCoords-1;
03621         while (Offset > -1)
03622         {
03623             FindStartOfSubPath(&Offset);
03624             if (Verbs[Close] & PT_CLOSEFIGURE)
03625                 Flags[Offset].IsSelected = Flags[Close].IsSelected;
03626             Close = Offset-1;
03627             Offset = Close;
03628         }
03629     }
03630 
03631     // Now set the selection state of the  control points.depending on their endpoints
03632     for (INT32 loop = 0; loop < NumCoords; loop++)
03633     {
03634         if (!Flags[loop].IsEndPoint)
03635         {
03636             ERROR3IF(loop==0, "Path started with a non-endpoint!");
03637             ERROR3IF(Verbs[loop]!=PT_BEZIERTO, "Found an non-endpoint that wasn't a Bezier");
03638             if (loop > 0)
03639             {
03640                 if (Flags[loop-1].IsEndPoint)
03641                     Flags[loop].IsSelected = Flags[loop-1].IsSelected;
03642                 else
03643                     Flags[loop].IsSelected = Flags[loop+1].IsSelected;
03644             }
03645         }
03646     }
03647 #endif
03648 }
03649 
03650 
03651 
03652 /********************************************************************************************
03653 
03654 >   void Path::SetAllSubSelection()
03655 
03656     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03657     Created:    5/1/95
03658     Purpose:    Goes through the path and sets all the IsSelected flags to TRUE,  also
03659                 selecting all control points in the path
03660 
03661 ********************************************************************************************/
03662 
03663 void Path::SetAllSubSelection()
03664 {
03665     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
03666 
03667     for (INT32 i=0; i<UsedSlots; i++)
03668         Flags[i].IsSelected = TRUE;
03669 }
03670 
03671 
03672 
03673 /********************************************************************************************
03674 
03675 >   BOOL Path::IsSubSelection()
03676 
03677     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03678     Created:    20/4/94
03679     Returns:    TRUE if any of the paths coords were selected
03680     Purpose:    determine if the path has a subselection
03681 
03682 ********************************************************************************************/
03683 
03684 BOOL Path::IsSubSelection()
03685 {
03686     // We need to find all the selected control points in the path and de-select them
03687     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
03688 
03689     // look for a selected coord
03690     for (INT32 i=0; i<UsedSlots; i++)
03691     {
03692         // if we find one then return TRUE
03693         if (Flags[i].IsSelected)
03694             return TRUE;
03695     }
03696 
03697     // we did not find one, so return FALSE
03698     return FALSE;
03699 }
03700 
03701 
03702 
03703 /********************************************************************************************
03704 
03705 >   BOOL Path::FindSelectionLimits(INT32 index, INT32* p, INT32* q)
03706 
03707     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03708     Created:    3/11/94
03709     Inputs:     index   = search start point
03710     Outputs:    p       = index of first selected point in region
03711                 q       = index of last selected point in region
03712 
03713     Returns:    TRUE    if the point set {index,....,UsedSlots-1} contains a set of selected 
03714                         control handles {p,...,q} where p and q are the limits of the 
03715                         selection and all points between are selected.
03716                 FALSE   if no points are selected within the region.
03717                  
03718     Purpose:    Finds the next connected region of selected control handles.
03719 
03720 ********************************************************************************************/
03721 
03722 BOOL Path::FindSelectionLimits(INT32 index, INT32* p, INT32* q)
03723 {
03724     ENSURE( index<UsedSlots, "Path position was not valid in FindSelectionLimits" );
03725     ENSURE( index>=0, "Path Position less than zero in FindSelectionLimits" );
03726 
03727     INT32 start = index;
03728 
03729     // clamp the index
03730     if (start<0)
03731         start=0;
03732     if (start>=UsedSlots)
03733         start=UsedSlots-1;
03734 
03735     if (!FindNextSelected(&start))
03736         return FALSE;
03737 
03738     INT32 end = start;
03739     INT32 search = start;
03740         
03741     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03742     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03743 
03744     BOOL done = FALSE;
03745 
03746     while (FindNextEndPoint(&search) && (!done))
03747     {
03748         if ((Flags[search].IsSelected) && (Flags[search].IsEndPoint))
03749         {
03750             if (Verbs[search] != PT_MOVETO)
03751                 end = search;
03752             else
03753                 done = TRUE;
03754         }
03755         else
03756             done = TRUE;
03757     }
03758 
03759     *p = start;
03760     *q = end; 
03761 
03762     return TRUE;
03763 }
03764 
03765 
03766 
03767 /********************************************************************************************
03768 
03769 >   void Path::ExpandRange(INT32* start, INT32* end, INT32 num)
03770 
03771     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03772     Created:    3/11/94
03773     Inputs:     start   = pointer to an index holding the region start
03774                 end     = pointer to an index holding the region end
03775                 num     = number of endpoints to expand the region by 
03776     Outputs:    start   = will be updated
03777                 end     = will be updated
03778     Purpose:    given the region [*start, *end] expand the region in both directions
03779                 by num elements. The region will not be expanded beyond its outer limits
03780                 described by the set {moveto,.....,endofsubpath}.
03781     
03782 ********************************************************************************************/
03783 
03784 void Path::ExpandRange(INT32* start, INT32* end, INT32 num)
03785 {
03786     
03787     ENSURE( (*start)<UsedSlots, "start index was not valid in ExpandRange" );
03788     ENSURE( (*start)>=0, "start index less than zero in ExpandRange" );
03789 
03790     ENSURE( (*end)<UsedSlots, "end index was not valid in ExpandRange" );
03791     ENSURE( (*end)>=0, "end index less than zero in ExpandRange" );
03792 
03793     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
03794     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03795 
03796     // clamp the indexes
03797 
03798     INT32 starti = *start;
03799     INT32 endi = *end;
03800 
03801     if (starti<0) 
03802         starti = 0;
03803     if (endi<0)
03804         endi = 0;
03805 
03806     if (starti >= UsedSlots)
03807         starti = UsedSlots-1;
03808     if (endi >= UsedSlots)
03809         endi = UsedSlots-1;
03810 
03811     INT32 prev = starti;
03812     INT32 next = endi;
03813 
03814     while (num>0)
03815     {
03816         if (Verbs[starti] != PT_MOVETO)
03817         { 
03818             if (FindPrev(&prev))
03819             {
03820                 if (Flags[prev].IsEndPoint)
03821                     starti = prev;
03822             }
03823         }
03824 
03825         if (FindNextEndPoint(&next))
03826         {   
03827             if ((Flags[next].IsEndPoint) &&
03828                 (Verbs[next] != PT_MOVETO))
03829                     endi = next;
03830         }                   
03831         num-=1;
03832     }
03833     *start = starti;
03834     *end = endi;
03835 }
03836 
03837 
03838 
03839 
03840 /********************************************************************************************
03841 
03842 >   BOOL Path::MakeSpaceInPath(INT32 NumSlots)
03843 
03844     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03845     Created:    01/02/94
03846     Inputs:     NumSlots - The number of slots you need to put your new thing in
03847     Returns:    TRUE    if it was able to find the memory,
03848                 FALSE   if it failed
03849     Purpose:    Checks to see if there is already enough memory available to make the
03850                 insertion and if not will try to allocate some more memory.
03851                 If this works it will move the path 
03852                 (after the position indicated by the path class variable CurrentPos)
03853                 along to make room for the correct number of slots at that point.
03854 
03855 ********************************************************************************************/
03856 
03857 BOOL Path::MakeSpaceInPath(INT32 NumSlots)
03858 {
03859     if (!MakeSpaceAtEnd(NumSlots))
03860         return FALSE;
03861 
03862     return OpenGap(NumSlots,CurrentPos);
03863 }
03864 
03865 
03866 /********************************************************************************************
03867 
03868 >   BOOL Path::MakeSpaceInPath(INT32 NumSlots, INT32 Position)
03869 
03870     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03871     Created:    01/02/94
03872     Inputs:     NumSlots - The number of slots you need to put your new thing in
03873                 Position - The position at which to put the slots
03874     Returns:    TRUE    if it was able to find the memory, 
03875                 FALSE   if it failed
03876     Purpose:    Checks to see if there is already enough memory available to make the
03877                 insertion and if not will try to allocate some more memory. If this works
03878                 it will move the path after the position indicated by Position along to make
03879                 room for the correct number of slots at that point.
03880 
03881 ********************************************************************************************/
03882 
03883 BOOL Path::MakeSpaceInPath(INT32 NumSlots, INT32 Position)
03884 {
03885     if (!MakeSpaceAtEnd(NumSlots))
03886         return FALSE;
03887 
03888     return OpenGap(NumSlots,Position);
03889 }
03890 
03891 
03892 
03893 
03894 /********************************************************************************************
03895 
03896 >   BOOL Path::MakeSpaceAtEnd(INT32 NumSlots)
03897 
03898     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 
03899     Created:    01/02/94
03900     Inputs:     NumSlots - The number of Unused slots you need free at the end of the path
03901     Returns:    TRUE    if it was able to find the memory, 
03902                 FALSE   if it failed
03903     Purpose:    Will make enough room for NumSlots unused slots at the end of the path. 
03904 
03905 ********************************************************************************************/
03906 
03907 BOOL Path::MakeSpaceAtEnd(INT32 NumSlots)
03908 {
03909     ERROR2IF(SlotAllocSize <= 0,FALSE,"MakeSpaceAtEnd has ZERO Slot Allocation Size!");
03910     if (UnUsedSlots<NumSlots)
03911     {
03912         // Find out how many slots to ask for and make sure it is a multiple of 4
03913         INT32 SlotsNeeded = NumSlots-UnUsedSlots;
03914             SlotsNeeded = ((INT32)(SlotsNeeded/SlotAllocSize))*SlotAllocSize+SlotAllocSize;
03915             SlotsNeeded = ((SlotsNeeded+3) >> 2) << 2;
03916 
03917         // we are out of spare slots, so try to alloc some extra ones
03918         if (!IncreaseBlock(VerbHandle, sizeof(PathVerb)*SlotsNeeded))
03919             return FALSE;
03920                 
03921         if (!IncreaseBlock(CoordHandle, sizeof(DocCoord)*SlotsNeeded))
03922             return FALSE;
03923 
03924         if (!IncreaseBlock(FlagsHandle, sizeof(PathFlags)*SlotsNeeded))
03925             return FALSE;
03926 
03927         if (ExtraInfo != NULL)
03928             if (!ExtraInfo->IncreaseExtraBlocks(SlotsNeeded))
03929                 return FALSE;
03930 
03931         // Got the memory back that we asked for, so update a few vars
03932         UnUsedSlots += SlotsNeeded;
03933     }
03934     return TRUE;
03935 }
03936 
03937 /********************************************************************************************
03938 
03939 >   BOOL Path::OpenGap(INT32 NumSlots)
03940 
03941     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 
03942     Created:    01/02/94
03943     Inputs:     NumSlots - The size of the gap to open
03944     Returns:    TRUE    if it was able to find the memory, 
03945                 FALSE   if it failed
03946     Purpose:    Will open a gap of NumSlots size in this path. The gap is positioned at
03947                 CurrentPos (A path class variable)
03948 
03949 ********************************************************************************************/
03950 
03951 BOOL Path::OpenGap(INT32 NumSlots)
03952 {
03953     return OpenGap(NumSlots, CurrentPos);
03954 }
03955 
03956 /********************************************************************************************
03957 
03958 >   BOOL Path::OpenGap(INT32 NumSlots, INT32 Position)
03959 
03960     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 
03961     Created:    01/02/94
03962     Inputs:     NumSlots = The size of the gap to open
03963                 Position = The index at which to open the gap (0...UsedSlots)
03964     Returns:    TRUE    if it was able to open the gap
03965                 FALSE   if it failed
03966     Purpose:    Will open up a gap of NumSlots at the position Position within the path.
03967                 If Position is set to the end of the path ie the same value as usedslots, 
03968                 no data will be moved.
03969                 FALSE will ofcourse always be returned if there is not enough free space
03970                 in the form of unused slots on the end of the path for the gap to open.
03971                 Use MakeSpaceInPath() if you know there isn't enough room.
03972     See Also:   MakeSpaceInPath, MakeSpaceAtEnd
03973 
03974 ********************************************************************************************/
03975 
03976 BOOL Path::OpenGap(INT32 NumSlots, INT32 Position)
03977 {
03978     if (UnUsedSlots<NumSlots)
03979     {          
03980         ERROR3("Path::OpenGap() overflow: Gap request is too large");
03981         return FALSE;
03982     }
03983 
03984     if (Position<0 || Position>UsedSlots)
03985     {
03986         ERROR3("Path::OpenGap() called with an illegal gap position");
03987         return FALSE;
03988     }
03989 
03990     INT32 SlotsToMove = UsedSlots-Position;
03991 
03992     if (SlotsToMove>0)
03993     {
03994         PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
03995         memmove( (void*)(&Verbs[Position+NumSlots]), (void*)(&Verbs[Position]), SlotsToMove*sizeof(PathVerb) );
03996 
03997         DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
03998         memmove( (void*)&Coords[Position+NumSlots], (void*)&Coords[Position], SlotsToMove*sizeof(DocCoord) );
03999 
04000         PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
04001         memmove( (void*)&Flags[Position+NumSlots], (void*)&Flags[Position], SlotsToMove*sizeof(PathFlags) );
04002 
04003         if (ExtraInfo != NULL)
04004             ExtraInfo->ShiftUpExtraInfo(Position, NumSlots, SlotsToMove);
04005     }
04006 
04007     return TRUE;
04008 }
04009 
04010 
04011 /********************************************************************************************
04012 
04013 >   BOOL Path::Compact()
04014 
04015     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
04016     Created:    17/11/94
04017     Inputs:     -
04018     Returns:    FALSE if the path failed to compact
04019     Purpose:    Removes any extraneous unused slots in a path. It will a maximum of 
04020                 2n-1 slots free where n is the size of an allocation chunk when increasing
04021                 the buffer (see Path::Initialise)
04022 
04023 ********************************************************************************************/
04024 
04025 BOOL Path::Compact()
04026 {
04027     // Calculate the buffer reduction factor.
04028     INT32 reduction = ((UnUsedSlots / SlotAllocSize)-1)*SlotAllocSize;
04029 
04030     if (reduction>0)
04031     {
04032         // reduce the number of free slots NOW, incase m out of the n
04033         // following reductions fail.
04034         UnUsedSlots -= reduction;
04035 
04036         if ((DecreaseBlock(VerbHandle, sizeof(PathVerb)*reduction)==FALSE) ||
04037             (DecreaseBlock(FlagsHandle, sizeof(PathFlags)*reduction)==FALSE) ||
04038             (DecreaseBlock(CoordHandle, sizeof(DocCoord)*reduction)==FALSE) ||
04039             (ExtraInfo!=NULL && ExtraInfo->DecreaseExtraBlocks(reduction)==FALSE))
04040         {
04041             // Memory shrink failed, so pass that back to the person using the Path
04042             // The path should still be useable at this stage so long as all handles
04043             // are valid.
04044             return FALSE;
04045         }
04046     }
04047     return TRUE;
04048 }
04049 
04050 
04051 /********************************************************************************************
04052 
04053 >   BOOL Path::InsertSection(INT32 StartSlot, INT32 NumSlots)
04054 
04055     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
04056     Created:    27/6/94
04057     Inputs:     StartSlot is the path slot where we want to insert the section
04058                 NumSlots is how many slots we want to insert
04059     Outputs:    -
04060     Returns:    TRUE if successful, FALSE otherwise
04061     Purpose:    This routine is the corollary to DeleteSection - it inserts a section
04062                 into the path at a given position and a given size, but does not put
04063                 any data into the path. Note that this routine is likely to move the
04064                 path data around in memory, so after calling it you should re-read any
04065                 pointers to the path you might have had.
04066     Errors:     -
04067     SeeAlso:    Path::DeleteSection
04068 
04069 ********************************************************************************************/
04070 
04071 BOOL Path::InsertSection(INT32 StartSlot, INT32 NumSlots)
04072 {
04073     INT32 OldPosition = CurrentPos;
04074     CurrentPos = StartSlot;
04075     if (!MakeSpaceInPath(NumSlots))
04076     {
04077         CurrentPos = OldPosition;
04078         return FALSE;
04079     }
04080     CurrentPos = OldPosition;
04081     UsedSlots+=NumSlots;
04082     UnUsedSlots -= NumSlots;
04083     return TRUE;
04084 }
04085 
04086 
04087 /********************************************************************************************
04088 
04089 >   BOOL Path::EnsureVolume(INT32 NumSlots)
04090 
04091     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
04092     Created:    02/02/95
04093     Inputs:     NumSlots - The minimum number of slots you need in the path arrays
04094     Returns:    TRUE if it was able to find the memory, FALSE if it failed
04095     Purpose:    This function ensures there are >= NumSlots in the path arrays.
04096                 It may try to alter the size of the path arrays by increasing the number
04097                 of unused slots to satisfy the request. It will never decrease the size of
04098                 the arrays, if NumSlots < UsedSlots+UnusedSlots then the volume is
04099                 automatically ensured. This allows path buffers to be resized ready for
04100                 cloning.
04101                 
04102 ********************************************************************************************/
04103 
04104 BOOL Path::EnsureVolume(INT32 NumSlots)
04105 {
04106     INT32 Slots=NumSlots-UsedSlots;
04107     if (Slots>0)
04108         return MakeSpaceAtEnd(Slots);
04109     return TRUE;
04110 }
04111 
04112 
04113 /********************************************************************************************
04114 
04115 >   void Path::SmoothCurve( BOOL SetRenderFlags = FALSE, 
04116                             BOOL SnapEnds = FALSE, 
04117                             INT32 SnapIndex = 0)
04118 
04119     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
04120     Created:    22/02/94
04121     Inputs:     SetRenderFlag is TRUE if the smoothing should set the NeedToRender flags on points
04122                 as it's smoothing.
04123                 SnapEnds tells the routine to snap two open ends together
04124                 SnapIndex is an index into the subpath which is to be snapped
04125     Purpose:    Smooths the path on the areas that have the IsSmooth flag set to TRUE.
04126                 This can change any or all of the path.
04127 
04128 ********************************************************************************************/
04129 
04130 void Path::SmoothCurve(BOOL SetRenderFlags, BOOL SnapEnds, INT32 SnapIndex)
04131 {
04132 
04133     // This is a less efficient (slightly) version of this routine (in that there is a 
04134     // certain amount of moving backward and forward in the path, but only for the start
04135     // and end points) but it copes with end snapping
04136 
04137     DocCoord* Coords = GetCoordArray();
04138     PathVerb* Verbs = GetVerbArray();
04139     PathFlags* Flags = GetFlagArray();
04140     INT32 StartSnapSub = 0;
04141     INT32 EndSnapSub = 0;       // Start and end of the subpath we're snapping
04142     DocCoord tempcoord(0,0);
04143     
04144     // Find the start and end of the subpath we're snapping
04145     if (SnapEnds)
04146     {
04147         StartSnapSub = EndSnapSub = SnapIndex;
04148         FindStartOfSubPath(&StartSnapSub);
04149         FindEndElOfSubPath(&EndSnapSub);
04150     }
04151     
04152     if (SetRenderFlags)
04153     {
04154         // As well as smoothing, we also set the NeedToRender flag on any path that's changed.
04155         // Notice that we never clear the NeedToRender flags - if they're rendered once, they're
04156         // rendered forever.
04157 
04158         for (INT32 i = 0; i < UsedSlots; i++)
04159         {
04160             if (Verbs[i] == PT_BEZIERTO && !(Flags[i].IsEndPoint) && Flags[i].IsSmooth)
04161             {
04162                 tempcoord = SmoothControlPoint(i, (SnapEnds && (i>=StartSnapSub && i<=EndSnapSub)));
04163                 if (Coords[i] != tempcoord)
04164                 {
04165                     if (Flags[i+1].IsEndPoint)
04166                         Flags[i+1].NeedToRender = TRUE;
04167                     else
04168                         Flags[i+2].NeedToRender = TRUE;
04169                 }
04170                 Coords[i] = tempcoord;
04171             }
04172         }
04173 
04174     }
04175     else
04176     {
04177         for (INT32 i = 0; i < UsedSlots; i++)
04178         {
04179             if (Verbs[i] == PT_BEZIERTO && !(Flags[i].IsEndPoint) && Flags[i].IsSmooth)
04180                 Coords[i] = SmoothControlPoint(i, (SnapEnds && (i>=StartSnapSub && i<=EndSnapSub)));
04181         }
04182     }
04183 }
04184 
04185 
04186 /*
04187 {
04188     // these are used to remember the position of the last endpoint and the one before that
04189 
04190     INT32 LastEndPoint = 0;
04191     INT32 LastLastEndPoint = 0;
04192 
04193     // get pointers to the arrays and find the number of coords
04194     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
04195     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
04196     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
04197 
04198     for (INT32 i=0; i<UsedSlots; i++)
04199     {
04200         if (Flags[i].IsEndPoint)
04201         {
04202             if (Flags[LastEndPoint].IsSmooth)
04203             {
04204                 // Find out the most recent verbs
04205                 PathVerb LastVerb = Verbs[LastEndPoint] & (~PT_CLOSEFIGURE);
04206                 PathVerb ThisVerb = Verbs[i] & (~PT_CLOSEFIGURE);
04207 
04208                 // The Previous point had the smooth flag on, so we had better deal with it
04209                 switch (LastVerb)
04210                 {
04211                     case PT_MOVETO :
04212                         if (ThisVerb == PT_BEZIERTO)
04213                             CalcPointEnd( Coords[i], Coords[LastEndPoint], &Coords[i-2] );
04214                         break;
04215 
04216                     case PT_LINETO :
04217                         if (ThisVerb == PT_BEZIERTO)
04218                             CalcPointLine( Coords[i], Coords[LastEndPoint], Coords[LastLastEndPoint], &Coords[i-2] ); 
04219                         break;
04220 
04221                     case PT_BEZIERTO :
04222                         if (ThisVerb == PT_BEZIERTO)
04223                             CalcDoubleCurve( Coords[LastLastEndPoint], Coords[LastEndPoint], Coords[i], &Coords[LastEndPoint-1], &Coords[i-2] );
04224 
04225                         if (ThisVerb == PT_LINETO)
04226                             CalcPointLine( Coords[LastLastEndPoint], Coords[LastEndPoint], Coords[i], &Coords[LastEndPoint-1] );
04227 
04228                         if (ThisVerb == PT_MOVETO)
04229                             CalcPointEnd( Coords[LastLastEndPoint], Coords[LastEndPoint], &Coords[LastEndPoint-1] );
04230 
04231                         break;
04232                         
04233                     default:
04234                         break;
04235                 }
04236             }
04237 
04238             // Get all the past coords worked out
04239             LastLastEndPoint = LastEndPoint;
04240             LastEndPoint = i;
04241         }
04242     }
04243 
04244 
04245 
04246 }
04247 
04248 */
04249 
04250 
04251 /********************************************************************************************
04252 
04253 >   void Path::SmartSmoothCurve(BOOL StartAndEndSnapped,
04254                                 BOOL JoinedToAnother,
04255                                 Path* OtherPath,
04256                                 BOOL JoinStartOfThis,
04257                                 BOOL JoinStartOfOther,
04258                                 BOOL RecordUndo,
04259                                 NodePath* UndoPath,
04260                                 Operation* UndoOperation)
04261 
04262     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
04263     Created:    5/7/94
04264     Inputs:     StartAndEndSnapped  TRUE if this path's start and end points should snap
04265                 JoinedToAnother     Flag which tells me if an endpoint on this path has
04266                                     temporarily been joined to another path
04267                 OtherPath           This is the path it's joined to if so
04268                 JoinStartOfThis     TRUE if it's the start of this path that's joined
04269                 JoinStartofOther    TRUE if it's the start of the other that's joined
04270                 RecordUndo          TRUE if undo information should be recorded
04271                 UndoPath            The NodePath that this path belongs to (undo purposes)
04272                 UndoOperation       Currently executing operation (undo purposes)
04273     Outputs:    -
04274     Returns:    -
04275     Purpose:    This function will handle the smoothing of a path. It is designed to take
04276                 account of all the possibilities that might occur (hence the huge list of 
04277                 parameters). The various places where this function might be needed are:
04278 
04279                 *   While dragging around points on a single path - this uses a temporary 
04280                     path and does not need to worry about undo. The only possibility for 
04281                     strangeness here is that the start and end points might snap together.
04282 
04283                 *   While dragging around points on more than one path - not only might start 
04284                     and end snap, but the dragged point might need to snap to other paths 
04285                     being dragged at the same time
04286 
04287                 *   When operating directly on a NodePath (for example when toggling the 
04288                     smoothness of a point). This is when Undo needs to be worried about. 
04289                     In this case it is likely that all the other flags will be default values.
04290 
04291     Errors:     -
04292     SeeAlso:    -
04293 
04294 ********************************************************************************************/
04295 
04296 /*
04297 void Path::SmartSmoothCurve(BOOL StartAndEndSnapped,
04298                             BOOL JoinedToAnother,
04299                             Path* OtherPath,
04300                             BOOL JoinStartOfThis,
04301                             BOOL JoinStartOfOther,
04302                             BOOL RecordUndo,
04303                             NodePath* UndoPath,
04304                             Operation* UndoOperation)
04305 {
04306     INT32 NumPointsChanged = 0;             // If we're undoing, we need to count
04307 }
04308 
04309 */
04310 
04311 /********************************************************************************************
04312 
04313 >   DocCoord Path::SmoothControlPoint(  INT32 Index,
04314                                         BOOL StartAndEndSnapped,
04315                                         BOOL JoinedToAnother,
04316                                         BOOL OtherIsCurve,
04317                                         const DocCoord& OtherCoord)
04318 
04319     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
04320     Created:    5/7/94
04321     Inputs:     Index               Index into this path of the control point
04322                 StartEndSnapped     TRUE if the start of the path snaps to the end
04323                 JoinedToAnother     TRUE if this path joins to another one
04324                 OtherIsCurve        TRUE if the other path element is a curve
04325                                     FALSE if other path element is a line
04326                 OtherCoord          Coordinate of element in other path
04327 
04328     Outputs:    -
04329     Returns:    Coordinates of the smoothed point
04330     Purpose:    Calculates the smoothed position of a control point in a path. The smoothing
04331                 is calculated based on the endpoint to which the control point is attached, 
04332                 and the endpoints on either side of this point. Since we are guaranteed that 
04333                 one of these segments is a curve (that's where the control point comes from)
04334                 the other possibilities are: The curve just terminates (at the end of a path);
04335                 The curve is attached to another curve; It is attached to a line; It is at the end, 
04336                 but the end joins the start of the same path, which could be a line or a curve; 
04337                 and the curve could be at the end, but snapping to a completely separate path segment
04338                 which might either be a line or curve (this is what the JoinedToAnother, OtherIsCurve and
04339                 OtherCoord parameters indicate.
04340 
04341     Errors:     -
04342     SeeAlso:    Path::SmartSmoothCurve
04343     Documentation:  Implemnt\smooth.doc
04344 
04345 ********************************************************************************************/
04346 
04347 DocCoord Path::SmoothControlPoint(  INT32 Index,
04348                                     BOOL StartAndEndSnapped,
04349                                     BOOL JoinedToAnother,
04350                                     BOOL OtherIsCurve,
04351                                     const DocCoord& OtherCoord)
04352 {
04353 #if !defined(EXCLUDE_FROM_RALPH)
04354     PathVerb* Verbs = GetVerbArray();
04355     DocCoord* Coords = GetCoordArray();
04356     PathFlags* Flags = GetFlagArray();
04357 
04358     // temp vars for calculating smoothness
04359     
04360     DocCoord p;     // Endpoint opposite the control point
04361     DocCoord q;     // Endpoint directly adjacent to control point
04362     DocCoord r;     // Endpoint on same side of control point but not adjacent
04363     DocCoord ret;   // Temp for returning value
04364 
04365     // We get the parameters for the calculation from different places depending on whether we're
04366     // at the first or second control point. We know this by looking at the EndPoint flags
04367 
04368     if (Flags[Index-1].IsEndPoint)
04369     {
04370         // This is the first control point of a curve
04371 
04372 /*
04373 
04374     These are the possibilities with the control point as the first one in the curve:
04375 
04376     1: Curve/Curve
04377     ...
04378     m/l/c   p
04379     c       a
04380     c       b
04381     c       q   <- endpoint to which point is attached
04382     c       c   <- Control point we're smoothing
04383     c       d
04384     c       r   
04385     ...
04386 
04387     2: Line/Curve
04388     ...
04389     m/l/c   p
04390     l       q
04391     c       a   <- Control point we're smoothing
04392     c       b
04393     c       r
04394     ...
04395 
04396     3: Curve/Open line
04397     m       q
04398     c       a   <- control point we're smoothing
04399     c       b
04400     c       r
04401     ...
04402 
04403     4: Curve/snapping to curve at end
04404     m       q
04405     c       a   <- control point we're snapping
04406     c       b
04407     c       r
04408     ...
04409     l/c     p
04410     c       c
04411     c       d
04412     c       q
04413 
04414     5: Curve/snapping to line at end
04415     m       q
04416     c       a   <- control point we're snapping to
04417     c       b
04418     c       r
04419     ...
04420     l/c     p
04421     l       q
04422 
04423     6: Curve/snapping to curve in another path
04424     7: Curve/snapping to line in another path
04425     m       q
04426     c       a   <- control point we're smoothing
04427     c       b
04428     c       r
04429     ...
04430 
04431     point p = OtherCoord
04432 */
04433 
04434         // The position of this point depends on: The previous endpoint (which is the point
04435         // to which this control point is 'attached'), the endpoint before that one, and the 
04436         // endpoint of the bezier segment containing the control point.
04437         // point q is the endpoint of the previous element (which always exists where we want it)
04438 
04439         q = Coords[Index-1];
04440 
04441         // point r is the endpoint of this curve element - again, always there
04442 
04443         r = Coords[Index+2];
04444 
04445         // point p, on the other hand, is problematical. It might not exist at all (if the 
04446         // previous element was a MoveTo and the path is open). It might be a lineto. It 
04447         // might be a CurveTo element, which is the simplest case. Or, it might be any of 
04448         // those three elements on a completely different path. Or it might be a curve or 
04449         // a line at the opposite end of the path (if the CLOSEFIGURE flag is set or the 
04450         // StartAndEndSnapped flag is TRUE).
04451 
04452         if (Verbs[Index-1] == PT_BEZIERTO)
04453         {
04454             // Simple - p = the start of this curve (case 1)
04455             p = Coords[Index-4];
04456             CalcPointCurve(p,q,r,&ret);
04457 
04458             // Bodge inserted by Peter to fix the problem that occurs when you have a closed path
04459             // consisting of just two Bezier segments.  In this case the returned position is wrong
04460             // - it positions the control point over the opposite one - so we will just flip it around
04461             /* Commented out by Markn 17/1/95 - replaced by new bodge written by Peter
04462             if ((GetNumCoords() == 7) && StartAndEndSnapped)
04463             {
04464                 ret = q - (ret - q);
04465             }
04466             */
04467 
04468             // New version of above bodge also by Peter (put in by Markn - 17/1/95)
04469             if ((GetNumCoords() == 7) && ((Verbs[GetNumCoords()-1] & PT_CLOSEFIGURE) || StartAndEndSnapped))
04470             {
04471                 ret = q - (ret - q);
04472             }
04473             return ret;
04474         }
04475         else if (Verbs[Index-1] == PT_LINETO)
04476         {
04477             // Simple - previous is a lineto, so get its start point and calculate (case 2)
04478             p = Coords[Index-2];
04479             CalcPointLine(p,q,r,&ret);
04480             return ret;
04481         }
04482         else
04483         {
04484             
04485             // Scan along to find the end element. This we find by scanning forward until
04486             // we find a MoveTo or we go off the end. Either way, the previous element is
04487             // the one we want.
04488 
04489             INT32 i = Index;
04490             while (i<UsedSlots && Verbs[i] != PT_MOVETO)
04491                 i++;
04492             
04493             // i either points after the end of the path, or at the next MoveTo. Move to the
04494             // previous element (could be the endpoint of a curve).
04495 
04496             i--;
04497             
04498             if (StartAndEndSnapped || (Verbs[i] & PT_CLOSEFIGURE) )
04499             {
04500                 // We have to get p from the end of the subpath (cases 4 & 5)
04501                 if ((Verbs[i] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
04502                 {
04503                     // Case 4
04504                     p = Coords[i-3];
04505                     CalcPointCurve(p,q,r,&ret);
04506 
04507                     // Bodge inserted by Peter to fix the problem that occurs when you have a path consisting
04508                     // of just two Bezier segments.  In this case the returned position is wrong - it
04509                     // positions the control point over the opposite one - so we will just flip it around
04510                     if (GetNumCoords() == 7)
04511                     {
04512                         ret = q - (ret - q);
04513                     }
04514 
04515                     return ret;
04516                 }
04517                 else
04518                 {
04519                     // Case 5
04520                     p = Coords[i-1];
04521                     CalcPointLine(p,q,r,&ret);
04522                     return ret;
04523                 }
04524             }
04525             else
04526             {
04527                 // This path hasn't snapped, and isn't closed. Check if it snaps with another
04528                 if (JoinedToAnother)
04529                 {
04530                     // Cases 6 & 7
04531                     p = OtherCoord;
04532                     if (OtherIsCurve)
04533                     {
04534                         // Case 6
04535                         CalcPointCurve(p,q,r,&ret);
04536                     }
04537                     else
04538                     {
04539                         // Case 7
04540                         CalcPointLine(p,q,r,&ret);
04541                     }
04542                     return ret;
04543                 }
04544                 else
04545                 {
04546                     // There's no snapping at all, so this point has to be calculated as an endpoint
04547                     CalcPointEnd(q,r,&ret);
04548                     return ret;
04549                 }
04550             }
04551         }
04552     }
04553     else
04554     {
04555         // This is the second control point of a curve
04556 
04557 /*
04558     These are the possible situations, with sections of path data
04559 
04560     1: Curve/Curve
04561         ...
04562         m/l/c   r
04563         c       a
04564         c       b   <- Control point pointed to by Index
04565         c       q   <- this is the endpoint to which the control point is connected
04566         c       c
04567         c       d
04568         c       p
04569         ...
04570 
04571     2: Curve/Line
04572         ...
04573         m/l/c   r
04574         c       a
04575         c       b   <- Control point pointed to by Index
04576         c       q   <- this is the endpoint to which the control point is connected
04577         l       p
04578         ...
04579 
04580     3: Curve/open line
04581         ...
04582         m/l/c   r
04583         c       a
04584         c       b   <- Control point pointed to by Index
04585         c       q   <- this is the endpoint to which the control point is connected
04586         either nothing or a moveto
04587         ...
04588 
04589     4: Curve/snapping to curve at start
04590         m       q
04591         c       c
04592         c       d
04593         c       p
04594         ...
04595         l/c     r
04596         c       a
04597         c       b   <- Control point pointed to by index
04598         c       q   <- Enpoint to which control is connected
04599         either nothing or moveto, CLOSEFIGURE or SnapToEnd set
04600         ...
04601 
04602     5: Curve/snapping to line at start
04603         m       q
04604         l       p
04605         ...
04606         l/c     r
04607         c       a
04608         c       b   <- Control point pointed to by index
04609         c       q   <- Enpoint to which control is connected
04610         either nothing or moveto, CLOSEFIGURE or SnapToEnd set
04611         ...
04612 
04613     6: Curve/snapping to curve in another path
04614     7: Curve/snapping to line in another path
04615         ...
04616         m/l/c   r
04617         c       a
04618         c       b   <- Control point pointed to by index
04619         c       q   <- Enpoint to which control is connected
04620         nothing/moveto
04621 
04622         point p = OtherCoord
04623 
04624 */
04625 
04626         // Point q is the endpoint of this curve
04627 
04628         q = Coords[Index+1];
04629 
04630         // Point r is the start point of this curve (always guaranteed to be there)
04631 
04632         r = Coords[Index-2];
04633 
04634         // Point p (as before) is difficult. It ight be a curve element. it might be a line
04635         // element. It might not exist at all (i.e. this might be the end point of an open subpath).
04636         // It also might be the start point of this subpath, either because this path is closed
04637         // or because we've been told to snap. Or even, it might be an element at the start or end 
04638         // of another path. My God, what a choice!
04639 
04640         // First check for the simple cases of curve/curve or curve/line (1 & 2)
04641         if (Index+2<UsedSlots && Verbs[Index+2] != PT_MOVETO)
04642         {
04643             // The next element is either a curve or a line
04644             if (Verbs[Index+2] == PT_BEZIERTO)
04645             {
04646                 // It's a curve, so point p is its endpoint
04647                 p = Coords[Index+4];
04648                 CalcPointCurve(p,q,r,&ret);
04649                 return ret;
04650             }
04651             {
04652                 // It's a line, so point p is the endpoint of that
04653                 p = Coords[Index+2];
04654                 CalcPointLine(p,q,r,&ret);
04655                 return ret;
04656             }
04657         }
04658         // Now we know there's nothing after this curve, so check snapping and stuff
04659         else if (StartAndEndSnapped || (Verbs[Index+1] & PT_CLOSEFIGURE))
04660         {
04661             // We're snapping to the start of this path (4 & 5) , so we have to find 
04662             // the start of this subpath
04663             INT32 i = Index;
04664             FindStartOfSubPath(&i);         // i returned pointing at start element
04665             i++;                            // Point at first element in path
04666             if (Verbs[i] == PT_BEZIERTO)
04667             {
04668                 p = Coords[i+2];
04669                 CalcPointCurve(p,q,r,&ret);
04670                 return ret;
04671             }
04672             else
04673             {
04674                 p = Coords[i];
04675                 CalcPointLine(p,q,r,&ret);
04676                 return ret;
04677             }
04678 
04679         }
04680         else if (JoinedToAnother)
04681         {
04682             // Joined to another path (6 & 7)
04683             p = OtherCoord;
04684             if (OtherIsCurve)
04685             {
04686                 CalcPointCurve(p,q,r,&ret);
04687             }
04688             else
04689             {
04690                 CalcPointLine(p,q,r,&ret);
04691             }
04692             return ret;
04693         }
04694         else
04695         {
04696             // The only thing left is a curve at the end of the path (3)
04697             CalcPointEnd(q,r,&ret);
04698             return ret;
04699         }
04700     }
04701 #else
04702     return DocCoord(0,0);
04703 #endif
04704 }
04705 
04706 
04707 /********************************************************************************************
04708 
04709 >   BOOL Path::IsSubPathClosed(INT32 Index)
04710 
04711     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
04712     Created:    6/7/94
04713     Inputs:     Index is the index of a point in the subpath
04714     Outputs:    -
04715     Returns:    TRUE if subpath is closed, FALSE otherwise
04716     Purpose:    Scans to the end of this subpath to see if it has the PT_CLOSEFIGURE flag set
04717     Errors:     -
04718     SeeAlso:    -
04719 
04720 ********************************************************************************************/
04721 
04722 BOOL Path::IsSubPathClosed(INT32 Index)
04723 {
04724     INT32 i = Index;
04725     PathVerb* Verbs = GetVerbArray();
04726     
04727     // skip through until we hit the end or a MoveTo
04728     do
04729     {
04730         i++;
04731     } while (i<UsedSlots && Verbs[i] != PT_MOVETO);
04732 
04733     // i points to the firsy element after this subpath (i.e. the top of the subpath)
04734     // Check the CloseFigure flag on the previous element to see if the subpath is closed
04735     if (Verbs[i-1] & PT_CLOSEFIGURE)
04736         return TRUE;
04737     return FALSE;
04738 }
04739 
04740 
04741 
04742 /********************************************************************************************
04743 
04744 >   BOOL Path::IsClosed()
04745 
04746     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
04747     Created:    05 April 2000
04748     Returns:    TRUE if all subpaths are cloesd,
04749                 FALSE otherwise.
04750     Purpose:    Tells you whether all subpaths of this path are closed.
04751 
04752     Notes:      This code taken from BevelHelpers::IsPathClosed() - seems more appropriate
04753                 for it to live here.
04754 
04755     See also:   IsSubPathClosed
04756 
04757 ********************************************************************************************/
04758 BOOL Path::IsClosed()
04759 {
04760     INT32 StartIndex    = 0;
04761     INT32 EndIndex  = 0;
04762 
04763     // test each subpath for closure.
04764     while (StartIndex < UsedSlots)
04765     {
04766         EndIndex = StartIndex;
04767         FindEndElOfSubPath(&EndIndex);
04768 
04769         if (!IsSubPathClosed(StartIndex))
04770             return FALSE;
04771         
04772         StartIndex = EndIndex + 1;
04773     }
04774 
04775     return TRUE;
04776 }
04777 
04778 
04779 
04780 /********************************************************************************************
04781 
04782 >   void Path::CalcDoubleCurve( DocCoord& p1, DocCoord& p2, DocCoord& p3, 
04783                                 DocCoord* ControlPoint1, DocCoord* ControlPoint2 )
04784 
04785     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
04786     Created:    18/11/93
04787     Inputs:     p1, p2, p3 - The End points of the path elements before, after and at the
04788                 the current position in the path
04789     Outputs:    ControlPoint1, ControlPoint2 - Pointers to the control points on either
04790                 side of the current point
04791     Purpose:    Calculates the position of the two control points on either side of p2,
04792                 using the position of the coords of either side of it to help (p1 & p3)
04793     SeeAlso:    Path::CalcPointLine; Path::CalcPointEnd
04794 
04795 ********************************************************************************************/
04796 
04797 void Path::CalcDoubleCurve( DocCoord& p1, DocCoord& p2, DocCoord& p3, DocCoord* ControlPoint1, DocCoord* ControlPoint2 )
04798 {
04799     // The positions of the two control points adjacent to p2 depend on:
04800     // a) The distance from p2 of p1 and p3, and
04801     // b) The angle between p1p2p3. The following logic is used:
04802     //
04803     // The distance from p2 to ControlPoint1 is 1/3 the distance from p2 to p1
04804     // The angle from p2 to ControlPoint1 and the horizontal is 90 degrees from the angle of the line
04805     // bisecting the angle p1p2p3. Whether it is -90 or +90 depends on whether the acute angle between p1p2p3
04806     // is negative or positive
04807 
04808 
04809     // Work out the angles
04810     double Alpha, Beta, Theta;
04811     Alpha = atan2((double)p3.y-p2.y, (double)p3.x-p2.x);            // Alpha = angle between line p2p3 and horizontal
04812     Beta = atan2((double)p1.y-p2.y, (double)p1.x-p2.x);             // beta = angle between line p2p1 and horizontal
04813     Theta = (Beta + ((Alpha - Beta)/2.0));          // Theta = bisection of Alpha and Beta
04814 
04815     // And calculate the distance between the various control points
04816     double Distp2p1, Distp2p3, lx, ly;
04817     lx = p2.x - p1.x;
04818     ly = p2.y - p1.y;
04819     Distp2p1 = sqrt( lx*lx + ly*ly ) / 3.0;
04820 
04821     lx = p2.x - p3.x;
04822     ly = p2.y - p3.y;
04823     Distp2p3 = sqrt( lx*lx + ly*ly ) / 3.0;
04824 
04825     if (Alpha - Beta < 0)
04826     {
04827         // Angle p2p1 is greater than p2p3, so we have to swap the angle used
04828 
04829         ControlPoint1->x = p2.x + (INT32)(Distp2p1 * cos(Theta + PI/2.0));
04830         ControlPoint1->y = p2.y + (INT32)(Distp2p1 * sin(Theta + PI/2.0));
04831         ControlPoint2->x = p2.x + (INT32)(Distp2p3 * cos(Theta - PI/2.0));
04832         ControlPoint2->y = p2.y + (INT32)(Distp2p3 * sin(Theta - PI/2.0));
04833 
04834     }
04835     else
04836     {
04837         ControlPoint1->x = p2.x + (INT32)(Distp2p1 * cos(Theta - PI/2.0));
04838         ControlPoint1->y = p2.y + (INT32)(Distp2p1 * sin(Theta - PI/2.0));
04839         ControlPoint2->x = p2.x + (INT32)(Distp2p3 * cos(Theta + PI/2.0));
04840         ControlPoint2->y = p2.y + (INT32)(Distp2p3 * sin(Theta + PI/2.0));
04841 
04842     }
04843 
04844 
04845 //  // Work out all the angles
04846 //  double Alpha, Beta, Theta;
04847 //  Alpha = atan2( (p3.y-p2.y), (p3.x - p2.x) );
04848 //  Beta  = atan2( (p2.x - p1.x), (p2.y - p1.y) );
04849 //  Theta = (PI/2.0 - Alpha - Beta) / 2.0;
04850 
04851 //  // And calculate the distance between the various control points
04852 //  double DistA, DistB, lx, ly;
04853 //  lx = p2.x - p1.x;
04854 //  ly = p2.y - p1.y;
04855 //  DistA = sqrt( lx*lx + ly*ly ) / 3.0;
04856 
04857 //  lx = p2.x - p3.x;
04858 //  ly = p2.y - p3.y;
04859 //  DistB = sqrt( lx*lx + ly*ly ) / 3.0;
04860 //
04861 //  if (Theta < PI/2.0)
04862 //  {
04863   //
04864 //  // Set the control point
04865 //  ControlPoint1->x = p2.x - (INT32)(DistA*sin(Theta+Beta));
04866 //  ControlPoint1->y = p2.y - (INT32)(DistA*cos(Theta+Beta));
04867 
04868 //  ControlPoint2->x = p2.x + (INT32)(DistB*cos(Theta+Alpha));
04869 //  ControlPoint2->y = p2.y + (INT32)(DistB*sin(Theta+Alpha));
04870     
04871 //  }
04872 //  else
04873 //  {
04874 
04875 //  // Set the control point
04876 //  ControlPoint1->x = p2.x + (INT32)(DistA*sin(Theta+Beta));
04877 //  ControlPoint1->y = p2.y + (INT32)(DistA*cos(Theta+Beta));
04878 
04879 //  ControlPoint2->x = p2.x - (INT32)(DistB*cos(Theta+Alpha));
04880 //  ControlPoint2->y = p2.y - (INT32)(DistB*sin(Theta+Alpha));
04881     
04882 //
04883 //  }
04884 
04885 }
04886 
04887 /********************************************************************************************
04888 
04889 >   void Path::CalcPointCurve( DocCoord& p1, DocCoord& p2, DocCoord& p3, DocCoord* ControlPoint )
04890 
04891     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
04892     Created:    18/11/93
04893     Inputs:     p1, p2, p3 - The End points of the path elements before, after and at the
04894                 the current position in the path
04895     Outputs:    ControlPoint - Pointer to the control point that appears just after p2.
04896     Purpose:    Calculates the position of the control point adjacent to p2 between p2 and p3,
04897                 using the position of the coords of either side of it to help (p1 & p3).
04898 
04899                 imagine the following path structure:
04900                 moveto  p1
04901                 curveto pa
04902                 curveto pb
04903                 curveto p2  <- This is the endpoint to which the ctrl point is attached
04904                 curveto pc  <- This is the control point being calculated
04905                 curveto pd
04906                 curveto p3
04907 
04908 
04909     SeeAlso:    Path::CalcPointLine; Path::CalcPointEnd
04910 
04911 ********************************************************************************************/
04912 
04913 void Path::CalcPointCurve( DocCoord& p1, DocCoord& p2, DocCoord& p3, DocCoord* ControlPoint )
04914 {
04915     // Work out all the angles
04916     double Alpha, Beta, Theta;
04917     Alpha = atan2((double)p3.y-p2.y, (double)p3.x-p2.x);
04918     Beta = atan2((double)p1.y-p2.y, (double)p1.x-p2.x);
04919     Theta = Beta - ((Beta - Alpha)/2.0);
04920 
04921 
04922     // And calculate the distance between the various control points
04923     double Dist, lx, ly;
04924     lx = p2.x - p3.x;
04925     ly = p2.y - p3.y;
04926     Dist = sqrt( lx*lx + ly*ly ) / 3.0;
04927 
04928     if (Beta - Alpha < 0)
04929         Theta += PI/2.0;
04930     else
04931         Theta -= PI/2.0;
04932 
04933     ControlPoint->x = p2.x + (INT32)(Dist * cos(Theta));
04934     ControlPoint->y = p2.y + (INT32)(Dist * sin(Theta));
04935 
04936 /*
04937     if (Theta < PI/2.0)
04938     {
04939 
04940         // Set the control point
04941         ControlPoint->x = p2.x - (INT32)(Dist*sin(Theta+Beta));
04942         ControlPoint->y = p2.y - (INT32)(Dist*cos(Theta+Beta));
04943     }
04944     else
04945     {
04946 
04947         // Set the control point
04948         ControlPoint->x = p2.x + (INT32)(Dist*sin(Theta+Beta));
04949         ControlPoint->y = p2.y + (INT32)(Dist*cos(Theta+Beta));
04950         
04951     }
04952 */
04953 
04954 }
04955 
04956 /********************************************************************************************
04957 
04958 >   void Path::CalcPointLine( DocCoord& p1, DocCoord& p2, DocCoord& p3, DocCoord* ControlPoint )
04959 
04960     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
04961     Created:    18/11/93
04962     Inputs:     p1, p2, p3 - The End points of the path elements before, after and at the
04963                 the current position in the path
04964     Outputs:    ControlPoint - Pointers to the control point between p2 and p3
04965     Purpose:    Calculates the position of the control point that appears after p2, which
04966                 is part of the curve ending at p3. The control point should be colinear
04967                 with p1 & p2 and 1/3 the distance between p2 & p3 from p2!
04968     SeeAlso:    Path::CalcPointLine; Path::CalcPointEnd
04969 
04970 ********************************************************************************************/
04971 
04972 void Path::CalcPointLine( DocCoord& p1, DocCoord& p2, DocCoord& p3, DocCoord* ControlPoint )
04973 {
04974     // Calc 1/3 the distance from p2 to p3
04975     double DistA, DistB, lx, ly;
04976     lx = p2.x - p3.x;
04977     ly = p2.y - p3.y;
04978     DistA = sqrt( lx*lx + ly*ly ) / 3.0;
04979 
04980     // calc the length of p1 to p2
04981     lx = p2.x - p1.x;
04982     ly = p2.y - p1.y;
04983     DistB = sqrt( lx*lx + ly*ly );
04984 
04985     // Calculate the scale factor
04986     double Factor = (DistB==0.0) ? 1.0 : (DistA / DistB);
04987     lx *= Factor;
04988     ly *= Factor;
04989 
04990     // Calc the position of the new control points
04991     ControlPoint->x = p2.x + (INT32)lx;
04992     ControlPoint->y = p2.y + (INT32)ly;
04993 }
04994 
04995 
04996 /********************************************************************************************
04997 
04998 >   void Path::CalcPointEnd( DocCoord& p1, DocCoord& p2, DocCoord* ControlPoint )
04999 
05000     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
05001     Created:    18/11/93
05002     Inputs:     p1, p2 - The coords of the last two points in the curve
05003     Outputs:    ControlPoint - The Coord of the last control point
05004     Purpose:    Calculates the position of the last control point in the sub-path
05005     SeeAlso:    Path::CalcPointLine; Path::CalcPointCurve
05006 
05007 ********************************************************************************************/
05008 
05009 void Path::CalcPointEnd( DocCoord& p1, DocCoord& p2, DocCoord* ControlPoint )
05010 {
05011     ControlPoint->x = p1.x + ((p2.x - p1.x) / 3);
05012     ControlPoint->y = p1.y + ((p2.y - p1.y) / 3);
05013 }
05014 
05015 
05016 
05017 
05018 
05019 /********************************************************************************************
05020 
05021 >   void Path::CalcRotate( DocCoord& p2, DocCoord* p1, DocCoord* p3, DocCoord& MouseOffset )
05022 
05023     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
05024     Created:    19/11/93
05025     Inputs:     p1, p2, p3 - The End points of the path elements before, after and at the
05026                 the current position in the path
05027     Outputs:    ControlPoint - Pointers to the control point between p2 and p3
05028     Purpose:    
05029     SeeAlso:    Path::CalcPointLine; Path::CalcPointEnd
05030 
05031 ********************************************************************************************/
05032 
05033 void Path::CalcRotate( DocCoord& p2, DocCoord* p1, DocCoord* p3, DocCoord& MouseOffset )
05034 {
05035     double dx, dy, Length;
05036 
05037     // Find the length of the vector from the middle to the point not being dragged
05038     dx = p2.x - p3->x;
05039     dy = p2.y - p3->y;
05040     Length = sqrt( dx*dx + dy*dy );
05041 
05042     // Move the point being dragged
05043     p1->x += MouseOffset.x;
05044     p1->y += MouseOffset.y;
05045 
05046     // Calculate the vector from the point being dragged to the centre
05047     double VecLen;
05048     dx = p1->x - p2.x;
05049     dy = p1->y - p2.y;
05050     VecLen = sqrt( dx*dx + dy*dy );
05051     if (VecLen == 0.0)
05052         // the dragged point is over the centre point, so leave the other control point where it is
05053         return;
05054 
05055     // Normalise the vector and
05056     // Scale the vector by the Length of the other side
05057     dx = dx * Length / VecLen;
05058     dy = dy * Length / VecLen;
05059 
05060     // Set the opposite point to the centre offset by the vector
05061     p3->x = p2.x - (INT32)dx;
05062     p3->y = p2.y - (INT32)dy;
05063 }
05064 
05065 
05066 
05067 
05068 /********************************************************************************************
05069 
05070 >   void Path::Reverse()
05071 
05072     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
05073     Created:    7/4/94
05074     Purpose:    Reverses this path, so that it is rendered and traversed etc in the other
05075                 direction. This involves reversing the order of all the coords etc in the
05076                 path
05077 
05078 ********************************************************************************************/
05079 
05080 void Path::Reverse()
05081 {
05082     ReverseSection(0, UsedSlots);
05083 }
05084 
05085 
05086 /********************************************************************************************
05087 
05088 >   void Path::ReverseSection(INT32 StartSlot)
05089 
05090     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
05091     Created:    27/4/94
05092     Inputs:     StartSlot - The slot in the path where the sub path begins
05093     Purpose:    Reverses the direction of the section of the path specified. This is
05094                 typically used to reverse subpaths of a complex path. It is also called
05095                 by Reverse, to reverse the entire path
05096     Errors:     ENSUREs if StartSlot is not at the start of a subpath
05097 
05098 ********************************************************************************************/
05099 
05100 void Path::ReverseSection(INT32 FirstPos, INT32 SubPathLen)
05101 {
05102     // We will need to change all the arrays
05103     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
05104     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
05105     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
05106 
05107     // First we will reverse all the Coords and their flags
05108     INT32 LastPos  = FirstPos+SubPathLen-1;
05109     INT32 Start = FirstPos;
05110     DocCoord  SwapCoord;
05111     PathFlags SwapFlag;
05112     while (FirstPos<LastPos)
05113     {
05114         // change the order of the coords in the array
05115         SwapCoord = Coords[FirstPos];
05116         Coords[FirstPos] = Coords[LastPos];
05117         Coords[LastPos]  = SwapCoord;
05118 
05119         // Change the order of the flags associated with the coord
05120         SwapFlag = Flags[FirstPos];
05121         Flags[FirstPos] = Flags[LastPos];
05122         Flags[LastPos]  = SwapFlag;
05123 
05124         // find the next two coords/flags to swap
05125         FirstPos++;
05126         LastPos--;
05127     }
05128 
05129     // Now reverse the verbs.
05130     // The first verb always stays as a MoveTo and everything else gets sorted around it
05131     FirstPos = Start+1;
05132     LastPos  = Start+SubPathLen-1;
05133     PathVerb SwapVerb;
05134     while (FirstPos<LastPos)
05135     {
05136         SwapVerb = Verbs[FirstPos];
05137         Verbs[FirstPos] = Verbs[LastPos];
05138         Verbs[LastPos]  = SwapVerb;
05139 
05140         // Find the next two verbs to swap
05141         FirstPos++;
05142         LastPos--;
05143     }
05144 
05145     // added by Ed 23/5/95 - fix up screwed up closed paths!
05146     // ie first element of subpath has PT_CLOSEFIGURE rather than last!
05147     INT32 StartOfSubPath     = Start;
05148     INT32 EndElOfLastSubPath = Start+SubPathLen-1;
05149 
05150 #ifdef _DEBUG
05151     // check assumption that we are reversing a set of complete subpaths
05152     INT32 temp=StartOfSubPath;
05153     FindStartOfSubPath(&temp);
05154     ERROR3IF(StartOfSubPath!=temp,"Path::ReverseSection() - section does not start at the start of a subpath");
05155     temp=EndElOfLastSubPath-1;
05156     FindEndElOfSubPath(&temp);
05157     ERROR3IF(EndElOfLastSubPath!=temp,"Path::ReverseSection() - section does not end at the end of a subpath");
05158 #endif
05159 
05160     // for each subpath, if the second element has an erroneous PT_CLOSEFIGURE,
05161     // move it to the end element of the subpath
05162     while (StartOfSubPath<=EndElOfLastSubPath)
05163     {
05164         INT32 EndElOfSubPath=StartOfSubPath;
05165         FindEndElOfSubPath(&EndElOfSubPath);
05166         if (Verbs[StartOfSubPath+1] & PT_CLOSEFIGURE)
05167         {
05168             Verbs[StartOfSubPath+1]&=~PT_CLOSEFIGURE;
05169             Verbs[EndElOfSubPath]  |= PT_CLOSEFIGURE;
05170         }
05171         StartOfSubPath=EndElOfSubPath+1;
05172     }
05173 
05174     return;
05175 }
05176 
05177 
05178 
05179 #if 0
05180 /********************************************************************************************
05181 
05182 >   void Path::Join(const Path& Other, BOOL BuildComplexPath = TRUE)
05183 
05184     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
05185     Created:    8/4/94
05186     Inputs:     Other - the path to Join with this path
05187                 BuildComplexPath - Defaults to TRUE. This determines if the first MoveTo of
05188                 the 'Other' path is copied across. By default it is.
05189     Purpose:    Joins the other path to this path to make a complex path. If BuildComplexPath
05190                 is FALSE then the first MoveTo is thrown away (it assumes that the MoveTo
05191                 has the same coord as the end point of 'this' path).
05192 
05193 ********************************************************************************************/
05194 
05195 BOOL Path::Join(const Path& Other, BOOL BuildComplexPath )
05196 {
05197     // Make space to copy all the data from the other path into this one
05198     CurrentPos = UsedSlots;
05199     if (MakeSpaceInPath(Other.UsedSlots) == FALSE)
05200         // Failed to get extra memory
05201         return FALSE;
05202 
05203     // We will need all the arrays for both paths
05204     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
05205     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
05206     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
05207     
05208     DocCoord*  OtherCoords = (DocCoord*)  DescribeHandle(Other.CoordHandle);
05209     PathFlags* OtherFlags  = (PathFlags*) DescribeHandle(Other.FlagsHandle);
05210     PathVerb*  OtherVerbs  = (PathVerb*)  DescribeHandle(Other.VerbHandle);
05211 
05212     // Copy all the data, skipping the first verb (MoveTo)
05213     INT32 DestPos = UsedSlots;
05214     INT32 StartCopyFrom = (BuildComplexPath ? 0 : 1);
05215     for (INT32 i=StartCopyFrom; i<Other.UsedSlots; i++)
05216     {
05217         // Copy this coord and its associated data
05218         Coords[DestPos] = OtherCoords[i];
05219         Flags[DestPos]  = OtherFlags[i];
05220         Verbs[DestPos]  = OtherVerbs[i];
05221 
05222         // move to the next position
05223         DestPos++;
05224         UsedSlots++;
05225         UnUsedSlots--;
05226     }
05227 
05228     // Update the paths info about itself
05229     if (BuildComplexPath==FALSE)
05230     {
05231         // Find the coord of the last element in the path
05232         CurrentPos = UsedSlots-1;
05233         DocCoord EndPoint = Coords[CurrentPos];
05234 
05235         // Find the coord of the first element in the sub path
05236         FindStartOfSubPath();
05237         DocCoord StartPoint = Coords[CurrentPos];
05238 
05239         // if they are the same, then close the path
05240         if (EndPoint == StartPoint)
05241         {
05242             // Make the path closed and mark it as filled
05243             Verbs[UsedSlots-1] |= PT_CLOSEFIGURE;
05244             IsFilled = TRUE;
05245         }
05246     }
05247 
05248     // and say that it worked
05249     return TRUE;
05250 }
05251 
05252 #endif
05253 
05254 /********************************************************************************************
05255 
05256 >   void Path::TryToClose()
05257 
05258     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
05259     Created:    19/4/94
05260     Purpose:    Tries to close the path. If the start coord and the end coord are the same,
05261                 it will close the path with a PT_CLOSEFIGURE and set the IsFilled flag. It
05262                 searches all the sub-paths in the path and if any of them are closed, the
05263                 whole path is marked as filled.
05264 
05265 ********************************************************************************************/
05266 
05267 void Path::TryToClose()
05268 {
05269     // Dereference the pointers to the coords and the verbs
05270     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
05271     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
05272 
05273     // Need to try all the sub paths, so loop through the whoel path
05274     INT32 EndOfSubPath = UsedSlots-1;
05275     for (INT32 i=UsedSlots-1; i>=0; i--)
05276     {
05277         // Have we found the begining of a sub-path
05278         if (Verbs[i]==PT_MOVETO)
05279         {
05280             // yes - is its start and end coords the same
05281             if (Coords[i] == Coords[EndOfSubPath])
05282             {
05283                 // This path has the same start and end coord
05284                 // so mark it as closed and fill it
05285                 Verbs[EndOfSubPath] |= PT_CLOSEFIGURE;
05286                 IsFilled = TRUE;
05287             }
05288 
05289             // Update the EndOfSubPath variable
05290             EndOfSubPath = i-1;
05291         }
05292     }
05293 }
05294 
05295 
05296 
05297 /********************************************************************************************
05298 
05299 >   BOOL Path::MergeTwoPaths(const Path& Other)
05300 
05301     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> (Split to arrays varient by Markn 22/11/94)
05302     Created:    27/4/94
05303     Inputs:     Other - A reference to another path to merge with this one
05304     Returns:    TRUE if the merge was a success, FALSE if it failed (lack of memory)
05305     Purpose:    Combines the two paths into a single complex path
05306 
05307 ********************************************************************************************/
05308 
05309 BOOL Path::MergeTwoPaths(const Path& Other)
05310 {
05311     // Make space to copy all the data from the other path into this one
05312     CurrentPos = UsedSlots;
05313     if (!MakeSpaceInPath(Other.UsedSlots))
05314         return FALSE;
05315 
05316     DocCoord*  OtherCoords = (DocCoord*)  DescribeHandle(Other.CoordHandle);
05317     PathVerb*  OtherVerbs  = (PathVerb*)  DescribeHandle(Other.VerbHandle);
05318     PathFlags* OtherFlags  = (PathFlags*) DescribeHandle(Other.FlagsHandle);
05319 
05320     return MergeTwoPaths(OtherCoords,OtherVerbs,OtherFlags,Other.UsedSlots,Other.IsFilled);
05321 }
05322 
05323 
05324 /********************************************************************************************
05325 
05326 >   BOOL Path::MergeTwoPaths(DocCoord* OtherCoords,PathVerb* OtherVerbs,PathFlags* OtherFlags,INT32 Length,BOOL Filled)
05327 
05328     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> (Split from Path& varient by Markn 22/11/94)
05329     Created:    27/4/94
05330     Inputs:     OtherCoords = ptr to a coords array
05331                 OtherVerbs  = ptr to a verbs array
05332                 OtherFlags  = ptr to a flags array
05333                 Length      = the number of elements in the three arrays
05334                 Filled      = TRUE if the arrays represent a filled path
05335     Returns:    TRUE if the merge was a success, FALSE if it failed (lack of memory)
05336     Purpose:    Appends the data in the three given arrays to the arrays in this path
05337 
05338 ********************************************************************************************/
05339 
05340 BOOL Path::MergeTwoPaths(DocCoord* OtherCoords,PathVerb* OtherVerbs,PathFlags* OtherFlags,INT32 Length,BOOL Filled)
05341 {
05342     // Do we have enough room?
05343     if (UnUsedSlots < Length)
05344         return FALSE;
05345 
05346     // We will need all the arrays for both paths
05347     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
05348     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
05349     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
05350     
05351     // Copy all the data
05352     INT32 DestPos = UsedSlots;
05353     for (INT32 i=0; i<Length; i++)
05354     {
05355         // Copy this coord and its associated data
05356         Coords[DestPos] = OtherCoords[i];
05357         Flags[DestPos]  = OtherFlags[i];
05358         Verbs[DestPos]  = OtherVerbs[i];
05359 
05360         // move to the next position
05361         DestPos++;
05362         UsedSlots++;
05363         UnUsedSlots--;
05364     }
05365 
05366     // If the other path was closed and marked as filled, then
05367     // the new path should also be filled
05368     if (Filled)
05369         IsFilled = TRUE;
05370 
05371     // Put current pos to the end of the used slots
05372     CurrentPos = UsedSlots;
05373 
05374     // and say that it worked
05375     return TRUE;
05376 }
05377 
05378 
05379 #if !defined(EXCLUDE_FROM_XARLIB)
05380 /********************************************************************************************
05381 
05382 >   double Path::CalculateFlatnessValueFromPath(double DividerValue = 375.0, double LowerLimit = 1.0,
05383                                                 double UpperLimit = 375.0)
05384 
05385     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
05386     Created:    25/10/2000
05387     Inputs:     Divider Value which is incorperated into the calculation.
05388                 Lower and Upper Limits that the return value should be within.
05389     Returns:    New calculated flatness value OR 0.0 if the path is empty!
05390     Purpose:    To calculate a suitable flatness value to be used with Contouring, Clipping
05391                 and Stroking. The value is based on the actual size of the paths dimensions.
05392 
05393 ********************************************************************************************/
05394 double Path::CalculateFlatnessValueFromPath(double DividerValue, double LowerLimit, double UpperLimit)
05395 {
05396     // Check to see if this path is empty, if so then just return 0.
05397     if(UsedSlots == 0)
05398         return 2.0;
05399 
05400     // Check to see if we`ve been given suitable values to use!
05401     if(LowerLimit < 1.0)
05402     {
05403         ERROR3("When I said give me a lower limit, I meant one larger than 1!");
05404         LowerLimit = 2.0;
05405     }
05406 
05407     if(UpperLimit < 1.0 || UpperLimit < LowerLimit)
05408     {
05409         ERROR3("When I said give me an Upper limit, I meant one larger than the Lower limit!");
05410         UpperLimit = 375.0;
05411     }
05412 
05413     if(DividerValue < 1.0)
05414     {
05415         ERROR3("When I said give me a Divider Value, I meant one greater than 1!");
05416         DividerValue = 375.0;
05417     }
05418 
05419     // Set up the CalcFlatness value
05420     double CalcFlatness = 375.0;
05421 
05422     // Get the smallest dimesion, Width or Height.
05423     DocRect drBounds = GetBoundingRect();
05424     double Smallest = drBounds.Height() < drBounds.Width() ? (double)drBounds.Height() : (double)drBounds.Width();
05425 
05426     // now make the flatness value equal to the smallest dimesion divided by the passed in DividerValue!
05427     CalcFlatness = Smallest / DividerValue;
05428 
05429     // Check to see if we`re within the specified limits
05430     if(LowerLimit > CalcFlatness) CalcFlatness = LowerLimit;
05431     if(UpperLimit < CalcFlatness) CalcFlatness = UpperLimit;
05432 
05433     TRACEUSER( "MarkH", _T("Calculated Flatness = %lf\n"),CalcFlatness);
05434 
05435     // Return the new calculated flatness value!
05436     return CalcFlatness;
05437 }
05438 
05439 /********************************************************************************************
05440 
05441 >   INT32 Path::ClipPathToPath(const Path& Src,const Path* pDest,UINT32 Flags,
05442                         UINT32 Tolerance = 100,double SrcFlatness = 750.0,double ClipFlatness = 750.0);
05443 
05444     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05445     Created:    17/1/95
05446     Inputs:     Src         = source path to clip
05447                 pDest       = ptr to path to place the result in
05448                 Flags       = Gavin's ClipPathToPath flags (see below)
05449                 Tolerence   = Two points closer than this value are considered the same point
05450                 SrcFlatness = the value used to flatten the src path
05451                 ClipPath    = the value used to flatten the clip path
05452     Outputs:    If returned a value >=0, *pDest contains the resultant path.
05453     Returns:    The length of the resultant path in pDest, or -1 for an error.
05454 
05455     Purpose:    This is an interface to Gavin's ClipPathToPath.
05456 
05457                 'this' path is used as the clip path. Src is clipped to this path and the result is placed
05458                 in pDest.
05459 
05460                 'Flags' controls how Src will be clipped to this path (taken from gclip.h - complaints go to Gavin)
05461                     CLIPPING_STYLE      - Clip style
05462                         001                 -     Source AND NOT Clip (subtract Clip from Source)
05463                         010                 -     Source AND     Clip (Intersection)
05464                         011                 -     Source
05465                         100                 - NOT Source AND     Clip (subtract Source from Clip)
05466                         101                 -     Source EOR     Clip
05467                         110                 -                    Clip
05468                         111                 -     Source OR      Clip (Union)
05469                                         - If stroked then
05470                         000                 - Source AND     Clip (Intersection)
05471                         ???                 - Source AND NOT Clip
05472                     CLIPPING_SOURCE_WINDING - Source path winding.
05473                     CLIPPING_CLIP_WINDING   - Clip path winding.
05474                     CLIPPING_IS_STROKED     - Source is stroked, not filled
05475                     CLIPPING_IS_CLOSED      - Source path is to be closed (only applicable if stroked).
05476 
05477                     NOTE:   The path placed in pDest will not have its flags array initialised. If you require
05478                             this (e.g. the path is part of a NodePath and is going in the tree) call
05479                             Path::InitialiseFlags() on pDest. If you don't, the path won't be editable via the
05480                             bezier tool.
05481 
05482 ********************************************************************************************/
05483 INT32 Path::ClipPathToPath(const Path& Src,Path* const pDest,UINT32 Flags,
05484                         UINT32 Tolerance,double SrcFlatness,double ClipFlatness)
05485 {
05486     ERROR2IF(pDest == NULL,-1,"pDest == NULL");
05487 
05488     // We need a temp path to place the result in (return -1 if it fails to initialise)
05489     Path Output;
05490     if (!Output.Initialise()) return -1;
05491 
05492     // Mark Howitt - 1/9/00
05493     // Guessing is not good practice. The major problem in this function is that we need
05494     // to allocate memory before we know how much to allocate!!! This results in more memory than needed being used
05495     // and also the risk of under estimating leading to failure!
05496     // The way to fix this is get Gavin to do the allocation for us and then return the amount. This is currently the
05497     // way the contouring code works which results in no guessing and exact memory allocation. NO WASTE!
05498     // To quickly fix this problem I`m going to make it so that if it comes back with buffer overflow, we double the
05499     // allocation and try again. If this fails after 5 times then return -1 and error2 with failure. This is still not
05500     // very good but will fix a majority of the bugs we are seeing. MUST BE REPLACED!!!!!
05501 
05502     // Try and guess how big the destination path's buffers will have to be in order to take the result.
05503     INT32 NewSize = ((UsedSlots + Src.UsedSlots)*2)+1000;
05504 
05505     // Make sure the destination path is big enough
05506     if (!Output.MakeSpaceInPath(NewSize)) return -1;
05507 
05508     // Set up the params for ClipPathToPath, Gavin-style!
05509     PPOINT SPoints,CPoints,OPoints ;
05510     PBYTE  STypes ,CTypes ,OTypes  ;
05511     UINT32  SLength,CLength,OLength ;
05512 
05513     if ( (Flags & 7)==6 && !(Flags & 0x40) )
05514     {
05515         SPoints = NULL;
05516         STypes  = NULL;
05517         SLength = 0;
05518     }
05519     else
05520     {
05521         SPoints = (PPOINT)  Src.GetCoordArray();
05522         STypes  = (PBYTE)   Src.GetVerbArray();
05523         SLength =           Src.UsedSlots;
05524     }
05525     if ( (Flags & 7)==3 && !(Flags & 0x40) )
05526     {
05527         CPoints = NULL;
05528         CTypes  = NULL;
05529         CLength = 0;
05530     }
05531     else
05532     {
05533         CPoints = (PPOINT)  GetCoordArray();
05534         CTypes  = (PBYTE)   GetVerbArray();
05535         CLength =           UsedSlots;
05536     }
05537 
05538     OPoints = (PPOINT)  Output.GetCoordArray();
05539     OTypes  = (PBYTE)   Output.GetVerbArray();
05540     OLength = NewSize;
05541     INT32 len = -2;
05542     INT32 Passes = 0;
05543 
05544     // Mark Howitt 1/9/00
05545     // New loop to help fix under allocation bugs. NEEDS REPLACING WITH BETTER CODE!!! See above!
05546     while(len == -2 && Passes++ < 10)
05547     {
05548         len = ::ClipPathToPath( SPoints,STypes,SLength,SrcFlatness,
05549                                         CPoints,CTypes,CLength,ClipFlatness,
05550                                         Flags,Tolerance,
05551                                         OPoints,OTypes,OLength-10);     // -10 to safe guard against possible minor buffer overflow
05552 
05553         if(len == -1)
05554             return -1;
05555         else if(len == -2)
05556         {
05557             // Ok, we under estimated so double it and reset the output path variables!
05558             OLength <<= 1;
05559             if (!Output.EnsureVolume(OLength)) return -1;
05560             Output.ClearPath();
05561             OPoints = (PPOINT)  Output.GetCoordArray();
05562             OTypes  = (PBYTE)   Output.GetVerbArray();
05563         }
05564     }
05565 
05566     // If we get here with len == -2 then we failed in 5 attempts to allocate enough space!
05567     if(len < 0)
05568         return -1;
05569 
05570     // Clear pDest and make sure there's enough space in pDest for the result
05571     pDest->ClearPath();
05572     if (!pDest->MakeSpaceInPath(len)) return -1;
05573 
05574     // Copy the resultant path into it.
05575     if (!pDest->MergeTwoPaths(Output.GetCoordArray(),Output.GetVerbArray(),Output.GetFlagArray(),len,Src.IsFilled))
05576         return -1;
05577 
05578     // YES! we have a result
05579     return len; 
05580 }
05581 
05582 
05583 
05584 /********************************************************************************************
05585 
05586 BOOL Path::StrokePathToPath(MILLIPOINT  LineWidth   = 250, 
05587                             LineCapType LineCap     = LineCapButt,
05588                             JointType   JoinStyle   = MitreJoin, 
05589                             DashType    *pDash      = NULL,
05590                             Path*       pDest       = NULL,
05591                             DWORD       Flatness    = 200,
05592                             BOOL        Close       = FALSE)
05593 
05594     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05595     Created:    26/1/95
05596     Inputs:     LineWidth   = line width in millipoints
05597                 LineCap     = line cap style
05598                 JoinStyle   = join style (what else!)
05599                 pDash       = ptr to dash pattern (NULL means no dash pattern)
05600                 pDest       = ptr to path to place result in (NULL means put result in 'this' path)
05601                 Flatness    = the value used to flatten the path
05602                 Close       = TRUE means the path is closed, FALSE otherwise
05603     Outputs:    -
05604     Returns:    TRUE if OK, FALSE if not enough memory, or the resultant path has no elements
05605     Purpose:    Strokes the path, and places result in pDest.
05606                 If pDest == NULL, the result is placed back in this path
05607     SeeAlso:    -
05608 
05609 ********************************************************************************************/
05610 BOOL Path::StrokePathToPath(MILLIPOINT  LineWidth, 
05611                             LineCapType LineCap,
05612                             JointType   JoinStyle, 
05613                             DashType    *pDash,
05614                             Path*       pDest,
05615                             double      Flatness,
05616                             BOOL        Close)
05617 {
05618     if (pDest == NULL) pDest = this;
05619 
05620     UINT32 NewFlatness = (UINT32)Flatness;
05621 
05622     INT32 NumCoords  = GetNumCoords();
05623     INT32 OutputSize = (NumCoords*40)+1000;
05624     Path OutputPath;
05625     if (!OutputPath.Initialise(OutputSize)) return FALSE;
05626 
05627     DocCoord*   IPoints =           GetCoordArray();
05628     BYTE*       ITypes  = (BYTE*)   GetVerbArray();
05629     DWORD       ILength = (DWORD)   NumCoords;
05630 
05631     DocCoord*   OPoints =           OutputPath.GetCoordArray();
05632     BYTE*       OTypes  = (BYTE*)   OutputPath.GetVerbArray();
05633     DWORD       OLength = (DWORD)   OutputSize;
05634 
05635     // Karim 22/11/2000
05636     // Modified - if line-width is 0, then we don't want dashes.
05637     DashType Dash;
05638     Dash.Length = 0;
05639     if (pDash == NULL || LineWidth == 0) pDash = &Dash;
05640 
05641     INT32 len = -10;
05642 
05643     // keep checking to see if we had an overflow!
05644     while(len < -2)
05645     {
05646         len = GRenderRegion::StrokePathToPath(IPoints,ITypes,ILength,
05647                                     OPoints,OTypes,OLength,
05648                                     Close,LineWidth,NewFlatness,LineCap,JoinStyle,pDash);
05649 
05650         if(len < -2)
05651         {
05652             OLength -= len;
05653             OutputPath.EnsureVolume(OLength);
05654             OPoints =           OutputPath.GetCoordArray();
05655             OTypes  = (BYTE*)   OutputPath.GetVerbArray();
05656         }
05657     }
05658 
05659     OutputPath.UsedSlots    = len;
05660     OutputPath.UnUsedSlots -= len;
05661     OutputPath.IsFilled     = TRUE;
05662 
05663     pDest->ClearPath();
05664     if (len > 0)
05665     {
05666         if (!pDest->MergeTwoPaths(OutputPath)) return FALSE;
05667         pDest->InitialiseFlags();
05668         INT32 n = pDest->GetNumCoords();
05669         PathVerb* pVerbs = pDest->GetVerbArray();
05670         pVerbs[n-1] |= PT_CLOSEFIGURE;
05671     }
05672 
05673     return TRUE;
05674 }
05675 
05677 //  Author :        MarkH
05678 //  Last UpDate :   10/07/00
05679 //  Function :      InitializeContourValues(UINT32 Width, JoinStyles JoinS, CapStyles CapS,
05680 //                                  bool IsAnOuterContour, bool ClosePath, UINT32 Flatness,
05681 //                                  UINT32 MitreLimit)
05682 //
05683 //  Inputs :        Width - Max Contour Width required.
05684 //                  JoinStyle - Contour Join Style.
05685 //                  CapStyle - Contour Cap Style.
05686 //                  IsAnOuterContour - Set to TRUE if outer required, FALSE for inner!
05687 //                  ClosePath - TRUE if the path is to be closed
05688 //                  UseContourmode - If true use the contouring, if FALSE use strokepathtopath
05689 //                  Flatness - required flatness
05690 //                  MitreLimit - MitreLimit required
05691 //  Return :        New Path Length for the countour
05692 //  Purpose :       This function is used to initialize the GenPathContour class with
05693 //                  the current path information and the required width,joinstyle and so on...
05694 //                  This should be called first before any other call to the GenPathContour
05695 //                  Functions!
05696 //
05698 INT32 Path::InitializeContourValues(UINT32 Width, JoinStyles JoinS, BOOL IsAnOuterContour, double Flatness,
05699                                    BOOL ClosePath, BOOL UseContourMode, CapStyles CapS, UINT32 MitreLimit)
05700 {
05701     m_ContourWidth = Width;
05702     m_ContourJoinS = JoinS;
05703     m_ContourCapS = CapS;
05704     m_IsAnOuterContour = IsAnOuterContour;
05705     m_DoClosePath = ClosePath;
05706     m_UseContourMode = UseContourMode;
05707     m_ContourFlatness = Flatness;
05708     m_ContourMitreLimit = MitreLimit;
05709 
05710     return GetNumCoords();
05711 }
05712 
05714 //  Author :        MarkH
05715 //  Last UpDate :   10/07/00
05716 //  Function :      GetContourForStep(Path* pDest, double StepValue = 1.0)
05717 //
05718 //  Inputs :        pDest - The path to place the new contour path into
05719 //                  StepValue - 0.0 to 1.0 value of the required step
05720 //  Return :        New Path Length for the current Step.
05721 //  Purpose :       This function is used to get the path at the requested StepValue.
05722 //                  This flavour of this function actually takes just the destination
05723 //                  path and fills it in for you automatically 
05725 INT32 Path::GetContourForStep(Path* pDest, double StepValue)
05726 {
05727     // First Check to see if the Path pasted in is valid!
05728     ERROR2IF(pDest==NULL,0,"Init GenPathContour Recieved a NULL Pointer!");
05729 
05730     PPOINT SPoints = (PPOINT)GetCoordArray();
05731     PBYTE STypes = (PBYTE)GetVerbArray();
05732     INT32 SLength = GetNumCoords();
05733 
05734     PPOINT OPoints = NULL;
05735     PBYTE OTypes = NULL;
05736     PPOINT INPoints = NULL;
05737     PBYTE INTypes = NULL;
05738 
05739     ERROR3IF(SLength < 0,"We`ve got a problem got a path with negative length!");
05740 
05741     Path CopyPath;
05742     CopyPath.Initialise();
05743 
05744     Path StrokedPath;
05745     StrokedPath.Initialise();
05746 
05747     INT32 PathLength = -1;
05748     INT32 Width = 100;
05749     Width = (INT32)(StepValue * m_ContourWidth);
05750 
05751     JointType JoinS = (m_ContourJoinS==JOIN_MITER) ? MitreJoin : (m_ContourJoinS==JOIN_ROUND) ? RoundJoin : BevelledJoin;
05752     LineCapType CapS = (m_ContourCapS==CAPS_BUTT) ? LineCapButt : (m_ContourCapS==CAPS_ROUND) ? LineCapRound : LineCapSquare;
05753 
05754     TRACEUSER( "MarkH", _T("Contouring with flatness %lf\n"),m_ContourFlatness);
05755     INT32 NewPathLength = -2;
05756     INT32 Passes = 0;
05757 
05758     // If we`re not using the contouring mode and we want to use the StrokePathToPAth function then do it here!
05759     if(!m_UseContourMode)
05760         PathLength = StrokePathToPath(Width, CapS, JoinS, NULL, &StrokedPath, m_ContourFlatness, m_DoClosePath);
05761 
05762     if(pDest->GetNumCoords() > 0)
05763     {
05764         // Now copy the Destination path as it needs to be clipped with the intermediate Path!
05765         CopyPath.CloneFrom(*pDest);
05766         pDest->ClearPath();
05767     }
05768     else
05769     {
05770         pDest->SetPathPosition(0);
05771     }
05772 
05773     // Check again to see if we`re doing the contour mode or the stroke path to path mode
05774     if(m_UseContourMode)
05775     {
05776         // now initialize and get the Points and Type pointers from a tempary path
05777         // that holds the intermediate step
05778         GenPathContour GStroke;
05779         // [Phil, 18/08/2005] Should this be assigning to NewPathLength? The test marked !!! below will always fail...
05780         PathLength = GStroke.ContourPath(SPoints, STypes, SLength, &OPoints, &OTypes,
05781                                         Width, m_ContourMitreLimit, m_ContourFlatness,
05782                                         m_ContourJoinS, (m_IsAnOuterContour == TRUE));
05783 
05784         INT32 OLength = (PathLength * 4)+1000;
05785 
05786         if ( PathLength > 0 && OPoints && OTypes)
05787         {
05788             if ( !m_IsAnOuterContour )
05789             {
05790                 // Create two new Arrays which are larger than the current OPoints and OTypes
05791                 // as gavin allocates his own memory for these points!
05792                 INPoints = new POINT[OLength];
05793                 INTypes = new BYTE[OLength];
05794 
05795                 if(INPoints && INTypes)
05796                 {
05797                     memcpy(INPoints,OPoints,sizeof(POINT) * PathLength);
05798                     memcpy(INTypes,OTypes,PathLength);
05799                     delete OPoints;
05800                     OPoints = INPoints;
05801                     delete OTypes;
05802                     OTypes = INTypes;
05803                 }
05804                 else
05805                 {
05806                     delete OTypes;
05807                     delete OPoints;
05808 
05809                     // If the NewPathLength is 0 then there`s no point continueing so
05810                     // make sure the destination path contains the data it came in with
05811                     // and return!
05812                     if(NewPathLength == 0)              // !!!
05813                         pDest->CloneFrom(CopyPath);
05814                     
05815                     return NewPathLength;
05816                 }
05817 
05818                 RECT obbox ;
05819                 XaDraw_CalcSimpleBBox( OPoints,PathLength,&obbox ) ;
05820                 OPoints[PathLength+0].x = obbox.right +0x1000 ;
05821                 OPoints[PathLength+0].y = obbox.top   -0x1000 ;
05822                 OPoints[PathLength+1].x = obbox.right +0x1000 ;
05823                 OPoints[PathLength+1].y = obbox.bottom+0x1000 ;
05824                 OPoints[PathLength+2].x = obbox.left  -0x1000 ;
05825                 OPoints[PathLength+2].y = obbox.bottom+0x1000 ;
05826                 OPoints[PathLength+3].x = obbox.left  -0x1000 ;
05827                 OPoints[PathLength+3].y = obbox.top   -0x1000 ;
05828                 OTypes[PathLength+0] = PT_MOVETO ;
05829                 OTypes[PathLength+1] = PT_LINETO ;
05830                 OTypes[PathLength+2] = PT_LINETO ;
05831                 OTypes[PathLength+3] = PT_LINETO|PT_CLOSEFIGURE ;
05832                 
05833                 while(NewPathLength == -2 && Passes++ < 6)
05834                 {
05835                     NewPathLength = ::ClipPathToPath(OPoints, OTypes, PathLength+4, m_ContourFlatness,
05836                                                     OPoints+PathLength, OTypes+PathLength, 4, m_ContourFlatness,
05837                                                     4 | 1<<4 | 1<<5, 30,
05838                                                     OPoints, OTypes, OLength);
05839                 }
05840 
05841                 PathLength = NewPathLength;
05842 
05843                 if(NewPathLength <= 0)
05844                 {
05845                     delete OTypes;
05846                     delete OPoints;
05847 
05848                     // If the NewPathLength is 0 then there`s no point continueing so
05849                     // make sure the destination path contains the data it came in with
05850                     // and return!
05851                     if(NewPathLength == 0)
05852                         pDest->CloneFrom(CopyPath);
05853                     
05854                     return NewPathLength;
05855                 }
05856             }
05857 
05858             pDest->EnsureVolume(OLength);
05859 
05860             PPOINT DPoints = (PPOINT)pDest->GetCoordArray();
05861             PBYTE DTypes = (PBYTE)pDest->GetVerbArray();
05862 //          INT32 DLength = pDest->GetNumCoords();
05863 
05864             PPOINT CPoints = (PPOINT)CopyPath.GetCoordArray();
05865             PBYTE CTypes = (PBYTE)CopyPath.GetVerbArray();
05866             INT32 CLength = CopyPath.GetNumCoords();
05867 
05868             NewPathLength = -2;
05869 
05870             while(NewPathLength == -2 && Passes++ < 6)
05871             {
05872                 NewPathLength = ::ClipPathToPath(   OPoints, OTypes, PathLength, m_ContourFlatness,
05873                                                 CPoints, CTypes, CLength, m_ContourFlatness,
05874                                                 7 | 1<<4, 30,
05875                                                 DPoints, DTypes, OLength);
05876 
05877                 if(NewPathLength == -2)
05878                 {
05879                     OLength <<= 1;
05880                     pDest->EnsureVolume(OLength);
05881                     pDest->SetPathPosition(0);
05882                     DPoints = (PPOINT)pDest->GetCoordArray();
05883                     DTypes = (PBYTE)pDest->GetVerbArray();
05884                     TRACEUSER( "MarkH", _T("Path to short so using new length %d\n"),OLength);
05885                 }
05886             }
05887 
05888             if(NewPathLength <= 0)
05889             {
05890                 delete OTypes;
05891                 delete OPoints;
05892 
05893                 // If the NewPathLength is 0 then there`s no point continueing so
05894                 // make sure the destination path contains the data it came in with
05895                 // and return!
05896                 if(NewPathLength == 0)
05897                     pDest->CloneFrom(CopyPath);
05898 
05899                 return NewPathLength;
05900             }
05901 
05902             pDest->UsedSlots += NewPathLength;
05903             pDest->UnUsedSlots -= NewPathLength;
05904 
05905             delete OTypes;
05906             delete OPoints;
05907 
05908             // Now try to close the destination path and tidyup the slot allocations!
05909             pDest->TryToClose();
05910         }
05911         else
05912         {
05913             delete OTypes;
05914             delete OPoints;
05915 
05916             // If the NewPathLength is 0 then there`s no point continueing so
05917             // make sure the destination path contains the data it came in with
05918             // and return!
05919 //          if(NewPathLength == 0)
05920             if (NewPathLength <=0)
05921             {
05922                 pDest->CloneFrom(CopyPath);
05923                 return 0;
05924             }
05925         }
05926     }
05927     else
05928     {
05929         // With the stroke path to path method we need to clean up the resultant path!
05930         PathLength = CopyPath.ClipPathToPath(StrokedPath, pDest, 7|(1<<4), 30, m_ContourFlatness, m_ContourFlatness);
05931         
05932         if(PathLength <= 0)
05933         {
05934             // If the NewPathLength is 0 then there`s no point continueing so
05935             // make sure the destination path contains the data it came in with
05936             // and return!
05937             if(PathLength == 0)
05938                 pDest->CloneFrom(CopyPath);
05939 
05940             TRACEUSER( "MarkH", _T("ClipFailed so copying path! PATHS\n"));
05941         }
05942     }
05943 
05944     // Return the New Path Length
05945     return PathLength;
05946 }
05947 
05948 #endif  // EXCLUDE_FROM_XARLIB
05949 
05950 
05951 /********************************************************************************************
05952 
05953     BOOL Path::ExternalArraysAdded(const INT32 Added)
05954 
05955     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
05956     Created:    16/12/94
05957     Inputs:     Added = the number of new elements added to path arrays.
05958     Returns:    TRUE if the external arrays have been validated
05959                 FALSE if the external array additions have been ignored
05960     Purpose:    A hidden function to adjust the number of elements in a path. This has been
05961                 added by me as a last resort and should not be used by others. (Hence hidden
05962                 from the documentation). It is required to cope with changes in the path
05963                 data external to the path class. This occurs when doing really nasty things
05964                 like moulding paths.
05965 
05966 ********************************************************************************************/
05967 
05968 BOOL Path::ExternalArraysAdded(const INT32 Added)
05969 {
05970     ERROR1IF(Added<1 || Added>UnUsedSlots, FALSE, "Path::ExternalArraysAdded() called with illegal additon");
05971 
05972     UsedSlots+=Added;
05973     UnUsedSlots-=Added;
05974 
05975     return TRUE;
05976 } 
05977 
05978 
05979 /********************************************************************************************
05980 
05981     BOOL Path::ExternalArraysReplaced(const INT32 Size)
05982 
05983     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
05984     Created:    16/12/94
05985     Inputs:     Size = the number of elements replaced in the path arrays
05986     Returns:    TRUE    if the external arrays have been validated
05987                 FALSE   if the external array replacements have been ignored
05988     Purpose:    A hidden function to adjust the number of elements in a path. This has been
05989                 added by me as a last resort and should not be used by others. (Hence hidden
05990                 from the documentation).
05991 
05992 ********************************************************************************************/
05993 
05994 BOOL Path::ExternalArraysReplaced(const INT32 Size)
05995 {
05996     ERROR1IF(Size<1 || Size>(UsedSlots+UnUsedSlots), FALSE, "Path::ExternalArraysCloned() called with an illegal clone");
05997 
05998     UnUsedSlots = (UsedSlots+UnUsedSlots)-Size;
05999     UsedSlots   = Size;
06000 
06001     return TRUE;
06002 }
06003 
06004 
06005 /********************************************************************************************
06006 
06007 >   BOOL Path::CopySectionFrom(const Path& Other, INT32 StartIndex, INT32 NumToCopy)
06008 
06009     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
06010     Created:    22/7/94
06011     Inputs:     Other - A reference to another path to merge with this one
06012     Returns:    TRUE if the merge was a success, FALSE if it failed (lack of memory)
06013     Purpose:    Copies a specified section from a path to the end of this one
06014 
06015 ********************************************************************************************/
06016 
06017 BOOL Path::CopySectionFrom(const Path& Other, INT32 StartIndex, INT32 NumToCopy)
06018 {
06019     return MergeSectionFrom(UsedSlots, Other, StartIndex, NumToCopy);
06020 } 
06021 
06022 
06023 /********************************************************************************************
06024 
06025 >   BOOL Path::CopySectionTo(Path* Dest, INT32 StartIndex, INT32 NumToCopy)
06026 
06027     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
06028     Created:    08/09/94
06029     Inputs:     Dest - A pointer to another path to add the data to
06030     Returns:    TRUE if the copy was a success, FALSE if it failed (lack of memory)
06031     Purpose:    Copies a specified section from this path to the end of another path
06032     
06033 ********************************************************************************************/
06034 
06035 BOOL Path::CopySectionTo(Path* Dest, INT32 StartIndex, INT32 NumToCopy)
06036 {
06037     return MergeSectionTo(StartIndex, NumToCopy, Dest, Dest->UsedSlots);
06038 }
06039 
06040 
06041 
06042 
06043 /********************************************************************************************
06044 
06045 >   BOOL Path::MergeSectionFrom(INT32 DestinPos, const Path& Source, INT32 SourcePos, INT32 Length)
06046 
06047     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
06048     Created:    12/10/94
06049     Inputs:     DestinPos   = Index of where to copy path elements to
06050                               All existing items from this index onwards will move up.
06051                 Source      = Object to take elements from
06052                 SourcePos   = Position in source to start copying elements from
06053                 Length      = Number of elements to copy over
06054 
06055     Returns:    TRUE if the merge was a success, FALSE if it failed (lack of memory)
06056     Purpose:    Copies a specified section from a source path to a specified position in
06057                 this path
06058 
06059 ********************************************************************************************/
06060 
06061 BOOL Path::MergeSectionFrom(INT32 DestinPos, const Path& Source, INT32 SourcePos, INT32 Length)
06062 {
06063 
06064     // Make space to copy all the data from the other path into this one
06065     CurrentPos = DestinPos;
06066     if (!MakeSpaceInPath(Length))
06067         // Failed to get extra memory
06068         return FALSE;
06069 
06070     // We will need all the arrays for both paths
06071     DocCoord*  DCoords = (DocCoord*)  DescribeHandle(CoordHandle);
06072     PathFlags* DFlags  = (PathFlags*) DescribeHandle(FlagsHandle);
06073     PathVerb*  DVerbs  = (PathVerb*)  DescribeHandle(VerbHandle);
06074     
06075     DocCoord*  SCoords = (DocCoord*)  DescribeHandle(Source.CoordHandle);
06076     PathFlags* SFlags  = (PathFlags*) DescribeHandle(Source.FlagsHandle);
06077     PathVerb*  SVerbs  = (PathVerb*)  DescribeHandle(Source.VerbHandle);
06078 
06079     // Copy all the data
06080     memmove((void*)(DCoords+DestinPos), (void*)(SCoords+SourcePos), Length*sizeof(DocCoord));
06081     memmove((void*)(DVerbs+DestinPos),  (void*)(SVerbs+SourcePos),  Length*sizeof(PathVerb));
06082     memmove((void*)(DFlags+DestinPos),  (void*)(SFlags+SourcePos),  Length*sizeof(PathFlags));
06083 
06084     UsedSlots+=Length;
06085     UnUsedSlots-=Length;
06086 
06087     // and say that it worked
06088     return TRUE;
06089 }
06090 
06091 
06092 
06093 /********************************************************************************************
06094 
06095 >   BOOL Path::MergeSectionTo(SourcePos, Length, Path* Destin, INT32 DestinPos)
06096 
06097     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
06098     Created:    12/10/94
06099     Inputs:     SourcePos   = Position within this path to copy data from
06100                 Length      = Number of items to copy
06101                 Destin      = Pointer to path to copy items to
06102                 DestinPos   = Position in destin path top copy data to
06103                               All existing items from this index and beyond will move up
06104     Returns:    TRUE if the copy was a success, FALSE if it failed (lack of memory)
06105     Purpose:    Copies a specified section from this path to a specific position within
06106                 another path. A gap is created if necessary to copy the data into
06107 
06108 ********************************************************************************************/
06109 
06110 BOOL Path::MergeSectionTo(INT32 SourcePos, INT32 Length, Path* Destin, INT32 DestinPos)
06111 {
06112     // Make space to copy all the data this path to the destination
06113 
06114     Destin->CurrentPos = DestinPos;
06115     if (!Destin->MakeSpaceInPath(Length))
06116         // Failed to get extra memory
06117         return FALSE;
06118 
06119     // We will need all the arrays for both paths
06120     DocCoord*  SCoords = (DocCoord*)  DescribeHandle(CoordHandle);
06121     PathFlags* SFlags  = (PathFlags*) DescribeHandle(FlagsHandle);
06122     PathVerb*  SVerbs  = (PathVerb*)  DescribeHandle(VerbHandle);
06123     
06124     DocCoord*  DCoords = (DocCoord*)  DescribeHandle(Destin->CoordHandle);
06125     PathFlags* DFlags  = (PathFlags*) DescribeHandle(Destin->FlagsHandle);
06126     PathVerb*  DVerbs  = (PathVerb*)  DescribeHandle(Destin->VerbHandle);
06127 
06128     // Copy all the data
06129     memmove((void*)(DCoords+DestinPos), (void*)(SCoords+SourcePos), Length*sizeof(DocCoord));
06130     memmove((void*)(DVerbs+DestinPos),  (void*)(SVerbs+SourcePos),  Length*sizeof(PathVerb));
06131     memmove((void*)(DFlags+DestinPos),  (void*)(SFlags+SourcePos),  Length*sizeof(PathFlags));
06132 
06133     Destin->UsedSlots+=Length;
06134     Destin->UnUsedSlots-=Length;
06135 
06136     // and say that it worked
06137     return TRUE;
06138 }
06139 
06140 
06141 /********************************************************************************************
06142 
06143 >   BOOL Path::JoinToAnother(Path* OtherPath, INT32 MainIndex, INT32 OtherIndex)
06144 
06145     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
06146     Created:    5/10/94
06147     Inputs:     OtherPath is the path which we're joining to this one
06148                 MainIndex is the index of the point in this path we're joining
06149                 OtherIndex is the index of the point in the other path we're joining to
06150     Outputs:    -
06151     Returns:    TRUE if successful, FALSE otherwise
06152     Purpose:    This routine is used to join the end of a line to the end of another line.
06153                 It will cope with the various possiblities: That both subpaths are in the same
06154                 complex paths, that one or other of the subpaths are in different complex paths,
06155                 and that one of the paths might need reversing.
06156                 The other path is left untouched. This path will contain all subpaths from that one.
06157                 This routine assumes that the two subpaths are different - it will not join the
06158                 start and end of the same subpath.
06159     Errors:     Returns FALSE if it failed (lack of memory, for example)
06160     SeeAlso:    -
06161 
06162 ********************************************************************************************/
06163 
06164 BOOL Path::JoinToAnother(Path* OtherPath, INT32 MainIndex, INT32 OtherIndex)
06165 {
06166     // First, get some indices pointing at the start and end of these subpaths
06167     INT32 MainSubStart;         // Start of subpath in main path (this)
06168     INT32 MainSubEnd;           // End of subpath in this path
06169     INT32 OtherSubStart;            // Start of subpath in other path
06170     INT32 OtherSubEnd;          // End of subpath in other path
06171 
06172     MainSubStart = MainSubEnd = MainIndex;
06173     FindStartOfSubPath(&MainSubStart);      
06174     FindEndElOfSubPath(&MainSubEnd);
06175 
06176     OtherSubStart = OtherSubEnd = OtherIndex;
06177     OtherPath->FindStartOfSubPath(&OtherSubStart);
06178     OtherPath->FindEndElOfSubPath(&OtherSubEnd);
06179 
06180     ERROR2IF(((this == OtherPath) && (MainSubStart == OtherSubStart)), FALSE, "Attempted to join this subpath with itself");
06181 
06182     // Because we'll be messing around with the path, it's easier if we make the
06183     // two indices relative to the starts of their respective subpaths
06184     MainIndex = MainIndex - MainSubStart;
06185     OtherIndex = OtherIndex - OtherSubStart;
06186 
06187     // Now that we know which subpaths we're interested in, let's see if we have to join the two paths
06188 
06189     if (this != OtherPath)
06190     {
06191         // OtherPath is not the same, so copy it into this one and adjust the subpath indices
06192         OtherSubStart += UsedSlots;
06193         OtherSubEnd += UsedSlots;
06194         //OtherIndex += UsedSlots;
06195         if (!MergeTwoPaths(*OtherPath))
06196             return FALSE;
06197     }
06198 
06199     // Now this path contains both subpaths (and others, maybe)
06200     // If the subpaths were originally in the same path, it's possible that the other subpath
06201     // is below the main subpath. We don't want that possibility, so swap them if that's the case
06202     if (OtherSubStart < MainSubStart)
06203     {
06204         INT32 temp = OtherSubStart;
06205         OtherSubStart = MainSubStart;
06206         MainSubStart = temp;
06207         temp = OtherSubEnd;
06208         OtherSubEnd = MainSubEnd;
06209         MainSubEnd = temp;
06210         temp = OtherIndex;
06211         OtherIndex = MainIndex;
06212         MainIndex = temp;
06213     }
06214 
06215     // See if the two subpaths are adjacent now
06216     if (OtherSubStart - MainSubEnd >1)
06217     {
06218         // The two subpaths are not adjacent, so let's shuffle the path around...
06219         // It's just possible that the Othersection might be at the top of the path so move the
06220         // main section to the top.
06221         CurrentPos = UsedSlots;
06222         if (!MakeSpaceInPath(MainSubEnd-MainSubStart+1))
06223             return FALSE;
06224         if (!InsertSectionAtEnd(this, MainSubStart, MainSubEnd-MainSubStart+1))
06225             return FALSE;
06226         DeleteSection( MainSubStart, MainSubEnd-MainSubStart+1);
06227 
06228         // Recalculate the subpath indices
06229         MainSubEnd = MainSubEnd-MainSubStart+1;     // Change into length
06230         MainSubStart = UsedSlots - MainSubEnd;
06231         OtherSubStart -= MainSubEnd;
06232         OtherSubEnd -= MainSubEnd;
06233         MainSubEnd = UsedSlots-1;
06234 
06235         // Now, are the subpaths adjacent? Main is now above Other
06236         if (MainSubStart - OtherSubEnd > 1)
06237         {
06238             // Do the same as above, but with Other instead of Main
06239             CurrentPos = UsedSlots;
06240             if (!MakeSpaceInPath(OtherSubEnd-OtherSubStart+1))
06241                 return FALSE;
06242             if (!InsertSectionAtEnd(this, OtherSubStart, OtherSubEnd-OtherSubStart+1))
06243                 return FALSE;
06244             DeleteSection( OtherSubStart, OtherSubEnd-OtherSubStart+1);
06245 
06246             // Recalculate the subpath indices
06247             OtherSubEnd = OtherSubEnd-OtherSubStart+1;      // Change into length
06248             OtherSubStart = UsedSlots - OtherSubEnd;
06249             MainSubStart -= OtherSubEnd;
06250             MainSubEnd -= OtherSubEnd;
06251             OtherSubEnd = UsedSlots-1;
06252 
06253 
06254         }
06255         
06256         if (OtherSubStart < MainSubStart)
06257         {
06258             // Swap Main and Other indices
06259             INT32 temp = MainSubStart;
06260             MainSubStart = OtherSubStart;
06261             OtherSubStart = temp;
06262             temp = MainSubEnd;
06263             MainSubEnd = OtherSubEnd;
06264             OtherSubEnd = temp;
06265             temp = OtherIndex;
06266             OtherIndex = MainIndex;
06267             MainIndex = temp;
06268         }
06269     }
06270 
06271     // Now we are guaranteed that the two subpaths are adjacent and that Main is below Other
06272     // Let's see if we have to reverse anything
06273     MainIndex+=MainSubStart;
06274     OtherIndex+=OtherSubStart;
06275 
06276     if (MainIndex == MainSubStart)
06277     {
06278         // Because Main is below Other, this means we have to reverse this subpath...
06279         ReverseSection(MainSubStart, MainSubEnd-MainSubStart+1);
06280         MainIndex = MainSubEnd;
06281     }
06282 
06283     if (OtherIndex == OtherSubEnd)
06284     {
06285         // We have to reverse the other subpath
06286         ReverseSection(OtherSubStart, OtherSubEnd-OtherSubStart+1);
06287         OtherIndex = OtherSubStart;
06288     }
06289 
06290     // At INT32 last, it's possible just to join the subpaths - we do this by deleting the
06291     // MoveTo between them.
06292     DeleteSection(OtherSubStart,1);
06293 
06294     // And clear selection, then select just the point we want
06295     ClearSubSelection();
06296     PathVerb* Verbs = GetVerbArray();
06297     PathFlags* Flags = GetFlagArray();
06298     Flags[MainSubEnd].IsSelected = TRUE;
06299     if (Verbs[MainSubEnd] == PT_BEZIERTO)
06300         Flags[MainSubEnd-1].IsSelected = TRUE;
06301     if (Verbs[MainSubEnd+1] == PT_BEZIERTO)
06302         Flags[MainSubEnd+1].IsSelected = TRUE;
06303 
06304     return TRUE;
06305 }
06306 
06307 
06308 /********************************************************************************************
06309 
06310 >   BOOL Path::SimpleJoin(const Path& Other, INT32 *OtherStartSlot, BOOL* IsOtherReversed)
06311 
06312     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06313     Created:    27/4/94
06314     Inputs:     Other - the path to join with this path
06315     Outputs:    OtherStartSlot - The Slot number in the combined path where the path
06316                 Other starts.
06317                 IsOtherReversed - TRUE if the 'Other' path was reversed during the joining
06318                 process. FALSE if it was not
06319     Returns:    TRUE if the Join Worked, FALSE if it failed
06320     Purpose:    Joins two paths to form a new, longer path. Both the paths invloved are
06321                 simple (ie not complex).
06322 
06323 ********************************************************************************************/
06324 
06325 BOOL Path::SimpleJoin(Path* Other, INT32 *OtherStartSlot, BOOL* IsOtherReversed, BOOL* ThisReversed)
06326 {
06327     // We have not reversed the other path yet
06328     *IsOtherReversed = FALSE;
06329 
06330     // Make a note of where the new path will start after the merge
06331     INT32 PathStart = UsedSlots;
06332     
06333     if (ThisReversed != NULL)
06334         *ThisReversed = FALSE;
06335     // Do we need to reverse any elements of this path
06336     INT32 StartSlot, NumSlots;
06337     BOOL JoinedAtStart;
06338     DocCoord*  OtherCoords = (DocCoord*)  DescribeHandle(Other->CoordHandle);
06339 
06340     // Find out if we need to reverse this path to help with the joining
06341     if (FindJoinedSubPathInfo(OtherCoords[0], &StartSlot, &NumSlots, &JoinedAtStart))
06342     {
06343         // Maybe need to reverse this path
06344         if (JoinedAtStart)
06345         {
06346             Reverse();
06347             if (ThisReversed != NULL)
06348                 *ThisReversed = TRUE;
06349         }
06350     }
06351 
06352     // And check to see if the other end needs checking as well.
06353     if (FindJoinedSubPathInfo(OtherCoords[Other->UsedSlots-1], &StartSlot, &NumSlots, &JoinedAtStart))
06354     {
06355         // Reverse the joining path always
06356         Other->Reverse();
06357 
06358         // We have reversed the other path
06359         *IsOtherReversed = TRUE;
06360 
06361         // and maybe reverse this path
06362         if (JoinedAtStart)
06363         {
06364             Reverse();
06365             if (ThisReversed != NULL)
06366                 *ThisReversed = TRUE;
06367         }
06368     }
06369     
06370     // merge the paths
06371     if (MergeTwoPaths(*Other)==FALSE)
06372         return FALSE;
06373 
06374     // Make sure that the new path starts with a MoveTo
06375     #ifdef _DEBUG
06376         PathVerb* Verbs  = (PathVerb*) DescribeHandle(VerbHandle);
06377         ENSURE( Verbs[PathStart]==PT_MOVETO, "Start of SubPath is not a MoveTo in SimpleJoin()" );
06378     #endif
06379 
06380     // The merge must have worked, so remove the initial MoveTo
06381     CurrentPos = PathStart;
06382     DeleteElement();
06383 
06384     // See if we can make it a closed path
06385     TryToClose();
06386 
06387     // report that it all worked
06388     CurrentPos = UsedSlots;
06389     *OtherStartSlot = PathStart-1;
06390     return TRUE;
06391 }
06392 
06393 
06394 
06395 
06396 /********************************************************************************************
06397 
06398 >   BOOL Path::ComplexJoin(Path* Other, INT32 *OtherStartSlot, BOOL* IsOtherReversed)
06399 
06400     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06401     Created:    27/4/94
06402     Inputs:     Other - The New path that needs Joining in
06403     Outputs:    OtherStartSlot - The Slot number in the combined path where the path
06404                 Other starts.
06405                 IsOtherReversed - TRUE if the 'Other' path was reversed during the joining
06406                 process. FALSE if it was not
06407     Returns:    TRUE if it worked, FALSE if it failed
06408     Purpose:    Tries to Join the New Path (must be simple) to a complex path
06409 
06410 ********************************************************************************************/
06411 
06412 BOOL Path::ComplexJoin(Path* Other, INT32 *OtherStartSlot, BOOL* IsOtherReversed)
06413 {
06414     // We have not reversed the other path yet
06415     *IsOtherReversed = FALSE;
06416 
06417     // Do we need to reverse any elements of this path
06418     INT32 StartSlot = 0;
06419     INT32 NumSlots = 0;
06420     INT32 JoinStart = 0;
06421     INT32 JoinLen = 0;
06422     BOOL JoinedAtStart = FALSE;
06423     BOOL JoinFound = FALSE;
06424     DocCoord*  OtherCoords = (DocCoord*)  DescribeHandle(Other->CoordHandle);
06425 
06426     // Find out if we need to reverse this path to help with the joining
06427     if (FindJoinedSubPathInfo(OtherCoords[0], &StartSlot, &NumSlots, &JoinedAtStart))
06428     {
06429         // We found a join
06430         JoinFound = TRUE;
06431         JoinStart = StartSlot;
06432         JoinLen = NumSlots;
06433 
06434         // Maybe need to reverse this path
06435         if (JoinedAtStart)
06436             ReverseSection(StartSlot, NumSlots);
06437     }
06438 
06439     // And check to see if the other end needs checking as well.
06440     if (FindJoinedSubPathInfo(OtherCoords[Other->UsedSlots-1], &StartSlot, &NumSlots, &JoinedAtStart))
06441     {
06442         // We found a join
06443         JoinFound = TRUE;
06444         JoinStart = StartSlot;
06445         JoinLen = NumSlots;
06446 
06447         // Reverse the joining path always
06448         Other->Reverse();
06449 
06450         // We have reversed the other path
06451         *IsOtherReversed = TRUE;
06452 
06453         // and maybe reverse this path
06454         if (JoinedAtStart)
06455             ReverseSection(StartSlot, NumSlots);
06456     }
06457 
06458     // If we found a join then everything is fabby
06459     if (JoinFound)
06460     {
06461         // Make sure that there is room in the path to copy one of the sub-paths
06462         CurrentPos = UsedSlots;
06463         if (!MakeSpaceInPath(JoinLen))
06464             return FALSE;
06465 
06466         // copy the sub path
06467         if (!InsertSectionAtEnd(this, JoinStart, JoinLen))
06468             return FALSE;
06469 
06470         // delete the original version of the sub path
06471         DeleteSection(JoinStart, JoinLen);
06472         
06473         // Make sure that there is enough room for the new path
06474         CurrentPos = UsedSlots;
06475         if (!MakeSpaceInPath(Other->UsedSlots-1))
06476             return FALSE;
06477 
06478         // copy the new path
06479         *OtherStartSlot = UsedSlots-1;
06480         if (!InsertSectionAtEnd(Other, 1, Other->UsedSlots-1))
06481             return FALSE;
06482     }
06483     else
06484     {
06485         // We did not find a join
06486         return FALSE;
06487     }
06488 
06489     // See if we can close anything
06490     TryToClose();
06491 
06492     // Must have worked, so Move the insertion point to the end and return happy
06493     CurrentPos = UsedSlots;
06494     return TRUE;
06495 }
06496 
06497 
06498 /********************************************************************************************
06499 
06500 >   BOOL Path::ComplexToSameComplexJoin(Path* Other, INT32 *OtherStartSlot, BOOL* IsOtherReversed)
06501 
06502     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06503     Created:    27/4/94
06504     Inputs:     Other - The path to Join with this complex path.
06505     Outputs:    OtherStartSlot - The Slot number in the combined path where the path
06506                 Other starts.
06507                 IsOtherReversed - TRUE if the 'Other' path was reversed during the joining
06508                 process. FALSE if it was not
06509     Purpose:    This path is assumed to be complex and Other is assumed to be a simple
06510                 path. The start and end point of the Simple Path must be the same as
06511                 the start or end point of one of the sub paths of this complex path.
06512                 The function Moves the two sub paths that are touched by the simple
06513                 path to the end of the path, and inserts the simple path between them.
06514                 This means that the two touched sub-paths and the new simple path
06515                 are combined into a single sub path. This may also mean that this
06516                 complex path is no longer complex
06517 
06518 ********************************************************************************************/
06519 
06520 BOOL Path::ComplexToSameComplexJoin(Path* Other, INT32 *OtherStartSlot, BOOL* IsOtherReversed)
06521 {
06522     INT32 BackStartSlot, BackNumSlots;
06523     INT32 FrontStartSlot, FrontNumSlots;
06524     BOOL FrontJoinedAtStart, BackJoinedAtStart;
06525 
06526     // We have not reversed the other path yet
06527     *IsOtherReversed = FALSE;
06528 
06529     // Get a few arrays
06530     DocCoord*  OtherCoords = (DocCoord*)  DescribeHandle(Other->CoordHandle);
06531 
06532     // First we have to gather data about the subpaths we are joined to
06533     if (!FindJoinedSubPathInfo(OtherCoords[0], &FrontStartSlot, &FrontNumSlots, &FrontJoinedAtStart))
06534         return FALSE;
06535 
06536     // And the other sub path, Fail if we there is no intersection
06537     if (!FindJoinedSubPathInfo(OtherCoords[Other->UsedSlots-1], &BackStartSlot, &BackNumSlots, &BackJoinedAtStart))
06538         return FALSE;
06539 
06540     // If this was trying to join a sub path to the other end of the same sub path
06541     // then we need to use a different function
06542     if (FrontStartSlot == BackStartSlot)
06543         return ComplexJoin(Other, OtherStartSlot, IsOtherReversed);
06544 
06545     // Build a path to store the combined paths in
06546     Path InsertPath;
06547     InsertPath.Initialise(FrontNumSlots+BackNumSlots+Other->UsedSlots);
06548 
06549     // Reverse the first subpath if we need to
06550     if (FrontJoinedAtStart)
06551         ReverseSection(FrontStartSlot, FrontNumSlots);
06552     
06553     // Reverse the Second subpath if we need to
06554     if (!BackJoinedAtStart)
06555         ReverseSection(BackStartSlot, BackNumSlots);
06556 
06557     // Make copys of all the path sections
06558     // The path joined to the start of the new path
06559     if (!InsertPath.InsertSectionAtEnd(this, FrontStartSlot, FrontNumSlots))
06560         return FALSE;
06561 
06562     // followed by the new path
06563     *OtherStartSlot = InsertPath.UsedSlots-1;
06564     if (!InsertPath.InsertSectionAtEnd(Other, 1, Other->UsedSlots-1))
06565         return FALSE;
06566 
06567     // followed by the path joined to the end of the new path
06568     if (!InsertPath.InsertSectionAtEnd(this, BackStartSlot+1, BackNumSlots-1))
06569         return FALSE;
06570 
06571     // Get rid of the original sub-path
06572     DeleteSection(FrontStartSlot, FrontNumSlots);
06573 
06574     // Go find the Other Coords array again as it may have moved
06575     OtherCoords = (DocCoord*)  DescribeHandle(Other->CoordHandle);
06576 
06577     // Find the location of the Second path again, as deleting the first path may have moved it
06578     if (!FindJoinedSubPathInfo(OtherCoords[Other->UsedSlots-1], &BackStartSlot, &BackNumSlots, &BackJoinedAtStart))
06579         return FALSE;
06580 
06581     // Get rid of the second sub-path
06582     DeleteSection(BackStartSlot, BackNumSlots);
06583 
06584     // Make enough space in the path to store a copy of the two subpaths and the new path
06585     CurrentPos = UsedSlots;
06586     if (!MakeSpaceInPath(InsertPath.UsedSlots))
06587         return FALSE;
06588 
06589     // copy the Insert Path to the end of the original path
06590     *OtherStartSlot += UsedSlots;
06591     if (!InsertSectionAtEnd(&InsertPath, 0, InsertPath.UsedSlots))
06592         return FALSE;
06593 
06594     // Must have worked, so return happy
06595     CurrentPos = UsedSlots;
06596     return TRUE;
06597 }
06598 
06599 
06600 /********************************************************************************************
06601 
06602 >   BOOL Path::FindJoinedSubPathInfo(const DocCoord& JoinCoord, INT32* Start,
06603                                     INT32* Length, BOOL* JoinAtStart)
06604 
06605     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06606     Created:    27/4/94
06607     Inputs:     JoinCoord - The Coord to compare with the start and end points od sub paths
06608     Outputs:    Start - Pointer to an INT32 to hold the starting slot number of the sub path
06609                 Length - The number of slots in the found Sub Path
06610                 JoinAtStart - TRUE if JoinCoord intersected the start of the Sub Path, 
06611                             FALSE otherwise
06612     Returns:    TRUE if an intersection was found, FALSE if not
06613     Purpose:    This function searchs the path, compareing JoinCoord with the start and end
06614                 points of each of the Sub-paths in the path. If it is the same as one of the
06615                 start or end points, then the output params are filled and TRUE is returned.
06616 
06617 ********************************************************************************************/
06618 
06619 BOOL Path::FindJoinedSubPathInfo(const DocCoord& JoinCoord, INT32* Start, INT32* Length, BOOL* JoinAtStart)
06620 {
06621     // We will need all the arrays for both paths
06622     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
06623     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
06624 
06625     // walk through all the coords, looking for start and end points in SubPaths
06626     INT32 EndPos = UsedSlots-1;
06627     for (INT32 i=UsedSlots-1; i>=0; i--)
06628     {
06629         // See if we are at the start of a sub path
06630         if (Verbs[i] == PT_MOVETO)
06631         {
06632             // Set these to the start and end positions of the sub path
06633             *Start = i;
06634             *Length = EndPos - i + 1;
06635 
06636             // We are, so check the start and end of the sub path against the coord
06637             if (Coords[i] == JoinCoord)
06638             {
06639                 // This is Joined at the start, so set the values and return
06640                 *JoinAtStart = TRUE;
06641                 return TRUE;
06642             }
06643 
06644             if (Coords[EndPos] == JoinCoord)
06645             {
06646                 // This is Joined at the End, so set the values and return
06647                 *JoinAtStart = FALSE;
06648                 return TRUE;
06649             }
06650 
06651             EndPos = i-1;
06652         }
06653     }
06654 
06655     // We did not find an intersection with the subpaths, so fail
06656     return FALSE;
06657 }
06658 
06659 
06660 
06661 /********************************************************************************************
06662 
06663 >   BOOL Path::InsertSectionAtEnd(const Path* Other, INT32 StartSlot, INT32 NumSlots)
06664 
06665     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06666     Created:    29/4/94
06667     Inputs:     Other - Pointer to the path to copy data from
06668                 StartSlot - The slot in the path to start copying from
06669                 NumSlots - The number of slots to copy
06670     Returns:    TRUE if the data was copied OK, FALSE if it failed
06671     Purpose:    Copies a section (usually a sub-path) of a path to the end of another path.
06672                 This is used extensivly to Join Paths together. If there is not enougth
06673                 space in this path, then the operation will fail - It will NOT try to make
06674                 the path bigger. It is up to the caller to ensure that there are sufficent
06675                 UnUsedSlots in the path before calling.
06676 
06677 ********************************************************************************************/
06678 
06679 BOOL Path::InsertSectionAtEnd(const Path* Other, INT32 StartSlot, INT32 NumSlots)
06680 {
06681     // Make sure that there is room in the path to store this extra info
06682     if (UnUsedSlots<NumSlots)
06683     {
06684         TRACE( _T("There was not enough room in this path to copy data into"));
06685         return FALSE;
06686     }
06687 
06688     // We need to know about the path arrays
06689     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
06690     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
06691     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
06692 
06693     // and the new path
06694     DocCoord*  OtherCoords = (DocCoord*)  DescribeHandle(Other->CoordHandle);
06695     PathFlags* OtherFlags  = (PathFlags*) DescribeHandle(Other->FlagsHandle);
06696     PathVerb*  OtherVerbs  = (PathVerb*)  DescribeHandle(Other->VerbHandle);
06697 
06698     // Go though and copy all the element from one path to the other
06699     for (INT32 i=StartSlot; i<StartSlot+NumSlots; i++)
06700     {
06701         ENSURE(UnUsedSlots>0, "Ran out of space in a path that should have enough space in it" );
06702         if (UnUsedSlots<=0)
06703             return FALSE;
06704 
06705         // Copy the coords
06706         Coords[UsedSlots] = OtherCoords[i];
06707         Flags[UsedSlots] = OtherFlags[i];
06708         Verbs[UsedSlots] = OtherVerbs[i];
06709 
06710         // Move on to the next slots
06711         UsedSlots++;
06712         UnUsedSlots--;
06713     }
06714 
06715     return TRUE;
06716 }
06717 
06718 
06719 
06720 
06721 
06722 /********************************************************************************************
06723 
06724 >   BOOL Path::FindNearestPoint(DocCoord pos, UINT32 flags, INT32* position)
06725 
06726     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
06727     Created:    27/4/94
06728     Inputs:     pos - mouse position to be tested
06729                 flags - POINTFLAG_ENDPOINTS = look at endpoints
06730                         POINTFLAG_CONTROLPOINTS - look at control points
06731                         
06732                         (the flag POINTFLAG_ENDSFIRST has been removed and has
06733                         no effect - the endpoints are always scanned first)
06734     Outputs:    position - index to the point that was matched
06735                            unchanged if no match
06736     Returns:    TRUE if a match was found, FALSE if not
06737     Purpose:    This function searches for a point in the path which is both selected
06738                 and lies inside the largest blob bounding rectangle surrounding 'pos'.
06739                 Points checked for are as follows...
06740                 if ENDPOINTS is set
06741                     check all selected endpoints in this path.
06742                 if CONTROLPOINTS is set
06743                     check all control points lying directly next to selected endpoints
06744                     The rules are that control points are only enabled when a single end
06745                     point is selected and hence a maximum of 2 control points will be
06746                     checked.
06747                 If any of these points are close enough to be clicked on, the function 
06748                 returns TRUE, with position holding the index to the point that was clicked
06749 
06750 ********************************************************************************************/
06751 
06752 BOOL Path::FindNearestPoint(DocCoord pos, UINT32 flags, INT32* position)
06753 {
06754 #if !defined(EXCLUDE_FROM_XARLIB)
06755     // If neither ENDPOINTS or CONTROLPOINTS flags are set, return FALSE since the caller
06756     // obviously doesn't want to look at anything!
06757     if (!(flags & (POINTFLAG_ENDPOINTS | POINTFLAG_CONTROLPOINTS)))
06758         return (FALSE);
06759 
06760     // Always set POINTFLAG_ENDSFIRST
06761     flags |= POINTFLAG_ENDSFIRST;
06762 
06763     // Get arrays for this path
06764     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
06765     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
06766     PathFlags* PFlags = (PathFlags*) DescribeHandle(FlagsHandle);
06767 
06768     // Get a pointer to the DocView so we can do blob proximity checks
06769     DocView* pDocView = DocView::GetSelected();
06770     ERROR3IF(pDocView == NULL, "Path::FindNearestPoint: Can't find selected DocView");
06771     if (pDocView == NULL)
06772         return(FALSE);
06773 
06774     DocRect BlobRect;
06775     // INT32    numloops;
06776     
06777     // Now to calculate a rectangle around the pointer position which is
06778     // the same size as the biggest possible blob. We then go through the
06779     // path and see if any of the coordinates fall into that rectangle.
06780     // This is a very cunning reversal of the normal logic - go through the
06781     // path, find a blob rectangle for each point, then see if the pointer 
06782     // coordinate falls inside it. The only problem with this method is that
06783     // it assumes that all blobs are the same size, but since this is only 
06784     // for click detection, and we need a little bit of leeway, I think 
06785     // it's all right. Anyway, the usual code for this also makes this assumption,
06786     // although it doesn't rely on it.
06787 
06788     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), pos,
06789                                 BT_SELECTEDLARGEST, &BlobRect);
06790 
06791     // Now BlobRect holds a rectangle around the pointer position.
06792 
06793     // We have to first go through and look at the endpoints, counting them as we go
06794     INT32 EndCount = 0;
06795     INT32 EndIndex = 0;
06796     INT32 i;
06797     
06798     for (i=0; i<UsedSlots; i++)
06799     {
06800         // check to see if it's an endpoint, and if we want this kind of point
06801         if ((PFlags[i].IsEndPoint && (flags & POINTFLAG_ENDPOINTS)))
06802         {
06803             if (BlobRect.ContainsCoord(Coords[i]))
06804             {
06805                 *position = i;
06806                 return(TRUE);
06807             }
06808         }
06809         if (PFlags[i].IsEndPoint && PFlags[i].IsSelected && ((Verbs[i] & PT_CLOSEFIGURE) == 0))
06810         {
06811             EndCount++;
06812             EndIndex = i;
06813         }
06814     }
06815 
06816     // Now we've scanned the path for endpoints, let's look at any adjacent control points
06817     // if EndCount == 1, EndIndex is the index of the selected enpoint, ie there is only one
06818     // selected endpoint and its control handles will be enabled.
06819 
06820     if ( (EndCount==1) && (flags & POINTFLAG_CONTROLPOINTS) )
06821     {
06822         INT32 Index = FindPrevControlPoint(EndIndex);
06823         if (Index>=0)
06824         {
06825             // There is a previous control point, so look at it (and the one before it)
06826             if (BlobRect.ContainsCoord(Coords[Index]))
06827             {
06828                 *position = Index;
06829                 return TRUE;
06830             }
06831 
06832             if (BlobRect.ContainsCoord(Coords[Index-1]))
06833             {
06834                 *position = Index-1;
06835                 return TRUE;
06836             }
06837         }
06838 
06839         // Now look at the next control point (if it's there)
06840         Index = FindNextControlPoint(EndIndex);
06841         if (Index>=0)
06842         {
06843             // Look at the next one
06844             if (BlobRect.ContainsCoord(Coords[Index]))
06845             {
06846                 *position = Index;
06847                 return TRUE;
06848             }
06849 
06850             if (BlobRect.ContainsCoord(Coords[Index+1]))
06851             {
06852                 *position = Index+1;
06853                 return TRUE;
06854             }
06855         }
06856     }
06857 #endif
06858     return FALSE;
06859 }
06860 
06861 /****
06862     // We have to do two passes of the path if the caller is interested in both endpoints
06863     // and control points, and they requested endpoints first. Otherwise, we only need to
06864     // run round once
06865 
06866     if (flags == POINTFLAG_ENDSFIRST | POINTFLAG_CONTROLPOINTS | POINTFLAG_ENDSFIRST)
06867     {
06868         numloops = 2;
06869         flags = POINTFLAG_ENDPOINTS | POINTFLAG_ENDSFIRST;
06870     }
06871     else
06872         numloops = 1;
06873 
06874     INT32 count;
06875     for (count=1; count<=numloops; count++ )
06876     {
06877         INT32 i;
06878         for (i=0;i<UsedSlots ;i++ )
06879         {
06880             // check to see if it's an endpoint or control point, and if we want 
06881             // this kind of point
06882             if (
06883                 (PFlags[i].IsEndPoint && (flags & POINTFLAG_ENDPOINTS)) 
06884                 || 
06885                 (!(PFlags[i].IsEndPoint) && (flags & POINTFLAG_CONTROLPOINTS) && PFlags[i].IsSelected)
06886                )
06887             {
06888                 if (BlobRect.ContainsCoord(Coords[i]))
06889                 {
06890                     *position = i;
06891                     return(TRUE);
06892                 }
06893             }
06894 
06895         }
06896         // Now we've looked at the endpoints, let's look at the control points
06897         // if we were only doing one pass anyway, this line has no effect
06898         flags = POINTFLAG_CONTROLPOINTS;
06899     }
06900 
06901 
06902 *****/
06903 
06904 
06905 
06906 /********************************************************************************************
06907 
06908 >   double Path::SqrDistanceToPoint(const DocCoord pt, INT32* NearEl, double* mu )
06909 
06910     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
06911     Created:    22/08/94
06912     Inputs:     pt      = DocCoord of point of interest (eg MouseCoord)
06913     Outputs:    NearEl  = path index pointer of closest element to be retured
06914                 mu      = element parameter of closest point to be returned
06915     Returns:    a double, being the closest distance on the path
06916                 or -1 if there are no points in the path, mu being unaffected
06917     Purpose:    Finds the distance to the closest point on the path. For efficiency
06918                 it does not evaluate the closest point. Use this routine in conjunction
06919                 with ClosestPoint to find the actual closest point.
06920 
06921 ********************************************************************************************/
06922  
06923 double Path::SqrDistanceToPoint(const DocCoord pt, INT32* NearEl, double* mu)
06924 {
06925     // Get the coordinate list and verb list for this path instance
06926 
06927     DocCoord* Coords = GetCoordArray();
06928     PathVerb* Verbs = GetVerbArray();
06929 
06930     // check for the presence of at least some elements
06931 
06932     if (UsedSlots==0)
06933     {
06934         *NearEl = -1;
06935         return -1;
06936     }
06937 
06938     // calculate the distance to the first control point
06939 
06940     double fdist;
06941     double ldist;
06942     double tmu;
06943 
06944     INT32 el=-1;
06945 
06946     fdist = PathUtil::SqrDistance(Coords[0], pt);
06947 
06948     for (INT32 i=0; i<UsedSlots; i++)
06949     {
06950         switch (Verbs[i] & ~PT_CLOSEFIGURE)
06951         {
06952 
06953             case PT_LINETO:
06954                 ldist = PathUtil::SqrDistanceToLine(&Coords[i-1], pt, &tmu);
06955                 if (ldist<=fdist) 
06956                 {
06957                     fdist=ldist;
06958                     el=i;
06959                     *mu=tmu;
06960                 } 
06961                 break;
06962 
06963             case PT_BEZIERTO:
06964                 ldist = PathUtil::SqrDistanceToCurve(MAXPATHDIFFRATE, &Coords[i-1], pt, &tmu);
06965                 if (ldist<=fdist) 
06966                 {
06967                     fdist=ldist;
06968                     el=i;
06969                     *mu=tmu;
06970                 }
06971                 i+=2;                           // skip next two bezierto el.
06972                 break;
06973         }
06974     }
06975 
06976     if (el>-1)                                  // have we an element? 
06977     {
06978         *NearEl = el;
06979         return fdist;
06980     }
06981 
06982     *NearEl = -1;
06983     return -1;
06984 }
06985 
06986 
06987 
06988 /********************************************************************************************
06989 
06990 >   DocCoord Path::ClosestPointTo(const double t, const INT32 index)
06991 
06992     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
06993     Created:    22/08/94
06994     Inputs:     t        = closest distance parameter
06995                 index    = closest distance path element index
06996     Outputs:
06997     Returns:    DocCoord, the closest point on the path
06998     Purpose:    This function returns the point at which the closest distance on the path
06999                 occurs.
07000     Errors:     Ensures the indexed path element is either a lineto or bezierto
07001     
07002 ********************************************************************************************/
07003 
07004 DocCoord Path::ClosestPointTo(const double t, const INT32 index)
07005 {
07006     // Get the coord list and verb list for this path instance
07007 
07008     DocCoord* Coords = GetCoordArray();
07009     PathVerb* Verbs = GetVerbArray();
07010     PathVerb Verb = Verbs[index] & ~PT_CLOSEFIGURE;
07011 
07012     if ((t==0) && (index==0))
07013         return Coords[0];
07014 
07015     ENSURE(UsedSlots>1, "Not enough elements in path");
07016     ENSURE(index>=0, "invalid formal parameter: index");
07017     ENSURE((Verb == PT_LINETO) || (Verb == PT_BEZIERTO), 
07018             "Unable to find point on path. Index does not specify line or curve");
07019 
07020     if (index<UsedSlots)
07021     {
07022         switch (Verb)
07023         {
07024             case PT_LINETO:
07025                 return PathUtil::PointOnLine(t,&Coords[index-1]);
07026                 break;
07027             case PT_BEZIERTO:
07028                 return PathUtil::PointOnCurve(t,&Coords[index-1]);
07029                 break;
07030         }
07031     }
07032 
07033     // code should not really get here! if it does, return the first element
07034     // of the curve
07035 
07036     return (Coords[0]);
07037 }
07038 
07039 
07040 
07041 
07042 
07043 /********************************************************************************************
07044 
07045 >   BOOL Path::IsPointCloseTo(const DocCoord ICoord, 
07046                               INT32 range,
07047                               INT32* CloseElement, 
07048                               double* mu )
07049 
07050     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07051     Created:    22/08/94
07052     Inputs:     ICoord          = DocCoord of point of interest (eg MouseCoord)
07053                 range           = specifes the squared perpendicular distance away from
07054                                   the path forming a region inside which a ICoord is said
07055                                   to be 'in range'
07056     Outputs:    NearElement     = path index pointer of closest element to be retured
07057                 mu              = element parameter of closest point to be returned
07058     Returns:    True if ICoord is in range.
07059     Purpose:    This routine can be used to determin whether a point is close enough to
07060                 a path to allow some action to occur. The range input parameter can be
07061                 used to determin the size of the sensitive area.
07062 
07063 ********************************************************************************************/
07064 
07065 BOOL Path::IsPointCloseTo(const DocCoord ICoord,
07066                           INT32 range,
07067                           INT32* NearElement, 
07068                           double* mu )
07069 {
07070     double dist = SqrDistanceToPoint(ICoord, NearElement, mu);
07071 //  if (IsUserName("Jim")) TRACE( _T("Distance from path = %f\n"),dist);
07072     if (dist<0) return FALSE;                   // return false (not close to path)
07073     return (dist<range);
07074 }
07075 
07076 
07077 
07078 
07079 
07080 /********************************************************************************************
07081 
07082 >   BOOL Path::SplitAtPoint(const DocCoord SplitPt,
07083                             INT32* SplitElement,
07084                             INT32* NumElements,
07085                             PathVerb* Verbs,
07086                             DocCoord* Coords)
07087 
07088     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07089     Created:    22/08/94
07090     Inputs:     SplitPt      = DocCoord to use to split the path as close as possible to
07091 
07092     Outputs:    SplitElement = The element index at which the split occured
07093                 NumElements  = The number of elements generated by the split operation
07094                 Verbs        = a list of NumElements path verbs.
07095                 Coords       = a list of NumElements doc coords generated by the split
07096 
07097     Returns:    TRUE    = if the path could be split, 
07098                 FALSE   = if SplitPt was closest to an already existing control vertex
07099                           and hence is unable to split the element.
07100 
07101     Purpose:    This function finds the element (line or curve) on which the closest point to
07102                 SplitPt occurs. A parameter t describes the point on this element and is used
07103                 to split the element into two distinct pieces. The verbs and coordinates of
07104                 these pieces are returned to the caller in the output lists. NumElements will
07105                 describe how many path components have been created and stored in the lists
07106                 This will be a value of 2 for lines and 6 for curves.
07107 
07108 ********************************************************************************************/
07109 
07110 BOOL Path::SplitAtPoint(const DocCoord SplitPt,
07111                         INT32* SplitElement,
07112                         UINT32* NumElements,
07113                         PathVerb* Verbs,
07114                         DocCoord* Coords)
07115 
07116 {
07117     double mu;
07118     double dist = SqrDistanceToPoint(SplitPt, SplitElement, &mu);
07119     if (dist < 0.0) return FALSE;
07120 
07121     PathVerb* InVerbs = GetVerbArray();
07122     DocCoord* InCoords = GetCoordArray();
07123 
07124     PathVerb vb = InVerbs[*SplitElement] & ~PT_CLOSEFIGURE;
07125     ENSURE((vb == PT_LINETO) || (vb == PT_BEZIERTO), 
07126             "Unable to find point on path. Index does not specify line or curve");
07127 
07128     switch (vb)
07129     {
07130         case PT_LINETO:
07131             return PathUtil::SplitLine(mu, &InCoords[(*SplitElement)-1], NumElements, Verbs, Coords);
07132             break;
07133         case PT_BEZIERTO:
07134             return PathUtil::SplitCurve(mu, &InCoords[(*SplitElement)-1], NumElements, Verbs, Coords);
07135             break;
07136     }
07137 
07138     return FALSE;
07139 }
07140 
07141 
07142 
07143 /********************************************************************************************
07144 
07145 >   BOOL Path::PointCloseToLine(DocCoord pos, INT32* position)
07146 
07147     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
07148     Created:    27/4/94
07149     Inputs:     pos - mouse position to be tested
07150     Outputs:    position - index to the point that was matched
07151                            unchanged if no match
07152     Returns:    TRUE if a match was found, FALSE if not
07153     Purpose:    This function takes the coordinate pos, and sees if it is close
07154                 to a line (close enough to drag the line around). It returns
07155                 TRUE if the point is close enough, and position is an index to
07156                 the element that it was closest to
07157 
07158                 Since this routine requires maths beyond that of which I am
07159                 capable, it returns FALSE at the moment
07160 
07161 ********************************************************************************************/
07162 
07163 BOOL Path::PointCloseToLine(DocCoord pos, INT32* position)
07164 {
07165 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARLIB)
07166 /*
07167     double d;
07168     INT32 Near;
07169     double Param;
07170 
07171     d = SqrDistanceToPoint(pos, &Near, &Param);
07172 
07173     TRACE( _T("Index=%d\n"),Near);
07174 
07175     DocCoord coord;
07176     coord = ClosestPointTo(Param, Near);
07177 
07178     TRACE( _T("x=%d, y=%d\n"),coord.x,coord.y);
07179 
07180  */
07181 
07182     DocRect temp;
07183     GetApplication()->GetBlobManager()->GetBlobRect(DocCoord(0,0),&temp);
07184     double mu;
07185     INT32 width = (temp.Width())/2; 
07186     return (IsPointCloseTo(pos, width*width, position, &mu));
07187 
07188 //  return(FALSE);
07189 #else
07190     return FALSE;
07191 #endif
07192 }
07193 
07194 
07195 /********************************************************************************************
07196 
07197 >   BOOL Path::ClosestSelectedEndpoint(DocCoord pos, INT32* position, double* distance)
07198 
07199     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
07200     Created:    27/4/94
07201     Inputs:     pos - mouse position to be tested
07202     Outputs:    position -  index to the point that was matched
07203                             unchanged if no match
07204                 distance -  distance between the specified point and the
07205                             closest selected endpoint (unchanged if there are none)
07206     Returns:    TRUE if a match was found, FALSE if not
07207     Purpose:    This function finds the closest selected line end to the
07208                 cursor position passed in. If the path has no selected line
07209                 ends, the function returns FALSE
07210 
07211 ********************************************************************************************/
07212 
07213 BOOL Path::ClosestSelectedEndpoint(DocCoord pos, INT32* position, double* distance)
07214 {
07215     // Get arrays for this path
07216     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
07217     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
07218     PathFlags* PFlags = (PathFlags*) DescribeHandle(FlagsHandle);
07219 
07220     double nearest = 0;
07221     double tempdistance = 0;
07222     BOOL foundany = FALSE;
07223 
07224     // Scan from the end of the path to the start. We remember the last point in the subpath
07225     // and go back until we hit a MoveTo. then we look at both the elements, and if the 
07226     // CloseFigure flag isn't set in the end element, the subpath must be open, in which
07227     // case we look at the endpoints and see if they are closer to pos than others.
07228 
07229     INT32 endpoint = UsedSlots-1;
07230     for (INT32 i=UsedSlots-1;i>=0;i--)
07231     {
07232         if (Verbs[i] == PT_MOVETO)
07233         {
07234             // only check the ends if CLOSEFIGURE is not set in the end verb
07235             if (!(Verbs[endpoint] & PT_CLOSEFIGURE))
07236             {
07237                 // first check the moveto
07238                 tempdistance = Coords[i].Distance(pos);
07239                 if ((PFlags[i].IsSelected) && (!foundany || (nearest>tempdistance)))
07240                 {
07241                     *position = i;
07242                     nearest = tempdistance;
07243                     foundany = TRUE;
07244                 }
07245 
07246                 // Now check the end point
07247                 tempdistance = Coords[endpoint].Distance(pos);
07248                 if ((PFlags[endpoint].IsSelected) && (!foundany || (nearest>tempdistance)))
07249                 {
07250                     *position = endpoint;
07251                     nearest = tempdistance;
07252                     foundany = TRUE;
07253                 }
07254             }
07255             endpoint = i-1;
07256         }
07257     }
07258     return(foundany);
07259     
07260 }
07261 
07262 
07263 /********************************************************************************************
07264 
07265 >   BOOL Path::CloneFrom(const Path& Other)
07266 
07267     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07268     Created:    26/10/94
07269     Inputs:     Other = pointer to a path to copy data from
07270     Returns     TRUE if successful
07271                 FALSE if unable in increase the size of this path to cope with all
07272                 data in 'Other'
07273     Purpose:    Replace the data within this path with that of Other path
07274 
07275 ********************************************************************************************/
07276 
07277 BOOL Path::CloneFrom(const Path& Other)
07278 {
07279     // Make space to copy all the data from the other path into this one
07280     INT32 SlotsToCopy = Other.UsedSlots;
07281     INT32 SlotsToAdd  = SlotsToCopy-UsedSlots;
07282 
07283     if (SlotsToAdd>0)
07284     {
07285         if (!MakeSpaceAtEnd(SlotsToAdd))
07286             return FALSE;
07287     }
07288 
07289     // We will need all the arrays from the other path
07290     DocCoord*  SCoords = (DocCoord*)  DescribeHandle(Other.CoordHandle);
07291     PathVerb*  SVerbs  = (PathVerb*)  DescribeHandle(Other.VerbHandle);
07292     PathFlags* SFlags  = (PathFlags*) DescribeHandle(Other.FlagsHandle);
07293 
07294     // Get the destination arrays
07295     DocCoord*  DCoords = (DocCoord*)  DescribeHandle(CoordHandle);
07296     PathVerb*  DVerbs  = (PathVerb*)  DescribeHandle(VerbHandle);
07297     PathFlags* DFlags  = (PathFlags*) DescribeHandle(FlagsHandle);
07298 
07299     // copy all arrays from the source to destination
07300     if (SlotsToCopy>0)
07301     {
07302         if (SCoords != NULL) memmove((void*)(DCoords), (void*)(SCoords), SlotsToCopy*sizeof(DocCoord));
07303         if (SVerbs  != NULL) memmove((void*)(DVerbs),  (void*)(SVerbs),  SlotsToCopy*sizeof(PathVerb));
07304         if (SFlags  != NULL) memmove((void*)(DFlags),  (void*)(SFlags),  SlotsToCopy*sizeof(PathFlags));
07305     }
07306 
07307     // Now update the array variables
07308     UsedSlots   += SlotsToAdd;
07309     UnUsedSlots -= SlotsToAdd;
07310     CurrentPos   = UsedSlots;
07311 
07312     return TRUE;
07313 }
07314 
07315 
07316 
07317 
07318 
07319 /********************************************************************************************
07320 
07321 >   BOOL Path::RetroReplaceSection(INT32 StartSlot, INT32 NumSlots, Path* NewPath, BOOL KeepStart)
07322 
07323     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
07324     Created:    20/5/94
07325     Inputs:     StartSlot - The slot to start replacing from
07326                 NumSlots - The number of slots to replace
07327                 NewPath - The new path to place in the path to replace the elements from
07328                 StartSlot to StartSlot+NumSlots
07329                 KeepStart - TRUE if the initial moveto of the NewPath should be pasted
07330                 into the path, FALSE if you want to throw away the initial moveto.
07331     Returns:    TRUE if it worked, FALSE if it failed (Could not alloc memory if NewPath
07332                 is bigger than the section it was replacing
07333     Purpose:    Deletes all the elements in the path from StartSlot to StartSlot+NumSlots
07334                 and then inserts the whole of NewPath into the hole. If the hole is not
07335                 big enough, then it is made bigger and if it is too big, then it is shrunk.
07336 
07337 ********************************************************************************************/
07338 
07339 BOOL Path::RetroReplaceSection(INT32 StartSlot, INT32 Len, Path* NewPath, BOOL KeepStart)
07340 {
07341     // Saftey Checks
07342     ENSURE( StartSlot<UsedSlots, "PathPosition is off end of path in Path::RetroReplaceSection" );
07343     ENSURE( StartSlot>=0, "PathPosition is off beginning of path in Path::RetroReplaceSection" );
07344 
07345     // Make enough space in the path to copy the new path in after the marked section has been removed
07346     INT32 SlotsToAdd = NewPath->UsedSlots - Len;
07347     if (SlotsToAdd>0)
07348     {
07349         // We need more space as the new path is bigger than the gap
07350         CurrentPos = StartSlot+1;
07351         if (!MakeSpaceInPath(SlotsToAdd))
07352             // Failed to get the memory, so fail
07353             return FALSE;
07354 
07355         // Update the usage counters
07356         UsedSlots += SlotsToAdd;
07357         UnUsedSlots -= SlotsToAdd;
07358     }
07359 
07360     // Dereference the pointers
07361     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
07362     DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
07363     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
07364 
07365     // If the section to be replaced is bigger than the new path, then we should move the end of
07366     // the path down a bit
07367     if (SlotsToAdd<0)
07368     {
07369         // Find out how much memory to move about
07370         INT32 SlotToMoveFrom = StartSlot+Len;
07371         INT32 SlotToMoveTo   = StartSlot+NewPath->UsedSlots;
07372         INT32 SlotsToMove = UsedSlots-SlotToMoveFrom;
07373 
07374         // Does it need moving
07375         if (SlotsToMove>0)
07376         {
07377             // yep, move it
07378             memmove((void*)(&Verbs[SlotToMoveTo]), (void*)(&Verbs[SlotToMoveFrom]), SlotsToMove*sizeof(PathVerb));
07379             memmove((void*)(&Flags[SlotToMoveTo]), (void*)(&Flags[SlotToMoveFrom]), SlotsToMove*sizeof(PathFlags));
07380             memmove((void*)(&Coords[SlotToMoveTo]), (void*)(&Coords[SlotToMoveFrom]), SlotsToMove*sizeof(DocCoord));
07381         }
07382 
07383         // Change the path flags (SlotsToAdd is negative in here)
07384         UsedSlots += SlotsToAdd;
07385         UnUsedSlots -= SlotsToAdd;
07386     }
07387 
07388     // Find out about the pointers to the new path
07389     PathVerb*  NewVerbs  = (PathVerb*)  DescribeHandle(NewPath->VerbHandle);
07390     DocCoord*  NewCoords = (DocCoord*)  DescribeHandle(NewPath->CoordHandle);
07391     PathFlags* NewFlags  = (PathFlags*) DescribeHandle(NewPath->FlagsHandle);
07392 
07393     // Now we have to copy NewPath into the hole
07394     INT32 ReadStart, ReadLen, WriteStart;
07395 
07396     // Work out which section we are copying
07397     if (KeepStart)
07398     {
07399         // Here we are keeping the initial moveto in the replacement path
07400         ReadStart = 0;
07401         ReadLen = NewPath->UsedSlots;
07402         WriteStart = StartSlot;
07403     }
07404     else
07405     {
07406         // Here we are throwing away the Moveto in the replacement path
07407         ReadStart = 1;
07408         ReadLen = NewPath->UsedSlots-1;
07409         WriteStart = StartSlot+1;
07410     }
07411 
07412     // Go though the path replacing all the relavent sections
07413     for (INT32 i=ReadStart; i<ReadStart+ReadLen; i++)
07414     {
07415         // Copy the data from the new path into the old path
07416         Verbs[WriteStart] = NewVerbs[i];
07417         Flags[WriteStart] = NewFlags[i];
07418         Coords[WriteStart] = NewCoords[i];
07419 
07420         // increment the destination slot
07421         WriteStart++;       
07422     }
07423 
07424     // See if this is a closed shape
07425     TryToClose();
07426 
07427     // All worked, so return happy
07428     return TRUE;
07429 }
07430 
07431 
07432 /******************************************************************************************
07433 
07434 >   BOOL Path::SmoothRegions1(const double error, BOOL smoothall, BOOL reselect)
07435 
07436     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07437     Created:    20/10/94
07438     Inputs:     error       = a double describing the max error to use when smoothing
07439                 smoothall   = true then ignore selected points within this path and
07440                               smooth the whole path.
07441                 reselect    = a boolean indicating whether the path sections should be 
07442                               re selected after smoothing
07443 
07444     Outputs:    Operates on this path. the path will be smoothed meaning that it
07445                 will change in an unpredictable manner. The number of elements in
07446                 the path and control point positioning will change, if TRUE is
07447                 returned.
07448 
07449     Returns:    FALSE   then unable to smooth the curve
07450                 TRUE    then the path has been smoothed successfully
07451 
07452     Purpose:    Given a path, search for regions of interest and smooth these regions.
07453                 Regions are defined by sequences of connected selected points ie
07454                 
07455                     if P is the set of all control points [p0,....,pn]
07456                     then a region Q is a connected subset of P , [pi....pj]
07457                     where all members of Q are selected.
07458 
07459                 If smoothall is true the whole path is smoothed.    
07460                 You should call this function with a path which you do not mind being
07461                 corrupted, ie changed in an unpredictable way. The function may
07462                 successfully smooth a number of regions within the path, then fail on
07463                 one particular region. The result will be a path with m out of n regions
07464                 smoothed.
07465 
07466 ******************************************************************************************/
07467 
07468 BOOL Path::SmoothRegions(const double error, BOOL smoothall, BOOL reselect)
07469 {
07470     INT32 index = 0;
07471     INT32 start = 0;
07472     INT32 end = UsedSlots-1;
07473 
07474     if (smoothall)
07475     {
07476         INT32 sel;
07477         (reselect) ? sel=4 : sel=0;
07478         return (Path::SmoothSection(start,&end,error, sel));
07479     }
07480     else
07481     {       
07482         while ((index<UsedSlots) && (FindSelectionLimits(index,&start,&end)))
07483         {
07484             // increase the region by 1 either way and smooth it.
07485             INT32 nstart = start;
07486             INT32 nend = end;
07487 
07488             ExpandRange(&nstart,&nend,1);
07489 
07490             if (nstart != nend)
07491             {
07492                 INT32 sel = 0;
07493                 if (reselect)
07494                 {
07495                     if (nstart<start) sel |=1 ;
07496                     if (nend>end) sel |=2 ;
07497                     if (sel==0) sel=4;
07498                 }
07499                 BOOL ok = Path::SmoothSection(nstart, &nend, error, sel);
07500                 if (!ok) return FALSE;
07501             }
07502             index = nend+1;
07503         }
07504     }
07505     return TRUE;
07506 }
07507 
07508 
07509 /******************************************************************************************
07510 
07511 >   BOOL Path::SmoothRegions(const double error, BOOL smoothall)
07512 
07513     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07514     Created:    20/10/94
07515     Inputs:     error       = a double describing the max error to use when smoothing
07516                 smoothall   = true then ignore selected points within this path and
07517                               smooth the whole path.
07518 
07519     Outputs:    Operates on this path. the path will be smoothed meaning that it
07520                 will change in an unpredictable manner. The number of elements in
07521                 the path and control point positioning will change, if TRUE is
07522                 returned.
07523 
07524     Returns:    FALSE   then unable to smooth the curve
07525                 TRUE    then the path has been smoothed successfully
07526 
07527     Purpose:    Given a path, search for regions of interest and smooth these regions.
07528                 Regions are defined by (start/end) selected points with any number of
07529                 none selected points inbetween.  
07530                 If smoothall is true or there are no selected regions within the path,
07531                 the whole path is smoothed.
07532                 You should call this function with a path which you do not mind being
07533                 corrupted, ie changed in an unpredictable way. The function may
07534                 successfully smooth a number of regions within the path, then fail on
07535                 one particular region. The result will be a path with m out of n regions
07536                 smoothed.
07537 
07538 ******************************************************************************************/
07539 /*
07540 BOOL Path::SmoothRegions(const double error, BOOL smoothall)
07541 {
07542 
07543     // ok, hunt through the path for a section to smooth
07544     // if we cannot find a section, smooth the whole lot.
07545 
07546     INT32 first = 0;
07547     INT32 last = UsedSlots-1;
07548     INT32 nregions = 0;
07549     INT32 numselpoints = 0;
07550     BOOL ok = TRUE;
07551 
07552     if (!smoothall)
07553         numselpoints = GetNumSelEndPoints();
07554 
07555     INT32 count = numselpoints;
07556 
07557     switch (numselpoints)
07558     {
07559         case 0:
07560         case 1:
07561             return (Path::SmoothSection(0,&last,error));
07562             break;
07563 
07564         default:
07565             ok = FindNextSelected(&first);
07566             while (ok)
07567             {
07568                 count--;
07569                 // find the next region of interest
07570                 last = first+1;
07571                 ok = FindNextSelected(&last);
07572                 if (ok)
07573                 {
07574                     count--;
07575                     if (count == 1)
07576                     {
07577                         last++;
07578                         ok = FindNextSelected(&last);
07579                         if (ok)
07580                             count--;
07581                     }
07582 
07583                     if (ok)
07584                     {   
07585                         ok = Path::SmoothSection(first, &last, error);
07586                         if (ok)
07587                         {
07588                             first = last+1;
07589                             ok = FindNextSelected(&first);
07590                         }
07591                     }
07592                 }
07593             }
07594             break;
07595     }
07596     if (count == 0) 
07597         return TRUE;
07598     return FALSE;
07599 }
07600 
07601 */
07602 
07603 
07604 /******************************************************************************************
07605 
07606 >   BOOL Path::SmoothSection(INT32 index1, INT32* index2, const double error, BOOL reselect)
07607 
07608     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07609     Created:    20/10/94
07610 
07611     Inputs:     index1      = path element index to begin smoothing from.
07612                 index2      = path element index to smooth to.
07613                               -1 then smooth to the end of the path.
07614                 error       = a double describing the max error to use when smoothing
07615                 selection   = 0 then do not reselect the smoothed regions
07616                             = 1 then reselect all but the first point in the section
07617                             = 2 then reselect all but the last point in the section
07618                                 a boolean indicating whether the smoothed region should
07619                             = 3 then reselect all but both end points
07620                             = 4 reselect all the points in the section
07621 
07622     Outputs:    index2  = the new index of the end of the smoothed section
07623                 this    = the altered path, smoothed between index1, index2
07624                 
07625     Returns:    BOOL    = TRUE if the path was smoothed successfully
07626                         = FALSE then the path remains unchanged
07627 
07628     Purpose:    Smooth a path between the given section delimited by [index1,index2].
07629 
07630 ******************************************************************************************/
07631 
07632 BOOL Path::SmoothSection(INT32 index1, INT32* index2, const double error, INT32 selection)
07633 {
07634     INT32 last = *index2;
07635     if (last==-1)
07636         last = UsedSlots-1;
07637 
07638     // Saftey Checks
07639     ERROR3IF( index1>=last, "Section delimiters are invalid in Path::SmoothSection");
07640     ERROR3IF( last>=UsedSlots, "Section end is off end of path in Path::SmoothSection");
07641     ERROR3IF( index1<0, "Section start is negative in Path::SmoothSection");
07642 
07643     // Release safety checks
07644     if (index1>=last)
07645         return FALSE;
07646     if (last>=UsedSlots)
07647         return FALSE;
07648     if (index1<0)
07649         return FALSE;
07650 
07651     INT32 numtosmooth = last-index1+1;
07652 
07653     // We are smoothing a section of the path so extract a copy from
07654     // the specified section.
07655     Path PathSection;
07656     BOOL ok = PathSection.Initialise(numtosmooth,12);
07657 
07658     if (ok)
07659     {
07660         ok = MakePathFromSection(index1, numtosmooth, &PathSection);
07661         if (ok) 
07662         {
07663             // smooth the section completely
07664             ok = PathSection.Smooth(error);
07665 
07666             if (ok)
07667             {
07668                 // stick the smoothed path back in as a section and return a new
07669                 // index2 value
07670                 ok = RetroReplaceSection(index1, numtosmooth, &PathSection, FALSE);
07671                 if (ok)
07672                 {
07673                     // set up the last coordinate index return param
07674                     *index2 = index1 + PathSection.GetNumCoords() - 1;
07675 
07676                     // if we need to reselect the points then go to it
07677                     if (selection)
07678                     {
07679                         PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
07680                         INT32 start = index1;
07681                         INT32 end = *index2;
07682                         if (!(selection & 4))
07683                         {
07684                             if (selection & 1)
07685                                 FindNextEndPoint(&start);
07686                             if (selection & 2)
07687                                 FindPrev(&end);
07688                         }
07689                         for (INT32 i=start; i<=end; i++)
07690                             Flags[i].IsSelected = TRUE;
07691                     }
07692                 }
07693             }
07694         }
07695     }
07696     return ok;
07697 }
07698 
07699 
07700 /********************************************************************************************
07701 
07702 >   BOOL Path::MakePathFromSection(const INT32 Start, const INT32 Length, Path* pDestin)
07703 
07704     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07705     Created:    12/10/94
07706     Inputs:     Start   = Position within this path to copy data from
07707                 Length  = Number of items to copy
07708                 pDestin = pointer to path to put copied section in.
07709                 
07710     Returns:    TRUE if the copy was a success, FALSE if it failed (lack of memory)
07711     Purpose:    Copies a specified section from this path to a specific position within
07712                 another path. A gap is created if necessary to copy the data into
07713 
07714 ********************************************************************************************/
07715 
07716 BOOL Path::MakePathFromSection(const INT32 Start, const INT32 Length, Path* pDestin)
07717 {
07718     // Validate the input params
07719     ERROR3IF(Start>(UsedSlots-1),"MakePathFromSection given an illegal start index");
07720     if (Start > (UsedSlots-1))
07721         return FALSE;
07722 
07723     ERROR3IF((Start+Length)>UsedSlots,"MakePathFromSection given an illegal Length");
07724     if ((Start+Length) > UsedSlots)
07725         return FALSE;
07726 
07727 //  PathVerb* SourceVerbs = GetVerbArray();
07728 
07729     BOOL ok = MergeSectionTo(Start, Length, pDestin, 0);
07730     if (ok)
07731     {
07732         PathVerb* pDestinVerbs = pDestin->GetVerbArray();
07733         pDestinVerbs[0] = PT_MOVETO;
07734     }
07735     return ok;
07736 }
07737 
07738 
07739 
07740 
07741 
07742 /******************************************************************************************
07743 
07744 >   BOOL Path::Smooth(const double error)
07745 
07746     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07747     Created:    20/10/94
07748     Inputs:     error   = a double describing the max error to use when smoothing
07749 
07750     Outputs:    Operates on this path. the path will be smoothed meaning that it
07751                 will change in an unpredictable manner. The number of elements in
07752                 the path and control point positioning will change, if TRUE is
07753                 returned.
07754 
07755     Returns:    BOOL    = TRUE if the path was smoothed successfully
07756                         = FALSE then consider 'this' path has been corrupted
07757                         
07758     Purpose:    Smooth a path, using the fitcurve function, which may turn out to be NOT
07759                 the way to do it but I'll give it a try. You should generally call this
07760                 funcion with a copy of the path you want to smooth. This smooth function
07761                 will corrupt the path if it fails.
07762                 Call the function SmoothSection() describing your entire path if you
07763                 wish to smooth a path without corrupting anything on failure.
07764     SeeAlso:    SmoothSection
07765 
07766 ******************************************************************************************/
07767 
07768 BOOL Path::Smooth(const double error)
07769 {
07770 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARLIB)
07771     double QuantOrder = 1024;
07772 
07773     INT32 orignumcoords = UsedSlots;
07774 
07775     // create a path and quantise to it
07776     Path QuantPath;
07777     QuantPath.Initialise(24,12);
07778 
07779     // quantise original path to QuantPath
07780     Quantise(QuantOrder, &QuantPath);
07781 
07782     // create and fit a smooth curve to the data points.
07783     CurveFitObject Smoothed(&QuantPath, error);
07784     BOOL ok = Smoothed.Initialise(&QuantPath,QuantPath.GetNumCoords());
07785 
07786     if (ok)
07787     {
07788         Smoothed.FitCurve();
07789 
07790         // now that we've smoothed the curve lets check on the number
07791         // of coordinates we've generated. If its more, then we should
07792         // leave the original path as it was. If its less then replace
07793         if (QuantPath.GetNumCoords()<orignumcoords)
07794             ok = CloneFrom(QuantPath);
07795         
07796     }
07797     return ok;
07798 #else
07799     return FALSE;
07800 #endif
07801 }
07802 
07803 
07804 
07805 /******************************************************************************************
07806 
07807 >   BOOL Path::Quantise(const double threshold, Path* pOutput)
07808 
07809     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07810     Created:    25/10/94
07811     Inputs:     threshold   = How quantised the curves should be (around 64 is good)
07812     Outputs:    pOutput     = will contain the quantised path   
07813     Returns:    BOOL        = TRUE if the path was flattened successfully
07814                             = FALSE then output path will be unaffected
07815                         
07816     Purpose:    Quantise a path. All curves within the path will be approximated by
07817                 straight lines. pOutput may point at 'this' path ie a path can be
07818                 quantised to itself. If the quantising fails for some reason, the
07819                 output path will be unaffected EVEN if you're quantising a path to
07820                 itself.
07821                 Note, Quantising differs from flattening in that it will approximate
07822                 straight lines within a path, generating n colinear points for each
07823                 line it meets. Whereas flattening does nothing with lines.
07824     
07825 ******************************************************************************************/
07826 
07827 BOOL Path::Quantise(const double threshold, Path* pOutput)
07828 {
07829     // when flattening, flatten curves but dont quantise lines
07830     ProcessFlags QuantFlags(TRUE,TRUE);
07831     return CreateFlatPath(QuantFlags, threshold, pOutput);
07832 }
07833 
07834 
07835 /******************************************************************************************
07836 
07837 >   BOOL Path::Flatten(const double flatness, Path* pOutput)
07838 
07839     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07840     Created:    25/10/94
07841     Inputs:     flatness    = How flat the curves should be (around 64 is good)
07842     Outputs:    pOutput     = will contain the flattened path   
07843     Returns:    BOOL        = TRUE if the path was flattened successfully
07844                             = FALSE then output path will be unaffected
07845                         
07846     Purpose:    Flatten a path. All curves within the path will be approximated by
07847                 straight lines. pOutput may point at 'this' path ie a path can be
07848                 flattened to itself. If the flattening fails for some reason, the
07849                 output path will be unaffected even if you are flattening a path to
07850                 itself.
07851     
07852 ******************************************************************************************/
07853 
07854 BOOL Path::Flatten(const double flatness, Path* pOutput)
07855 {
07856     // when flattening, flatten curves but dont quantise lines
07857     ProcessFlags FlatFlags(TRUE,FALSE);
07858     return CreateFlatPath(FlatFlags, flatness, pOutput);
07859 }
07860 
07861 
07862 /******************************************************************************************
07863 
07864 >   BOOL Path::CreateFlatPath(ProcessFlags PFlags, const double flatness, Path* pOutput)
07865 
07866     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07867     Created:    25/10/94
07868     Inputs:     PFlags      = process flags, to control the path processor function
07869                               (see classes ProcessPath and ProcessFlags)
07870                 flatness    = How flat the curves should be (around 64 is good)
07871     
07872     Outputs:    pOutput     = will contain the flattened path   
07873     
07874     Returns:    BOOL        = TRUE if the path was flattened successfully
07875                             = FALSE then output path will be unaffected
07876                         
07877     Purpose:    Flatten a path. All curves within the path will be approximated by
07878                 straight lines. pOutput may point at 'this' path ie a path can be
07879                 flattened to itself. If the flattening fails for some reason, the
07880                 output path will be unaffected even if you're flattening a path to
07881                 itself.
07882     
07883 ******************************************************************************************/
07884 
07885 BOOL Path::CreateFlatPath(const ProcessFlags& PFlags, const double flatness, Path* pOutput)
07886 {
07887 
07888     // create a flattening object
07889     ProcessFlatten FlattenObj(flatness);
07890 //  INT32 numcoords = pOutput->GetNumCoords();
07891 
07892     // ok, generate a flattened path on the end of Output
07893     return FlattenObj.FlattenPath(PFlags, this, pOutput);
07894 
07895 }
07896 
07897 
07898 
07899 /******************************************************************************************
07900 >   double Path::SqrLength()
07901 
07902     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07903     Created:    1/11/94
07904     Inputs:     -
07905     Outputs:    -       
07906     Returns:    The squared length of the path defined in the same coordinate system as 
07907                 the paths points.       
07908     Purpose:    Find the length of a path in millipoints. The length is calculated by
07909                 flattening the path and finding straight line segment lengths and so is
07910                 a discrete numeric approximation to a line integral.
07911 ******************************************************************************************/
07912 double Path::SqrLength()
07913 {
07914     double Acc = 64;
07915     double Len = 0;
07916     ProcessLength LengthObj(Acc);
07917 
07918 
07919     BOOL ok = LengthObj.PathLength(this, &Len);
07920     if (ok)
07921         return Len;
07922     return 0;
07923 
07924 }
07925 
07926 
07927 
07928 /******************************************************************************************
07929 >   double Path::GetPathLength(double dFlatness = 64.0)
07930 
07931     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
07932     Created:    19/8/96
07933     Inputs:     -
07934     Outputs:    -       
07935     Returns:    The length of the path in millipoints
07936     Purpose:    Find the length of a path in millipoints.
07937 ******************************************************************************************/
07938 double Path::GetPathLength(double dFlatness)
07939 {
07940     double TotalLength = 0.0;
07941     INT32 LastEndPoint = GetNumCoords()-1;
07942     
07943     if (LastEndPoint==-1)
07944         return 0.0;
07945 
07946     BOOL ok = DistanceTo(LastEndPoint, 1.0, &TotalLength, dFlatness);
07947     ERROR3IF(!ok, "Path.DistanceTo failed");
07948 
07949     if (ok)
07950         return TotalLength;
07951     else
07952         return 0.0;
07953 }
07954 
07955 
07956 
07957 /********************************************************************************************
07958 
07959 >   DocCoord Path::ConvSqrDistToCoord(const double sqrdist)
07960 
07961     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07962     Created:    1/11/94
07963     Inputs:     sqrdist = squared distance along the path
07964     Outputs:    
07965     Returns:    a doccoord which is dist units along the path
07966     Purpose:    Finds the coordinate which is dist units along 'this' path
07967 
07968 
07969 ********************************************************************************************/
07970  
07971 DocCoord Path::ConvSqrDistToCoord(const double sqrdist)
07972 {
07973     INT32 Index;
07974     double mu = DistReparameterise(sqrdist, &Index);
07975     return ClosestPointTo(mu, Index);   
07976 }
07977 
07978 
07979 
07980 /********************************************************************************************
07981 
07982 >   double Path::DistReparameterise(const double sqrdist, INT32* Element)
07983 
07984     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
07985     Created:    1/11/94
07986     Inputs:     sqrdist = squared distance along the path
07987     Outputs:    Element = index of element which bounds dist 
07988                           as a half open interval, (El.Plo < dist <= El.Phi)
07989     Returns:    a double, being the parameter along the returned element, 
07990                 which is dist units along the whole path. 
07991     Purpose:
07992     SeeAlso:    ConvDistToCoord()
07993                 ClosestPointTo()
07994 
07995 ********************************************************************************************/
07996 
07997 double Path::DistReparameterise(const double sqrdist, INT32* Element)
07998 {
07999     // set up a distance object and call its processor
08000     double Acc = 64;
08001     ProcessDistance DistanceObj(Acc);
08002     return DistanceObj.PathDistance(sqrdist, this, Element);
08003 }
08004 
08005 
08006 
08007 
08008 /********************************************************************************************
08009 
08010 >   INT32   Path::CalcCRC()
08011 
08012     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
08013     Created:    11/11/94
08014     Inputs:     -
08015     Returns:    The CRC checksum for the path, relative to the first coord
08016     Purpose:    Calculates a cyclic redundancy check value for a path.
08017                 This CRC is invariant under translation only.
08018                 It can be used to determine whether a new path matches that of an old
08019                 path whose CRC we already know, whether the new path has been translated
08020                 or not.
08021 
08022 ********************************************************************************************/
08023 
08024 INT32   Path::CalcCRC()
08025 {
08026     return CalcRegionCRC(0,UsedSlots-1);
08027 }
08028 
08029 
08030 /********************************************************************************************
08031 
08032 >   INT32 Path::CalcRegionCRC(const INT32 StartIndex, const INT32 EndIndex)
08033 
08034     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>/Mike
08035     Created:    11/11/94
08036     Inputs:     StartIndex = index into coord array to start calculating from
08037                 EndIndex   = index into coord array to end calculation (inclusive)
08038     Returns:    The CRC checksum for the region, relative to StartIndex
08039     Purpose:    Calculates a cyclic redundancy check value for a path region.
08040                 This CRC is only invariant under translation.
08041                 It can be used to determine whether a new path matches that of an old
08042                 path whose CRC we already know, whether the new path has been translated
08043                 or not.
08044 
08045 ********************************************************************************************/
08046 
08047 INT32 Path::CalcRegionCRC(const INT32 StartIndex, const INT32 EndIndex)
08048 {
08049     INT32 TotalCRC = 0;
08050     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
08051 
08052     for (INT32 i=StartIndex; i<(EndIndex+1); i++)
08053     {
08054         // Get the coords relative from the first coord
08055         // making this process transparent to translation
08056         // Add it in to the CRC total
08057         TotalCRC += (Coords[i].x-Coords[StartIndex].x) + 
08058                     (Coords[i].y-Coords[StartIndex].y);
08059     }
08060     return TotalCRC;
08061 }
08062 
08063 
08064 
08065 /********************************************************************************************
08066 
08067 >   INT32   Path::CalcSelectedCRC()
08068 
08069     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
08070     Created:    11/11/94
08071     Inputs:     -
08072     Returns:    The CRC checksum for the path, relative to the first coord
08073     Purpose:    Calculates a cyclic redundancy check value for a paths selected coordinates.
08074                 This CRC is invariant under translation only.
08075                 It can be used to determine whether a new path matches that of an old
08076                 path whose CRC we already know, whether the new path has been translated
08077                 or not.
08078 
08079 ********************************************************************************************/
08080 
08081 INT32   Path::CalcSelectedCRC()
08082 {
08083     return CalcSelPointsCRC(0,UsedSlots-1);
08084 }
08085 
08086 
08087 /********************************************************************************************
08088 
08089 >   INT32 Path::CalcSelPointsCRC(const INT32 StartIndex, const INT32 EndIndex)
08090 
08091     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>/Mike
08092     Created:    11/11/94
08093     Inputs:     StartIndex = index into coord array to start calculating from
08094                 EndIndex   = index into coord array to end calculation (inclusive)
08095     Returns:    The CRC checksum for the region, relative to StartIndex
08096     Purpose:    Calculates a cyclic redundancy check value for a paths selected coordinates.
08097                 This CRC is invariant under translation only.
08098                 It can be used to determine whether a new path matches that of an old
08099                 path whose CRC we already know, whether the new path has been translated
08100                 or not.
08101     
08102 ********************************************************************************************/
08103 
08104 INT32 Path::CalcSelPointsCRC(const INT32 StartIndex, const INT32 EndIndex)
08105 {
08106     INT32 TotalCRC = 0;
08107     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
08108     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
08109 
08110     for (INT32 i=StartIndex; i<(EndIndex+1); i++)
08111     {
08112         if (Flags[i].IsSelected)
08113             TotalCRC += (Coords[i].x-Coords[StartIndex].x) + 
08114                         (Coords[i].y-Coords[StartIndex].y);
08115     }
08116     return TotalCRC;
08117 }
08118 
08119 
08120 /********************************************************************************************
08121 
08122 >   void Path::Translate(const DocCoord& coord)
08123 
08124     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
08125     Created:    23/11/94
08126     Inputs:     x = x coordinate to translate path by
08127                 y = y coordinate to translate path by
08128     Returns:    
08129     Purpose:    Translate all coordinate within 'this' path.
08130 
08131 ********************************************************************************************/
08132 
08133 void Path::Translate(const DocCoord& coord)
08134 {
08135     INT32 x=coord.x;
08136     INT32 y=coord.y;
08137     Translate(x,y);
08138 }
08139 
08140 
08141 /********************************************************************************************
08142 
08143 >   void Path::Translate(const INT32 x, const INT32 y)
08144 
08145     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
08146     Created:    23/11/94
08147     Inputs:     x = x coordinate to translate path by
08148                 y = y coordinate to translate path by
08149     Returns:    
08150     Purpose:    Translate all coordinate within 'this' path.
08151 
08152 ********************************************************************************************/
08153 
08154 void Path::Translate(const INT32 x, const INT32 y)
08155 {
08156     // if there's any translation then apply it
08157     if (x || y)
08158     {
08159         DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
08160 
08161         for (INT32 i=0; i<UsedSlots; i++)
08162         {
08163             Coords[i].x += x;
08164             Coords[i].y += y;
08165         }
08166     }
08167 }
08168 
08169 
08170 
08171 /********************************************************************************************
08172 
08173 >   BOOL Path::InitExtraInfo(ChannelIndex Index)
08174 
08175     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08176     Created:    08/06/94
08177     Inputs:     The Index of the Channel to Initialise.
08178     Returns:    TRUE if we got all the memory we needed, FALSE if not
08179     Purpose:    Allocates memory in the path for the extra info arrays.
08180     Errors:     May error if it runs out of memory.
08181 
08182 ********************************************************************************************/
08183 
08184 BOOL Path::InitExtraInfo(ChannelIndex Index)
08185 {
08186     TRACEUSER( "Will", _T("InitExtraInfo, Index=%d\n"), Index );
08187     ENSURE(Index <= NUMEXTRACHANNELS, "Index out of range in InitExtraInfo");
08188                         
08189     // If we haven't any extra info yet then we need to create some
08190     if (ExtraInfo == NULL)
08191     {
08192         ExtraInfo =  new PathExtraInfo();
08193         if (ExtraInfo == NULL)
08194             return FALSE;
08195     }
08196 
08197     // Now we can initialise the channel.
08198     // We use the same initial slot size as was used for the main path data.
08199     return ExtraInfo->Init(Index, SlotInitSize);
08200 }
08201 
08202 /********************************************************************************************
08203 
08204 >   BOOL Path::AddExtraInfo(ChannelIndex Index, INT32 ExtraValue)
08205 
08206     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08207     Created:    08/06/94
08208     Inputs:     -
08209     Returns:    TRUE if all went well, FALSE if not
08210     Purpose:    Adds an extra Info Value at the current path position.
08211     Errors:     Can fail if it runs out of memory
08212 
08213 ********************************************************************************************/
08214 
08215 BOOL Path::AddExtraInfo(ChannelIndex Index, INT32 ExtraValue)
08216 {
08217     ENSURE(ExtraInfo != NULL, "ExtraInfo Pointer is NULL in AddExtraInfo");
08218     return ExtraInfo->Add(Index, ExtraValue);
08219 }
08220 
08221 /********************************************************************************************
08222 
08223 >   void Path::SyncExtraInfo()
08224 
08225     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08226     Created:    08/06/94
08227     Inputs:     -
08228     Returns:    -
08229     Purpose:    Sets the Extra Info Ptr to the current Path Position.
08230     Errors:     -
08231 
08232 ********************************************************************************************/
08233 
08234 void Path::SyncExtraInfo()
08235 {
08236     if (ExtraInfo == NULL)
08237         return;
08238 
08239     ExtraInfo->Sync(CurrentPos);
08240 }
08241 
08242 /********************************************************************************************
08243 
08244 >   PathWidth Path::GetWidth() const
08245 
08246     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08247     Created:    21/06/94
08248     Inputs:     -
08249     Returns:    The width of the path at the current path postion.
08250     Purpose:    Fetches width values from the path.
08251     Errors:     -
08252 
08253 ********************************************************************************************/
08254 
08255 PathWidth Path::GetWidth() const
08256 {
08257     if (ExtraInfo == NULL)
08258         return 0;
08259 
08260     return ExtraInfo->GetWidthInfo();   
08261 }
08262 
08263 /********************************************************************************************
08264 
08265 >   PathWidth* Path::GetWidthArray()
08266 
08267     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08268     Created:    21/06/94
08269     Returns:    A pointer to an array of Path Width Info.
08270     Purpose:    The array holds the width of all the points in the path.
08271                 DO NOT MODIFY THE CONTENTS OF THE ARRAY.
08272 
08273 ********************************************************************************************/
08274 
08275 PathWidth* Path::GetWidthArray()
08276 {
08277     if (ExtraInfo == NULL)
08278         return NULL;
08279 
08280     return ExtraInfo->GetWidthArray();  
08281 }
08282 
08283 /********************************************************************************************
08284 
08285 >   BOOL Path::HasWidth()
08286 
08287     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08288     Created:    21/06/94
08289     Returns:    TRUE if width information is available.
08290     Purpose:    To determine if the path has any width information.
08291 
08292 ********************************************************************************************/
08293 
08294 BOOL Path::HasWidth()
08295 {
08296     if (ExtraInfo == NULL)
08297         return FALSE;
08298 
08299     return ExtraInfo->HasWidthInfo();
08300 }
08301 
08302 /********************************************************************************************
08303 
08304 >   void Path::MapWidth(ChannelIndex Index)
08305 
08306     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08307     Created:    21/06/94
08308     Returns:    -
08309     Purpose:    Alters the mapping of the Width Information to one of the ExtraInfo channels.
08310 
08311 ********************************************************************************************/
08312 
08313 void Path::MapWidth(ChannelIndex Index)
08314 {
08315     ENSURE(Index <= NUMEXTRACHANNELS, "Index out of range in MapWidth()");
08316 
08317     ExtraInfo->MapWidthInfo(Index);
08318 }
08319 
08320 
08321 
08322 
08323 
08324 
08325 
08326 
08327 
08328 
08329 
08330 
08331 /********************************************************************************************
08332 
08333     PathExtraInfo
08334 
08335 ********************************************************************************************/
08336 
08337 /********************************************************************************************
08338 
08339 >   PathExtraInfo::PathExtraInfo()
08340 
08341     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08342     Created:    21/06/94
08343     Inputs:     -
08344     Returns:    -
08345     Purpose:    Constructor for PathExtraInfo class.
08346     Errors:     -
08347 
08348 ********************************************************************************************/
08349 
08350 PathExtraInfo::PathExtraInfo()
08351 {
08352     CurrentExtraPos = 0;
08353     WidthChannel = CI_PRESSURE;     // Default mapping of Pressure to Width
08354 
08355     INT32 i;
08356     for (i = 0; i < NUMEXTRACHANNELS; ++i)
08357         ExtraInfoHandles[i] = BAD_MHANDLE;      // Clear all the handles.
08358 
08359     for (i = 0; i < NUMEXTRACHANNELS; ++i)
08360         Scaling[i] = 1;                         // Clear all the scaling values.
08361 
08362 // WEBSTER - markn 25/4/97
08363 // No pen stuff required in Webster
08364 // Taken out by vector stroking code Neville 2/10/97
08365 #ifdef VECTOR_STROKING
08366     // Set the Pressure scaling so that the Maximum pressure is always stored as EXTRAVALUEMAX
08367     //if ((Camelot.GetPressurePen())->IsPressureOn())
08368     //  Scaling[CI_PRESSURE] = fixed16((INT32)EXTRAVALUEMAX/((INT32)(Camelot.GetPressurePen())->GetPressureMax()));
08369 #endif // VECTOR_STROKING
08370 }
08371 
08372 /********************************************************************************************
08373 
08374 >   PathExtraInfo::~PathExtraInfo()
08375 
08376     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08377     Created:    21/06/94
08378     Inputs:     -
08379     Returns:    -
08380     Purpose:    Destructor for PathExtraInfo class.
08381     Errors:     -
08382 
08383 ********************************************************************************************/
08384 
08385 PathExtraInfo::~PathExtraInfo()
08386 {
08387     // Scan thru all the extra channels that we know of, and release and blocks
08388     // that we have claimed.
08389     for (INT32 Index = 0; Index < NUMEXTRACHANNELS; ++Index)
08390     {
08391         // Have we claimed a block for this channel ?
08392         if (ExtraInfoHandles[Index] != BAD_MHANDLE)
08393         {
08394             ReleaseBlock(ExtraInfoHandles[Index]);
08395             TRACEUSER( "Will", _T("Released Block with Index=%d\n"),Index);
08396         }
08397     }
08398 }
08399 
08400 /********************************************************************************************
08401 
08402 >   BOOL PathExtraInfo::Init(ChannelIndex Index, INT32 SlotInitSize)
08403 
08404     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08405     Created:    08/06/94
08406     Inputs:     The Index of the channel to Init, and the initial size for it.
08407     Returns:    TRUE if we got all the memory we needed, FALSE if not
08408     Purpose:    Allocates memory in the path for the extra info arrays.
08409     Errors:     Can fail if it runs out of memory
08410 
08411 ********************************************************************************************/
08412 
08413 BOOL PathExtraInfo::Init(ChannelIndex Index, INT32 SlotInitSize)
08414 {
08415     TRACEUSER( "Will", _T("InitExtraInfo, Index=%d\n"), Index );
08416     ENSURE(Index <= NUMEXTRACHANNELS, "Index out of range in InitExtraInfo");
08417                         
08418     if (ExtraInfoHandles[Index] != BAD_MHANDLE)
08419         return TRUE;                                // Already Initialised
08420 
08421     // Claim an initial block of memory for this Index.
08422     ExtraInfoHandles[Index] = ClaimBlock(sizeof(PathExtraElement)*SlotInitSize);
08423 
08424     // Check that the block claimed ok.
08425     return (ExtraInfoHandles[Index] != BAD_MHANDLE);
08426 }
08427 
08428 /********************************************************************************************
08429 
08430 >   BOOL PathExtraInfo::Add(ChannelIndex Index, INT32 ExtraValue)
08431 
08432     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08433     Created:    08/06/94
08434     Inputs:     The channel to add to, and the value to add.
08435     Returns:    TRUE if all went well, FALSE if not
08436     Purpose:    Adds an extra Info Value at the current path position.
08437                 The value stored will be scaled so that it is between 0 and 65536.
08438     Errors:     -
08439 
08440 ********************************************************************************************/
08441 
08442 BOOL PathExtraInfo::Add(ChannelIndex Index, INT32 ExtraValue)
08443 {
08444     TRACEUSER( "Will", _T("AddExtraInfo, Index=%d, Value=%d\n"),Index, ExtraValue);
08445     ENSURE(Index <= NUMEXTRACHANNELS, "Index out of range in PathExtraInfo::Add()");
08446 
08447     // get pointer to info array
08448     PathExtraElement* Info = (PathExtraElement*) DescribeHandle(ExtraInfoHandles[Index]);
08449 
08450     // Wop the info in the array at the postion coresponding to the last point inserted.
08451     Info[CurrentExtraPos] = XLONG(ExtraValue) * Scaling[Index];
08452 
08453     return TRUE;
08454 }
08455 
08456 /********************************************************************************************
08457 
08458 >   void PathExtraInfo::Sync(INT32 CurrentPos)
08459 
08460     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08461     Created:    08/06/94
08462     Inputs:     The path position to set the extrainfo ptr to.
08463     Returns:    -
08464     Purpose:    Sets the Extra Info Ptr to the current Path Postion.
08465     Errors:     -
08466 
08467 ********************************************************************************************/
08468 
08469 void PathExtraInfo::Sync(INT32 CurrentPos)
08470 {
08471     TRACEUSER( "Will", _T("SyncExtraInfo, Pos=%d\n"),CurrentPos);
08472     CurrentExtraPos = CurrentPos;
08473 }
08474 
08475 /********************************************************************************************
08476 
08477 >   BOOL PathExtraInfo::IncreaseExtraBlocks(INT32 SlotsNeeded)
08478 
08479     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08480     Created:    08/06/94
08481     Returns:    TRUE is the Blocks were increase correctly.
08482     Purpose:    Allocates more memory for extra info.
08483 
08484 ********************************************************************************************/
08485 
08486 BOOL PathExtraInfo::IncreaseExtraBlocks(INT32 SlotsNeeded)
08487 {
08488     TRACEUSER( "Will", _T("IncreaseExtraBlocks, Slots=%d\n"),SlotsNeeded);
08489 
08490     // Scan thru all our channels and increase their size by 'SlotsNeeded'.
08491     for (INT32 Index = 0; Index < NUMEXTRACHANNELS; ++Index)
08492     {
08493         if (ExtraInfoHandles[Index] != BAD_MHANDLE)
08494             if (!IncreaseBlock(ExtraInfoHandles[Index], sizeof(PathExtraElement)*SlotsNeeded))
08495                 return FALSE;
08496     }
08497     return TRUE;
08498 }
08499 
08500 /********************************************************************************************
08501 
08502 >   BOOL PathExtraInfo::DecreaseExtraBlocks(INT32 SlotsLost)
08503 
08504     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08505     Created:    08/06/94
08506     Returns:    TRUE is the Blocks were increase correctly.
08507     Purpose:    De-allocates memory used for extra info.
08508 
08509 ********************************************************************************************/
08510 
08511 BOOL PathExtraInfo::DecreaseExtraBlocks(INT32 SlotsLost)
08512 {
08513     TRACEUSER( "Will", _T("DecreaseExtraBlocks, Slots=%d\n"),SlotsLost);
08514 
08515     // Scan thru all our channels and decrease their size by 'SlotsNeeded'.
08516     for (INT32 Index = 0; Index < NUMEXTRACHANNELS; ++Index)
08517     {
08518         if (ExtraInfoHandles[Index] != BAD_MHANDLE)
08519             if (!DecreaseBlock(ExtraInfoHandles[Index], sizeof(PathExtraElement)*SlotsLost))
08520                 return FALSE;
08521     }
08522     return TRUE;
08523 }
08524 
08525 /********************************************************************************************
08526 
08527 >   void PathExtraInfo::ShiftUpExtraInfo(INT32 CurrentPos, INT32 NumSlots, INT32 SlotsToMove)
08528 
08529     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08530     Created:    08/06/94
08531     Returns:    -
08532     Purpose:    Shifts up the ExtraInfo memory to make room for more.
08533 
08534 ********************************************************************************************/
08535 
08536 void PathExtraInfo::ShiftUpExtraInfo(INT32 CurrentPos, INT32 NumSlots, INT32 SlotsToMove)
08537 {
08538     TRACEUSER( "Will", _T("ShiftUpExtraInfo, NumSlots=%d, SlotsToMove=%d\n"),NumSlots, SlotsToMove);
08539 
08540     // The channel data has been resized and so we need to shift it around a bit.
08541     for (INT32 Index = 0; Index < NUMEXTRACHANNELS; ++Index)
08542     {
08543         if (ExtraInfoHandles[Index] != BAD_MHANDLE)
08544         {
08545             PathExtraElement* ExtraInfoPtr = (PathExtraElement*) DescribeHandle(ExtraInfoHandles[Index]);
08546             memmove( (void*)&ExtraInfoPtr[CurrentPos+NumSlots], (void*)&ExtraInfoPtr[CurrentPos], SlotsToMove*sizeof(PathExtraElement) );
08547         }
08548     }
08549 
08550 }
08551 
08552 /********************************************************************************************
08553 
08554 >   void PathExtraInfo::ShiftDownExtraInfo(INT32 StartSlot, INT32 NumSlots, INT32 SlotsToMove)
08555 
08556     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08557     Created:    08/06/94
08558     Returns:    -
08559     Purpose:    Shifts down the ExtraInfo memory when it gets shrunk.
08560 
08561 ********************************************************************************************/
08562 
08563 void PathExtraInfo::ShiftDownExtraInfo(INT32 StartSlot, INT32 NumSlots, INT32 SlotsToMove)
08564 {
08565     TRACEUSER( "Will", _T("ShiftDownExtraInfo, Start=%d, NumSlots=%d, SlotsToMove=%d\n"),
08566             StartSlot, NumSlots, SlotsToMove);
08567 
08568     // The channel data has been resized and so we need to shift it around a bit.
08569     for (INT32 Index = 0; Index < NUMEXTRACHANNELS; ++Index)
08570     {
08571         if (ExtraInfoHandles[Index] != BAD_MHANDLE)
08572         {
08573             PathExtraElement* ExtraInfoPtr = (PathExtraElement*) DescribeHandle(ExtraInfoHandles[Index]);
08574             memmove( (void*)&ExtraInfoPtr[StartSlot], (void*)&ExtraInfoPtr[StartSlot+NumSlots], SlotsToMove*sizeof(PathExtraElement) );
08575         }
08576     }
08577 
08578 }
08579 
08580 /********************************************************************************************
08581 
08582 >   void PathExtraInfo::CopyExtraInfo(PathExtraInfo* SrcInfo)
08583 
08584     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08585     Created:    08/06/94
08586     Returns:    -
08587     Purpose:    Copies all the ExtraInfo from another path.
08588 
08589 ********************************************************************************************/
08590 
08591 void PathExtraInfo::CopyExtraInfo(PathExtraInfo* SrcInfo)
08592 {
08593     ENSURE(FALSE, "Warning. Path Extra Info lost in CopyExtraInfo()");
08594 }
08595 
08596 // Extra Info Mapping Functions
08597 
08598 /********************************************************************************************
08599 
08600 >   void PathExtraInfo::MapWidthInfo(ChannelIndex Index)
08601 
08602     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08603     Created:    21/06/94
08604     Returns:    -
08605     Purpose:    Alters the mapping of the Width Information to one of the ExtraInfo channels.
08606 
08607 ********************************************************************************************/
08608 
08609 void PathExtraInfo::MapWidthInfo(ChannelIndex Index)
08610 {
08611     ENSURE(Index <= NUMEXTRACHANNELS, "Index out of range in MapWidthInfo()");
08612     WidthChannel = Index;
08613 }
08614 
08615 /********************************************************************************************
08616 
08617 >   PathWidth PathExtraInfo::GetWidthInfo()
08618 
08619     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08620     Created:    21/06/94
08621     Returns:    -
08622     Purpose:    Get the width at the current path position.
08623 
08624 ********************************************************************************************/
08625 
08626 PathWidth PathExtraInfo::GetWidthInfo()
08627 {
08628     // de-reference the Extra Info array
08629     PathExtraElement* WidthInfo = (PathExtraElement*) DescribeHandle(ExtraInfoHandles[WidthChannel]);
08630 
08631     return (PathWidth)WidthInfo[CurrentExtraPos];   
08632 }
08633 
08634 /********************************************************************************************
08635 
08636 >   PathWidth* PathExtraInfo::GetWidthArray()
08637 
08638     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08639     Created:    21/06/94
08640     Returns:    A pointer to an array of Path Width Information
08641     Purpose:    The array holds the width of the path.
08642 
08643 ********************************************************************************************/
08644 
08645 PathWidth* PathExtraInfo::GetWidthArray()
08646 {
08647     // de-reference an Extra Info array
08648     return (PathWidth*) DescribeHandle(ExtraInfoHandles[WidthChannel]);
08649 }
08650 
08651 /********************************************************************************************
08652 
08653 >   BOOL PathExtraInfo::HasWidthInfo()
08654 
08655     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
08656     Created:    21/06/94
08657     Returns:    TRUE if width information is available.
08658     Purpose:    To determine if the path has any width information.
08659 
08660 ********************************************************************************************/
08661 
08662 BOOL PathExtraInfo::HasWidthInfo()
08663 {
08664     return (WidthChannel!=-1);
08665 }
08666 
08667 /********************************************************************************************
08668 
08669 >   BOOL Path::CheckPathValid()
08670 
08671     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
08672     Created:    11/7/94
08673     Returns:    TRUE if the path was OK, FALSE if there was something wrong with it
08674     Purpose:    Tests a path to see if it is valid. This is only built into DEBUG versions
08675                 of Camelot so always surround calls to it with an ifdef _DEBUG block.
08676                 If the path is not valid in some way, this function will dump an explanation
08677                 to the debug terminal and return FALSE.
08678 
08679 ********************************************************************************************/
08680 
08681 #ifdef _DEBUG
08682 
08683 BOOL Path::CheckPathValid()
08684 {
08685     BOOL IsValid = TRUE;
08686 
08687     // Go through the path making sure that it is a valid path
08688     PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
08689 //  DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
08690 //  PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
08691 
08692     // Make sure there is at least a single lineto in the path
08693     if (UsedSlots<2)
08694     {
08695         TRACE( _T("Path too short! It had less than 2 components (ie one or none!)"));
08696         IsValid = FALSE;
08697     }
08698 
08699     // Make sure that the path does not end in a MoveTo
08700     if (Verbs[UsedSlots-1]==PT_MOVETO)
08701     {
08702         TRACE( _T("The last element of the path was a MoveTo. This is bad..."));
08703         IsValid = FALSE;
08704     }
08705 
08706     // make sure that the first element in the path is a MoveTo
08707     if (Verbs[0]!=PT_MOVETO)
08708     {
08709         TRACE( _T("First element of the path is NOT a MoveTo. This is really bad"));
08710         IsValid = FALSE;
08711     }
08712 
08713     INT32 CurveCount = 0;
08714     for (INT32 i=0; i<UsedSlots; i++)
08715     {
08716         // make sure that CurveTos come in groups of 3
08717         if ((Verbs[i] & ~PT_CLOSEFIGURE)!=PT_BEZIERTO)
08718         {
08719             if ((CurveCount!=0) && ((CurveCount%3)!=0))
08720             {
08721                 TRACE( _T("CurveTo elements were not a multiple of 3 (%d)"), CurveCount);
08722                 IsValid = FALSE;
08723             }
08724 
08725             CurveCount = 0;
08726         }
08727         else
08728             CurveCount++;
08729 
08730         // see if there is a MoveTo after a close figure
08731         if ((Verbs[i] & PT_CLOSEFIGURE) && (i<UsedSlots-1) && (Verbs[i+1]!=PT_MOVETO))
08732         {
08733             TRACE( _T("A Close Figure was not followed by the end of the path or a MoveTo"));
08734             IsValid = FALSE;
08735         }
08736     }
08737 
08738     if ((CurveCount!=0) && ((CurveCount%3)!=0))
08739     {
08740         TRACE( _T("CurveTo elements were not a multiple of 3 (%d)"), CurveCount);
08741         IsValid = FALSE;
08742     }
08743 
08744     if (IsValid == FALSE)
08745         // Put a break point here to trap all the invalid paths
08746         TRACE( _T("This path has failed the Validity Check"));
08747 
08748     // we passed all the tests, so return TRUE
08749     return IsValid;
08750 }
08751 
08752 #endif
08753 
08754 /********************************************************************************************
08755 
08756 >   INT32 Path::BreakInTwo(Path* pChildPath)
08757 
08758     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
08759     Created:    30/8/94
08760     Inputs:     pChildPath, a pointer to a split path which should have been passed to new()
08761     Outputs:    -
08762     Returns:   -1       If unable to claim enough memory to split the path.
08763                         Note pChildPath is unchanged and 'this' is also unaffected
08764                 0       If no selected points were found in the path.
08765                 1       Then the current path is opened at the first selected point.
08766                         pChildPath is unaffected.
08767                 2       Then the current path now contains the left child data of the
08768                         split and pChildPath points at a new path object containing
08769                         the right child data. 
08770                                 
08771     Purpose:    This function will attempt to break the current path at its first
08772                 selected point.
08773                 All selected points should still be in place in both parent and child.
08774     Errors:     -
08775     SeeAlso:    -
08776 
08777 ********************************************************************************************/
08778 
08779 
08780 INT32 Path::BreakInTwo(Path* pChildPath)
08781 {
08782     // Get arrays of flags,verbs and points.
08783 
08784     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
08785     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
08786     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
08787 
08788     // p,q indexes to selected point and endof(sub)path
08789     INT32 p = -1;
08790     INT32 q = -1;
08791     INT32 s = 0;
08792 
08793     // vp,vq local verb holders
08794     PathVerb vp;
08795     PathVerb vq;
08796 
08797     while (p++, FindNextSelected(&p))
08798     {
08799         q = p;
08800 
08801         FindEndElOfSubPath(&q);                         // returns index of last element in this subpath/path
08802         if (p>q) return 0;                              // just a quick check on sanity.
08803 
08804         s = p;
08805         FindStartOfSubPath(&s);                         // grab the start of the subpath
08806 
08807         if (p==q)
08808         {
08809             if (!(Verbs[p] & PT_CLOSEFIGURE)) continue; // ignore selected points at end of open subpaths
08810             p=s;
08811         }   
08812 
08813         vp = (Verbs[p] & ~PT_CLOSEFIGURE);
08814         vq = (Verbs[q]);
08815 
08816 
08817         // Have we found a selected start point?
08818         if (vp == PT_MOVETO)
08819         {
08820             if (vq & PT_CLOSEFIGURE)
08821             {
08822                 // subpath figure is closed, so open the path and resolve flags
08823                 Verbs[q] &= ~PT_CLOSEFIGURE;
08824                 if (Flags[q].IsSelected)
08825                 {
08826                     Flags[q].IsSelected = FALSE;
08827                     Flags[q].IsSmooth = FALSE;
08828                     if (Verbs[q] == PT_BEZIERTO)
08829                     {
08830                         Flags[q-1].IsSelected = FALSE;
08831                         Flags[q-1].IsSmooth = FALSE;
08832                     }
08833                     if (Verbs[p+1] == PT_BEZIERTO)
08834                         Flags[p+1].IsSmooth = FALSE;
08835                 }
08836                 return 1;
08837             }
08838             continue;
08839         }
08840 
08841         // Note these elements should not need to be checked against PT_CLOSEFIGURE as
08842         // (p!=q) and q is the following close element.
08843         
08844         if ((vp == PT_LINETO) || (vp == PT_BEZIERTO))
08845         {
08846 
08847             // ok, we have a selected element on a lineto/curveto. We need to check
08848             // whether we are in a closed subpath or an open one
08849 
08850             if ( vq & PT_CLOSEFIGURE )                  // is this sub path closed?
08851             {
08852 
08853                 // We need to reorder the subpath points and open the path.
08854                 // This involves
08855                 // (1) insert moveto(p) at p+1
08856                 // (2) increment q
08857                 // (3) remove closefigure at q
08858                 // (4) replace moveto(s) by lineto(s)
08859                 // (5) rotate the whole path left/right until element p
08860                 //     lies at element s
08861 
08862                 // subpath figure is closed, so open the path in parent
08863 
08864                 if ((Coords[s].x == Coords[q].x) && (Coords[s].y == Coords[q].y))
08865                 {
08866 
08867                     // I'm not totally convinced about this but here goes nothing!
08868                     // When dealing with a chain of bezier control points, the previous 
08869                     // and following points are selected also. What we select and deselect
08870                     // depends on which point (the start or end) we want selected when the
08871                     // path is opened. I thought it would be sensible to leave the end point
08872                     // selected but the click logic then does not allow you to drag the damn
08873                     // point around! as it still lies on top of the deselected start point.
08874                     // So lets try to select the new moveto and see what happens.
08875 
08876                     Flags[p].IsSelected = FALSE;            // deselect the current control point
08877                     Flags[p].IsSmooth = FALSE;
08878                     if (vp == PT_BEZIERTO)
08879                     {
08880                         Flags[p-1].IsSelected = FALSE;
08881                         Flags[p-1].IsSmooth = FALSE;
08882                     }
08883 
08884                     CurrentPos = s;
08885                     DeleteElement();                        // Get rid of the original moveto
08886                     CurrentPos = p;
08887                     InsertMoveTo(Coords[p-1]);
08888 
08889                     Flags = (PathFlags*) DescribeHandle(FlagsHandle);
08890                     Verbs = (PathVerb*) DescribeHandle(VerbHandle);
08891 
08892                     Flags[p].IsSelected = TRUE;             // make sure the moveto is selected
08893                     Flags[p].IsSmooth = FALSE;              // but not smooth
08894 
08895                     if ((Verbs[p+1] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
08896                         Flags[p+1].IsSmooth = FALSE;
08897 
08898                     Verbs[q] &= ~PT_CLOSEFIGURE;            // open the subpath.
08899                     RotateElementsLeft(s,q,p-s);            // rotate elements 
08900                     return 1;
08901                 }
08902                 else
08903                 {
08904 
08905                     // this section definitely needs more work!
08906 
08907                     CurrentPos = p+1;
08908                     if (!InsertMoveTo(Coords[p]))
08909                         return -1;
08910                 
08911                     q++;
08912 
08913                     Verbs = (PathVerb*) DescribeHandle(VerbHandle);
08914                     Verbs[q] &= ~PT_CLOSEFIGURE;            // open the subpath.
08915                     Verbs[s] = PT_LINETO;                   // alter move to line
08916                     RotateElementsLeft(s,q,p-s+1);          // rotate elements 
08917                     return 1;
08918                 }
08919             }
08920             else
08921             {
08922                 // I need to alter the current path object and create a completely new
08923                 // path object in the tree. This will contain the split section of the
08924                 // parent path. This involves
08925 
08926                 // (1) create newpath(p => q) by
08927                 //   (1.1) creating enough space for elements (p-q + 1)
08928                 //   (1.2) copying elements in
08929                 //   (1.3) replacing verb(p) by moveto
08930                 // (2) removal of elements (p+1 => q)
08931 
08932                 // Initialise the path with enough space for splinter
08933 
08934                 q = UsedSlots-1;
08935 
08936                 if (!pChildPath->Initialise(q-p+1,12))  
08937                     return -1;
08938 
08939                 // Copy the section out of the current path into the child  
08940                 if (!(CopySectionTo(pChildPath, p, q-p+1)))
08941                     return -1;
08942 
08943                 // Alter the childs first element once split, to a moveto.
08944                 PathVerb* pChildVerbs = (PathVerb*) DescribeHandle(pChildPath->VerbHandle);
08945                 PathFlags* pChildFlags = (PathFlags*) DescribeHandle(pChildPath->FlagsHandle);
08946                 pChildVerbs[0] = PT_MOVETO;
08947 
08948                 // Make sure the child paths first element is no longer smooth
08949                 pChildFlags[0].IsSmooth = FALSE;
08950                 if (pChildVerbs[1] == PT_BEZIERTO)
08951                     pChildFlags[1].IsSmooth = FALSE;
08952 
08953                 // Make sure the parent subpath start is deselected. It may have been selected
08954                 // by the previous instructions from a previous iteration.
08955                 Flags[s].IsSelected = FALSE;
08956                 if (Verbs[s] == PT_BEZIERTO)
08957                     Flags[s+1].IsSelected = FALSE;
08958 
08959                 // Remove the parents end point smooth bit, but leave it selected
08960                 Flags[p].IsSmooth = FALSE;
08961                 if (vp == PT_BEZIERTO)
08962                     Flags[p-1].IsSmooth = FALSE;
08963 
08964                 // ok remove the necessary elements from the original path
08965                 DeleteFromElement(p+1);
08966 
08967                 return 2;
08968             }
08969         }
08970     }
08971     return 0;
08972 }
08973 
08974 
08975 
08976 
08977 
08978 /********************************************************************************************
08979 
08980 >   void Path::RotateElementsLeft(INT32* Start, INT32 End, INT32 Rotate)
08981 
08982     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
08983     Created:    04/09/94
08984     Inputs:     Start   = The index of the first element in the component list
08985                 End     = The index of the last element in the component list
08986                 Rotate  = The number of positions to rotate left by.
08987                           This may be negative, in which case the path data will
08988                           be rotated right by the specified amount.
08989     Outputs:    -
08990     Returns:    -
08991     Purpose:    Rotates the specified elements in a path by the number of positions
08992                 indicated. Elements will be rotated from right to left.
08993         
08994     
08995 ********************************************************************************************/
08996 
08997 void Path::RotateElementsLeft(const INT32 Start, const INT32 End, INT32 Rotate)
08998 {
08999     // just make sure the start and end positions are sensible
09000 
09001     INT32 Width = End-Start+1;
09002     if (Width<2) return;
09003     Rotate = Rotate % Width;
09004     if (Rotate==0) return;
09005     if (Start<0) return;
09006     if (End>=UsedSlots) return;
09007 
09008     // make a rotate right into a rotate left
09009     if (Rotate<0) Rotate=Width-Rotate;
09010 
09011     INT32 Shift = 64;
09012     INT32 Step = Rotate/Shift;
09013     INT32 Residual = Rotate % Shift;
09014 
09015     UINT32 Size = sizeof(PathVerb);
09016     if (Size<sizeof(PathFlags)) Size=sizeof(PathFlags);
09017     if (Size<sizeof(DocCoord)) Size=sizeof(DocCoord);
09018 
09019     void* TempBuff = (void*)CCMalloc(Shift*Size);
09020 
09021     // Get arrays of flags,verbs and points.
09022     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
09023     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
09024     DocCoord* Coords = (DocCoord*) DescribeHandle(CoordHandle);
09025 
09026     if (TempBuff!=NULL)
09027     {
09028         void* Destin;
09029         void* Source;
09030         while (Step>0)
09031         {
09032             Destin = TempBuff;
09033             Source = (void*)&Flags[Start];
09034             memmove(Destin, Source, Shift*sizeof(PathFlags));
09035             Destin = (void*)&Flags[Start];
09036             Source = (void*)&Flags[Start+Shift];
09037             memmove(Destin, Source, (Width-Shift)*sizeof(PathFlags));
09038             Destin = (void*)&Flags[End-Shift+1];
09039             Source = TempBuff;
09040             memmove(Destin, Source, Shift*sizeof(PathFlags));
09041 
09042             Destin = TempBuff;
09043             Source = (void*)&Verbs[Start];
09044             memmove(Destin, Source, Shift*sizeof(PathVerb));
09045             Destin = (void*)&Verbs[Start];
09046             Source = (void*)&Verbs[Start+Shift];
09047             memmove(Destin, Source, (Width-Shift)*sizeof(PathVerb));
09048             Destin = (void*)&Verbs[End-Shift+1];
09049             Source = TempBuff;
09050             memmove(Destin, Source, Shift*sizeof(PathVerb));
09051 
09052             Destin = TempBuff;
09053             Source = (void*)&Coords[Start];
09054             memmove(Destin, Source, Shift*sizeof(DocCoord));
09055             Destin = (void*)&Coords[Start];
09056             Source = (void*)&Coords[Start+Shift];
09057             memmove(Destin, Source, (Width-Shift)*sizeof(DocCoord));
09058             Destin = (void*)&Coords[End-Shift+1];
09059             Source = TempBuff;
09060             memmove(Destin, Source, Shift*sizeof(DocCoord));
09061 
09062             Step--;
09063         }
09064         if (Residual>0)
09065         {
09066 
09067             Destin = TempBuff;
09068             Source = (void*)&Flags[Start];
09069             memmove(Destin, Source, Residual*sizeof(PathFlags));
09070             Destin = (void*)&Flags[Start];
09071             Source = (void*)&Flags[Start+Residual];
09072             memmove(Destin, Source, (Width-Residual)*sizeof(PathFlags));
09073             Destin = (void*)&Flags[End-Residual+1];
09074             Source = TempBuff;
09075             memmove(Destin, Source, Residual*sizeof(PathFlags));
09076 
09077             Destin = TempBuff;
09078             Source = (void*)&Verbs[Start];
09079             memmove(Destin, Source, Residual*sizeof(PathVerb));
09080             Destin = (void*)&Verbs[Start];
09081             Source = (void*)&Verbs[Start+Residual];
09082             memmove(Destin, Source, (Width-Residual)*sizeof(PathVerb));
09083             Destin = (void*)&Verbs[End-Residual+1];
09084             Source = TempBuff;
09085             memmove(Destin, Source, Residual*sizeof(PathVerb));
09086 
09087             Destin = TempBuff;
09088             Source = (void*)&Coords[Start];
09089             memmove(Destin, Source, Residual*sizeof(DocCoord));
09090             Destin = (void*)&Coords[Start];
09091             Source = (void*)&Coords[Start+Residual];
09092             memmove(Destin, Source, (Width-Residual)*sizeof(DocCoord));
09093             Destin = (void*)&Coords[End-Residual+1];
09094             Source = TempBuff;
09095             memmove(Destin, Source, Residual*sizeof(DocCoord));
09096 
09097         }
09098         CCFree(TempBuff);
09099     }
09100     else
09101     {
09102         PathFlags TempFlags;
09103         PathVerb TempVerb;
09104         DocCoord TempCoord;
09105 
09106         while (Rotate>0)
09107         {
09108             TempFlags = Flags[Start];
09109             TempVerb = Verbs[Start];
09110             TempCoord = Coords[Start];
09111             for (INT32 i=Start; i<End; i++)
09112             {
09113                 Flags[i] = Flags[i+1];
09114                 Verbs[i] = Verbs[i+1];
09115                 Coords[i] = Coords[i+1];
09116             }
09117             Flags[End]=TempFlags;
09118             Verbs[End]=TempVerb;
09119             Coords[End]=TempCoord;
09120             Rotate--;
09121         }
09122     }
09123 }
09124 
09125 /********************************************************************************************
09126 
09127 >   BOOL Path::ChangeStartElement(INT32 StartIndex)
09128 
09129     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
09130     Created:    8/11/94
09131     Inputs:     StartIndex - The index to the element in the path that you want to be the start
09132     Outputs:    -
09133     Returns:    TRUE if OK, FALSE otherwise
09134     Purpose:    This will rotate the path so that the element that was at StartIndex is now at
09135                 index 0.
09136                 You should only call this for closed paths that don't contain subpaths.
09137     Errors:     In debug builds, an error is given if the last coord != first coord
09138     
09139 ********************************************************************************************/
09140 
09141 BOOL Path::ChangeStartElement(INT32 StartIndex)
09142 {
09143     if (StartIndex < 1 || StartIndex >= UsedSlots)
09144     {
09145         ERROR3_PF(("StartIndex out of range : %ld",StartIndex));
09146         return FALSE;
09147     }
09148 
09149     DocCoord*  pCoords = GetCoordArray();
09150     PathVerb*  pVerbs  = GetVerbArray();
09151     PathFlags* pFlags  = GetFlagArray();
09152 
09153     ERROR3IF(pCoords[0] != pCoords[UsedSlots-1],"Not closed because first coord != last coord");
09154 
09155     // Make sure the last verb has the PT_CLOSEFIGURE flag cleared
09156     pVerbs[UsedSlots-1] = pVerbs[UsedSlots-1] & ~PT_CLOSEFIGURE;
09157 
09158     // Delete the MoveTo
09159     CurrentPos = 0;
09160     DeleteElement();
09161 
09162     ERROR3IF(UnUsedSlots < 1,"DeleteElement() didn't leave me any unused slots");
09163     if (UnUsedSlots < 1) return FALSE;
09164 
09165     // Rotate the element at StartIndex to index 0
09166     RotateElementsLeft(0,UsedSlots-1,StartIndex-1);
09167 
09168     // We need a duplicate element type & verb to finish the path off, so copy on end of arrays
09169     pCoords[UsedSlots] = pCoords[0];
09170     pVerbs [UsedSlots] = pVerbs [0] | PT_CLOSEFIGURE;   // Make sure last verb has also got this flag set
09171     pFlags [UsedSlots] = pFlags [0];
09172 
09173     // The first element is always a MoveTo
09174     pVerbs[0] = PT_MOVETO;
09175 
09176     // Update the path vars.
09177     UsedSlots++;
09178     UnUsedSlots--;
09179 
09180     return TRUE;
09181 }
09182 
09183 
09184 /********************************************************************************************
09185 
09186 >   BOOL Path::FindNextSelected(INT32* Index)
09187 
09188     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
09189     Created:    01/09/94
09190     Inputs:     Index - The position in the path to start looking from
09191     Outputs:    Index - the position of the next selected element 
09192     Returns:    TRUE    if there exists a selected point at or beyond index
09193                 FALSE   if there is no more selected points along the path
09194     Purpose:    Searches the point set {*index, usedslots-1} for a selected end point
09195                 and returns its index if found.  
09196     
09197 ********************************************************************************************/
09198 
09199 BOOL Path::FindNextSelected(INT32* Index)
09200 {
09201     ENSURE( (*Index)<=UsedSlots, "Path position was not valid in FindNextSelected" );
09202     ENSURE( (*Index)>=0, "Path Position less than zero in FindNextSelected" );
09203 
09204     PathFlags* Flags = (PathFlags*) DescribeHandle(FlagsHandle);
09205 
09206     while (   
09207             ((*Index)<UsedSlots) && 
09208             !(  (Flags[(*Index)].IsSelected) && 
09209                 (Flags[*Index].IsEndPoint)
09210              )
09211           )
09212             (*Index)++;
09213     
09214     return ((*Index)<UsedSlots);
09215     
09216 }
09217     
09218 /********************************************************************************************
09219 
09220 >   void Path::FindEndElOfSubPath(INT32* Index) const
09221 
09222     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
09223     Created:    12/09/94
09224     Inputs:     Index - The position in the path to start looking from
09225     Outputs:    Index - the position of the last element of the subpath, ie the final bezier
09226                         control point, or the final line to etc.
09227     Purpose:    Searches forwards until it finds the last element of the current sub-path.
09228                 ie
09229                   0  1  2  3
09230                 {MT,CT,CT,CT} => Index = 3.
09231     See Also:   FindEndOfSubPath (Note equivelent)
09232 
09233 ********************************************************************************************/
09234 
09235 void Path::FindEndElOfSubPath(INT32* Index) const
09236 {
09237     ENSURE( (*Index)<UsedSlots, "Path position was not valid in FindEndOfSubPath" );
09238     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
09239 
09240     do
09241     {
09242         (*Index)++;
09243     } while (((*Index)<UsedSlots) && (Verbs[(*Index)]!=PT_MOVETO));
09244 
09245     (*Index)--;
09246 }
09247 
09248 
09249 
09250 /********************************************************************************************
09251 
09252 > INT32 Path::NumSplinters()
09253 
09254     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> (& Markn)
09255     Created:    4/9/94
09256     Inputs:     
09257     Outputs:    -
09258     Returns:    -
09259     Purpose:    Returns the number of splinters a selected path will be broken into
09260                 when calls to BreakPath is called. Splinters include opened subpaths,
09261                 ie if the algorithm will open a closed subpath, this is classed as a
09262                 spinter along with breaking a path in two.
09263     Errors:     -
09264     SeeAlso:    -
09265 
09266 ********************************************************************************************/
09267 
09268 INT32 Path::NumSplinters()
09269 {
09270     // New implementation by Markn 15/2/95
09271     // This version will return 2 when there's a selected point in the middle of a line.  The old code
09272     // returned 1 in this case
09273 
09274     INT32 Index = -1;
09275     INT32 EndIndex = -1;
09276     INT32 LastEndIndex = -1;
09277     INT32 NumSplinters = 0;
09278     BOOL SubPathOpen;
09279 
09280     PathVerb* pVerbs = GetVerbArray();
09281 
09282     while (Index++, FindNextSelected(&Index))
09283     {
09284         PathVerb Verb = pVerbs[Index] & ~PT_CLOSEFIGURE;
09285 
09286         // Ignore selected moveto elements
09287         if (Verb == PT_MOVETO)
09288             continue;
09289 
09290         EndIndex = Index;
09291         FindEndElOfSubPath(&EndIndex);  // returns index of last element in this subpath/path
09292         SubPathOpen = !(pVerbs[EndIndex] & PT_CLOSEFIGURE);
09293 
09294         // ignore selected end points when the subpath is open
09295         if (Index == EndIndex && SubPathOpen)
09296             continue;
09297 
09298         NumSplinters++;
09299 
09300         if (EndIndex != LastEndIndex)
09301         {
09302             LastEndIndex = EndIndex;
09303 
09304             // if we have found a selected point in the middle of an open subpath
09305             // an extra splinter will be produced.
09306             if (SubPathOpen)
09307                 NumSplinters++;
09308         }
09309     }
09310 
09311     return NumSplinters;
09312 
09313 /*
09314     // Get array of verbs
09315     PathVerb* Verbs = (PathVerb*) DescribeHandle(VerbHandle);
09316 
09317     // p,q indexes to selected point and endof(sub)path
09318     // vp,vq local verb holders
09319 
09320     INT32 p = -1;
09321     INT32 q = -1;
09322     PathVerb vp;
09323     PathVerb vq;
09324 
09325     INT32 splinters = 0;
09326     while (p++, FindNextSelected(&p))
09327     {
09328         q = p;
09329 
09330         FindEndElOfSubPath(&q);                         // returns index of last element in this subpath/path
09331         if (p>q) break;                                 // just a quick check on sanity.
09332 
09333         if (p==q)
09334         {
09335             if (!(Verbs[p] & PT_CLOSEFIGURE)) continue; // ignore selected points at end of open subpaths
09336         }   
09337 
09338         vp = (Verbs[p] & ~PT_CLOSEFIGURE);
09339         vq = (Verbs[q]);
09340 
09341         if (vp==PT_MOVETO)
09342         {
09343             if (!(vq & PT_CLOSEFIGURE))                 // is this sub path already open?
09344             {
09345                 p++;                                    // move forward past moveto
09346                 continue;                               // continue do loop
09347             }
09348         }
09349 
09350         if ((vp == PT_MOVETO) || (vp == PT_LINETO) || (vp == PT_BEZIERTO))
09351             splinters++;
09352     }   
09353 
09354     return splinters;
09355 */
09356 }
09357 
09358 
09359 
09360 /********************************************************************************************
09361 
09362 >   BOOL Path::EnsureValid(BOOL* ChangesMade = NULL)
09363 
09364     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
09365     Created:    12/1/95
09366     Inputs:     Pointer to a BOOL to return a result in (you can pass in NULL)
09367     Outputs:    ChangesMade will be TRUE if changes have been made to the path, FALSE if otherwise
09368     Returns:    TRUE for success, FALSE for failure (see below)
09369     Purpose:    Will attempt to make a path into a correct state.  Checks are :-  
09370                 1. All subpaths must start with a MoveTo
09371                 2. Consecutive MoveTos are removed
09372                 3. Zero element paths are unrepairable
09373                 4. MoveTos at the end of the path are removed
09374                 5. Should be three BezierTos in a row, only the last can be CloseFigure
09375                 6. A LineTo to the same location as the previous point is removed.
09376 
09377     ERRORs:     FALSE is returned if the path was knackered OR if we encountered an error in
09378                 trying to repair it.  (SetError will have been called)
09379 
09380 ********************************************************************************************/
09381 BOOL Path::EnsureValid(BOOL* ChangesMade)
09382 {
09383 
09384     BOOL PathOK = FALSE;
09385     BOOL Temp;
09386     BOOL* pChangeFlag;
09387     pChangeFlag = (ChangesMade == NULL) ? &Temp : ChangesMade;
09388     *pChangeFlag = FALSE;
09389 
09390     while (!PathOK)
09391     {
09392         // Recache the path pointers as things may have moved
09393         PathVerb*  Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
09394         DocCoord*  Coords = (DocCoord*)  DescribeHandle(CoordHandle);
09395         PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
09396         INT32 NumCoords = GetNumCoords();
09397 
09398         // (3) Check for zero element path
09399         if (NumCoords == 0) 
09400         {
09401             TRACEALL( _T("Empty path detected\n") );
09402             Error::SetError(_R(IDE_INVALID_PATH));
09403             return FALSE;
09404         }
09405 
09406         INT32 Counter = 0;
09407 
09408         while (Counter < NumCoords)
09409         {
09410             // Recache the path pointers in case the previous pass caused the arrays to move
09411             Verbs  = (PathVerb*)  DescribeHandle(VerbHandle);
09412             Coords = (DocCoord*)  DescribeHandle(CoordHandle);
09413             Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
09414 
09415             // (1) Check that all subpaths start with a MoveTo
09416             if ( (Counter == 0) || (Verbs[Counter-1] & PT_CLOSEFIGURE) )
09417             {
09418                 if ((Verbs[Counter] & ~PT_CLOSEFIGURE) != PT_MOVETO)
09419                 {
09420                     // Insert a MoveTo at the same location as this point
09421                     TRACEALL( _T("Inserting a MoveTo at %d\n"), Counter );
09422                     SetPathPosition(Counter);
09423                     *pChangeFlag = TRUE;
09424                     if (!InsertMoveTo(Coords[Counter]))
09425                         return FALSE;
09426                     break;
09427                 }
09428             }
09429 
09430             // (2) Check there are no two consecutive MoveTos
09431             if ( (Counter != NumCoords-1) && (Verbs[Counter] == PT_MOVETO) )
09432             {
09433                 if (Verbs[Counter+1] == PT_MOVETO)
09434                 {
09435                     // Remove this MoveTo
09436                     TRACEALL( _T("Removing a MoveTo at %d\n"), Counter );
09437                     SetPathPosition(Counter);
09438                     *pChangeFlag = TRUE;
09439                     if (!DeleteElement())
09440                         return FALSE;
09441                     break;
09442                 }
09443             }
09444 
09445             // (4) Check for trailing moveto at the end of a path
09446             if ( (Counter == NumCoords-1) && (Verbs[Counter] == PT_MOVETO) )
09447             {
09448                 // Remove this MoveTo
09449                 TRACEALL( _T("Removing a MoveTo at the end (%d)\n"), Counter );
09450                 SetPathPosition(Counter);
09451                 *pChangeFlag = TRUE;
09452                 if (!DeleteElement())
09453                     return FALSE;
09454                 break;
09455             }
09456 
09457             switch (Verbs[Counter] & ~PT_CLOSEFIGURE)
09458             {
09459                 case PT_MOVETO: 
09460                     // MoveTos shouldn't have CloseFigures
09461                     if (Verbs[Counter] != PT_MOVETO)
09462                     {
09463                         TRACEALL( _T("Detected a MoveTo+CloseFigure combination\n at %d"), Counter );
09464                         Verbs[Counter] = PT_MOVETO;
09465                         *pChangeFlag = TRUE;
09466                     }
09467                     Counter++;
09468                     break;
09469                 case PT_LINETO:
09470                     // If the coord of this is the same as the last (which must be a MoveTo) then delete it
09471                     if ((Counter > 0) && (Coords[Counter] == Coords[Counter-1]) && (Verbs[Counter-1] == PT_MOVETO))
09472                     {
09473                         TRACEALL( _T("Removing a LineTo at %d\n"), Counter );
09474                         SetPathPosition(Counter);
09475                         *pChangeFlag = TRUE;
09476 
09477                         // If the LineTo has the PT_CLOSEFIGURE bit set, ensure that the element
09478                         // before it has this bit set.
09479                         // If we don't then it is possible for shapes to become lines.
09480                         if ((Verbs[Counter] & PT_CLOSEFIGURE) && (Verbs[Counter-1] != PT_MOVETO))
09481                             Verbs[Counter-1] |= PT_CLOSEFIGURE;
09482 
09483                         // Delete the surplus LineTo element
09484                         if (!DeleteElement())
09485                             return FALSE;
09486 
09487                         Counter = NumCoords+1;      // So we go round the entire path again
09488                     }
09489                     else
09490                     {
09491                         // This point should be endpoint
09492                         if (!Flags[Counter].IsEndPoint)
09493                         {
09494                             TRACEALL( _T("Fixing endpoint status at %d\n"), Counter );
09495                             *pChangeFlag = TRUE;
09496                             Flags[Counter].IsEndPoint = TRUE;
09497                         }
09498                         Counter ++;
09499                     }
09500                     break;
09501                 case PT_BEZIERTO:
09502                     // (5) Should be three BezierTos in a row, only the last can be CloseFigure
09503                     if (Counter >= NumCoords-2)
09504                     {
09505                         TRACEALL( _T("Path ends with a short Bezier section\n") );
09506                         return FALSE;
09507                     }
09508                     if ( (Verbs[Counter] != PT_BEZIERTO) || (Verbs[Counter+1] != PT_BEZIERTO) ||
09509                                                 ((Verbs[Counter] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) )
09510                     {
09511                         TRACEALL( _T("InvalidBezier section detected starting at %d\n"), Counter );
09512                         return FALSE;
09513                     }
09514                     Flags[Counter++].IsEndPoint = FALSE;
09515                     Flags[Counter++].IsEndPoint = FALSE;
09516                     Flags[Counter++].IsEndPoint = TRUE;
09517                     break;
09518                 default:
09519                     TRACEALL( _T("This path is *very* corrupt (Invalid verb found)\n") );
09520                     return FALSE;
09521             }
09522             if (Counter == NumCoords)
09523                 PathOK = TRUE;
09524         }
09525     }
09526 
09527     return TRUE;
09528 }
09529 
09530 
09531 
09532 /********************************************************************************************
09533 
09534 >   void Path::ClearNeedToRender()
09535 
09536     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
09537     Created:    24/1/95
09538     Inputs:     -
09539     Outputs:    -
09540     Returns:    -
09541     Purpose:    Sets the NeedToRender flags on all of the path elements to FALSE
09542     Errors:     -
09543 
09544 ********************************************************************************************/
09545 void Path::ClearNeedToRender()
09546 {
09547     PathFlags* Flags  = (PathFlags*) DescribeHandle(FlagsHandle);
09548     for (INT32 loop = GetNumCoords()-1; loop >= 0 ; loop--)
09549     {
09550         Flags[loop].NeedToRender = FALSE;
09551     }
09552 }
09553 
09554 
09555 
09556 /********************************************************************************************
09557 
09558 >   BOOL Path::DistanceTo(const INT32 index, const double t, double* distance, double dFlatness=64)
09559 
09560     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
09561     Created:    14/06/95
09562     Inputs:     index  = an index into the path arrays describing the first coordinate
09563                          of an element.
09564                 t      = a parameter 0..1 describing a point on the path element
09565     Outputs:    distance- a 72000 inch measurement
09566     Returns:    TRUE if distance was calculated
09567                 FALSE if not
09568     Purpose:    This function takes a description of a point lying on a path. The point is
09569                 described by (i,t) where i is the index of the element t lies in, and t
09570                 is a parametric variable 0<=t<=1.
09571                 The function calculates the distance along this path to the point described
09572                 by (i,t).
09573                 This routine complements SqrDistanceToPoint() which calculates the pair (i,t)
09574                 for a given coordinate close to the path. 
09575 
09576     SeeAlso:    SqrDistanceToPoint
09577     
09578 ********************************************************************************************/
09579 BOOL Path::DistanceTo(INT32 index, double t, double* distance, double dFlatness)
09580 {
09581     PathVerb* InVerbs = GetVerbArray();
09582     PathVerb vb = InVerbs[index] & ~PT_CLOSEFIGURE;
09583     DocCoord* InCoords = GetCoordArray();
09584     double LoDist = 0.0;
09585     double HiDist = 0.0;
09586 
09587     if (GetNumCoords()==0)
09588         return FALSE;
09589     
09590     // If t is near 1 then we can skip forward to the next segment
09591     // this is to avoid math problems with splitting beziers near their ends
09592     INT32 IndexEP = index;
09593     if (t>=0.99995 && vb==PT_BEZIERTO)
09594     {
09595         // Move up to next complete segment
09596         if (IndexEP != GetNumCoords()-1)
09597         {
09598             if (!FindNextEndPoint(&IndexEP))
09599                 return FALSE;
09600             t = 0.0;
09601         }
09602     }
09603     
09604     // find the length to the path up to the start of the nearest element
09605     // specified by index.
09606     ProcessLength LengthObj(dFlatness);
09607     if (!LengthObj.PathLength(this, &LoDist, IndexEP-1))
09608         return FALSE;
09609 
09610     // ok we need to evaluate the distance along the
09611     // path element i at parameter 0<=t<1
09612     switch (vb)
09613     {
09614         case PT_LINETO:
09615         {
09616             double TotalLength = InCoords[index-1].Distance(InCoords[index]);
09617             HiDist = TotalLength * t;
09618         }
09619         break;
09620 
09621         case PT_BEZIERTO:
09622         {
09623             if ((t>0.00005) && (t<0.99995))     // avoids problems at the extremes.
09624             {
09625                 UINT32 NumElements;
09626 
09627                 PathVerb NewVerbs[6];
09628                 DocCoord NewCoords[6];
09629                 Path     TempPath;
09630 
09631                 BOOL    ok = TempPath.Initialise(12,12);
09632                 if (ok) ok = PathUtil::SplitCurve(t, &InCoords[index-1], &NumElements, NewVerbs, NewCoords);
09633                 if (ok) ok = TempPath.CopyPathDataFrom(NewCoords, NewVerbs, 3);
09634                 if (ok)
09635                 {
09636                     TempPath.SetPathPosition(0);
09637                     ok = TempPath.InsertMoveTo(InCoords[index-1]);
09638                 }
09639                 if (ok)
09640                 {
09641                     ProcessLength LengthObj2(64);
09642                     ok = LengthObj2.PathLength(&TempPath, &HiDist);
09643                 }
09644                 if (!ok)
09645                     return FALSE;
09646             }
09647         }
09648         break;
09649 
09650         default:
09651             ERROR3("Unable to find point on path. Index does not specify line or curve");
09652             break;
09653     }
09654 
09655     *distance = LoDist+HiDist;
09656 
09657     return TRUE;
09658 }
09659 
09660 
09661 
09662 /********************************************************************************************
09663 
09664 >   BOOL Path::DistanceTo(DocCoord Coord, double* distance, double dFlatness=64.0)
09665 
09666     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
09667     Created:    14/06/95
09668     Inputs:     Coord       - A coordinate
09669     Outputs:    distance    - a 72000 inch measurement
09670     Returns:    TRUE if distance was calculated
09671                 FALSE if not
09672     Purpose:    This function takes as input a coordinate (usually a mouse click point). It
09673                 will calculate the closest point on the path to this coordinate and return
09674                 the distance along the path from its start point to this coordinate
09675     
09676 ********************************************************************************************/
09677 BOOL Path::DistanceTo(DocCoord Coord, double* dist, double dFlatness)
09678 {
09679     Path TempPath;
09680     INT32 SplitAt;
09681     INT32 NumNewEl;
09682 
09683     if (!TempPath.Initialise(GetNumCoords(),24))
09684         return FALSE;
09685 
09686     if (!TempPath.CopyPathDataFrom(this))
09687         return FALSE;
09688 
09689     if (!TempPath.SplitAtPoint(Coord, &SplitAt, &NumNewEl))
09690         return FALSE;
09691 
09692     ProcessLength LengthObj(dFlatness);
09693     if (!LengthObj.PathLength(&TempPath, dist, SplitAt))
09694         return FALSE;
09695 
09696     return TRUE;
09697 }
09698 
09699 
09700 
09701 /********************************************************************************************
09702 
09703 >   BOOL Path::SplitAtPoint(const DocCoord& SplitPoint, INT32* SplitAt, INT32* NewElements)
09704 
09705     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> (from Riks routine in OpFreeHand)
09706     Created:    15/06/95
09707     Inputs:     SplitPoint  - The coord that we want the split to happen close to
09708     Outputs:    SplitAt     - The Slot number of the new end point
09709                 NewElements - The number of new elements inserted into the path
09710     Returns:    TRUE if it was able to split the path ok,
09711                 FALSE if it failed
09712     Purpose:    This function takes a coordinate (typically not on but) close to this path 
09713                 and splits the path at a calculated point on the path which happens to be
09714                 closest to the input coord. By Split I mean a new coordinate in will appear
09715                 in the path, but the path will not have changed its shape in anyway.
09716 
09717 ********************************************************************************************/
09718 
09719 BOOL Path::SplitAtPoint(const DocCoord& SplitPoint, INT32* SplitAt, INT32* NewElements)
09720 {
09721     INT32       SplitElement=0;
09722     UINT32      NumElements;
09723     PathVerb    NewVerbs[6];
09724     DocCoord    NewCoords[6];
09725     PathFlags   NewFlags[6];
09726 
09727     // Try and split the path. This returns FALSE if the path did not need splitting
09728     if (SplitAtPoint(SplitPoint, &SplitElement, &NumElements, NewVerbs, NewCoords))
09729     {
09730         // Make a note of where the new endpoint will be
09731         *SplitAt = (SplitElement+(NumElements/2)-1);
09732         *NewElements = NumElements;
09733 
09734         // The path was split, so we know where, and how, so let's party on the path
09735         PathVerb* Verbs = GetVerbArray();
09736         PathFlags* Flags = GetFlagArray();
09737 //      DocCoord* Coords = GetCoordArray();
09738 //      UINT32 NumCoords = GetNumCoords();  
09739         INT32 NumToChange;
09740 
09741         // We're adding something, either a line or curve - check which
09742         if ((Verbs[SplitElement] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
09743         {
09744             // Adding a curve - number to change = 6
09745             NumToChange = 6;
09746 
09747             // Initialise the flags appropriately
09748             for (INT32 i=0; i<6; i++)
09749             {
09750                 // copy the flags from the old curve (repeat the flag in the first 3 elements)
09751                 NewFlags[i] = Flags[SplitElement+(i%3)];
09752                 NewFlags[i].IsSelected = FALSE;
09753 
09754                 // Want it to be a cusp in the middle
09755                 if ((i>0) && (i<4))
09756                 {
09757                     NewFlags[i].IsSmooth = FALSE;
09758                     NewFlags[i].IsRotate = FALSE;
09759                 }
09760             }
09761 
09762             // If this was happening at the end of a path,
09763             // then make sure the close figure flag is set correctly
09764             if (Verbs[SplitElement+2] & PT_CLOSEFIGURE)
09765                 NewVerbs[5] |= PT_CLOSEFIGURE;
09766         }
09767         else
09768         {
09769             // The slit happened in a straight line section
09770             NumToChange = 2;
09771             NewFlags[0] = NewFlags[1] = Flags[SplitElement];
09772             NewFlags[0].IsSmooth = NewFlags[0].IsRotate = FALSE;
09773             NewFlags[0].IsSelected = FALSE;
09774 
09775             // Make sure that the close figure flag is maintained
09776             if (Verbs[SplitElement] & PT_CLOSEFIGURE)
09777                 NewVerbs[1] |= PT_CLOSEFIGURE;
09778         }
09779 
09780         // Move to the place in the path where the split took place
09781         SetPathPosition(SplitElement);
09782         PathFlags InsertFlags;
09783         BOOL      InsertWorked;
09784 
09785         // insert a curve or a line, depending on where the split happened
09786         if (NumToChange==6)
09787             InsertWorked = InsertCurveTo(NewCoords[0], NewCoords[1], NewCoords[2], &InsertFlags);
09788         else
09789             InsertWorked = InsertLineTo(NewCoords[0], &InsertFlags);
09790 
09791         // see if the insertion worked
09792         if (!InsertWorked)
09793             return FALSE;
09794     }
09795     else
09796     {
09797         // else we tried to split at an existing control point (endpoint)
09798         // That means that there were no new endpoints
09799         *NewElements = 0;
09800         *SplitAt = (SplitElement-1);
09801 
09802         // See if we can find the control point in question
09803         DocCoord* Coords = GetCoordArray();
09804         INT32 NumCoords = GetNumCoords();
09805 
09806         // see if the split point is over the last point on the curve
09807         if (Coords[NumCoords-1]==SplitPoint)
09808             *SplitAt = NumCoords;
09809     }   
09810 
09811     // all worked
09812     return TRUE;
09813 }
09814 
09815 
09816 
09817 /********************************************************************************************
09818 >   BOOL Path::GetPointAtDistance(MILLIPOINT Distance, DocCoord* pPoint)
09819 
09820     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
09821     Created:    7/3/96
09822     Inputs:     Distance - a length along the path
09823     Outputs:    pPoint - the coordinate of the point that distance along the path
09824                 pTangent = tangent at this point (can be NULL if tangent not required)
09825     Returns:    TRUE/FALSE for success/failure
09826     Purpose:    Gets the coordinate of a point a certain distance along this path
09827 ********************************************************************************************/
09828 BOOL Path::GetPointAtDistance(MILLIPOINT Distance, DocCoord* pPoint, double* pTangent, UINT32* pPressure)
09829 {
09830     ERROR2IF(pPoint==NULL, FALSE, "NULL output parameter");
09831     ERROR2IF(Distance<0, FALSE, "-ve distance");
09832 
09833     ProcessPathDistance PathProc(64);
09834     BOOL Found = FALSE;
09835     BOOL ok = PathProc.GetCoordAndTangent(pPoint, pTangent, &Found, Distance, this, pPressure);
09836 
09837     return ok && Found;
09838 }
09839 
09840 
09841 
09842 /********************************************************************************************
09843 >   BOOL Path::GetDistanceToPoint(DocCoord Point, MILLIPOINT* Distance)
09844 
09845     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
09846     Created:    22/8/96
09847     Inputs:     Point - A DocCoord              
09848     Outputs:    Distance - a length along the path
09849     Returns:    TRUE/FALSE for success/failure
09850     Purpose:    Gets the distance along the path for a given point
09851 ********************************************************************************************/
09852 BOOL Path::GetDistanceToPoint(DocCoord Point, MILLIPOINT* Distance)
09853 {
09854     ERROR2IF(Distance==NULL, FALSE, "NULL output parameter");
09855 
09856     INT32 NextEndpoint = 0;
09857     double dDistance = 0.0;
09858     double mu = 0.0;
09859     BOOL ok = (SqrDistanceToPoint(Point, &NextEndpoint, &mu) != -1);
09860     ERROR3IF(!ok, "SqrDistanceToPoint failed");
09861     if (ok)
09862     {
09863         ok = DistanceTo(NextEndpoint, mu, &dDistance);
09864         ERROR3IF(!ok, "Path::DistanceTo failed");
09865     }
09866 
09867     *Distance = (MILLIPOINT)dDistance;
09868 
09869     return ok;
09870 }
09871 
09872 
09873 
09874 /********************************************************************************************
09875 
09876 >   INT32 FindNonColinear(const DocCoord CoordArray[], const UINT32& ulNumCoords)
09877 
09878     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
09879     Created:    28/08/96
09880     Inputs:     CoordArray: The path with which to comapre this one
09881                 Tolerance:  The accuracy to which this path transformed to the otherPath
09882                             may be considered equal.
09883     Returns:    The index in the array of the first point
09884                 -1 if a straight line
09885     Purpose:    Support function for IsIsometric
09886                 Finds the first of three points in the given CoordArray that are not in a
09887                 straight line.
09888 
09889 ********************************************************************************************/
09890 INT32 FindNonColinear(const DocCoord CoordArray[], const UINT32& ulNumCoords)
09891 {
09892     UINT32 index = 0;
09893     UINT32 x1,y1,x2,y2;
09894     // Three points are colinear if the vectors between them have a zero cross-product
09895 
09896     while (index < ulNumCoords - 2)
09897     {
09898         // Might save time to work out the first two outside the loop
09899         // Depends on number of linear paths really
09900         x1 = CoordArray[index + 1].x - CoordArray[index].x;
09901         y1 = CoordArray[index + 1].y - CoordArray[index].y;
09902 
09903         x2 = CoordArray[index + 2].x - CoordArray[index + 1].x;
09904         y2 = CoordArray[index + 2].y - CoordArray[index + 1].y;
09905 
09906         if (x1 * y2 != x2 * y1)
09907         {
09908             return index;
09909         }
09910         ++index;
09911     }
09912 
09913     return -1;
09914 }
09915 
09916 
09917 /********************************************************************************************
09918 
09919 >   BOOL IsNear(const T& x, const T& value, const T& Tolerance)
09920                         
09921     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
09922     Created:    28/08/96
09923     Inputs:     x1:         The first of the two values to compare
09924                 x2:         The second of the two values to compare
09925                 Tolerance:  The value within which x1 is considered to be near to x2
09926     Returns:    TRUE if the two values have an absolute difference greater than or equal to
09927                 the given Tolerance
09928                 FALSE otherwise
09929     Purpose:    Support function for IsIsometric
09930                 Determines whether two values are near enough to each other to be considered
09931                 equal.
09932     Notes:      This is a templated function with <class T>
09933 
09934 ********************************************************************************************/
09935 template <class T>
09936 inline BOOL IsNear(const T& x1, const T& x2, const T& Tolerance)
09937 {
09938     return (ABS(x1 - x2) <= Tolerance) ? TRUE : FALSE;
09939 }
09940 
09941 
09942 
09943 /********************************************************************************************
09944 
09945 >   BOOL IsNear(const T& x, const T& value, const T& Tolerance)
09946                         
09947     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
09948     Created:    28/08/96
09949     Inputs:     x1:         The first of the two values to compare
09950                 x2:         The second of the two values to compare
09951                 Tolerance:  The value within which x1 is considered to be near to x2
09952     Returns:    TRUE if the two values have an absolute difference greater than or equal to
09953                 the given Tolerance
09954                 FALSE otherwise
09955     Purpose:    Support function for IsIsometric
09956                 Determines whether two values are near enough to each other to be considered
09957                 equal.
09958     Notes:      This is a templated function with <class T>
09959 
09960 ********************************************************************************************/
09961 
09962 /********************************************************************************************
09963 
09964 >   BOOL Solve3Simultaneous(const CCVector3& constsX0, const CCVector3& constsX1,
09965                         const CCVector3& constsY0, double& v0, double& v1, double& v2)
09966 
09967     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
09968     Created:    28/08/96
09969     Inputs:     constsX0:   First values in the 3 simultaneous equations
09970                 constsX1:   Second values in the 3 simultaneous equations
09971                 constsY0:   Third values in the 3 simultaneous equations
09972     Outputs:    v1,v2,v3:   The three solution values
09973     Returns:    TRUE if there were a solution
09974                 FALSE otherwise
09975     Purpose:    Support function for IsIsometric
09976                 Solves 3 simultaneous equations for the three values v1,v2,v3
09977 
09978 ********************************************************************************************/
09979 BOOL Solve3Simultaneous(const CCVector3& constsX0, const CCVector3& constsX1, const CCVector3& constsY0, double& v0, double& v1, double& v2)
09980 {
09981     const double Tolerance = 1e-10;
09982 
09983     double DivA = (constsX0.v0 - constsX0.v1) * (constsY0.v0 - constsY0.v2) - (constsX0.v0 - constsX0.v2) * (constsY0.v0 - constsY0.v1);
09984     if (IsNear(DivA, 0.0, Tolerance))
09985     {
09986         return FALSE;
09987     }
09988 
09989     double DivB = (constsY0.v0 - constsY0.v1) * (constsX0.v0 - constsX0.v2) - (constsY0.v0 - constsY0.v2) * (constsX0.v0 - constsX0.v1);
09990     if (IsNear(DivB, 0.0, Tolerance))
09991     {
09992         return FALSE;
09993     }
09994 
09995     v0 = ((constsY0.v0 - constsY0.v2) * (constsX1.v0 - constsX1.v1) - (constsY0.v0 - constsY0.v1) * (constsX1.v0 - constsX1.v2)) / DivA;
09996 
09997 
09998     v1 = ((constsX1.v0 - constsX1.v1) * (constsX0.v0 - constsX0.v2) - (constsX1.v0 - constsX1.v2) * (constsX0.v0 - constsX0.v1)) / DivB;
09999     
10000 
10001     v2 = constsX1.v0 - v0 * constsX0.v0 - v1 * constsY0.v0;
10002 
10003     return TRUE;
10004 }
10005 
10006 /*
10007 inline void Transform(const DocCoord& xy0,
10008                         const double& a, const double& b, const double& c, const double& d, 
10009                         const double& e, const double& f,
10010                     DocCoord& xy1)
10011 {
10012     xy1.x = (INT32)(a * (double)xy0.x + c * (double)xy0.y + e);
10013     xy1.y = (INT32)(b * (double)xy0.x + d * (double)xy0.y + f);
10014 }
10015 */
10016 
10017 /********************************************************************************************
10018 
10019 >   BOOL Path::IsIsometric(const Path& otherPath, Matrix* pTransform, 
10020                             const double& Tolerance = 1.0)
10021 
10022 
10023     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
10024     Created:    28/08/96
10025     Inputs:     OtherPath:  The path with which to compare this one
10026                 Tolerance:  The accuracy to which this path transformed to the otherPath
10027                             may be considered equal.
10028     Outputs:    pTransform: A pointer to the resulting transformation matrix if the return
10029                             value is TRUE
10030     Returns:    TRUE : this path is Isometric to the otherPath
10031                 FALSE : if not Isometric or inaccuracy losses
10032     Purpose:    Determines whether two paths are isometric.
10033                 In this context, isometric does not include true isomtric paths with
10034                 different starting points.
10035                 This can then be used to provide a transformation of this path, resulting in 
10036                 the given otherPath.
10037 
10038 ********************************************************************************************/
10039 BOOL Path::IsIsometric(const Path& OtherPath, Matrix* pTransform, const double& Tolerance) const
10040 {
10041     // Check path lengths for equality
10042     if (GetNumCoords() != OtherPath.GetNumCoords())
10043     {
10044         // If not equal can't possibly be the same
10045         return FALSE;
10046     }
10047     
10048     // Determine the transformation matrix from the first three coords
10049     DocCoord*   ThisCoordArray = GetCoordArray();
10050     DocCoord*   OtherCoordArray = OtherPath.GetCoordArray();
10051 
10052     double  a, b, c, d, e, f = Tolerance;       // Matrix components
10053 
10054     // Find First Non-Colinear points
10055     INT32 First = FindNonColinear(ThisCoordArray, GetNumCoords());
10056     if (First == -1)
10057     {
10058         // this path was a straight line.
10059         // If the other one is we might be in with a chance
10060         First = FindNonColinear(OtherCoordArray, OtherPath.GetNumCoords());
10061         if (First != -1)
10062         {
10063             // Unfortunately it had a kink so it can't match
10064             return FALSE;
10065         }
10066         // Shears don't work with the following. But then I doubt they're that common
10067         DocCoord ef(- ThisCoordArray[0].x, - ThisCoordArray[0].y);
10068 
10069         DocCoord v1(ThisCoordArray[1].x - ThisCoordArray[0].x, ThisCoordArray[1].y - ThisCoordArray[0].y);
10070         DocCoord v2(OtherCoordArray[1].x - OtherCoordArray[0].x, OtherCoordArray[1].y - OtherCoordArray[0].y);
10071         double v1dotv2 = v1.x * v2.x + v1.y * v2.y;
10072         double modv1sqrd = v1.x * v1.x + v1.y * v1.y;
10073         double v1crossv2 = v1.x * v2.y - v1.y * v2.x;
10074         a = v1dotv2 / modv1sqrd;
10075         b = v1crossv2 / modv1sqrd;
10076         c = -b;
10077         d = a;
10078         e = a * (double)ef.x - b * (double)ef.y + OtherCoordArray[0].x;
10079         f = b * (double)ef.x + a * (double)ef.y + OtherCoordArray[0].y;
10080     }
10081     else
10082     {
10083         // We have three non-colinear points:
10084 
10085         /*
10086             In fact, we have:
10087                 ax00 + cy00 + e = x10
10088                 bx00 + dy00 + f = y10
10089             &
10090                 ax01 + cy01 + e = x11
10091                 bx01 + dy01 + f = y11
10092             &
10093                 ax02 + cy02 + e = x12
10094                 bx02 + dy02 + f = y12
10095 
10096             Hence need to solve 2 sets of simultaneous equations for (a,c,e) & (b,d,f)
10097         */
10098 
10099         // Try to find a solution for the a,b,e part
10100         CCVector3   x0s(ThisCoordArray[First].x, ThisCoordArray[First + 1].x, ThisCoordArray[First + 2].x);
10101         CCVector3   y0s(ThisCoordArray[First].y, ThisCoordArray[First + 1].y, ThisCoordArray[First + 2].y);
10102         CCVector3   x1s(OtherCoordArray[First].x, OtherCoordArray[First + 1].x, OtherCoordArray[First + 2].x);
10103 
10104         if (!Solve3Simultaneous(x0s, x1s, y0s, a, c, e))
10105         {
10106             return FALSE;
10107         }
10108 
10109         // Try to find a solution for the (b,d,f) part
10110         CCVector3   y1s(OtherCoordArray[First].y, OtherCoordArray[First + 1].y, OtherCoordArray[First + 2].y);
10111 
10112         if (!Solve3Simultaneous(x0s, y1s, y0s, b, d, f))
10113         {
10114             return FALSE;
10115         }
10116     }
10117 
10118     // Determine the transformation matrix from the first three coords
10119     Matrix Solution(a, b, c, d, INT32(e), INT32(f));
10120 
10121     // Check the flags & bits   
10122     PathVerb*   ThisVerbArray   = GetVerbArray();
10123     PathVerb*   OtherVerbArray  = OtherPath.GetVerbArray();
10124 
10125     INT32 lTolerance = (INT32)Tolerance;
10126     // For each remaining coordinate in this path, check we can transform to the otherPath
10127     DocCoord CurrentCoord;
10128     for (INT32 i = 0; i < GetNumCoords(); ++i)
10129     {
10130 //      Transform(ThisCoordArray[i], a, b, c, d, e, f, CurrentCoord);
10131 
10132         // Compare the flags & bits (unlikely to be different if coords same though)
10133         if (ThisVerbArray[i] != OtherVerbArray[i])
10134         {
10135             return FALSE;
10136         }
10137         CurrentCoord = ThisCoordArray[i];
10138         Solution.transform(&CurrentCoord);      
10139         if (!IsNear(CurrentCoord.x, OtherCoordArray[i].x, lTolerance) ||
10140             !IsNear(CurrentCoord.y, OtherCoordArray[i].y, lTolerance))
10141         {
10142             return FALSE;
10143         }
10144     }
10145 
10146     *pTransform = Solution;
10147 
10148     return TRUE;
10149 }
10150 
10151 /********************************************************************************************
10152 
10153  >  void Path::Scale(const DocCoord dcOrigin, const double dDPI=96.0)
10154                                            
10155     Author:     Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com>
10156     Created:    16/4/97
10157     Inputs:     dDPI        The DPI by which to scale the path
10158                 dcOrigin    The origin by which to scale the path
10159 
10160                             This point should be the top left hand
10161                             corner of the region that is used to create
10162                             a bitmap.
10163 
10164   
10165   Purpose:      This function is called on a path specified in DocCoords, to
10166                 convert it into pixel coordinates.
10167 
10168                 This is used in imagemaps, to convert a path specified in the
10169                 document to a polygon to specify an imagemap clickable area
10170                 over a bitmap.
10171 
10172                 This function does the following to all the coordinates in
10173                 the path
10174 
10175                 a. Subtracts the origin dcOrigin
10176                 b. Inverts the y-axis
10177                 c. Multiplies the coordinates to convert them to the number
10178                    of DPI specified.
10179 
10180     
10181   SeeAlso:      Imagemap::AddPolygon()
10182     
10183 ********************************************************************************************/
10184 void Path::Scale(const DocCoord dcOrigin, const double dDPI)
10185 {
10186     //First find out how many coordinates we have to process
10187     INT32 lNumCoords=GetNumCoords();
10188 
10189     //And get a pointer to the array
10190     DocCoord* pdcArray=GetCoordArray();
10191 
10192     //For every coord in the array
10193     for (INT32 l=0; l<lNumCoords; l++)
10194     {
10195         //Get a pointer to the coordinate we are changing
10196         DocCoord* pdcThisCoord=&pdcArray[l];
10197 
10198         if (pdcThisCoord==NULL)
10199         {
10200             ERROR2RAW("Path::Scale - coord error");
10201             return;
10202         }
10203 
10204         //And scale it
10205         pdcThisCoord->Scale(dcOrigin, (float)dDPI);
10206     }
10207 }
10208 
10209 /********************************************************************************************
10210 
10211  >  BOOL Path::MakePathFromSubPath(const INT32 lSubpathIndex, Path* ppthToFill)
10212                     
10213     Author:     Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com>
10214     Created:    1/5/97 - Enough is enough
10215     Inputs:     lSubpathIndex   Index of the subpath to copy - that is,
10216                                 0 copies the first subpath
10217                                 1 copies the second
10218                                 etc
10219                 ppthToFill  Path to copy the subpath into
10220                 
10221     Outputs:    ppthToFill
10222     Returns:    TRUE if there were no problems
10223                                                  
10224   Purpose:      Makes a subpath into a path in its own right.
10225     
10226   SeeAlso:      Imagemap::DrawPathToOutputDevice()
10227     
10228 ********************************************************************************************/
10229 
10230 BOOL Path::MakePathFromSubPath(const INT32 lSubpathIndex, Path* ppthToFill)
10231 {
10232     //Check our variables
10233     ERROR2IF(ppthToFill==NULL,FALSE, "Path::MakePathFromSubPath - null parameter");
10234     ERROR2IF(lSubpathIndex>(GetNumSubpaths()-1),FALSE, "Path::MakePathFromSubPath - null parameter");
10235 
10236     //First we must find the start and end of the subpath
10237 
10238     //This variable will find the start of the subpath
10239     INT32 lStart=0;
10240 
10241     //For each subpath we must skip over
10242     for (INT32 l=0; l<lSubpathIndex; l++)
10243     {
10244         //Move to the end of the current subpath
10245         FindEndElOfSubPath(&lStart);
10246 
10247         //Move on one place to take us into the next subpath
10248         lStart++;
10249 
10250         //And if we haven't overshot the end of the path
10251         if (lStart<GetNumCoords())
10252         {
10253             //Move to the beginning of that subpath
10254             FindStartOfSubPath(&lStart);
10255         }
10256     }
10257 
10258     //If we have overshot the end of the path, return FALSE
10259     ERROR2IF(lStart>GetNumCoords(),FALSE, "Path::MakePathFromSubPath failed");
10260 
10261     //lLookPos is now pointing to the start of the subpath we want to copy
10262     //This variable will find the end of that subpath
10263     INT32 lEnd=lStart;
10264 
10265     //And move it to the end of the subpath
10266     FindEndElOfSubPath(&lEnd);
10267 
10268     //Now, copy the subpath into the path
10269     MakePathFromSection(lStart, (lEnd-lStart+1), ppthToFill);
10270 
10271     //And return TRUE
10272     return TRUE;
10273 }
10274 
10275 /********************************************************************************************
10276 
10277 >   INT32 Path::CalcArea()
10278                     
10279     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
10280     Created:    01/03/2005
10281     Inputs:     -
10282     Outputs:    -
10283     Returns:    Area of path in path units (typically millipoints)
10284                 Or -1 if no area could be calculated
10285     Purpose:    Calculate the area of this path
10286     SeeAlso:    -
10287     
10288 ********************************************************************************************/
10289 
10290 XLONG Path::CalcArea()
10291 {
10292     // Check whether it's closed!
10293     if (!IsFilled)
10294         return 0;
10295 
10296     // First, use ClipPathToPath to get a path that has no self-intersections...
10297 
10298     // Next flatten it
10299     Path FlatPath;
10300     FlatPath.Initialise();
10301 
10302     BOOL bOK = Flatten(64, &FlatPath);
10303     if (!bOK)
10304         return -1;
10305 
10306     // Finally, compute the area under each flattened segment and sum them
10307     // (+dx adds to area, -dx subtracts from area)
10308     INT32 i = 0;
10309     XLONG area = 0;
10310     XLONG dx = 0;
10311     DocRect bounds = GetBoundingRect();
10312     INT32 baseline = bounds.lo.y;
10313 //  PathFlags* pFlags = FlatPath.GetFlagArray();
10314     DocCoord* pCoords = FlatPath.GetCoordArray();
10315     PathVerb* pVerbs  = FlatPath.GetVerbArray();
10316     DocCoord origin(0,0);
10317     for (i=0; i < FlatPath.GetNumCoords(); i++)
10318     {
10319         if (pVerbs[i] == PT_MOVETO)
10320             origin = pCoords[i];
10321         else if (pVerbs[i] == PT_LINETO)
10322         {
10323             // area of this segment is area of rectangle under origin.y + area of triangle from origin to current point
10324             dx = pCoords[i].x - origin.x;
10325             area += dx*(XLONG)(origin.y-baseline) + dx*(XLONG)((ABS(pCoords[i].y - origin.y))/2);
10326 
10327             origin = pCoords[i];
10328         }
10329         else if (pVerbs[i] == PT_CLOSEFIGURE)
10330         {
10331             // do nothing
10332         }
10333         else
10334             ERROR3("Unexpected path verb");
10335     }
10336 
10337     // Counter-clockwise paths will generate -ve areas
10338     return ABS(area);
10339 }
10340 

Generated on Sat Nov 10 03:46:28 2007 for Camelot by  doxygen 1.4.4