nodebldr.cpp

Go to the documentation of this file.
00001 // $Id: nodebldr.cpp 1315 2006-06-14 09:51:34Z 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 // NodeBlender implementation
00099 
00100 /*
00101 */
00102 
00103 
00104 #include "camtypes.h"
00105 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "osrndrgn.h"
00109 
00110 #include "nodepath.h"
00111 #include "ophist.h"
00112 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "lineattr.h"
00115 #include "blendatt.h"
00116 #include "qualattr.h"
00117 //#include "markn.h"
00118 #include "gblend.h"
00119 #include "gwinding.h"
00120 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00121 #include "grnddib.h"
00122 //#include "grndclik.h"
00123 #include "ndbldpth.h"
00124 
00125 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "aw_eps.h"
00127 #include "nativeps.h"       // The old style EPS native filter, used in v1.1
00128 #include "cameleps.h"
00129 #include "attrmap.h"
00130 #include "progress.h"
00131 
00132 #include "cxftags.h"
00133 //#include "cxfdefs.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00134 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00135 //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00136 
00137 #include "nodeblnd.h"
00138 #include "nodebldr.h"
00139 
00140 //#include "bitmap.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00141 #include "oilbitmap.h"
00142 
00143 #include "brshattr.h"
00144 #include "blndhelp.h"
00145 #include "fthrattr.h"
00146 #include "nodecont.h"
00147 
00148 #include "brshbeca.h"
00149 #include "strkattr.h"
00150 
00151 DECLARE_SOURCE("$Revision: 1315 $");
00152 
00153 // Declare smart memory handling in Debug builds
00154 //#define Swap(a,b,t) { t=a; a=b; b=t; }
00155 
00156 #define SWAP(type,a,b) { type x=a; a=b; b=x; }
00157 
00158 #define DELPTR(p) if (p != NULL) { delete p; p = NULL; }
00159 
00160 
00161 //-----------------------------------------------------------
00162 
00163 void ListStepper::Init(UINT32 NumInStart,UINT32 NumInEnd,UINT32 Start)
00164 {
00165     if (NumInStart > NumInEnd)
00166     {
00167         Min = NumInEnd;   pNextInMin = &NextInEnd;
00168         Max = NumInStart; pNextInMax = &NextInStart;
00169     }
00170     else
00171     {
00172         Min = NumInStart; pNextInMin = &NextInStart;
00173         Max = NumInEnd;   pNextInMax = &NextInEnd;
00174     }
00175 
00176     NextInStart = NextInEnd = Inc = 0;
00177 
00178     for (INT32 dummy;Start > 0;Start--)
00179         GetNext(&dummy,&dummy);
00180 }
00181 
00182 void ListStepper::GetNext(INT32* pNextInStart,INT32* pNextInEnd)
00183 {
00184     if ((*pNextInMax) < Max)
00185     {
00186         *pNextInStart = NextInStart;
00187         *pNextInEnd   = NextInEnd;
00188 
00189         Inc += Min;
00190         if (Inc >= Max)
00191         {
00192             Inc -= Max;
00193             (*pNextInMin)++;
00194         }
00195         (*pNextInMax)++;
00196     }
00197     else
00198     {
00199         *pNextInStart = -1;
00200         *pNextInEnd   = -1;
00201     }
00202 }
00203 
00204 /***********************************************************************************************
00205 
00206 >   class BlendSubRenderContext: public SubRenderContext
00207 
00208     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00209     Created:    30/11/94
00210     Purpose:    Used during rendering to store the render context of a blender node if it has
00211                 taken too long to render so far.
00212 
00213 ***********************************************************************************************/
00214 
00215 class BlendSubRenderContext: public SubRenderContext
00216 {
00217 CC_DECLARE_DYNCREATE(BlendSubRenderContext);
00218 public:
00219     BlendSubRenderContext() { BlendStep = 1; }
00220 
00221     UINT32 BlendStep;
00222 };
00223 
00224 CC_IMPLEMENT_DYNCREATE(BlendSubRenderContext,SubRenderContext);
00225 
00226 //-----------------------------------------------------------
00227 
00228 // Put my version Number into the About box
00229 DECLARE_SOURCE( "$Revision: 1315 $" );
00230 
00231 CC_IMPLEMENT_DYNCREATE(NodeBlender, NodeRenderableInk)
00232 CC_IMPLEMENT_MEMDUMP(BlendRef,CC_CLASS_MEMDUMP)
00233 CC_IMPLEMENT_MEMDUMP(BlendPath,ListItem)
00234 
00235 CC_IMPLEMENT_MEMDUMP(BecomeA,CC_CLASS_MEMDUMP)
00236 CC_IMPLEMENT_MEMDUMP(BlendBecomeA,BecomeA)
00237 CC_IMPLEMENT_MEMDUMP(HandleBecomeA,CC_CLASS_MEMDUMP)
00238 CC_IMPLEMENT_MEMDUMP(NodeCompoundBlendBecomeA,BecomeA)
00239 
00240 // Declare smart memory handling in Debug builds
00241 // #define new CAM_DEBUG_NEW
00242 
00243 #define MAX_NUM_BLEND_STEPS 999
00244 #define FLATNESS 1024
00245 
00246 /***********************************************************************************************
00247 
00248 >   NodeBlender::NodeBlender(Node*  ContextNode,
00249                         AttachNodeDirection Direction,
00250                         const DocRect&      BoundingRect,
00251                         BOOL                Locked = FALSE,
00252                         BOOL                Mangled = FALSE,
00253                         BOOL                Marked = FALSE,
00254                         BOOL                Selected = FALSE,
00255                         BOOL                Renderable = FALSE
00256                         )
00257 
00258     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00259     Created:    11/10/94
00260     
00261     Inputs:     ContextNode: Pointer to a node which this node is to be attached to.
00262                 MonoOn Direction: MonoOff
00263                 Specifies the direction in which the node is to be attached to the
00264                 ContextNode. The values this variable can take are as follows:
00265                                   
00266                 PREV      : Attach node as a previous sibling of the context node
00267                 NEXT      : Attach node as a next sibling of the context node
00268                 FIRSTCHILD: Attach node as the first child of the context node
00269                 LASTCHILD : Attach node as a last child of the context node
00270 
00271                 BoundingRect: Bounding rectangle
00272 
00273                 The remaining inputs specify the status of the node:
00274             
00275                 Locked:     Is node locked ?
00276                 Mangled:    Is node mangled ?
00277                 Marked:     Is node marked ?
00278                 Selected:   Is node selected ?
00279 
00280     Purpose:    This constructor initialises the nodes flags and links it to ContextNode in the
00281                 direction specified by Direction. All neccesary tree links are updated.
00282 
00283     Note:       Initialise() must be called before the NodeBlender is in a state in which it can be used.
00284     SeeAlso:    Initialise()   
00285     Errors:     An assertion error will occur if ContextNode is NULL
00286 
00287 ***********************************************************************************************/
00288 
00289 NodeBlender::NodeBlender(Node* ContextNode,  
00290                     AttachNodeDirection Direction,  
00291                     BOOL Locked, 
00292                     BOOL Mangled,  
00293                     BOOL Marked, 
00294                     BOOL Selected    
00295               ):NodeRenderableInk(ContextNode, Direction, Locked, Mangled, Marked, Selected )  
00296 {                         
00297     ResetVars();
00298 }
00299 
00300 /*********************************************************************************************
00301 
00302 >   NodeBlender::NodeBlender() 
00303 
00304     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00305     Created:    11/10/94
00306     Purpose:    This constructor creates a NodeBlender linked to no other with all status
00307                 flags false and an uninitialized bounding rectangle.           
00308     Note:       Initialise() must be called before the NodeBlender is in a state in which it can be 
00309                 used.       
00310     SeeAlso:    Initialise()                                                        
00311  
00312 **********************************************************************************************/
00313 /* Technical Notes:   
00314     The default constructor is required so that SimpleCopy will work 
00315 */  
00316 
00317 NodeBlender::NodeBlender(): NodeRenderableInk()
00318 {
00319     ResetVars();
00320 }
00321 
00322 /*********************************************************************************************
00323 
00324 >   NodeBlender::~NodeBlender() 
00325 
00326     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00327     Created:    12/10/94
00328     Purpose:    Default deconstructor
00329  
00330 **********************************************************************************************/
00331 
00332 NodeBlender::~NodeBlender()
00333 {
00334     // Call PostBlend() in case we're trying to be deleted in the middle of rendering.
00335     PostBlend();
00336 
00337     // Delete the two blend references
00338     DELPTR(m_pRefStart);
00339     DELPTR(m_pRefEnd);
00340 }
00341 
00342 /*********************************************************************************************
00343 
00344 >   void NodeBlender::ResetVars() 
00345 
00346     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00347     Created:    8/11/94
00348     Purpose:    Resets all the member vars to default values.
00349                 Should only be called by NodeBlender constructors
00350  
00351 **********************************************************************************************/
00352 
00353 void NodeBlender::ResetVars() 
00354 {
00355     m_pRefStart         = NULL;
00356     m_pRefEnd           = NULL;
00357     m_pNodeStart        = NULL;
00358     m_pNodeEnd      = NULL;
00359     m_ObjIndexStart   = -1;
00360     m_ObjIndexEnd     = -1;
00361     m_PathIndexStart    = -1;
00362     m_PathIndexEnd      = -1;
00363 
00364     m_Initialised       = FALSE;
00365 
00366     m_TempArraySize     = 0;
00367     m_pTempCoords   = NULL;
00368     m_pTempVerbs        = NULL;
00369     m_pTempFlags        = NULL;
00370     m_GBlendBuffSize    = 0;
00371     m_pGBlendBuff   = NULL;
00372 
00373     m_ProportionOfPathDistStart = -1.0;
00374     m_ProportionOfPathDistEnd   = -1.0;
00375 
00376     m_AngleStart = 0.0;
00377     m_AngleEnd   = 0.0;
00378 
00379     m_Reversed = FALSE;
00380     m_BlendedOnCurve = FALSE;
00381     m_NodeBlendPathIndex = -2; //-2 to deal with the case of the previous file format - see
00382                                 // HandleBlendPathRecord for details
00383 }                        
00384  
00385 /***********************************************************************************************
00386 
00387 >   virtual Node* NodeBlender::SimpleCopy()
00388 
00389     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00390     Created:    11/10/94
00391     Returns:    Pointer to a Node
00392     Purpose:    Makes a copy of all the data in the node
00393 
00394 ***********************************************************************************************/
00395 
00396 Node* NodeBlender::SimpleCopy()
00397 {
00398     // Make a new NodeBlender and then copy things into it
00399     NodeBlender* pCopyOfNode = new NodeBlender();
00400     if (pCopyOfNode)
00401         CopyNodeContents(pCopyOfNode);
00402     
00403     return pCopyOfNode;
00404 }            
00405 
00406 
00407 /***********************************************************************************************
00408 
00409 >   void NodeBlender::CopyNodeContents(NodeBlender* pCopyOfNode)
00410 
00411     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00412     Created:    11/10/94
00413     Inputs:     pCopyOfNode - The node to copy data to
00414     Purpose:    Copies the data in this node to pCopyOfNode by first calling the base class to get it to
00415                 copy its stuff, and then copying its own stuff
00416     Scope:      protected
00417     SeeAlso:    NodeRenderableInk::CopyNodeContents
00418 
00419 ***********************************************************************************************/
00420 
00421 void NodeBlender::CopyNodeContents(NodeBlender* pCopyOfNode)
00422 {
00423     NodeRenderableInk::CopyNodeContents(pCopyOfNode);
00424     
00425     // Copy contents specific to derived class here
00426 
00427     pCopyOfNode->m_ObjIndexStart    = m_ObjIndexStart;
00428     pCopyOfNode->m_ObjIndexEnd      = m_ObjIndexEnd;
00429     pCopyOfNode->m_PathIndexStart   = m_PathIndexStart;
00430     pCopyOfNode->m_PathIndexEnd     = m_PathIndexEnd;
00431 
00432     pCopyOfNode->m_ProportionOfPathDistStart = m_ProportionOfPathDistStart;
00433     pCopyOfNode->m_ProportionOfPathDistEnd   = m_ProportionOfPathDistEnd;
00434     pCopyOfNode->m_NodeBlendPathIndex        = m_NodeBlendPathIndex;
00435     pCopyOfNode->m_BlendedOnCurve            = m_BlendedOnCurve;
00436     pCopyOfNode->SetReversed(m_Reversed);
00437 
00438     pCopyOfNode->m_AngleEnd                  = m_AngleEnd;
00439     pCopyOfNode->m_AngleStart                = m_AngleStart;
00440 
00441     pCopyOfNode->SetUninitialised();
00442 }
00443 
00444 
00445 
00446 /***********************************************************************************************
00447 >   void NodeBlender::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00448 
00449     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00450     Created:    18/12/2003
00451     Outputs:    -
00452     Purpose:    Polymorphically copies the contents of this node to another
00453     Errors:     An assertion failure will occur if NodeCopy is NULL
00454     Scope:      protected
00455                                      
00456 ***********************************************************************************************/
00457 
00458 void NodeBlender::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00459 {
00460     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
00461     ENSURE(IS_A(pNodeCopy, NodeBlender), "PolyCopyNodeContents given wrong dest node type");
00462 
00463     if (IS_A(pNodeCopy, NodeBlender))
00464         CopyNodeContents((NodeBlender*)pNodeCopy);
00465 }
00466 
00467 
00468 
00469 /***********************************************************************************************
00470 
00471 >   void NodeBlender::ShowDebugTreeDetails() const
00472 
00473     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00474     Created:    11/10/94
00475     Purpose:    Displays debugging info of the tree
00476     SeeAlso:    NodeRenderableInk::ShowDebugTreeDetails
00477 
00478 ***********************************************************************************************/
00479 #ifdef _DEBUG
00480 void NodeBlender::ShowDebugTreeDetails() const
00481 {                     
00482     // Display a bit of debugging info
00483     // For now, we will just call the base class version
00484     NodeRenderableInk::ShowDebugTreeDetails();  
00485 }
00486 #endif
00487 
00488 /********************************************************************************************
00489 
00490 >   void NodeBlender::GetDebugDetails( StringBase* Str )
00491 
00492     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00493     Created:    11/10/94
00494     Outputs:    Str: String giving debug info about the node
00495     Purpose:    For obtaining debug information about the Node
00496 
00497 ********************************************************************************************/
00498 
00499 void NodeBlender::GetDebugDetails( StringBase* Str )
00500 {
00501 #ifdef _DEBUG
00502     // Call base class
00503     NodeRenderableInk::GetDebugDetails( Str );
00504     
00505     (*Str) += TEXT( "\r\nBlender Data Dump\r\n" );
00506 
00507     TCHAR               buf[500];
00508     camSnprintf( buf, 500,  _T("Blend steps = %ld\r\n")
00509                         _T("m_ObjIndex start = %ld\r\n")
00510                         _T("m_ObjIndex end   = %ld\r\n")
00511                         _T("m_ProportionOfPathDistStart = %f\r\n")
00512                         _T("m_ProportionOfPathDistEnd   = %f\r\n")
00513                         _T("m_AngleStart = %f\r\n")
00514                         _T("m_AngleEnd   = %f\r\n")
00515                         _T("m_NodeBlendPathIndex = %ld\r\n")
00516                         _T("\r\n"),
00517                         GetNumBlendSteps(),
00518                         m_ObjIndexStart,
00519                         m_ObjIndexEnd,
00520                         m_ProportionOfPathDistStart,
00521                         m_ProportionOfPathDistEnd,
00522                         m_AngleStart,
00523                         m_AngleEnd,
00524                         m_NodeBlendPathIndex
00525                         );
00526 
00527     *Str += buf;
00528 
00529     *Str += _T("--------- Start\r\n\r\n");
00530     GetDebugDetails(Str,m_pRefStart);
00531 
00532     *Str += _T("--------- End\r\n\r\n");
00533     GetDebugDetails(Str,m_pRefEnd);
00534 #endif
00535 }
00536 
00537 
00538 /********************************************************************************************
00539 
00540 >   void NodeBlender::GetDebugDetails( StringBase* Str,BlendRef* pBlendRef )
00541 
00542     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00543     Created:    22/2/95
00544     Outputs:    Str: String giving debug info about the node
00545     Purpose:    For obtaining debug information about the blend reference
00546 
00547 ********************************************************************************************/
00548 
00549 void NodeBlender::GetDebugDetails( StringBase* Str, BlendRef* pBlendRef )
00550 {
00551 #ifdef _DEBUG
00552     String_256 TempStr;
00553 
00554     TempStr._MakeMsg( _T("Num blend paths = #1%d\r\n"),pBlendRef->GetNumBlendPaths());
00555     *Str += TempStr;
00556     BlendPath* pBlendPath = pBlendRef->GetFirstBlendPath();
00557     while (pBlendPath != NULL)
00558     {
00559         Path*      pPath   = pBlendPath->GetPath();
00560         PathVerb*  pVerbs  = pPath->GetVerbArray();
00561         PathFlags* pFlags  = pPath->GetFlagArray();
00562         DocCoord*  pCoords = pPath->GetCoordArray();
00563 
00564         TempStr._MakeMsg( _T("Original Mapping = #1%d\r\n"), pBlendPath->GetOrigMapping());
00565         *Str += TempStr;
00566 
00567         for (INT32 i=0; i<pPath->GetNumCoords(); i++)
00568         {
00569             // Add the info to the string
00570             TempStr._MakeMsg( TEXT("#1%d.\t#2%d\t#3%ld,\t#4%ld\t"),
00571                               i, pVerbs[i], pCoords[i].x, pCoords[i].y );
00572             (*Str) += TempStr;
00573 
00574             if (pFlags[i].IsEndPoint)
00575             {
00576                 TempStr._MakeMsg( _T(": E\t") );
00577                 (*Str) += TempStr;
00578             }
00579 
00580             *Str += _T("\r\n");
00581         }
00582         pBlendPath = pBlendRef->GetNextBlendPath(pBlendPath);
00583     }
00584 #endif
00585 }
00586 
00587 
00588 /********************************************************************************************
00589 
00590 >   virtual void NodeBlender::Transform( TransformBase& Trans )
00591 
00592     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00593     Created:    11/10/94
00594     Inputs:     Trans - The transform Object
00595     Purpose:    Transforms all the paths attached to this blender node
00596     SeeAlso:    NodeRenderableInk::Transform()
00597 
00598 ********************************************************************************************/
00599 
00600 void NodeBlender::Transform( TransformBase& Trans )
00601 {
00602     InvalidateBoundingRect();
00603     Deinit();
00604 /*
00605     if (!Initialised) return;
00606 
00607     // The blender's bounding rect is no longer valid
00608     InvalidateBoundingRect();
00609 
00610     // Transform all the BlendPaths in the two blend references
00611 
00612     if (pRefStart != NULL) pRefStart->Transform(Trans);
00613     if (pRefEnd   != NULL) pRefEnd  ->Transform(Trans);
00614 */
00615 }
00616 
00617 
00618 /***********************************************************************************************
00619 
00620 >   BOOL NodeBlender::HidingNode()
00621 
00622     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00623     Created:    14/11/94
00624     Inputs:     -
00625     Outputs:    -
00626     Purpose:    Called whenever the node gets hidden.
00627                 It calls the Deinit() member function
00628 
00629 ***********************************************************************************************/
00630 
00631 BOOL NodeBlender::HidingNode()
00632 {
00633     PostBlend();
00634     Deinit();
00635     return TRUE;
00636 }
00637 
00638 /***********************************************************************************************
00639 
00640 >   BOOL NodeBlender::ShowingNode()
00641 
00642     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00643     Created:    14/11/94
00644     Inputs:     -
00645     Outputs:    -
00646     Purpose:    Called whenever the node gets shown after it's been hidden.
00647                 It calls the Reinit() member function
00648 
00649 ***********************************************************************************************/
00650 
00651 BOOL NodeBlender::ShowingNode()
00652 {
00653     return TRUE;
00654 //  return (Reinit());
00655 }
00656 
00657 /***********************************************************************************************
00658 
00659 >   void NodeBlender::Render(RenderRegion* pRender)
00660 
00661     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00662     Created:    11/10/94
00663     Inputs:     Pointer to a render region
00664     Purpose:    Will render the blended steps of this blender node
00665 
00666 ***********************************************************************************************/
00667 
00668 void NodeBlender::Render(RenderRegion* pRender)
00669 {
00670     pRender->SaveContext();
00671     CreateBlends(pRender,NULL);     // Only supply a render region, because we're only rendering
00672     pRender->RestoreContext();
00673 }  
00674 
00675 /***********************************************************************************************
00676 
00677 >   BOOL NodeBlender::CallBeginBlendStep(BlendNodeParam * pParam)
00678 
00679     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00680     Created:    7/3/2000
00681     Inputs:     pParam      -   the bevel node parameter to pass to all nodes
00682     Outputs:    -
00683     Returns:    -
00684     Purpose:    Calls all begin blend step functions on appropriate nodes
00685 
00686 ***********************************************************************************************/
00687 BOOL NodeBlender::CallBeginBlendStep(BlendNodeParam * pParam, BOOL* usingSumAllPathsPathProcessor)
00688 {
00689     if (pParam->GetRenderRegion() == NULL)
00690     {
00691         // CGS:  if the RenderRegion == NULL, then we might be doing a convert to shapes
00692         // test whether or not we are
00693         if (!pParam->GetHandleBecomeA ())
00694         {
00695             return TRUE;    // er - there ain't much we can do
00696         }
00697         // else we are in a convert to editable shapes - continue
00698     }
00699 
00700     NodeRenderableInk * pCompoundStep = pParam->GetStartBlendPath()->GetBlendNode();
00701 
00702     if (!pCompoundStep->IsCompound())
00703     {
00704         *usingSumAllPathsPathProcessor = FALSE;
00705         return TRUE;
00706     }
00707 
00708     if (((NodeCompound *)pCompoundStep)->IsStartBlendGroupNode())
00709     {
00710         if (!((NodeCompound *)pCompoundStep)->BeginBlendStep(pParam))
00711         {
00712             return FALSE;
00713         }
00714         else
00715         {
00716             // we are using a path processor to do our funky stuff ....
00717             *usingSumAllPathsPathProcessor = TRUE;
00718         }
00719     }
00720 
00721     Node * pStep = pCompoundStep->FindFirstChild();
00722 
00723     while (pStep)
00724     {
00725         if (pStep->IsCompound())
00726         {
00727             NodeCompound * pCompound = (NodeCompound *)pStep;
00728                 
00729             if (pCompound->IsStartBlendGroupNode())
00730             {
00731                 if (!pCompound->BeginBlendStep(pParam))
00732                 {
00733                     return FALSE;
00734                 }
00735                 else
00736                 {
00737                     *usingSumAllPathsPathProcessor = TRUE;
00738                 }
00739             }
00740         }
00741 
00742         pStep = pStep->FindNext();
00743     }
00744 
00745     return TRUE;
00746 }
00747 
00748 /***********************************************************************************************
00749 
00750 >   BOOL NodeBlender::CallEndBlendStep(BlendNodeParam * pParam)
00751 
00752     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00753     Created:    7/3/2000
00754     Inputs:     pCompound   -   the root of the tree to use
00755                 pParam      -   the bevel node parameter to pass to all nodes
00756     Outputs:    -
00757     Returns:    -
00758     Purpose:    Calls all end blend step functions on appropriate nodes
00759 
00760 ***********************************************************************************************/
00761 BOOL NodeBlender::CallEndBlendStep(BlendNodeParam * pParam)
00762 {
00763     // we need to do this bottom up 
00764     if (pParam->GetRenderRegion() == NULL)
00765     {
00766         // CGS:  if the RenderRegion == NULL, then we might be doing a convert to shapes
00767         // test whether or not we are (or were)
00768         if (!pParam->GetHandleBecomeA ())
00769         {
00770             return TRUE;    // er - there ain't much we can do
00771         }
00772         // else we are in a convert to editable shapes - continue
00773     }
00774 
00775     NodeRenderableInk * pCompoundStep =  pParam->GetStartBlendPath()->GetBlendNode();
00776 
00777 //  View * pView = View::GetCurrent();
00778 
00779     if (!pCompoundStep->IsCompound())
00780         return TRUE;
00781 
00782     Node * pStep = pCompoundStep->FindFirstDepthFirst();
00783 
00784     while (pStep && pStep != pCompoundStep)
00785     {
00786         if (pStep->IsCompound())
00787         {
00788             NodeCompound * pCompound = (NodeCompound *)pStep;
00789                 
00790             if (pCompound->IsEndBlendGroupNode())
00791             {
00792                 if (!pCompound->EndBlendStep(pParam))
00793                 {
00794                     return FALSE;
00795                 }
00796             }
00797         }
00798 
00799         pStep = pStep->FindNextDepthFirst(pCompoundStep);
00800     }
00801 
00802     if (((NodeCompound *)pCompoundStep)->IsEndBlendGroupNode())
00803     {
00804         if (!((NodeCompound *)pCompoundStep)->EndBlendStep(pParam))
00805         {
00806             return FALSE;
00807         }
00808     }
00809 
00810     return TRUE;
00811 }
00812 
00813 /***********************************************************************************************
00814 
00815 >   void NodeBlender::CreateBlends(RenderRegion* pRender,HandleBecomeA* pHandleBecomeA)
00816 
00817     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00818     Created:    17/10/94
00819     Inputs:     pRender         = ptr to a render region (Can be NULL)
00820                 pHandleBecomeA  = ptr to class to pass each created blended path to (can be NULL)
00821     Outputs:    -
00822     Returns:    -
00823     Purpose:    Will generate the blended steps of this blender node.
00824                 If pRender != NULL, it will render each blend step to it.
00825                 if pHandleBecomeA != NULL, it will pass each blend step to pHandleBecomeA->PassBack() as it's generated
00826 
00827 ***********************************************************************************************/
00828 
00829 void NodeBlender::CreateBlends(RenderRegion* pRender,HandleBecomeA* pHandleBecomeA)
00830 {
00831     TRACEUSER( "ChrisS", _T("Entering create blends\n"));
00832     
00833     // Ensure that we are initialised (if it can be done!!)
00834     if (!Reinit())
00835         return;
00836     if (!m_Initialised) return;     // we MUST handle the this case! (blending NULL to NULL etc.)
00837     // Check the important member vars, to be on the safe side
00838     if (m_pRefStart == NULL || m_pRefEnd == NULL)
00839     {
00840         ERROR3("Dodgey pRef params");
00841         return;
00842     }
00843     if (m_pNodeStart == NULL || m_pNodeEnd == NULL)
00844     {
00845         ERROR3("Dodgey m_pNodeStart and/or m_pNodeEnd params");
00846         return;
00847     }
00848 
00849     // If the render region's quality does not state full blend rendering,
00850     // proceed as if we are not rendering at all (this is done by pretending we haven't
00851     // got a render region
00852     if (pRender != NULL && pRender->RRQuality.GetBlendQuality() != Quality::FullBlend)
00853         pRender = NULL;
00854 
00855     // If we're not rendering and not handling becoming paths, exit now.
00856     if (pRender == NULL && pHandleBecomeA == NULL)
00857         return;
00858 
00859     // Only allow background rendering when going to a DIB
00860     BOOL AllowBackgroundRender = (pRender!=NULL &&
00861                                   ( IS_A(pRender,GRenderDIB) ||
00862                                     pRender->IsHitDetect() )
00863                                   );
00864     
00865     // If somebody else has locked the sub render context, then we cannot BG render without stepping on their toes.
00866     if (AllowBackgroundRender && pRender->IsSubRenderStateLocked())
00867         AllowBackgroundRender = FALSE;
00868 
00869     // This should stop any problems with blends not fully rendering in separation views
00870     if (AllowBackgroundRender && pRender->IsPrinting())
00871         AllowBackgroundRender = FALSE;
00872 
00873     // If we are rendering (i.e. pRender != NULL) do some work with the quality settings
00874     if (pRender != NULL)
00875     {
00876         // If this blend is set for non-antialiased blend steps AND the quality is set for full antialiasing
00877         // we need to render an attr that means "do everything except antialias"
00878         if (IsNotAntialiased() && pRender->RRQuality.GetAntialiasQuality() == Quality::FullAntialias)
00879         {
00880             // Create a non-antialias quality attr to render
00881             Quality Qual;
00882             QualityAttribute *pQualAttrVal = new QualityAttribute(Qual);
00883 
00884             if (pQualAttrVal != NULL)
00885             {
00886                 // Render the attribute, and mark it Temporary so it will be auto-deleted
00887                 // when it is finished with.
00888                 pRender->SetQuality(pQualAttrVal, TRUE);
00889             }
00890         }
00891     }
00892 
00893     // Get the start and end bounds so we can do fast blended bounds intersection tests
00894     // on the render region before going ahead with the heavy stuff
00895     DocRect BoundsStart = m_pNodeStart->GetBoundingRect();
00896     DocRect BoundsEnd   = m_pNodeEnd  ->GetBoundingRect();
00897 
00898     UINT32 NumBlendSteps = GetNumBlendSteps();
00899 
00900     // Check for silly numbers of blend steps
00901     if (NumBlendSteps > MAX_NUM_BLEND_STEPS) 
00902         NumBlendSteps = MAX_NUM_BLEND_STEPS;
00903 
00904     m_BlendStep = IsReversed() ? NumBlendSteps : 1;
00905 
00906     BlendSubRenderContext* pSubRenderContext = NULL;
00907     if (pRender != NULL)
00908     {
00909         pSubRenderContext = (BlendSubRenderContext*)pRender->GetSubRenderState();
00910 
00911 //      ERROR3IF(pSubRenderContext != NULL && !IS_A(pSubRenderContext,BlendSubRenderContext),"Sub render context is of wrong type");
00912 
00913         // If we don't own the S.R.C, then we won't treat it as our own
00914         if (pSubRenderContext != NULL && !IS_A(pSubRenderContext,BlendSubRenderContext))
00915             pSubRenderContext = NULL;
00916 
00917         if (pSubRenderContext != NULL)
00918         {
00919             m_BlendStep = pSubRenderContext->BlendStep;
00920         }
00921     }
00922 
00923     // Find num blend paths in start and end, and keep hold of the MAX value
00924     UINT32 NumPathsInStart = m_pRefStart->GetNumBlendPaths();
00925     UINT32 NumPathsInEnd   = m_pRefEnd  ->GetNumBlendPaths();
00926     UINT32 MaxNumPaths   = max(NumPathsInStart,NumPathsInEnd);
00927 
00928     // If either start or end has 0 blend paths, return.
00929     // DMc - not any more !
00930     if (min(NumPathsInStart,NumPathsInEnd) == 0)
00931     {
00932         return;
00933     }
00934     
00935     // Let the HandleBecomeA class know the max num paths in the start and end objects
00936     if (pHandleBecomeA != NULL)
00937         pHandleBecomeA->SetNumPathsPerStep(MaxNumPaths);
00938 
00939     // This object is used to step along the the two lists of objects choosing a pair to go and blend.
00940     // It mainly comes into play when there are different numbers of objects in the two lists.
00941     ListStepper Stepper;
00942 
00943     // BlendRatio     - always a value between 0.0 and 1.0;
00944     // BlendRatioStep - the value that is added to BlendRatio on every blend step
00945     double BlendRatio;
00946     double BlendRatioStep = 1.0/(NumBlendSteps+1);
00947 
00948     Path BlendedPath;
00949     if (!BlendedPath.Initialise())
00950         return;
00951 
00952     // Do some stuff before we start blending (if this is the first time this node has been asked to render)
00953     if (pSubRenderContext == NULL)
00954     {
00955         PreBlend();
00956 
00957     }
00958 
00959     // This holds blended attributes that are generated during the blend
00960     CCAttrMap BlendedAttrMap(30);
00961 
00962 
00963     // If we will use background rendering, we must make sure nobody inside the blend tries
00964     // to do backgrounding as well, as we can only store one sub-render context. This bodge
00965     // locks the sub render state so that nobody else is allowed to use it. (Quick bodge
00966     // by Jason to allow strokes to background render, but not crash when they're inside
00967     // a blend - in this case, individual strokes become non-interruptible, and leave
00968     // the background render support entirely to the containing blend)
00969     // We must take care to see that we unlock the state when we are done rendering.
00970     if (AllowBackgroundRender)
00971         pRender->LockSubRenderState(TRUE);
00972 
00973     // ContinueRender is set to FALSE if we should stop rendering the blend
00974     BOOL                ContinueRender = TRUE;
00975     INT32                   Counter = 1;
00976     UINT32              StopBlendStep = NumBlendSteps +1 ;
00977 
00978     if (IsReversed())
00979     {
00980         Counter = -1;
00981         StopBlendStep = 0;
00982     }
00983 
00984 //  NodeRenderableInk * pLastCreatedByNode = NULL;
00985 //  NodeRenderableInk * pLastBlendStartNode = NULL;
00986 
00987     // the parameter for the blending of nodes
00988     BlendNodeParam BNParam;
00989 
00990     // Graeme (21-11-00) - Tightened up the check on what was the for loop (and now a while
00991     // loop), so that it stops when the blend step gets to be equal to, or greater than, the
00992     // StopBlendStep value. (Or less than if it's a reversed blend.)
00993     //
00994     // The original code stopped when the current blend step equalled the stop value, which
00995     // meant that if the m_BlendStep value leapfrogged StopBlendStep an infinite loop could
00996     // occur. (The BlendRatio would get out of bounds pretty quick, though, and that would
00997     // stop the loop within a couple more iterations.)
00998     while ( ContinueRender &&
00999             ( !IsReversed() && m_BlendStep < StopBlendStep ) ||
01000             ( IsReversed() && m_BlendStep > StopBlendStep ) )
01001     {
01002 
01003         // Calc the blend ratio using the current blend step
01004         double LinearBlendRatio = BlendRatioStep * m_BlendStep;
01005         BlendRatio = MapObjectBlendRatio(LinearBlendRatio);
01006         
01007         // Get the stepper object ready for the current blend step
01008         INT32 NextPathStart,NextPathEnd;
01009         Stepper.Init(NumPathsInStart,NumPathsInEnd,0);
01010 
01011         // Reset the ptrs and subpath IDs of the previously blended blendpath pairs.
01012         BlendPath*  pPrevBlendPathStart = NULL;
01013         BlendPath*  pPrevBlendPathEnd   = NULL;
01014         UINT32      PrevSubPathIDStart  = 0;
01015         UINT32      PrevSubPathIDEnd    = 0;
01016 
01017         // Get the first pair of objects to blend
01018         Stepper.GetNext(&NextPathStart,&NextPathEnd);
01019 
01020         // The first pair of blend paths become the current blend path pair
01021         // Get ptrs to these, plus the subpath IDs of these
01022         BlendPath*  pCurrBlendPathStart = m_pRefStart->GetBlendPath(NextPathStart);
01023         BlendPath*  pCurrBlendPathEnd   = m_pRefEnd  ->GetBlendPath(NextPathEnd  );
01024         UINT32      CurrSubPathIDStart  = pCurrBlendPathStart->GetSubPathID();
01025         UINT32      CurrSubPathIDEnd    = pCurrBlendPathEnd  ->GetSubPathID();
01026 
01027         // We also need info on the next blend path pair, so get some vars ready
01028         BlendPath*  pNextBlendPathStart;
01029         BlendPath*  pNextBlendPathEnd;
01030         UINT32      NextSubPathIDStart;
01031         UINT32      NextSubPathIDEnd;
01032 
01033         // Check to see if the blended bounds intersect with the render region's clip rect
01034         BOOL BoundsIntersect = TRUE;
01035         if (pRender != NULL && pHandleBecomeA == NULL)
01036             BoundsIntersect = CheckBoundsIntersect(pRender,BoundsStart,BoundsEnd,BlendRatio);
01037 
01038         DocRect  RawBlendedBounds = BlendBounds(BoundsStart,BoundsEnd,BlendRatio,FALSE);
01039         DocCoord BBCentre = RawBlendedBounds.Centre();
01040 
01041         // CGS:  I have to allocate this here - because otherwise memory becomes toast within
01042         // subsequent iterations of the following loop ....
01043         SumAllPathsPathProcessor * ptrProc = new SumAllPathsPathProcessor (FALSE);
01044         BOOL* usingSumAllPathsPathProcessor = new BOOL;
01045         *usingSumAllPathsPathProcessor = FALSE;             // and whether we are using it or not
01046                                                             // this is reset within each call to
01047         // BeginBlendStep ().  This allows us to select between conventional passback and
01048         // new SumAllPathsPathProcessor stuff when blending.
01049 
01050         // This thereby allows us to handles (.e.g):
01051 
01052         // simple shape to simple shape
01053         // shadow to shadow
01054         // grouped shadow(s) to shadow
01055         // shadowed group to shadow
01056         // shadow to simple shape
01057         // grouped shadow(s) to simple shape
01058         // shadowed group to simple shape
01059         // grouped shadow(s) to grouped shadow(s)
01060         // shadowed group to shadowed group
01061         // shadowed group to grouped shadow
01062         
01063         // you get the general gist ....
01064 
01065         // We need to blend all the blend paths together, so do MaxNumPaths blends
01066         for (UINT32 NextPath = 0; ContinueRender && BoundsIntersect && (NextPath < MaxNumPaths); NextPath++)
01067         {
01068             // Get the next pair of objects to blend
01069             Stepper.GetNext(&NextPathStart,&NextPathEnd);
01070 
01071             // Ensure GLA's which applied indiviually to child objects (ie individual BlendPaths within 
01072             // either BlendRef) are removed from the BlendAttrMap when changing to next BlendPath
01073             // If this doesn't occur then blended GLA left from Blend of previous BlendPath will be
01074             // applied to subsequent BlendPaths if they're not replaced (ie if subsequent BlendPaths
01075             // don't have a valid GLA applied)
01076             BlendedAttrMap.RemoveGeometryLinkedAttrs();
01077 
01078             if (NextPathStart >= 0 && NextPathEnd >= 0)
01079             {
01080                 // There's another pair to blend, so get ptrs to them, plus their subpath IDs
01081                 pNextBlendPathStart = m_pRefStart->GetBlendPath(NextPathStart);
01082                 pNextBlendPathEnd   = m_pRefEnd  ->GetBlendPath(NextPathEnd  );
01083                 NextSubPathIDStart  = pNextBlendPathStart->GetSubPathID();
01084                 NextSubPathIDEnd    = pNextBlendPathEnd  ->GetSubPathID();
01085             }
01086             else
01087             {
01088                 // No more blend pairs after this one
01089                 pNextBlendPathStart = NULL;
01090                 pNextBlendPathEnd   = NULL;
01091                 NextSubPathIDStart  = 0;
01092                 NextSubPathIDEnd    = 0;
01093             }
01094 
01095             // Check the ptrs to be on the safe side
01096             // DMc - we don't anymore - since if either are null this means we'll
01097             // go into the new code
01098             if (pCurrBlendPathStart == NULL || pCurrBlendPathEnd == NULL)
01099             {
01100                 if (AllowBackgroundRender)
01101                     pRender->LockSubRenderState(FALSE);         // Unlock the sub-render state
01102                 return;
01103             }
01104             
01105             double AttrBlendRatio = MapAttrBlendRatio(LinearBlendRatio);
01106 
01107             NodeRenderableInk * pStartBlendNode = NULL;
01108             NodeRenderableInk * pEndBlendNode = NULL;
01109 
01110             if (LinearBlendRatio <= 0.5)    // CGS:  27/7/2000  We used to use BlendRatio for
01111                                             // this comparison.  We now use LinearBlendRatio since
01112                                             // this avoids the problem of 'compound jumping' when
01113                                             // blending from a compound to a non-compound
01114             {
01115                 BNParam.Init(GetNodeBlend (), pRender, pCurrBlendPathStart, pCurrBlendPathEnd, BlendRatio,
01116                     AttrBlendRatio, m_AngleStart, m_AngleEnd, GetObjectRatio(),
01117                     GetInvertedAttributeRatio(), GetColourBlendType(),
01118                     pCurrBlendPathStart->GetBlendNode(), pCurrBlendPathEnd->GetBlendNode(),
01119                     IsOneToOne(), GetNodeBlendPath(), pHandleBecomeA, ptrProc);
01120 
01121                 pStartBlendNode = pCurrBlendPathStart->GetBlendNode();
01122                 pEndBlendNode = pCurrBlendPathEnd->GetBlendNode();
01123             }
01124             else
01125             {
01126                 BNParam.Init(GetNodeBlend (), pRender, pCurrBlendPathEnd, pCurrBlendPathStart, 1.0 - BlendRatio,
01127                     1.0 - AttrBlendRatio, m_AngleStart, m_AngleEnd, 1.0 - GetObjectRatio(),
01128                     1.0 - GetInvertedAttributeRatio(), GetColourBlendType(),
01129                     pCurrBlendPathEnd->GetBlendNode(), pCurrBlendPathStart->GetBlendNode(),
01130                     IsOneToOne(), GetNodeBlendPath(), pHandleBecomeA, ptrProc);
01131 
01132                 pStartBlendNode = pCurrBlendPathEnd->GetBlendNode();
01133                 pEndBlendNode = pCurrBlendPathStart->GetBlendNode();
01134             }
01135 
01136             if (!CallBeginBlendStep(&BNParam, usingSumAllPathsPathProcessor))
01137             {
01138                 return ;
01139             }
01140 
01141             if (BlendAttributes(pRender,pCurrBlendPathStart,pCurrBlendPathEnd,&BlendedAttrMap,AttrBlendRatio, BlendRatio, GetNodeBlend ()->GetObjectProfileProcessing ()))
01142             {
01143                 // Blend the paths together, putting the blended path in BlendedPath
01144                 if (BlendPaths(pCurrBlendPathStart,pCurrBlendPathEnd,BlendRatio))
01145                 {
01146                     // The blended path will be filled  if either of the paths are filled
01147                     // The blended path will be stroked if either of the paths are stroked
01148                     BOOL Filled  = pCurrBlendPathStart->IsFilled()  || pCurrBlendPathEnd->IsFilled();
01149                     BOOL Stroked = pCurrBlendPathStart->IsStroked() || pCurrBlendPathEnd->IsStroked();
01150                     
01151                     // If the current subpath ID is the same as the previous one, and the current path is
01152                     // different to the previous path, dont clear the blended path 
01153                     // (this applies to the start OR the end path)
01154                     BOOL DontClearPath = ((CurrSubPathIDStart == PrevSubPathIDStart && pCurrBlendPathStart != pPrevBlendPathStart) ||
01155                         (CurrSubPathIDEnd   == PrevSubPathIDEnd   && pCurrBlendPathEnd   != pPrevBlendPathEnd  ));
01156                     
01157                     if (!DontClearPath)
01158                         BlendedPath.ClearPath(FALSE);
01159                     
01160                     if (!BlendedPath.MakeSpaceInPath(m_ArrayLength)) goto ExitRender;
01161                     BlendedPath.MergeTwoPaths(m_pTempCoords,m_pTempVerbs,m_pTempFlags,m_ArrayLength,Filled);
01162                     BlendedPath.IsFilled  = Filled;
01163                     BlendedPath.IsStroked = Stroked;
01164                     
01165                     // We haven't yet got a complete path to render or pass back if next path has the same ID and is
01166                     // a different path
01167                     // (this applies to the start OR the end path)
01168                     BOOL NotACompletePath = ((CurrSubPathIDStart == NextSubPathIDStart && pCurrBlendPathStart != pNextBlendPathStart) ||
01169                         (CurrSubPathIDEnd   == NextSubPathIDEnd   && pCurrBlendPathEnd   != pNextBlendPathEnd  ));
01170                     
01171                     if (!NotACompletePath)
01172                     {
01173                         // DY 9/99 added this condition to enable us to blend to 
01174                         // and from blends on paths.
01175                         
01176                         
01177                         if (m_BlendedOnCurve)
01178                         {
01179                             DocCoord PointOnPath;
01180                             double Angle = 0.0;
01181                             if (GetPointOnNodeBlendPath(BlendRatio,&PointOnPath,&Angle))                                    
01182                             {
01183                                 Trans2DMatrix Translate(PointOnPath.x-BBCentre.x,PointOnPath.y-BBCentre.y);
01184                                 Trans2DMatrix Rotate(BBCentre,Angle);
01185                                 
01186                                 if (Angle != 0.0)
01187                                 {
01188                                     Rotate.Transform( (DocCoord*)BlendedPath.GetCoordArray(), BlendedPath.GetNumCoords() );
01189                                     BlendedAttrMap.Transform(Rotate);
01190                                 }
01191                                 
01192                                 Translate.Transform( (DocCoord*)BlendedPath.GetCoordArray(), BlendedPath.GetNumCoords() );
01193                                 // not a regular transform for the attributes, as we may want to tile
01194                                 BlendedAttrMap.TransformForBrush(Translate);
01195                             }
01196                         }
01197 
01198                         // Ilan 4/5/00
01199                         // Call the PostDynCreateInit functions for the attributes which require this
01200                         // NB attrs which don't succeed in init'ing will mark themselves so that they don't NeedToRender()
01201                         // ie the AttrMap can safely be rendered - the united attrs simply won't do their job
01202                         BlendedAttrMap.PostBlendInit(&BlendedPath, GetRuntimeClass());
01203                         
01204                         // CGS:  Now are we doing a convert to editable shapes ????
01205 
01206                         if (pHandleBecomeA != NULL)
01207                         {
01208                             // we are - so test if we have a SumAllPathsPathProcessor ....
01209                             
01210                             if (!(*usingSumAllPathsPathProcessor))
01211                             {
01212                                 // we don't - so we are not dealing with compound nodes
01213                                 // Pass the path back (as usual)
01214                                 ContinueRender = pHandleBecomeA->PassBack(&BlendedPath,&BlendedAttrMap,m_BlendStep);
01215                             }
01216                             else
01217                             {   
01218                                 // we do - so we are dealing with compound nodes ....
01219 
01220                                 if  ((pHandleBecomeA->GetBecomeA ()->GetReason () == BECOMEA_REPLACE) ||
01221                                      (pHandleBecomeA->GetBecomeA ()->GetReason () == BECOMEA_PASSBACK)
01222                                     )
01223                                 {
01224                                     Path* pNewBlendedPath = new Path;
01225                                     pNewBlendedPath->Initialise (BlendedPath.GetNumCoords ());
01226                                     
01227                                     pNewBlendedPath->CopyPathDataFrom (&BlendedPath);
01228 
01229                                     CCAttrMap* pNewBlendedAttrMap = BlendedAttrMap.Copy ();
01230                                     
01231                                     SumAllPathsElem * pElem = new SumAllPathsElem(pNewBlendedPath, pNewBlendedAttrMap);
01232 
01233                                     ptrProc->InsertSumAllPathsElem (pElem);
01234 
01235                                     ContinueRender = TRUE;
01236 
01237                                     delete (pNewBlendedPath);
01238                                 }
01239                             }
01240                         }
01241 
01242                         if (pRender != NULL)
01243                         {
01244                             pRender->SaveContext();
01245 
01246                             // CGS:
01247 
01248                             if (!(*usingSumAllPathsPathProcessor))
01249                             {
01250                                 RenderAttributes(pRender,&BlendedAttrMap);
01251                                             
01252                                 pRender->DrawPath(&BlendedPath);
01253                             }
01254                             else
01255                             {
01256                                 Path* pNewBlendedPath = new Path;
01257                                 pNewBlendedPath->Initialise (BlendedPath.GetNumCoords ());
01258                                     
01259                                 pNewBlendedPath->CopyPathDataFrom (&BlendedPath);
01260 
01261                                 CCAttrMap* pNewBlendedAttrMap = BlendedAttrMap.Copy ();
01262                                     
01263                                 SumAllPathsElem * pElem = new SumAllPathsElem(pNewBlendedPath, pNewBlendedAttrMap);
01264 
01265                                 ptrProc->InsertSumAllPathsElem (pElem);
01266 
01267                                 ContinueRender = TRUE;
01268 
01269                                 delete (pNewBlendedPath);
01270                             }
01271                         
01272                             pRender->RestoreContext();
01273                         }
01274 
01275                         // Ilan 4/5/00
01276                         // Deinitialise the attrs which allocated memory in the PostBlendInit() fn call
01277                         BlendedAttrMap.PostBlendDeinit();
01278                     }
01279                 }
01280             }
01281 
01282             TRACEUSER( "ChrisS", _T("Blend step == %f\n"), BlendRatio);
01283 
01284             // restore the blend paths
01285             if (!CallEndBlendStep(&BNParam))
01286             {
01287                 return ;
01288             }
01289             
01290             // Set up the vars for the next pair of blend paths
01291             // The Current vars become the previous vars
01292             PrevSubPathIDStart  = CurrSubPathIDStart;
01293             PrevSubPathIDEnd    = CurrSubPathIDEnd;
01294             pPrevBlendPathStart = pCurrBlendPathStart;
01295             pPrevBlendPathEnd   = pCurrBlendPathEnd;
01296 
01297             // The next vars become the current vars
01298             CurrSubPathIDStart  = NextSubPathIDStart;
01299             CurrSubPathIDEnd    = NextSubPathIDEnd;
01300             pCurrBlendPathStart = pNextBlendPathStart;
01301             pCurrBlendPathEnd   = pNextBlendPathEnd;
01302         }
01303 
01304         if (usingSumAllPathsPathProcessor != NULL)
01305         {
01306             delete (usingSumAllPathsPathProcessor);     // clear out the boolean that was allocated
01307         }
01308         
01309         if (ptrProc != NULL)
01310         {
01311             delete (ptrProc);       //  clear out the path processor if we allocated one
01312         }
01313 
01314         // This section communicates with the render region (if there is one) to see if
01315         // we have taken too long to render this step, and hence need to update the screen during
01316         // background redraw.
01317 
01318         // Have we got a render region and have we actually rendered anything on this step?
01319         if (pRender != NULL && BoundsIntersect)
01320         {
01321             View* pView = pRender->GetRenderView();
01322             if (pView != NULL)
01323             {
01324                 if (AllowBackgroundRender && (pRender->IsHitDetect() || !pRender->RenderTreeCanContinue()))
01325                 {
01326                     // CanContinue() has returned FALSE, so we need to try and save the sub render
01327                     // context so that the render region can call us back next time
01328 
01329                     // Have we got a sub render context object?
01330                     if (pSubRenderContext == NULL)
01331                     {
01332                         // If not, create a new BlendSubRenderContext object and make this the one
01333                         // the render region should use via SetSubRenderState()
01334                         pSubRenderContext = new BlendSubRenderContext;
01335                         if (pSubRenderContext != NULL && pRender->GetSubRenderState() != pSubRenderContext)
01336                         {
01337                             // Turn off the sub render lock now, or my silly ERROR3 will go off!
01338                             pRender->LockSubRenderState(FALSE);
01339                             pRender->SetSubRenderState(pSubRenderContext);
01340                         }
01341                     }
01342 
01343                     if (pSubRenderContext != NULL)
01344                     {
01345                         // If we have a sub render context object, set it up and exit this render
01346                         // call at an appropriate point
01347                         pSubRenderContext->BlendStep = m_BlendStep+Counter; // Render the next blend step next time
01348                         goto ExitRender;
01349                     }
01350                 }
01351             }
01352             
01353         }
01354 
01355         // Increment the counter.
01356         m_BlendStep+= Counter;
01357 
01358     } // end while
01359 
01360     if (pRender != NULL && pSubRenderContext != NULL)
01361     {
01362         // Here we have finished rendering the whole blender node
01363         // We must make sure we delete the sub render context object, and set the render region's
01364         // sub render state to NULL
01365         delete pSubRenderContext;
01366         pSubRenderContext = NULL;
01367         pRender->SetSubRenderState(NULL);
01368     }
01369     
01370     // Do some stuff after the blend
01371     PostBlend();
01372 
01373 ExitRender:
01374     // delete any attributes that are left in the blended attr map
01375     BlendedAttrMap.DeleteAttributes();
01376     
01377     // Finally, unlock the sub-render context so others may use it (now that it is relatively safe)
01378     if (AllowBackgroundRender)
01379         pRender->LockSubRenderState(FALSE);
01380 
01381 
01382     TRACEUSER( "ChrisS", _T("Exiting create blends\n"));
01383 }  
01384 
01385 
01386 /********************************************************************************************
01387 
01388 > BOOL NodeBlender::CheckBoundsIntersect(RenderRegion* pRender,DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio)
01389 
01390     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01391     Created:    2/12/94
01392     Inputs:     pRender     = ptr to the current render region
01393                 BoundsStart = the blob bounds of start object
01394                 BoundsEnd   = the blob bounds of end object
01395                 BlendRatio  = current blend point (between 0.0 and 1.0)
01396     Outputs:    -
01397     Returns:    TRUE if blended bounds intersect with the render region's clip rect.
01398     Purpose:    Finds the blended bounds that lies at point BlendRatio and tests for intersection.
01399     SeeAlso:    -
01400 
01401 ********************************************************************************************/
01402 
01403 BOOL NodeBlender::CheckBoundsIntersect(RenderRegion* pRender,DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio)
01404 {
01405     BOOL Clip = TRUE;
01406 
01407     if (pRender != NULL)
01408     {
01409         DocRect BlendedBounds = BlendBounds(BoundsStart,BoundsEnd,BlendRatio,TRUE);
01410         Clip = BlendedBounds.IsIntersectedWith(pRender->GetClipRect());
01411         //pRender->DrawRect(&BlendedBounds);
01412     }
01413 
01414     return Clip;
01415 }
01416 
01417 /********************************************************************************************
01418 
01419 > DocRect NodeBlender::BlendBounds(DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio)
01420 
01421     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01422     Created:    5/5/99
01423     Inputs:     BoundsStart = the blob bounds of start object
01424                 BoundsEnd   = the blob bounds of end object
01425                 BlendRatio  = current blend point (between 0.0 and 1.0)
01426     Outputs:    -
01427     Returns:    The blended bounds.
01428     Purpose:    Finds the blended bounds that lies at point BlendRatio and tests for intersection.
01429                 Takes into account the path the blend is following, if it has one.
01430     SeeAlso:    -
01431 
01432 ********************************************************************************************/
01433 
01434 DocRect NodeBlender::BlendBounds(DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio,BOOL UseBlendPath)
01435 {
01436     DocRect BlendedBounds,OldBoundsStart,OldBoundsEnd;
01437 
01438     if (UseBlendPath)
01439     {
01440         OldBoundsStart = BoundsStart;
01441         OldBoundsEnd   = BoundsEnd;
01442         RotateBounds(BoundsStart,-m_AngleStart);
01443         RotateBounds(BoundsEnd,-m_AngleEnd);
01444     }
01445 
01446     BlendedBounds.lo.x = INT32(BoundsStart.lo.x + ((BoundsEnd.lo.x-BoundsStart.lo.x)*BlendRatio));
01447     BlendedBounds.lo.y = INT32(BoundsStart.lo.y + ((BoundsEnd.lo.y-BoundsStart.lo.y)*BlendRatio));
01448     BlendedBounds.hi.x = INT32(BoundsStart.hi.x + ((BoundsEnd.hi.x-BoundsStart.hi.x)*BlendRatio));
01449     BlendedBounds.hi.y = INT32(BoundsStart.hi.y + ((BoundsEnd.hi.y-BoundsStart.hi.y)*BlendRatio));
01450 
01451     if (UseBlendPath)
01452     {
01453         DocCoord PointOnPath;
01454         double Angle = 0.0;
01455         if (GetPointOnNodeBlendPath(BlendRatio,&PointOnPath,&Angle))
01456         {
01457             DocCoord BBCentre = BlendedBounds.Centre();
01458             BlendedBounds.Translate(PointOnPath.x-BBCentre.x,PointOnPath.y-BBCentre.y);
01459 
01460             RotateBounds(BlendedBounds,Angle);
01461         }
01462     }
01463 
01464     if (UseBlendPath)
01465     {
01466         BoundsStart = OldBoundsStart;
01467         BoundsEnd   = OldBoundsEnd;
01468     }
01469 
01470     return BlendedBounds;
01471 }
01472 
01473 /********************************************************************************************
01474 
01475 > void NodeBlender::RotateBounds(DocRect& Bounds,double Angle)
01476 
01477     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01478     Created:    21/5/99
01479     Inputs:     Bounds = the bounds to rotate
01480                 Angle  = the angle in degrees by which to rotate it by
01481     Outputs:    -
01482     Returns:    -
01483     Purpose:    The bounds are updated to contain the rotated version
01484     SeeAlso:    -
01485 
01486 ********************************************************************************************/
01487 
01488 void NodeBlender::RotateBounds(DocRect& Bounds,double Angle)
01489 {
01490     if (Angle != 0.0)
01491     {
01492         DocCoord BBCentre = Bounds.Centre();
01493 
01494         DocCoord Coords[4];
01495         Coords[0].x = Bounds.lo.x;  Coords[0].y = Bounds.lo.y;
01496         Coords[1].x = Bounds.hi.x;  Coords[1].y = Bounds.lo.y;
01497         Coords[2].x = Bounds.hi.x;  Coords[2].y = Bounds.hi.y;
01498         Coords[3].x = Bounds.lo.x;  Coords[3].y = Bounds.hi.y;
01499 
01500         Trans2DMatrix Rotate(BBCentre,Angle);
01501         Rotate.Transform(Coords,4);
01502 
01503         Bounds.lo.x = min(min(Coords[0].x,Coords[1].x), min(Coords[2].x,Coords[3].x));
01504         Bounds.hi.x = max(max(Coords[0].x,Coords[1].x), max(Coords[2].x,Coords[3].x));
01505         Bounds.lo.y = min(min(Coords[0].y,Coords[1].y), min(Coords[2].y,Coords[3].y));
01506         Bounds.hi.y = max(max(Coords[0].y,Coords[1].y), max(Coords[2].y,Coords[3].y));
01507     }
01508 }
01509 
01510 /********************************************************************************************
01511 
01512 > Trans2DMatrix* NodeBlender::GetRotateMatrix(NodeRenderableBounded* pNode,double Angle)
01513 
01514     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01515     Created:    25/5/99
01516     Inputs:     pNode = the node who's bounds define the centre of rotation
01517                 Angle = Angle to rotate by, in degrees
01518     Outputs:    -
01519     Returns:    ptr to the matrix that will perform the rotation
01520                 or NULL if no rotation is necessary (i.e. Angle % 360.0 == 0.0)
01521     Purpose:    Function that returns a matrix that will rotate around the centre of the bounds of
01522                 the given node
01523     SeeAlso:    -
01524 
01525 ********************************************************************************************/
01526 
01527 Trans2DMatrix* NodeBlender::GetRotateMatrix(NodeRenderableBounded* pNode,double Angle)
01528 {
01529     Trans2DMatrix* pMatrix = NULL;
01530 
01531     if (pNode != NULL && fmod(Angle,360.0) != 0.0)
01532     {
01533         DocRect BoundsStart = pNode->GetBoundingRect();
01534         DocCoord Centre = BoundsStart.Centre();
01535         pMatrix = new Trans2DMatrix(Centre,Angle);
01536     }
01537 
01538     return pMatrix;
01539 }
01540 
01541 /********************************************************************************************
01542 
01543 > DocRect NodeBlender::GetUnionBlendedBounds(DocRect& BoundsStart,DocRect& BoundsEnd)
01544 
01545     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01546     Created:    5/5/99
01547     Inputs:     -
01548     Outputs:    -
01549     Returns:    The union of blended bounds.
01550     Purpose:    Finds the blended bounds that lies at point BlendRatio and tests for intersection.
01551                 Takes into account the path the blend is following, if it has one.
01552     SeeAlso:    -
01553 
01554 ********************************************************************************************/
01555 
01556 DocRect NodeBlender::GetUnionBlendedBounds(DocRect& BoundsStart,DocRect& BoundsEnd)
01557 {
01558     DocRect UnionBlendedBounds(0,0,0,0);
01559 
01560     UINT32 NumBlendSteps = GetNumBlendSteps();
01561     double BlendRatio = 0.0;
01562     double BlendRatioStep = 1.0/(NumBlendSteps+1);
01563 
01564     for (UINT32 BlendStep = 1; BlendStep <= NumBlendSteps; BlendStep++)
01565     {
01566         // Calc the blend ratio using the current blend step
01567         BlendRatio = BlendRatioStep * BlendStep;
01568 
01569         DocRect BlendedBounds = BlendBounds(BoundsStart,BoundsEnd,BlendRatio,TRUE);
01570 
01571         if (BlendStep == 1)
01572             UnionBlendedBounds = BlendedBounds;
01573         else
01574             UnionBlendedBounds = UnionBlendedBounds.Union(BlendedBounds);
01575     }
01576 
01577     return UnionBlendedBounds;
01578 }
01579 
01580 /********************************************************************************************
01581 
01582 >   void NodeBlender::RenderAttributes(RenderRegion* pRender,CCAttrMap* pAttrMap)
01583 
01584     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01585     Created:    20/10/94
01586     Inputs:     pRender = ptr to render region
01587                 pAttrMap = ptr to map of ttributes
01588     Outputs:    -
01589     Returns:    -
01590     Purpose:    Renders the attributes to the given render region
01591     SeeAlso:    -
01592 
01593 ********************************************************************************************/
01594 
01595 void NodeBlender::RenderAttributes(RenderRegion* pRender,CCAttrMap* pAttrMap)
01596 {
01597     // Check entry params
01598     BOOL ok = (pRender != NULL && pAttrMap != NULL);
01599     ERROR3IF(!ok,"One or more NULL entry params");
01600     if (!ok) return;
01601 
01602     CCRuntimeClass* pType;
01603     void* pVal;
01604 
01605     // iterating all (key, value) pairs
01606     for (CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); Pos != pAttrMap->GetEndPosition();)
01607     {
01608         // Get attr at CMapPtrToPtr::iterator Pos
01609         pAttrMap->GetNextAssoc(Pos,pType,pVal);
01610 
01611         // Don't render quality attributes, to ensure steps are rendered with the same quality
01612         // as the start and end objects.
01613         if (pType != CC_RUNTIME_CLASS(AttrQuality))
01614         {
01615             // pVal is actually an attribute, so get a ptr to it and render it
01616             NodeAttribute* pNodeAttr = (NodeAttribute *)pVal;
01617         
01618             // *********+**** BODGE (removed 1/12/94) ****************
01619             // Ignore these two attrs until I get Wills attr changes, involving unimplemented Restore() funcs
01620             //  if (!pNodeAttr->IS_KIND_OF(AttrDashPattern) && !pNodeAttr->IS_KIND_OF(AttrTranspFillMapping))
01621             //      pNodeAttr->Render(pRender);
01622 
01623         if (pNodeAttr->IsABrush())
01624         {
01625             //TRACEUSER( "Diccon", _T("Blend step %d, rendering Brush\n"), m_BlendStep);
01626             PathProcessor* pPathProc = pRender->GetFirstPathProcessor();
01627             if (pPathProc != NULL && pPathProc->IsAPathProcessorBrush())
01628                 continue;
01629         }
01630             pNodeAttr->Render(pRender);
01631         }
01632     }
01633 }
01634 
01635 /********************************************************************************************
01636 
01637 >   BOOL NodeBlender::BlendAttributes(RenderRegion *pRender, BlendPath* pBlendPathStart,
01638                                   BlendPath* pBlendPathEnd,CCAttrMap* pBlendedAttrMap,
01639                                   double BlendRatio, double objectRatio,
01640                                   BOOL objectProfileProcessing)
01641 
01642     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01643     Created:    20/10/94
01644     Inputs:     pRender             = The Render Region into which we are rendering the blend, or
01645                                         NULL if not rendering (i.e. doing a make shapes)
01646                 pBlendPathStart     = ptr to blend path to blend from
01647                 pBlendPathEnd       = ptr to blend path to blend to
01648                 pBlendedAttrMap     = ptr to map to store the blended attributes in
01649                 BlendRatio          = amount to blend by (0.0 <= BlendPath <= 1.0)
01650                 objectRatio         = the corresponding object ratio (CGS:  allows us to blend
01651                                       object stuff with attributes)
01652                 objectProfileProcessing = whether we are allowed to blend object (i.e.  CMapPtrToPtr::iterator)
01653                                       data of attributes
01654     Outputs:    -
01655     Returns:    TRUE if successful, FALSE otherwise
01656     Purpose:    Blends the attributes of the two BlendPath objects by the amount specified in BlendRatio
01657     SeeAlso:    -
01658 
01659 ********************************************************************************************/
01660 
01661 BOOL NodeBlender::BlendAttributes(RenderRegion *pRender, BlendPath* pBlendPathStart,
01662                                   BlendPath* pBlendPathEnd,CCAttrMap* pBlendedAttrMap,
01663                                   double BlendRatio, double objectRatio,
01664                                   BOOL objectProfileProcessing)
01665 {
01666     // Check entry params
01667     BOOL ok = (pBlendPathStart != NULL &&
01668                 pBlendPathEnd != NULL && pBlendedAttrMap != NULL);
01669     ERROR3IF(!ok,"One or more NULL entry params");
01670     if (!ok) return FALSE;
01671 
01672     // Find the attributes that are applied to the blend paths
01673     CCAttrMap* pAttrMapStart = m_pRefStart->FindAppliedAttributes(pBlendPathStart);
01674     CCAttrMap* pAttrMapEnd   = m_pRefEnd  ->FindAppliedAttributes(pBlendPathEnd);
01675 
01676     // If either are NULL, return FALSE
01677     if (pAttrMapStart == NULL || pAttrMapEnd == NULL) return FALSE;
01678 
01679 //#ifdef _DEBUG
01680 //  AttrTranspFillGeometry* pLineWidth = NULL;
01681 //  pMap->Lookup((void *)CC_RUNTIME_CLASS(AttrLineWidth), (void *&)pIndent);
01682 //  //UINT32* pCol = pLineWidth->GetStartTransp ();
01683 //
01684 //  pAttrMapEnd->Lookup((void*) CC_RUNTIME_CLASS(AttrTranspFillGeometry),(void* &)pLineWidth);
01685 //  //ol = pLineWidth->GetStartTransp ();
01686 //#endif
01687 
01688     pAttrMapStart->attrMapCreator = pBlendPathStart->GetBlendNode ();
01689     pAttrMapEnd->attrMapCreator = pBlendPathEnd->GetBlendNode ();
01690 
01691     if (GetNodeBlendPath() != NULL)
01692     {
01693         Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pNodeStart,360.0 - m_AngleStart);
01694         Trans2DMatrix* pRotateEnd   = GetRotateMatrix(m_pNodeEnd  ,360.0 - m_AngleEnd  );
01695         if (pRotateStart)   pAttrMapStart->Transform(*pRotateStart);
01696         if (pRotateEnd)     pAttrMapEnd  ->Transform(*pRotateEnd);
01697         DELPTR(pRotateStart);
01698         DELPTR(pRotateEnd);
01699     }
01700 
01701     // Diccon 24/5/2000 Brush attributes need to know about stroke colour, so we need to 
01702     // keep track of both as we find them
01703     AttrBrushType* pBlendedAttrBrush = NULL;
01704     AttrStrokeColour* pBlendedStrokeColour = NULL;;
01705         
01706     // These vars are used as params to the CCAttrMap funcs
01707     CCRuntimeClass* pTypeStart;
01708     void* pValStart;
01709     void* pValEnd;
01710     double OldBlendRatio = BlendRatio;
01711     // Process each attribute in turn
01712     CMapPtrToPtr::iterator PosStart = pAttrMapStart->GetStartPosition();
01713     while ( PosStart != pAttrMapStart->GetEndPosition() )
01714     {
01715         // Get a ptr to the attr at CMapPtrToPtr::iterator PosStart in the start node's attr map
01716         pAttrMapStart->GetNextAssoc(PosStart,pTypeStart,pValStart);
01717         NodeAttribute* pNodeAttrStart = (NodeAttribute *)pValStart;
01718     
01719         BlendRatio = OldBlendRatio; 
01720         // Diccon 10/99 When using non-linear profiles for the objects those attributes
01721         // that make use of control points were not being profiled, making the objects look strange.
01722         // to avoid this those attributes now share the same profiles as the objects.
01723         if (pNodeAttrStart->IsAGradFill())
01724         {
01725         
01726             if (!((AttrFillGeometry*)pNodeAttrStart)->IsAColourFill())
01727             {
01728                 
01729                 BlendRatio = GetObjectRatio();
01730             
01731             }
01732             else
01733             {
01734                 BlendRatio = GetInvertedAttributeRatio();
01735             
01736             }
01737 
01738         }
01739         if (pNodeAttrStart->IsAFlatFill() || (pNodeAttrStart->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrLineWidth)))
01740         {
01741             BlendRatio = GetInvertedAttributeRatio();
01742         }
01743     
01744         // Get a blended attribute
01745         NodeAttribute* pBlendedNodeAttr = NULL;
01746 
01747         // Find an attr of the same type in the end object's attr list,
01748         // and blend the two attrs together
01749         if (pAttrMapEnd->Lookup(pTypeStart,pValEnd))
01750         {
01751             // We've found a matching end attr, so try to blend it with the start attr
01752 
01753             // Set up the param object to pass to the start attr's blend method
01754             BlendAttrParam BlendParam;
01755 
01756             // Initialise the BlendParam with the end attr and blend ratio
01757             if (BlendParam.Init(pRender,
01758                                 (NodeAttribute *)pValEnd,BlendRatio,objectRatio,objectProfileProcessing,GetColourBlendType(),
01759                                 pAttrMapStart, pAttrMapEnd) )
01760             {
01761                 // Successfully initialised, so now try blending the attributes
01762                 if (pNodeAttrStart->Blend(&BlendParam))
01763                 {
01764                     // Attrs successfully blended, now get a ptr to the new attr.
01765                     // Once we get the blended attr ptr, it belongs to us, so we have
01766                     // to delete it when it is not needed
01767                     pBlendedNodeAttr = BlendParam.GetBlendedAttr();
01768                 }
01769                 else
01770                 {
01771 #ifdef _DEBUG
01772                     void *tmp = NULL;
01773                     ENSURE(!pBlendedAttrMap->Lookup(pNodeAttrStart->GetRuntimeClass(),tmp),"Didn't blend attr, but there's still one in pBlendedAttrMap");
01774 #endif
01775                 }
01776             }
01777         }
01778 
01779         // If we have a blended attr, pBlendedNodeAttr != NULL
01780         if (pBlendedNodeAttr != NULL)
01781         {
01782             // Get the type of the blended attr
01783             CCRuntimeClass* pTypeBlend = pBlendedNodeAttr->GetAttributeType();
01784             void* pValBlend;
01785 
01786             // If we already have an attr in the blended attr map of the same type,
01787             // remove it and delete it, before inserting a new attr of this type
01788             if (pBlendedAttrMap->Lookup(pTypeBlend,pValBlend))
01789             {
01790                 if (pValBlend != NULL)
01791                 {
01792                     pBlendedAttrMap->RemoveKey(pTypeBlend);
01793                     delete (NodeAttribute*)pValBlend;
01794                 }
01795             }
01796             // add it to the blend map
01797             pBlendedAttrMap->SetAt(pTypeBlend,pBlendedNodeAttr);
01798 
01799             // find out if we are a stroke colour or a brush
01800             if (pBlendedNodeAttr->IsAStrokeColour())
01801                 pBlendedStrokeColour = (AttrStrokeColour*)pBlendedNodeAttr;
01802             if (pBlendedNodeAttr->IsABrush())
01803                 pBlendedAttrBrush = (AttrBrushType*)pBlendedNodeAttr;
01804         }
01805     }
01806 
01807     if (GetNodeBlendPath() != NULL)
01808     {
01809         Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pNodeStart,m_AngleStart);
01810         Trans2DMatrix* pRotateEnd   = GetRotateMatrix(m_pNodeEnd  ,m_AngleEnd  );
01811         if (pRotateStart)   pAttrMapStart->Transform(*pRotateStart);
01812         if (pRotateEnd)     pAttrMapEnd  ->Transform(*pRotateEnd);
01813         DELPTR(pRotateStart);
01814         DELPTR(pRotateEnd);
01815     }
01816 
01817     // if we got a stroke colour and a brush then tell the brush what the stroke colour is
01818     if (pBlendedAttrBrush != NULL && pBlendedStrokeColour != NULL)
01819         pBlendedAttrBrush->SetBlendedStrokeColour(&(pBlendedStrokeColour->Value.Colour));
01820 
01821     return TRUE;
01822 }
01823 
01824 /********************************************************************************************
01825 
01826 >   BOOL NodeBlender::BlendPaths(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio)
01827 
01828     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01829     Created:    14/10/94
01830     Inputs:     pBlendPathStart = ptr to blend path to blend from
01831                 pBlendPathEnd   = ptr to blend path to blend to
01832                 BlendRatio      = amount to blend by (0.0 <= BlendPath <= 1.0)
01833     Outputs:    The blended path is stored in three arrays: the coords, the verbs, and the flags.
01834                 The arrays are:
01835     
01836                     pTempCoords
01837                     pTempVerbs
01838                     pTempFlags
01839 
01840                     ArrayLength = the length of all three arrays
01841 
01842                 This allows the caller to decide what to do with the blended path in a very flexible way.
01843 
01844     Returns:    TRUE if successful, FALSE otherwise
01845     Purpose:    Blends two BlendPath objects by the amount specified in BlendRatio
01846     SeeAlso:    -
01847 
01848 ********************************************************************************************/
01849 
01850 BOOL NodeBlender::BlendPaths(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio)
01851 {
01852     // Check entry params
01853     BOOL    ok = (pBlendPathStart != NULL && pBlendPathEnd != NULL);
01854     if (ok) ok = (pBlendPathStart->GetBlendNode() != NULL && pBlendPathEnd->GetBlendNode() != NULL);
01855     ERROR3IF(!ok,"One or more NULL entry params");
01856     if (!ok) return FALSE;
01857 
01858     // Get the types of the two paths
01859     PathTypeEnum PathTypeStart = pBlendPathStart->GetPathType();
01860     PathTypeEnum PathTypeEnd   = pBlendPathEnd  ->GetPathType();
01861 
01862     // The blended path will be closed if either of the paths is a shape
01863     BOOL Closed = (PathTypeStart == PATHTYPE_SHAPE) || (PathTypeEnd == PATHTYPE_SHAPE);
01864 
01865     Path * pPathStart = NULL;
01866 
01867     // Find the paths associated with the start and end blend paths
01868     if (pBlendPathStart->GetBlendNode()->IsNodePath())
01869     {
01870         pPathStart = &(((NodePath *)pBlendPathStart->GetBlendNode())->InkPath);
01871     }
01872     else
01873     {
01874         pPathStart = &(((NodePath *)((NodeCompound *)pBlendPathStart->GetBlendNode())->GetNodeToBlend())->InkPath);
01875     }
01876 
01877     Path * pPathEnd = NULL;
01878 
01879     if (pBlendPathEnd->GetBlendNode()->IsNodePath())
01880     {
01881         pPathEnd   = &(((NodePath *)pBlendPathEnd->GetBlendNode())->InkPath);
01882     }
01883     else
01884     {
01885         pPathEnd = &(((NodePath *)((NodeCompound *)pBlendPathEnd->GetBlendNode())->GetNodeToBlend())->InkPath);
01886     }
01887 
01888     // Calculate how large the arrays have to be to store the blended path definition
01889     INT32 DestPathSize = ((pPathStart->GetNumCoords()+pPathEnd->GetNumCoords())*3)+500;
01890 
01891     // Get some arrays used to hold the blended path data, and error if any are NULL
01892     DocCoord*   pDestCoords = GetCoordArray(DestPathSize);
01893     PathVerb*   pDestVerbs  = GetVerbArray(DestPathSize);
01894     PathFlags*  pDestFlags  = GetFlagArray(DestPathSize);
01895     UINT32*         pBuff       = GetGBlendBuff(DestPathSize);
01896     if (pDestCoords == NULL || pDestVerbs == NULL || pDestFlags == NULL || pBuff == NULL)
01897         return FALSE;
01898 
01899     // This section copes with the case when blending a line with a shape.
01900     // In this case we need to get a temp path the is actually a shape version of the line.
01901     // The line is simply reversed back onto itself to form a shape that would look identical to the 
01902     // line if rendered.  This allows the line to appear to open up to the shape when blended.
01903     Path Shape;
01904     if (PathTypeStart != PathTypeEnd)
01905     {
01906         BOOL ok = FALSE;
01907         if (!Shape.Initialise()) return FALSE;
01908 
01909         // if going from a line to a shape, convert the start path to a shape
01910         if (PathTypeStart == PATHTYPE_LINE && PathTypeEnd == PATHTYPE_SHAPE)
01911         {
01912             ok = NodeBlender::ConvertLineToShape(pPathStart,&Shape);
01913             pPathStart = &Shape;
01914         }
01915 
01916         // if going from a shape to a line, convert the end path to a shape
01917         if (PathTypeStart == PATHTYPE_SHAPE && PathTypeEnd == PATHTYPE_LINE)
01918         {
01919             ok = NodeBlender::ConvertLineToShape(pPathEnd,&Shape);
01920             pPathEnd = &Shape;
01921         }
01922 
01923         if (!ok) return FALSE;
01924     }
01925 
01926     // The blend should do a one-to-one mapping when the OneToOne flag is set AND both paths
01927     // have the same number of elements
01928     BOOL OneToOne = FALSE;
01929     if (IsOneToOne())
01930         OneToOne = (pBlendPathStart->GetNumElements() == pBlendPathEnd->GetNumElements());
01931 
01932     // Now actually blend the two paths
01933 
01934     GBlend GBlendObj;
01935 
01936     // Define the blend
01937     GBlendObj.Define(   (PPOINT)pPathStart->GetCoordArray(),    // Specify the start path
01938                         pPathStart->GetVerbArray(),
01939                         pPathStart->GetNumCoords(),
01940 
01941                         (PPOINT)pPathEnd  ->GetCoordArray(),    // Specify the end path
01942                         pPathEnd  ->GetVerbArray(),
01943                         pPathEnd  ->GetNumCoords(),
01944 
01945                         OneToOne,                               // The one-to-one flag
01946                         FLATNESS,                               // Flatness
01947 
01948                         pBuff,                                  // Buffer for GBlend to use
01949                         DestPathSize*sizeof(UINT32));           // The buffer size
01950 
01951     // Blend the paths
01952     m_ArrayLength = GBlendObj.Blend(BlendRatio,                 // The blend ratio, 0.0 < BlendRatio < 1.0
01953                                     (PPOINT)pDestCoords,        // Array to store blended coords
01954                                     pDestVerbs,                 // Array to store blended verbs
01955                                     DestPathSize);              // The num elements in the two arrays
01956 
01957 
01958     // If we're blending a line to another line, we need to make sure that the blended line
01959     // is going in a direction that corresponds to the source lines.  This ensures attributes
01960     // that depend on this direction (e.g. start and end arrows) look correct.
01961     //
01962     // When creating blend paths of lines, we can detect if the blend path has been reversed,
01963     // in relation to the original path, by the original mapping value.
01964     // If it's 0 it has NOT been reversed, otherwise it's been reversed.
01965     //
01966     // If the blend ratio is <=0.5, the blended path is closest to the start blend path,
01967     // so we look at the start blend path's original mapping.
01968     //
01969     // If blend ration > 0.5, look at the end blend path's original mapping.
01970     //
01971     // The (BlendRation <= 0.5) cut-off point is the same as the cut-off point used in the blending
01972     // of attributes.
01973     if (pBlendPathStart->IsLine() && pBlendPathEnd->IsLine())
01974     {
01975         BlendPath* pBlendPath;
01976         if (BlendRatio <= 0.5) 
01977             pBlendPath = pBlendPathStart;
01978         else
01979             pBlendPath = pBlendPathEnd;
01980 
01981         if (pBlendPath->GetOrigMapping() != 0)
01982             ReversePath(pDestCoords,pDestVerbs,m_ArrayLength);
01983     }
01984 
01985     // We need to do some work on the blended path
01986     if (!ProcessBlendedPath(pDestCoords,pDestVerbs,pDestFlags,m_ArrayLength,Closed))
01987         return FALSE;
01988 
01989     return TRUE;
01990 }
01991 
01992 
01993 /********************************************************************************************
01994 
01995 >   void NodeBlender::ReversePath(DocCoord* pCoords,PathVerb* pVerbs,UINT32 Len)
01996 
01997     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01998     Created:    24/1/96
01999     Inputs:     pCoords = ptr to coords array
02000                 pVerbs  = ptr to verbs array
02001                 Len     = the length of above arrays
02002     Outputs:    -
02003     Returns:    -
02004     Purpose:    This will reverse the path definition given in the two arrays.
02005 
02006                 Path flags are not delt with as these are specific to the Path class.
02007                 This function is designed to process raw paths returned from Gavin.
02008     SeeAlso:    -
02009 
02010 ********************************************************************************************/
02011 
02012 void NodeBlender::ReversePath(DocCoord* pCoords,PathVerb* pVerbs,UINT32 Len)
02013 {
02014     // First we will reverse all the Coords
02015     UINT32 LastPos  = Len-1;
02016     UINT32 FirstPos = 0;
02017     while (FirstPos<LastPos)
02018     {
02019         SWAP(DocCoord,pCoords[FirstPos],pCoords[LastPos]);  // change the order of the coords in the array
02020 
02021         // find the next two coords to swap
02022         FirstPos++;
02023         LastPos--;
02024     }
02025 
02026     // Now reverse the verbs.
02027     // The first verb always stays as a MoveTo and everything else gets sorted around it
02028     FirstPos = 1;
02029     LastPos  = Len-1;
02030     while (FirstPos<LastPos)
02031     {
02032         SWAP(PathVerb,pVerbs[FirstPos],pVerbs[LastPos]);
02033 
02034         // Find the next two verbs to swap
02035         FirstPos++;
02036         LastPos--;
02037     }
02038 }
02039 
02040 /********************************************************************************************
02041 
02042 >   void NodeBlender::UpdateBlendStartAngles (Trans2DMatrix& trans)
02043 
02044     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02045     Created:    25/7/2000
02046     Inputs:     trans   =   the transformation matrix
02047     Outputs:    -
02048     Returns:    -
02049     Purpose:    Updates a blend on curves start angles after we have rotated the blend.
02050     SeeAlso:    -
02051 
02052     NOTE:       If problems arise the function should be modified to store undo information.
02053                 However at present, this seems to be generated for us somewhere else; which
02054                 is fine by me.
02055 
02056 ********************************************************************************************/
02057 
02058 void NodeBlender::UpdateBlendStartAngles (Trans2DMatrix& trans)
02059 {
02060     // only update if these are non-zero
02061     if ((m_AngleStart != 0.0 || m_AngleEnd != 0.0))
02062     {
02063         double ang = (trans.GetRotation ()).MakeDouble ();
02064         
02065         m_AngleStart    += ang;
02066         m_AngleEnd      += ang;
02067     }
02068 }
02069 
02070 
02071 
02072 /********************************************************************************************
02073 
02074 >   virtual INT32 NodeBlender::EstimateNodeComplexity (OpParam* details)
02075 
02076     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02077     Created:    6/09/2000
02078 
02079     Inputs:     details     any data that should be used for the calculation
02080 
02081     Outputs:    -
02082 
02083     Returns:    an estimate of the nodes complexity
02084 
02085     Purpose:    This function estimates a complexity value for the node.  The complexity
02086                 value is based upon the total length of all paths in the node.
02087 
02088     See Also:   OpBlendNodes::DeterminBlendObjectsProcessorHit ()
02089 
02090 ********************************************************************************************/
02091 
02092 INT32 NodeBlender::EstimateNodeComplexity (OpParam* details)
02093 {
02094     //m_pNodeStart,&m_pRefStart
02095 
02096     if ((m_pNodeStart == NULL) || (m_pNodeEnd == NULL))
02097     {
02098         return (-1);
02099     }
02100     
02101     INT32 complexityEstimate1 = m_pNodeStart->EstimateNodeComplexity (details);
02102     INT32 complexityEstimate2 = m_pNodeEnd->EstimateNodeComplexity (details);
02103 
02104     UINT32 NumBlendStepsby2 = 0;
02105     if (!details)
02106     {
02107         NumBlendStepsby2 = GetNumBlendSteps()/2;
02108     }
02109     else
02110     {
02111         NumBlendStepsby2 = (UINT32) (INT32) details->Param1;
02112     }
02113 
02114     INT32 generatedPathsEstimate = NumBlendStepsby2+1*complexityEstimate1 + NumBlendStepsby2*complexityEstimate2;
02115 
02116     return (generatedPathsEstimate);
02117 }
02118 
02119 /********************************************************************************************
02120 
02121 >   BOOL NodeBlender::ConvertLineToShape(Path* pPath,Path* pShapePath)
02122 
02123     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02124     Created:    17/11/94
02125     Inputs:     pPath       = ptr to the line to convert
02126                 pShapePath  = ptr to path to store the shape version of the line
02127     Outputs:    -
02128     Returns:    TRUE if OK, FALSE if it goes bang
02129     Purpose:    This takes a line and creates a shape out of it, placing the shape in pShapePath.
02130 
02131                 It does this:
02132                     A copy of the line is made, and reversed.
02133                     The reversed copy has its start movetto element removed.
02134                     A shape is formed by merging the line with the reversed copy.
02135 
02136                 This allows us to blend a line with a shape, giving the impression that the line 
02137                 opens out into the shape.
02138     SeeAlso:    -
02139 
02140 ********************************************************************************************/
02141 
02142 BOOL NodeBlender::ConvertLineToShape(Path* pPath,Path* pShapePath)
02143 {
02144     ERROR3IF(pPath == NULL || pShapePath == NULL,"One or more NULL entry params");
02145     if (pPath == NULL || pShapePath == NULL) return FALSE;
02146 
02147     Path ReversePath;
02148     if (!ReversePath.Initialise(pPath->GetNumCoords())) return FALSE;
02149     if (!ReversePath.CopyPathDataFrom(pPath)) return FALSE;
02150 
02151     PathVerb* pRevVerb = ReversePath.GetVerbArray();
02152     PathVerb* pShapeVerb = pShapePath->GetVerbArray();
02153 
02154     ReversePath.Reverse();
02155     ReversePath.FindStartOfPath();
02156     ReversePath.DeleteElement();
02157 
02158     pRevVerb = ReversePath.GetVerbArray();
02159     pShapeVerb = pShapePath->GetVerbArray();
02160 
02161     pShapePath->ClearPath(FALSE);
02162     if (!pShapePath->MergeTwoPaths(*pPath)) return FALSE;
02163     if (!pShapePath->MergeTwoPaths(ReversePath)) return FALSE;
02164 
02165     return TRUE;
02166 }
02167 
02168 /********************************************************************************************
02169 
02170 >   BOOL NodeBlender::ProcessBlendedPath(DocCoord* pCoords,PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len,BOOL Closed)
02171 
02172     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02173     Created:    8/11/94
02174     Inputs:     pCoords = ptr to coords of the blended path
02175                 pVerbs  = ptr to verbs of blended path
02176                 pFlags  = ptr to uninitialised flags array
02177                 Len     = num verbs in pVerbs
02178                 Closed  = The resultant path should be closed
02179     Outputs:    pCoords array contains a valid set of coords
02180                 pVerbs array contains a valid set of verbs
02181                 pFlags array contains valid flags for each of the verbs in pVerbs
02182     Returns:    TRUE if OK, FALSE if it goes bang
02183     Purpose:    This does any processing necessary on the path generated by GBlend.
02184                 The three arrays can be used to generate renderable paths, and also legal paths
02185                 that can be used to create NodePath objects on the tree.
02186     SeeAlso:    -
02187 
02188 ********************************************************************************************/
02189 
02190 BOOL NodeBlender::ProcessBlendedPath(DocCoord* pCoords,PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len,BOOL Closed)
02191 {
02192     if (Len < 1)
02193         return FALSE;
02194 
02195     // Generate a legal path flags array
02196     SetPathFlags(pVerbs,pFlags,Len);
02197 
02198     // If closed, make sure there's a PT_CLOSEFIGURE on the last end point
02199     if (Closed)
02200         pVerbs[Len-1] = pVerbs[Len-1] | PT_CLOSEFIGURE;     
02201 
02202     return TRUE;
02203 }
02204 
02205 /********************************************************************************************
02206 
02207 >   void NodeBlender::SetPathFlags(PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len)
02208 
02209     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02210     Created:    8/11/94
02211     Inputs:     pVerbs = ptr to path verbs
02212                 pFlags = ptr to flags
02213                 Len    = num verbs in pVerbs
02214     Outputs:    pFlags array contains valid flags for each of the verbs in pVerbs
02215     Returns:    -
02216     Purpose:    This generates a legal path flags array based on the given verbs.
02217                 All it does is set the end point flags correctly, leaving all other flags FALSE
02218     SeeAlso:    -
02219 
02220 ********************************************************************************************/
02221 
02222 void NodeBlender::SetPathFlags(PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len)
02223 {
02224     UINT32 n;
02225 
02226     // Reset all the path flags to default values
02227     PathFlags DefaultPathFlags;
02228     for (n=0; n < Len; n++)
02229         pFlags[n] = DefaultPathFlags;
02230 
02231     // Scan the verbs, so that the end point flags can be set correctly
02232     for (n=0; n < Len; n++)
02233     {
02234         PathVerb Verb = pVerbs[n] & ~PT_CLOSEFIGURE;
02235         switch (Verb)
02236         {
02237             case PT_LINETO:
02238             case PT_MOVETO:
02239                 pFlags[n].IsEndPoint = TRUE;
02240                 break;
02241 
02242             case PT_BEZIERTO:
02243                 n += 2;
02244                 ERROR3IF(n>=Len,"Found a PT_BEZIERTO, but third pt is off the end of the array");
02245                 pFlags[n].IsEndPoint = TRUE;
02246                 break;
02247 
02248             default:
02249                 ERROR3_PF(("Illegal path verb found : %c",Verb));
02250                 break;
02251         }
02252     }
02253 }
02254 
02255 /********************************************************************************************
02256 
02257 >   Matrix NodeBlender::MakeMatrix(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio)
02258 
02259     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> (clever bit by Mike)
02260     Created:    20/10/94
02261     Inputs:     pBlendPathStart = ptr to blend path to blend from
02262                 pBlendPathEnd   = ptr to blend path to blend to
02263                 BlendRation     = amount to blend by (0.0 <= BlendPath <= 1.0)
02264     Outputs:    -
02265     Returns:    A matrix
02266     Purpose:    Makes the matrix for the transfrom that should be applied to blend paths and attributes
02267     SeeAlso:    -
02268 
02269 ********************************************************************************************/
02270 
02271 Matrix NodeBlender::MakeMatrix(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio)
02272 {
02273     // Check entry params
02274     BOOL    ok = (pBlendPathStart != NULL && pBlendPathEnd != NULL);
02275     if (ok) ok = (pBlendPathStart->GetCreatedByNode() != NULL && pBlendPathEnd->GetCreatedByNode() != NULL);
02276     ERROR3IF(!ok,"One or more NULL entry params");
02277     if (!ok)
02278     {
02279         Matrix M;
02280         return M;
02281     }
02282 
02283     // Get the bounds of the objects that generated the paths
02284     DocRect BoundsStart = pBlendPathStart->GetCreatedByNode()->GetBoundingRect(TRUE);
02285     DocRect BoundsEnd   = pBlendPathEnd  ->GetCreatedByNode()->GetBoundingRect(TRUE);
02286     
02287     // The following creates a matrix that transforms the start rect to the end rect
02288     // dependent on the blend ratio, courtesy of Mike (thanks!)
02289 
02290     double Sx0 = BoundsStart.lo.x;
02291     double Sy0 = BoundsStart.lo.y;
02292 
02293     double Sx1 = BoundsStart.hi.x;
02294     double Sy1 = BoundsStart.hi.y;
02295 
02296     double Dx0 = BoundsEnd.lo.x;   
02297     double Dy0 = BoundsEnd.lo.y;
02298 
02299     double Dx1 = BoundsEnd.hi.x;   
02300     double Dy1 = BoundsEnd.hi.y;
02301 
02302     double Ex0 = Sx0 + BlendRatio*(Dx0-Sx0);
02303     double Ey0 = Sy0 + BlendRatio*(Dy0-Sy0);
02304     double Ex1 = Sx1 + BlendRatio*(Dx1-Sx1);
02305     double Ey1 = Sy1 + BlendRatio*(Dy1-Sy1);
02306 
02307 
02308     double t0 = (Ex1-Ex0)/(Sx1-Sx0);
02309     double t1 = 0;
02310     double t2 = 0;
02311     double t3 = (Ey1-Ey0)/(Sy1-Sy0);
02312 
02313     FIXED16 a(t0);
02314     FIXED16 b(t1);
02315     FIXED16 c(t2);
02316     FIXED16 d(t3);
02317 
02318     INT32 e = (INT32) (Ex0-Sx0*t0);
02319     INT32 f = (INT32) (Ey0-Sy0*t3);
02320 
02321     Matrix M(a,b,c,d,e,f);
02322 
02323     return (M);
02324 }
02325 
02326 
02327 /********************************************************************************************
02328 
02329 >   double NodeBlender::MapObjectBlendRatio(double BlendRatio)
02330 
02331     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02332     Created:    1/6/99
02333     Inputs:     BlendRatio - the linear blend ration, between 0.0 and 1.0
02334     Returns:    The mapped blend ratio to use for object blending
02335     Purpose:    Function that works out what blend ratio to use when creating a blended bath.
02336                 Allows non-linear processing of blends
02337     SeeAlso:    NodePath::Render; NodeRenderableInk::RenderEorDrag
02338 
02339 ********************************************************************************************/
02340 
02341 double NodeBlender::MapObjectBlendRatio(double BlendRatio)
02342 {
02343     NodeBlend* pNodeBlend = GetNodeBlend();
02344     if (pNodeBlend)
02345     {
02346         IProfile* pProfile = pNodeBlend->GetObjectProfile();
02347         if (pProfile)
02348             return pProfile->MapZeroToOne(BlendRatio);
02349     }
02350 
02351     return BlendRatio;
02352 }
02353 
02354 /********************************************************************************************
02355 
02356 >   double NodeBlender::MapAttrBlendRatio(double BlendRatio)
02357 
02358     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02359     Created:    1/6/99
02360     Inputs:     BlendRatio - the linear blend ration, between 0.0 and 1.0
02361     Returns:    The mapped blend ratio to use for attribute blending
02362     Purpose:    Function that works out what blend ratio to use when blending attributes
02363                 Allows non-linear processing of blend attributes
02364     SeeAlso:    NodePath::Render; NodeRenderableInk::RenderEorDrag
02365 
02366 ********************************************************************************************/
02367 
02368 double NodeBlender::MapAttrBlendRatio(double BlendRatio)
02369 {
02370     NodeBlend* pNodeBlend = GetNodeBlend();
02371     if (pNodeBlend)
02372     {
02373         IProfile* pProfile = pNodeBlend->GetAttrProfile();
02374         if (pProfile)
02375             return pProfile->MapZeroToOne(BlendRatio);
02376     }
02377 
02378     return BlendRatio;
02379 }
02380 
02381 /********************************************************************************************
02382 
02383 >   double NodeBlender::MapCMapPtrToPtr::iteratorBlendRatio(double BlendRatio)
02384 
02385     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02386     Created:    1/6/99
02387     Inputs:     BlendRatio - the linear blend ration, between 0.0 and 1.0
02388     Returns:    The mapped blend ratio to use for CMapPtrToPtr::iteratoring the blended shapes
02389     Purpose:    Function that works out what blend ratio to use when CMapPtrToPtr::iteratoring the blended shapes
02390                 Allows non-linear processing of blend shape CMapPtrToPtr::iteratoring
02391     SeeAlso:    NodePath::Render; NodeRenderableInk::RenderEorDrag
02392 
02393 ********************************************************************************************/
02394 
02395 double NodeBlender::MapPositionBlendRatio(double BlendRatio)
02396 {
02397     NodeBlend* pNodeBlend = GetNodeBlend();
02398     if (pNodeBlend)
02399     {
02400         IProfile* pProfile = pNodeBlend->GetPositionProfile();
02401         if (pProfile)
02402             return pProfile->MapZeroToOne(BlendRatio);
02403     }
02404 
02405     return BlendRatio;
02406 }
02407 
02408 
02409 /********************************************************************************************
02410 
02411 >   double NodeBlender::GetObjectRatio()
02412 
02413     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02414     Created:    1/10/99
02415     Inputs:     -
02416     Returns:    The mapped blend ratio to use for blend objects
02417     Purpose:    returns the current mapping of objects
02418 
02419 
02420 ********************************************************************************************/
02421 
02422 double NodeBlender::GetObjectRatio()
02423 {
02424     UINT32 NumSteps = GetNumBlendSteps() + 1;
02425     double StepRatio = 1.0/NumSteps;
02426     double LinearRatio = StepRatio * m_BlendStep;
02427 
02428     CProfileBiasGain* ObjectProfile = GetNodeBlend()->GetObjectProfile();
02429 
02430     return ObjectProfile->MapZeroToOne(LinearRatio);
02431 
02432 }
02433 
02434 
02435 
02436 /********************************************************************************************
02437 
02438 >   double NodeBlender::GetInvertedAttributeRatio()
02439 
02440     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02441     Created:    1/10/99
02442     Inputs:     -
02443     Returns:    The mapped blend ratio to use for blend attributes
02444     Purpose:    Function that inverts the current attribute profile and returns the mapped value
02445                 This is needed to fix the problem with profiles and transparencies and other fills
02446                 that use control points.
02447     SeeAlso:    NodePath::Render; NodeRenderableInk::RenderEorDrag
02448 
02449 ********************************************************************************************/
02450 
02451 double NodeBlender::GetInvertedAttributeRatio()
02452 {
02453     UINT32 NumSteps = GetNumBlendSteps() + 1;
02454     double StepRatio = 1.0/NumSteps;
02455     double LinearRatio = StepRatio * m_BlendStep;
02456 
02457     CProfileBiasGain* AttributeProfile = GetNodeBlend()->GetAttrProfile();
02458 
02459     CProfileBiasGain InvertedProfile;
02460     InvertedProfile.SetBias(-1*(AttributeProfile->GetBias()));
02461     InvertedProfile.SetGain(-1*(AttributeProfile->GetGain()));
02462     
02463     /*double Test =*/  AttributeProfile->MapZeroToOne(LinearRatio);
02464 
02465     return InvertedProfile.MapZeroToOne(LinearRatio);
02466 
02467 }
02468 
02469 
02470 /********************************************************************************************
02471 
02472 >   void NodeBlender::RenderEorDrag( RenderRegion* pRender )
02473 
02474     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02475     Created:    11/10/94
02476     Inputs:     pRender - A Pointer to the current RenderRegion
02477     Purpose:    Renders a version of the blender node for EORed dragging of blends.
02478     SeeAlso:    NodePath::Render; NodeRenderableInk::RenderEorDrag
02479 
02480 ********************************************************************************************/
02481 
02482 void NodeBlender::RenderEorDrag( RenderRegion* pRender )
02483 {
02484     if (!Reinit()) return;
02485 }
02486 
02487 /********************************************************************************************
02488 
02489 > virtual void NodeBlender::RenderBlendBlobs(RenderRegion* pRender,BOOL RenderStart,BOOL RenderEnd)
02490 
02491     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02492     Created:    9/11/94
02493     Inputs:     pRender = region to render blobs into
02494                 RenderStart = TRUE if blobs of start of blend should be rendered
02495                 RenderEnd   = TRUE if blobs of end   of blend should be rendered
02496     Outputs:    -
02497     Returns:    -
02498     Purpose:    Renders the blobs of the blend paths associated with the given blender
02499                 NOTE:   This only renders blobs if there's only one blend path in both the start
02500                         and the end of the blend
02501     SeeAlso:    -
02502 
02503 ********************************************************************************************/
02504 
02505 void NodeBlender::RenderBlendBlobs(RenderRegion* pRender,BOOL RenderStart,BOOL RenderEnd)
02506 {
02507 #if !defined(EXCLUDE_FROM_RALPH)
02508     if (!Reinit()) return;
02509     if (m_pRefStart == NULL || m_pRefEnd == NULL) return;
02510 
02511     if (m_pRefStart->GetNumBlendPaths() == 1 && m_pRefEnd->GetNumBlendPaths() == 1)
02512     {
02513         Trans2DMatrix*  pRotateStart= NULL;
02514         Trans2DMatrix*  pRotateEnd  = NULL;
02515 
02516         if (GetNodeBlendPath() != NULL)
02517         {
02518             pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),m_AngleStart);
02519             pRotateEnd   = GetRotateMatrix(m_pRefEnd  ->GetNode(),m_AngleEnd  );
02520         }
02521 
02522         if (RenderStart)    m_pRefStart->RenderBlendBlobs(pRender,pRotateStart);
02523         if (RenderEnd)      m_pRefEnd  ->RenderBlendBlobs(pRender,pRotateEnd);
02524 
02525         BlendPath* pBlendPathStart = m_pRefStart->GetFirstBlendPath();
02526         BlendPath* pBlendPathEnd   = m_pRefEnd  ->GetFirstBlendPath();
02527 
02528         if (pBlendPathStart != NULL && pBlendPathEnd != NULL)
02529         {
02530             DocCoord Start = pBlendPathStart->GetPathCoord(0);
02531             DocCoord End   = pBlendPathEnd  ->GetPathCoord(0);
02532 
02533             if (pRotateStart)   pRotateStart->Transform(&Start,1);
02534             if (pRotateEnd)     pRotateEnd  ->Transform(&End  ,1);
02535 
02536             // Get the scaled pixel size for the view
02537             FIXED16 ScaledPixelWidth,
02538                     ScaledPixelHeight; // Not used
02539             pRender->GetRenderView()->GetScaledPixelSize(&ScaledPixelWidth, &ScaledPixelHeight);
02540 
02541             pRender->SetLineWidth((ScaledPixelWidth.MakeLong())*2);
02542             pRender->SetLineColour(COLOUR_BLACK);
02543             pRender->DrawLine(Start,End);
02544         }
02545 
02546         DELPTR(pRotateStart);
02547         DELPTR(pRotateEnd);
02548     }
02549 
02550 #endif
02551 }
02552 
02553 
02554 
02555 
02556 /********************************************************************************************
02557 
02558 >   DocRect NodeBlender::GetBoundingRect(BOOL DontUseAttrs=FALSE, BOOL HitTest=FALSE)
02559 
02560     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02561     Created:    11/10/94
02562     Inputs:     DontUseAttrs - TRUE if we should ignore the nodes attributes.
02563                 Defaults to FALSE
02564                 HitTest      - TRUE if being called during HitTest
02565     Returns:    The nodes bounding rect
02566     Purpose:    if the bounding rect is valid it is returned, if not, it is recalculated
02567                 and then returned.
02568     SeeAlso:    NodeBlender::GetBlobBoundingRect
02569 
02570 ********************************************************************************************/
02571 
02572 DocRect NodeBlender::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
02573 {
02574     if (Parent != NULL && IS_A(Parent,NodeBlend) && !((NodeBlend*)Parent)->AreBoundsValid())
02575         IsBoundingRectValid = FALSE;
02576 
02577     if (!IsBoundingRectValid || DontUseAttrs)
02578     {
02579         // We need ptrs to the start and end objects this blender is blending
02580         NodeRenderableInk* pStart = NULL;
02581         NodeRenderableInk* pEnd   = NULL;
02582 
02583         // If we are initialised, get the start and end ink nodes
02584         if (m_pRefStart != NULL)    pStart = m_pRefStart->GetNode();
02585         if (m_pRefEnd   != NULL)    pEnd   = m_pRefEnd  ->GetNode();
02586         
02587         if (pStart == NULL) pStart = m_pNodeStart;
02588         if (pEnd == NULL)   pEnd   = m_pNodeEnd;
02589 
02590         // If we don't have ptrs to the start and end objects of this blend, have a look for them
02591         if (pStart == NULL) pStart = (NodeRenderableInk*)FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk));
02592         if (pEnd   == NULL) pEnd   = (NodeRenderableInk*)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
02593 
02594         // Check the ink nodes to ensure that they are what we expect.
02595         ERROR3IF(pStart == NULL,"pStart == NULL");
02596         ERROR3IF(pEnd   == NULL,"pEnd == NULL");
02597         ERROR3IF(!pStart->IS_KIND_OF(NodeRenderableInk),"pStart not a NodeRenderableInk");
02598         ERROR3IF(!pEnd  ->IS_KIND_OF(NodeRenderableInk),"pEnd not a NodeRenderableInk");
02599         ERROR3IF(pStart->IS_KIND_OF(NodeBlender),"pStart is a NodeBlender");
02600         ERROR3IF(pEnd  ->IS_KIND_OF(NodeBlender),"pEnd not a NodeBlender");
02601 
02602         // Continue only if we have nodes that we expect
02603         BOOL ok =   (pStart != NULL) &&
02604                     (pEnd   != NULL) &&
02605                     (pStart->IS_KIND_OF(NodeRenderableInk)) &&
02606                     (pEnd  ->IS_KIND_OF(NodeRenderableInk)) &&
02607                     (!pStart->IS_KIND_OF(NodeBlender)) && 
02608                     (!pEnd  ->IS_KIND_OF(NodeBlender));
02609 
02610         if (ok)
02611         {
02612             // The bounds for the entire blender is the union of the bounds of the start and end objects blended.
02613             // Assume here that blended paths can't lie outside this bounding rect.
02614 
02615             DocRect BoundsStart = pStart->GetBoundingRect(DontUseAttrs,HitTest);
02616             DocRect BoundsEnd   = pEnd  ->GetBoundingRect(DontUseAttrs,HitTest);
02617 
02618             DocRect NewBounds;
02619             NewBounds = BoundsStart;
02620             NewBounds = NewBounds.Union(BoundsEnd);
02621 
02622             if (GetNodeBlendPath() != NULL)
02623                 NewBounds = NewBounds.Union(GetUnionBlendedBounds(BoundsStart,BoundsEnd));
02624 
02625             // If the caller didn't want bounds included, then return the bounds immediately,
02626             // and do not cache the bounds rectangle.
02627             if (DontUseAttrs)
02628                 return(NewBounds);
02629 
02630             // Cache the new bounding rectangle
02631             IsBoundingRectValid = TRUE;
02632             BoundingRectangle = NewBounds;
02633         }
02634     }
02635 
02636 
02637 /*  if (!IsBoundingRectValid && Reinit())
02638     {
02639         if (pRefStart != NULL &&  pRefStart->GetNode() != NULL &&
02640             pRefEnd   != NULL &&  pRefEnd  ->GetNode() != NULL)
02641         {
02642             BoundingRectangle = pRefStart->GetNode()->GetBoundingRect(DontUseAttrs);
02643             BoundingRectangle = BoundingRectangle.Union(pRefEnd->GetNode()->GetBoundingRect(DontUseAttrs));
02644 
02645             IsBoundingRectValid = TRUE;
02646         }
02647     }
02648 */
02649 
02650     // return the current state of the bounding rect
02651     return BoundingRectangle;
02652 }
02653 
02654 
02655 /********************************************************************************************
02656 
02657 >   DocRect NodeBlender::GetBlobBoundingRect()
02658 
02659     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02660     Created:    11/10/94
02661     Returns:    DocRect - Returns the bounding rect of the blender node and its blobs
02662     Purpose:    At the moment, blender nodes don't have any blobs, so just the standard
02663                 bounding box is returned
02664 
02665 ********************************************************************************************/
02666 
02667 DocRect NodeBlender::GetBlobBoundingRect()
02668 {
02669     return (GetBoundingRect());
02670 }
02671 
02672 
02673 
02674 
02675 /********************************************************************************************
02676 
02677 >   virtual UINT32 NodeBlender::GetNodeSize() const
02678 
02679     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02680     Created:    11/10/94
02681     Returns:    The size of the node in bytes 
02682     Purpose:    For finding the size of the node 
02683     SeeAlso:    Node::GetSubtreeSize
02684 
02685 ********************************************************************************************/
02686 
02687 UINT32 NodeBlender::GetNodeSize() const 
02688 {     
02689     return (sizeof(NodeBlender)); 
02690 }  
02691 
02692 
02693 
02694 /********************************************************************************************
02695 
02696 >   BOOL NodeBlender::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
02697                         Spread* pSpread )
02698 
02699     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02700     Created:    11/10/94
02701     Inputs:     PointerPos - The Location of the mouse pointer at the time of the click
02702                 Click - The type of click received (single, double, drag etc)
02703                 ClickMods - The modifiers to the click (eg shift, control etc )
02704     Returns:    BOOL - TRUE if the node claims the click as its own and FALSE if it is
02705                 not interested in the click
02706     Purpose:    Allows the Node to respond to clicks by selecting its blobs or starting
02707                 drags etc.
02708                 This functions should be overridden in the all the NodeRenderableInk classes
02709                 so that this version never gets called. Eg the NodePath class might claim
02710                 the click if it happened over one of its unselected blobs.
02711 
02712 ********************************************************************************************/
02713 
02714 BOOL NodeBlender::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
02715                         Spread* pSpread )
02716 {
02717     if (!Reinit()) return FALSE;
02718 
02719     // we did not use the click, so let someone else try
02720     return FALSE;
02721 }
02722 
02723 
02724 /********************************************************************************************
02725 
02726 >   virtual BOOL NodeBlender::CanBecomeA(BecomeA* pBecomeA) 
02727 
02728     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02729     Created:    11/10/94
02730     Inputs:     InkClass: The class of object
02731                 pNumObjects = ptr to place number of objects of type pClass that will be created (Note: can be NULL).
02732                               *pNumObects in undefined on entry
02733     Returns:    TRUE if the node, or any of its children can transmogrify themselves to become 
02734                 an InkClass object
02735     Purpose:    This function is used by the convert to shapes operation. It determines if 
02736                 the node or any of its children can convert themselves into an InkClass object. 
02737 
02738                 The number you put into pNumObjects (if it's not NULL) should exactly equal the total number
02739                 of pClass objects you create.  It should NOT contain any additional objects you may produce
02740                 such as group objects for containing the pClass object, or attributes.
02741 
02742                 Also, the entry value of *pNumObjects cannot be assumed to be 0.
02743 
02744 ********************************************************************************************/
02745 
02746 BOOL NodeBlender::CanBecomeA(BecomeA* pBecomeA)
02747 {
02748     // The NodeBlender can become a NodePath
02749     if (pBecomeA->BAPath())
02750     {
02751         if (pBecomeA->IsCounting())
02752         {
02753             if (!Reinit()) return FALSE;
02754 
02755             // Check that we have a valid blender
02756             if (m_pRefStart == NULL || m_pRefEnd == NULL)
02757                 return FALSE;
02758 
02759             UINT32 NumBlendSteps = GetNumBlendSteps();
02760 
02761             // Find num blend paths in start and end, and keep hold of the MAX value
02762             // The max value is the number of individual paths created for each blend step
02763             UINT32 NumPathsInStart = m_pRefStart->GetNumBlendPaths();
02764             UINT32 NumPathsInEnd   = m_pRefEnd  ->GetNumBlendPaths();
02765             UINT32 MaxNumPaths   = max(NumPathsInStart,NumPathsInEnd);
02766 
02767             // The number of objects 
02768 //          *pNumObjects = NumBlendSteps * MaxNumPaths;
02769             pBecomeA->AddCount(NumBlendSteps * MaxNumPaths);
02770         }
02771 
02772         return TRUE;
02773     }
02774 
02775     return FALSE;
02776 }
02777 
02778            
02779 /********************************************************************************************
02780 
02781 >   virtual BOOL NodeBlender::DoBecomeA(CCRuntimeClass* InkClass, UndoableOperation* pOp) 
02782 
02783     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02784     Created:    11/10/94
02785     Inputs:     pBecomeA =  ptr to a class that contains all the info needed to become a new
02786                             type of node.
02787     Outputs:    -
02788     Returns:    TRUE if the object has been transformed, FALSE if we run out of memory
02789 
02790     Purpose:    Transforms the object into another type of object. 
02791     SeeAlso:    NodeBlender::CanBecomeA
02792 
02793 ********************************************************************************************/
02794 
02795 BOOL NodeBlender::DoBecomeA(BecomeA* pBecomeA)
02796 {
02797     if (!Reinit()) return TRUE;
02798 
02799     if (!m_Initialised) return TRUE;        // we MUST handle this for future versions !
02800 
02801     // Check for a NULL entry param
02802     ERROR2IF(pBecomeA == NULL,FALSE,"pBecomeA is NULL");
02803 
02804     // This lump checks that the Reason is one that we understand
02805     // It also makes sure that we don't have a NULL UndoOp ptr
02806     BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK);
02807     ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason()));
02808 
02809     // Set up a HandleBecomeA object and call CreateBlends().
02810     // This mechanism means the the same code is used to both render blends and create paths
02811     // from blends.
02812     HandleBecomeA HBecomeA(this,this,pBecomeA);
02813     CreateBlends(NULL,&HBecomeA);
02814 
02815     switch (pBecomeA->GetReason())
02816     {
02817         case BECOMEA_REPLACE :
02818         {
02819             // It's a BECOMEA_REPLACE, so hide this node in an undoable way
02820 
02821             // Can't do it in an undoable way without an Undo Op
02822 //          ERROR2IF(pBecomeA->GetUndoOp() == NULL,FALSE,"GetUndoOp() returned NULL");
02823 
02824             UndoableOperation* pOp = pBecomeA->GetUndoOp();
02825 
02826             // We need to make sure that all the groups we may have produced have their attrs correctly
02827             // factored out.  This is so the final factoring out done by the NodeBlend node will be able
02828             // to complete the job.
02829             // Without this step, leaf nodes can have common child attrs that should be factored up the
02830             // to a higher level.
02831             Node* pNode = m_pNodeStart->FindNext();
02832 
02833             while (pNode != NULL && pNode != m_pNodeEnd)
02834             {
02835                 //if (IS_A (pNode, NodeGroup))
02836                 if (pNode->IsCompoundClass())
02837                 {
02838                     if (pOp)
02839                     {
02840                         if (!pOp->DoFactorOutCommonChildAttributes((NodeCompound*)pNode))
02841                             return FALSE;
02842                     }
02843                     else
02844                     {
02845                         if (!((NodeCompound*)pNode)->FactorOutCommonChildAttributes())
02846                             return(FALSE);
02847                     }
02848                 }
02849 
02850                 pNode = pNode->FindNext();
02851             }
02852 
02853             NodeHidden* pHiddenNodeBlender = NULL;
02854 
02855             // hide this blender node
02856             if (pOp)
02857             {
02858                 if (!pOp->DoHideNode(this, TRUE, &pHiddenNodeBlender))
02859                     return FALSE;
02860             }
02861             else
02862             {
02863                 CascadeDelete();
02864                 delete this;
02865             }
02866         }
02867         break;
02868 
02869         case BECOMEA_PASSBACK :
02870             // Don't do anything for this reason
02871         break;
02872         default: break;
02873     }
02874 
02875     return TRUE;
02876 }
02877 
02878 /***********************************************************************************************
02879 
02880 > BOOL NodeBlender::Snap(DocCoord* pDocCoord)
02881 
02882     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02883     Created:    11/10/94
02884     Inputs:     pDocCoord   = a coord in Spread coords
02885     Outputs:    
02886     Returns:    TRUE    - the DocCoord has been snapped .
02887                 FALSE   - the DocCoord has not been processed.
02888                                                          
02889     Purpose:    Snaps to given coord to the nearest point on the blender node.
02890                 Just returns FALSE currently
02891     Errors:        
02892     Scope:      public
02893            
02894 **********************************************************************************************/
02895 
02896 BOOL NodeBlender::Snap(DocCoord* pDocCoord)
02897 {
02898     return FALSE;
02899 }
02900 
02901 /***********************************************************************************************
02902 
02903 > BOOL NodeBlender::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord)
02904 
02905     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02906     Created:    11/10/94
02907     Inputs:     pDocCoord   - the rectangle to snap
02908                 StartDrag   - Start coord of drag
02909                 EndDrag     - End coord of drag
02910     Outputs:    
02911     Returns:    TRUE    - the DocRect been snapped.
02912                 FALSE   - the DocRect has not been processed.
02913                                                          
02914     Purpose:    Snaps the given rect to the nearest CMapPtrToPtr::iterator on the grid, preserving its width
02915                 and height.
02916                 The coords of the rect used for the snapping are determined by the PrevCoord and
02917                 CurCoord coords supplied.  This is done to allow the user to control how a
02918                 selection rectangle is snapped to the grid by the direction of his/her last mouse 
02919                 movement.
02920                 To force the bottom left hand corner of the rect to be snapped, 
02921                 supply PrevCoord=(0,0) and CurCoord(-1,-1).
02922 
02923                 ALWAYS RETURNS FALSE currently.
02924 
02925     Scope:      public
02926            
02927 **********************************************************************************************/
02928 
02929 BOOL NodeBlender::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord)
02930 {
02931     return FALSE;
02932 }
02933 
02934 /********************************************************************************************
02935 
02936 > BOOL NodeBlender::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,BOOL* pAStart);
02937 
02938     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02939     Created:    10/11/94
02940     Inputs:     pPointerPos = point to check against
02941                 ppBlendPath= ptr to place to store blend path ptr if a match is found
02942                 pIndex     = ptr to place to store index if match is found
02943                 pAStart    = ptr to place to store start or end flag
02944     Outputs:    *ppBlendPath & *pIndex & *pAStart updated if TRUE returned, undefined otherwise
02945     Returns:    TRUE if match  found, FALSE otherwise
02946     Purpose:    This sees if the point given lies on a selected blob.
02947                 If a match is found, the ptr to the blend path and index to blob element is returned.
02948                 Also, if found,*pAStart = TRUE if it belongs to the start path, FALSE for the end path
02949 
02950                 Markn 26-5-99:  Modified so that if the function returns TRUE, *pPointerPos is updated
02951                                 so that it contains the coords of the blob it is over
02952     SeeAlso:    -
02953 
02954 ********************************************************************************************/
02955 
02956 BOOL NodeBlender::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,BOOL* pAStart)
02957 {
02958 #if !defined(EXCLUDE_FROM_RALPH)
02959     if (m_pRefStart == NULL || m_pRefEnd == NULL || pPointerPos == NULL) return FALSE;
02960 
02961     BOOL Found = FALSE;
02962 
02963     if (m_pRefStart->GetNumBlendPaths() == 1 && m_pRefEnd->GetNumBlendPaths() == 1)
02964     {
02965         Trans2DMatrix*  pRotateStart= NULL;
02966         Trans2DMatrix*  pRotateEnd  = NULL;
02967 
02968         if (GetNodeBlendPath() != NULL)
02969         {
02970             pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),m_AngleStart);
02971             pRotateEnd   = GetRotateMatrix(m_pRefEnd  ->GetNode(),m_AngleEnd  );
02972         }
02973 
02974         if (m_pRefStart->IsPointOverBlob(pPointerPos,ppBlendPath,pIndex,pRotateStart))
02975         {
02976             Found = TRUE;
02977             *pAStart = TRUE;
02978         }
02979 
02980         if (m_pRefEnd->IsPointOverBlob(pPointerPos,ppBlendPath,pIndex,pRotateEnd))
02981         {
02982             Found = TRUE;
02983             *pAStart = FALSE;
02984         }
02985 
02986         DELPTR(pRotateStart);
02987         DELPTR(pRotateEnd);
02988     }
02989 
02990     return Found;
02991 #else
02992     return FALSE;
02993 #endif
02994 }
02995 
02996 //--------------------------------------------------------------------------
02997 //--------------------------------------------------------------------------
02998 //--------------------------------------------------------------------------
02999 
03000 /***********************************************************************************************
03001 
03002 > BOOL NodeBlender::Initialise( NodeRenderableInk*  pNodeStart,
03003                                 NodeRenderableInk*  pNodeEnd,
03004                                 INT32               ThisPathIndexStart,
03005                                 INT32               ThisPathIndexEnd,
03006                                 UndoableOperation*  pUndoOp,
03007                                 Progress*           pProgress,
03008                                 BOOL                IgnoreEscape)
03009 
03010     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03011     Created:    12/10/94
03012     Inputs:     pNodeStart          = ptr to the start of the blend (can be NULL - see later)
03013                 pNodeEnd            = ptr to the end   of the blend (can be NULL - see later)
03014                 ThisPathIndexStart  = the index into the start path of the blob the user blended from (-1 means none)
03015                 ThisPathIndexEnd    = the index into the end   path of the blob the user blended to   (-1 means none)
03016                 pUndoOp             = ptr to an undoable op so that initialisation can be done in an undoable way
03017                                     (can be NULL)
03018                 pProgress           = ptr to progress object for maintaining hour glass
03019                 IgnoreEscape        = TRUE if you don't want this process abortable via the Escape key
03020     Outputs:    -
03021     Returns:    TRUE if successfully initialises, FALSE otherwise
03022     Purpose:    This initialises the blender node so that it is in a state in which it can
03023                 blend the two nodes together.
03024 
03025                 This should be called AFTER this NodeBlender and its start and end nodes have been
03026                 placed under the corresponding NodeBlend node. This is so that it can get a reference
03027                 to the nodes relative to the parent NodeBlend node.
03028 
03029                 If pNodeStart == NULL, then the previous node is chosen.  If there isn't a previous node
03030                 or the node is not a NodeRenderableInk, FALSE will be returned.
03031 
03032                 If pNodeEnd   == NULL, then the next node is chosen.  If there isn't a next node
03033                 or the node is not a NodeRenderableInk, FALSE will be returned.
03034 
03035 ***********************************************************************************************/
03036 
03037 BOOL NodeBlender::Initialise(   NodeRenderableInk*  pThisNodeStart,
03038                                 NodeRenderableInk*  pThisNodeEnd,
03039                                 INT32               ThisPathIndexStart,
03040                                 INT32               ThisPathIndexEnd,
03041                                 UndoableOperation*  pUndoOp,
03042                                 Progress*           pProgress,
03043                                 BOOL                IgnoreEscape)
03044 {
03045     // Copy the ink node ptrs into member vars
03046     m_pNodeStart = pThisNodeStart;
03047     m_pNodeEnd   = pThisNodeEnd;
03048 
03049     // Copy the path index params into member vars
03050     m_PathIndexStart = ThisPathIndexStart;
03051     m_PathIndexEnd   = ThisPathIndexEnd;
03052 
03053     // If the ink nodes are NULL, search for appropriate ink nodes
03054     if (m_pNodeStart == NULL) m_pNodeStart = (NodeRenderableInk*)FindPrevious();
03055     if (m_pNodeEnd   == NULL) m_pNodeEnd   = (NodeRenderableInk*)FindNext();
03056 
03057     // Check the ink nodes
03058     ERROR2IF(m_pNodeStart == NULL,FALSE,"m_pNodeStart == NULL");
03059     ERROR2IF(m_pNodeEnd   == NULL,FALSE,"m_pNodeEnd == NULL");
03060     ERROR2IF(!m_pNodeStart->IS_KIND_OF(NodeRenderableInk),FALSE,"m_pNodeStart not a NodeRenderableInk");
03061     ERROR2IF(!m_pNodeEnd  ->IS_KIND_OF(NodeRenderableInk),FALSE,"m_pNodeEnd not a NodeRenderableInk");
03062     ERROR2IF(m_pNodeStart->IS_KIND_OF(NodeBlender),FALSE,"m_pNodeStart is a NodeBlender");
03063     ERROR2IF(m_pNodeEnd  ->IS_KIND_OF(NodeBlender),FALSE,"m_pNodeEnd not a NodeBlender");
03064 
03065     // Delete old BlendRefs we may have (we might be reinitialising)
03066     DELPTR(m_pRefStart);
03067     DELPTR(m_pRefEnd);
03068 
03069     // Reset the member vars before we try and initialise the blender
03070     m_pRefStart     = NULL;
03071     m_pRefEnd       = NULL;
03072     m_ObjIndexStart = -1;
03073     m_ObjIndexEnd   = -1;
03074     m_Initialised   = FALSE;
03075 
03076     Trans2DMatrix*  pRotateStart= NULL;
03077     Trans2DMatrix*  pRotateEnd  = NULL;
03078 
03079     if (GetNodeBlendPath() != NULL)
03080     {
03081         pRotateStart = GetRotateMatrix(m_pNodeStart,360.0 - m_AngleStart);
03082         pRotateEnd   = GetRotateMatrix(m_pNodeEnd  ,360.0 - m_AngleEnd  );
03083     }
03084 
03085     BOOL ok = TRUE;
03086 
03087     // CGS:  we can have funky blends on paths.  Everything works fine if they both have a path.
03088     // However, if they do NOT both have paths - then Mr Blender (incorrectly) blends the NodeBlendPath
03089     // into the paths generated by the other blend!
03090 
03091     // we SHOULD prevent this from happening ....
03092 
03093     if (ok) ok = InitBlendRef(m_pNodeStart,&m_pRefStart,m_PathIndexStart,pUndoOp,pProgress,IgnoreEscape,pRotateStart);
03094     if (ok) ok = InitBlendRef(m_pNodeEnd  ,&m_pRefEnd  ,m_PathIndexEnd  ,pUndoOp,pProgress,IgnoreEscape,pRotateEnd);
03095 
03096     if (ok) { m_pRefStart->StripRedundantNodeBlendPaths (m_pRefEnd); }
03097 
03098     if (ok) ok = CalcObjIndex(m_pNodeStart,&m_ObjIndexStart);
03099     if (ok) ok = CalcObjIndex(m_pNodeEnd  ,&m_ObjIndexEnd);
03100 
03101     // Ensure these indexes are kept up to date (needed for cut/copy/paste, export, etc).
03102     // Note that the following code dumps the indexes computed by the calls to InitBlendRef
03103     // above and recomputes them before assigning them to m_PathIndexStart and m_PathIndexEnd
03104     // (A lot of DoBecomeA work to compute two numbers...)
03105     if (((m_pRefStart->GetNumBlendPaths () > 1) && (m_pRefEnd->GetNumBlendPaths () == 1))
03106         ||
03107         ((m_pRefStart->GetNumBlendPaths () == 1) && (m_pRefEnd->GetNumBlendPaths () > 1)))
03108     {
03109         m_PathIndexStart = -1;
03110         m_PathIndexEnd = -1;
03111         DELPTR(m_pRefStart);
03112         DELPTR(m_pRefEnd);
03113 
03114         if (ok) ok = InitBlendRef(m_pNodeStart,&m_pRefStart,m_PathIndexStart,pUndoOp,pProgress,IgnoreEscape,pRotateStart);
03115         if (ok) ok = InitBlendRef(m_pNodeEnd  ,&m_pRefEnd  ,m_PathIndexEnd  ,pUndoOp,pProgress,IgnoreEscape,pRotateEnd);
03116 
03117         if (ok) { m_pRefStart->StripRedundantNodeBlendPaths (m_pRefEnd); }
03118     }
03119 
03120     if (ok) m_PathIndexStart = m_pRefStart->GetOrigMapping();
03121     if (ok) m_PathIndexEnd   = m_pRefEnd  ->GetOrigMapping();
03122 
03123     // Make sure the Initialised flag is TRUE only if we have done everything successfully.
03124     if (ok) CheckFullyInitialised();
03125     if (ok) ok = m_Initialised;
03126 
03127     DELPTR(pRotateStart);
03128     DELPTR(pRotateEnd);
03129 
03130     // Don't need to delete the BlendRefs if things go wrong.
03131     // They'll get deleted next time it's initialised, or by the deconstructor
03132 
03133     return (ok);
03134 }
03135 
03136 
03137 
03138 /***********************************************************************************************
03139 
03140 > BOOL NodeBlender::InitBlendRef(NodeRenderableInk* pNode,BlendRef** ppRef,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix)
03141 
03142     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03143     Created:    12/10/94
03144     Inputs:     pNode       = ptr to a node in the tree
03145                 ppRef       = ptr to place to store a new BlendRef structure
03146                 Index       = index into path of blob clicked on (-1 means ignore)
03147                 pUndoOp     = ptr to an undoable op so that initialisation can be done in an undoable way
03148                               (can be NULL)
03149                 pProgress   = ptr to progress object for maintaining hour glass
03150                 IgnoreEscape= TRUE if you don't want this process abortable via the Escape key
03151                 pMatrix     = transformation to apply to each blend path (can be NULL)
03152     Outputs:    *ppRef = ptr to an initialised blend reference
03153     Returns:    TRUE if successfully initialises, FALSE otherwise
03154     Purpose:    This creates and initialises a blend reference.
03155 
03156 ***********************************************************************************************/
03157 
03158 BOOL NodeBlender::InitBlendRef(NodeRenderableInk* pNode,BlendRef** ppRef,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix)
03159 {
03160     ERROR2IF(ppRef == NULL,FALSE,"ppRef == NULL");
03161 
03162     ALLOC_WITH_FAIL(*ppRef,new BlendRef,pUndoOp);
03163     BOOL ok = (*ppRef != NULL);
03164 
03165     if (ok) ok = (*ppRef)->Initialise(pNode,Index,pUndoOp,pProgress,IgnoreEscape,pMatrix);
03166 
03167     return (ok);
03168 }
03169 
03170 /********************************************************************************************
03171 
03172 >   BOOL NodeBlender::CalcObjIndex(NodeRenderableInk* pInkNode,INT32* pObjIndex)
03173 
03174     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03175     Created:    12/10/94
03176     Inputs:     pInkNode   = ptr to a node that this blender will blend
03177                 pObjIndex = ptr to place to store the relative reference to this node
03178     Outputs:    *pObjIndex will contain the reference if TRUE is returned, -1 if FALSE is returned
03179     Returns:    TRUE if successful, FALSE otherwise
03180     Purpose:    Firstly, this will check that this node and pInkNode have the same NodeBlend parent node.
03181                 If all's well, it will scan the children of the NodeBlend, starting with the first child,
03182                 counting until it comes to pInkNode.
03183                 If pInkNode is the first child, *pObjIndex == 0. If the second child, *pObjIndex == 1, etc
03184     SeeAlso:    -
03185 
03186 ********************************************************************************************/
03187 
03188 BOOL NodeBlender::CalcObjIndex(NodeRenderableInk* pInkNode,INT32* pObjIndex)
03189 {
03190     ERROR2IF(pInkNode == NULL,FALSE,"pInkNode is NULL");
03191     ERROR2IF(pObjIndex == NULL,FALSE,"pObjIndex is NULL");
03192 
03193     Node* pParent = pInkNode->FindParent();
03194 
03195     /*if (pInkNode->IsNodeHidden ())
03196     {
03197         ERROR3 ("Node is hidden!");
03198     }*/
03199 
03200     ERROR2IF(pParent == NULL,FALSE,"pInkNode has no parent");
03201 
03202     if (!pParent->IsController ())      // CGS:  we can now have compound nodes in here!
03203     {
03204         ERROR2IF(!IS_A(pParent,NodeBlend),FALSE,"pInkNode parent is not a NodeBlend");
03205         ERROR2IF(pParent != this->FindParent(),FALSE,"pInkNode has different parent to this node");
03206     }
03207     else
03208     {
03209         // compound might be shadowed - if it is, find the shadow, and check that its parent is the NodeBlend ....
03210         if (IS_A (pParent->FindParent (), NodeShadowController))
03211         {
03212             pParent = pParent->FindParent ()->FindParent ();
03213         }
03214         
03215         ERROR2IF(pParent->FindParent () != this->FindParent(),FALSE,"pInkNode has different parent to this node");
03216     }
03217 
03218     INT32 Index = 0;
03219     Node* pNode = pParent->FindFirstChild();
03220 
03221     while (pNode != NULL && pNode != pInkNode)
03222     {
03223         if (pNode->IS_KIND_OF(NodeRenderableInk))
03224             Index++;
03225         if (pNode->IS_KIND_OF(NodeBlendPath))
03226             Index--;
03227         pNode = pNode->FindNext();
03228     }
03229 
03230     if (pNode == pInkNode)
03231         *pObjIndex = Index;
03232     else
03233         *pObjIndex = -1;
03234 
03235     return (pNode == pInkNode);
03236 }
03237 
03238 /********************************************************************************************
03239 
03240 >   NodeRenderableInk* NodeBlender::FindObjIndexedNode(INT32 ObjIndex)
03241 
03242     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03243     Created:    12/10/94  Adjusted Diccon Yamanaka 16/9/99
03244     Inputs:     ObjIndex = Relative ref to the object under the parent of this node
03245     Outputs:    -
03246     Returns:    Ptr to the node. NULL if couldn't be found or node is of wrong type
03247     Purpose:    This scans the children of the parent of this blender, until it finds
03248                 the ObjIndex'th NodeRenderableInk. I.e. if ObjIndex == 0, it returns the first child NodeRenderableInk.
03249                 if ObjIndex == 1, returns the second child NodeRenderableInk.
03250                 Fails if:
03251                     ObjIndex >= number of children the parent has
03252                     The node found is not a NodeRenderableInk or is a NodeBlender
03253     SeeAlso:    -
03254 
03255 ********************************************************************************************/
03256 
03257 NodeRenderableInk* NodeBlender::FindObjIndexedNode(INT32 ObjIndex)
03258 {
03259     ERROR3IF_PF(ObjIndex < 0,("ObjIndex is -ve!! (%ld)",ObjIndex));
03260     if (ObjIndex < 0) return NULL;
03261 
03262     Node* pParent = this->FindParent();
03263     Node* pFoundNode = NULL;
03264 
03265     ERROR3IF(pParent == NULL,"pInkNode has no parent");
03266     ERROR3IF(!IS_A(pParent,NodeBlend),"parent is not a NodeBlend");
03267 
03268     if (pParent != NULL && IS_A(pParent,NodeBlend))
03269     {
03270         Node* pNode = pParent->FindFirstChild();
03271         while (pNode != NULL && ObjIndex >= 0)
03272         {
03273             if (pNode->IS_KIND_OF(NodeRenderableInk))
03274             {
03275                 //DY 16/9/99 fix to make blending along a curve possible
03276                 // as we insert NodeBlendPath during that operation.
03277                 if (!pNode->IS_KIND_OF(NodeBlendPath))
03278                 {
03279                     if (ObjIndex == 0)  
03280                         pFoundNode = pNode;
03281                     ObjIndex--;
03282                 }
03283             }
03284             pNode = pNode->FindNext();
03285         }
03286     }
03287 
03288     if (pFoundNode != NULL)
03289     {
03290         if (IS_A(pFoundNode,NodeBlender))
03291         {
03292             ERROR3("Found node is a NodeBlender");
03293             pFoundNode = NULL;
03294         }
03295         else if (!pFoundNode->IS_KIND_OF(NodeRenderableInk))
03296         {
03297             ERROR3("Found node is not a NodeRenderableInk");
03298             pFoundNode = NULL;
03299         }
03300     }
03301     else
03302         ERROR3("Couldn't find ref node");
03303 
03304     return ((NodeRenderableInk*)pFoundNode);
03305 }
03306 
03307 
03308 /********************************************************************************************
03309 >   virtual BOOL NodeBlender::CanSelectAsSimple()
03310 
03311     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03312     Created:    06/01/95
03313     Inputs:     -
03314     Outputs:    -
03315     Returns:    TRUE if the node will allow itself to be selected when it has been "hit"
03316                 FALSE otherwise
03317     Purpose:    Ask a node whether or not it's prepared to become selected when it's
03318                 clicked on.
03319                 This function is called in the FindSimple routines when they have just detected
03320                 a "hit" on a node.
03321                 This virtual base function is overridden here to indicate that the NodeBlender
03322                 object is definitely NOT prepared to become selected!
03323     Errors:     -
03324     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint
03325 ********************************************************************************************/
03326 
03327 BOOL NodeBlender::CanSelectAsSimple()
03328 {
03329     return  FALSE;
03330 }
03331 
03332 
03333 
03334 
03335 /********************************************************************************************
03336 
03337 >   void NodeBlender::CheckFullyInitialised()
03338 
03339     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03340     Created:    12/10/94
03341     Inputs:     -
03342     Outputs:    -
03343     Returns:    -
03344     Purpose:    This will set the object's Initialise member to TRUE if it is fully initialised,
03345                 i.e. pRefStart & pRefEnd != NULL, and ObjIndexStart and ObjIndexEnd and >= 0
03346     SeeAlso:    -
03347 
03348 ********************************************************************************************/
03349 
03350 void NodeBlender::CheckFullyInitialised()
03351 {
03352     m_Initialised = ((m_pRefStart != NULL) &&
03353                      (m_pRefEnd   != NULL) &&
03354                      (m_ObjIndexStart >= 0)  &&
03355                      (m_ObjIndexEnd   >= 0));
03356 }
03357 
03358 
03359 /********************************************************************************************
03360 
03361 >   BOOL NodeBlender::Reinit(NodeRenderableInk* pInkNodeStart = NULL,NodeRenderableInk* pInkNodeEnd = NULL,BOOL ProgressBar = TRUE)
03362 
03363     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03364     Created:    12/10/94  adjusted Diccon Yamanaka 16/9/99
03365     Inputs:     pInkNodeStart = ptr to the node to blend from (can be NULL)
03366                 pInkNodeEnd   = ptr to the node to blend to   (can be NULL)
03367                 ProgressBar   = if TRUE a progress bar will be displayed.
03368     Outputs:    -
03369     Returns:    TRUE if successful, FALSE otherwise
03370     Purpose:    This will try and reinitialise the blender by using either the relative reference
03371                 values ObjIndexStart and ObjIndexEnd (if input params are NULL) or the nodes provided.
03372                 
03373                 If using the indexes, it finds the actual pointers to the nodes that they reference, and calls Initialise()
03374 
03375                 If already initialised, it returns TRUE immediately
03376     SeeAlso:    -
03377 
03378 ********************************************************************************************/
03379 
03380 BOOL NodeBlender::Reinit(NodeRenderableInk* pInkNodeStart,NodeRenderableInk* pInkNodeEnd,BOOL ProgressBar)
03381 {
03382     if (GetNodeBlend())
03383     {
03384         CProfileBiasGain* pObjProfile = GetNodeBlend()->GetObjectProfile();
03385         if (pObjProfile)
03386         {
03387             // this ensures that blend object profile processing is ON if we need it ....
03388             GetNodeBlend()->RequestObjectProfileProcessing(TRUE);
03389         }
03390     }
03391     
03392     if (m_Initialised) return TRUE;
03393 
03394     /* Diccon 9/99 changes to permit blending between two blends using arbitrary start and end points
03395      mean that NodeBlenders are no longer necessarily CMapPtrToPtr::iteratored in between the two nodes that they are
03396      blending.  This means that the third method of finding start and end nodes is now likely to yield
03397      incorrect results, with the sole exception of when you are loading an old format file  */
03398 
03399     if (pInkNodeStart == NULL) pInkNodeStart = m_pNodeStart;
03400     if (pInkNodeEnd   == NULL) pInkNodeEnd   = m_pNodeEnd;
03401 
03402     // If still no start and end node, use the ObjIndex value to find them
03403     if (pInkNodeStart == NULL && m_ObjIndexStart >= 0) pInkNodeStart = FindObjIndexedNode(m_ObjIndexStart);
03404     if (pInkNodeEnd   == NULL && m_ObjIndexEnd   >= 0) pInkNodeEnd   = FindObjIndexedNode(m_ObjIndexEnd);
03405 
03406     // the only time we should be using this method is if we are loading old format files with blends in them
03407     if (pInkNodeStart == NULL) pInkNodeStart = (NodeRenderableInk*)FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk));
03408     if (pInkNodeEnd   == NULL) pInkNodeEnd   = (NodeRenderableInk*)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
03409     
03410     while (pInkNodeEnd->IS_KIND_OF(NodeBlendPath))
03411         pInkNodeEnd   = (NodeRenderableInk*)pInkNodeEnd->FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
03412 
03413     
03414     // In debug builds, inform the user, as something's gone wrong
03415     ERROR3IF(pInkNodeStart == NULL,"pInkNodeStart is NULL");
03416     ERROR3IF(pInkNodeEnd   == NULL,"pInkNodeEnd is NULL");
03417     
03418     if (pInkNodeStart == NULL || pInkNodeEnd == NULL) return TRUE;
03419 
03420     if (ProgressBar)
03421     {
03422         Progress Hourglass(_R(IDS_REINTBLEND));
03423         return (Initialise(pInkNodeStart,pInkNodeEnd,m_PathIndexStart,m_PathIndexEnd,NULL,&Hourglass,TRUE));
03424     }
03425     else
03426         return (Initialise(pInkNodeStart,pInkNodeEnd,m_PathIndexStart,m_PathIndexEnd,NULL,NULL,TRUE));
03427 }
03428 
03429 /********************************************************************************************
03430 
03431 >   void NodeBlender::Deinit(BOOL bNodesMayBeChanged = FALSE)
03432 
03433     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03434     Created:    4/11/94
03435     Inputs:     -
03436     Outputs:    -
03437     Returns:    -
03438     Purpose:    This deinits the node, setting its internal Initialised flag to FALSE,
03439                 and deleting any memory that is created during initialisation.
03440                 It's like a combination of the constructor and destructor, leaving it in 
03441                 a state similar to defualt construction.
03442                 The state is such that a call to Reinit() can exactly recreate the blender node.
03443                 Used when hiding NodeBlenders to free up some memory.
03444     SeeAlso:    -
03445 
03446 ********************************************************************************************/
03447 
03448 void NodeBlender::Deinit(BOOL bNodesMayBeChanged)
03449 {
03450     // CGS:  we must now delete any intermediate blend step generated stuff ....
03451     // NOTE:  these lists will only contains items when we have been asked to do
03452     // a blend of a grouped blend to another grouped blend.
03453 
03454     Node* node = FindPrevious ();
03455 
03456     NodeBlend* node2 = (NodeBlend*) node->FindFirstChild (CC_RUNTIME_CLASS (NodeBlend));
03457 
03458     NodeGroup::KillAllBlendBecomeAConsListItem (node2);
03459 
03460     if (bNodesMayBeChanged)
03461     {
03462         m_pNodeStart = NULL;
03463         m_pNodeEnd = NULL;
03464     }
03465     
03466     if (m_Initialised)
03467     {
03468         DELPTR(m_pRefStart);
03469         DELPTR(m_pRefEnd);
03470 
03471         m_Initialised   = FALSE;
03472     }
03473     
03474     // if we are on a curve then destroy the cached points of the curve
03475     NodeBlendPath* pNodeBlendPath = GetNodeBlendPath();
03476     if (pNodeBlendPath != NULL)
03477         pNodeBlendPath->DestroyCachedInformation();
03478 }
03479 
03480 /********************************************************************************************
03481 
03482 >   UINT32 NodeBlender::GetNumBlendSteps()
03483 
03484     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03485     Created:    4/11/94
03486     Inputs:     -
03487     Outputs:    -
03488     Returns:    Num blend steps for this blender
03489     Purpose:    Gets the number of blend steps for this blender, by asking the parent blend node
03490                 for its num blend steps
03491     SeeAlso:    -
03492 
03493 ********************************************************************************************/
03494 
03495 UINT32 NodeBlender::GetNumBlendSteps()
03496 {
03497     UINT32 NumBlendSteps = 5;
03498 
03499     NodeBlend* pNode = GetNodeBlend();
03500     if (pNode != NULL)
03501         NumBlendSteps = pNode->GetNumBlendSteps();
03502     else
03503     {
03504         ERROR3("Parent of blender is not there, or is not a NodeBlend!!!");
03505     }
03506 
03507     return NumBlendSteps;
03508 }
03509 
03510 /********************************************************************************************
03511 
03512 >   ColourBlendType NodeBlender::GetColourBlendType()
03513 
03514     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03515     Created:    17/3/95
03516     Inputs:     -
03517     Outputs:    -
03518     Returns:    How colours are blended
03519     Purpose:    Gets the way colours are blended by asking the parent blend node
03520     SeeAlso:    -
03521 
03522 ********************************************************************************************/
03523 
03524 ColourBlendType NodeBlender::GetColourBlendType()
03525 {
03526     ColourBlendType ColBlendType = COLOURBLEND_FADE;
03527 
03528     NodeBlend* pNode = GetNodeBlend();
03529     if (pNode != NULL)
03530         ColBlendType = pNode->GetColourBlendType();
03531     else
03532     {
03533         ERROR3("Parent of blender is not there, or is not a NodeBlend!!!");
03534     }
03535 
03536     return ColBlendType;
03537 }
03538 
03539 /********************************************************************************************
03540 
03541 >   BOOL NodeBlender::IsOneToOne()
03542 
03543     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03544     Created:    30/11/94
03545     Inputs:     -
03546     Outputs:    -
03547     Returns:    The one-to-one flag state
03548     Purpose:    Gets the one-to-one blend mapping state for this blender, by asking the parent blend node
03549     SeeAlso:    -
03550 
03551 ********************************************************************************************/
03552 
03553 BOOL NodeBlender::IsOneToOne()
03554 {
03555     BOOL OneToOne = FALSE;
03556 
03557     NodeBlend* pNode = GetNodeBlend();
03558     if (pNode != NULL)
03559         OneToOne = pNode->IsOneToOne();
03560     else
03561     {
03562         ERROR3("Parent of blender is not there, or is not a NodeBlend!!!");
03563     }
03564 
03565     return OneToOne;
03566 }
03567 
03568 /********************************************************************************************
03569 
03570 >   BOOL NodeBlender::IsNotAntialiased()
03571 
03572     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03573     Created:    30/11/94
03574     Inputs:     -
03575     Outputs:    -
03576     Returns:    The NotAntialiased flag state
03577     Purpose:    Gets the NotAntialiased state for this blender, by asking the parent blend node
03578     SeeAlso:    -
03579 
03580 ********************************************************************************************/
03581 
03582 BOOL NodeBlender::IsNotAntialiased()
03583 {
03584     BOOL NotAntialiased = FALSE;
03585 
03586     NodeBlend* pNode = GetNodeBlend();
03587     if (pNode != NULL)
03588         NotAntialiased = pNode->IsNotAntialiased();
03589     else
03590     {
03591         ERROR3("Parent of blender is not there, or is not a NodeBlend!!!");
03592     }
03593 
03594     return NotAntialiased;
03595 }
03596 
03597 /********************************************************************************************
03598 
03599 >   BOOL NodeBlender::IsTangential()
03600 
03601     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03602     Created:    21/5/96
03603     Inputs:     -
03604     Returns:    TRUE if this blend should follow the curve tangentially
03605     Purpose:    See above
03606     SeeAlso:    -
03607 
03608 ********************************************************************************************/
03609 
03610 BOOL NodeBlender::IsTangential()
03611 {
03612     NodeBlend* pNode = GetNodeBlend();
03613     if (pNode != NULL)
03614         return pNode->IsTangential();
03615 
03616     return FALSE;
03617 }
03618 
03619 
03620 /********************************************************************************************
03621 
03622 >   INT32 NodeBlender::GetNodeBlendPathIndex()
03623 
03624     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03625     Created:    26/9/99
03626     Inputs:     -
03627     Returns:    the index to the nodeblendpath to which this blender refers, or -1
03628                 if it is not on a curve
03629     Purpose:    See above
03630     SeeAlso:    -
03631 
03632 ********************************************************************************************/
03633 
03634 INT32 NodeBlender::GetNodeBlendPathIndex()
03635 {
03636     return m_NodeBlendPathIndex;
03637 }
03638 
03639 
03640 /********************************************************************************************
03641 
03642 >   void NodeBlender::SetNodeBlendPathIndex(INT32 Index)
03643 
03644     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03645     Created:    26/9/99
03646     Inputs:     the index to the nodeblendpath to which this blender refers, or -1
03647                 if it is not on a curve
03648     Returns:    -
03649     Purpose:    See above
03650     SeeAlso:    -
03651 
03652 ********************************************************************************************/
03653 
03654 void NodeBlender::SetNodeBlendPathIndex(INT32 Index)
03655 {
03656     if (Index < -2)
03657     {
03658         ERROR3("Invalid index value");
03659         return;
03660     }
03661 
03662     m_NodeBlendPathIndex = Index;
03663     return;
03664 }
03665 
03666 
03667 /********************************************************************************************
03668 
03669 >   BOOL NodeBlender::ReverseEnds()
03670 
03671     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03672     Created:    29/9/99
03673     Inputs:     -
03674     Returns:    TRUE if successfull, FALSE otherwise
03675     Purpose:    reverses the blend so that the start node becomes the end node and vice versa
03676     SeeAlso:    -
03677 
03678 ********************************************************************************************/
03679 
03680 BOOL NodeBlender::ReverseEnds()
03681 {
03682     NodeRenderableInk* pTemp = NULL;
03683     pTemp = m_pNodeStart;
03684     m_pNodeStart = m_pNodeEnd;
03685     m_pNodeEnd = pTemp;
03686 
03687     SetReversed(!IsReversed());
03688 
03689     INT32 Temp = m_ObjIndexStart;
03690     m_ObjIndexStart = m_ObjIndexEnd;
03691     m_ObjIndexEnd = Temp;
03692 
03693     return TRUE;
03694 }
03695 
03696 
03697 /********************************************************************************************
03698 
03699 >   BOOL NodeBlender::PreBlend()
03700 
03701     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03702     Created:    7/11/94
03703     Inputs:     -
03704     Outputs:    -
03705     Returns:    TRUE if it's OK to continue, FALSE otherwise
03706     Purpose:    This should be called just before blending occurs.
03707                 At the moment it does the following -
03708                     Initialises stuff so you can call GetCoordArray(), GetVerbArray() and GetFlagArray()
03709     SeeAlso:    -
03710 
03711 ********************************************************************************************/
03712 
03713 BOOL NodeBlender::PreBlend()
03714 {
03715     // Clean up in case PostBlend() wasn't called after the last PreBlend() call
03716     PostBlend();
03717     if (m_pRefStart != NULL) m_pRefStart->PreBlend();
03718     if (m_pRefEnd   != NULL) m_pRefEnd  ->PreBlend();
03719 
03720     // Allocate the arrays
03721     return (ReallocTempBuffers(1000));
03722 }
03723 
03724 /********************************************************************************************
03725 
03726 >   void NodeBlender::PostBlend()
03727 
03728     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03729     Created:    7/11/94
03730     Inputs:     -
03731     Outputs:    -
03732     Returns:    -
03733     Purpose:    This should be called just after blending occurs.
03734                 At the moment it does the following -
03735                     Releases memory allocated for the temp path arrays
03736     SeeAlso:    -
03737 
03738 ********************************************************************************************/
03739 
03740 void NodeBlender::PostBlend()
03741 {
03742     if (m_pRefStart != NULL) m_pRefStart->PostBlend();
03743     if (m_pRefEnd   != NULL) m_pRefEnd  ->PostBlend();
03744     DeallocTempBuffers();
03745 }
03746 
03747 /********************************************************************************************
03748 
03749 > BOOL NodeBlender::ReallocTempBuffers(UINT32 Size)
03750 
03751     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03752     Created:    7/11/94
03753     Inputs:     Size = Size the temp arrays should be
03754     Outputs:    -
03755     Returns:    -
03756     Purpose:    Allocates memory for the temp path arrays, and sets the size var to 0
03757                 You can use calls to GetCoordArray(), GetVerbArray() and GetFlagArray() to get the alloced arrays.
03758     SeeAlso:    -
03759 
03760 ********************************************************************************************/
03761 
03762 BOOL NodeBlender::ReallocTempBuffers(UINT32 Size)
03763 {
03764     DeallocTempBuffers();
03765 
03766     // Allocate the arrays
03767     m_TempArraySize = Size;
03768     m_pTempCoords   = (DocCoord*)  CCMalloc(Size*sizeof(DocCoord));
03769     m_pTempVerbs    = (PathVerb*)  CCMalloc(Size*sizeof(PathVerb));
03770     m_pTempFlags    = (PathFlags*) CCMalloc(Size*sizeof(PathFlags));
03771 
03772     // If any of the arrays are not allocated, dealloc the alloced ones, and return FALSE
03773     if (m_pTempCoords == NULL || m_pTempVerbs == NULL || m_pTempFlags == NULL)
03774     {
03775         DeallocTempBuffers();
03776         return FALSE;
03777     }
03778 
03779     // It's all OK, so return TRUE
03780     return TRUE;
03781 }
03782 
03783 /********************************************************************************************
03784 
03785 >   void NodeBlender::DeallocTempBuffers()
03786 
03787     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03788     Created:    7/11/94
03789     Inputs:     -
03790     Outputs:    -
03791     Returns:    -
03792     Purpose:    Releases memory allocated for the temp path arrays, and sets the size var to 0
03793     SeeAlso:    -
03794 
03795 ********************************************************************************************/
03796 
03797 void NodeBlender::DeallocTempBuffers()
03798 {
03799     if (m_pTempCoords != NULL) { CCFree(m_pTempCoords); m_pTempCoords = NULL; }
03800     if (m_pTempVerbs  != NULL) { CCFree(m_pTempVerbs);  m_pTempVerbs  = NULL; }
03801     if (m_pTempFlags  != NULL) { CCFree(m_pTempFlags);  m_pTempFlags  = NULL; }
03802     m_TempArraySize = 0;
03803 
03804     if (m_pGBlendBuff != NULL) { CCFree(m_pGBlendBuff); m_pGBlendBuff = NULL; }
03805     m_GBlendBuffSize = 0;
03806 }
03807 
03808 /********************************************************************************************
03809 
03810 >   DocCoord*  NodeBlender::GetCoordArray(UINT32 MinSize);
03811 
03812     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03813     Created:    7/11/94
03814     Inputs:     MinSize = min size the array should be
03815     Outputs:    -
03816     Returns:    Ptr to the array, or NULL if can't get memory
03817     Purpose:    Used to get an array you can write to.
03818     SeeAlso:    -
03819 
03820 ********************************************************************************************/
03821 
03822 DocCoord* NodeBlender::GetCoordArray(UINT32 MinSize)
03823 {
03824     MinSize++;
03825     if (m_TempArraySize >= MinSize) return m_pTempCoords;
03826 
03827     if (ReallocTempBuffers(MinSize))
03828         return m_pTempCoords;
03829     else
03830         return NULL;
03831 }
03832 
03833 /********************************************************************************************
03834 
03835 >   PathVerb*  NodeBlender::GetVerbArray(UINT32 MinSize);
03836 
03837     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03838     Created:    7/11/94
03839     Inputs:     MinSize = min size the array should be
03840     Outputs:    -
03841     Returns:    Ptr to the array, or NULL if can't get memory
03842     Purpose:    Used to get an array you can write to.
03843     SeeAlso:    -
03844 
03845 ********************************************************************************************/
03846 
03847 PathVerb* NodeBlender::GetVerbArray(UINT32 MinSize)
03848 {
03849     MinSize++;
03850     if (m_TempArraySize >= MinSize) return m_pTempVerbs;
03851 
03852     if (ReallocTempBuffers(MinSize))
03853         return m_pTempVerbs;
03854     else
03855         return NULL;
03856 }
03857 
03858 /********************************************************************************************
03859 
03860 >   PathFlags*  NodeBlender::GetFlagArray(UINT32 MinSize);
03861 
03862     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03863     Created:    7/11/94
03864     Inputs:     MinSize = min size the array should be
03865     Outputs:    -
03866     Returns:    Ptr to the array, or NULL if can't get memory
03867     Purpose:    Used to get an array you can write to.
03868     SeeAlso:    -
03869 
03870 ********************************************************************************************/
03871 
03872 PathFlags* NodeBlender::GetFlagArray(UINT32 MinSize)
03873 {
03874     MinSize++;
03875     if (m_TempArraySize >= MinSize) return m_pTempFlags;
03876 
03877     if (ReallocTempBuffers(MinSize))
03878         return m_pTempFlags;
03879     else
03880         return NULL;
03881 }
03882 
03883 /********************************************************************************************
03884 
03885 >   UINT32*  NodeBlender::GetGBlendBuff(UINT32 MinSize);
03886 
03887     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03888     Created:    7/11/94
03889     Inputs:     MinSize = min size the buffer should be
03890     Outputs:    -
03891     Returns:    Ptr to the buffer, or NULL if can't get memory
03892     Purpose:    Used to get a buffer you can write to.
03893     SeeAlso:    -
03894 
03895 ********************************************************************************************/
03896 
03897 UINT32* NodeBlender::GetGBlendBuff(UINT32 MinSize)
03898 {
03899     MinSize++;
03900     if (m_GBlendBuffSize >= MinSize) return m_pGBlendBuff;
03901 
03902     if (m_pGBlendBuff != NULL) CCFree(m_pGBlendBuff);
03903 
03904     m_pGBlendBuff = (UINT32*) CCMalloc(MinSize*sizeof(UINT32));
03905 
03906     if (m_pGBlendBuff != NULL)
03907         m_GBlendBuffSize = MinSize;
03908     else
03909         m_GBlendBuffSize = 0;
03910 
03911     return m_pGBlendBuff;
03912 }
03913 
03914 /********************************************************************************************
03915 
03916 > BOOL NodeBlender::Remap(DocCoord PosStart,DocCoord PosEnd,DocCoord* pInvPosStart,DocCoord* pInvPosEnd)
03917 
03918     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03919     Created:    10/11/94
03920     Inputs:     PosStart    = Coord of element to remap start node to
03921                 PosEnd      = Coord of element to remap end node to
03922                 pInvPosStart= ptr to place to put the coord to use to invert the start mapping
03923                 pInvPosEnd  = ptr to place to put the coord to use to invert the end mapping
03924     Outputs:    -
03925     Returns:    TRUE if remapped, FALSE otherwise
03926     Purpose:    If this blender manages to remap a pair of blend paths, then this will return
03927                 TRUE. 
03928     SeeAlso:    -
03929 
03930 ********************************************************************************************/
03931 
03932 BOOL NodeBlender::Remap(DocCoord PosStart,DocCoord PosEnd,DocCoord* pInvPosStart,DocCoord* pInvPosEnd)
03933 {
03934     BOOL Mapped = FALSE;
03935 
03936     if (m_pRefStart != NULL && m_pRefEnd != NULL)
03937     {
03938         BlendPath* pBlendPathStart = m_pRefStart->GetFirstBlendPath();
03939         BlendPath* pBlendPathEnd   = m_pRefEnd  ->GetFirstBlendPath();
03940 
03941         if (pBlendPathStart != NULL && pBlendPathEnd != NULL)
03942         {
03943             if (GetNodeBlendPath() != NULL)
03944             {
03945                 Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),360.0 - m_AngleStart);
03946                 Trans2DMatrix* pRotateEnd   = GetRotateMatrix(m_pRefEnd  ->GetNode(),360.0 - m_AngleEnd  );
03947                 if (pRotateStart)   pRotateStart->Transform(&PosStart,1);
03948                 if (pRotateEnd)     pRotateEnd  ->Transform(&PosEnd  ,1);
03949                 DELPTR(pRotateStart);
03950                 DELPTR(pRotateEnd);
03951             }
03952 
03953             *pInvPosStart = pBlendPathStart->MapPath(PosStart);
03954             *pInvPosEnd   = pBlendPathEnd  ->MapPath(PosEnd);
03955             Mapped = TRUE;
03956 
03957             if (GetNodeBlendPath() != NULL)
03958             {
03959                 Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),m_AngleStart);
03960                 Trans2DMatrix* pRotateEnd   = GetRotateMatrix(m_pRefEnd  ->GetNode(),m_AngleEnd  );
03961                 if (pRotateStart)   pRotateStart->Transform(pInvPosStart,1);
03962                 if (pRotateEnd)     pRotateEnd  ->Transform(pInvPosEnd  ,1);
03963                 DELPTR(pRotateStart);
03964                 DELPTR(pRotateEnd);
03965             }
03966         }
03967 
03968         // Keep the path mapping index up to date (necessary for cut/copy/paste, export, etc).
03969         m_PathIndexStart = m_pRefStart->GetOrigMapping();
03970         m_PathIndexEnd   = m_pRefEnd  ->GetOrigMapping();
03971     }
03972 
03973     return Mapped;
03974 }
03975 
03976 /********************************************************************************************
03977 
03978 > BOOL NodeBlender::ConvertAWPathIndexesToCamelot(INT32 *pPathIndexStart,INT32* pPathIndexEnd)
03979 
03980     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03981     Created:    25/11/94
03982     Inputs:     pPathIndexStart = index of element in start node 
03983                 pPathIndexEnd   = index of element in end node
03984     Outputs:    *pPathIndexStart = possibly modified index for start node
03985                 *pPathIndexEnd   = possibly modified index for end node
03986     Returns:    TRUE if ok, FALSE otherwise
03987     Purpose:    This converts ArtWorks path indexes to a Camelot path indexes, for this blender.
03988                 An AW index is just an index to the nth end point, so all that's done is the nth end
03989                 point in the path is found.
03990     SeeAlso:    -
03991 
03992 ********************************************************************************************/
03993 
03994 BOOL NodeBlender::ConvertAWPathIndexesToCamelot(INT32 *pPathIndexStart,INT32* pPathIndexEnd)
03995 {
03996     BOOL    ok = FindPathEndPoint(FindPrevious(),pPathIndexStart);
03997     if (ok) ok = FindPathEndPoint(FindNext(),    pPathIndexEnd);
03998     return ok;
03999 }
04000 
04001 /********************************************************************************************
04002 
04003 > BOOL NodeBlender::FindPathEndPoint(Node* pNodePath,INT32* pIndex)
04004 
04005     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04006     Created:    25/11/94
04007     Inputs:     pNodePath   = ptr to a NodePath
04008                 pIndex      = ptr to INT32 - must contain the number of the endpoint desired (N)
04009     Outputs:    *pIndex     = the index to the N'th endpoint.
04010     Returns:    TRUE if ok, FALSE otherwise
04011     Purpose:    This finds the N'th end point in the given NodePath.
04012                 This is used to convert AW end point indexes into Camelot end point indexes.
04013                 If *pIndex <=0 on entry, *pIndex = 0 on exit
04014     SeeAlso:    -
04015 
04016 ********************************************************************************************/
04017 
04018 BOOL NodeBlender::FindPathEndPoint(Node* pNodePath,INT32* pIndex)
04019 {
04020     ERROR3IF(pIndex == NULL,"pIndex == NULL");
04021     ERROR3IF(pNodePath == NULL,"pNodePath == NULL");
04022     if (pIndex == NULL || pNodePath == NULL)
04023         return FALSE;
04024 
04025 //  ERROR3IF(!pNodePath->IS_KIND_OF(NodePath),"pNodePath is not kind of NodePath");
04026     if (!pNodePath->IS_KIND_OF(NodePath))
04027         *pIndex = 0;
04028 
04029     // if *pIndex < 0 then this has a special meaning so preserve it and return TRUE
04030     // (e.g. -1 means find bottom left element) 
04031     if (*pIndex < 0)
04032         return TRUE;
04033 
04034     Path* pPath = &(((NodePath*)pNodePath)->InkPath);
04035 
04036     INT32 Index=0;
04037     for (INT32 n=*pIndex;n>0;n--)
04038     {
04039         if (!pPath->FindNextEndPoint(&Index))
04040             return FALSE;
04041     }
04042 
04043     *pIndex = Index;
04044 
04045     return TRUE;
04046 }
04047 
04048 
04049 //-------------------------------------------------------------------------------
04050 //-------------------------------------------------------------------------------
04051 //-------------------------------------------------------------------------------
04052 
04053 /***********************************************************************************************
04054 
04055 > BlendRef::BlendRef()
04056 
04057     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04058     Created:    12/10/94
04059     Inputs:     -
04060     Outputs:    -
04061     Returns:    -
04062     Purpose:    Default constructor.
04063 
04064 ***********************************************************************************************/
04065 
04066 BlendRef::BlendRef()
04067 {
04068     m_pNode             = NULL;
04069     m_NumBlendPaths     = 0;
04070     m_pBlendPathAttrMap = NULL;
04071 }
04072 
04073 /***********************************************************************************************
04074 
04075 > BlendRef::~BlendRef()
04076 
04077     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04078     Created:    12/10/94
04079     Inputs:     -
04080     Outputs:    -
04081     Returns:    -
04082     Purpose:    Default destructor.
04083 
04084 ***********************************************************************************************/
04085 
04086 BlendRef::~BlendRef()
04087 {
04088     m_BlendPathList.DeleteAll();
04089     DELPTR(m_pBlendPathAttrMap);
04090 }
04091 
04092 /***********************************************************************************************
04093 
04094 > BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix)
04095 
04096     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04097     Created:    12/10/94
04098     Inputs:     pThisNode   = ptr to the node in the tree this BlendRef refers to.
04099                 Index       = index into path of blob clicked on (-1 means ignore)
04100                 pUndoOp     = ptr to an undoable op so that initialisation can be done in an undoable way
04101                               (can be NULL)
04102                 pProgress   = ptr to progress object for maintaining hour glass
04103                 IgnoreEscape= TRUE if you don't want this process abortable via the Escape key
04104                 pMatrix     = transformation to apply to each blend path (can be NULL)
04105     Outputs:    -
04106     Returns:    TRUE if successfully initialises, FALSE otherwise
04107     Purpose:    This inits the BlendRef object with the given node.
04108 
04109 ***********************************************************************************************/
04110 /*
04111 BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix)
04112 {
04113     ERROR2IF(pThisNode == NULL,FALSE,"pThisNode == NULL");
04114     ERROR2IF(!pThisNode->CanBecomeA(CC_RUNTIME_CLASS(NodePath)),FALSE,"pThisNode can't become a NodePath!");
04115 
04116     // Reset the member vars of this blend reference
04117     m_pNode         = pThisNode;
04118     m_NumBlendPaths = 0;
04119     m_BlendPathList.DeleteAll();
04120 
04121     // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode.
04122     BlendBecomeA ParamBecomeA(  BECOMEA_PASSBACK, 
04123                                 CC_RUNTIME_CLASS(NodePath),
04124                                 pUndoOp,
04125                                 this,
04126                                 Index,
04127                                 pProgress,
04128                                 IgnoreEscape,
04129                                 pMatrix);
04130     
04131 
04132     // Get pNode to generate its paths for us
04133     BOOL ok = m_pNode->DoBecomeA(&ParamBecomeA);
04134 
04135     if (!ok)
04136     {
04137         // tidy up if things go wrong
04138         m_pNode           = NULL;
04139         m_NumBlendPaths = 0;
04140         m_BlendPathList.DeleteAll();
04141     }
04142 
04143     return (ok);
04144 }
04145 */
04146 
04147 /***********************************************************************************************
04148 
04149 > BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,
04150                             BOOL IgnoreEscape,Trans2DMatrix* pMatrix, AttrBrushType* pAttrBrush)
04151 
04152     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04153     Created:    12/10/94
04154     Inputs:     pThisNode   = ptr to the node in the tree this BlendRef refers to.
04155                 Index       = index into path of blob clicked on (-1 means ignore)
04156                 pUndoOp     = ptr to an undoable op so that initialisation can be done in an undoable way
04157                               (can be NULL)
04158                 pProgress   = ptr to progress object for maintaining hour glass
04159                 IgnoreEscape= TRUE if you don't want this process abortable via the Escape key
04160                 pMatrix     = transformation to apply to each blend path (can be NULL)
04161                 pAttrBrush  = the brush attribute applying to pThisNode, using this parameter indicates that
04162                               you wish to extract paths from the brush.
04163     Outputs:    -
04164     Returns:    TRUE if successfully initialises, FALSE otherwise
04165     Purpose:    This inits the BlendRef object with the given node.
04166 
04167 ***********************************************************************************************/
04168 
04169 BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,
04170                          Trans2DMatrix* pMatrix, AttrBrushType* pAttrBrush)
04171 {
04172     ERROR2IF(pThisNode == NULL,FALSE,"pThisNode == NULL");
04173 
04174     // Reset the member vars of this blend reference
04175     m_pNode         = pThisNode;
04176     m_NumBlendPaths = 0;
04177     m_BlendPathList.DeleteAll();
04178 
04179     // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode.
04180     BlendBecomeA ParamBecomeA(  BECOMEA_PASSBACK, 
04181                                 CC_RUNTIME_CLASS(NodePath),
04182                                 pUndoOp,
04183                                 this,
04184                                 Index,
04185                                 pProgress,
04186                                 IgnoreEscape,
04187                                 pMatrix);
04188     
04189     ERROR2IF(!pThisNode->CanBecomeA(&ParamBecomeA), FALSE, "pThisNode can't become a NodePath!");
04190 
04191     BOOL ok = FALSE;
04192 
04193     if (pAttrBrush != NULL)
04194     {
04195         // check to see if it is default, if it is not then let the attribute do the work 
04196         if (pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush)
04197             ok = pAttrBrush->DoBecomeA(&ParamBecomeA, pThisNode);
04198         else
04199             ok = m_pNode->DoBecomeA(&ParamBecomeA);
04200     }   
04201     else
04202         // Get pNode to generate its paths for us
04203         ok = m_pNode->DoBecomeA(&ParamBecomeA);
04204 
04205     if (!ok)
04206     {
04207         // tidy up if things go wrong
04208         m_pNode           = NULL;
04209         m_NumBlendPaths = 0;
04210         m_BlendPathList.DeleteAll();
04211     }
04212 
04213     return (ok);
04214 }
04215 
04216 
04217 /***********************************************************************************************
04218 
04219 > BOOL BlendRef::InitialiseForBrush(NodeRenderableInk* pThisNode)
04220 
04221     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04222     Created:    12/10/94
04223     Inputs:     pThisNode   = ptr to the node in the tree this BlendRef refers to.
04224             
04225     Outputs:    -
04226     Returns:    TRUE if successfully initialises, FALSE otherwise
04227     Purpose:    This inits the BlendRef object with the given node.
04228 
04229 ***********************************************************************************************/
04230 
04231 BOOL BlendRef::InitialiseForBrush(NodeRenderableInk* pThisNode)
04232 {
04233     ERROR2IF(pThisNode == NULL,FALSE,"pThisNode == NULL");
04234 
04235     // Reset the member vars of this blend reference
04236     m_pNode         = pThisNode;
04237     m_NumBlendPaths = 0;
04238     m_BlendPathList.DeleteAll();
04239 
04240     // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode.
04241     BrushBecomeA MyBecomeA(BECOMEA_PASSBACK, 
04242                                 CC_RUNTIME_CLASS(NodePath),
04243                                 NULL,
04244                                 this);
04245     
04246     ERROR2IF(!pThisNode->CanBecomeA(&MyBecomeA), FALSE, "pThisNode can't become a NodePath!");
04247 
04248     // if we've got a stroke attribute applied then we must make a special case
04249 /*  AttrStrokeType* pStroke = NULL;
04250     AttrVariableWidth* pVarWidth = NULL;
04251     pThisNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke);
04252     pThisNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth);
04253 
04254     BOOL ok = FALSE;
04255     if (pStroke && pVarWidth && pVarWidth->HasActiveValueFunction())
04256         ok = pStroke->DoBecomeA(&BecomeA, m_pNode);
04257     else
04258     */
04259     BOOL ok = m_pNode->DoBecomeA(&MyBecomeA);
04260 
04261     if (!ok)
04262     {
04263         // tidy up if things go wrong
04264         m_pNode           = NULL;
04265         m_NumBlendPaths = 0;
04266         m_BlendPathList.DeleteAll();
04267     }
04268 
04269     return (ok);
04270 
04271 }
04272 
04273 /***********************************************************************************************
04274 
04275 > BOOL BlendRef::AddBlendPath(BlendPath* pBlendPath)
04276 
04277     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04278     Created:    12/10/94
04279     Inputs:     pBlendPath = ptr to blend path to add
04280     Outputs:    -
04281     Returns:    TRUE if successfully initialises, FALSE otherwise
04282     Purpose:    Add the blend path object to this blend ref
04283                 NOTE: Once you add a BlendPath to a BlendRef, it becomes property of the BlendRef
04284                       which can alter/delete it at any time.
04285                       DO NOT KEEP A COPY OF THE PTR pBlendPath!!!! It may not be there next time
04286                       use it.
04287 
04288 ***********************************************************************************************/
04289 
04290 BOOL BlendRef::AddBlendPath(BlendPath* pBlendPath)
04291 {
04292     m_BlendPathList.AddTail(pBlendPath);
04293     m_NumBlendPaths++;
04294 
04295     return TRUE;
04296 }
04297 
04298 /***********************************************************************************************
04299 
04300 > BlendPath* BlendRef::GetFirstBlendPath()
04301 
04302     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04303     Created:    12/10/94
04304     Inputs:     -
04305     Outputs:    -
04306     Returns:    First blend path in the list, or NULL if the list is empty
04307     Purpose:    Lets you get the first blend path in the list
04308 
04309 ***********************************************************************************************/
04310 
04311 BlendPath* BlendRef::GetFirstBlendPath()
04312 {
04313     return ((BlendPath*)m_BlendPathList.GetHead());
04314 }
04315 
04316 /***********************************************************************************************
04317 
04318 > BlendPath* BlendRef::GetNextBlendPath(BlendPath* pCurrentBlendPath)
04319 
04320     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04321     Created:    12/10/94
04322     Inputs:     pCurrentBlendPath = ptr the current blend path in the list
04323     Outputs:    -
04324     Returns:    Returns the next blend path after pCurrentBlendPath, or NULL if there isn't a next one
04325     Purpose:    Lets you get the next blend path in the list
04326 
04327 ***********************************************************************************************/
04328 
04329 BlendPath* BlendRef::GetNextBlendPath(BlendPath* pCurrentBlendPath)
04330 {
04331     return ((BlendPath*)m_BlendPathList.GetNext(pCurrentBlendPath));
04332 }
04333 
04334 /***********************************************************************************************
04335 
04336 > BlendPath* BlendRef::GetBlendPath(INT32 Index)
04337 
04338     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04339     Created:    12/10/94
04340     Inputs:     Index = index of the list item (0 is the first item)
04341     Outputs:    -
04342     Returns:    Returns the blend path who's index is Index, or NULL if there isn't a next one
04343     Purpose:    Lets you get the nth blend path in the list
04344     SeeAlso:    GetNumBlendPaths();
04345 
04346 ***********************************************************************************************/
04347 
04348 BlendPath* BlendRef::GetBlendPath(INT32 Index)
04349 {
04350     if (m_NumBlendPaths <= 0)
04351         return NULL;
04352 
04353     if (Index >= INT32(m_NumBlendPaths))    { ERROR3("Index too big"); Index = m_NumBlendPaths-1; }
04354     if (Index < 0)                      { ERROR3("Index is -ve "); Index = 0; }
04355 
04356     BlendPath* pBlendPath = GetFirstBlendPath();
04357     while (pBlendPath != NULL && Index > 0)
04358     {
04359         pBlendPath = GetNextBlendPath(pBlendPath);
04360         Index--;
04361     }
04362     ERROR3IF(pBlendPath==NULL,"pBlendPath == NULL");
04363     return (pBlendPath);
04364 }
04365 
04366 
04367 /********************************************************************************************
04368 
04369 >   void BlendRef::Transform( TransformBase& Trans )
04370 
04371     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04372     Created:    12/10/94
04373     Inputs:     Trans - The transform Object
04374     Purpose:    Transforms all the BlendPaths attached to this blend reference
04375 
04376 ********************************************************************************************/
04377 
04378 void BlendRef::Transform( TransformBase& Trans )
04379 {
04380     BlendPath* pBlendPath = GetFirstBlendPath();
04381 
04382     while (pBlendPath != NULL)
04383     {
04384         pBlendPath->Transform(Trans);
04385         pBlendPath = GetNextBlendPath(pBlendPath);
04386     }   
04387 }
04388 
04389 /********************************************************************************************
04390 
04391 >   void BlendRef::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix)
04392 
04393     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04394     Created:    9/11/94
04395     Inputs:     pRender = region to render blobs into.
04396                 pMatrix = matrix to apply to the blob coords
04397     Outputs:    -
04398     Returns:    -
04399     Purpose:    Renders all the blend blobs associated with all the blend paths in this blend ref
04400 
04401 ********************************************************************************************/
04402 
04403 void BlendRef::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix)
04404 {
04405     BlendPath* pBlendPath = GetFirstBlendPath();
04406 
04407     while (pBlendPath != NULL)
04408     {
04409         pBlendPath->RenderBlendBlobs(pRender,pMatrix);
04410         pBlendPath = GetNextBlendPath(pBlendPath);
04411     }   
04412 }
04413 
04414 /********************************************************************************************
04415 
04416 > BOOL BlendRef::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,Trans2DMatrix* pMatrix);
04417 
04418     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04419     Created:    10/11/94
04420     Inputs:     pPointerPos  = point to check against
04421                 ppBlendPath = ptr to place to store blend path ptr if a match is found
04422                 pIndex      = ptr to place to store index if match is found
04423                 pMatrix     = matrix to apply to the path points before testing with pointer pos (can be NULL)
04424     Outputs:    *ppBlendPath & *pIndex, updated if TRUE returned, undefined otherwise
04425     Returns:    TRUE if match  found, FALSE otherwise
04426     Purpose:    This sees if the point given lies on a selected blob.
04427                 If a match is found, the ptr to the blend path and index to blob element is returned.
04428 
04429                 Markn 26-5-99:  Modified so that if the function returns TRUE, *pPointerPos is updated
04430                                 so that it contains the coords of the blob it is over
04431     SeeAlso:    -
04432 
04433 ********************************************************************************************/
04434 
04435 BOOL BlendRef::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,Trans2DMatrix* pMatrix)
04436 {
04437     if (pPointerPos == NULL) return FALSE;
04438     if (GetNumBlendPaths() != 1) return FALSE;
04439 
04440     BOOL Found = FALSE;
04441     BlendPath* pBlendPath = GetFirstBlendPath();
04442 
04443     if (pBlendPath != NULL)
04444     {
04445         Found = pBlendPath->IsPointOverBlob(pPointerPos,pIndex,pMatrix);
04446         if (Found) *ppBlendPath = pBlendPath;
04447     }
04448 
04449     return Found;
04450 }
04451 
04452 /********************************************************************************************
04453 
04454 > INT32 BlendRef::GetOrigMapping()
04455 
04456     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04457     Created:    11/11/94
04458     Inputs:     -
04459     Outputs:    -
04460     Returns:    The mapping that should be applied to the original path
04461     Purpose:    The path in question could well have been remapping (i.e. rotated to change the el
04462                 at index 0) many times.  This func returns the mapping (i.e. the index of the el that
04463                 should be at index 0) that should be appied to the original path in order to get the same result.
04464                 If the ref has more than one blend path, -1 is returned, meaning choose a default element as the
04465                 start node (e.g. left most, bottom most element)
04466     SeeAlso:    -
04467 
04468 ********************************************************************************************/
04469 
04470 INT32 BlendRef::GetOrigMapping()
04471 {
04472     INT32 Mapping = -1;
04473 
04474     if (GetNumBlendPaths() == 1)
04475     {       
04476         BlendPath* pBlendPath = GetFirstBlendPath();
04477 
04478         if (pBlendPath != NULL)
04479             Mapping = pBlendPath->GetOrigMapping();
04480     }
04481         
04482     return Mapping;
04483 }
04484 
04485 /********************************************************************************************
04486 
04487 >   void BlendRef::PreBlend()
04488 
04489     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04490     Created:    16/11/94
04491     Inputs:     -
04492     Outputs:    -
04493     Returns:    -
04494     Purpose:    Called just before a blender node is about to do its blend.
04495                 It asks all its blend paths to find their applied attributes.
04496 
04497 ********************************************************************************************/
04498 
04499 void BlendRef::PreBlend()
04500 {
04501     // If we haven't got a blend path attr map yet, make one (don't worry if we fail)
04502     if (m_pBlendPathAttrMap == NULL && m_NumBlendPaths > 0)
04503 PORTNOTE("other","Maps aren't hash bashed, so have no initial size")
04504         m_pBlendPathAttrMap = new CMapPtrToPtr(); // (m_NumBlendPaths);
04505 }
04506 
04507 /********************************************************************************************
04508 
04509 >   void BlendRef::PostBlend()
04510 
04511     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04512     Created:    16/11/94
04513     Inputs:     -
04514     Outputs:    -
04515     Returns:    -
04516     Purpose:    Called just after a blender node has done its blend.
04517                 It asks all its blend paths to free their applied attributes.
04518 
04519 ********************************************************************************************/
04520 
04521 void BlendRef::PostBlend()
04522 {
04523     // Free up any memory used to hold applied attribute tables in the blend paths
04524     BlendPath* pBlendPath = GetFirstBlendPath();
04525     while (pBlendPath != NULL)
04526     {
04527         pBlendPath->FreeAppliedAttributes();
04528         pBlendPath = GetNextBlendPath(pBlendPath);
04529     }   
04530 
04531     // Remove all the items in the map of blend path attr maps
04532     if (m_pBlendPathAttrMap != NULL)
04533         m_pBlendPathAttrMap->clear();
04534 }
04535 
04536 /********************************************************************************************
04537 
04538 >   CCAttrMap* BlendRef::FindAppliedAttributes(BlendPath* pBlendPath)
04539 
04540     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04541     Created:    16/11/94
04542     Inputs:     pBlendPath = the blend path that has the attrs you want
04543     Outputs:    -
04544     Returns:    -
04545     Purpose:    Use this to get the attrs of a blend path that belongs to this blend reference.
04546                 It keeps its own map keyed by the node which created the blend path, so that
04547                 FindAppliedAttributes() is only called once for each node that created blend paths,
04548                 rather than once for every blend path produced by the node (potentially many).
04549 
04550 ********************************************************************************************/
04551 
04552 CCAttrMap* BlendRef::FindAppliedAttributes(BlendPath* pBlendPath)
04553 {
04554     // If the attrs applied to this blend path are unique to the blend path (i.e. not the same
04555     // as the attrs applid to the node that created the blend path) return the unique attrs
04556     if (pBlendPath->UniqueAppliedAttrs())
04557         return (pBlendPath->FindAppliedAttributes());
04558 
04559     // If we haven't got a map of attr maps yet, try the blend path directly
04560     if (m_pBlendPathAttrMap == NULL)
04561         return (pBlendPath->FindAppliedAttributes());
04562 
04563     // pAttrMap will hold the attrs applied to pBlendPath
04564     CCAttrMap* pAttrMap = NULL;
04565 
04566     // the map of attr maps is keyed by the ptr to the node that created the blend path
04567     NodeRenderableInk* pCreatedByNode = pBlendPath->GetCreatedByNode();
04568 
04569     if (pCreatedByNode->IsCompound())
04570     {
04571         pCreatedByNode = ((NodeCompound *)pCreatedByNode)->GetNodeToBlend();
04572     }
04573 
04574     if (pCreatedByNode != NULL)
04575     {
04576         // Do we have an attr map for pCreatedByNode?
04577         CMapPtrToPtr::iterator iter = m_pBlendPathAttrMap->find( pCreatedByNode );
04578         if( m_pBlendPathAttrMap->end() != iter )
04579             pAttrMap = (CCAttrMap *)iter->second;
04580         else
04581         {
04582             // If not, get the blend path to return us an attr map
04583             // and put the ptr in our map of attr maps, keyed by pCreatedByNode.
04584             pAttrMap = pBlendPath->FindAppliedAttributes();
04585             if (pAttrMap != NULL)
04586                 (*m_pBlendPathAttrMap)[pCreatedByNode] = pAttrMap;
04587         }
04588     }
04589 
04590     return pAttrMap;
04591 }
04592 
04593 
04594 
04595 /********************************************************************************************
04596 
04597 >   DocRect BlendRef::GetBoundingRect()
04598 
04599     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
04600     Created:    24/11/99
04601     Inputs:     -
04602     Outputs:    -
04603     Returns:    the bounding rect of all the blendpaths in the blendref
04604     Purpose:
04605 
04606 ********************************************************************************************/
04607 
04608 DocRect BlendRef::GetBoundingRect()
04609 {
04610     DocRect Rect;
04611 
04612     BlendPath *pBlendPath = GetFirstBlendPath();
04613     while (pBlendPath != NULL)
04614     {
04615         DocRect PathRect = pBlendPath->m_pPath->GetBoundingRect();
04616         Rect = Rect.Union(PathRect);
04617 
04618         pBlendPath = GetNextBlendPath(pBlendPath);
04619     }
04620     return Rect;
04621 }
04622 
04623 
04624 /********************************************************************************************
04625 
04626 >   void BlendRef::StripRedundantNodeBlendPaths (BlendRef* spouse)
04627 
04628     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
04629     Created:    25/7/2000
04630     Inputs:     -
04631     Outputs:    -
04632     Returns:    -
04633     Purpose:    Strips redundant NodeBlendPaths from the blend refs.  This is needed in the
04634                 case that we are required to blend a blend on a both to a non-blend on a path
04635                 (and vice versa).
04636 
04637 ********************************************************************************************/
04638 
04639 void BlendRef::StripRedundantNodeBlendPaths (BlendRef* spouse)
04640 {
04641     INT32 numbBlendPathInited = 0, spouseNumbBlendPathInited = 0;
04642     
04643     BlendPath *pBlendPath = GetFirstBlendPath();
04644     while (pBlendPath != NULL)
04645     {
04646         if (pBlendPath->GetCreatedViaNodeBlendPath ())
04647         {
04648             numbBlendPathInited++;
04649         }
04650 
04651         pBlendPath = GetNextBlendPath(pBlendPath);
04652     }
04653 
04654     pBlendPath = spouse->GetFirstBlendPath();
04655     while (pBlendPath != NULL)
04656     {
04657         if (pBlendPath->GetCreatedViaNodeBlendPath ())
04658         {
04659             spouseNumbBlendPathInited++;
04660         }
04661 
04662         pBlendPath = spouse->GetNextBlendPath(pBlendPath);
04663     }
04664 
04665     // only strip in this case ....
04666     if (numbBlendPathInited != spouseNumbBlendPathInited)
04667     {
04668         if ((numbBlendPathInited == 0) && (spouseNumbBlendPathInited > 0))
04669         {
04670             // strip our spouse ....
04671 
04672             pBlendPath = spouse->GetFirstBlendPath();
04673             while (pBlendPath != NULL)
04674             {
04675                 if (pBlendPath->GetCreatedViaNodeBlendPath ())
04676                 {
04677                     BlendPath* remover = pBlendPath;
04678                     pBlendPath = spouse->GetNextBlendPath(pBlendPath);
04679                     delete (spouse->m_BlendPathList.RemoveItem (remover));
04680                     spouse->m_NumBlendPaths--;
04681                 }
04682                 else
04683                 {
04684                     pBlendPath = spouse->GetNextBlendPath(pBlendPath);
04685                 }
04686             }
04687         }
04688         else if ((spouseNumbBlendPathInited == 0) && (numbBlendPathInited > 0))
04689         {
04690             // strip ourselves ....
04691 
04692             pBlendPath = GetFirstBlendPath();
04693             while (pBlendPath != NULL)
04694             {
04695                 if (pBlendPath->GetCreatedViaNodeBlendPath ())
04696                 {
04697                     BlendPath* remover = pBlendPath;
04698                     pBlendPath = GetNextBlendPath(pBlendPath);
04699                     delete (m_BlendPathList.RemoveItem (remover));
04700                     m_NumBlendPaths--;
04701                 }
04702                 else
04703                 {
04704                     pBlendPath = GetNextBlendPath(pBlendPath);
04705                 }
04706             }
04707         }
04708         // else - oh well
04709     }
04710 }
04711 
04712 
04713 
04714 /********************************************************************************************
04715 
04716 >   BOOL BlendRef::RemoveLastBlendPath()
04717 
04718     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
04719     Created:    24/11/99
04720     Inputs:     -
04721     Outputs:    -
04722     Returns:    TRUE if successful, FALSE if not
04723     Purpose:    Not a function that most people will have cause to use, removes the tail item in 
04724                 the blendpath list.
04725 
04726 ********************************************************************************************/
04727 
04728 BOOL BlendRef::RemoveLastBlendPath()
04729 {
04730     if (m_BlendPathList.IsEmpty())
04731         return FALSE;
04732 
04733     m_BlendPathList.RemoveTail();
04734     m_NumBlendPaths--;
04735     
04736     return TRUE;
04737 }
04738 
04739 
04740 //--------------------------------------------------------------------------------------
04741 //--------------------------------------------------------------------------------------
04742 //--------------------------------------------------------------------------------------
04743 
04744 /***********************************************************************************************
04745 
04746 > BlendPath::BlendPath()
04747 
04748     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04749     Created:    12/10/94
04750     Inputs:     -
04751     Outputs:    -
04752     Returns:    -
04753     Purpose:    Default constructor
04754 
04755 ***********************************************************************************************/
04756 
04757 BlendPath::BlendPath()
04758 {
04759     m_pBlendNode    = NULL;
04760     m_pPath         = NULL;
04761     m_pCreatedByNode= NULL;
04762     m_pAppliedAttrs = NULL;
04763 
04764     m_CloseFigure   = 0;
04765     m_OrigMapping   = 0;
04766     m_PathType      = PATHTYPE_NONE;
04767     m_SubPathID     = 0;
04768     m_DirChanged    = FALSE;
04769     m_NumElements   = -1;
04770     m_UniqueAttrs   = FALSE;
04771 
04772     m_bDeleteAll    = FALSE;
04773     m_bFreeAttrMapOnDestruct = FALSE;
04774 
04775     m_AppliedLineWidth = -1;
04776 
04777     m_bCreatedViaNodeBlendPath = FALSE;
04778 
04779     m_pCopyPath = NULL;
04780     m_pCopyAttrs = NULL;
04781 }
04782 
04783 /***********************************************************************************************
04784 
04785 > BlendPath::~BlendPath()
04786 
04787     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04788     Created:    12/10/94
04789     Inputs:     -
04790     Outputs:    -
04791     Returns:    -
04792     Purpose:    Default destructor
04793 
04794     Notes:      This destructor has actually gotten quite complicated as it Blendpaths are an important
04795                 part of the brush attribute and there are a number of different cases to deal with:
04796                 - The normal case is where the blendpath is part of a blend, in this case it may wish to 
04797                 delete its applied attributes, but more than often will not, as they are still used in the tree.
04798 
04799                 The brush has two deletion scenarios:
04800                 - normally the m_bFreeAttributes flag is set, as we wish to ensure the attribute map is 
04801                 deleted but we do not wish to delete the attributes (as they are probably shared)
04802                 - if this blendpath has been created specially as part of a blend between two brush 
04803                 attributes, then the m_bDeleteAll flag will be set, which asks that we delete everything
04804 
04805 ***********************************************************************************************/
04806 
04807 BlendPath::~BlendPath()
04808 {
04809     // Delete our copies
04810     DeleteCopyPathAndAttributes();
04811     
04812     // Only delete the applied attr map if this is the first sub path that was generated from the
04813     // source path supplied when making shapes.
04814     // This assumes that all sub-paths within a given path have the same attributes (far assumption)
04815 
04816     if (m_SubPathNum == 0 || m_bDeleteAll)
04817     {
04818         if ((m_UniqueAttrs || m_bDeleteAll) && m_pAppliedAttrs != NULL)
04819         {
04820             CCRuntimeClass *pType;
04821             void       *pVal;
04822             // delete all attrs
04823             CMapPtrToPtr::iterator Pos = m_pAppliedAttrs->GetStartPosition();
04824             CMapPtrToPtr::iterator End = m_pAppliedAttrs->GetEndPosition();
04825             for(; Pos != End;)
04826             {
04827                 // Get attr at CMapPtrToPtr::iterator Pos
04828                 m_pAppliedAttrs->GetNextAssoc( Pos, pType, pVal );
04829                 if (pVal != NULL)
04830                     delete (NodeAttribute*)pVal;
04831             }
04832         }
04833         if (m_pAppliedAttrs != NULL)
04834         {
04835             delete m_pAppliedAttrs;
04836             m_pAppliedAttrs = NULL;
04837         }
04838     }
04839     // m_bFreeAttributes should only really be set by brushing code, so unless you are a 
04840     // brush you should never enter the following section
04841     if (m_bFreeAttrMapOnDestruct)
04842     {
04843         if (m_pAppliedAttrs != NULL)
04844         {
04845             delete m_pAppliedAttrs;
04846             m_pAppliedAttrs = NULL;
04847         }
04848     }
04849 
04850     if (m_pBlendNode)
04851     {
04852         m_pBlendNode->CascadeDelete();
04853         delete m_pBlendNode;
04854     }
04855     
04856     if (m_bDeleteAll)
04857     {
04858         if (m_pPath != NULL)
04859             delete m_pPath;
04860     }
04861     
04862 }
04863 
04864 /***********************************************************************************************
04865 
04866 > BOOL BlendPath::Initialise(   NodeRenderableInk* pThisBlendNode,
04867                                 INT32 Index,
04868                                 NodeRenderableInk* pThisCreatedByNode,
04869                                 UINT32 ThisSubPathID,
04870                                 CCAttrMap* pAttrMap)
04871 
04872     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04873     Created:    12/10/94
04874     Inputs:     pThisBlendNode      = ptr to the node that this BlendPath will use (should NOT be linked in the tree)
04875                 Index               = index to path element the path should start on (-1 means bottom left most el)
04876                 pThisCreatedByNode  = ptr to the node that created pThisNodePath
04877                 ThisSubPathID       = ID for this subpath (all blend paths generated from the same path have the
04878                                       same subpath ID). Must be >=1
04879                 ThisSubPathNum      = The sub path number specifying which sub path of the original path this
04880                                       represents.  The first sub path is numbered 0.
04881                 pAttrMap            = ptr to applied attrs (or NULL if to use attrs applied to pThisCreatedByNode)
04882     Outputs:    -
04883     Returns:    TRUE if Successful, FALSE otherwise
04884     Purpose:    Inits the BlendPath object to use the given NodePath.
04885                 NOTE: pThisNodePath should point to isolated instance, and not to a used NodePath linked into
04886                       the tree! 
04887                 NOTE: Once you give a BlendPath a NodePath, this becomes property of the BlendPath
04888                       and can be altered and/or deleted by the BlendPath at any time.
04889                       DO NOT KEEP A COPY OF THE PTR pThisNodePath!!! If you want a copy of the
04890                       NodePath at pThisNodePath, make a new copy of the node.
04891 
04892 ***********************************************************************************************/
04893 
04894 BOOL BlendPath::Initialise( NodeRenderableInk* pThisBlendNode,
04895                             INT32 Index,
04896                             NodeRenderableInk* pThisCreatedByNode,
04897                             UINT32 ThisSubPathID,
04898                             UINT32 ThisSubPathNum,
04899                             CCAttrMap* pAttrMap, 
04900                             BOOL CheckDirection)
04901 {
04902     if (pThisBlendNode == NULL || pThisCreatedByNode == NULL) return FALSE;
04903 
04904     if (pThisBlendNode->IsNodePath())
04905         ERROR3IF(ThisSubPathID == 0,"Invalid subpath ID");
04906 
04907     DELPTR(m_pBlendNode);
04908 
04909     m_pBlendNode    = pThisBlendNode;
04910 
04911     if (m_pBlendNode->IsNodePath())
04912     {
04913         m_pPath         = &(((NodePath *)m_pBlendNode)->InkPath);
04914     
04915     }
04916     else
04917     {
04918         // get the node to blend out of this path node & use this
04919         if (pThisBlendNode->IsCompound())
04920         {
04921             NodeRenderableInk * pInk = ((NodeCompound *)pThisBlendNode)->GetNodeToBlend();
04922 
04923             if (pInk)
04924             {
04925                 if (pInk->IsNodePath())
04926                 {
04927                     m_pPath         = &(((NodePath *)pInk)->InkPath);
04928                 }
04929                 else
04930                 {
04931                     ERROR3("Node to blend isn't a node path");
04932                 }
04933             }
04934             else
04935             {
04936                 ERROR3("No node to blend");
04937             }
04938         }
04939     }
04940 
04941     m_pCreatedByNode= pThisCreatedByNode;
04942     m_SubPathID     = ThisSubPathID;
04943     m_SubPathNum    = ThisSubPathNum;
04944     m_pAppliedAttrs = pAttrMap;
04945     m_UniqueAttrs   = (pAttrMap != NULL);
04946 
04947     if (m_pPath)
04948     {
04949         m_NumElements   = m_pPath->GetNumElements();
04950         m_PathType      = m_pPath->GetPathType();
04951     }
04952     else
04953     {
04954         m_NumElements = 0;
04955         m_PathType = PATHTYPE_SHAPE;
04956     }
04957 
04958     // If we don't have an start element index (i.e. == -1) and its a shape (not a line)
04959     // then pick the bottom left element
04960     if (m_pPath && CheckDirection)
04961     {
04962         
04963         if (Index < 0 && m_PathType == PATHTYPE_SHAPE) Index = FindBottomLeft();
04964         
04965         ProcessVerbs();
04966         MapPath(Index);
04967         CorrectDirection(Index);
04968             
04969     }
04970         
04971     // record the initial CMapPtrToPtr::iterator (used for loading and saving brushes so that we don't get
04972     // the control coordinates of fill attributes wrong)
04973     DocRect BRect;
04974 
04975     if (m_pPath)
04976     {
04977         BRect = m_pPath->GetBoundingRect();
04978     }
04979     else
04980     {
04981         BRect = m_pBlendNode->GetBoundingRect(FALSE, FALSE);
04982     }
04983 
04984     m_BrushStartCoord = BRect.Centre();
04985 
04986     return TRUE;
04987 }
04988 
04989 /***********************************************************************************************
04990 
04991 > BOOL BlendPath::Initialise( BlendPath *pBlendPath, 
04992                             NodeRenderableInk * pNodeToBlend)
04993 
04994     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04995     Created:    6/3/2000
04996     Inputs:     pBlendPath - the blend path to copy from
04997     Outputs:    -
04998     Returns:    TRUE if Successful, FALSE otherwise
04999     Purpose:    Inits this blend path, replacing the blend node with the one given
05000 
05001 ***********************************************************************************************/
05002 
05003 BOOL BlendPath::Initialise( BlendPath *pBlendPath, 
05004                             NodeRenderableInk * pNodeToBlend)
05005 {
05006     m_pPath             = pBlendPath->GetPath();
05007 
05008     pNodeToBlend->NodeCopy((Node **)(&m_pBlendNode));
05009     m_pCreatedByNode    = pBlendPath->GetCreatedByNode();
05010     m_OrigMapping       = pBlendPath->GetOrigMapping();
05011     m_PathType          = pBlendPath->GetPathType();
05012     m_SubPathID         = pBlendPath->GetSubPathID();
05013     m_SubPathNum        = pBlendPath->GetSubPathNum();
05014     m_NumElements       = pBlendPath->GetNumElements();
05015     m_UniqueAttrs       = pBlendPath->UniqueAppliedAttrs();
05016     m_DirChanged        = pBlendPath->m_DirChanged;
05017     m_BrushStartCoord   = pBlendPath->m_BrushStartCoord;
05018     m_BrushCurrentCoord = pBlendPath->m_BrushCurrentCoord;
05019 
05020     if (m_pAppliedAttrs)
05021         m_pAppliedAttrs     = pBlendPath->m_pAppliedAttrs->Copy();
05022     else
05023         m_pAppliedAttrs     = NULL;
05024 
05025     return TRUE;
05026 }
05027 
05028 /********************************************************************************************
05029 
05030 >   void BlendPath::CorrectDirection(INT32 Index)
05031 
05032     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05033     Created:    9/11/94
05034     Inputs:     Index = index of the node the blend was attempted from
05035     Outputs:    pPath potentially reversed so it is rendered in the correct direction
05036     Returns:    -
05037     Purpose:    This ensures that the path is going in the correct direction.
05038                 It doesn't matter which direction, as long as the direction is the same for all blend paths.
05039                 
05040 ********************************************************************************************/
05041 
05042 void BlendPath::CorrectDirection(INT32 Index)
05043 {
05044     switch (m_PathType)
05045     {
05046         case PATHTYPE_SHAPE:
05047             {
05048                 // Dummy vars for the GWinding() call
05049                 UINT32 l; POINT p;
05050 
05051                 // Get the direction of the path
05052                 BOOL Direction = GWinding((PPOINT)m_pPath->GetCoordArray(),m_pPath->GetVerbArray(),m_pPath->GetNumCoords(),FLATNESS,l,p);
05053 
05054                 // If clockwise, reverse it so it's anticlockwise (this is ArtWorks compatible!!)
05055                 if (Direction)
05056                     m_pPath->Reverse();
05057 
05058                 m_DirChanged = Direction;
05059             }
05060             break;
05061 
05062         case PATHTYPE_LINE:
05063             {
05064                 // Get num coords
05065                 INT32 NumCoords = m_pPath->GetNumCoords();
05066 
05067                 if (Index != 0 && Index != (NumCoords-1))
05068                 {
05069                     // Get the coords array of the line
05070                     DocCoord* pCoords = m_pPath->GetCoordArray();
05071 
05072                     // Find the coords of the two end points of the line
05073                     DocCoord Coord1 = pCoords[0];
05074                     DocCoord Coord2 = pCoords[NumCoords-1];
05075 
05076                     // if the start of the line is less bottom-left than the end, reverse the line.
05077                     if ((Coord1.x + Coord1.y) > (Coord2.x + Coord2.y))
05078                     {
05079                         m_pPath->Reverse();
05080                         m_OrigMapping = NumCoords-1; // Pretend that user mapped to end of line
05081                     }
05082                 }
05083             }
05084             break;
05085 
05086         default : ERROR3("Unknown path type"); break;
05087     }
05088 }
05089 
05090 /********************************************************************************************
05091 
05092 >   INT32 BlendPath::MapPath(INT32 Index)
05093 
05094     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05095     Created:    8/11/94
05096     Inputs:     Index = index of element you want the path to start from
05097     Outputs:    pPath potentially rotated so it's first el is the one that was indexed by Index
05098                 OrigMapping will be the mapping that should be applied to the original path
05099                 to get the same effect.
05100     Returns:    The mapping to apply to invert this mapping
05101     Purpose:    This will ensure that the path's first element is the element at Index.
05102                 It does this by rotating the path until element at index 'Index' is at index 0.
05103                 The value returned can be used to undo the mapping, i.e. to apply an inverse mapping
05104                 
05105 ********************************************************************************************/
05106 
05107 INT32 BlendPath::MapPath(INT32 Index)
05108 {
05109     INT32 NumCoords = m_pPath->GetNumCoords();
05110 
05111     INT32 InverseMapping = 0;
05112 
05113     // Make sure Index is in range
05114     if (Index > 0 && Index < NumCoords)
05115     {
05116         // Check that we're trying to remap an end point
05117         PathFlags* pFlags  = m_pPath->GetFlagArray();
05118         PathVerb*  pVerbs  = m_pPath->GetVerbArray();
05119         DocCoord*  pCoords = m_pPath->GetCoordArray();
05120 
05121         ERROR3IF(!pFlags[Index].IsEndPoint,"Index not on end point");
05122         if (!pFlags[Index].IsEndPoint)
05123         {
05124             // If index is not on an end point, choose the index of the end point that
05125             // appears before this index.
05126             INT32 OldIndex = Index;
05127             INT32 PrevIndex = 0;
05128             while (PrevIndex < OldIndex)
05129             {
05130                 Index = PrevIndex;
05131                 if (!m_pPath->FindNextEndPoint(&PrevIndex))
05132                 {
05133                     Index = 0;
05134                     break;
05135                 }
05136             }
05137         }
05138 
05139         switch (m_PathType)
05140         {
05141             case PATHTYPE_SHAPE:
05142 
05143                 if (m_DirChanged)
05144                 {
05145                     DocCoord Pos = pCoords[Index];
05146                     m_pPath->Reverse();
05147                     m_pPath->FindNearestPoint(Pos,POINTFLAG_ENDPOINTS,&Index);
05148                 }
05149 
05150                 // Change the path so the el at index 'Index' is now at index 0
05151                 m_pPath->ChangeStartElement(Index);
05152 
05153                 // The above call will introduce a PT_CLOSEFIGURE flag into the end verb, so
05154                 // we need to clear this flag from the last verb
05155                 pVerbs[NumCoords-1] &= ~PT_CLOSEFIGURE;
05156 
05157                 // Work out a mapping that can be applied to the original path in order to get the same effect
05158                 // This is used when the blender reinitialises after its been deinitialised
05159                 m_OrigMapping += Index;
05160 
05161                 if (m_OrigMapping >= (NumCoords-1))
05162                     m_OrigMapping = (m_OrigMapping+1) % NumCoords;
05163 
05164                 InverseMapping = NumCoords-Index-1;
05165 
05166                 if (m_DirChanged) 
05167                 {
05168                     DocCoord Pos = pCoords[InverseMapping];
05169                     m_pPath->Reverse();
05170                     m_pPath->FindNearestPoint(Pos,POINTFLAG_ENDPOINTS,&InverseMapping);
05171                 }
05172                 break;
05173 
05174             case PATHTYPE_LINE:
05175                 // If it's a line then we will only try and remap if the index is the end element
05176                 // In this case, this just means reversing the path
05177                 if (Index == (NumCoords-1))
05178                 {
05179                     // Make the end of the line the start of the line
05180                     m_pPath->Reverse();
05181 
05182                     // The mapping to apply to the orig path to get the same effect if the opposite end point index
05183                     if (m_OrigMapping == 0)
05184                         m_OrigMapping = NumCoords-1;
05185                     else
05186                         m_OrigMapping = 0;
05187 
05188                     InverseMapping = NumCoords-1;
05189                 }
05190                 break;
05191 
05192             default : ERROR3("Unknown path type"); break;
05193         }
05194     }
05195 
05196     return InverseMapping;
05197 }
05198 
05199 
05200 /********************************************************************************************
05201 
05202 >   DocCoord BlendPath::MapPath(DocCoord Pos)
05203 
05204     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05205     Created:    23/2/95
05206     Inputs:     Pos = coord of element you want the path to start from
05207     Outputs:    pPath potentially rotated so it's first el is the one that was closest to 'Pos'
05208                 OrigMapping will be the mapping that should be applied to the original path
05209                 to get the same effect.
05210     Returns:    The mapping to apply to invert this mapping
05211     Purpose:    This will ensure that the path's first element is the element closest to Pos.
05212                 It does this by rotating the path until element at Pos is at index 0.
05213                 The value returned can be used to undo the mapping, i.e. to apply an inverse mapping
05214                 
05215 ********************************************************************************************/
05216 
05217 DocCoord BlendPath::MapPath(DocCoord Pos)
05218 {
05219     DocCoord InversePos(0,0); 
05220 
05221     ERROR2IF(m_pPath == NULL,InversePos,"m_pPath == NULL");
05222     DocCoord* pCoords = m_pPath->GetCoordArray();
05223     ERROR2IF(pCoords == NULL,InversePos,"pCoords == NULL");
05224 
05225     // The inverse of this op is to remap the element currently at index 0
05226     InversePos = pCoords[0];
05227 
05228     INT32 Index = 0;
05229     INT32 InvIndex = 0;
05230 
05231 /*  {
05232         StringBase Str;
05233         if (Str.Alloc(50000))
05234         {
05235             Str = "";
05236             pNodePath->GetDebugDetails(&Str);
05237             TRACE((TCHAR*)Str);
05238         }
05239     }
05240 */
05241     
05242     if (m_pPath->FindNearestPoint(Pos,POINTFLAG_ENDPOINTS,&Index))
05243         InvIndex = MapPath(Index);
05244     
05245     InversePos = pCoords[InvIndex];
05246 
05247 /*  {
05248         StringBase Str;
05249         if (Str.Alloc(50000))
05250         {
05251             Str = "";
05252             pNodePath->GetDebugDetails(&Str);
05253             TRACE((TCHAR*)Str);
05254         }
05255     }
05256   */
05257     return InversePos;
05258 }
05259 
05260 
05261 /********************************************************************************************
05262 
05263 >   INT32 BlendPath::FindBottomLeft()
05264 
05265     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05266     Created:    8/11/94
05267     Inputs:     -
05268     Outputs:    -
05269     Returns:    The index of the bottom left most element (or -1 if something when wrong)
05270     Purpose:    This scans the path for the bottom left most element, and returns the index to it
05271                 
05272 ********************************************************************************************/
05273 
05274 INT32 BlendPath::FindBottomLeft()
05275 {
05276     DocCoord* pCoords = m_pPath->GetCoordArray();
05277     if (pCoords == NULL) return -1;
05278 
05279     INT32 BottomLeftIndex = -1;
05280     INT32 Index;
05281     BOOL ok;
05282 
05283     DocCoord VeryMostBottomLeft(0x7fffffff,0x7fffffff);
05284     double   SmallestDist = 1.0e100,ThisDist;
05285 
05286     for (Index=0,ok=TRUE;ok;)
05287     {
05288         if (pCoords[Index].x < VeryMostBottomLeft.x) VeryMostBottomLeft.x = pCoords[Index].x;
05289         if (pCoords[Index].y < VeryMostBottomLeft.y) VeryMostBottomLeft.y = pCoords[Index].y;
05290         ok = m_pPath->FindNextEndPoint(&Index);
05291     }
05292 
05293     for (Index=0,ok=TRUE;ok;)
05294     {
05295         ThisDist = VeryMostBottomLeft.Distance(pCoords[Index]);
05296         if (ThisDist < SmallestDist)
05297         {
05298             BottomLeftIndex = Index;
05299             SmallestDist = ThisDist;
05300         }
05301         ok = m_pPath->FindNextEndPoint(&Index);
05302     }
05303 
05304 //  return BottomLeftIndex;
05305 
05306     UINT32 u; POINT p;
05307 
05308     // Get the direction of the path
05309     GWinding((PPOINT)m_pPath->GetCoordArray(),m_pPath->GetVerbArray(),m_pPath->GetNumCoords(),FLATNESS,u,p);
05310 
05311 //  TRACEUSER( "Markn", _T("\n"));
05312 
05313     INT32 GIndex=-1;
05314     DocCoord Coord(p.x,p.y);
05315     SmallestDist = 1.0e100;
05316     for (Index=0,ok=TRUE;ok;)
05317     {
05318         ThisDist = Coord.Distance(pCoords[Index]);
05319         if ((ThisDist-SmallestDist) < 5.0)
05320         {
05321             // This point is close enough, so remember it's index
05322             GIndex = Index;
05323 
05324             // Only update the smallest dist if it is indeed the smallest so far
05325             if (ThisDist < SmallestDist)
05326                 SmallestDist = ThisDist;
05327         }
05328         //TRACEUSER( "Markn", _T("(%d) %f - "),Index,ThisDist);
05329         ok = m_pPath->FindNextEndPoint(&Index);
05330     }
05331 
05332 //  TRACEUSER( "Markn", _T("\n"));
05333 //  TRACEUSER( "Markn", _T("GIndex          = %ld\n"),GIndex);
05334 //  TRACEUSER( "Markn", _T("BottomLeftIndex = %ld\n"),BottomLeftIndex);
05335 //  if (GIndex != BottomLeftIndex)
05336 //  {
05337 //      TRACEUSER( "Markn", _T("*^*^*^*^*^*^*^*^*^*^*^*^*^*^"));
05338 //  }
05339 
05340 //  ERROR3IF_PF(GIndex != BottomLeftIndex,("GIndex != BottomLeftIndex: GIndex = %ld, BottomLeftIndex = %ld",GIndex,BottomLeftIndex));
05341 
05342     return GIndex;
05343 //  return BottomLeftIndex;
05344 }
05345 
05346 /********************************************************************************************
05347 
05348 >   void BlendPath::ProcessVerbs()
05349 
05350     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05351     Created:    8/11/94
05352     Inputs:     -
05353     Outputs:    Contents of pPath's verb array potentially changed
05354                 CloseFigure = PT_CLOSEFIGURE if one or more PT_CLOSEFIGURE flags are found in the verbs
05355                               0 otherwise
05356     Returns:    -
05357     Purpose:    This processes the verbs so the GBlend can cope with them.
05358                 It clears any PT_CLOSEFIGURE flags it finds.
05359                 If it finds one or more PT_CLOSEFIGURE, CloseFigure = PT_CLOSEFIGURE
05360                 
05361 ********************************************************************************************/
05362 
05363 void BlendPath::ProcessVerbs()
05364 {
05365     PathVerb* pPathVerb = m_pPath->GetVerbArray();
05366     INT32 n = m_pPath->GetNumCoords()-1;
05367 
05368     m_CloseFigure = 0;
05369 
05370     for (;n>=0;n--)
05371     {
05372         // Get the next verb
05373         PathVerb Verb = pPathVerb[n];
05374 
05375         // Have we found a close figure flag yet?
05376         if (m_CloseFigure == 0) m_CloseFigure = Verb & PT_CLOSEFIGURE;
05377 
05378         // Remove any wierd flags from the verb, as GBlend doesn't like them.
05379         pPathVerb[n] = Verb & ~PT_CLOSEFIGURE;
05380     }
05381 }
05382     
05383 
05384 
05385 /********************************************************************************************
05386 
05387 >   void BlendPath::Transform( TransformBase& Trans )
05388 
05389     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05390     Created:    12/10/94
05391     Inputs:     Trans - The transform Object
05392     Purpose:    Transforms the NodePath attached to this blend path, plus any applied attrs it may have
05393 
05394 ********************************************************************************************/
05395 
05396 void BlendPath::Transform( TransformBase& Trans )
05397 {
05398     if (m_pBlendNode != NULL) m_pBlendNode->Transform(Trans);
05399 
05400     if (m_UniqueAttrs && m_pAppliedAttrs != NULL)
05401     {
05402         CCRuntimeClass *pType;
05403         void           *pVal;
05404 
05405         // iterating all (key, value) pairs
05406         for( CMapPtrToPtr::iterator Pos = m_pAppliedAttrs->GetStartPosition(); 
05407             Pos != m_pAppliedAttrs->GetEndPosition();)
05408         {
05409             // Get attr at CMapPtrToPtr::iterator Pos
05410             m_pAppliedAttrs->GetNextAssoc(Pos,pType,pVal);
05411 
05412             // pVal is actually an attribute, so get a ptr to it and render it
05413             NodeAttribute* pNodeAttr = (NodeAttribute *)pVal;
05414 
05415             pNodeAttr->Transform(Trans);
05416         }
05417     }
05418 
05419 }
05420 
05421 
05422 
05423 /********************************************************************************************
05424 
05425 >   void BlendPath::Transform( Matrix* pTransMatrix,BOOL TransformLines, BOOL Tile)
05426 
05427     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
05428     Created:    12/1/2000
05429     Inputs:     TransMatrix - the matrix describing the transformation
05430                 TransformLineWidth - do we transform the line width
05431                 Tile - flag to say whether to tile or not. If set this means that attributes such as 
05432                 bitmap fills etc. are only rendered once
05433     Purpose:    Transforms the NodePath attached to this blend path, plus any applied attrs it may have
05434                 Plus it also records transforms in the member variable, so that later they may be undone
05435                 This is specifically required by brush attributes.
05436 
05437 ********************************************************************************************/
05438 
05439 void BlendPath::Transform( Matrix* pTransMatrix, BOOL TransformLines,   BOOL Tile )
05440 {
05441     Trans2DMatrix Trans(*pTransMatrix); 
05442 
05443     if (m_pBlendNode != NULL) 
05444         m_pBlendNode->Transform(Trans);
05445     else
05446     {
05447         // added this for the case where we create the blendpath manually, i.e. when blending
05448         // brush attributes
05449         DocCoord* pCoords = m_pPath->GetCoordArray();
05450         INT32 NumCoords = m_pPath->GetNumCoords();
05451         if (pCoords != NULL && NumCoords > 0)
05452             Trans.Transform(pCoords, NumCoords);
05453         else
05454             ERROR3("No coordinates in BlendPath::Transform");
05455     }
05456 
05457     CCAttrMap* pAttrMap = FindAppliedAttributes();
05458     if (pAttrMap != NULL)
05459     {
05460         if (Tile)
05461             pAttrMap->TransformForBrush(Trans);
05462         else
05463             pAttrMap->Transform(Trans);
05464     }
05465     else
05466     {
05467         ERROR3("No applied attributes");
05468     }
05469 
05470     
05471     return;
05472 }
05473 
05474 
05475 /********************************************************************************************
05476 
05477 >   BOOL BlendPath::TransformBrushAttributesBeforeSave()
05478 
05479     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
05480     Created:    12/1/2000
05481     Inputs:     -
05482     Returns:    TRUE if successful, FALSE otherwise
05483     Purpose:    Undoes the transformations performed on the attributes of this blendpath.  
05484                 This function must be called prior to saving a brush attribute. If you wish
05485                 to keep using the attribute after the save call TransformBrushAttributesAfterSave
05486                 or else your attributes will be all over the place.
05487 ********************************************************************************************/
05488 
05489 BOOL BlendPath::TransformBrushAttributesBeforeSave()
05490 {
05491     if (m_pPath == NULL)
05492     {
05493         ERROR2(FALSE, "This blendpath has no path");
05494         return FALSE;
05495     }
05496     DocRect BRect = m_pPath->GetBoundingRect();
05497     m_BrushCurrentCoord = BRect.Centre();
05498 
05499     Trans2DMatrix Trans(m_BrushStartCoord.x - m_BrushCurrentCoord.x, 
05500                         m_BrushStartCoord.y - m_BrushCurrentCoord.y);
05501 
05502     CCAttrMap* pAttrMap = FindAppliedAttributes();
05503 
05504     if (pAttrMap == NULL)
05505     {
05506         ERROR3("No applied attributes");
05507         return FALSE;
05508     }
05509     
05510     // quick pre-release bodge fix - fractal fills seem to be getting wrongly transformed
05511     // when we do this, so exclude them
05512     AttrFractalFill* pFrac = NULL;
05513     if( pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrFractalFill), (void*&)pFrac ) )
05514         pAttrMap->RemoveKey( CC_RUNTIME_CLASS(AttrFractalFill) );
05515     
05516     pAttrMap->Transform(Trans);
05517 
05518     return TRUE;
05519 }
05520 
05521 
05522 /********************************************************************************************
05523 
05524 >   void BlendPath::TransformBrushAttributesAfterSave()
05525 
05526     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
05527     Created:    12/1/2000
05528     Inputs:     -
05529     Returns:    TRUE if successful , FALSE otherwise
05530     Purpose:    Undoes the transformations performed on the attributes by the previous function.                                
05531 ********************************************************************************************/
05532 
05533 BOOL BlendPath::TransformBrushAttributesAfterSave()
05534 {
05535     Trans2DMatrix Trans(m_BrushCurrentCoord.x - m_BrushStartCoord.x, 
05536                         m_BrushCurrentCoord.y - m_BrushStartCoord.y);
05537 
05538     CCAttrMap* pAttrMap = FindAppliedAttributes();
05539 
05540     if (pAttrMap == NULL)
05541     {
05542         ERROR3("No applied attributes");
05543         return FALSE;
05544     }
05545 
05546     pAttrMap->TransformForBrush(Trans);
05547 
05548     return TRUE;
05549 }
05550 
05551 /********************************************************************************************
05552 
05553 >   BOOL BlendPath::IsFilled()
05554 
05555     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05556     Created:    8/11/94
05557     Inputs:     -
05558     Outputs:    -
05559     Returns:    TRUE if the associated path is filled
05560     Purpose:    Find out if the path is filled
05561 
05562 ********************************************************************************************/
05563 
05564 BOOL BlendPath::IsFilled()
05565 {
05566     if (m_pPath != NULL)
05567         return (m_pPath->IsFilled);
05568     else
05569         return FALSE;
05570 }
05571 
05572 /********************************************************************************************
05573 
05574 >   BOOL BlendPath::IsStroked()
05575 
05576     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05577     Created:    8/11/94
05578     Inputs:     -
05579     Outputs:    -
05580     Returns:    TRUE if the associated path is Stroked
05581     Purpose:    Find out if the path is Stroked
05582 
05583 ********************************************************************************************/
05584 
05585 BOOL BlendPath::IsStroked()
05586 {
05587     if (m_pPath != NULL)
05588         return (m_pPath->IsStroked);
05589     else
05590         return FALSE;
05591 }
05592 
05593 /********************************************************************************************
05594 
05595 >   DocCoord BlendPath::GetPathCoord(INT32 Index)
05596 
05597     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05598     Created:    10/11/94
05599     Inputs:     Index = index into the path's coord array of coord required
05600                         If index < 0 or index >= num coords in array, an value of 0 used
05601     Outputs:    -
05602     Returns:    The DocCoord at index Index
05603     Purpose:    Access to path's coord array
05604 
05605 ********************************************************************************************/
05606 
05607 DocCoord BlendPath::GetPathCoord(INT32 Index)
05608 {
05609     DocCoord Result(0,0);
05610 
05611     if (m_pPath != NULL)
05612     {
05613         if (m_DirChanged)
05614             m_pPath->Reverse();
05615 
05616         DocCoord*   pCoords = m_pPath->GetCoordArray();
05617         INT32       n       = m_pPath->GetNumCoords();
05618         if (Index >= n || Index < 0) Index = 0;
05619 
05620         if (Index < n && pCoords != NULL)
05621             Result = pCoords[Index];
05622 
05623         if (m_DirChanged)
05624             m_pPath->Reverse();
05625     }
05626 
05627     return Result;
05628 }
05629 
05630 /********************************************************************************************
05631 
05632 >   void BlendPath::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix)
05633 
05634     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05635     Created:    9/11/94
05636     Inputs:     pRender = region to render blobs into.
05637                 pMatrix = matrix to apply to blob coords
05638     Outputs:    -
05639     Returns:    -
05640     Purpose:    Renders the blend blobs by asking the pNodePath's Path to render its path blobs
05641 
05642 ********************************************************************************************/
05643 
05644 void BlendPath::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix)
05645 {
05646 #if !defined(EXCLUDE_FROM_RALPH)
05647     if (m_pPath != NULL)
05648     {
05649 //      PathVerb* pVerbs  = m_pPath->GetVerbArray();
05650         DocCoord* pCoords = m_pPath->GetCoordArray();
05651         INT32      n       = m_pPath->GetNumCoords();
05652         INT32     Index   = 0;
05653 
05654         pRender->SetLineColour(COLOUR_NONE);
05655         pRender->SetFillColour(COLOUR_UNSELECTEDBLOB);
05656 
05657         INT32 i=0;
05658         DocCoord* pCoordList = (DocCoord*) malloc(n*sizeof(DocCoord));
05659 
05660         if (pCoordList)
05661         {
05662             switch (m_PathType)
05663             {
05664                 case PATHTYPE_SHAPE:
05665 //                  while (m_pPath->FindNextEndPoint(&Index))
05666 //                      pRender->DrawBlob(pCoords[Index], BT_UNSELECTED);
05667                     while (m_pPath->FindNextEndPoint(&Index))
05668                         pCoordList[i++] = pCoords[Index];
05669                     break;
05670 
05671                 case PATHTYPE_LINE:
05672 //                  pRender->DrawBlob(pCoords[0]  , BT_UNSELECTED);
05673 //                  pRender->DrawBlob(pCoords[n-1], BT_UNSELECTED);
05674                     pCoordList[i++] = pCoords[0];
05675                     pCoordList[i++] = pCoords[n-1];
05676                     break;
05677 
05678                 default:
05679                     ERROR3("Unknown pathtype");
05680                     break;
05681             }
05682 
05683             if (pMatrix && i > 0)
05684                 pMatrix->Transform(pCoordList,i);
05685 
05686             for (;i>0;i--)
05687                 pRender->DrawBlob(pCoordList[i-1], BT_UNSELECTED);
05688         }
05689         DELPTR(pCoordList);
05690     }
05691 #endif
05692 }
05693 
05694 /********************************************************************************************
05695 
05696 > BOOL BlendPath::IsPointOverBlob(DocCoord* pPointerPos,INT32* pIndex,Trans2DMatrix* pMatrix)
05697 
05698     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05699     Created:    10/11/94
05700     Inputs:     pPointerPos = point to check against
05701                 pIndex     = ptr to place to store index if match is found
05702                 pMatrix     = matrix to apply to the path points before testing with pointer pos
05703     Outputs:    *pIndex, updated if TRUE returned, undefined otherwise
05704     Returns:    TRUE if match  found, FALSE otherwise
05705     Purpose:    This sees if the point given lies on a selected blob.
05706                 If a match is found, the index to blob element is updated
05707 
05708                 Markn 26-5-99:  Modified so that if the function returns TRUE, *pPointerPos is updated
05709                                 so that it contains the coords of the blob it is over
05710     SeeAlso:    -
05711 
05712 ********************************************************************************************/
05713 
05714 BOOL BlendPath::IsPointOverBlob(DocCoord* pPointerPos,INT32* pIndex,Trans2DMatrix* pMatrix)
05715 {
05716 #if !defined(EXCLUDE_FROM_RALPH)
05717     BOOL Found = FALSE;
05718 
05719     if (m_pPath != NULL && pIndex != NULL)
05720     {
05721         Path* pPath = m_pPath;
05722         if (pMatrix)
05723         {
05724             pPath = new Path;
05725             if (pPath != NULL && pPath->Initialise())
05726             {
05727                 pPath->MergeTwoPaths(*m_pPath);
05728                 pMatrix->Transform((DocCoord*)(pPath->GetCoordArray()), pPath->GetNumCoords() );
05729             }
05730         }
05731 
05732         Found = pPath->FindNearestPoint(*pPointerPos,POINTFLAG_ENDPOINTS,pIndex);
05733 
05734         if (Found)
05735         {
05736             if (m_PathType == PATHTYPE_LINE)
05737                 Found = ((*pIndex == 0) || (*pIndex == (pPath->GetNumCoords()-1)));
05738             else if (m_DirChanged)
05739             {
05740                 pPath->Reverse();
05741                 pPath->FindNearestPoint(*pPointerPos,POINTFLAG_ENDPOINTS,pIndex);
05742                 pPath->Reverse();
05743             }
05744 
05745             if (Found)
05746             {
05747                 *pPointerPos = GetPathCoord(*pIndex);
05748                 if (pMatrix)
05749                     pMatrix->Transform(pPointerPos,1);
05750             }
05751         }
05752 
05753         if (pMatrix) 
05754             DELPTR(pPath);
05755     }
05756 
05757     return Found;
05758 #else
05759     return FALSE;
05760 #endif
05761 }
05762 
05763 /********************************************************************************************
05764 
05765 > CCAttrMap* BlendPath::FindAppliedAttributes()
05766 
05767     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05768     Created:    16/11/94
05769     Inputs:     -
05770     Outputs:    -
05771     Returns:    Ptr to the attributes applied to this blend path
05772                 NULL if unable to get attributes
05773     Purpose:    This calls FindAppliedAttributes() on the node that created this blend path.
05774                 It will only do this if this is the first call since the last FreeAppliedAttributes()
05775                 or is the first ever call (in other words, it is cached).
05776     SeeAlso:    -
05777 
05778 ********************************************************************************************/
05779 
05780 CCAttrMap* BlendPath::FindAppliedAttributes()
05781 {
05782     if (m_pAppliedAttrs == NULL)
05783     {
05784         m_pAppliedAttrs = new CCAttrMap(30);
05785         if (m_pAppliedAttrs != NULL)
05786         {
05787             // Ilan 4/5/00
05788             // Ensure geometry linked attributes which are applied to the entire Blend compound
05789             // aren't found
05790             if (!m_pCreatedByNode->FindAppliedAttributes(m_pAppliedAttrs,5000,NULL,TRUE))
05791             {
05792                 delete m_pAppliedAttrs;
05793                 m_pAppliedAttrs = NULL;
05794             }
05795             AttrTranspFillGeometry* pTransp = NULL;
05796             m_pCreatedByNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrTranspFillGeometry), (NodeAttribute**)&pTransp);
05797         }
05798     }
05799 
05800     return m_pAppliedAttrs;
05801 }
05802 
05803 /********************************************************************************************
05804 
05805 > void BlendPath::FreeAppliedAttributes()
05806 
05807     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
05808     Created:    16/11/94
05809     Inputs:     -
05810     Outputs:    -
05811     Returns:    -
05812     Purpose:    This will free any memory used to store the applied attributes of this blend path
05813     SeeAlso:    -
05814 
05815 ********************************************************************************************/
05816 
05817 void BlendPath::FreeAppliedAttributes()
05818 {
05819     if (!m_UniqueAttrs && m_pAppliedAttrs != NULL)
05820     {
05821         delete m_pAppliedAttrs;
05822         m_pAppliedAttrs = NULL;
05823     }
05824 }
05825 
05826 /********************************************************************************************
05827 
05828 >   BOOL BlendPath::MakeAttributeMap()
05829 
05830     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
05831     Created:    11/9/2000
05832     Inputs:     -
05833     Outputs:    -
05834     Returns:    A copy of the applied attributes of this blendpath, or NULL if we fail
05835 
05836     Purpose:    To make a complete copy of the applied attribute map
05837     SeeAlso:    -
05838 
05839 ********************************************************************************************/
05840 
05841 CCAttrMap* BlendPath::MakeAttributeMapCopy()
05842 {
05843     // easiest thing to do is get the attribute pointers the normal way
05844     // and then copy them one by one
05845     CCAttrMap* pAttrMap = FindAppliedAttributes();
05846 
05847     if (pAttrMap == NULL)
05848     {
05849         ERROR3("Unable to find attributes in BlendPath::MakeAttributeMap");
05850         return NULL; 
05851     }
05852 
05853     // make a new map
05854     CCAttrMap* pCopyMap = new CCAttrMap(30);
05855     if (pCopyMap == NULL)
05856         return NULL;
05857 
05858     // now copy each one and set it back to the map
05859     CCRuntimeClass     *pType;
05860     void               *pVal;
05861     NodeAttribute* pNodeAttr = NULL;
05862     NodeAttribute* pAttrCopy = NULL;
05863 
05864     // iterating all (key, value) pairs
05865     for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); 
05866         Pos != pAttrMap->GetEndPosition();)
05867     {
05868         // Get attr at CMapPtrToPtr::iterator Pos
05869         pAttrMap->GetNextAssoc(Pos,pType,pVal);
05870 
05871         // pVal is actually an attribute, so get a ptr to it and render it
05872         pNodeAttr = (NodeAttribute *)pVal;
05873 
05874         pAttrCopy = (NodeAttribute*)pNodeAttr->SimpleCopy();
05875         if (pAttrCopy == NULL)
05876         {
05877             ERROR3("Unable to make attribute copy in BlendPath::MakeAttributeMap");
05878             DeleteAttributeMapAndAttributes(pCopyMap);
05879             return NULL;
05880         }
05881         
05882         pCopyMap->SetAt( pAttrCopy->GetAttributeType(), (void *)pAttrCopy );
05883         pAttrCopy = NULL;
05884     }
05885 
05886     return pCopyMap;
05887 }
05888 
05889 
05890 /********************************************************************************************
05891 
05892 >   CCAttrMap* BlendPath::UpdateAttributeMapCopy(CCAttrMap* pCopyMap)
05893 
05894     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
05895     Created:    14/01/2004
05896     Inputs:     -
05897     Outputs:    -
05898     Returns:    A copy of the applied attributes of this blendpath, or NULL if we fail
05899 
05900     Purpose:    To make a complete copy of the applied attribute map
05901     SeeAlso:    -
05902 
05903 ********************************************************************************************/
05904 
05905 CCAttrMap* BlendPath::UpdateAttributeMapCopy(CCAttrMap* pCopyMap)
05906 {
05907     // easiest thing to do is get the attribute pointers the normal way
05908     // and then copy them one by one
05909     CCAttrMap* pAttrMap = FindAppliedAttributes();
05910 
05911     if (pAttrMap == NULL)
05912     {
05913         ERROR3("Unable to find attributes in BlendPath::UpdateAttributeMapCopy");
05914         return NULL; 
05915     }
05916 
05917     ERROR2IF(pCopyMap==NULL, NULL, "Unable to find attribute copies in BlendPath::UpdateAttributeMapCopy");
05918 
05919     // now copy the contents of attributes from the original map back into the copy map
05920     CCRuntimeClass     *pType;
05921     void               *pVal;
05922     void               *pCopyVal;
05923     NodeAttribute      *pNodeAttr = NULL;
05924     NodeAttribute      *pAttrCopy = NULL;
05925 
05926     // iterating all (key, value) pairs
05927     for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); 
05928         Pos != pAttrMap->GetEndPosition();)
05929     {
05930         // Get attr at CMapPtrToPtr::iterator Pos
05931         pAttrMap->GetNextAssoc(Pos, pType, pVal);
05932 
05933         pNodeAttr = (NodeAttribute*)pVal;
05934 
05935         BOOL bFound = pCopyMap->Lookup( pType, pCopyVal );
05936         if (bFound)
05937         {
05938             pAttrCopy = (NodeAttribute*)pCopyVal;
05939             pNodeAttr->PolyCopyNodeContents(pAttrCopy);
05940         }
05941     }
05942 
05943     return pCopyMap;
05944 }
05945 
05946 
05947 /********************************************************************************************
05948 
05949 >   void BlendPath::DeleteAttributeMapAndAttributes()
05950 
05951     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
05952     Created:    11/9/2000
05953     Inputs:     -
05954     Outputs:    -
05955     Returns:    -
05956 
05957     Purpose:    Counterpart to MakeAttributeMap, deletes all the copied attributes and then the
05958                 map.
05959     SeeAlso:    -
05960 
05961 ********************************************************************************************/
05962 
05963 void BlendPath::DeleteAttributeMapAndAttributes(CCAttrMap* pMap)
05964 {
05965 
05966     if (pMap != NULL)
05967     {
05968         pMap->DeleteAttributes();
05969 
05970         delete pMap;
05971         pMap = NULL;
05972     }
05973 
05974 }
05975 
05976 /********************************************************************************************
05977 
05978 > void BlendPath::SetFreeAttributeFlag(BOOL Value)
05979 
05980     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
05981     Created:    10/5/2000
05982     Inputs:     Value to set the flag to
05983     Outputs:    -
05984     Returns:    -
05985     Purpose:    Sets the flag which indicates whether or not we definitely want to delete
05986                 the attribute map upon destruction of this blendpath.  This was designed
05987                 pretty specifically for use with the Brush code so if you want to use it for
05988                 other purposes you should be careful.
05989     SeeAlso:    -
05990 
05991 ********************************************************************************************/
05992 
05993 void BlendPath::SetFreeAttributeFlag(BOOL Value)
05994 {
05995     m_bFreeAttrMapOnDestruct = TRUE;
05996 }
05997 
05998 /********************************************************************************************
05999 
06000 >   void BlendPath::SetAppliedAttributes(CCAttrMap* pMap)
06001 
06002     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06003     Created:    28/3/2000
06004     Inputs:     pointer to an attribute map to set for
06005     Outputs:    -
06006     Returns:    -
06007     Purpose:    Designed specifically for use when blending brush attributes, which need to 
06008                 generate blendpaths and attribute maps by hand,so to speak.  This function
06009                 allows you to set an external attribute map for this blendpath.
06010     SeeAlso:    -
06011 
06012 ********************************************************************************************/
06013 
06014 void BlendPath::SetAppliedAttributes(CCAttrMap* pMap)
06015 {
06016     if (pMap == NULL)
06017         return;
06018     
06019     // get rid of any existing attributes
06020     FreeAppliedAttributes();
06021     m_pAppliedAttrs = pMap;
06022 }
06023 
06024 
06025 /********************************************************************************************
06026 
06027 >   void BlendPath::SetPath(Path* pPath)
06028 
06029     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06030     Created:    28/3/2000
06031     Inputs:     pointer to a path to set 
06032     Outputs:    -
06033     Returns:    -
06034     Purpose:    Designed specifically for use when blending brush attributes, which need to 
06035                 generate blendpaths and attribute maps by hand,so to speak.  This function
06036                 allows you to set an external path for this blendpath.
06037     SeeAlso:    -
06038 
06039 ********************************************************************************************/
06040 
06041 void BlendPath::SetPath(Path* pPath)
06042 {
06043     if (pPath != NULL)
06044     {
06045         m_pPath = pPath;
06046         m_NumElements   = m_pPath->GetNumElements();
06047         m_PathType      = m_pPath->GetPathType();
06048         m_SubPathNum    = m_pPath->GetNumSubpaths();
06049     }
06050 }
06051 
06052 
06053 /********************************************************************************************
06054 
06055 >   void BlendPath::DeleteAttributesAndPath()
06056 
06057     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06058     Created:    28/3/2000
06059     Inputs:     -
06060     Outputs:    -
06061     Returns:    -
06062     Purpose:    Sets m_SubPathID to zero thus ensuring that all the attributes and the attribute
06063                 map are deleted.  We need to do this for blendpaths created by blending brush
06064                 attributes as mentioned in the previous two functions.  I don't recommend use
06065                 of this function under normal circumstances.
06066     SeeAlso:    -
06067 
06068 ********************************************************************************************/
06069 
06070 void BlendPath::DeleteAttributesAndPath()
06071 {
06072     m_bDeleteAll = TRUE;
06073 }
06074 
06075 
06076 /********************************************************************************************
06077 
06078 > BitmapFillAttribute* BlendPath::GetAppliedBitmapFill()
06079 
06080     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
06081     Created:    16/11/94
06082     Inputs:     -
06083     Outputs:    -
06084     Returns:    the bitmap colour fill attribute applying to this blendpath, if there is one
06085     Purpose:    as above
06086     SeeAlso:    -
06087 
06088 ********************************************************************************************/
06089 
06090 AttrBitmapFill* BlendPath::GetAppliedBitmapColourFill()
06091 {
06092     // Get our applied attribute map
06093     CCAttrMap* pAttrMap = FindAppliedAttributes();
06094     if (pAttrMap == NULL)
06095         return NULL;
06096 
06097     // loop through the map until we find an bitmap fill
06098     CCRuntimeClass     *pType;
06099     void               *pVal;
06100     NodeAttribute* pNodeAttr = NULL;
06101     // iterating all (key, value) pairs
06102     for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); 
06103         Pos != pAttrMap->GetEndPosition();)
06104     {
06105         // Get attr at CMapPtrToPtr::iterator Pos
06106         pAttrMap->GetNextAssoc(Pos,pType,pVal);
06107 
06108         // pVal is actually an attribute, so get a ptr to it 
06109         pNodeAttr = (NodeAttribute *)pVal;
06110     
06111         if (pNodeAttr->IsABitmapFill() &&!pNodeAttr->IsATranspFill())
06112             break;
06113 
06114         pNodeAttr = NULL;
06115     }
06116 
06117     // first get the attribute value from the attribute node
06118     if (pNodeAttr != NULL)
06119     {
06120         return (AttrBitmapFill*)pNodeAttr;
06121     }
06122 
06123     return NULL;
06124         
06125 }   
06126 
06127 
06128 /********************************************************************************************
06129 
06130 > BitmapFillAttribute* BlendPath::GetAppliedBitmapTranspFill()
06131 
06132     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
06133     Created:    16/11/94
06134     Inputs:     -
06135     Outputs:    -
06136     Returns:    the bitmapfill transparent attribute applying to this blendpath, if there is one
06137     Purpose:    as above
06138     SeeAlso:    -
06139 
06140 ********************************************************************************************/
06141 
06142 AttrBitmapFill* BlendPath::GetAppliedBitmapTranspFill()
06143 {
06144     // Get our applied attribute map
06145     CCAttrMap* pAttrMap = FindAppliedAttributes();
06146     if (pAttrMap == NULL)
06147         return NULL;
06148 
06149     // loop through the map until we find an bitmap fill
06150     CCRuntimeClass     *pType;
06151     void               *pVal;
06152     NodeAttribute* pNodeAttr = NULL;
06153     // iterating all (key, value) pairs
06154     for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); 
06155         Pos != pAttrMap->GetEndPosition();)
06156     {
06157         // Get attr at CMapPtrToPtr::iterator Pos
06158         pAttrMap->GetNextAssoc(Pos,pType,pVal);
06159 
06160         // pVal is actually an attribute, so get a ptr to it 
06161         pNodeAttr = (NodeAttribute *)pVal;
06162     
06163         if (pNodeAttr->IsABitmapFill() &&pNodeAttr->IsATranspFill())
06164             break;
06165 
06166         pNodeAttr = NULL;
06167     }
06168 
06169     // first get the attribute value from the attribute node
06170     if (pNodeAttr != NULL)
06171     {
06172         return (AttrBitmapFill*)pNodeAttr;
06173     }
06174 
06175     return NULL;
06176         
06177 }   
06178 
06179 
06180 
06181 /********************************************************************************************
06182 
06183 >   void BlendPath::DeleteAttributesAndPath()
06184 
06185     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06186     Created:    28/3/2000
06187     Inputs:     -
06188     Outputs:    -
06189     Returns:    the applied linewidth, or -1 indicating that no line width was found
06190     Purpose:    Returns the cached line width applying to this blendpath
06191     SeeAlso:    -
06192 
06193 ********************************************************************************************/
06194 
06195 MILLIPOINT BlendPath::GetAppliedLineWidth()
06196 {
06197     // find the line width attribute
06198     NodeAttribute* pAttr = NULL;
06199     m_pCreatedByNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), &pAttr);
06200     if (pAttr != NULL)
06201         m_AppliedLineWidth = ((AttrLineWidth*)pAttr)->Value.LineWidth;  
06202     
06203     return m_AppliedLineWidth;
06204 }
06205 
06206 
06207 /********************************************************************************************
06208 
06209 >   BOOL BlendPath::MakeCopyPathAndAttributes()
06210 
06211     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06212     Created:    28/3/2000
06213     Inputs:     -
06214     Outputs:    -
06215     Returns:    TRUE if successful, FALSE otherwise
06216     Purpose:    Because of all the imprecision problems we've been having I've decided it is
06217                 best if we never ever access the orignal blendpath path and attribute data for 
06218                 rendering. Instead what we will do is generate copies of this data and supply it
06219                 to anyone who wants to render with this brush.  The copies will then be destroyed
06220                 after we have finished rendering.
06221 
06222                 This function makes copies of the paths and attributes and and assigns them to
06223                 our member pointers.
06224     SeeAlso:    -
06225 
06226 ********************************************************************************************/
06227 
06228 BOOL BlendPath::MakeCopyPathAndAttributes()
06229 {
06230     // if we've no path then fail
06231     ERROR2IF(m_pPath == NULL, FALSE, "Path is NULL in BlendPath::MakeCopyPathAndAttributes");
06232 
06233     // likewise we need the attributes
06234     CCAttrMap* pAttrMap = FindAppliedAttributes();
06235     
06236     ERROR2IF(pAttrMap == NULL, FALSE, "Unable to find attributes in BlendPath::MakeCopyPathAndAttributes");
06237 
06238     // if we've already got them then get rid of the existing ones
06239     if (m_pCopyPath != NULL || m_pCopyAttrs != NULL)
06240         DeleteCopyPathAndAttributes();
06241 
06242     // first copy the path
06243     m_pCopyPath = new Path;
06244 
06245     if (m_pCopyPath == NULL)
06246         return FALSE;
06247 
06248     if (!m_pCopyPath->Initialise(m_pPath->GetNumCoords()))
06249     {
06250         delete m_pCopyPath;
06251         m_pCopyPath = NULL;
06252         return FALSE;
06253     }
06254     if (!m_pCopyPath->CopyPathDataFrom(m_pPath))
06255     {
06256         delete m_pCopyPath;
06257         m_pCopyPath = NULL;
06258         return FALSE;
06259     }
06260 
06261     // get a copy of our attributes
06262     m_pCopyAttrs = MakeAttributeMapCopy();
06263 
06264     if (m_pCopyAttrs == NULL)
06265     {
06266         delete m_pCopyPath;
06267         m_pCopyPath = NULL;
06268         return FALSE;
06269     }
06270     return TRUE;
06271 }
06272 
06273 
06274 /********************************************************************************************
06275 
06276 >   BOOL BlendPath::UpdateCopyPathAndAttributes()
06277 
06278     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06279     Created:    28/3/2000
06280     Inputs:     -
06281     Outputs:    -
06282     Returns:    TRUE if successful, FALSE otherwise
06283     Purpose:    Because of all the imprecision problems we've been having I've decided it is
06284                 best if we never ever access the orignal blendpath path and attribute data for 
06285                 rendering. Instead what we will do is generate copies of this data and supply it
06286                 to anyone who wants to render with this brush.  The copies will then be destroyed
06287                 after we have finished rendering.
06288 
06289                 This function makes copies of the paths and attributes and and assigns them to
06290                 our member pointers.
06291     SeeAlso:    -
06292 
06293 ********************************************************************************************/
06294 
06295 BOOL BlendPath::UpdateCopyPathAndAttributes()
06296 {
06297     // if we've no path then fail
06298     ERROR2IF(m_pPath == NULL, FALSE, "Path is NULL in BlendPath::UpdateCopyPathAndAttributes");
06299     ERROR2IF(m_pCopyPath == NULL, FALSE, "CopyPath is NULL in BlendPath::UpdateCopyPathAndAttributes");
06300 
06301     // likewise we need the attributes
06302     ERROR2IF(m_pAppliedAttrs==NULL, FALSE, "No applied attrs in BlendPath::UpdateCopyPathAndAttributes");
06303     
06304     ERROR2IF(m_pPath->GetNumCoords()!=m_pCopyPath->GetNumCoords(), FALSE, "Mismatching paths in BlendPath::UpdateCopyPathAndAttributes");
06305 
06306     if (!m_pCopyPath->Initialise(m_pPath->GetNumCoords()))
06307     {
06308         delete m_pCopyPath;
06309         m_pCopyPath = NULL;
06310         return FALSE;
06311     }
06312     if (!m_pCopyPath->CopyPathDataFrom(m_pPath))
06313     {
06314         delete m_pCopyPath;
06315         m_pCopyPath = NULL;
06316         return FALSE;
06317     }
06318 
06319     // get a copy of our attributes
06320     m_pCopyAttrs = UpdateAttributeMapCopy(m_pCopyAttrs);
06321 
06322     if (m_pCopyAttrs == NULL)
06323     {
06324         delete m_pCopyPath;
06325         m_pCopyPath = NULL;
06326         return FALSE;
06327     }
06328     return TRUE;
06329 }
06330 
06331 
06332 /********************************************************************************************
06333 
06334 >   void BlendPath::DeleteCopyPathAndAttributes()
06335 
06336     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06337     Created:    28/3/2000
06338     Inputs:     -
06339     Outputs:    -
06340     Returns:    -
06341     Purpose:    Deletes the copies we made of the paths and attributes
06342     SeeAlso:    -
06343 
06344 ********************************************************************************************/
06345 
06346 void BlendPath::DeleteCopyPathAndAttributes()
06347 {
06348     if (m_pCopyPath != NULL)
06349     {
06350         delete m_pCopyPath;
06351         m_pCopyPath = NULL;
06352     }
06353     if (m_pCopyAttrs != NULL)
06354     {
06355         DeleteAttributeMapAndAttributes(m_pCopyAttrs);
06356         m_pCopyAttrs = NULL;
06357     }
06358 }
06359 
06360 
06361 /********************************************************************************************
06362 
06363 >   Path* BlendPath::GetCopyPath()
06364 
06365     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06366     Created:    28/3/2000
06367     Inputs:     -
06368     Outputs:    -
06369     Returns:    the copy we made of our path
06370     Purpose:    access
06371     SeeAlso:    -
06372 
06373 ********************************************************************************************/
06374 
06375 Path* BlendPath::GetCopyPath()
06376 {
06377     return m_pCopyPath;
06378 }
06379 
06380 
06381 /********************************************************************************************
06382 
06383 >   Path* BlendPath::GetCopyPath()
06384 
06385     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
06386     Created:    28/3/2000
06387     Inputs:     -
06388     Outputs:    -
06389     Returns:    the copy we made of our attribute map
06390     Purpose:    access
06391     SeeAlso:    -
06392 
06393 ********************************************************************************************/
06394 
06395 CCAttrMap* BlendPath::GetCopyAttributes()
06396 {
06397     return m_pCopyAttrs;
06398 }
06399 
06400 //-------------------------------------------
06401 //-------------------------------------------
06402 //-------------------------------------------
06403 //-------------------------------------------
06404 
06405 BecomeA::BecomeA(BecomeAReason ThisReason,
06406             CCRuntimeClass* pClass,
06407             UndoableOperation* pOp /* = NULL */,
06408             BOOL sel /*= TRUE*/,
06409             BOOL First /*= FALSE*/
06410             ) : Reason(ThisReason), pClassToBecome(pClass), pUndoOp(pOp)
06411             {   Select = sel;
06412                 pCallNode = NULL;
06413                 AmFirstNode = First;
06414                 fSilhouette = FALSE;
06415                 fShadowSilhouettes = FALSE;
06416                 insertComplexBlendStepsAsPaths = FALSE;
06417                 m_Count = 0;
06418                 m_bIsCounting = FALSE;
06419                 m_bBecomeANodePath = (pClassToBecome==CC_RUNTIME_CLASS(NodePath));
06420                 m_bInPlace = FALSE;
06421                 m_bSecondary = FALSE;
06422                 m_bPathsOnly = FALSE;
06423             }
06424 
06425 BOOL BecomeA::BAPath() const {return m_bBecomeANodePath;}
06426 
06427 // This function should be called when Reason == BECOMEA_PASSBACK 
06428 // (see above for a description of this reason code)
06429 BOOL BecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap /*=NULL*/)
06430 {
06431     if (pAttrMap)
06432     {
06433         pAttrMap->DeleteAttributes();
06434         delete pAttrMap;
06435     }
06436     return FALSE;
06437 }
06438 
06439 
06440 /***********************************************************************************************
06441 
06442 > NodeRenderableInk * BlendBecomeA::CreateWrapNode(NodeRenderableInk * pNode, 
06443                                                         NodeCompound *pTree,
06444                                                         CCAttrMap * pMap)
06445 
06446     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
06447     Created:    15/3/2000
06448     Inputs:     pNode   -   the node to wrap up
06449                 pTree   -   the root of the tree to use to wrap the node
06450                 pAttrMap    -   the attribute map to use in the wrapping
06451     Outputs:    -
06452     Returns:    TRUE if Successful, FALSE otherwise
06453     Pupose:     Recursively wraps up the given node using the tree specified
06454     
06455 ***********************************************************************************************/
06456 NodeRenderableInk * BlendBecomeA::CreateWrapNode(NodeRenderableInk * pNode, NodeCompound *pTree,
06457                                                         CCAttrMap * pMap)
06458 {
06459     NodeCompound * pCompound = (NodeCompound *)pTree->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound));
06460 
06461     if (pCompound)
06462     {
06463         // create a wrapped node for the compound node
06464         pNode = CreateWrapNode(pNode, pCompound, pMap);
06465     }
06466 
06467     // create the wrapped node from the given compound node
06468     return pTree->CreateTreeFromNodeToBlend(pNode, pMap);
06469 }
06470 
06471 /***********************************************************************************************
06472 
06473 > BOOL BlendBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap)
06474 
06475     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
06476     Created:    18/10/94
06477     Inputs:     pNewNode        = ptr to Node that been generated by a node via its DoBecomeA() method
06478                 pCreatedByNode  = ptr to the node that created pNewNode
06479                 pAttrMap        = ptr to attr map (or NULL if to use applied attrs of pCreatedByNode)
06480     Outputs:    -
06481     Returns:    TRUE if Successful, FALSE otherwise
06482     Purpose:    This is called by each node that is blended. When the blend tool asks an object to turn itself
06483                 into loads of paths, it gives the object a BlendBecomeA ptr. The object calls the PassBack()
06484                 method of BlendBecomeA (i.e. this routine) for each path that is generated.
06485                 This generates a BlendPath for each sub-path in the given NodePath, and adds it to a blend 
06486                 reference object.
06487 
06488                 NOTE: pNewNode should point to isolated instance, and not to a used NodePath linked into
06489                       the tree! 
06490 
06491                 NOTE: Once you give a BlendPath a NodePath, this becomes property of the BlendPath
06492                       and can be altered and/or deleted by the BlendPath at any time.
06493                       DO NOT KEEP A COPY OF THE PTR pThisNodePath!!! If you want a copy of the
06494                       NodePath at pThisNodePath, make a new copy of the node.
06495 
06496                 NOTE: If it fails, pNewNode will have to be deleted by the caller
06497 
06498 ***********************************************************************************************/
06499 
06500 BOOL BlendBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap)
06501 {
06502     ERROR2IF(pBlendRef == NULL,FALSE,"pBlendRef == NULL");
06503     ERROR2IF(pNewNode == NULL,FALSE,"pNewNode == NULL");
06504 
06505     if (pProgress != NULL)
06506     {
06507         if (!pProgress->Update(pProgress->GetCount(1),TRUE) && !IgnoreEscape)
06508             return FALSE;
06509     }
06510 
06511     Path* pInkPath = NULL;
06512 
06513     Node * pPathNode = NULL;
06514 
06515     // DMc
06516     // we have a game of let's pretend in here
06517     // because all passback nodes can only have 1 path node in them,
06518     // then let's get this node & set up the path appropriately
06519     if (pNewNode->IsNodePath())
06520     {
06521         pInkPath = &(((NodePath*)pNewNode)->InkPath);
06522         pPathNode = (NodePath *)pNewNode;
06523     }
06524     else if (pNewNode->IsCompound())
06525     {
06526         NodeRenderableInk * pNodeToBlend = ((NodeCompound *)pNewNode)->GetNodeToBlend();
06527 
06528         if (pNodeToBlend)
06529         {
06530             if (pNodeToBlend->IsNodePath())
06531             {
06532                 pInkPath = &(((NodePath *)pNodeToBlend)->InkPath);
06533                 pPathNode = (NodePath *)pNodeToBlend;
06534             }
06535         }
06536     }
06537 
06538     INT32 NumCoords = pInkPath->GetNumCoords();
06539     INT32 StartIndex=0,EndIndex=0;
06540     UINT32 NumSubPaths=0;
06541 
06542     Path TempPath;
06543     BOOL ok = TempPath.Initialise(NumCoords);
06544 
06545     // All subpaths generated by this call will have the same subpath ID
06546     SubPathID++;
06547 
06548     NodeRenderableInk * pTreeNode = NULL;
06549 
06550     // This loop interates for each subpath in pInkPath.
06551     while (ok && StartIndex < NumCoords)
06552     {
06553         // Get the start and end indexes for the current subpath
06554         EndIndex = StartIndex;
06555         pInkPath->FindEndElOfSubPath(&EndIndex);
06556 
06557         ERROR2IF(EndIndex >= NumCoords,FALSE,"EndIndex gone beyond num coords in path");
06558 
06559         // Clear the temp path we've set up, and copy the subpath into it
06560         TempPath.ClearPath(FALSE);
06561         ok = pInkPath->CopySectionTo(&TempPath,StartIndex,(EndIndex-StartIndex)+1);
06562 
06563         if (ok)
06564         {
06565             // Make sure the filled and stroked flags are correct
06566             TempPath.IsFilled  = pInkPath->IsFilled;
06567             TempPath.IsStroked = pInkPath->IsStroked;
06568 
06569             if (m_pMatrix != NULL)
06570                 m_pMatrix->Transform((DocCoord*)TempPath.GetCoordArray(), TempPath.GetNumCoords() );
06571 
06572             // Create a new NodePath for this subpath
06573             NodePath* pNewNodePath = new NodePath;
06574             ok = (pNewNodePath != NULL);
06575             if (ok)
06576             {
06577                 // Copy the subpath in TempPath into the new NodePath's path object
06578                 Path* pNewPath = &(pNewNodePath->InkPath);
06579                 ok = pNewPath->Initialise(TempPath.GetNumCoords());
06580 
06581                 if (ok) ok = pNewPath->CopyPathDataFrom(&TempPath);
06582 
06583                 if (ok)
06584                 {
06585                     // Create a new BlendPath obect
06586                     BlendPath* pBlendPath = new BlendPath;
06587                     ok = (pBlendPath != NULL);
06588 
06589                     // now, have we been passed a compound node ?
06590                     // if so, wrap up the given node with the new node
06591                     if (pNewNode->IsCompound())
06592                     {
06593                         NodeRenderableInk * pWrapNode = CreateWrapNode(pNewNodePath, (NodeCompound *)pNewNode, pAttrMap);
06594 
06595                         if (!pWrapNode)
06596                             return FALSE;
06597 
06598                         // set up its created by node
06599                         ((NodeCompound *)pWrapNode)->SetBlendCreatedByNode(((NodeCompound *)pNewNode)->GetBlendCreatedByNode());
06600                         
06601                         // now, we can only have start blend nodes on first blend path, and we can
06602                         // only have end blend nodes on the last blend path
06603                         if (NumSubPaths != 0)
06604                         {
06605                             pTreeNode = pWrapNode;
06606 
06607                             while (pTreeNode && pTreeNode->IsCompound())
06608                             {
06609                                 if (((NodeCompound *)pTreeNode)->IsStartBlendGroupNode())
06610                                 {
06611                                     ((NodeCompound *)pTreeNode)->SetStartBlendGroupNode(FALSE);
06612                                 }
06613 
06614                                 pTreeNode = (NodeCompound *)pTreeNode->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound));
06615                             }
06616                         }
06617 
06618                         // reset end blend nodes
06619                         if (EndIndex != NumCoords-1)
06620                         {
06621                             pTreeNode = pWrapNode;
06622 
06623                             while (pTreeNode && pTreeNode->IsCompound())
06624                             {
06625                                 if (((NodeCompound *)pTreeNode)->IsEndBlendGroupNode())
06626                                 {
06627                                     ((NodeCompound *)pTreeNode)->SetEndBlendGroupNode(FALSE);
06628                                 }
06629 
06630                                 pTreeNode = (NodeCompound *)pTreeNode->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound));
06631                             }
06632                         }                           
06633 
06634                         if (pWrapNode->IsCompound())
06635                         {
06636                             if (((NodeCompound *)pWrapNode)->IsStartBlendGroupNode())
06637                             {
06638                                 TRACEUSER( "ChrisS", _T("BlendBecomeA::Start node\n"));
06639                             }
06640                             
06641                             if (((NodeCompound *)pWrapNode)->IsEndBlendGroupNode())
06642                             {
06643                                 TRACEUSER( "ChrisS", _T("BlendBecomeA::End node\n"));
06644                             }
06645                         }
06646                             
06647                         if (ok) ok = pBlendPath->Initialise(pWrapNode,Index,pCreatedByNode,SubPathID,NumSubPaths,NULL);
06648                         if (ok) ok = pBlendRef->AddBlendPath(pBlendPath);
06649                     }
06650                     else
06651                     {
06652                         // Initialise the BlendPath object with the generated NodePath
06653                         // and add the BlendPath to the BlendRef
06654                         if (ok) ok = pBlendPath->Initialise(pNewNodePath,Index,pCreatedByNode,SubPathID,NumSubPaths,pAttrMap);
06655                         if (ok) ok = pBlendRef->AddBlendPath(pBlendPath);
06656 
06657                         if (IS_A (pNewNode, NodeBlendPath))
06658                         {
06659                             pBlendPath->SetCreatedViaNodeBlendPath (TRUE);
06660                         }
06661                     }
06662                 }
06663             }
06664         }
06665         Index = -1;
06666         StartIndex = EndIndex+1;    // the start of the next subpath begins after the end of the last one
06667 
06668         // Get the next sub path
06669         NumSubPaths++;
06670     }
06671 
06672     if (pNewNode->IsCompound())
06673     {
06674         pAttrMap->DeleteAttributes();
06675         delete pAttrMap;
06676     }
06677 
06678     if (ok)
06679     {
06680         pBlendRef->SetAWComplex(NumSubPaths > 1);   // This is the 'complex' flag used when exporting to AW EPS
06681         pNewNode->CascadeDelete();
06682         delete pNewNode;
06683         pNewNode = NULL;
06684     }
06685 
06686     return (ok);
06687     
06688 }
06689 
06690 /***********************************************************************************************
06691 
06692 > BOOL HandleBecomeA::PassBack(Path* pBlendedPath,CCAttrMap* pBlendedAttrMap,UINT32 Step)
06693 
06694     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
06695     Created:    17/10/94
06696     Inputs:     pBlendedPath    = ptr to a blended path object
06697                 pBlendedAttrMap = ptr to the blended attributes for this path
06698                 Step            = The step number this path belongs to
06699     Outputs:    -
06700     Returns:    TRUE if successful, FALSE otherwise
06701     Purpose:    This takes a path and an attribute map generated by a NodeBlender object,
06702                 and generates a NodePath object, if it wants to become a path object
06703                 If pBecomeA->Reason == BECOMEA_REPLACE,  the NodePath and attributes are added to the tree.
06704                 If pBecomeA->Reason == BECOMEA_PASSBACK, the NodePath is passed to the routine.
06705 
06706 ***********************************************************************************************/
06707 
06708 BOOL HandleBecomeA::PassBack(Path* pBlendedPath,CCAttrMap* pBlendedAttrMap,UINT32 Step)
06709 {
06710     // Check BecomeA mechanism has been set up
06711     ERROR3IF(pBecomeA     == NULL,"pBecomeA is NULL");
06712     ERROR3IF(pNodeBlender == NULL,"pNodeBlender is NULL");
06713     if (pBecomeA == NULL || pNodeBlender == NULL)
06714         return FALSE;
06715 
06716     // Check for a NULL entry params
06717     ERROR3IF(pBlendedPath      == NULL,"pBlendedPath is NULL");
06718     ERROR3IF(pBlendedAttrMap == NULL,"pBlendedAttrMap is NULL");
06719     if (pBlendedPath == NULL || pBlendedAttrMap == NULL) return FALSE;
06720 
06721     // This lump checks that the Reason is one that we understand
06722     // It also makes sure that we don't have a NULL UndoOp ptr
06723     BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK);
06724     ERROR3IF_PF(!ValidReason,("Unkown BecomeA reason %d",pBecomeA->GetReason()));
06725     if (!ValidReason) return FALSE;
06726 
06727     // pBecomeA->Reason is one that we understand.
06728 
06729     BOOL        Success = TRUE;         // Our success flag (Important that this defaults to TRUE)
06730     NodePath*   pNewNodePath = NULL;    // Ptr to a new NodePath, if we get to make one.
06731 
06732     if (pBecomeA->BAPath())
06733     {
06734         // We need to create a new NodePath, no matter what the reason.
06735         
06736         // Allocate a new NodePath node
06737         ALLOC_WITH_FAIL(pNewNodePath, (new NodePath), pBecomeA->GetUndoOp());
06738         Success = (pNewNodePath != NULL);
06739 
06740         // Initialise the path
06741         if (Success) CALL_WITH_FAIL(pNewNodePath->InkPath.Initialise(pBlendedPath->GetNumCoords(),12), pBecomeA->GetUndoOp(), Success);
06742         if (Success) CALL_WITH_FAIL(pNewNodePath->InkPath.CopyPathDataFrom(pBlendedPath), pBecomeA->GetUndoOp(), Success);
06743 
06744         // If Success is TRUE, then we now have a new NodePath object that contains this shape's path
06745 
06746         if (Success)
06747         {
06748             switch (pBecomeA->GetReason())
06749             {
06750                 case BECOMEA_REPLACE :
06751                 {
06752                     // It's a BECOMEA_REPLACE, so put the new NodePath in the tree in an undoable way
06753 
06754                     // Can't do it in an undoable way without an Undo Op
06755 //                  ERROR2IF_PF(pBecomeA->GetUndoOp() == NULL,FALSE,("GetUndoOp() returned NULL"));
06756 
06757                     // Can't do it if we don't have a context node
06758                     ERROR2IF(pContextNode == NULL,FALSE,"pContextNode is NULL");
06759 
06760                     // By default, attach the new path as the previous child of the context node
06761                     Node*               pAttachNode = pContextNode;
06762                     AttachNodeDirection AttachDir   = PREV;
06763 
06764                     // By default, create a hide node action for the new path we're attaching to the tree
06765                     BOOL    HideNode    = TRUE;
06766                     Node*   pNodeToHide = pNewNodePath;
06767 
06768                     // Should we be placing all created paths inside a group?
06769                     if (MakeGroupPerStep())
06770                     {
06771                         // Do we need to create a new group? I.e. is this the first path created for this step of the blend?
06772                         if (Step != LastStep)
06773                         {
06774                             // Remember this step value so we can tell if the next path we receive belongs to the
06775                             // same blend step
06776                             LastStep = Step;
06777 
06778                             // Create a new group and place it in the tree next to the context node
06779                             pNodeGroup  = new NodeGroup;
06780                             Success     = (pNodeGroup != NULL);
06781                             if (Success)
06782                             {
06783                                 // Put group into the tree
06784                                 pNodeGroup->AttachNode(pContextNode,PREV);
06785 
06786                                 // Make a hide node action for this group
06787                                 HideNode = TRUE;
06788                                 pNodeToHide = pNodeGroup;
06789                             }
06790                         }
06791                         else
06792                             HideNode = FALSE;   // We created a hide node action when this group was created
06793 
06794                         // If we have a group node, make sure the new path gets placed in the tree as the
06795                         // group's last child
06796                         if (Success)
06797                         {
06798                             pAttachNode = pNodeGroup;
06799                             AttachDir   = LASTCHILD;
06800                         }
06801                     }
06802                                 
06803                     if (Success)
06804                     {
06805                         ERROR2IF(pAttachNode == NULL,FALSE,"The attach node is NULL!!!");
06806 
06807                         // Insert the new NodePath into the tree
06808                         pNewNodePath->AttachNode(pAttachNode,AttachDir);
06809 
06810                         // Apply all the attributes to the new path
06811                         pNewNodePath->ApplyAttributes(pBlendedAttrMap,TRUE);
06812 
06813                         // Set the bounds  
06814                         pNewNodePath->ValidateBoundingRect();
06815                         pNewNodePath->InvalidateBoundingRect();
06816 
06817                         // Create a hide node action to hide the node when we undo 
06818                         if (HideNode)
06819                         {
06820                             ERROR2IF(pNodeToHide == NULL,FALSE,"pNodeToHide is NULL");
06821 
06822                             if (pBecomeA->GetUndoOp())
06823                             {
06824                                 HideNodeAction* UndoHideNodeAction;     
06825                                 Success = (HideNodeAction::Init(pBecomeA->GetUndoOp(),
06826                                                          pBecomeA->GetUndoOp()->GetUndoActionList(),
06827                                                          pNodeToHide,
06828                                                          TRUE,       // Include subtree size 
06829                                                          ( Action**)(&UndoHideNodeAction))
06830                                                          != AC_FAIL);
06831                             }
06832                         }
06833 
06834                         CCAttrMap* pNewAttrMap = pBlendedAttrMap->Copy();
06835                         if (pNewAttrMap != NULL)
06836                             pBecomeA->PassBack(pNewNodePath, pNodeBlender, pNewAttrMap);
06837                     }
06838                 }
06839                 break;
06840 
06841                 case BECOMEA_PASSBACK :
06842                 {
06843                     // We need to create a copy of the blended attributes to give to the caller
06844                     // as they don't exist in the tree, and the ones in pBlendedAttrMap will be
06845                     // deleted eventually.
06846                     CCAttrMap* pNewAttrMap = pBlendedAttrMap->Copy();
06847                     if (pNewAttrMap != NULL)
06848                         Success = pBecomeA->PassBack(pNewNodePath,pNodeBlender,pNewAttrMap);
06849                     else
06850                         Success = FALSE;
06851                     break;
06852                 }
06853 
06854                 default:
06855                     break;
06856             }
06857         }
06858     }
06859 
06860     if (!Success)
06861     {
06862         if (pNodeGroup != NULL)
06863         {
06864             // Delete all the groups children (if it has any) and unlink it from the tree (if it's linked)
06865             // This is all done by CascadeDelete()
06866             pNodeGroup->CascadeDelete(); 
06867             delete pNodeGroup;
06868             pNodeGroup = NULL;
06869         }
06870         else if (pNewNodePath != NULL)
06871         {
06872             // Delete all the NodePath's children (if it has any) and unlink it from the tree (if it's linked)
06873             // This is all done by CascadeDelete()
06874             pNewNodePath->CascadeDelete(); 
06875             delete pNewNodePath;
06876             pNewNodePath = NULL;
06877         }
06878     }
06879 
06880     return Success;
06881 }
06882 
06883 
06884 
06885 //------------------------------------------------------------------------------------------
06886 //------------------------------------------------------------------------------------------
06887 //------------------------------------------------------------------------------------------
06888 // Export code...
06889 
06890 /*********************************************************************************************
06891 
06892 >    void NodeBlender::PreExportRender(RenderRegion* pRegion)
06893 
06894      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
06895      Created:   28/11/94
06896      Inputs:    pRegion = ptr to the export render region to export to
06897      Outputs:   
06898      Returns:   
06899      Purpose:   Called before this node or any of its children have been rendered to the export region.
06900                 This outputs the "start blender" command.
06901                 Supports ArtWorks EPS and Camelot EPS
06902      Errors:    
06903 
06904 **********************************************************************************************/
06905  
06906 void NodeBlender::PreExportRender(RenderRegion* pRegion)
06907 {
06908 #ifdef DO_EXPORT
06909     // Ensure that we are initialised (if it can be done!!)
06910     if (!Reinit())
06911         return;
06912 
06913     if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion))
06914     {
06915         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
06916 
06917         if (IS_A(pRegion, CamelotEPSRenderRegion))
06918         {
06919             // only want renderable stuff in EPS
06920             return;
06921         }
06922         else if (pRegion->IS_KIND_OF(NativeRenderRegion))
06923         {
06924             // Output the Camelot format for blenders
06925             pDC->OutputValue(INT32(0));         // Reserved INT32 1
06926             pDC->OutputValue(INT32(0));         // Reserved INT32 2
06927             pDC->OutputValue(INT32(0));         // Reserved INT32 3
06928             pDC->OutputValue(INT32(0));         // Reserved INT32 4
06929             pDC->OutputValue(m_ObjIndexStart);  // Index to object at start of blend
06930             pDC->OutputValue(m_ObjIndexEnd);    // Index to object at end of blend
06931             pDC->OutputValue(m_PathIndexStart); // Path index to el num of start path
06932             pDC->OutputValue(m_PathIndexEnd);   // Path index to el num of end path
06933             pDC->OutputToken(_T("csbr"));           // Camelot "start blender" token
06934             pDC->OutputNewLine();
06935 
06936         }
06937         else 
06938         {
06939             NodeBlend* pNodeBlend = GetNodeBlend();
06940 
06941             if (pNodeBlend != NULL)
06942             {
06943                 if (pNodeBlend->IsArtWorksEPSCompatible())
06944                 {
06945                     // Output the AW format for blenders, if this blender is ArtWorks-compatible
06946                     pDC->OutputValue(INT32(0));                                 // Expanded flag
06947                     pDC->OutputValue(INT32(IsComplex()));                       // Complex flag
06948                     pDC->OutputValue(INT32(IsOneToOne()));                      // The OneToOne flag
06949                     pDC->OutputValue(INT32(GetNumBlendSteps()+1));              // The number of blend steps (+1 for AW)
06950                     pDC->OutputValue(GetAWStartPathIndex());                    // Get start path index
06951                     pDC->OutputValue(GetAWEndPathIndex());                      // Get end path index
06952                     pDC->OutputToken(_T("asbr"));                                   // ArtWorks "start blender" token
06953                     pDC->OutputNewLine();
06954 
06955                     // Blend is done
06956                     return;
06957                 }
06958                 else
06959                 {
06960                     // Blend cannot be done as AW - just render intermediate shapes.
06961                     Render(pRegion);
06962                 }
06963             }
06964             else
06965             {   ERROR3("Parent is not a NodeBlend"); }
06966         }
06967     }
06968 
06969     // These two lines will output the first pair of blendable paths - useful for testing
06970     //pRefStart->GetFirstBlendPath()->GetNodePath()->Render(pRegion);
06971     //pRefEnd  ->GetFirstBlendPath()->GetNodePath()->Render(pRegion);
06972 #endif
06973 }
06974 
06975 /*********************************************************************************************
06976 
06977 >    BOOL NodeBlender::ExportRender(RenderRegion* pRegion)
06978 
06979      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
06980      Created:   28/11/94
06981      Inputs:    pRegion = ptr to the export render region to export to
06982      Outputs:   
06983      Returns:   TRUE if ok, FALSE if something went wrong
06984      Purpose:   Called after this node and all of its children have been rendered to the export region.
06985                 This outputs the "end blender" command.
06986                 Supports ArtWorks EPS and Camelot EPS
06987      Errors:    
06988 
06989 **********************************************************************************************/
06990  
06991 BOOL NodeBlender::ExportRender(RenderRegion* pRegion) 
06992 {
06993 #ifdef DO_EXPORT
06994     // Ensure that we are initialised (if it can be done!!)
06995     if (!Reinit())
06996         return FALSE;
06997 
06998     if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion))
06999     {
07000         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
07001 
07002         if (IS_A(pRegion, CamelotEPSRenderRegion))
07003         {
07004             // only want renderable stuff in EPS
07005             return FALSE; // render this as paths.
07006         }
07007         else if (pRegion->IS_KIND_OF(NativeRenderRegion))
07008         {
07009             pDC->OutputToken(_T("cebr"));               // Camelot "end blender" token
07010             pDC->OutputNewLine();
07011         }
07012         else
07013         {
07014             NodeBlend* pNodeBlend = GetNodeBlend();
07015 
07016             if (pNodeBlend != NULL)
07017             {
07018                 if (pNodeBlend->IsArtWorksEPSCompatible())
07019                 {
07020                     pDC->OutputToken(_T("aebr"));               // ArtWorks "end blender" token
07021                     pDC->OutputNewLine();
07022                 }
07023             }
07024         }
07025         // Tell caller we rendered ourselves ok
07026         return TRUE;
07027     }
07028 #endif
07029     // Render this node in the normal way
07030     return FALSE;
07031 }
07032 
07033 /*********************************************************************************************
07034 
07035 >    BOOL NodeBlend::IsArtWorksEPSCompatible()
07036 
07037      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07038      Created:   28/11/94
07039      Inputs:    
07040      Outputs:   
07041      Returns:   TRUE if this blender is AW compatible
07042      Purpose:   A blender is AW EPS compatible if ALL the following conditions are TRUE:
07043                     1) This node is between two path nodes.
07044                     2) The blender is blending the two path nodes it lies between
07045                     3) The two path nodes contain the same number of sub paths.
07046 
07047                 This is called by the parent blend's IsArtWorksEPSCompatible() func.
07048      Errors:    
07049 
07050 **********************************************************************************************/
07051  
07052 BOOL NodeBlender::IsArtWorksEPSCompatible()
07053 {
07054     ERROR2IF(m_pRefStart == NULL,FALSE,"m_pRefStart == NULL");
07055     ERROR2IF(m_pRefEnd   == NULL,FALSE,"m_pRefEnd   == NULL");
07056 
07057     Node* pPrevNode = FindPrevious();
07058     Node* pNextNode = FindNext();
07059 
07060     return (pPrevNode == m_pNodeStart   &&
07061             pNextNode == m_pNodeEnd     &&
07062             pPrevNode != NULL           &&
07063             pNextNode != NULL           &&
07064             IS_A(pPrevNode,NodePath)    &&
07065             IS_A(pNextNode,NodePath)    &&
07066             m_pRefStart->GetNumBlendPaths() == m_pRefEnd->GetNumBlendPaths());
07067 }
07068 
07069 /*********************************************************************************************
07070 
07071 >    BOOL NodeBlend::IsComplex()
07072 
07073      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07074      Created:   28/11/94
07075      Inputs:    
07076      Outputs:   
07077      Returns:   TRUE if this blender is complex
07078      Purpose:   The blender is "complex" as far as AW EPS is concerned, if either the start path
07079                 or the end path of the blend has more that one sub-path.
07080      Errors:    
07081 
07082 **********************************************************************************************/
07083  
07084 BOOL NodeBlender::IsComplex()
07085 {
07086     ERROR2IF(m_pRefStart == NULL,FALSE,"m_pRefStart == NULL");
07087     ERROR2IF(m_pRefEnd   == NULL,FALSE,"m_pRefEnd   == NULL");
07088 
07089     return (m_pRefStart->GetAWComplex() || m_pRefEnd->GetAWComplex());
07090 }
07091 
07092 /*********************************************************************************************
07093 
07094 > INT32 NodeBlender::GetAWStartPathIndex()
07095 
07096      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07097      Created:   28/11/94
07098      Inputs:    
07099      Outputs:   
07100      Returns:   AW path index based on the PathIndexStart member var
07101      Purpose:   This converts PathIndexStart into an index AW understands (compatible with RISCOS path format)
07102      Errors:    
07103 
07104 **********************************************************************************************/
07105 
07106 INT32 NodeBlender::GetAWStartPathIndex()
07107 {
07108     ERROR2IF(m_pRefStart == NULL,-1,"m_pRefStart == NULL");
07109     return (GetAWPathIndex(m_pRefStart,m_PathIndexStart));
07110 }
07111 
07112 /*********************************************************************************************
07113 
07114 > INT32 NodeBlender::GetAWEndPathIndex()
07115 
07116      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07117      Created:   28/11/94
07118      Inputs:    
07119      Outputs:   
07120      Returns:   AW path index based on the PathIndexEnd member var
07121      Purpose:   This converts PathIndexEnd into an index AW understands (compatible with RISCOS path format)
07122      Errors:    
07123 
07124 **********************************************************************************************/
07125 
07126 INT32 NodeBlender::GetAWEndPathIndex()
07127 {
07128     ERROR2IF(m_pRefEnd == NULL,-1,"m_pRefEnd == NULL");
07129     return (GetAWPathIndex(m_pRefEnd,m_PathIndexEnd));
07130 }
07131 
07132 /*********************************************************************************************
07133 
07134 > INT32 NodeBlender::GetAWPathIndex(BlendRef *pRef,INT32 PathIndex)
07135 
07136      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07137      Created:   28/11/94
07138      Inputs:    pRef = ptr to a blend ref
07139                 PathIndex = index into the blend ref's first path
07140      Outputs:   
07141      Returns:   AW path index based on PathIndex and the first path in the blend ref
07142      Purpose:   This converts the index into an index AW understands (compatible with RISCOS path format)
07143      Errors:    
07144 
07145 **********************************************************************************************/
07146 
07147 INT32 NodeBlender::GetAWPathIndex(BlendRef *pRef,INT32 PathIndex)
07148 {
07149     BlendPath* pBlendPath = pRef->GetFirstBlendPath();
07150     if (pBlendPath == NULL || pBlendPath->GetPath() == NULL) 
07151         return -1;
07152 
07153     Path* pPath = pBlendPath->GetPath();
07154     INT32 AWPathIndex=0,Index=0;
07155     while (Index < PathIndex)
07156     {
07157         pPath->FindNextEndPoint(&Index);
07158         AWPathIndex++;
07159     }
07160 
07161     return AWPathIndex;     
07162 }
07163 
07164 //-------------------------------------------------------------------------
07165 //-------------------------------------------------------------------------
07166 //-------------------------------------------------------------------------
07167 
07168 /********************************************************************************************
07169 
07170   > virtual BOOL NodeBlender::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
07171 
07172     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07173     Created:    30/5/96
07174     Inputs:     pFilter = ptr to the filter
07175     Returns:    TRUE if record is written, FALSE if not
07176     Purpose:    Writes the blender record to the filter
07177     SeeAlso:    -
07178 
07179 ********************************************************************************************/
07180 
07181 BOOL NodeBlender::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
07182 {
07183 #ifdef DO_EXPORT
07184     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
07185 
07186     BOOL ok = TRUE;
07187 
07188     CXaraFileRecord Rec(TAG_BLENDER,TAG_BLENDER_SIZE);
07189 
07190     if (ok) ok = Rec.Init();
07191     if (ok) ok = Rec.WriteINT32(m_PathIndexStart);
07192     if (ok) ok = Rec.WriteINT32(m_PathIndexEnd);
07193     if (ok) ok = pFilter->Write(&Rec);
07194 
07195     // Diccon 9/99 added extra record
07196     CXaraFileRecord Additional(TAG_BLENDERADDITIONAL, TAG_BLENDERADDITIONAL_SIZE);
07197     if (ok) ok = Additional.Init();
07198     if (ok) ok = Additional.WriteINT32(m_BlendedOnCurve);
07199     
07200     if (ok) 
07201     {   
07202         if (m_NodeBlendPathIndex == -2)
07203             ok = Additional.WriteINT32(-1);
07204         else
07205             ok = Additional.WriteINT32(m_NodeBlendPathIndex);
07206     }
07207     if (ok) ok = Additional.WriteINT32(m_ObjIndexStart);
07208     if (ok) ok = Additional.WriteINT32(m_ObjIndexEnd);
07209 
07210     // Karim 05/02/2001
07211     // Extra bitfield tacked onto the end of the record.
07212     // Currently this stores only our reversed-on-path flag.
07213     if (ok)
07214     {
07215         BYTE BitField = IsReversed() ? 1 : 0;
07216         ok = Additional.WriteBYTE(BitField);
07217     }
07218 
07219     if (ok) ok = pFilter->Write(&Additional);
07220 
07221     return ok;
07222 #else
07223     return FALSE;
07224 #endif
07225 }
07226 
07227 //--------------------------------------------------------------
07228 // See NodeBlender::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
07229 //
07230 BOOL NodeBlender::WritePreChildrenNative(BaseCamelotFilter* pFilter)
07231 {
07232 #ifdef DO_EXPORT
07233     return WritePreChildrenWeb(pFilter);
07234 #else
07235     return FALSE;
07236 #endif
07237 }
07238 
07239 /********************************************************************************************
07240 
07241 >   BOOL NodeBlender::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter)
07242 
07243     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07244     Created:    11/5/99
07245     Inputs:     pFilter = ptr to the filter to write to
07246     Outputs:    -
07247     Returns:    TRUE ok, FALSE otherwise
07248     Purpose:    Begins the child record sequence for the blender
07249 
07250                 Blenders need to write out curve CMapPtrToPtr::iteratoring information if the blend
07251                 is on a curve
07252     SeeAlso:    WritePreChildrenNative()
07253 
07254 ********************************************************************************************/
07255 
07256 BOOL NodeBlender::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter)
07257 {
07258 #ifdef DO_EXPORT
07259     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
07260 
07261     if (GetNodeBlendPath() == NULL)
07262         return NodeRenderableInk::WriteBeginChildRecordsNative(pFilter);
07263 
07264     // First thing to do is write out the Down record
07265     BOOL ok = (pFilter->WriteZeroSizedRecord(TAG_DOWN));
07266 
07267     // Write out the blender's curve CMapPtrToPtr::iterator proportions
07268     {
07269         CamelotFileRecord PropRec(pFilter,TAG_BLENDER_CURVEPROP,TAG_BLENDER_CURVEPROP_SIZE);
07270         if (ok) ok = PropRec.Init();
07271         if (ok) ok = PropRec.WriteDOUBLE(m_ProportionOfPathDistStart);
07272         if (ok) ok = PropRec.WriteDOUBLE(m_ProportionOfPathDistEnd);
07273         if (ok) ok = pFilter->Write(&PropRec);
07274     }
07275         
07276     // Write out the blender's curve angles
07277     if (m_AngleStart != 0.0 || m_AngleEnd != 0.0)
07278     {
07279         CamelotFileRecord AngleRec(pFilter,TAG_BLENDER_CURVEANGLES,TAG_BLENDER_CURVEANGLES_SIZE);
07280         if (ok) ok = AngleRec.Init();
07281         if (ok) ok = AngleRec.WriteDOUBLE(m_AngleStart);
07282         if (ok) ok = AngleRec.WriteDOUBLE(m_AngleEnd);
07283         if (ok) ok = pFilter->Write(&AngleRec);
07284     }
07285 
07286     return ok;
07287 #else
07288     return TRUE;
07289 #endif //DO_EXPORT
07290 }
07291 
07292 /********************************************************************************************
07293 
07294 >   BOOL NodeBlender::WriteEndChildRecordsNative(BaseCamelotFilter* pFilter)
07295 
07296     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07297     Created:    11/5/99
07298     Inputs:     pFilter = ptr to the filter to write to
07299     Outputs:    -
07300     Returns:    TRUE ok, FALSE otherwise
07301     Purpose:    Ends the child record sequence for the blend
07302 
07303                 Only does anything if the blend is on a curve
07304 
07305     SeeAlso:    WritePreChildrenNative()
07306 
07307 ********************************************************************************************/
07308 
07309 BOOL NodeBlender::WriteEndChildRecordsNative(BaseCamelotFilter* pFilter)
07310 {
07311 #ifdef DO_EXPORT
07312     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
07313 
07314     if (GetNodeBlendPath() == NULL)
07315         return NodeRenderableInk::WriteEndChildRecordsNative(pFilter);
07316 
07317     return pFilter->WriteZeroSizedRecord(TAG_UP);
07318 #else
07319     return FALSE;
07320 #endif
07321 }
07322 
07323 /********************************************************************************************
07324 
07325 >   BOOL NodeBlender::WriteBeginChildRecordsWeb(BaseCamelotFilter* pFilter)
07326 
07327     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07328     Created:    11/5/99
07329     Inputs:     pFilter = ptr to the filter to write to
07330     Outputs:    -
07331     Returns:    TRUE ok, FALSE otherwise
07332     Purpose:    Same as the Native version
07333 
07334     SeeAlso:    WritePreChildrenNative()
07335 
07336 ********************************************************************************************/
07337 
07338 BOOL NodeBlender::WriteBeginChildRecordsWeb(BaseCamelotFilter* pFilter)
07339 {
07340     return WriteBeginChildRecordsNative(pFilter);
07341 }
07342 
07343 /********************************************************************************************
07344 
07345 >   BOOL NodeBlender::WriteEndChildRecordsWeb(BaseCamelotFilter* pFilter)
07346 
07347     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07348     Created:    11/5/99
07349     Inputs:     pFilter = ptr to the filter to write to
07350     Outputs:    -
07351     Returns:    TRUE ok, FALSE otherwise
07352     Purpose:    Same as the Native version
07353 
07354     SeeAlso:    WritePreChildrenNative()
07355 
07356 ********************************************************************************************/
07357 
07358 BOOL NodeBlender::WriteEndChildRecordsWeb(BaseCamelotFilter* pFilter)
07359 {
07360     return WriteEndChildRecordsNative(pFilter);
07361 }
07362 
07363 //--------------------------------------------------------------
07364 //--------------------------------------------------------------
07365 //--------------------------------------------------------------
07366 
07367 /********************************************************************************************
07368 
07369 >   NodeBlend* NodeBlender::GetNodeBlend()
07370 
07371     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07372     Created:    21/5/99
07373     Inputs:     -
07374     Returns:    ptr to the blend that this blender belongs to, or NULL if it's all gone pear shaped
07375     Purpose:    Central NodeBlend "getter"
07376     SeeAlso:    -
07377 
07378 ********************************************************************************************/
07379 
07380 NodeBlend* NodeBlender::GetNodeBlend()
07381 {
07382     Node* pNode = FindParent();
07383     if (pNode != NULL && IS_A(pNode,NodeBlend))
07384         return (NodeBlend*)pNode;
07385 
07386     return NULL;
07387 }
07388 
07389 /********************************************************************************************
07390 
07391 >   NodeBlendPath* NodeBlender::GetNodeBlendPath()
07392 
07393     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07394     Created:    30/4/99
07395     Inputs:     -
07396     Returns:    ptr to the blend path for this blender, or NULL if it fails
07397     Purpose:    It gets the node blend path from its parent
07398     SeeAlso:    -
07399 
07400 ********************************************************************************************/
07401 
07402 NodeBlendPath* NodeBlender::GetNodeBlendPath()
07403 {
07404     if (m_NodeBlendPathIndex > -1)
07405     {
07406         NodeBlend* pNode = GetNodeBlend();
07407         if (pNode != NULL)
07408             return pNode->GetNodeBlendPath(m_NodeBlendPathIndex);
07409     }
07410     return NULL;
07411 }
07412 
07413 /********************************************************************************************
07414 
07415 >   BOOL NodeBlender::GetPointOnNodeBlendPath(DocCoord* pPoint,double BlendRatio,double* pAngle)
07416 
07417     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
07418     Created:    4/5/99
07419     Inputs:     BlendRatio = the blend ratio to use
07420     Outputs:    pPoint = ptr to store coord
07421                 pAngle = angle at this point in degrees (ptr can be NULL)
07422     Returns:    TRUE if it could find the point on the NodeBlendPath
07423                 FALSE if there's no path to blend along, or the point-on-path function fails
07424     Purpose:    Access function to make it easy to get the correct point in the path
07425                 for this NodeBlender with the given blend ratio
07426     SeeAlso:    -
07427 
07428 ********************************************************************************************/
07429 
07430 BOOL NodeBlender::GetPointOnNodeBlendPath(double BlendRatio,DocCoord* pPoint,double* pAngle)
07431 {
07432     ERROR2IF(pPoint == NULL,FALSE,"NULL coord ptr");
07433 
07434     BOOL ok = FALSE;
07435 
07436     NodeBlendPath* pNodeBlendPath = GetNodeBlendPath();
07437 
07438     if (pNodeBlendPath)
07439     {
07440         double PropStart = GetProportionOfPathDistStart();
07441         double PropEnd   = GetProportionOfPathDistEnd();
07442 
07443         double PathLen       = pNodeBlendPath->GetPathLength();
07444         double PropLen       = PropEnd-PropStart;
07445         double PathLenOffset = PathLen*PropStart;
07446 
07447         double Tangent = 0.0;
07448         MILLIPOINT Dist = MILLIPOINT(((PathLen*PropLen) * BlendRatio)+PathLenOffset);
07449         ok = pNodeBlendPath->GetPointAtDistance(Dist,pPoint,&Tangent);
07450 
07451         if (ok && pAngle)
07452         {
07453             if (IsTangential())
07454                 *pAngle = Tangent/PI*180;
07455             else
07456                 *pAngle = 0.0;
07457         }
07458     }
07459 
07460     return ok;
07461 }
07462 
07463 
07464 /********************************************************************************************
07465 
07466 >   BOOL NodeBlender::GetPointFromDistance(double Distance, DocCoord8 pPoint, double* pAngle)
07467 
07468     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
07469     Created:    17/9/99
07470     Inputs:     Distance - the distance along the nodeblendpath 
07471     Outputs:    pPoint = ptr to store coord
07472                 pAngle = angle at this point in degrees (ptr can be NULL)
07473     Returns:    TRUE if it could find the point on the NodeBlendPath
07474                 FALSE if there's no path to blend along, or the point-on-path function fails
07475     Purpose:    Access function to make it easy to get the correct point in the path
07476                 for this NodeBlender with the given distance along the path.
07477     SeeAlso:    -
07478 
07479 ********************************************************************************************/
07480 
07481 BOOL NodeBlender::GetPointFromDistance(double Distance, DocCoord* pPoint, double* pAngle)
07482 {
07483     ERROR2IF(pPoint == NULL,FALSE,"NULL coord ptr");
07484 
07485     BOOL ok = FALSE;
07486 
07487     NodeBlendPath* pNodeBlendPath = GetNodeBlendPath();
07488 
07489     if (pNodeBlendPath)
07490     {
07491         double Tangent = 0.0;
07492         ok = pNodeBlendPath->GetPointAtDistance((INT32)Distance,pPoint,&Tangent);
07493 
07494         if (ok && pAngle)
07495         {
07496             if (IsTangential())
07497                 *pAngle = Tangent/PI*180;
07498             else
07499                 *pAngle = 0.0;
07500         }
07501     }
07502 
07503     return ok;
07504 }
07505 
07506 
07507 
07508 
07509 double NodeBlender::GetProportionOfPathDistStart()
07510 { 
07511     if (m_ProportionOfPathDistStart < 0.0) return 0.0;
07512     return m_ProportionOfPathDistStart;
07513 }
07514 
07515 double NodeBlender::GetProportionOfPathDistEnd()
07516 { 
07517     if (m_ProportionOfPathDistEnd < 0.0) return 1.0;
07518     return m_ProportionOfPathDistEnd;
07519 }
07520 
07521 void NodeBlender::SetProportionOfPathDistStart(double p)
07522 { 
07523     if (p < 0.0) p = 0.0;
07524     if (p > 1.0) p = 1.0;
07525     if (p > GetProportionOfPathDistEnd()) p = GetProportionOfPathDistEnd();
07526     m_ProportionOfPathDistStart = p;
07527 }
07528 
07529 void NodeBlender::SetProportionOfPathDistEnd(double p)
07530 { 
07531     if (p < 0.0) p = 0.0;
07532     if (p > 1.0) p = 1.0;
07533     if (p < GetProportionOfPathDistStart()) p = GetProportionOfPathDistStart();
07534     m_ProportionOfPathDistEnd = p;
07535 }
07536 
07537 
07538 double  NodeBlender::GetAngleStart()
07539 {
07540     return m_AngleStart;
07541 }
07542 
07543 double  NodeBlender::GetAngleEnd()
07544 {
07545     return m_AngleEnd;
07546 }
07547 
07548 void NodeBlender::SetAngleStart(double Angle)
07549 {
07550     m_AngleStart = fmod(Angle,360.0);
07551 }
07552 
07553 void NodeBlender::SetAngleEnd(double Angle)
07554 {
07555     m_AngleEnd = fmod(Angle,360.0);
07556 }
07557 
07558 
07559 
07560 /********************************************************************************************
07561 
07562 >   double NodeBlender::GetBlendDistance()
07563 
07564     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
07565     Created:    26/9/99
07566     Inputs:     -
07567     Returns:    distance between the centre of the start and end nodes
07568     Purpose:    to get the distance between the start and end nodes of the blender 
07569                 when the blend is not on a curve.  Note that it will return an incorrect
07570                 value if the blend is on a curve.
07571     SeeAlso:    NodeBlendPath::GetPathLength();
07572 
07573 ********************************************************************************************/
07574 
07575 double NodeBlender::GetLinearDistance()
07576 {
07577     DocCoord StartCentre;
07578     DocCoord EndCentre;
07579 
07580     BOOL Valid = GetBlendObjectCentres(&StartCentre, &EndCentre);
07581     if (!Valid)
07582         return FALSE;
07583         
07584     Coord pStart = (Coord)StartCentre;
07585     Coord pEnd = (Coord)EndCentre;
07586 
07587     double Distance = pStart.Distance(pEnd);
07588         
07589     return Distance;
07590         
07591 }
07592 
07593 
07594 /********************************************************************************************
07595 
07596 >   BOOL NodeBlender::GetBlendDirection(double* Gradient)
07597 
07598     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
07599     Created:    10/9/99
07600     Inputs:     -
07601     Outputs:    Gradient of the line that the blend follows
07602     Returns:    TRUE if successful, FALSE otherwise
07603     Purpose:    To get a vector of the direction of a linear blend.
07604 
07605 ********************************************************************************************/
07606 
07607 BOOL NodeBlender::GetBlendDirection(double* Gradient)
07608 {
07609     if (IsBlendedOnCurve())
07610     {
07611         ERROR3("Trying to get linear direction of blend on a curve");
07612         return FALSE;
07613     }
07614 
07615     DocCoord StartCentre;
07616     DocCoord EndCentre;
07617 
07618     BOOL Valid = GetBlendObjectCentres(&StartCentre, &EndCentre);
07619     if (!Valid)
07620         return FALSE;
07621 
07622     double XDiff = EndCentre.x - StartCentre.x;
07623     double YDiff = EndCentre.y - StartCentre.y;
07624 
07625     *Gradient = YDiff/XDiff;
07626 
07627     return TRUE;
07628 }
07629 
07630 
07631 /********************************************************************************************
07632 
07633 >   BOOL NodeBlender::GetBlendObjectCentres(DocCoord* pFirstCentre, DocCoord* pLastCentre)
07634 
07635     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
07636     Created:    10/9/99
07637     Inputs:     -
07638     Outputs:    Coordinates of the centres of the first and last objects of this blender
07639     Returns:    TRUE if successful, FALSE otherwise
07640     Purpose:    To get the centres of the objects that are blended between
07641 
07642 ********************************************************************************************/
07643 
07644 BOOL NodeBlender::GetBlendObjectCentres(DocCoord* pFirstCentre, DocCoord* pEndCentre)
07645 {
07646     // get the start and end nodes
07647     NodeRenderableInk* pBlendStart =  GetNodeStart();
07648     NodeRenderableInk* pBlendEnd = GetNodeEnd();
07649 
07650     if (pBlendStart ==NULL || pBlendEnd == NULL)
07651     {
07652         ERROR3IF(pBlendStart ==NULL, "Blender has no start nodes");
07653         ERROR3IF(pBlendEnd ==NULL, "Blender has no end nodes");
07654         return FALSE;
07655     }
07656 
07657     DocRect StartRect = pBlendStart->GetBoundingRect();
07658     DocRect EndRect = pBlendEnd->GetBoundingRect();
07659 
07660     DocCoord StartCentre = StartRect.Centre();
07661     DocCoord EndCentre = EndRect.Centre();
07662 
07663     *pFirstCentre = StartCentre;
07664     *pEndCentre = EndCentre;
07665 
07666     return TRUE;
07667 }

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