00001 // $Id: extender.cpp 1282 2006-06-09 09:46:49Z alex $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 // 00099 00100 00101 #include "camtypes.h" 00102 #include "extender.h" 00103 00104 #include "ncntrcnt.h" // for NodeContourController. 00105 #include "nodecont.h" // for NodeShadowController. 00106 #include "nbevcont.h" // for NodeBevelController. 00107 #include "layer.h" // for Layer. 00108 //#include "becomea.h" // for BecomeA. - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "range.h" // for SelRange. - in camtypes.h [AUTOMATICALLY REMOVED] 00110 //#include "undoop.h" // for UndoableOperation. - in camtypes.h [AUTOMATICALLY REMOVED] 00111 #include "nodershp.h" // for NodeRegularShape. 00112 #include "objchge.h" // for ObjChange stuff. 00113 #include <list> // for std::list. 00114 #include "nodeblnd.h" // for NodeBlend 00115 #include "nodebldr.h" // for NodeBlender 00116 #include "nodepath.h" 00117 00118 #if 0 00119 #ifdef _DEBUG 00120 #undef THIS_FILE 00121 static char BASED_CODE THIS_FILE[] = __FILE__; 00122 #endif 00123 #endif 00124 00125 DECLARE_SOURCE("$Revision: 1282 $"); 00126 00127 // Place any IMPLEMENT type statements here 00128 CC_IMPLEMENT_MEMDUMP(Extender, CC_CLASS_MEMDUMP) 00129 CC_IMPLEMENT_MEMDUMP(ExtendParams, CC_CLASS_MEMDUMP) 00130 00131 // We want better memory tracking 00132 // Declare smart memory handling in Debug builds 00133 #define new CAM_DEBUG_NEW 00134 00135 00136 // Functions follow 00137 00138 00139 00140 /******************************************************************************************** 00141 00142 > void Extender::Extend( NodeRenderableInk* pNode, 00143 DocRect* pStartRect, 00144 DocRect* pEndRect, 00145 BYTE fExtendFlags, 00146 DocRect* pOldStartRect = NULL) 00147 00148 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00149 Created: 10/09/1999 00150 Inputs: 00151 Outputs: 00152 Purpose: ** DEPRECATED - DO NOT CALL ** 00153 Errors: ERROR2 always. 00154 See also: 00155 00156 ********************************************************************************************/ 00157 void Extender::Extend( NodeRenderableInk* pNode, 00158 DocRect* pStartRect, 00159 DocRect* pEndRect, 00160 BYTE fExtendFlags, 00161 DocRect* pOldStartRect) 00162 { 00163 ERROR2RAW("This implementation of Extender::Extend is no longer used or valid"); 00164 } 00165 00166 00167 00168 /******************************************************************************************** 00169 00170 > BOOL Extender::CheckValidExtend(NodeRenderableInk *pNode, 00171 DocRect *pStartRect, 00172 DocRect *pEndRect, 00173 BYTE fExtendFlags, 00174 DocRect* pOldStartRect = NULL) 00175 00176 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00177 Created: 17/11/1999 00178 Inputs: 00179 Outputs: 00180 Purpose: ** DEPRECATED - DO NOT CALL ** 00181 Returns: FALSE 00182 Errors: ERROR2 always. 00183 See also: 00184 00185 ********************************************************************************************/ 00186 BOOL Extender::CheckValidExtend(NodeRenderableInk* pNode, 00187 DocRect* pStartRect, 00188 DocRect* pEndRect, 00189 BYTE fExtendFlags, 00190 DocRect* pOldStartRect) 00191 { 00192 ERROR2( FALSE, 00193 "This implementation of Extender::CheckValidExtend is no longer used or valid" ); 00194 } 00195 00196 00197 00199 // 00200 // Extender helper function for make-shapes capability. 00201 // 00202 00203 00204 /******************************************************************************************** 00205 00206 > BOOL Extender::ConvertQuickShapesInSelRangeToPaths( UndoableOperation* pUndoOp, 00207 SelRange* pSel ) 00208 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00209 Created: 21 March 2000 00210 Inputs: pUndoOp we need an UndoOp to make this undoable. 00211 pSel the SelRange we'll iterate over. 00212 Outputs: 00213 Returns: TRUE if any quickshapes were successfully turned, 00214 FALSE if not. 00215 00216 Purpose: Iterates over the given SelRange looking for any NodeRegularShapes, 00217 ie QuickShapes. If we find any then they get zapped, and all that is left 00218 is a smoking pair of shoes! ... erm, sorry - a NodePath. 00219 00220 Notes: This is all done undoably. There is however a problem in that we do not 00221 necessarily want to completely unwind the UndoOp we were given if anything 00222 goes wrong. But if we don't do that then what _can_ we do??? 00223 Currently, if things go wrong while converting to shapes, we just try to 00224 convert as many shapes to paths as possible and return FALSE at the end. 00225 00226 We don't mind if pSel is empty or has no QuickShapes. 00227 00228 Our search of the SelRange is deep - we look at children of nodes in the 00229 range too. 00230 00231 Errors: ERROR2 with FALSE if we get invalid parameters. 00232 See also: 00233 00234 ********************************************************************************************/ 00235 BOOL Extender::ConvertQuickShapesInSelRangeToPaths( UndoableOperation* pUndoOp, 00236 Range* pRange ) 00237 { 00238 // validate params. 00239 ERROR2IF(pUndoOp == NULL || pRange == NULL, FALSE, "Invalid parameter(s)"); 00240 00241 // TODO: May need to set the range's RangeControl to avoid promoting to parents. 00242 00243 // iterate over the range and build a list of QuickShapes. 00244 // we do a depth-first search from every node in the range, 00245 // looking everywhere for those those pesky QuickShapes :) 00246 BOOL bQuickShapesConverted = FALSE; 00247 std::list<NodeRegularShape*> lpShapes; 00248 Node* pCurrentRangeNode = pRange->FindFirst(); 00249 while (pCurrentRangeNode != NULL) 00250 { 00251 Node* pNextNode = NULL; 00252 Node* pCurrentNode = pCurrentRangeNode->FindFirstDepthFirst(); 00253 while (pCurrentNode != NULL) 00254 { 00255 pNextNode = pCurrentNode->FindNextDepthFirst(pCurrentRangeNode); 00256 if (pCurrentNode->IsARegularShape()) 00257 { 00258 if (!(pCurrentNode->FindParent (CC_RUNTIME_CLASS (NodeBlend)))) 00259 { 00260 // note thate we're *saying* we're going to replace with only one node. 00261 // this isn't necessarily true - DoBecomeA on a brushed path results in 00262 // many ungrouped nodes. it's just the best we can do, considering blends. 00263 ObjChangeFlags ocf; 00264 ocf.ReplaceNode = TRUE; 00265 ObjChangeParam ObjChange(OBJCHANGE_STARTING, ocf, pCurrentNode, pUndoOp); 00266 if (pCurrentNode->AllowOp(&ObjChange, FALSE, FALSE)) 00267 lpShapes.push_front((NodeRegularShape*)pCurrentNode); 00268 } 00269 /*else 00270 { 00271 // note thate we're *saying* we're going to replace with only one node. 00272 // this isn't necessarily true - DoBecomeA on a brushed path results in 00273 // many ungrouped nodes. it's just the best we can do, considering blends. 00274 ObjChangeFlags ocf; 00275 ocf.ReplaceNode = TRUE; 00276 ObjChangeParam ObjChange(OBJCHANGE_STARTING, ocf, pCurrentNode, pUndoOp); 00277 if (pCurrentNode->AllowOp(&ObjChange, TRUE, FALSE)) 00278 lpShapes.push_front((NodeRegularShape*)pCurrentNode); 00279 }*/ 00280 } 00281 pCurrentNode = pNextNode; 00282 } 00283 pCurrentRangeNode = pRange->FindNext(pCurrentRangeNode, TRUE); 00284 } 00285 00286 // okay, if our list isn't empty then we have work to do. 00287 if (!lpShapes.empty()) 00288 { 00289 // invalidate the region of the selection. 00290 if (pUndoOp->DoInvalidateNodesRegions(*pRange, TRUE)) 00291 { 00292 // change all the QuickShapes into NodePaths. 00293 NodeRegularShape* pCurrentShape = NULL; 00294 while (!lpShapes.empty()) 00295 { 00296 pCurrentShape = lpShapes.front(); 00297 lpShapes.pop_front(); 00298 BecomeA BecomeAPath( BECOMEA_REPLACE, 00299 CC_RUNTIME_CLASS(NodePath), 00300 pUndoOp, 00301 pCurrentShape->IsSelected() ); 00302 00303 if (pCurrentShape->CanBecomeA(&BecomeAPath)) 00304 { 00305 if (!(pCurrentShape->FindParent (CC_RUNTIME_CLASS (NodeBlend)))) 00306 { 00307 if (pCurrentShape->DoBecomeA(&BecomeAPath)) 00308 { 00309 pCurrentShape->DeSelect(FALSE); 00310 bQuickShapesConverted = TRUE; 00311 } 00312 } 00313 /*else 00314 { 00315 NodeBlend* ptrBlend = (NodeBlend*) pCurrentShape->FindParent (CC_RUNTIME_CLASS (NodeBlend)); 00316 BecomeA BecomeAPath( BECOMEA_REPLACE, 00317 CC_RUNTIME_CLASS(NodePath), 00318 pUndoOp, 00319 pCurrentShape->IsSelected() ); 00320 if (pCurrentShape->DoBecomeA(&BecomeAPath)) 00321 { 00322 pCurrentShape->DeSelect(FALSE); 00323 bQuickShapesConverted = TRUE; 00324 } 00325 00326 // nastiness - to reinitialise the blend, I need a ptr to the converted path 00327 // BUT there is no easy way of doing this! This is a hack - which seems to 00328 // hold up well .... NOTE: assumes that the last action executed was a 00329 // HideNodeAction (i.e. hide pCurrentShape) 00330 00331 ActionList* actList = pUndoOp->GetUndoActionList (); // get undo history 00332 ListItem* pItem = actList->GetTail (); // get last op 00333 ERROR3IF (!IS_A (pItem, HideNodeAction), "Assumed HideNodeAction is not a HideNodeAction!"); 00334 HideNodeAction* hnAct = (HideNodeAction*) pItem; 00335 NodeRenderableInk* newNode = (NodeRenderableInk*) hnAct->GetNode (); 00336 ERROR3IF (!IS_A (ptrBlend, NodeBlend), "NodeBlend is not a NodeBlend!"); 00337 00338 BOOL done = FALSE; 00339 NodeBlender* ptrNode = ptrBlend->FindFirstBlender (); 00340 00341 while (!done) 00342 { 00343 ptrNode->Deinit (); 00344 00345 if (ptrNode->GetNodeStart () == pCurrentShape) 00346 { 00347 ptrNode->Reinit(newNode, NULL, FALSE); 00348 } 00349 if (ptrNode->GetNodeEnd () == pCurrentShape) 00350 { 00351 ptrNode->Reinit(NULL, newNode, FALSE); 00352 } 00353 00354 ptrNode = ptrBlend->FindNextBlender (ptrNode); 00355 00356 if (!ptrNode) 00357 { 00358 done = TRUE; 00359 } 00360 } 00361 }*/ 00362 } 00363 } 00364 } 00365 if (bQuickShapesConverted) 00366 pRange->Update(); 00367 } 00368 00369 return bQuickShapesConverted; 00370 } 00371 00372 00373 00375 // 00376 // New Extender functions, for virtual-fn extend procedure. 00377 // 00378 00379 00380 /******************************************************************************************** 00381 00382 > DocRect Extender::CheckValidExtend( NodeRenderableInk* pNode, BYTE fExtendFlags, 00383 const DocRect& drTriggerRelation, const DocRect& drTargetRelation, 00384 const DocRect& drNewTrigger, const DocRect& drLastTarget ) 00385 00386 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00387 Created: 06/12/1999 00388 Inputs: pNode pointer to a NodeRenderableInk which will be extended. 00389 fExtendFlags bitwise flags, describing any combination of x- or y- 00390 stretching or extending, except that you may not extend 00391 and stretch along an axis at the same time. Values: 00392 00393 X_EXTEND the node should be extended along the x-axis 00394 Y_EXTEND the node should be extended along the y-axis 00395 X_STRETCH the node should be stretched along the x-axis 00396 Y_STRETCH the node should be stretched along the y-axis 00397 00398 drTriggerRelation relationship rectangle - the bounding box of the trigger 00399 sets when the relationship is defined. 00400 drTargetRelation relationship rectangle - the bounding box of the target 00401 sets when the relationship is defined. 00402 drNewTrigger bounding box of sets of triggers which started the call 00403 to this function. 00404 drLastTrigger the bounding box of the trigger set(s) before it changed. 00405 drLastTarget the bounding box of the target set before it is extended. 00406 00407 Purpose: This function tests whether or not a call to the extend function will extend 00408 an object in a valid way. For example, a path cannot be shrunken so that its 00409 control points cross its centre, as this operation is irreversible - if the 00410 path was extended back again, these points would extend the wrong direction. 00411 Similarly, an invalid extend occurs when a trigger set is shrunk so that its 00412 centre happens to move over the centre of a target node which is not moving 00413 in this particular extension; undoing this operation would end up dragging 00414 the target node back with the trigger. 00415 00416 This function should always be called on all objects sharing the same name, 00417 *before* Extender::Extend is called to actually perform the extension on 00418 those objects, to make sure that no irreversible operation is performed. 00419 00420 Returns: A DocRect, whose values will all be set to INT32_MAX if the test was passed. 00421 For each side on which the test failed, the corresponding value will be set 00422 to how far in it is safe to extend the node, eg if the node could only be 00423 shrunk inwards by 10 millipoints on the right-hand side, then the hix value 00424 of the DocRect will contain 10, while all other values will be INT32_MAX. 00425 00426 Errors: ERROR2 with FALSE if fExtendFlags holds contradictory flags 00427 or any parameters are NULL. 00428 00429 See also: Extender::CheckValidExtend() and for more information, see: 00430 \\Earth\Develop\CAMELOT\DOCS\Specs\Camelot V3\Extending Objects - Mechanism.doc 00431 00432 NOTE: A call to Extender::CheckValidExtend must *always* be made before performing 00433 a corresponding Extender::Extend process. 00434 00435 ********************************************************************************************/ 00436 DocRect Extender::CheckValidExtend( NodeRenderableInk* pNode, BYTE fExtendFlags, 00437 const DocRect& drTriggerRelation, const DocRect& drTargetRelation, 00438 const DocRect& drNewTrigger, const DocRect& drLastTrigger, 00439 const DocRect& drLastTarget, BOOL * pOK, BOOL ExtendAroundTarget) 00440 { 00441 // validate parameters. 00442 ERROR2IF( pNode == NULL, DocRect(0, 0, 0, 0), 00443 "NULL parameters passed to Extender::CheckValidExtend" ); 00444 00445 ERROR2IF( ((fExtendFlags & X_EXTEND) && (fExtendFlags & X_STRETCH)) || 00446 ((fExtendFlags & Y_EXTEND) && (fExtendFlags & Y_STRETCH)), 00447 DocRect(0, 0, 0, 0), 00448 "Extender::CheckValidExtend called with contradictory flags in fExtendFlags" ); 00449 00450 if (pOK) 00451 *pOK = TRUE; 00452 // quit immediately if any of the bounding rectangles is invalid, 00453 // or is of zero width or height. 00454 if( !drTriggerRelation.IsValid() || 00455 !drTargetRelation.IsValid() || 00456 !drNewTrigger.IsValid() || 00457 !drLastTrigger.IsValid() || 00458 !drLastTarget.IsValid() || 00459 drTriggerRelation.Height() == 0 || 00460 drTriggerRelation.Width() == 0 || 00461 drTargetRelation.Height() == 0 || 00462 drTargetRelation.Width() == 0 || 00463 drNewTrigger.Height() == 0 || 00464 drNewTrigger.Width() == 0 || 00465 drLastTrigger.Height() == 0 || 00466 drLastTrigger.Width() == 0 || 00467 drLastTarget.Height() == 0 || 00468 drLastTarget.Width() == 0) 00469 { 00470 if (pOK) 00471 *pOK = FALSE; 00472 00473 return DocRect(0, 0, 0, 0); 00474 } 00475 00476 00477 00478 // determine extension working values. 00479 ExtendParams eps; 00480 CalculateExtendParams( &eps, fExtendFlags, drTriggerRelation, drTargetRelation, 00481 drNewTrigger, drLastTrigger, drLastTarget, NULL, ExtendAroundTarget ); 00482 00483 // check whether pNode, or any child of pNode, is extendible. 00484 // also, check that extending all of these Nodes is a valid operation. 00485 // if this is the case, recursively extend pNode and its children. 00486 DocRect drMinExtend = pNode->ValidateExtend(eps); 00487 BOOL bValidExtend = ( drMinExtend.lo.x == INT32_MAX && 00488 drMinExtend.lo.y == INT32_MAX && 00489 drMinExtend.hi.x == INT32_MAX && 00490 drMinExtend.hi.y == INT32_MAX ); 00491 00492 // DEBUG: 00493 #ifdef _DEBUG 00494 TRACEUSER( "Karim", _T("%5s ValidateExtend() on %s\n"), bValidExtend ? _T("TRUE") : _T("FALSE"), 00495 pNode->Name()); 00496 #endif 00497 00498 BOOL bExtendible = TRUE; 00499 00500 if (!bExtendible || !bValidExtend) 00501 { 00502 //#ifdef _DEBUG 00503 // TRACEUSER( "Karim", _T("Invalid Extend on %s; Ext: %d, Valid: %d\n"), 00504 // pNode->Name(), bExtendible, bValidExtend); 00505 //#endif 00506 return drMinExtend; 00507 } 00508 00509 // return the outcome of the test, in the form of drMinExtend. 00510 return drMinExtend; 00511 } 00512 00513 00514 00515 /******************************************************************************************** 00516 00517 > void Extender::Extend( NodeRenderableInk* pNode, BYTE fExtendFlags, 00518 const DocRect& drTriggerRelation, const DocRect& drTargetRelation, 00519 const DocRect& drNewTrigger, const DocRect& drLastTarget, 00520 const DocRect* pdrDifference = NULL) 00521 00522 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00523 Created: 03/12/1999 00524 Inputs: pNode pointer to a NodeRenderableInk which will be extended. 00525 fExtendFlags bitwise flags, describing any combination of x- or y- 00526 stretching or extending, except that you may not extend 00527 and stretch along an axis at the same time. Values: 00528 00529 X_EXTEND the node should be extended along the x-axis 00530 Y_EXTEND the node should be extended along the y-axis 00531 X_STRETCH the node should be stretched along the x-axis 00532 Y_STRETCH the node should be stretched along the y-axis 00533 00534 drTriggerRelation relationship rectangle - the bounding box of the trigger 00535 sets when the relationship is defined. 00536 drTargetRelation relationship rectangle - the bounding box of the target 00537 sets when the relationship is defined. 00538 drNewTrigger bounding box of sets of triggers which started the call 00539 to this function. 00540 drLastTrigger the bounding box of the trigger set(s) before it changed. 00541 drLastTarget the bounding box of the target set before it is extended. 00542 pdrDifference a rectangle containing four difference parameters which 00543 may be used to alter the extend. ok to omit or be NULL. 00544 00545 Outputs: pNode may be extended or stretched horizontally 00546 or vertically in any combination. 00547 00548 Purpose: Perform an extend operation on the given Node and its children, using the 00549 given rectangles and flags to determine how the Node should transform. 00550 00551 Errors: ERROR2 if fExtendFlags holds contradictory flags or any parameters are NULL. 00552 00553 See also: Extender::CheckValidExtend() and for more information, see: 00554 \\Earth\Develop\CAMELOT\DOCS\Specs\Camelot V3\Extending Objects - Mechanism.doc 00555 00556 NOTE: A call to Extender::CheckValidExtend must always be made before performing 00557 a corresponding Extender::Extend process. 00558 00559 ********************************************************************************************/ 00560 void Extender::Extend( NodeRenderableInk* pNode, BYTE fExtendFlags, 00561 const DocRect& drTriggerRelation, const DocRect& drTargetRelation, 00562 const DocRect& drNewTrigger, const DocRect& drLastTrigger, 00563 const DocRect& drLastTarget, const DocRect* pdrDifference, const BOOL ExtendAroundTarget, 00564 UndoableOperation * pOp) 00565 { 00566 // validate parameters. 00567 if (pNode == NULL) 00568 { 00569 ERROR2RAW("NULL parameters passed to Extender::Extend."); 00570 return; 00571 } 00572 00573 if (((fExtendFlags & X_EXTEND) && (fExtendFlags & X_STRETCH)) || 00574 ((fExtendFlags & Y_EXTEND) && (fExtendFlags & Y_STRETCH))) 00575 { 00576 ERROR2RAW("Extender::Extend called with contradictory flags in fExtendFlags"); 00577 return; 00578 } 00579 00580 // quit immediately if any of the bounding rectangles is invalid, 00581 // or is of zero width or height. 00582 if (!drTriggerRelation.IsValid() || 00583 !drTargetRelation.IsValid() || 00584 !drNewTrigger.IsValid() || 00585 !drLastTrigger.IsValid() || 00586 !drLastTarget.IsValid() || 00587 drTriggerRelation.Height() == 0 || 00588 drTriggerRelation.Width() == 0 || 00589 drTargetRelation.Height() == 0 || 00590 drTargetRelation.Width() == 0 || 00591 drNewTrigger.Height() == 0 || 00592 drNewTrigger.Width() == 0 || 00593 drLastTrigger.Height() == 0 || 00594 drLastTrigger.Width() == 0 || 00595 drLastTarget.Height() == 0 || 00596 drLastTarget.Width() == 0) 00597 { 00598 ERROR2RAW("Extender::Extend; Invalid or zero-width rectangle"); 00599 return; 00600 } 00601 00602 // determine extension working values. 00603 ExtendParams eps; 00604 eps.pOp = pOp; 00605 CalculateExtendParams( &eps, fExtendFlags, drTriggerRelation, drTargetRelation, 00606 drNewTrigger, drLastTrigger, 00607 drLastTarget, pdrDifference, ExtendAroundTarget ); 00608 00609 // this hasn't really changed size so do nothing 00610 if (!eps.xdec && !eps.xinc && !eps.ydec && !eps.yinc && eps.xscale == 1.0 && eps.yscale == 1.0 00611 && eps.doccOffset.x == 0 && eps.doccOffset.y == 0) 00612 return; 00613 00614 // invalidate the old image of the object, perform the extension 00615 // and flag that the object should be redrawn. 00616 // we must look above this node, to detect whether we are the child 00617 // of a node whose on-screen bounds are greater than ours. 00618 // if this is the case, we need to invalidate *that* node and 00619 // get it to redraw. 00620 // TODO: there may be a better way of doing this (is it necessary at all with the AllowOp mechanism?) 00621 BOOL bFoundBigParent = FALSE; 00622 Node* pInvalidateNode = pNode->FindParent(); 00623 while (pInvalidateNode != NULL && !IS_A(pInvalidateNode, Layer) && !bFoundBigParent) 00624 { 00625 if (IS_A(pInvalidateNode, NodeBevelController) || 00626 IS_A(pInvalidateNode, NodeShadowController) || 00627 IS_A(pInvalidateNode, NodeContourController)) 00628 bFoundBigParent = TRUE; 00629 else 00630 pInvalidateNode = pInvalidateNode->FindParent(); 00631 } 00632 00633 if (!bFoundBigParent) 00634 pInvalidateNode = pNode; 00635 00636 ((NodeRenderableInk*)pInvalidateNode)->RedrawObject(); 00637 ((NodeRenderableInk*)pInvalidateNode)->InvalidateBoundingRect(); 00638 pNode->Extend(eps); 00639 00640 // DEBUG: 00641 //#ifdef _DEBUG 00642 // TRACEUSER( "Karim", _T("Extend() on %s\n"), pNode->Name()); 00643 //#endif 00644 00645 ((NodeRenderableInk*)pInvalidateNode)->RedrawObject(); 00646 } 00647 00648 00649 00650 /******************************************************************************************** 00651 00652 > DocRect Extender::ValidateControlPoints(INT32 numPoints, const DocCoord* doccArray, const ExtendParams& ExtParams) 00653 00654 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00655 Created: 07/12/1999 00656 Inputs: numPoints number of coordinates to validate. 00657 doccArray the array of coordinates. 00658 ExtParams extend parameters. 00659 00660 Returns: A DocRect containing maximum safe shrink distances for the points: 00661 00662 lox maximum shrinkage in from the left. 00663 hix maximum shrinkage in from the right. 00664 loy maximum shrinkage in from the bottom. 00665 hiy maximum shrinkage in from the top. 00666 00667 If shrinking in a particular direction is ok, the 00668 corresponding DocRect value will be set to INT32_MAX. 00669 00670 Purpose: Validate the given points, using the provided extend parameters. 00671 If extending the points is invalid, then return a rectangle containing the 00672 largest distances which the points can be shrunk in by. 00673 Errors: ERROR3 if doccArray is NULL. 00674 See also: 00675 00676 ********************************************************************************************/ 00677 DocRect Extender::ValidateControlPoints(INT32 numPoints, const DocCoord* doccArray, const ExtendParams& ExtParams) 00678 { 00679 // validate data 00680 ERROR3IF(doccArray == NULL, "Extender::ValidateControlPoints- NULL doccArray passed!"); 00681 00682 DocRect drMaxSafeShrink(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drCheckExtend; 00683 00684 // this check is only necessary if extension will be occurring along an 00685 // axis; if the extension type is stretch, or no action, no validation 00686 // is required. 00687 00688 // for each of the four directions of displacement, and only if we shrank, check that 00689 // none of the points lie in a position to which they would not be returned under the 00690 // inverse extend operation. if they do, we record the largest safe amount we could 00691 // shrink them in by. 00692 00693 // providing the extend operation involves displacing all points by the difference 00694 // between start- and end- centres, followed by the extension, these invalid points 00695 // will be only those which would be dragged inwards, to cross over the end-centre. 00696 00697 if (ExtParams.fExtendFlags & X_EXTEND) 00698 { 00699 if (ExtParams.xinc < 0) 00700 { 00701 INT32 minXshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.x, 00702 ExtParams.xinc, 00703 ExtParams.doccOffset.x, 00704 numPoints, doccArray, TRUE ); 00705 if (minXshrink >= 0) // invalid, so record the return value. 00706 drMaxSafeShrink.hi.x = minXshrink; 00707 } 00708 00709 // we do a different test if we're expanding. 00710 else if (ExtParams.xinc > 0) 00711 { 00712 INT32 minXshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.x, 00713 ExtParams.xinc, 00714 ExtParams.doccOffset.x, 00715 numPoints, doccArray, TRUE ); 00716 if (minXshrink >= 0) // invalid, so record the return value. 00717 drMaxSafeShrink.hi.x = minXshrink; 00718 } 00719 00720 if (ExtParams.xdec < 0) 00721 { 00722 INT32 minXshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.x, 00723 -ExtParams.xdec, 00724 ExtParams.doccOffset.x, 00725 numPoints, doccArray, TRUE ); 00726 if (minXshrink >= 0) // invalid, so record the return value. 00727 drMaxSafeShrink.lo.x = minXshrink; 00728 } 00729 00730 // we do a different test if we're expanding. 00731 else if (ExtParams.xdec > 0) 00732 { 00733 INT32 minXshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.x, 00734 -ExtParams.xdec, 00735 ExtParams.doccOffset.x, 00736 numPoints, doccArray, TRUE ); 00737 if (minXshrink >= 0) // invalid, so record the return value. 00738 drMaxSafeShrink.lo.x = minXshrink; 00739 } 00740 } 00741 00742 if (ExtParams.fExtendFlags & Y_EXTEND) 00743 { 00744 if (ExtParams.yinc < 0) 00745 { 00746 INT32 minYshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.y, 00747 ExtParams.yinc, 00748 ExtParams.doccOffset.y, 00749 numPoints, doccArray, FALSE ); 00750 if (minYshrink >= 0) // invalid, so record the return value. 00751 drMaxSafeShrink.hi.y = minYshrink; 00752 } 00753 00754 // we do a different test if we're expanding. 00755 else if (ExtParams.yinc > 0) 00756 { 00757 INT32 minYshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.y, 00758 ExtParams.yinc, 00759 ExtParams.doccOffset.y, 00760 numPoints, doccArray, FALSE ); 00761 if (minYshrink >= 0) // invalid, so record the return value. 00762 drMaxSafeShrink.hi.y = minYshrink; 00763 } 00764 00765 if (ExtParams.ydec < 0) 00766 { 00767 INT32 minYshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.y, 00768 -ExtParams.ydec, 00769 ExtParams.doccOffset.y, 00770 numPoints, doccArray, FALSE ); 00771 if (minYshrink >= 0) // invalid, so record the return value. 00772 drMaxSafeShrink.lo.y = minYshrink; 00773 } 00774 00775 // we do a different test if we're expanding. 00776 else if (ExtParams.ydec > 0) 00777 { 00778 INT32 minYshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.y, 00779 -ExtParams.ydec, 00780 ExtParams.doccOffset.y, 00781 numPoints, doccArray, FALSE ); 00782 if (minYshrink >= 0) // invalid, so record the return value. 00783 drMaxSafeShrink.lo.y = minYshrink; 00784 } 00785 } 00786 00787 return drMaxSafeShrink; 00788 } 00789 00790 00791 00792 /******************************************************************************************** 00793 00794 > INT32 Extender::CheckInvalidShrinkingPoints( INT32 nCentre, INT32 nDelta, INT32 nOffset, 00795 INT32 nNumPoints, const DocCoord* doccArray, 00796 BOOL bExamineX ) 00797 00798 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00799 Created: 07/12/1999 00800 Inputs: nCentre the limiting x or y centre-value, to which points are shrinking. 00801 nDelta the distance by which the points are being shrunk. 00802 nOffset an offset which should be applied to the points before testing. 00803 nNumPoints the number of points to test. 00804 doccArray the array of points to test. 00805 bExamineX whether to examine x-coords (TRUE) or y-coords (FALSE). 00806 Outputs: 00807 Returns: The maximum safe distance that points can be extended towards nCentre by. 00808 This value will be non-negative if any invalid points were found, or 00809 negative if all points passed the test. 00810 Purpose: Check whether any of the points in the given array will be shrunk past the 00811 given limiting value. If all the points check out, we return an all-clear 00812 value, which is negative. Otherwise, the maximum safe shrinkage is given. 00813 00814 Karim 11/05/2000 00815 With the addition of invariant points, this method only checks those points 00816 which would move under extension. If a point is invariant, ie it is so close 00817 to nCentre that it would not be moved, it is disregarded. It is up to the 00818 various extend methods to ensure that these invariant points actually *stay* 00819 invariant! 00820 Errors: 00821 See also: 00822 00823 ********************************************************************************************/ 00824 INT32 Extender::CheckInvalidShrinkingPoints( INT32 nCentre, INT32 nDelta, INT32 nOffset, 00825 INT32 nNumPoints, const DocCoord* doccArray, 00826 BOOL bExamineX ) 00827 { 00828 // return with an all-clear if delta is zero. 00829 if (nDelta == 0) 00830 return -1; 00831 00832 // test the points we were given, and determine the furthest we can move them without 00833 // dragging any into the dead zone, defined as the limit value +/- our buffer size. 00834 // this value will be the least of all the maximum distances that each point can 00835 // shrink by. 00836 INT32 maxShrink, smallest_maxShrink = INT32_MAX; 00837 00838 // each point should be offset by nOffset so for testing, we 'un-offset' the centre. 00839 INT32 myCentre = nCentre - nOffset; 00840 00841 // nDelta < 0 means we are being dragged down or left. 00842 // we're checking that each point can stretch by nDelta down towards nCentre. 00843 // if it can't, we see how far it _can_ go. 00844 if (nDelta < 0) 00845 { 00846 // validate the x-component of the coordinates. 00847 if (bExamineX) 00848 { 00849 for (INT32 i = 0; i < nNumPoints; i ++) 00850 { 00851 maxShrink = CheckLimits(myCentre, nDelta, doccArray[i].x); 00852 if (maxShrink >= 0 && smallest_maxShrink > maxShrink) 00853 smallest_maxShrink = maxShrink; 00854 } 00855 } 00856 00857 // validate the y-component of the coordinates. 00858 else 00859 { 00860 for (INT32 i = 0; i < nNumPoints; i ++) 00861 { 00862 maxShrink = CheckLimits(myCentre, nDelta, doccArray[i].y); 00863 if (maxShrink >= 0 && smallest_maxShrink > maxShrink) 00864 smallest_maxShrink = maxShrink; 00865 } 00866 } 00867 } 00868 00869 // nDelta > 0 means we are being dragged up or right. 00870 // we're checking that each point can stretch by nDelta up towards nCentre. 00871 // if it can't, we see how far it _can_ go. 00872 else 00873 { 00874 // validate the x-component of the coordinates. 00875 if (bExamineX) 00876 { 00877 for (INT32 i = 0; i < nNumPoints; i ++) 00878 { 00879 maxShrink = CheckLimits(myCentre, -nDelta, myCentre - doccArray[i].x + myCentre); 00880 if (maxShrink >= 0 && smallest_maxShrink > maxShrink) 00881 smallest_maxShrink = maxShrink; 00882 } 00883 } 00884 00885 // validate the y-component of the coordinates. 00886 else 00887 { 00888 for (INT32 i = 0; i < nNumPoints; i ++) 00889 { 00890 maxShrink = CheckLimits(myCentre, -nDelta, myCentre - doccArray[i].y + myCentre); 00891 if (maxShrink >= 0 && smallest_maxShrink > maxShrink) 00892 smallest_maxShrink = maxShrink; 00893 } 00894 } 00895 } 00896 00897 return smallest_maxShrink == INT32_MAX ? -1 : smallest_maxShrink; 00898 } 00899 00900 00901 00902 /******************************************************************************************** 00903 00904 > inline INT32 Extender::CheckLimits(const INT32 limit, const INT32 delta, const INT32 value) 00905 00906 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00907 Created: 07/12/1999 00908 Inputs: limit the limiting value to use. 00909 delta the amount to attempt to shrink by - should always be negative. 00910 value the value to validate. 00911 Outputs: 00912 Returns: -1 if the validation was successful, 00913 non-negative integer value otherwise. 00914 00915 Purpose: Checks that the given value may be reduced by the amount delta without 00916 going below the given limit value. If it cannot, we return the most which 00917 the given value can be safely reduced by. 00918 00919 A preset buffer value is applied, defining a 'dead zone' around the limit, 00920 into which the value may not be reduced. All calculations performed within 00921 this method take account of this dead zone. Also note that if a value begins 00922 within the dead zone, then an all-clear value of -1 will be returned, as we 00923 don't want this invariant point to interfere with shrinking of other values. 00924 The actual extension code should always check for and not alter these points. 00925 00926 Errors: ERROR3 if delta is non-negative (ie >= 0). 00927 See also: 00928 00929 ********************************************************************************************/ 00930 inline INT32 Extender::CheckLimits(const INT32 limit, const INT32 delta, const INT32 value) 00931 { 00932 // data validation. 00933 ERROR3IF(delta >= 0, "Extender::CheckLimits called with delta non-negative!"); 00934 00935 // we return an all-clear if the test value starts in the buffer zone. 00936 if (value >= limit - EXT_SHRINK_BUFFER && value <= limit + EXT_SHRINK_BUFFER) 00937 return -1; 00938 00939 // give the all-clear if the test value lies below the limit value already. 00940 // also give the all-clear if the test value will not be drawn down into the dead-zone. 00941 if (value < limit - EXT_SHRINK_BUFFER || value + delta > limit + EXT_SHRINK_BUFFER) 00942 return -1; 00943 00944 // ok, we're invalid - return the safe shrinkage distance for the test value. 00945 return value - (limit + EXT_SHRINK_BUFFER + 1); 00946 } 00947 00948 00949 00950 /******************************************************************************************** 00951 00952 > void Extender::CalculateExtendParams(ExtendParams* pEPS, BYTE fExtendFlags, 00953 const DocRect& drTriggerRelation, const DocRect& drTargetRelation, 00954 const DocRect& drNewTrigger, const DocRect& drLastTarget ) 00955 00956 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00957 Created: 06/12/1999 00958 Inputs: pEPS pointer to an ExtendParams object to fill. 00959 fExtendFlags bitwise flags, describing any combination of x- or y- 00960 stretching or extending, except that you may not extend 00961 and stretch along an axis at the same time. Values: 00962 00963 X_EXTEND the node should be extended along the x-axis 00964 Y_EXTEND the node should be extended along the y-axis 00965 X_STRETCH the node should be stretched along the x-axis 00966 Y_STRETCH the node should be stretched along the y-axis 00967 00968 drTriggerRelation relationship rectangle - the bounding box of the trigger 00969 sets when the relationship is defined. 00970 drTargetRelation relationship rectangle - the bounding box of the target 00971 sets when the relationship is defined. 00972 drNewTrigger bounding box of sets of triggers which started the call 00973 to this function. 00974 drLastTrigger the bounding box of the trigger set(s) before it changed. 00975 drLastTarget the bounding box of the target set before it is extended. 00976 pdrDifference a rectangle containing four difference parameters which 00977 may be used to alter the extend. ok to omit or be NULL. 00978 00979 Outputs: *pEPS will be set up to contain extend information derived from the inputs. 00980 Purpose: Given the rectangles and flags which define an extension, this function 00981 derives the bare information which a Node requires to perform the extension 00982 on itself. 00983 Errors: ERROR3 in debug builds if pEPS is NULL. 00984 See also: Extender::Extend(). 00985 00986 ********************************************************************************************/ 00987 void Extender::CalculateExtendParams(ExtendParams* pEPS, BYTE fExtendFlags, 00988 const DocRect& drTriggerRelation, const DocRect& drTargetRelation, 00989 const DocRect& drNewTrigger, const DocRect& drLastTrigger, 00990 const DocRect& drLastTarget, const DocRect* pdrDifference, 00991 const BOOL ExtendAroundTarget) 00992 { 00993 // validate input. 00994 ERROR3IF(pEPS == NULL, "Extender::CalculateExtendParams- pEPS is NULL!"); 00995 00996 // use the relationship rects and the new trigger rect to calculate the new target rect. 00997 DocRect drNewTarget(drNewTrigger); 00998 00999 // x-extend - the new target rect must be x-offset from new-trigger on each 01000 // side, by the same amount as target-relation from trigger-relation. 01001 if (fExtendFlags & X_EXTEND) 01002 { 01003 // if the trigger and target-relation rects do not overlap in the x-direction, 01004 // then the new target rect must be x-offset from new-trigger on its near-side, 01005 // by the same amount as target-relation from trigger-relation. 01006 if (drTargetRelation.lo.x > drTriggerRelation.hi.x) 01007 { 01008 drNewTarget.lo.x = drNewTrigger.hi.x + (drTargetRelation.lo.x - drTriggerRelation.hi.x); 01009 drNewTarget.hi.x = drNewTrigger.hi.x + (drTargetRelation.hi.x - drTriggerRelation.hi.x); 01010 } 01011 else if (drTargetRelation.hi.x < drTriggerRelation.lo.x) 01012 { 01013 drNewTarget.lo.x = drNewTrigger.lo.x - (drTriggerRelation.lo.x - drTargetRelation.lo.x); 01014 drNewTarget.hi.x = drNewTrigger.lo.x - (drTriggerRelation.lo.x - drTargetRelation.hi.x); 01015 } 01016 01017 // normal extend behaviour - distances between corresponding sides are kept constant. 01018 else 01019 { 01020 drNewTarget.lo.x += drTargetRelation.lo.x - drTriggerRelation.lo.x; 01021 drNewTarget.hi.x += drTargetRelation.hi.x - drTriggerRelation.hi.x; 01022 01023 // if the target's new sides are coincident or invalid, then 01024 // they need to be moved back into valid positions. 01025 if (drNewTarget.lo.x >= drNewTarget.hi.x) 01026 { 01027 drNewTarget.hi.x = drNewTarget.lo.x + 2; 01028 } 01029 } 01030 } 01031 else if (fExtendFlags & X_STRETCH) 01032 { 01033 double a = drNewTrigger.Width() / (double)drTriggerRelation.Width(); 01034 INT32 b = drNewTrigger.hi.x - (INT32)(a * drTriggerRelation.hi.x); 01035 01036 drNewTarget.lo.x = b + (INT32)(a * drTargetRelation.lo.x); 01037 drNewTarget.hi.x = b + (INT32)(a * drTargetRelation.hi.x); 01038 } 01039 01040 // y-extend - the new target rect must be y-offset from new-trigger on top and 01041 // bottom, by the same amount as target-relation from trigger-relation. 01042 if (fExtendFlags & Y_EXTEND) 01043 { 01044 // if the trigger and target-relation rects do not overlap in the y-direction, 01045 // then the new target rect must be y-offset from new-trigger on its near-side, 01046 // by the same amount as target-relation from trigger-relation. 01047 if (drTargetRelation.lo.y > drTriggerRelation.hi.y) 01048 { 01049 drNewTarget.lo.y = drNewTrigger.hi.y + (drTargetRelation.lo.y - drTriggerRelation.hi.y); 01050 drNewTarget.hi.y = drNewTrigger.hi.y + (drTargetRelation.hi.y - drTriggerRelation.hi.y); 01051 } 01052 else if (drTargetRelation.hi.y < drTriggerRelation.lo.y) 01053 { 01054 drNewTarget.lo.y = drNewTrigger.lo.y - (drTriggerRelation.lo.y - drTargetRelation.lo.y); 01055 drNewTarget.hi.y = drNewTrigger.lo.y - (drTriggerRelation.lo.y - drTargetRelation.hi.y); 01056 } 01057 01058 // normal extend behaviour - distances between corresponding sides are kept constant. 01059 else 01060 { 01061 drNewTarget.lo.y += drTargetRelation.lo.y - drTriggerRelation.lo.y; 01062 drNewTarget.hi.y += drTargetRelation.hi.y - drTriggerRelation.hi.y; 01063 01064 // if the target's new top and bottom are coincident or invalid, then 01065 // they need to be moved back into valid positions. 01066 if (drNewTarget.lo.y >= drNewTarget.hi.y) 01067 { 01068 drNewTarget.hi.y = drNewTarget.lo.y + 2; 01069 } 01070 } 01071 } 01072 else if (fExtendFlags & Y_STRETCH) 01073 { 01074 double a = drNewTrigger.Height() / (double)drTriggerRelation.Height(); 01075 INT32 b = drNewTrigger.hi.y - (INT32)(a * drTriggerRelation.hi.y); 01076 01077 drNewTarget.lo.y = b + (INT32)(a * drTargetRelation.lo.y); 01078 drNewTarget.hi.y = b + (INT32)(a * drTargetRelation.hi.y); 01079 } 01080 01081 // use the old and new target rectangles to calculate our extension working values. 01082 01083 // extend flags. 01084 pEPS->fExtendFlags = fExtendFlags; 01085 01086 // the start- and end- centres of extension + the offset between them. 01087 // Karim 14/03/2000 - changed to use the trigger set bounds if their old centre 01088 // lies within the bounds of the old target set. 01089 if (!ExtendAroundTarget && drLastTarget.ContainsCoord(drLastTrigger.Centre())) 01090 { 01091 pEPS->doccStartCentre = drLastTrigger.Centre(); 01092 pEPS->doccEndCentre = drNewTrigger.Centre(); 01093 pEPS->doccOffset = pEPS->doccEndCentre - pEPS->doccStartCentre; 01094 } 01095 else 01096 { 01097 pEPS->doccStartCentre = drLastTarget.Centre(); 01098 pEPS->doccEndCentre = drNewTarget.Centre(); 01099 pEPS->doccOffset = pEPS->doccEndCentre - pEPS->doccStartCentre; 01100 } 01101 01102 // magnitudes of extension. note that under extension, objects are first offset 01103 // from start- to end- centre, then extended. this makes extension mirror-symmetric 01104 // so that xinc == xdec (+/- 1). 01105 pEPS->xinc = drNewTarget.hi.x - (drLastTarget.hi.x + pEPS->doccOffset.x); 01106 pEPS->xdec = (drLastTarget.lo.x + pEPS->doccOffset.x) - drNewTarget.lo.x; 01107 pEPS->yinc = drNewTarget.hi.y - (drLastTarget.hi.y + pEPS->doccOffset.y); 01108 pEPS->ydec = (drLastTarget.lo.y + pEPS->doccOffset.y) - drNewTarget.lo.y; 01109 01110 // scale factors, if automatic _stretching_ is required. 01111 pEPS->xscale = drNewTarget.Width() / (double)drLastTarget.Width(); 01112 pEPS->yscale = drNewTarget.Height() / (double)drLastTarget.Height(); 01113 01114 // if our difference rect is non-NULL, then we may need to substitute 01115 // xinc, xdec, yinc, ydec with negative copies of some of its values. 01116 if (pdrDifference != NULL) 01117 { 01118 if (pdrDifference->lo.x != INT32_MAX) pEPS->xdec = -pdrDifference->lo.x; 01119 if (pdrDifference->lo.y != INT32_MAX) pEPS->ydec = -pdrDifference->lo.y; 01120 if (pdrDifference->hi.x != INT32_MAX) pEPS->xinc = -pdrDifference->hi.x; 01121 if (pdrDifference->hi.y != INT32_MAX) pEPS->yinc = -pdrDifference->hi.y; 01122 } 01123 01124 // TODO: should probably not use these, but the equivalent of b (used above) instead. 01125 pEPS->doccScaleStart = drLastTarget.lo; 01126 pEPS->doccScaleEnd = drNewTarget.lo; 01127 01128 // set the various dead-zones; these depend on the signs of the expansions in each dirn. 01129 pEPS->xincExtendBuffer = (pEPS->xinc >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER; 01130 pEPS->xdecExtendBuffer = (pEPS->xdec >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER; 01131 pEPS->yincExtendBuffer = (pEPS->yinc >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER; 01132 pEPS->ydecExtendBuffer = (pEPS->ydec >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER; 01133 } 01134 01135 01136 01137 /******************************************************************************************** 01138 01139 > INT32 Extender::CheckInvalidExpandingPoints( INT32 nCentre, INT32 nDelta, INT32 nOffset, 01140 INT32 nNumPoints, const DocCoord* doccArray, 01141 BOOL bExamineX ) 01142 01143 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 01144 Created: 25/01/2000 01145 Inputs: nCentre the centre x- or y- value from which point are expanding. 01146 nDelta (currently unused) the distance by which the points are moving. 01147 nOffset an offset which should be applied to the points before testing. 01148 nNumPoints the number of points to test. 01149 doccArray the array of points to test. 01150 bExamineX whether to examine x-coords (TRUE) or y-coords (FALSE). 01151 Outputs: 01152 Returns: Negative value if all points can expand safely away from nCentre, or 01153 Zero if any of the points cannot be safely moved away. 01154 01155 Purpose: Two checks: 01156 01157 No points may lie inside the dead-zone. 01158 01159 Due to rounding errors, the centre-point of an object depends on whether its 01160 width is even or odd. There is a potential for error here, if a point lying 01161 one millipoint outside the dead zone is moved out from the centre, and the 01162 operation then reversed. If the centre-point changes position due to a new 01163 width then the point may be moving back *inside* the dead zone, which will 01164 be disallowed -> irreversible operation. 01165 01166 This method checks for these points. (boy, what a mouthful!) 01167 01168 Assumptions: That the extension being checked is symmetric - ie all points are moving 01169 away from the centre point, regardless of their position relative to it. 01170 Errors: 01171 See also: 01172 01173 ********************************************************************************************/ 01174 INT32 Extender::CheckInvalidExpandingPoints( INT32 nCentre, INT32 nDelta, INT32 nOffset, 01175 INT32 nNumPoints, const DocCoord* doccArray, 01176 BOOL bExamineX ) 01177 { 01178 // return with an all-clear if delta is zero. 01179 if (nDelta == 0) 01180 return -1; 01181 01182 // each point should be offset by nOffset, so instead we 'un-offset' the centre. 01183 // INT32 myCentre = nCentre - nOffset; 01184 01185 /* 01186 * Karim 11/05/2000 01187 * Commented out, as this test is being moved into the actual extension code - rather than 01188 * have invariant points limit the extension, they will have no effect, and will instead 01189 * just not themselves extend. 01190 * 01191 // The test we do is simple - ensure that no points lie within one millipoint of the 01192 // dead zone. We only need one invalid point for the test to fail. 01193 BOOL bPassedTest = TRUE; 01194 if (bExamineX) 01195 { 01196 for (INT32 i = 0; i < nNumPoints && bPassedTest; i ++) 01197 { 01198 bPassedTest = (doccArray[i].x > (myCentre + EXT_EXPAND_BUFFER)) || 01199 (doccArray[i].x < (myCentre - EXT_EXPAND_BUFFER)); 01200 } 01201 } 01202 else 01203 { 01204 for (INT32 i = 0; i < nNumPoints && bPassedTest; i ++) 01205 { 01206 bPassedTest = (doccArray[i].y > (myCentre + EXT_EXPAND_BUFFER)) || 01207 (doccArray[i].y < (myCentre - EXT_EXPAND_BUFFER)); 01208 } 01209 } 01210 01211 return bPassedTest ? -1 : 0; 01212 */ 01213 01214 return -1; 01215 }