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