00001 // $Id: rsmooth.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 // Implementation file containing routines to control the interactive smoothing 00099 // of selected path regions 00100 00101 /* 00102 */ 00103 00104 #include "camtypes.h" 00105 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 #include "rsmooth.h" 00107 #include "nodepath.h" 00108 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 #include <math.h> 00110 //#include "mike.h" 00111 //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00113 #include "objchge.h" 00114 #include "nodeblnd.h" 00115 #include "nodebldr.h" 00116 #include "ophist.h" 00117 00118 CC_IMPLEMENT_DYNCREATE( OpRetroSmooth, SelOperation ) 00119 00120 DECLARE_SOURCE("$Revision: 1492 $"); 00121 00122 // Declare smart memory handling in Debug builds 00123 #define new CAM_DEBUG_NEW 00124 00125 00126 /******************************************************************************************** 00127 00128 > RetroSmooth::RetroSmooth() 00129 00130 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00131 Created: 11/11/94 00132 Purpose: RetroSmooth constructor 00133 Sets default values for internal variables used by the RetroSmooth 00134 class. 00135 SeeAlso: - 00136 00137 ********************************************************************************************/ 00138 00139 RetroSmooth::RetroSmooth() 00140 { 00141 pRetroNode = NULL; 00142 pRetroSpread = NULL; 00143 RetroSmoothAcc = SMOOTH_MIN; 00144 RetroSmoothing = FALSE; 00145 RetroPathCRC = 0; 00146 RetroSelCRC = 0; 00147 RetroSmoothPercent = 0; 00148 00149 m_bRender = TRUE; 00150 } 00151 00152 00153 00154 /******************************************************************************************** 00155 00156 > BOOL RetroSmooth::Initialise() 00157 00158 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00159 Created: 11/11/94 00160 Inputs: - 00161 Returns: TRUE - then the retro smooth initialisation has succedded 00162 FALSE - then no memory to perform the retro smooth. The caller should 00163 abort the operation. 00164 Purpose: Initialise various internal lists of data used by the retrosmooth class. 00165 00166 ********************************************************************************************/ 00167 00168 BOOL RetroSmooth::Initialise() 00169 { 00170 BOOL ok = RetroPath.Initialise(12,12); 00171 if (ok) ok = RetroEorPath.Initialise(12,12); 00172 if (ok) ok = RetroEorRegion.Initialise(12,12); 00173 return ok; 00174 } 00175 00176 00177 00178 /******************************************************************************************** 00179 00180 > void RetroSmooth::Changing(NodePath* pNodePath, Spread* pSpread, double smooth) 00181 00182 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00183 Created: 11/11/94 00184 Inputs: pNodePath = pointer to a node path to smooth 00185 pSpread = pointer to the nodes spread 00186 smooth = smoothness level to use 00187 Returns: 00188 Purpose: This function is called when a change has occured to the retrosmooth input 00189 control device, and it wishes to inform the retrosmoother. The function takes 00190 a node and spread and renders a new smoothed eor'd version of the path to 00191 the display device. No changes occur to the NodePath provided. All changes 00192 occur internally to this smooth class. 00193 If this is the first of a sequence of calls to the changing function, 00194 a cached version of the path is created, along with information about the 00195 selected regions within the path. On subsequent calls, the cached version 00196 of the path will be used to create a smooth display path. 00197 00198 ********************************************************************************************/ 00199 00200 void RetroSmooth::Changing(NodePath* pNodePath, Spread* pSpread, double smooth) 00201 { 00202 /* 00203 // Find the docview that we are looking at 00204 DocView* pDocView = DocView::GetCurrent(); 00205 00206 // Refit the path - First work out the current error factor 00207 double ScaleFactor = (pDocView->GetViewScale()).MakeDouble(); 00208 double ErrorLevel = (64 + (160*smooth)) / ScaleFactor; 00209 RetroSmoothAcc = ErrorLevel * ErrorLevel; 00210 */ 00211 00212 // calculate a useful smooth accuracy 00213 double newacc = SMOOTH_MIN + (SMOOTH_MAX - SMOOTH_MIN)*pow((smooth/100), 3.0); 00214 00215 if (newacc == RetroSmoothAcc) 00216 return; 00217 00218 // keep track of some variables for later 00219 RetroSmoothAcc = newacc; 00220 RetroSmoothPercent = smooth; 00221 pRetroSpread = pSpread; 00222 00223 // if this is the first call to us then do the setup 00224 if (!RetroSmoothing) 00225 if (!RetroSmooth::Starting(pNodePath)) 00226 return; 00227 00228 // finally we are in a position to start retro activity. 00229 RetroSmoothing = TRUE; 00230 RetroSmooth::Update(); 00231 00232 } 00233 00234 00235 00236 /******************************************************************************************** 00237 00238 > void RetroSmooth::Finished() 00239 00240 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00241 Created: 11/11/94 00242 Inputs: - 00243 Returns: - 00244 Purpose: This function should be called to terminate the retro smooth action. The 00245 data cached during the calls to RetroSmooth::Changing() will now be used to 00246 actually affect the path held in the document. 00247 00248 ********************************************************************************************/ 00249 00250 void RetroSmooth::Finished() 00251 { 00252 // if no retro path, just do nothing 00253 if (!pRetroNode || !RetroSmoothing) 00254 return; 00255 // tidy up after retro smoothing 00256 RetroEorRegion.Empty(); 00257 RecordPathRegions(&RetroEorPath); 00258 RetroPathCRC = RetroEorPath.CalcCRC(); 00259 RetroSelCRC = RetroEorPath.CalcSelectedCRC(); 00260 RetroSmoothing = FALSE; 00261 00262 // finally create an op to really smooth the tree object 00263 // path and execute it. 00264 OpRetroSmooth* pOpRetroSmooth = new OpRetroSmooth; 00265 if (pOpRetroSmooth != NULL) 00266 pOpRetroSmooth->DoRetroSmooth(pRetroNode, &RetroEorPath, RetroSmoothAcc); 00267 } 00268 00269 00270 /******************************************************************************************** 00271 00272 > void RetroSmooth::Invalidate() 00273 00274 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00275 Created: 22/11/94 00276 Inputs: - 00277 Returns: - 00278 Purpose: Invalidates all cached information about the currently active retro 00279 smoothing path. Cached paths and region buffers are compacted. 00280 00281 ********************************************************************************************/ 00282 00283 void RetroSmooth::Invalidate() 00284 { 00285 pRetroNode = NULL; 00286 pRetroSpread = NULL; 00287 RetroSmoothAcc = SMOOTH_MIN; 00288 RetroSmoothing = FALSE; 00289 RetroPathCRC = 0; 00290 RetroSelCRC = 0; 00291 00292 RetroPath.ClearPath(TRUE); 00293 RetroEorPath.ClearPath(TRUE); 00294 RetroEorRegion.Empty(); 00295 } 00296 00297 00298 /******************************************************************************************** 00299 00300 > double RetroSmooth::ReturnCachedAccuracy(Path* pPath) 00301 00302 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00303 Created: 23/11/94 00304 Inputs: pPath = pointer to a path 00305 Returns: a percentage value for the current smoothness of this curve 00306 Purpose: This function can be used to determin the smoothness of any curve. It will 00307 simply check for a match between the path given and an internal cached 00308 representation. If a match is found the current smoothness is returned, ie 00309 the last value passed to the RetroSmooth::Changing() function. If no match 00310 is found a value of 0 is returned, meaning the curve has not been smoothed. 00311 00312 ********************************************************************************************/ 00313 00314 double RetroSmooth::ReturnCachedAccuracy(Path* pPath) 00315 { 00316 // calculate the cyclic redundancy for this path. 00317 INT32 new_crc = pPath->CalcCRC(); 00318 INT32 sel_crc = pPath->CalcSelectedCRC(); 00319 00320 if ((RetroPathCRC == new_crc) && (RetroSelCRC == sel_crc)) 00321 return RetroSmoothPercent; 00322 00323 return 0; 00324 } 00325 00326 00327 00328 /******************************************************************************************** 00329 00330 BOOL RetroSmooth::Starting(NodePath* pDocNodePath) 00331 00332 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00333 Created: 11/11/94 00334 Inputs: pDocNodePath = pointer to a node path to smooth 00335 Returns: 00336 Purpose: Set up the internal data structures for this new NodePath. This allows us 00337 to continue resmoothing the path given new smooth values, until the outside 00338 world calls RetroSmooth::Finished. We cache the path and its selected regions 00339 to help us with some hairy redraw problems. 00340 SeeAlso: RetroSmooth::Update() 00341 00342 ********************************************************************************************/ 00343 00344 BOOL RetroSmooth::Starting(NodePath* pDocNodePath) 00345 { 00346 if (pRetroSpread == NULL) 00347 return FALSE; 00348 00349 // calculate the cyclic redundancy for this path. 00350 INT32 new_crc = pDocNodePath->InkPath.CalcCRC(); 00351 INT32 sel_crc = pDocNodePath->InkPath.CalcSelectedCRC(); 00352 00353 if (!new_crc) 00354 return FALSE; 00355 00356 if ((RetroPathCRC == new_crc) && (RetroSelCRC == sel_crc)) 00357 { 00358 pRetroNode = pDocNodePath; 00359 TranslateRetroData(&(pDocNodePath->InkPath)); 00360 return TRUE; 00361 } 00362 00363 // if our data doesn't match the path given then we are now 00364 // in the situation where we need to reto smooth a new path 00365 // if there are no selected points in the path then we have 00366 // nothing to retro smooth 00367 if (!sel_crc) 00368 return FALSE; 00369 00370 return NewPathToSmooth(pDocNodePath); 00371 } 00372 00373 00374 /******************************************************************************************** 00375 00376 void RetroSmooth::TranslateRetroData(Path* pPath) 00377 00378 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00379 Created: 22/11/94 00380 Inputs: pPath = a path to translate the cached retro path to 00381 Returns: - 00382 Purpose: Translate the retro path data to the origin of pPath 00383 00384 ********************************************************************************************/ 00385 00386 void RetroSmooth::TranslateRetroData(Path* pPath) 00387 { 00388 ENSURE(pPath !=NULL, "NULL path passed to RetroSmooth::TranslateRetroData()"); 00389 00390 if (pPath == NULL) 00391 return; 00392 00393 if (pPath->GetNumCoords() <= 0) 00394 return; 00395 00396 DocCoord* Coords = pPath->GetCoordArray(); 00397 DocCoord* RCoords = RetroPath.GetCoordArray(); 00398 00399 INT32 transx = Coords[0].x - RCoords[0].x; 00400 INT32 transy = Coords[0].y - RCoords[0].y; 00401 00402 RetroPath.Translate(transx,transy); 00403 RetroEorPath.Translate(transx,transy); 00404 } 00405 00406 00407 00408 /******************************************************************************************** 00409 00410 void RetroSmooth::Update() 00411 00412 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00413 Created: 11/11/94 00414 Inputs: - 00415 Returns: - 00416 Purpose: We have been given a new smooth value. We need to resmooth our internal 00417 data and render an eor version to the display device. 00418 What we try to do (to avoid hideous eoring problems) is this 00419 (1) Create a temporary path and copy our cached path data to it 00420 (2) Create a temp region list to save smoothed region start end indexes 00421 (3) Go through a loop smoothing each region in the temp eor path 00422 whilst recording the new smoothed region indexes as we go. 00423 (4) Check this new data against our previous cached eorpath and region 00424 list. 00425 (5) If all match then 00426 do nothing as the smoothed path has not changed since the last 00427 smooth even though we know the smooth value has. 00428 else 00429 render the cached eor path to the output device, removing the item 00430 copy the temporary eor path and region list to the cache eor 00431 path and region list 00432 render the cached eor path to the output device 00433 (6) exit 00434 00435 ********************************************************************************************/ 00436 00437 void RetroSmooth::Update() 00438 { 00439 INT32 numcoords = RetroPath.GetNumCoords(); 00440 00441 // create a temp path to smooth 00442 Path RenderPath; 00443 if (!RenderPath.Initialise(numcoords, 12)) 00444 return; 00445 RenderPath.CloneFrom(RetroPath); 00446 00447 DynArray TempRegions; 00448 if (!TempRegions.Initialise(RetroEorRegion.NumItems(),12)) 00449 return; 00450 00451 INT32 Region = 0; 00452 INT32 index = 0; 00453 INT32 start,end; 00454 // now scan the path for regions of interest 00455 while ((index<numcoords) && RenderPath.FindSelectionLimits(index,&start,&end)) 00456 { 00457 INT32 nstart = start; 00458 INT32 nend = end; 00459 RenderPath.ExpandRange(&nstart,&nend,1); 00460 00461 Set region; 00462 region.first = nstart; 00463 region.last = nend; 00464 region.Finclusive = !(nstart<start); 00465 region.Linclusive = !(nend>end); 00466 00467 INT32 sel=0; 00468 if (nstart<start) sel |= 1; 00469 if (nend>end) sel |= 2; 00470 if (sel==0) sel=4; 00471 00472 if (nstart != nend) 00473 { 00474 if (RetroSmoothAcc != SMOOTH_MIN) 00475 { 00476 BOOL ok = RenderPath.SmoothSection(nstart,®ion.last,RetroSmoothAcc,sel); 00477 if (!ok) 00478 return; 00479 } 00480 00481 if (!TempRegions.AddItem(region)) 00482 return; 00483 00484 Region++; 00485 } 00486 00487 numcoords = RenderPath.GetNumCoords(); 00488 index = region.last; 00489 00490 if (!RenderPath.FindNextEndPoint(&index)) 00491 index=numcoords; 00492 } 00493 00494 // now we've smoothed the path, we need to check to see if its worth 00495 // updating the version on screen. 00496 00497 INT32 new_crc = RenderPath.CalcCRC(); 00498 if (new_crc != RetroPathCRC) 00499 { 00500 // get rid of the old eor path and put the new path back on again. 00501 if (RenderRegions()) 00502 { 00503 RetroEorPath.CloneFrom(RenderPath); 00504 RetroEorRegion.CloneFrom(&TempRegions); 00505 RenderRegions(); 00506 RetroPathCRC = new_crc; 00507 } 00508 } 00509 } 00510 00511 00512 00513 /******************************************************************************************** 00514 00515 statci BOOL RetroSmooth::FinishedNoUndo(NodePath* pNodePath, double SmoothValue) 00516 00517 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00518 Created: 11/11/94 00519 Inputs: pNodePath = pointer to a node path to use, the should generally be the same 00520 nodepath that was previously passed in to Changing() 00521 00522 Returns: TRUE if successful, FALSE otherwise 00523 Purpose: Finishes off the smoothing operation by replacing the path in pNodePath with 00524 the Retropath that we have generated. This differs from the regular Finished 00525 function because it does not alter the tree and it does not launch an operation, 00526 therefore it is not undoable. 00527 00528 See Also: If you want to know why it had to be non-undoable look at the implementation 00529 of OpDrawBrush, where it is used in this way. 00530 ********************************************************************************************/ 00531 00532 BOOL RetroSmooth::FinishedNoUndo(NodePath* pNodePath) 00533 { 00534 ERROR2IF(pNodePath == NULL, FALSE, "pNodePath is NULL in RetroSmooth::RetroSmoothNoUndo"); 00535 00536 BOOL ok = TRUE; 00537 00538 #ifdef _DEBUG 00539 ok = RetroEorPath.CheckPathValid(); 00540 #endif 00541 00542 if (ok) 00543 { 00544 pNodePath->InkPath.ClearPath(FALSE); 00545 pNodePath->InkPath.CopyPathDataFrom(&RetroEorPath); 00546 } 00547 RetroEorPath.ClearPath(FALSE); 00548 00549 return ok; 00550 } 00551 00552 00553 /******************************************************************************************** 00554 00555 void RetroSmooth::SetRenderFlag(BOOL Value) 00556 00557 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00558 Created: 12/4/2000 00559 Inputs: Value - the value to set the flag 00560 Returns: 00561 Purpose: This sets the flag that says whether or not to bother doing EOR rendering. 00562 00563 Notes: You may wish to set the flag to false if you are using the retrosmoother separately 00564 from the Bezier tool. It will mean that you can call one-off smoothing procedures 00565 without having to put up with all those blobs being rendered. 00566 00567 ********************************************************************************************/ 00568 00569 void RetroSmooth::SetRenderFlag(BOOL Value) 00570 { 00571 m_bRender = Value; 00572 } 00573 00574 00575 /******************************************************************************************** 00576 00577 BOOL RetroSmooth::NewPathToSmooth(NodePath* pNodePath) 00578 00579 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00580 Created: 11/11/94 00581 Inputs: pNodePath = pointer to a node path to use. 00582 Returns: 00583 Purpose: Given a new document node path object, we need to initialise various 00584 buffers to hold temporary data. 00585 00586 Notes: We keep a copy of the path data to refer back to each time a retro smooth 00587 changing message comes through. This allows us to go back to the original 00588 shape of the curve and smooth from that each time the user starts dragging 00589 the slider around, on the same path. 00590 We also keep a transient copy of the path that we are actually smoothing 00591 so that we can draw and undraw the sections which are changing. 00592 00593 ********************************************************************************************/ 00594 00595 BOOL RetroSmooth::NewPathToSmooth(NodePath* pNodePath) 00596 { 00597 pRetroNode = pNodePath; 00598 00599 // clear the path elements but dont bother moving memory 00600 RetroPath.ClearPath(FALSE); 00601 RetroEorPath.ClearPath(FALSE); 00602 00603 // take a local copy of some useful variables 00604 Path* pPath = &(pNodePath->InkPath); 00605 INT32 numcoords = pPath->GetNumCoords(); 00606 00607 if (RetroPath.MakeSpaceInPath(numcoords)) 00608 { 00609 if (RetroEorPath.MakeSpaceInPath(numcoords)) 00610 { 00611 RetroPath.CopyPathDataFrom(pPath); 00612 RetroEorPath.CopyPathDataFrom(pPath); 00613 } 00614 else 00615 { 00616 RetroPath.Compact(); 00617 return FALSE; 00618 } 00619 } 00620 else 00621 return FALSE; 00622 00623 // clear any regions 00624 RetroEorRegion.Empty(); 00625 00626 // try to record the path regions 00627 if (!RecordPathRegions(pPath)) 00628 { 00629 RetroPath.ClearPath(TRUE); 00630 RetroEorPath.ClearPath(TRUE); 00631 return FALSE; 00632 } 00633 00634 return TRUE; 00635 } 00636 00637 00638 00639 /******************************************************************************************** 00640 00641 BOOL RetroSmooth::RenderRegions() 00642 00643 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00644 Created: 11/11/94 00645 Inputs: - 00646 Returns: TRUE - then regions have been eor rendered 00647 FALSE - then no regions have been rendered 00648 Purpose: Scan through the cached region list, rendering the path and blobs in 00649 each region. 00650 00651 Extra Note: Diccon 12/4/2000 I want to use the RetroSmoother as a one-off smoothing 00652 procedure without the EOR Rendering, therefore I have added an extra flag 00653 which returns TRUE from this function without actually rendering anything. 00654 ********************************************************************************************/ 00655 00656 BOOL RetroSmooth::RenderRegions() 00657 { 00658 // if our flag is set then just return 00659 if (!m_bRender) 00660 return TRUE; 00661 00662 // check we've actually got a region to render 00663 INT32 numregions = RetroEorRegion.NumItems(); 00664 if (!numregions) 00665 return TRUE; 00666 00667 INT32 regionsize = FindMasterRegion(); 00668 if (!regionsize) 00669 return FALSE; 00670 00671 // create a render path to copy sections into 00672 Path RenderPath; 00673 if (!RenderPath.Initialise(regionsize,12)) 00674 return FALSE; 00675 00676 // Go into a RenderOnTop loop 00677 RenderRegion* pRegion = DocView::RenderOnTop(NULL, pRetroSpread, ClippedEOR); 00678 while (pRegion!=NULL) 00679 { 00680 // render each region by copying the section into our temp path 00681 // and rendering each of these. 00682 INT32 reg = numregions; 00683 while (reg>0) 00684 { 00685 reg--; 00686 00687 INT32 start = RetroEorRegion[reg].first; 00688 if (!RetroEorRegion[reg].Finclusive) 00689 RetroEorPath.FindNextEndPoint(&start); 00690 00691 INT32 end = RetroEorRegion[reg].last; 00692 if (!RetroEorRegion[reg].Linclusive) 00693 RetroEorPath.FindPrev(&end); 00694 00695 INT32 length = end - start + 1; 00696 if (length>0) 00697 { 00698 // make a path to render 00699 if (RetroEorPath.MakePathFromSection(start, length, &RenderPath)) 00700 { 00701 RenderPath.RenderPathBlobs(pRegion); 00702 // set a colour during our render 00703 // pRegion->SetLineColour(COLOUR_XOREDIT); 00704 // pRegion->SetLineWidth(0); 00705 // pRegion->DrawPath(&RenderPath); 00706 RenderPath.ClearPath(FALSE); 00707 } 00708 } 00709 } 00710 pRegion = DocView::GetNextOnTop(NULL); 00711 } 00712 00713 return TRUE; 00714 } 00715 00716 00717 /******************************************************************************************** 00718 00719 INT32 RetroSmooth::FindMasterRegion() 00720 00721 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00722 Created: 11/11/94 00723 Inputs: 00724 Returns: The largest region 00725 Purpose: Scans the region list and finds the region with the greatest number of 00726 coordinates in it, returning that number 00727 00728 ********************************************************************************************/ 00729 00730 INT32 RetroSmooth::FindMasterRegion() 00731 { 00732 INT32 numcoords = RetroEorRegion.NumItems(); 00733 INT32 largest=0; 00734 00735 for (INT32 i=0; i<numcoords; i++) 00736 { 00737 INT32 length = RetroEorRegion[i].last - RetroEorRegion[i].first +1; 00738 if (length>largest) 00739 largest = length; 00740 } 00741 return largest; 00742 } 00743 00744 00745 00746 /******************************************************************************************** 00747 00748 BOOL RetroSmooth::MatchRegions(NodePath* pNodePath) 00749 00750 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00751 Created: 11/11/94 00752 Inputs: pNodePath - a nodepath object pointer 00753 Returns: TRUE - if the selected regions within pNodePath match those of our 00754 cached path. 00755 FALSE - if no match is found. 00756 00757 Purpose: Scan through the path data in pNodePath and match selected regions 00758 with our cached version. If the regions match, then we may be able to 00759 use our previously cached none smooth path as the source for a new 00760 retro active smooth, rather than pNodePath. 00761 00762 ********************************************************************************************/ 00763 00764 BOOL RetroSmooth::MatchRegions(NodePath* pNodePath) 00765 { 00766 // ok, scan through all sel regions in this cached 00767 // object and match against those in the searchpath 00768 // if they all match, then return true. 00769 00770 00771 return TRUE; 00772 } 00773 00774 00775 /******************************************************************************************** 00776 00777 > BOOL RetroSmooth::RecordPathRegions(Path* pPath) 00778 00779 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00780 Created: 11/11/94 00781 Inputs: pPath = 00782 Returns: 00783 Purpose: Record the selected regions of the path on the original regions list 00784 or the smoothed regions 00785 00786 ********************************************************************************************/ 00787 00788 BOOL RetroSmooth::RecordPathRegions(Path* pPath) 00789 { 00790 INT32 index = 0; 00791 INT32 ncoords = pPath->GetNumCoords(); 00792 00793 // The reasons are deeply rooted why I'm about to use a doccoord class 00794 // to hold two longs which represent the start and end of a region. 00795 // Its because I cannot use 'templates' at the moment to define an 00796 // associative array, so the dynamic array class I'm about to used is 00797 // typed to hold doccoords instead of generic 'types'. 00798 00799 INT32 start; 00800 INT32 end; 00801 00802 while ( (index<ncoords) && pPath->FindSelectionLimits(index,&start,&end) ) 00803 { 00804 Set PtSet; 00805 PtSet.first = start; 00806 PtSet.last = end; 00807 00808 pPath->ExpandRange(&PtSet.first,&PtSet.last,1); 00809 00810 PtSet.Finclusive = !(PtSet.first<start); 00811 PtSet.Linclusive = !(PtSet.last>end); 00812 00813 if (!RetroEorRegion.AddItem(PtSet)) 00814 { 00815 index = -1; 00816 break; 00817 } 00818 index=PtSet.last; 00819 if (!pPath->FindNextEndPoint(&index)) 00820 index=ncoords; 00821 } 00822 00823 // make sure we've tidied up incase of failure 00824 if (index==-1) 00825 { 00826 RetroEorRegion.Empty(); 00827 return FALSE; 00828 } 00829 00830 return TRUE; 00831 } 00832 00833 00834 /******************************************************************************************** 00835 00836 void RetroSmooth::AlterRegion(INT32 index, INT32 newstart, INT32 newend) 00837 00838 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00839 Created: 16/11/94 00840 Purpose: Alters a specific regions start,end indexes. 00841 00842 ********************************************************************************************/ 00843 00844 void RetroSmooth::AlterRegion(INT32 index, INT32 newstart, INT32 newend) 00845 { 00846 Set set; 00847 set.first = newstart; 00848 set.last = newend; 00849 set.Finclusive = TRUE; 00850 set.Linclusive = TRUE; 00851 RetroEorRegion[index] = set; 00852 } 00853 00854 00855 /******************************************************************************************** 00856 00857 > OpRetroSmooth::OpRetroSmooth() 00858 00859 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00860 Created: 18/11/94 00861 Purpose: OpRetroSmooth() constructor 00862 SeeAlso: - 00863 00864 ********************************************************************************************/ 00865 00866 OpRetroSmooth::OpRetroSmooth() 00867 { 00868 // Dummy constructor 00869 } 00870 00871 00872 00873 /******************************************************************************************** 00874 00875 > BOOL OpRetroSmooth::Init() 00876 00877 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00878 Created: 26/7/94 00879 Inputs: - 00880 Outputs: - 00881 Returns: TRUE if the operation could be successfully initialised 00882 FALSE if no more memory could be allocated 00883 00884 Purpose: OpRetroSmooth initialiser method 00885 Errors: ERROR will be called if there was insufficient memory to allocate the 00886 operation. 00887 SeeAlso: - 00888 00889 ********************************************************************************************/ 00890 00891 BOOL OpRetroSmooth::Init() 00892 { 00893 return (RegisterOpDescriptor(0, // tool ID 00894 _R(IDS_RETROSMOOTHOP), // string resource ID 00895 CC_RUNTIME_CLASS(OpRetroSmooth), // runtime class for Op 00896 OPTOKEN_RETROSMOOTH, // Ptr to token string 00897 OpRetroSmooth::GetState, // GetState function 00898 0, // help ID 00899 _R(IDBBL_RETROSMOOTHOP), // bubble help ID 00900 0 // resource ID =0 00901 )); 00902 00903 } 00904 00905 00906 /******************************************************************************************** 00907 00908 > OpState OpRetroSmooth::GetState(String_256*, OpDescriptor*) 00909 00910 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00911 Created: 20/10/94 00912 Inputs: - 00913 Outputs: - 00914 Returns: The state of the OpRetroSmooth 00915 Purpose: For finding the OpRetroSmooth state. 00916 Errors: - 00917 SeeAlso: - 00918 00919 ********************************************************************************************/ 00920 00921 OpState OpRetroSmooth::GetState(String_256* UIDescription, OpDescriptor*) 00922 { 00923 OpState OpSt; 00924 // Always enabled at the moment. 00925 return OpSt; 00926 } 00927 00928 /******************************************************************************************** 00929 00930 > void OpRetroSmooth::GetOpName(String_256* OpName) 00931 00932 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00933 Created: 20/10/94 00934 Inputs: - 00935 Outputs: The undo string for the operation 00936 Returns: 00937 Purpose: The GetOpName fn is overridden so that we return back a description 00938 appropriate to the type of attribute that the operation applies. 00939 00940 ********************************************************************************************/ 00941 00942 void OpRetroSmooth::GetOpName(String_256* OpName) 00943 { 00944 *OpName = String_256(_R(IDS_UNDO_RETROSMOOTHOP)); 00945 } 00946 00947 00948 00949 /******************************************************************************************** 00950 00951 > void OpRetroSmooth::DoRetroSmooth(NodePath* pThisNode, double smoothacc) 00952 00953 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00954 Created: 29/9/94 00955 Inputs: pThisNode = pointer to a path node in the tree to smooth 00956 smoothacc = accuracy value to smooth to 00957 Outputs: - 00958 Returns: - 00959 Purpose: This operation will smooth a node path given an accuracy. It will create a 00960 new node in the tree after the node specified and hide node passed if all 00961 is succesfull. 00962 00963 ********************************************************************************************/ 00964 00965 void OpRetroSmooth::DoRetroSmooth(NodePath* pThisNode, Path* pPath, double smoothacc) 00966 { 00967 BOOL ok; 00968 // remember the selection before the operation 00969 if (!DoStartSelOp(FALSE,FALSE)) 00970 { 00971 FailAndExecute(); End(); return; 00972 } 00973 00974 // also check for blends needing to be re-initialised 00975 NodeBlender * pBlender = NULL; 00976 NodeBlend * pBlend = NULL; 00977 NodeBlend* pOrigBlend = NULL; 00978 pBlender = (NodeBlender *)pThisNode->FindNext(CC_RUNTIME_CLASS(NodeBlender)); 00979 if (!pBlender) pBlender = (NodeBlender *)pThisNode->FindPrevious(CC_RUNTIME_CLASS(NodeBlender)); 00980 00981 if (pBlender) 00982 { 00983 pBlend = (NodeBlend *)pBlender->FindParent(); 00984 //pBlend->forOpRetroSmooth = FALSE; 00985 pOrigBlend = pBlend; 00986 } 00987 00988 // Invalidate the region 00989 CALL_WITH_FAIL( DoInvalidateNodeRegion(pThisNode, TRUE, FALSE), this, ok); 00990 if (!ok) 00991 { 00992 FailAndExecute(); End(); return; 00993 } 00994 00995 ObjChangeFlags cFlags; 00996 cFlags.ReplaceNode = TRUE; 00997 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,pThisNode,this); 00998 if (pThisNode->AllowOp(&ObjChange)) 00999 { 01000 // create the new node path 01001 CALL_WITH_FAIL( DoMakeNodeFromPath(pThisNode, pPath, NEXT, TRUE), this, ok); 01002 if (!ok) 01003 { 01004 FailAndExecute(); End(); return; 01005 } 01006 01007 // see if were part of a blend .... 01008 01009 pBlender = (NodeBlender *)pThisNode->FindNext(CC_RUNTIME_CLASS(NodeBlender)); 01010 01011 if (pBlender) 01012 { 01013 pBlend = (NodeBlend *)pBlender->FindParent(); 01014 01015 if (InitBlendAction::InitOnBlender(this, GetUndoActionList(), pBlender, TRUE, TRUE) != AC_OK) 01016 { 01017 ERROR2RAW("Couldn't Initialise blend action"); 01018 } 01019 01020 // re-insert this action at the head of the list 01021 01022 pBlender->Reinit((NodeRenderableInk*) pThisNode->FindNext (), pBlender->GetNodeEnd(), FALSE); 01023 01024 // Invalidate the region 01025 CALL_WITH_FAIL( DoInvalidateNodeRegion(pBlender, TRUE, FALSE), this, ok); 01026 if (!ok) 01027 { 01028 FailAndExecute(); End(); return; 01029 } 01030 } 01031 else 01032 { 01033 pBlender = (NodeBlender *)pThisNode->FindPrevious(CC_RUNTIME_CLASS(NodeBlender)); 01034 01035 if (pBlender) 01036 { 01037 pBlend = (NodeBlend *)pBlender->FindParent(); 01038 01039 if (InitBlendAction::InitOnBlender(this, GetUndoActionList(), pBlender, TRUE, TRUE) != AC_OK) 01040 { 01041 ERROR2RAW("Couldn't Initialise blend action"); 01042 } 01043 01044 // re-insert this action at the head of the list 01045 01046 pBlender->Reinit(pBlender->GetNodeStart(), (NodeRenderableInk*) pThisNode->FindNext (), FALSE); 01047 01048 // Invalidate the region 01049 CALL_WITH_FAIL( DoInvalidateNodeRegion(pBlender, TRUE, FALSE), this, ok); 01050 if (!ok) 01051 { 01052 FailAndExecute(); End(); return; 01053 } 01054 } 01055 } 01056 01057 if (pBlender) 01058 { 01059 NodeBlend* ptrBlend = (NodeBlend*) pBlender->FindParent (); 01060 01061 ERROR3IF (!IS_A (ptrBlend, NodeBlend), "NodeBlend is not a NodeBlend!"); 01062 01063 BOOL done = FALSE; 01064 NodeBlender* ptrNode = ptrBlend->FindFirstBlender (); 01065 01066 while (!done) 01067 { 01068 if (ptrNode != pBlender) 01069 { 01070 if (ptrNode->GetNodeStart () == pThisNode) 01071 { 01072 if (InitBlendAction::InitOnBlender(this, GetUndoActionList(), ptrNode, TRUE, TRUE) != AC_OK) 01073 { 01074 ERROR2RAW("Couldn't Initialise blend action"); 01075 } 01076 01077 ptrNode->Reinit((NodeRenderableInk*) pThisNode->FindNext (), NULL, FALSE); 01078 } 01079 if (ptrNode->GetNodeEnd () == pThisNode) 01080 { 01081 if (InitBlendAction::InitOnBlender(this, GetUndoActionList(), ptrNode, TRUE, TRUE) != AC_OK) 01082 { 01083 ERROR2RAW("Couldn't Initialise blend action"); 01084 } 01085 01086 ptrNode->Reinit(NULL, (NodeRenderableInk*) pThisNode->FindNext (), FALSE); 01087 } 01088 01089 // Invalidate the region 01090 CALL_WITH_FAIL( DoInvalidateNodeRegion(ptrNode, TRUE, FALSE), this, ok); 01091 if (!ok) 01092 { 01093 FailAndExecute(); End(); return; 01094 } 01095 } 01096 01097 ptrNode = ptrBlend->FindNextBlender (ptrNode); 01098 01099 if (!ptrNode) 01100 { 01101 done = TRUE; 01102 } 01103 } 01104 } 01105 01106 ObjChange.Define(OBJCHANGE_FINISHED,cFlags,pThisNode,this); 01107 if (!UpdateChangedNodes(&ObjChange)) 01108 { 01109 FailAndExecute(); End(); return; 01110 } 01111 01112 // Now we've formed a smoothed path, let's hide the original 01113 CALL_WITH_FAIL( DoHideNode(pThisNode,TRUE), this, ok); 01114 if (!ok) 01115 { 01116 FailAndExecute(); End(); return; 01117 } 01118 } 01119 //pOrigBlend->forOpRetroSmooth = FALSE; 01120 // DoSmoothNodePath(pThisNode,smoothacc),this,ok); 01121 End(); 01122 } 01123 01124 01125 01126 /******************************************************************************************** 01127 01128 > DynArray::DynArray() 01129 01130 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01131 Created: 16/11/94 01132 Purpose: DynArray constructor 01133 01134 ********************************************************************************************/ 01135 01136 DynArray::DynArray() 01137 { 01138 UsedSlots = 0; 01139 UnusedSlots = 0; 01140 SlotInitSize = 0; 01141 SlotAllocSize = 0; 01142 01143 BlockHandle = BAD_MHANDLE; 01144 } 01145 01146 01147 /******************************************************************************************** 01148 01149 > DynArray::~DynArray() 01150 01151 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01152 Created: 16/11/94 01153 Purpose: Destructor - releases the memory that the template class has 01154 been using to store items in. 01155 01156 ********************************************************************************************/ 01157 01158 DynArray::~DynArray() 01159 { 01160 if (BlockHandle != BAD_MHANDLE) 01161 ReleaseBlock(BlockHandle); 01162 } 01163 01164 01165 /******************************************************************************************** 01166 01167 > BOOL DynArray::Initialise(INT32 inititems, INT32 newitems) 01168 01169 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01170 Created: 16/11/94 01171 Inputs: inititems = The number of items of the list can hold initially 01172 newitems = The number of items to allocate for each time we run 01173 out of space 01174 Returns: TRUE if we got all the memory we needed, FALSE if not 01175 Purpose: Allocates memory for a list to hold a particular type of object 01176 Errors: Can fail if it runs out of memory. The function will return 01177 FALSE 01178 01179 ********************************************************************************************/ 01180 01181 BOOL DynArray::Initialise(INT32 inititems, INT32 newitems) 01182 { 01183 ERROR3IF((newitems & 3),"DynArray::Initialise called with none quad byte 'new' multiple"); 01184 01185 if (inititems<12) 01186 inititems=12; 01187 01188 if (newitems<12) 01189 newitems=12; 01190 01191 if (BlockHandle == BAD_MHANDLE) 01192 BlockHandle = ClaimBlock(sizeof(ITEM_TYPE)*inititems); 01193 else 01194 { 01195 // some ones initialising an already existant buffer 01196 // so lets just resize it to fit. 01197 INT32 SizeDiff = (UsedSlots+UnusedSlots) - inititems; 01198 BOOL ok; 01199 if (SizeDiff>0) 01200 ok = IncreaseBlock(BlockHandle,sizeof(ITEM_TYPE)*SizeDiff); 01201 else 01202 { 01203 SizeDiff = -SizeDiff; 01204 ok = DecreaseBlock(BlockHandle,sizeof(ITEM_TYPE)*SizeDiff); 01205 } 01206 if (!ok) 01207 return FALSE; 01208 } 01209 01210 if (BlockHandle==BAD_MHANDLE) 01211 return FALSE; 01212 01213 UsedSlots = 0; 01214 UnusedSlots = inititems; 01215 SlotInitSize = inititems; 01216 SlotAllocSize = newitems; 01217 01218 return TRUE; 01219 } 01220 01221 01222 01223 /******************************************************************************************** 01224 01225 > TYPE_ITEM* DynArray::GetFirstItem() 01226 01227 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01228 Created: 16/11/94 01229 Inputs: - 01230 Returns: a pointer to the start of this dynamic array. 01231 Purpose: Get a pointer to the start of this array. The ptr can then be used in 01232 conjunction with the [] operator to access the stored values with array 01233 operations. 01234 01235 ********************************************************************************************/ 01236 01237 ITEM_TYPE* DynArray::GetFirstItem() 01238 { 01239 return ((ITEM_TYPE*)DescribeHandle(BlockHandle)); 01240 } 01241 01242 01243 01244 /******************************************************************************************** 01245 01246 > BOOL DynArray::MakeSpaceInList(const INT32 NumSlots) 01247 01248 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01249 Created: 16/11/94 01250 Inputs: NumSlots = the number of slots to ensure free 01251 Returns: TRUE if NumSlots are now freely available 01252 FALSE if unable to ensure NumSlots of free space. 01253 Purpose: Ensure that there are NumSlots free within this dynamic array. 01254 01255 ********************************************************************************************/ 01256 01257 BOOL DynArray::MakeSpaceInList(const INT32 NumSlots) 01258 { 01259 if (UnusedSlots<NumSlots) 01260 { 01261 INT32 Needed = NumSlots-UnusedSlots+SlotAllocSize; 01262 if (!IncreaseBlock(BlockHandle,sizeof(ITEM_TYPE)*Needed)) 01263 return FALSE; 01264 UnusedSlots += Needed; 01265 } 01266 return TRUE; 01267 } 01268 01269 01270 /******************************************************************************************** 01271 01272 > BOOL DynArray::AddItem(ITEM_TYPE& item) 01273 01274 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01275 Created: 16/11/94 01276 Inputs: a reference to an item to store in the array 01277 Returns: TRUE if the item was successfully stored 01278 FALSE if the item could not be stored due to lack of memory 01279 Purpose: Store the array item by increasing the size of the array by one item 01280 and placing the item at this position. 01281 01282 ********************************************************************************************/ 01283 01284 BOOL DynArray::AddItem(ITEM_TYPE& item) 01285 { 01286 if (!MakeSpaceInList(1)) 01287 return FALSE; 01288 01289 ITEM_TYPE* List = (ITEM_TYPE*) DescribeHandle(BlockHandle); 01290 01291 List[UsedSlots] = item; 01292 01293 UsedSlots++; 01294 UnusedSlots--; 01295 01296 return TRUE; 01297 } 01298 01299 01300 01301 /******************************************************************************************** 01302 01303 > void DynArray::Empty(BOOL compress = TRUE) 01304 01305 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01306 Created: 16/11/94 01307 Inputs: - 01308 Returns: - 01309 Purpose: Remove all stored items from this array. All array accesses via the 01310 [] operator will now be faulted. Use AddItem() to begin reconstructing 01311 values in the array. 01312 01313 ********************************************************************************************/ 01314 01315 void DynArray::Empty(BOOL compress) 01316 { 01317 if (UsedSlots>0) 01318 { 01319 // reset the path definitions 01320 UnusedSlots+=UsedSlots; 01321 UsedSlots=0; 01322 01323 // make sure we compress memory if necessary 01324 if (compress) 01325 Compact(); // ignore any error coming from here 01326 } 01327 } 01328 01329 01330 01331 /******************************************************************************************** 01332 01333 > void DynArray:::DeleteItems(INT32 firstitem, INT32 numitems) 01334 01335 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01336 Created: 16/11/94 01337 Inputs: first item = an index of the first item to start the deletion at (0..n-1) 01338 num items = the number of items to delete (0<=n<=NumItems) 01339 Returns: - 01340 Purpose: Delete a set of items from this dynamic array. 01341 01342 Example: 01343 01344 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 01345 L = [ 9,8,7,6,5,4,3,2,1,0 ] DeleteItems(3, 4) L = [ 9,8,7,2,1,0 ] 01346 01347 ********************************************************************************************/ 01348 01349 void DynArray::DeleteItems(INT32 firstitem, INT32 numitems) 01350 { 01351 ENSURE(firstitem<UsedSlots, "Specified position is off end of array in DynArray::DeleteItems"); 01352 ENSURE(firstitem>=0, "Specified position is off begining of array in DynArray::DeleteItems"); 01353 01354 if (numitems<=0) 01355 return; 01356 01357 ITEM_TYPE* List = (ITEM_TYPE*) DescribeHandle(BlockHandle); 01358 01359 INT32 Moving = UsedSlots-(firstitem+numitems); 01360 01361 if (Moving>0) 01362 memmove((void*)(&List[firstitem]), (void*)(&List[firstitem+numitems]), Moving*sizeof(ITEM_TYPE)); 01363 01364 UsedSlots -= numitems; 01365 UnusedSlots += numitems; 01366 01367 Compact(); 01368 } 01369 01370 01371 /******************************************************************************************** 01372 01373 > void DynArray::Compact() 01374 01375 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01376 Created: 16/11/94 01377 Inputs: - 01378 Returns: - 01379 Purpose: Checks for excessive amounts of unused space left at the end of a dynamic 01380 array, (possibly left from a Empty(FALSE) call) and removes it. The quantity 01381 of space removed is always an exact multiple of newitems (See Initialise()) 01382 01383 ********************************************************************************************/ 01384 01385 void DynArray::Compact() 01386 { 01387 INT32 unused = UnusedSlots / SlotAllocSize; 01388 unused-=1; 01389 if (unused>0) 01390 { 01391 if (DecreaseBlock(BlockHandle, sizeof(ITEM_TYPE)*(unused*SlotAllocSize))) 01392 UnusedSlots-=unused*SlotAllocSize; 01393 } 01394 } 01395 01396 01397 /******************************************************************************************** 01398 01399 > ITEM_TYPE& DynArray::operator[](const INT32 i) 01400 01401 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01402 Created: 16/11/94 01403 Inputs: i = the index of an item in this dynamic array 01404 Returns: A reference to the indexed item in this dynamic array 01405 Purpose: Overload the array operator to allow array access functionality for this 01406 dynamic set of items. This functions provides both item reteival and item 01407 assignment for example 01408 MyArray[i] = item, item = MyArray[i] 01409 Errors: Access or assignment will be faulted if the index i is outside the range 01410 of items stored in the array. 01411 01412 ********************************************************************************************/ 01413 01414 ITEM_TYPE& DynArray::operator[](const INT32 i) 01415 { 01416 ENSURE(i>=0 && i<UsedSlots, "index out of range in Template list operator[]"); 01417 ITEM_TYPE* List = (ITEM_TYPE*) DescribeHandle(BlockHandle); 01418 return (List[i]); 01419 } 01420 01421 01422 /******************************************************************************************** 01423 01424 > BOOL DynArray::CloneFrom(ITEM_TYPE* SourceArray) 01425 01426 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 01427 Created: 16/11/94 01428 Inputs: SourceArray = pointer to a source dynamic array 01429 Returns TRUE if successful 01430 FALSE if unable in increase the size of this dynamic array to cope with all 01431 data in the supplied array. 01432 Purpose: Replace the data within this dynamic array with that of the other array 01433 supplied 01434 01435 ********************************************************************************************/ 01436 01437 BOOL DynArray::CloneFrom(DynArray* SourceArray) 01438 { 01439 // Make space to copy all the data from the other path into this one 01440 INT32 SlotsToCopy = SourceArray->UsedSlots; 01441 INT32 SlotsToAdd = SlotsToCopy-UsedSlots; 01442 01443 if (SlotsToAdd>0) 01444 { 01445 if (!MakeSpaceInList(SlotsToAdd)) 01446 return FALSE; 01447 } 01448 01449 // Get the arrays 01450 ITEM_TYPE* pSource = (ITEM_TYPE*) DescribeHandle(SourceArray->BlockHandle); 01451 ITEM_TYPE* pDestin = (ITEM_TYPE*) DescribeHandle(BlockHandle); 01452 01453 memmove((void*)(pDestin), (void*)(pSource), SlotsToCopy*sizeof(ITEM_TYPE)); 01454 01455 // Now update the array variables 01456 UsedSlots += SlotsToAdd; 01457 UnusedSlots -= SlotsToAdd; 01458 01459 return TRUE; 01460 }