extender.cpp

Go to the documentation of this file.
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 }

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