nodecntr.cpp

Go to the documentation of this file.
00001 // $Id: nodecntr.cpp 1361 2006-06-25 16:43:38Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 #include "camtypes.h"
00100 //#include "nodecomp.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00101 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00102 
00103 // Save/load
00104 //#include "cxfrec.h"       // CXaraFileRecord - in camtypes.h [AUTOMATICALLY REMOVED]
00105 
00106 #include "cxftags.h"
00107 
00108 #include "nodecntr.h"
00109 #include "ncntrcnt.h"
00110 //#include "cntres.h"
00111 #include "gdraw.h"
00112 #include "gclips.h"
00113 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "lineattr.h"
00115 #include "blobs.h"
00116 #include "objchge.h"
00117 #include "opbevel.h"
00118 #include "ophist.h"
00119 #include "nodeblnd.h"
00120 #include "ndbldpth.h"
00121 #include "csrstack.h"
00122 #include "ppbevel.h"
00123 #include "opcntr.h"
00124 #include "nodetxts.h"
00125 #include "moldtool.h"
00126 #include "extender.h"
00127 //#include "mario.h"
00128 #include "rsmooth.h"
00129 #include "fitcurve.h"
00130 #include "contmenu.h"
00131 #include "blndhelp.h"
00132 #include "brshattr.h"
00133 #include "pbecomea.h"
00134 #include "ppstroke.h"
00135 #include "attrmap.h"
00136 
00137 #include "gclip.h"
00138 
00139 #include "ngcore.h"     // NameGallery, for stretching functionality
00140 
00141 CC_IMPLEMENT_DYNCREATE(NodeContour, NodeRenderableInk)
00142 
00143 // Declare smart memory handling in Debug builds
00144 #define new CAM_DEBUG_NEW
00145 
00146 #define NEWVERSION
00147 
00148 /**********************************************************************************************/
00149 
00150 const UINT32 ClipPathToPath_Tolerance   =  30 ;
00151 const UINT32 ClipPathToPath_Flatness    = 200 ;
00152 const UINT32 StrokePathToPath_Flatness  = 200 ;
00153 
00154 /***********************************************************************************************
00155 
00156 >   NodeContour::NodeContour()
00157 
00158     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00159     Created:    12/8/99
00160     Purpose:    Constructor
00161 
00162 ***********************************************************************************************/
00163 
00164 NodeContour::NodeContour()
00165 {
00166     m_pPathList = NULL;
00167     m_NumPaths = 0;
00168     m_SourcePath.Initialise();
00169 
00170     IsBoundingRectValid = FALSE;
00171     m_FirstRender = TRUE;
00172 }
00173 
00174 /***********************************************************************************************
00175 
00176 >   NodeContour::NodeContour(Node* ContextNode,  
00177                 AttachNodeDirection Direction,  
00178                 BOOL Locked = FALSE, 
00179                 BOOL Mangled = FALSE,  
00180                 BOOL Marked = FALSE, 
00181                 BOOL Selected = FALSE    
00182                 ) ;
00183 
00184 
00185     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00186     Created:    12/8/99
00187     Purpose:    Constructor
00188 
00189 ***********************************************************************************************/
00190 NodeContour::NodeContour(Node* ContextNode,  
00191                 AttachNodeDirection Direction,  
00192                 BOOL Locked, 
00193                 BOOL Mangled,  
00194                 BOOL Marked, 
00195                 BOOL Selected    
00196                 ) 
00197 {
00198     m_pPathList = NULL;
00199     m_NumPaths = 0;
00200     m_SourcePath.Initialise();
00201     IsBoundingRectValid = FALSE;
00202     m_FirstRender = TRUE;
00203 }
00204 
00205 /***********************************************************************************************
00206 
00207 >   NodeContour::~NodeContour()
00208 
00209     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00210     Created:    12/8/99
00211     Purpose:    Destructor
00212 
00213 ***********************************************************************************************/
00214 
00215 NodeContour::~NodeContour()
00216 {
00217     if (m_pPathList)
00218     {
00219         delete [] m_pPathList;
00220         m_pPathList = NULL;
00221     }
00222 }
00223 
00224 /***********************************************************************************************
00225 
00226 >   DocRect NodeContour::GetBoundingRect(BOOL DontUseAttrs = FALSE, BOOL HitTest = FALSE)
00227 
00228     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00229     Created:    12/8/99
00230     Inputs:     See base class
00231     Purpose:    Get the bounding rect of this node
00232 
00233 ***********************************************************************************************/
00234 DocRect NodeContour::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
00235 {
00236     // sum all the bounding rects & then add the width
00237     if (!(!IsBoundingRectValid || DontUseAttrs))
00238     {   
00239         return BoundingRectangle;
00240     }   
00241 
00242     DocRect dr;
00243 
00244     NodeContourController * pController = (NodeContourController *)FindParent();
00245 
00246     if (!pController)
00247         return DocRect(0,0,0,0);
00248 
00249     INT32 Width = pController->GetWidth();
00250 
00251     Node * pNode = FindParent();
00252 
00253     if (!pNode)
00254         return BoundingRectangle;
00255 
00256     pNode = pNode->FindFirstChild();
00257     
00258     while (pNode)
00259     {
00260         if (pNode->IsAnObject() && pNode!=this)
00261         {
00262             if (!IsBoundingRectValid)
00263             {
00264                 ((NodeRenderableInk *)pNode)->InvalidateBoundingRect();
00265             }
00266             
00267             dr = dr.Union(((NodeRenderableInk *)pNode)->GetBoundingRect(TRUE, HitTest));
00268         }
00269         
00270         pNode = pNode->FindNext();
00271     }
00272     
00273     if (Width < 0)
00274     {
00275         dr.lo.x += Width;
00276         dr.lo.y += Width;
00277         dr.hi.x -= Width;
00278         dr.hi.y -= Width;
00279     }
00280 
00281     DocRect PathRect;
00282 
00283     // include all the paths in the bounding rect
00284     for (INT32 i = 0 ; i < m_NumPaths && m_pPathList; i++)
00285     {
00286         PathRect = m_pPathList[i].GetBoundingRect();
00287 
00288         if (PathRect.IsValid() && !PathRect.IsEmpty())
00289         {
00290             dr = dr.Union(PathRect);
00291         }
00292     }
00293 
00294     // take the bounding rect from the paths, if they're present
00295     if (Width < 0)
00296     {
00297         if (m_NumPaths != 0)
00298         {
00299             dr = (m_pPathList[m_NumPaths-1].GetBoundingRect());
00300         }
00301     }
00302     else
00303     {
00304         if (m_NumPaths != 0)
00305         {
00306             dr = (m_pPathList[0].GetBoundingRect());
00307         }
00308     }
00309 
00310     if (!dr.IsValid() || dr.IsEmpty())
00311     {
00312         dr = ((NodeCompound *)FindParent())->GetInsideBoundingRect();
00313 
00314         // find the centre point
00315         DocCoord Centre = dr.Centre();
00316 
00317         dr.lo.x = Centre.x - 5;
00318         dr.hi.x = Centre.x + 5;
00319         dr.lo.y = Centre.y - 5;
00320         dr.hi.y = Centre.y + 5;
00321     }
00322     
00323     // find out the line width and adjust the bounding rectangle appropriately
00324     AttrLineWidth * pAttr = NULL;
00325     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), (NodeAttribute **)(&pAttr));
00326 
00327     AttrStrokeTransp * pStrokeAttr = NULL;
00328     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeTransp), (NodeAttribute **)(&pStrokeAttr));
00329 
00330     AttrStrokeColour * pStrokeColour = NULL;
00331     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), (NodeAttribute **)(&pStrokeColour));
00332 
00333     BOOL bTransparent = FALSE;
00334 
00335     if (pStrokeAttr)
00336     {
00337         if (pStrokeAttr->Value.Transp == 255)
00338         {
00339             bTransparent = TRUE;
00340         }
00341     }
00342 
00343     if (pStrokeColour)
00344     {
00345         if (pStrokeColour->Value.Colour == DocColour(COLOUR_NONE))
00346         {
00347             bTransparent = TRUE;
00348         }
00349     }
00350 
00351     if (pAttr && !bTransparent)
00352     {
00353         MILLIPOINT LineWidth = pAttr->Value.LineWidth / 2;
00354 
00355         dr.lo.x -= LineWidth;
00356         dr.lo.y -= LineWidth;
00357         dr.hi.x += LineWidth;
00358         dr.hi.y += LineWidth;
00359     }
00360 
00361     dr.Union(pController->GetInsideBoundingRect());
00362 
00363     IsBoundingRectValid = TRUE;
00364     BoundingRectangle = dr;
00365 
00366     return dr;
00367 }
00368 
00369 /***********************************************************************************************
00370 
00371 >   DocRect NodeContour::GetBlobBoundingRect()
00372 
00373     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00374     Created:    12/8/99
00375     Inputs:     See base class
00376     Purpose:    Get the blob bounding rect of this node
00377 
00378 ***********************************************************************************************/
00379 DocRect NodeContour::GetBlobBoundingRect()
00380 {
00381     DocRect dr = GetBoundingRect(FALSE, FALSE);
00382 
00383     BlobManager * pMgr = GetApplication()->GetBlobManager();
00384 
00385     INT32 Size = 0;
00386 
00387     if (pMgr)
00388         Size = pMgr->GetBlobSize();
00389 
00390     dr.lo.x -= Size*2;
00391     dr.lo.y -= Size*2;
00392     dr.hi.x += Size*2;
00393     dr.hi.y += Size*2;
00394 
00395     IncludeChildrensBoundingRects(&dr);
00396 
00397     return dr;
00398 }
00399 
00400 /***********************************************************************************************
00401 
00402 >   void NodeContour::Render(RenderRegion* pRender)
00403 
00404     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00405     Created:    12/8/99
00406     Inputs:     See base class
00407     Purpose:    Renders this node
00408 
00409 ***********************************************************************************************/
00410 void NodeContour::Render(RenderRegion* pRender)
00411 {
00412     double Ratio = 0;
00413     double StepSize = 0;
00414 
00415     NodeContourController * pControl = (NodeContourController *)FindParent();
00416 
00417     // check we have something to render !
00418     if (pControl)
00419     {
00420         pControl->DisableInsetPathPathProcessor();
00421 
00422         if (pControl->GetInsideBoundingRect().IsEmpty())
00423             return;
00424     }
00425     else
00426         return;
00427 
00428     if(m_FirstRender)
00429     {
00430         GenerateContour();
00431         m_FirstRender = FALSE;
00432     }
00433 
00434     // get the first path processor
00435     ContourNodePathProcessor * pProc = (ContourNodePathProcessor *)pRender->GetFirstPathProcessor();
00436 
00437     if (pProc)
00438     {
00439         if (!((PathProcessor *)pProc)->IS_KIND_OF(ContourNodePathProcessor))
00440             pProc = NULL;
00441     }
00442 
00443     // do we have anything to render ??
00444     if (m_NumPaths == 0)
00445         return;
00446 
00447     pRender->SaveContext();
00448 
00449     // find out the line width
00450     LineWidthAttribute * pLineWidth = (LineWidthAttribute *)pRender->GetCurrentAttribute(ATTR_LINEWIDTH);
00451 
00452     TRACEUSER( "DaivdM", _T("Contour node render 1\n"));
00453 
00454     MILLIPOINT LineWidth = 0;
00455 
00456     if (pLineWidth)
00457         LineWidth = pLineWidth->LineWidth;
00458 
00459     if (m_pPathList != NULL && m_NumPaths > 0)
00460     {
00461         // do the blended attributes map
00462         CCAttrMap BlendAttrMap(30); 
00463 
00464         // build the attribute maps for the start & end nodes
00465         NodeRenderableInk * pEndNode = NULL;
00466         
00467         if (pControl->GetWidth() > 0)
00468             pEndNode = (NodeRenderableInk *)FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk));
00469         else
00470             pEndNode = (NodeRenderableInk *)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
00471 
00472         if (!pEndNode)
00473             return;
00474 
00475         if (pEndNode->IsCompound())
00476             pEndNode = FindNodeToContour(pEndNode);
00477 
00478         if (pEndNode->IS_KIND_OF(NodeBlend))
00479             pEndNode = (NodeRenderableInk *)pEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
00480 
00481         // do the blend of the attributes
00482         CCAttrMap * pAttrMapStart = CCAttrMap::MakeAttrMapFromRenderRegion(pRender);
00483         WindingRuleAttribute * pWinding = NULL;
00484 
00485         if(!pAttrMapStart)
00486         {
00487             ENSURE(FALSE,"Not enough mem to create start attr map for rendering NodeContour.");
00488             return;
00489         }
00490         CCAttrMap * pAttrMapEnd   = CCAttrMap::MakeAppliedAttrMap(pEndNode);
00491 
00492         StepSize = 1.0 / m_NumPaths;
00493         
00494         for (INT32 i = m_NumPaths - 1; i >= 0; i--)
00495         {
00496             TRACEUSER( "Diccon", _T("Rendering contour path %d\n"), i);
00497             if (pControl->GetWidth() < 0)
00498             {
00499                 // outer contour
00500                 TRACEUSER( "DaivdM", _T("Contour node render %d\n"), i);
00501     
00502                 if (i == m_NumPaths - 1)
00503                 {
00504                     if (pProc)
00505                         pProc->SetActive(FALSE);
00506                     
00507                     pRender->DrawPath(&(m_pPathList[i]));
00508     
00509                 }
00510                 else
00511                 {
00512                     if (pProc)
00513                         pProc->SetActive(TRUE);
00514                     
00515                     Ratio = (StepSize * i) + StepSize;
00516 
00517                     // calculate the correct ratio given the profile for attributes
00518                     CProfileBiasGain Profile = pControl->GetAttrProfile();
00519                     Profile.SetBias(-Profile.GetBias());
00520                     Ratio = (double)Profile.MapZeroToOne((AFp)Ratio);
00521                     
00522                     Ratio = 1.0 - Ratio;
00523                     
00524                     if (!BlendAttributes(pRender, pAttrMapStart, pAttrMapEnd, &BlendAttrMap, 
00525                         Ratio))
00526                     {
00527                         ERROR3("Blend attributes failed");
00528     
00529                         // pop the path processor first
00530                         pRender->PopPathProcessor();                        
00531                         return;
00532 
00533                     }
00534 
00535                     // Ilan 5/5/00
00536                     // Call the PostDynCreateInit functions for the attributes which require this
00537                     // NB attrs which don't succeed in init'ing will mark themselves so that they don't NeedToRender()
00538                     // ie the AttrMap can safely be rendered - the united attrs simply won't do their job
00539                     BlendAttrMap.PostBlendInit(&(m_pPathList[i]), GetRuntimeClass());
00540 
00541                     pRender->SaveContext();
00542                     BlendAttrMap.Render(pRender);
00543 
00544                     // Quality Max(Quality::QualityDefault);
00545                     // QualityAttribute * pAttr = new QualityAttribute(Max);
00546                     // pRender->SetQuality(pAttr, FALSE);   
00547                     
00548                     PathProcessor* pPP = pRender->GetFirstPathProcessor();
00549                     if (pPP && pPP->IsAPathProcessorStroke())
00550                         pPP->SetDisabled(TRUE);
00551 
00552                     pRender->DrawPath(&(m_pPathList[i]));
00553                     
00554                     if (pPP)
00555                         pPP->SetDisabled(FALSE);
00556 
00557                     pRender->RestoreContext();
00558 
00559                     // Ilan 5/5/00
00560                     // Deinitialise the attrs which allocated memory in the PostBlendInit() fn call
00561                     BlendAttrMap.PostBlendDeinit();
00562                 }
00563             }
00564             else
00565             {
00566                 if (pProc)
00567                     pProc->SetActive(FALSE);
00568                     
00569                 Ratio = StepSize * i;
00570                 
00571                 // calculate the correct ratio given the profile for attributes
00572                 CProfileBiasGain Profile = pControl->GetAttrProfile();
00573                 Profile.SetBias(-Profile.GetBias());
00574                 Ratio = (double)Profile.MapZeroToOne((AFp)Ratio);
00575                 
00576                 if (Ratio > 0 && Ratio < 1.0)
00577                 {
00578                     if (!BlendAttributes(pRender, pAttrMapStart, pAttrMapEnd, &BlendAttrMap, 
00579                         Ratio))
00580                     {
00581                         ERROR3("Blend attributes failed");
00582                         // pop the path processor first
00583                         pRender->PopPathProcessor();                
00584                         return;
00585                     }
00586                     pRender->SaveContext();
00587                     // Ilan 5/5/00
00588                     // Call the PostDynCreateInit functions for the attributes which require this
00589                     // NB attrs which don't succeed in init'ing will mark themselves so that they don't NeedToRender()
00590                     // ie the AttrMap can safely be rendered - the united attrs simply won't do their job
00591                     BlendAttrMap.PostBlendInit(&(m_pPathList[(m_NumPaths-1) - i]), GetRuntimeClass());
00592 
00593                     BlendAttrMap.Render(pRender);
00594                 }
00595                 else if (Ratio == 0)
00596                 {
00597                     pRender->SaveContext();
00598                     pAttrMapStart->Render(pRender);
00599                 }
00600                 else
00601                 {
00602                     pRender->SaveContext();
00603                     pAttrMapEnd->Render(pRender);
00604                 }
00605                         
00606                 // set the winding rule
00607                 pWinding = (WindingRuleAttribute *)pRender->GetCurrentAttribute(ATTR_WINDINGRULE);
00608                 
00609                 pWinding->WindingRule = NegativeWinding;
00610             
00611                 pRender->DrawPath(&(m_pPathList[(m_NumPaths-1) - i]));
00612                 pRender->RestoreContext();
00613 
00614                 if (Ratio > 0 && Ratio < 1.0)
00615                 {
00616                     // Ilan 5/5/00
00617                     // Deinitialise the attrs which allocated memory in the PostBlendInit() fn call
00618                     BlendAttrMap.PostBlendDeinit();
00619                 }
00620             }
00621         }
00622 
00623         pAttrMapStart->DeleteAttributes();
00624         delete pAttrMapStart;
00625         delete pAttrMapEnd;
00626         
00627         BlendAttrMap.DeleteAttributes();
00628     }
00629     
00630     pRender->RestoreContext();
00631 
00632     // pop the path processor
00633     if (pProc)
00634         pRender->PopPathProcessor();                        
00635     
00636 
00637     pControl->EnableInsetPathPathProcessor();
00638 }
00639 
00640 /***********************************************************************************************
00641 
00642 >   NodeRenderableInk * NodeContour::FindNodeToContour(NodeRenderableInk * pInk)
00643 
00644     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00645     Created:    10/3/2000
00646     Inputs:     The node to start searching from
00647     Purpose:    Does a depth first seach to find a node to take the attribute
00648                 map from when blending
00649 
00650 ***********************************************************************************************/
00651 NodeRenderableInk * NodeContour::FindNodeToContour(NodeRenderableInk * pInk)
00652 {
00653     if (!pInk->IsCompound())
00654         return pInk;
00655 
00656     Node * pNodeStep = pInk->FindFirstDepthFirst();
00657 
00658     AttrFillGeometry * pFill = NULL;
00659 
00660     while (pNodeStep && pNodeStep != pInk)
00661     {
00662         if (pNodeStep->IsAnObject())
00663         {
00664             if (!pNodeStep->NeedsParent(NULL))
00665             {
00666                 // does this have a fill attribute ?
00667                 if (((NodeRenderableInk *)pNodeStep)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry),
00668                     (NodeAttribute **)(&pFill)))
00669                 {               
00670                     // let's check this to see if there's a colour on the node or not
00671                     if (pFill->GetStartColour())
00672                     {
00673                         if (!pFill->GetStartColour()->IsTransparent())
00674                             return (NodeRenderableInk *)pNodeStep;
00675                     }
00676                 }
00677             }
00678         }
00679 
00680         pNodeStep = pNodeStep->FindNextDepthFirst(pInk);
00681     }
00682 
00683     return pInk;
00684 }
00685 
00686 /***********************************************************************************************
00687 
00688 >   BOOL NodeContour::DoBecomeA(BecomeA* pBecomeA)
00689 
00690     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00691     Created:    12/8/99
00692     Inputs:     See base class
00693     Purpose:    Turns this node into another node of a particular type
00694 
00695 ***********************************************************************************************/
00696 BOOL NodeContour::DoBecomeA(BecomeA* pBecomeA)
00697 {
00698 // Check for a NULL entry param                            
00699     ERROR2IF_PF(pBecomeA == NULL,FALSE,("pBecomeA is NULL"));
00700 
00701     if (!pBecomeA->BAPath())
00702     {
00703         return FALSE;
00704     }
00705 
00706     BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK);
00707     ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason()));
00708 
00709     UndoableOperation* pOp = pBecomeA->GetUndoOp();
00710 
00711     double StepSize = 0;
00712     double Ratio = 0;
00713 
00714     NodeContourController * pController = (NodeContourController *)FindParent();
00715 
00716     if (!pController)
00717     {
00718         ERROR3("No contour controller for become a");
00719         return FALSE;
00720     }
00721 
00722     INT32 Width = pController->GetWidth();
00723 
00724 //  Node * pContourBegin = NULL;
00725 
00726     BOOL bPassback = FALSE;
00727 
00728     if (pBecomeA->GetReason() == BECOMEA_PASSBACK)
00729         bPassback = TRUE;
00730 
00731 //  CCAttrMap * pTmpMap = NULL;
00732 
00733     // turn each path into a node path with the appropriate attributes
00734     if (m_pPathList != NULL && m_NumPaths > 0)
00735     {
00736         // do the blended attributes map
00737         CCAttrMap BlendAttrMap(30); 
00738 
00739         // build the attribute maps for the start & end nodes
00740         NodeRenderableInk * pEndNode = NULL;
00741 
00742         if (pController->GetWidth() > 0)
00743         {
00744             pEndNode = (NodeRenderableInk *)FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk));
00745         }
00746         else
00747         {
00748             pEndNode = (NodeRenderableInk *)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
00749         }
00750 
00751         if (!pEndNode)
00752         {
00753             ERROR3("No node present to contour");
00754             return FALSE;
00755         }
00756 
00757         // CGS:  need to check whether pEndNode is compound - and if it is, do some extra stuff
00758 
00759         if (pEndNode->IsCompound())
00760         {
00761             pEndNode = FindNodeToContour(pEndNode);
00762         }
00763 
00764         if (pEndNode->IS_KIND_OF(NodeBlend))
00765         {
00766             pEndNode = (NodeRenderableInk *)pEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
00767         }
00768         
00769         // do the blend of the attributes
00770         CCAttrMap * pAttrMapStart = CCAttrMap::MakeAppliedAttrMap(this);
00771         CCAttrMap * pAttrMapEnd   = CCAttrMap::MakeAppliedAttrMap(pEndNode);
00772 
00773         ERROR3IF (pAttrMapStart == NULL, "pAttrMapStart is NULL!");
00774         ERROR3IF (pAttrMapEnd == NULL, "pAttrMapEnd is NULL!");
00775         
00776         StepSize = 1.0 / ((double)(m_NumPaths));
00777         
00778         NodePath* pPath = NULL;
00779         Node* pInsertContext = this;
00780 
00781         // For DoBecomeA we must return the paths in the order in which they will be placed
00782         // in the tree and rendered
00783         // For Outer contours that means: end node, steps, original node
00784         // For Inner contours that means: original node, steps, end node
00785         //
00786         if (Width < 0)
00787         {
00788             // outer contour !
00789             // 
00790             // End node first
00791             INT32 i = m_NumPaths-1;
00792 
00793             if (pOp)
00794             {
00795                 ALLOC_WITH_FAIL(pPath, new NodePath, pOp);
00796             }
00797             else
00798             {
00799                 pPath = new NodePath;
00800 
00801                 ERRORIF(pPath == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00802             }
00803 
00804             pPath->InkPath.Initialise();
00805             pPath->InkPath.CloneFrom(m_pPathList[i]);
00806             pPath->InkPath.IsFilled = TRUE;
00807             pPath->InkPath.InitialiseFlags(0, pPath->InkPath.GetNumCoords());
00808             
00809             // insert the node
00810             if (!bPassback)
00811             {
00812                 pPath->ApplyAttributes(pAttrMapStart, TRUE);
00813 
00814                 if (pOp)
00815                     pOp->DoInsertNewNode(pPath, pInsertContext, NEXT, TRUE, FALSE, TRUE, TRUE);
00816                 else
00817                 {
00818                     pPath->AttachNode(pInsertContext, NEXT, FALSE, FALSE);
00819                     pPath->NormaliseAttributes();
00820                 }
00821                 pInsertContext = pPath;
00822                 pPath->SetSelected(TRUE);
00823 
00824                 pBecomeA->PassBack(pPath, this, pAttrMapStart->Copy());
00825             }
00826             else
00827             {
00828                 pBecomeA->PassBack(pPath, this, pAttrMapStart->Copy());
00829             }
00830 
00831             // Now all the steps
00832             for (i = m_NumPaths-2; i>=0; i--)
00833             {
00834                 // blend the attributes at this point
00835                 Ratio = (StepSize * ((double)i)) + StepSize;
00836                 
00837                 // calculate the correct ratio given the profile for attributes
00838                 CProfileBiasGain Profile = pController->GetAttrProfile();
00839                 Profile.SetBias(-Profile.GetBias());
00840                 Ratio = (double)Profile.MapZeroToOne((AFp)Ratio);
00841                 
00842                 Ratio = 1.0 - Ratio;
00843                 
00844                 if (!BlendAttributes(NULL, pAttrMapStart, pAttrMapEnd, &BlendAttrMap, 
00845                     Ratio))
00846                 {
00847                     ERROR3("Blend attributes failed");
00848                     return FALSE;
00849                 }
00850                 
00851                 if (pOp)
00852                 {
00853                     ALLOC_WITH_FAIL(pPath, new NodePath, pOp);
00854                 }
00855                 else
00856                 {
00857                     pPath = new NodePath;
00858 
00859                     ERRORIF(pPath == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00860                 }
00861 
00862                 pPath->InkPath.Initialise();
00863                 pPath->InkPath.CloneFrom(m_pPathList[i]);
00864 
00865                 pPath->InkPath.IsFilled = TRUE;
00866                 pPath->InkPath.InitialiseFlags(0, pPath->InkPath.GetNumCoords());
00867                 
00868                 // insert the node
00869                 if (!bPassback)
00870                 {
00871                     // copy the blended attributes to the new node
00872                     // CopyAttributes(pPath, &BlendAttrMap);
00873                     pPath->ApplyAttributes(&BlendAttrMap, TRUE);                        
00874 
00875                     if (pOp)
00876                         pOp->DoInsertNewNode(pPath, pInsertContext, NEXT, TRUE, FALSE, TRUE, TRUE);
00877                     else
00878                     {
00879                         pPath->AttachNode(pInsertContext, NEXT, FALSE, FALSE);
00880                         pPath->NormaliseAttributes();
00881                     }
00882 
00883                     pInsertContext = pPath;
00884                     pPath->SetSelected(TRUE);
00885 
00886                     pBecomeA->PassBack(pPath, this, BlendAttrMap.Copy());
00887                 }
00888                 else
00889                 {
00890                     pBecomeA->PassBack(pPath, this, BlendAttrMap.Copy());
00891                 }
00892             }
00893         }
00894         else
00895         {
00896             // inner contour !
00897             INT32 i = 0;
00898             for (i = m_NumPaths-1; i!=0 ; i--)
00899             {
00900                 // Check if the path has any points before outputing it
00901                 // This stops degenerate paths being added to the document
00902                 // which will cause errors in the saving code
00903                 Path* pStepPath = &(m_pPathList[(m_NumPaths - 1) - i]);
00904                 if (pStepPath && pStepPath->GetNumCoords() > 0)
00905                 {
00906                     // calculate the correct ratio given the profile for attributes
00907                     Ratio = (StepSize * ((double)i));
00908                     
00909                     // calculate the correct ratio given the profile for attributes
00910                     CProfileBiasGain Profile = pController->GetAttrProfile();
00911                     Profile.SetBias(-Profile.GetBias());
00912                     Ratio = (double)Profile.MapZeroToOne((AFp)Ratio);
00913                     
00914                     if (!BlendAttributes(NULL, pAttrMapStart, pAttrMapEnd, &BlendAttrMap, 
00915                         Ratio))
00916                     {
00917                         ERROR3("Blend attributes failed");
00918                         return FALSE;
00919                     }
00920                     
00921                     pPath = new NodePath;
00922 
00923                     pPath->InkPath.Initialise();
00924                     pPath->InkPath.CloneFrom(*pStepPath);
00925                     pPath->InkPath.IsFilled = TRUE;
00926                     pPath->InkPath.InitialiseFlags(0, pPath->InkPath.GetNumCoords());
00927                     
00928                     if (!bPassback)
00929                     {   
00930                         pPath->ApplyAttributes(&BlendAttrMap, TRUE);
00931                         if (pOp)
00932                             pOp->DoInsertNewNode(pPath, pInsertContext, NEXT, TRUE, FALSE, TRUE, TRUE);
00933                         else
00934                         {
00935                             pPath->AttachNode(pInsertContext, NEXT, FALSE, FALSE);
00936                             pPath->NormaliseAttributes();
00937                         }
00938 
00939                         pInsertContext = pPath;
00940                         pBecomeA->PassBack(pPath, pPath, BlendAttrMap.Copy());
00941                     }
00942                     else
00943                     {
00944                         pBecomeA->PassBack(pPath, pPath, BlendAttrMap.Copy());
00945                     }
00946                 }
00947             }
00948 
00949             // Finally, the end node
00950             Path* pStepPath = &(m_pPathList[m_NumPaths - 1]);
00951             if (pStepPath && pStepPath->GetNumCoords() > 0)
00952             {
00953                 pPath = new NodePath;
00954                 pPath->InkPath.Initialise();
00955 
00956                 pPath->InkPath.CloneFrom(*pStepPath);
00957                 pPath->InkPath.IsFilled = TRUE;
00958                 pPath->InkPath.InitialiseFlags(0, pPath->InkPath.GetNumCoords());
00959                 
00960                 // insert the node
00961                 if (!bPassback)
00962                 {
00963                     // CopyAttributes(pPath, pAttrMapStart);
00964                     pPath->ApplyAttributes(pAttrMapStart, TRUE);
00965                     if (pOp)
00966                         pOp->DoInsertNewNode(pPath, pInsertContext, NEXT, TRUE, FALSE, TRUE, TRUE);
00967                     else
00968                     {
00969                         pPath->AttachNode(pInsertContext, NEXT, FALSE, FALSE);
00970                         pPath->NormaliseAttributes();
00971                     }
00972 
00973                     pInsertContext = pPath;
00974 
00975                     pBecomeA->PassBack(pPath, pPath, pAttrMapStart->Copy());
00976                 }
00977                 else
00978                 {
00979                     pBecomeA->PassBack(pPath, pPath, pAttrMapStart->Copy());
00980                 }
00981             }
00982         }
00983 
00984         BlendAttrMap.DeleteAttributes();
00985 
00986         delete pAttrMapStart;
00987         delete pAttrMapEnd;
00988         
00989         // hide the contour begin node
00990         if (!bPassback)
00991         {
00992             if (pOp)
00993             {
00994                 NodeHidden * pHidden = NULL;
00995                 pOp->DoHideNode(this, TRUE, &pHidden, TRUE);
00996             }
00997             else
00998             {
00999                 CascadeDelete();
01000                 delete this;        // Scary!
01001             }
01002         }
01003         
01004         return TRUE;
01005     }
01006 
01007     return FALSE;
01008 }
01009 
01010 /***********************************************************************************************
01011 
01012 >   BOOL NodeContour::CopyAttributes(NodeRenderableInk * pNode, CCAttrMap * pAttrMap)
01013 
01014     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01015     Created:    12/8/99
01016     Inputs:     See base class
01017     Purpose:    Copies all the attributes in the attribute map onto the given node
01018 
01019 ***********************************************************************************************/
01020 BOOL NodeContour::CopyAttributes(NodeRenderableInk * pNode, CCAttrMap * pAttrMap)
01021 {
01022     ERROR2IF(pNode == NULL, FALSE, "Node is null");
01023     ERROR2IF(pAttrMap == NULL, FALSE, "Attribute map is null");
01024     
01025     CCAttrMap::iterator end = pAttrMap->GetEndPosition();
01026     CCAttrMap::iterator PosStart = pAttrMap->GetStartPosition();
01027 
01028     while( PosStart != end )
01029     {
01030         CCRuntimeClass *pTypeStart = NULL; 
01031         void           *pValStart = NULL; 
01032 
01033         pAttrMap->GetNextAssoc( PosStart, pTypeStart, pValStart );
01034         NodeAttribute* pNodeAttr = (NodeAttribute *)pValStart;
01035 
01036         // copy the attribute onto the node
01037         if (pNodeAttr)
01038         {
01039             if (pNodeAttr->CanBeAppliedToObject())
01040             {
01041                 if (!pNodeAttr->CopyNode(pNode, FIRSTCHILD))
01042                 {
01043                     ERROR3("Copy node failed");
01044                     return FALSE;
01045                 }
01046             }
01047         }
01048     }
01049 
01050     return TRUE;
01051 }
01052 
01053 
01054 
01055 
01056 
01057 
01058 
01059 
01060 
01061 /***********************************************************************************************
01062 
01063 >   BOOL NodeContour::CanBecomeA(BecomeA* pBecomeA)
01064 
01065     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01066     Created:    12/8/99
01067     Inputs:     See base class
01068     Purpose:    Can this node turn itself into a node of a particular type ?
01069 
01070 ***********************************************************************************************/
01071 BOOL NodeContour::CanBecomeA(BecomeA* pBecomeA)
01072 {
01073     if (pBecomeA->BAPath())
01074     {
01075         if (pBecomeA->IsCounting())
01076         {
01077             NodeContourController * pControl = (NodeContourController *)FindParent();
01078     
01079             if (pControl->GetInsetPathFlag())
01080             {
01081                 pBecomeA->AddCount(1);
01082             }
01083             else
01084             {
01085                 pBecomeA->AddCount(pControl->GetNumberOfSteps());
01086             }
01087         }
01088 
01089         return TRUE;
01090     }
01091 
01092     return FALSE;
01093 }
01094 
01095 /***********************************************************************************************
01096 
01097 >   virtual BOOL NodeContour::NeedsParent(Node* pClassNode) const;
01098 
01099     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01100     Created:    12/8/99
01101     Inputs:     See base class
01102     Purpose:    Does this node need its parent ?
01103 
01104 ***********************************************************************************************/
01105 BOOL NodeContour::NeedsParent(Node* pClassNode) const
01106 {
01107     if (!pClassNode)
01108         return TRUE;
01109 
01110     if (pClassNode->IsKindOf(CC_RUNTIME_CLASS(NodeContourController)))
01111         return TRUE;
01112 
01113     return FALSE;
01114 }
01115 
01116 /***********************************************************************************************
01117 
01118 >   String NodeContour::Describe(BOOL Plural, BOOL Verbose)
01119 
01120     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01121     Created:    12/8/99
01122     Inputs:     See base class
01123     Purpose:    Gets the string descriptor for this object
01124 
01125 ***********************************************************************************************/
01126 String NodeContour::Describe(BOOL Plural, BOOL Verbose)
01127 {
01128     String Name;
01129     Name.Load(_R(IDS_CONTOUR_NODE_NAME));
01130 
01131     if (Plural)
01132     {
01133         Name += _T("s");
01134     }
01135 
01136     return Name;
01137 }
01138 
01139 
01140 
01141 /********************************************************************************************
01142 
01143 >   BOOL NodeContour::GenerateContourPathForNode(   Path* pDestPath,
01144                                                     const Node* pSourceNode,
01145                                                     const List* pSourceList,
01146                                                     const MILLIPOINT nWidth,
01147                                                     const BOOL fOuterContour    = TRUE,
01148                                                     const JointType jt          = RoundJoin,
01149                                                     const MILLIPOINT nFlatness  = -1,
01150                                                     const BOOL fUseLineWidths   = TRUE,
01151                                                     const BOOL fIncludeShadows  = FALSE )
01152     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01153     Created:    12 September 2000
01154 
01155     Inputs:     pDestPath       ptr to a Path, which will receive the contoured result.
01156                 pSourceNode     ptr to a node to contour.
01157                 pSourceList     ptr to a list of paths + attrs to contour.
01158                 nWidth          the width of the required contour, in millipoints.
01159                 fOuterContour   TRUE => do an outer contour; FALSE => do an inner contour.
01160                 jt              the required join type.
01161                 nFlatness       the flatness to use, in millipoints. if <= 0, then this
01162                                 routine will use a default value which takes into account
01163                                 the current view's zoom factor. If there is no current
01164                                 view, then a default value of 200 will be used.
01165                 fUseLineWidths  whether or not to include line width attributes.
01166                 fIncludeShadows whether or not to include the outlines of shadows
01167                                 in our calculations; required by feathering.
01168 
01169     Outputs:    if successful, pDestPath is filled with the required contour path.
01170 
01171     Returns:    TRUE if successful, FALSE otherwise.
01172 
01173     Purpose:    Generate a contour for the given node or list of paths + attrs.
01174 
01175     Errors:     ERROR2 if pDestPath is NULL or both pSourceNode and pSourceList are NULL.
01176 
01177     Notes:      Karim 18/11/2000
01178                 Major rewrite - previously this method used ContourBecomeA. We no longer
01179                 rely on that procedure, but use PathBecomeA instead, and contour the result.
01180                 See Camelot2000 SourceSafe db for changes.
01181     See also:   
01182 
01183 ********************************************************************************************/
01184 BOOL NodeContour::GenerateContourPathForNode(   Path* pDestPath,
01185                                                 Node* pSourceNode,
01186                                                 const List* pSourceList,
01187                                                 const MILLIPOINT nWidth,
01188                                                 const BOOL fOuterContour,
01189                                                 const JointType jt,
01190                                                 const MILLIPOINT nFlatness,
01191                                                 const BOOL fUseLineWidths,
01192                                                 const BOOL fIncludeShadows  )
01193 {
01194     // pDestPath *must* be valid, as must one of pSourceNode or pSourceList.
01195     ERROR2IF(pDestPath == NULL ||
01196             (pSourceNode == NULL && pSourceList == NULL), FALSE,
01197             "NodeContour::GenerateContourPathForNode; Invalid input parameters!");
01198 
01199     pDestPath->ClearPath();
01200     pDestPath->Initialise();
01201 
01202 /* Proposed implementation of this method: 
01203     ContourBecomeA2 baContour(NULL, nWidth, pDestPath);
01204     BOOL fSuccess = pSourceNode->DoBecomeA(&baContour);
01205 */
01206 /* Current implementation of this method: */
01207     PathBecomeA baOutline(  BECOMEA_PASSBACK,
01208                             CC_RUNTIME_CLASS(NodePath),
01209                             NULL,
01210                             FALSE,
01211                             pDestPath,
01212                             PathBecomeA::ADD_OUTLINES
01213                         );
01214 
01215     baOutline.SetDoSilhouette(TRUE);
01216     baOutline.SetDoShadowSilhouettes(TRUE);
01217 
01218     BOOL fSuccess = pSourceNode->DoBecomeA(&baOutline);
01219 
01220     if (fSuccess && pDestPath->GetNumCoords() > 2)
01221     {
01222         Path WorkPath;
01223         WorkPath.Initialise();
01224         WorkPath.CloneFrom(*pDestPath);
01225         WorkPath.InitializeContourValues(2 * nWidth);
01226         fSuccess = (WorkPath.GetContourForStep(pDestPath) > 0);
01227     }
01228 
01229 
01230     return fSuccess;
01231 }
01232 
01233 
01234 
01235 /***********************************************************************************************
01236 
01237 >   BOOL NodeContour::GenerateContour(List * pList)
01238 
01239     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01240     Created:    18/8/99
01241     Inputs:     If we're blending we pass in the path processor's list
01242     Purpose:    Generates the contour paths in this node
01243 
01244 ***********************************************************************************************/
01245 BOOL NodeContour::GenerateContour(List * pList, CCAttrMap * pAttrMap)
01246 {   
01247     // build up the source path
01248     NodeContourController * pControl = (NodeContourController *)FindParent();
01249     Node * pNode = pControl;
01250 
01251     // check for a valid set of children
01252     if (!pNode)
01253         return FALSE;
01254 
01255     // if we're printing, don't bother - probably called by mistake !
01256     if (pControl->IsPrinting())
01257     {
01258         ERROR3("Generate contour called when printing");
01259         return FALSE;
01260     }
01261 
01262     // an inset path is the same as a 1 step contour
01263     if (pControl->GetInsetPathFlag())
01264         m_NumPaths = 0;
01265     
01266     if (pControl->GetInsideBoundingRect().IsEmpty())
01267         return FALSE;
01268 
01269     DeleteCache();
01270 
01271     pNode = pNode->FindFirstChild();
01272 
01273     if (!pNode)
01274         return FALSE;
01275 
01276     m_SourcePath.ClearPath();
01277 
01278     if (m_pPathList)
01279     {
01280         delete [] m_pPathList;
01281         m_pPathList = NULL;
01282     }
01283 
01284     INT32 Width = 0;
01285     INT32 NumSteps = 0;
01286     BOOL bOuter = FALSE;
01287 
01288     if (pControl)
01289     {
01290         NumSteps = pControl->GetNumberOfSteps();
01291         Width = pControl->GetWidth();
01292 
01293         if (Width < 0)
01294         {
01295             Width = -Width;
01296             bOuter = TRUE;
01297         }
01298 
01299         NumSteps ++;
01300 
01301         if (pControl->GetInsetPathFlag())
01302             NumSteps = 1;
01303     }   
01304 
01305     ERROR3IF (NumSteps <= 0, "I'm not going to work with that!");
01306     
01307     // create and initialise my contour path list
01308     m_pPathList = new Path[NumSteps];
01309 
01310     if (!m_pPathList)
01311     {
01312         ERROR3("Can't create path list");
01313         return FALSE;
01314     }
01315     
01316     for (INT32 i = 0; i < NumSteps; i++)
01317         m_pPathList[i].Initialise();
01318 
01319     // CGS - we need to generate the bevel with respect to the JoinType applied to the path
01320     // that we are generating it with; BUT if the original bevel had a jointype; then we need
01321     // to generate it with respect to this!
01322     // get the current join type attribute
01323     AttrJoinType * pJT = NULL;
01324     AttrStartCap * pCT = NULL;
01325     JointType generateJoinType = BevelledJoin;//RoundJoin;
01326     LineCapType ct = LineCapRound;
01327 
01328     if (!pList)
01329     {
01330         if (FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType), (NodeAttribute **)&pJT))
01331         {
01332             // found an attribute so use this
01333             if (pJT)
01334                 generateJoinType = pJT->Value.JoinType;
01335         }
01336 
01337         if (FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStartCap), (NodeAttribute **)&pCT))
01338         {
01339             // found an attribute so use this
01340             if (pCT)
01341                 ct = pCT->Value.StartCap;
01342         }
01343     }
01344 
01345     double Flatness = 200.0;
01346 
01348     // Mark Howitt 31/10/00
01349     // This function call now replaces the passed in value when the BecomeA function is created.
01350     // This has been done to elliminate zoom dependancies which change the flatness values
01351     // depending on the current zoom value. See the function header for more details
01352     Node* pNodeToBevel = pControl->FindFirstChildInk();
01353 
01354     while(pNodeToBevel == this && pNodeToBevel)
01355         pNodeToBevel = pNodeToBevel->FindNextInk();
01356 
01357     if(pNodeToBevel)
01358     {
01359         DocRect DR = ((NodeRenderableInk*)pNodeToBevel)->GetBoundingRect();
01360         
01361         if(!DR.IsEmpty())
01362         {
01363             // Get the smallest dimesion, Width or Height.
01364             double Smallest = DR.Height() < DR.Width() ? (double)DR.Height() : (double)DR.Width();
01365 
01366             // now make the flatness value equal to the smallest dimesion divided by 375
01367             Flatness = Smallest / 375.0;
01368         }
01369 
01370         // Check to see if we`re within the specified limits
01371         if(1.0 > Flatness) Flatness = 1.0;
01372         if(375.0 < Flatness) Flatness = 375.0;
01373     }
01375 
01376     // use the BecomeA passback mechanism to contour everything
01377     CProfileBiasGain    ObjectProfile( pControl->GetObjectProfile() );
01378     ContourBecomeA MyBecomeA( BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath),
01379         NULL, FALSE, m_pPathList, &m_SourcePath, Width, NumSteps, bOuter, ObjectProfile,
01380         generateJoinType, Flatness, FALSE );
01381 
01382     // tell the becomeA structure who's using it (i.e. Me !)
01383     MyBecomeA.SetCallNode(this);
01384 
01385     Path BecomeAStrokedPath;
01386     BecomeAStrokedPath.Initialise();
01387 
01388     Path CopyPath;
01389     CopyPath.Initialise();
01390 
01391     Path TempPath;
01392     TempPath.Initialise();
01393     NodePath * pPathNode = NULL;
01394 
01395     // run through all nodes, contouring them
01396     if (!pList)
01397     {
01398         while (pNode)
01399         {
01400             // is the node a blend path ?
01401             if (!pNode->IS_KIND_OF(NodeBlendPath))
01402             {   
01403                 MyBecomeA.ResetCount();
01404 
01405                 if (pNode->IsNodePath())
01406                 {
01407                     // make the node out of this path node, first we'll try to see if we have a stroke
01408                     // attribute applied, if so then just make a copy of the path, as the passback function
01409                     // deals with getting the right path out of the stroke
01410                     BOOL IsStroked = (((NodeRenderableInk*)pNode)->GetActiveStroke() != NULL);
01411 
01412                     if(IsStroked)
01413                         pPathNode = (NodePath*)pNode;
01414                     else
01415                         pPathNode = ((NodePath *)pNode)->MakeNodePathFromAttributes(Flatness,NULL,FALSE,FALSE);
01416 
01417                     pPathNode->DoBecomeA(&MyBecomeA);
01418 
01419                     if(!IsStroked)
01420                     {
01421                         delete pPathNode;
01422                         pPathNode = NULL;
01423                     }
01424                 }
01425                 else if (pNode->CanBecomeA(&MyBecomeA) && pNode != this)
01426                 {
01427                     if (MyBecomeA.GetCount() > 0)
01428                     {
01429                         if (pNode->IsABaseTextClass())
01430                         {
01431                             // reformat the text first
01432                             if (pNode->IS_KIND_OF(TextStory))
01433                             {
01434                                 ((TextStory *)pNode)->FormatAndChildren();
01435                             }
01436                             else
01437                             {
01438                                 if (((BaseTextClass *)pNode)->FindParentStory())
01439                                 {
01440                                     ((BaseTextClass *)pNode)->FindParentStory()->FormatAndChildren();
01441                                 }
01442                                 else
01443                                 {
01444                                     ERROR3("Found a base text class with no parent story");
01445                                 }
01446                             }
01447                         }
01448                         
01449                         pNode->DoBecomeA(&MyBecomeA);
01450 
01451                         if(MyBecomeA.GetCount() > 1)
01452                         {
01453                             // Need to clip!
01454                             CopyPath.ClearPath();
01455                             CopyPath.CloneFrom(*m_pPathList);
01456                             m_pPathList->ClearPath();
01457 
01458                             TempPath.ClipPathToPath(CopyPath,m_pPathList,7|(1<<4),50,Flatness,Flatness);
01459                         }
01460                     }
01461                 }
01462             }
01463             
01464             pNode = pNode->FindNext();
01465         }
01466     }
01467     else
01468     {
01469         SumAllPathsElem *pElem = (SumAllPathsElem *)pList->GetHead();
01470 
01471         while (pElem)
01472         {
01473             Path BlankPath;
01474             BlankPath.Initialise();
01475             
01476             NodePath * pPathNode = new NodePath;
01477             ERRORIF(pPathNode == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
01478             
01479             pPathNode->InkPath.Initialise();
01480             
01481             pElem->GetPath()->ClipPathToPath(BlankPath, &(pPathNode->InkPath), 6);
01482             
01483             AttrJoinType * pJoinType = NULL;        // whats in the blend step?
01484             AttrStartCap * pStartType = NULL;
01485 
01486             pElem->GetAttrMap ()->Lookup( CC_RUNTIME_CLASS(AttrJoinType), (void *&)pJoinType );
01487             pElem->GetAttrMap ()->Lookup( CC_RUNTIME_CLASS(AttrStartCap), (void *&)pStartType );
01488 
01489             if (pJoinType)
01490                 generateJoinType = pJoinType->Value.JoinType;       // use the one in the blend step
01491 
01492             if (pStartType)
01493                 ct = pStartType->Value.StartCap;        // use the one in the blend step
01494 
01495             NodePath * pStrokedPathNode = pPathNode->MakeNodePathFromAttributes(Flatness, pElem->GetAttrMap(),FALSE,FALSE);
01496             
01497             if (pStrokedPathNode)
01498             {   
01499                 pStrokedPathNode->DoBecomeA(&MyBecomeA);
01500                 delete pStrokedPathNode;
01501             }
01502             
01503             if(pPathNode)
01504             {
01505                 delete pPathNode;
01506                 pPathNode = NULL;
01507             }
01508             
01509             pElem = (SumAllPathsElem *)pList->GetNext(pElem);
01510         }
01511     }
01512 
01513     // set all the filled flags on the path list
01514     for( INT32 i = NumSteps-1; i >= 0; i-- )
01515         m_pPathList[i].IsStroked = TRUE;
01516 
01517     m_NumPaths = NumSteps;
01518     
01519     InvalidateBoundingRect();
01520 
01521     if (FindParent())
01522         ((NodeRenderableInk *)FindParent())->InvalidateBoundingRect(TRUE);
01523 
01524     return TRUE;
01525 }
01526 
01527 /***********************************************************************************************
01528 
01529 >   void NodeContour::DeleteCache()
01530 
01531     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01532     Created:    12/8/99
01533     Inputs:     
01534     Purpose:    Deletes all the paths
01535 
01536 ***********************************************************************************************/
01537 void NodeContour::DeleteCache()
01538 {
01539     if (m_pPathList)
01540     {
01541         delete [] m_pPathList;
01542         m_pPathList = NULL;
01543         m_NumPaths = 0;
01544     }
01545 }
01546 
01547 /***********************************************************************************************
01548 
01549 >   void NodeContour::Transform(TransformBase &Trans)
01550 
01551     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01552     Created:    12/8/99
01553     Inputs:     
01554     Purpose:    Transforms me & all my paths
01555 
01556 ***********************************************************************************************/
01557 void NodeContour::Transform(TransformBase &Trans)
01558 {
01559     DocRect dr ;
01560 
01561     for (INT32 i = 0 ; i < m_NumPaths; i++)
01562     {
01563         Trans.Transform(m_pPathList[i].GetCoordArray(), m_pPathList[i].GetNumCoords());
01564         dr = dr.Union(m_pPathList[i].GetBoundingRect());
01565     }
01566 
01567     InvalidateBoundingRect(TRUE);
01568 
01569     Node * pNode = NULL;
01570     NodeContourController * pControl = (NodeContourController *)FindParent();
01571 
01572     if (!pControl)
01573         return;
01574     
01575     if (pControl->GetWidth() > 0)
01576         pNode = FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk));
01577     else
01578         pNode = FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
01579 
01580     // reformat all text nodes if we're not just a translation
01581     Trans2DMatrix * pMat2D = NULL;
01582     
01583     if (Trans.IsInvertable())
01584         pMat2D = (Trans2DMatrix*)(&Trans);
01585 
01586     if (pMat2D)
01587     {
01588         if (!pMat2D->GetMatrix().IsTranslation())
01589         {
01590             while (pNode)
01591             {
01592                 if (pNode->IsABaseTextClass())
01593                 {
01594                     // reformat the text first
01595                     if (pNode->IS_KIND_OF(TextStory))
01596                     {
01597                         ((TextStory *)pNode)->FormatAndChildren();
01598                     }
01599                     else
01600                     {
01601                         if (((BaseTextClass *)pNode)->FindParentStory())
01602                         {
01603                             ((BaseTextClass *)pNode)->FindParentStory()->FormatAndChildren();
01604                         }
01605                         else
01606                         {
01607                             ERROR3("Found a base text class with no parent story");
01608                         }
01609                     }
01610                 }
01611                 
01612                 if (pControl->GetWidth() > 0)
01613                     pNode = pNode->FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk));
01614                 else
01615                     pNode = pNode->FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
01616             }
01617         }
01618     }
01619 
01620     TransformChildren(Trans);
01621 }
01622 
01623 /***********************************************************************************************
01624 
01625 >   void NodeContour::RenderEorDrag(RenderRegion * pRender)
01626 
01627     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01628     Created:    12/8/99
01629     Inputs:     
01630     Purpose:    Renders my eor drag stuff
01631 
01632 ***********************************************************************************************/
01633 void NodeContour::RenderEorDrag(RenderRegion * pRender)
01634 {
01635     if (!m_pPathList || m_NumPaths == 0)
01636         return;
01637     
01638     if (m_pPathList[0].GetNumCoords()>0)
01639         pRender->DrawPath(&(m_pPathList[0]));               // render the first contour
01640 
01641     if (m_pPathList[m_NumPaths-1].GetNumCoords()>0)
01642         pRender->DrawPath(&(m_pPathList[m_NumPaths-1]));    // render the last contour
01643 }
01644 
01645 /***********************************************************************************************
01646 
01647 >   BOOL NodeContour::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
01648 
01649     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01650     Created:    12/8/99
01651     Inputs:     See base class
01652     Purpose:    Writes the node to a file
01653 
01654 ***********************************************************************************************/
01655 BOOL NodeContour::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
01656 {
01657     CXaraFileRecord Rec(TAG_CONTOUR, TAG_CONTOUR_SIZE);
01658 
01659     BOOL ok = Rec.Init();
01660     if (ok) ok = pFilter->Write(&Rec);
01661 
01662     return ok;
01663 }
01664 
01665 /***********************************************************************************************
01666 
01667 >   BOOL NodeContour::WritePreChildrenNative(BaseCamelotFilter* pFilter)
01668 
01669     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01670     Created:    12/8/99
01671     Inputs:     See base class
01672     Purpose:    Writes the node to a file
01673 
01674 ***********************************************************************************************/
01675 BOOL NodeContour::WritePreChildrenNative(BaseCamelotFilter* pFilter)
01676 {
01677     return WritePreChildrenWeb(pFilter);
01678 }
01679 
01680 /***********************************************************************************************
01681 
01682 >   Node* NodeContour::SimpleCopy()
01683 
01684     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01685     Created:    12/8/99
01686     Inputs:     See base class
01687     Purpose:    Writes the node to a file
01688 
01689 ***********************************************************************************************/
01690 Node* NodeContour::SimpleCopy()
01691 {
01692     NodeContour * pNode = new NodeContour;
01693 
01694     if (!pNode)
01695     {
01696         ERROR3("Can't create node contour");
01697         return NULL;
01698     }
01699 
01700     CopyNodeContents(pNode);
01701 
01702     return pNode;
01703 }
01704 
01705 /********************************************************************************************
01706 >   void NodeContour::CopyNodeContents(NodeContour* pNewNode)
01707 
01708     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01709     Created:    05/01/2004
01710     Inputs:     -
01711     Returns:    
01712     Purpose:    Sort of backwards copy constructor
01713 
01714 ********************************************************************************************/
01715 void NodeContour::CopyNodeContents(NodeContour* pNewNode)
01716 {
01717     if (pNewNode->m_pPathList)
01718     {
01719         for (INT32 i = 0 ; i < pNewNode->m_NumPaths; i++)
01720         {
01721             pNewNode->m_pPathList[i].ClearPath();
01722         }
01723         delete[] pNewNode->m_pPathList;
01724     }
01725 
01726     pNewNode->m_NumPaths = m_NumPaths;
01727     pNewNode->m_pPathList = new Path[m_NumPaths];
01728     pNewNode->m_FirstRender = m_FirstRender;
01729 
01730     if (pNewNode->m_pPathList)
01731     {
01732         for (INT32 i = 0 ; i < m_NumPaths; i++)
01733         {
01734             pNewNode->m_pPathList[i].Initialise();
01735             pNewNode->m_pPathList[i].CloneFrom(m_pPathList[i]);
01736             pNewNode->m_pPathList[i].IsFilled = TRUE;
01737             pNewNode->m_pPathList[i].IsStroked = TRUE;
01738         }
01739     }
01740 
01741     pNewNode->m_SourcePath.CloneFrom(m_SourcePath);
01742 
01743     NodeRenderableInk::CopyNodeContents(pNewNode);
01744 }
01745 
01746 
01747 
01748 
01749 /***********************************************************************************************
01750 >   void NodeContour::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
01751 
01752     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01753     Created:    18/12/2003
01754     Outputs:    -
01755     Purpose:    Polymorphically copies the contents of this node to another
01756     Errors:     An assertion failure will occur if NodeCopy is NULL
01757     Scope:      protected
01758                                      
01759 ***********************************************************************************************/
01760 
01761 void NodeContour::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
01762 {
01763     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
01764     ENSURE(IS_A(pNodeCopy, NodeContour), "PolyCopyNodeContents given wrong dest node type");
01765 
01766     if (IS_A(pNodeCopy, NodeContour))
01767         CopyNodeContents((NodeContour*)pNodeCopy);
01768 }
01769 
01770 
01771 
01772    
01773 /***********************************************************************************************
01774 
01775 >   BOOL NodeContour::BlendAttributes(RenderRegion * pRegion, CCAttrMap * pBlendedAttrMap, 
01776                                             double BlendRatio)
01777 
01778     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01779     Created:    12/8/99
01780     Inputs:     Render region to blend the attributes with, and the ratio (i.e. the
01781                 position of the blend 0 <= BlendRatio <= 1.0
01782     Purpose:    Blends all the attributes to this particular position,
01783 
01784 ***********************************************************************************************/
01785 BOOL NodeContour::BlendAttributes(RenderRegion * pRender, CCAttrMap * pAttrMapStart, 
01786                                   CCAttrMap * pAttrMapEnd, CCAttrMap * pBlendedAttrMap, 
01787                                             double BlendRatio)
01788 {
01789     // out of nodebldr.cpp - ln 1388
01790     // Process each attribute in turn
01791 
01792     // These vars are used as params to the CCAttrMap funcs
01793     CCRuntimeClass     *pTypeStart = NULL; 
01794     void               *pValStart = NULL; 
01795     void               *pValEnd = NULL;
01796 
01797     NodeContourController * pController = (NodeContourController *)FindParent(CC_RUNTIME_CLASS(NodeContourController));
01798 
01799     if (!pController)
01800         return FALSE;
01801 
01802     BOOL useStartAttrMap = FALSE;
01803 
01804     CCAttrMap::iterator PosStart = pAttrMapStart->GetStartPosition();
01805     CCAttrMap::iterator end      = pAttrMapStart->GetEndPosition();
01806     while ( PosStart != end )
01807     {
01808         // Get a ptr to the attr at position PosStart in the start node's attr map
01809         pAttrMapStart->GetNextAssoc(PosStart,pTypeStart,pValStart);
01810         NodeAttribute* pNodeAttrStart = (NodeAttribute *)pValStart;
01811 
01812         if (pNodeAttrStart->CanBeAppliedToObject())
01813         {
01814             // Get a blended attribute
01815             NodeAttribute* pBlendedNodeAttr = NULL;
01816             
01817             if (pAttrMapEnd)
01818             {
01819                 // Find an attr of the same type in the end object's attr list,
01820                 // and blend the two attrs together
01821                 if (pAttrMapEnd->Lookup(pTypeStart,pValEnd))
01822                 {
01823                     // Set up the param object to pass to the start attr's blend method
01824                     BlendAttrParam BlendParam;
01825                     
01826                     // Initialise the BlendParam with the end attr and blend ratio
01827                     if (BlendParam.Init(pRender,
01828                                         (NodeAttribute *)pValEnd,BlendRatio,pController->GetColourBlendType(),
01829                                         pAttrMapStart, pAttrMapEnd))
01830                     {
01831                         // Successfully initialised, so now try blending the attributes
01832                         if (pNodeAttrStart->Blend(&BlendParam))
01833                         {
01834                             // Attrs successfully blended, now get a ptr to the new attr.
01835                             // Once we get the blended attr ptr, it belongs to us, so we have
01836                             // to delete it when it is not needed
01837                             pBlendedNodeAttr = BlendParam.GetBlendedAttr();
01838                         }
01839                     }
01840                     
01841                 }
01842             }
01843             else
01844             {
01845                 useStartAttrMap = TRUE;
01846             }
01847             
01848             // If we have a blended attr, pBlendedNodeAttr != NULL
01849             if (pBlendedNodeAttr != NULL)
01850             {
01851                 // Get the type of the blended attr
01852                 CCRuntimeClass *pTypeBlend = pBlendedNodeAttr->GetAttributeType();
01853                 
01854                 void* pValBlend;
01855                 
01856                 // If we already have an attr in the blended attr map of the same type,
01857                 // remove it and delete it, before inserting a new attr of this type
01858                 if (pBlendedAttrMap->Lookup(pTypeBlend,pValBlend))
01859                 {
01860                     if (pValBlend != NULL)
01861                     {
01862                         pBlendedAttrMap->RemoveKey(pTypeBlend);
01863                         delete (NodeAttribute*)pValBlend;
01864                     }
01865                 }
01866                 // add it to the blend map
01867                 pBlendedAttrMap->SetAt(pTypeBlend,pBlendedNodeAttr);
01868             }
01869         }
01870     }
01871 
01872     if (useStartAttrMap)
01873     {
01874         pBlendedAttrMap = pAttrMapStart->Copy ();
01875     }
01876 
01877     return TRUE;
01878 }
01879 
01880 
01881 
01882 /***********************************************************************************************
01883 
01884 >   BOOL NodeContour::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState = TRUE,
01885                                                       BOOL DoPreTriggerEdit = TRUE)
01886 
01887     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>; Karim 12/01/2000
01888     Created:    12/8/99
01889     Inputs:     pParam                  describes the way an op wants to change the node
01890                 SetOpPermissionState    if TRUE the OpPermission of nodes should be set
01891                 DoPreTriggerEdit        if TRUE then NameGallery::PreTriggerEdit is called.
01892                                         *Must* be TRUE if the calling Op may make any nodes
01893                                         change their bounds, eg move, line width, cut.
01894                                         Use TRUE if unsure.
01895     Purpose:    Does the allow op mechanism
01896 
01897 ***********************************************************************************************/
01898 BOOL NodeContour::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState,
01899                                                   BOOL DoPreTriggerEdit)
01900 {
01901     ERROR2IF(pParam==NULL,FALSE,"NodeContourController::AllowOp() - pParam==NULL");
01902 
01903     // clean out the calling-child ptr, so it doesn't get passed around unintentionally.
01904     pParam->SetCallingChild(NULL);
01905 
01906     // Set up a flag to see if any of the child objects get changed
01907     BOOL allowed=TRUE;
01908 
01909     UndoableOperation* pOp = pParam->GetOpPointer();
01910 
01911     // see whether our parent allows the op.
01912     if (allowed && Parent != NULL && pParam->GetDirection() != OBJCHANGE_CALLEDBYPARENT)
01913     {
01914         ObjChangeDirection OldDirection=pParam->GetDirection();
01915         pParam->SetCallingChild(this);
01916         pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD);
01917         allowed=Parent->AllowOp(pParam,SetOpPermissionState,DoPreTriggerEdit);
01918         pParam->SetDirection(OldDirection);
01919     }
01920 
01921     // if the parent allows this, then test the op
01922     if (allowed)
01923     {
01924         // ok, test for ops which we don't allow.
01925         if(pOp != NULL)
01926         {
01927             if(
01928                 pOp->IS_KIND_OF(OpCreateBevel) ||
01929                 pOp->IS_KIND_OF(OpCreateNewMould) ||
01930                 pOp->IS_KIND_OF(OpPasteEnvelope) ||
01931                 pOp->IS_KIND_OF(OpPastePerspective) ||
01932                 false )
01933             {
01934                 allowed = FALSE;
01935             }
01936         }
01937     }
01938 
01939     // if necessary, set permissions for OnChildChange.
01940     if (SetOpPermissionState)
01941         SetOpPermission(allowed ? PERMISSION_ALLOWED : PERMISSION_DENIED, TRUE);
01942 
01943     if (allowed)
01944     {
01945         UndoableOperation* pChangeOp = pParam->GetOpPointer();
01946 
01947         // check for geometry linked attributes
01948         BOOL InformGeomLinkedAttrs = SetOpPermissionState && pChangeOp && pChangeOp->MayChangeNodeBounds();
01949         if (InformGeomLinkedAttrs)
01950         {
01951             NodeAttribute* pNA = FindFirstGeometryLinkedAttr();
01952             while(pNA)
01953             {
01954                 pNA->LinkedNodeGeometryHasChanged(pChangeOp);
01955                 pNA = pNA->FindNextGeometryLinkedAttr();
01956             }
01957         }       
01958 
01959         // if we're ok so far and were asked to do a PreTriggerEdit, then
01960         // determine whether the Op may change the bounds of some nodes.
01961         // If it may, then call NameGallery::PreTriggerEdit.
01962         if (DoPreTriggerEdit && pChangeOp && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance())
01963         {
01964             allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
01965         }
01966     }
01967 
01968     // return result (directly, or indirectly via a child AllowOp()) to op
01969     return allowed;
01970 }
01971 
01972 
01973 
01974 /***********************************************************************************************
01975 
01976 >   void NodeContour::RenderTinyBlobs(RenderRegion * pRegion)
01977 
01978     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01979     Created:    12/8/99
01980     Inputs:     Render region to render the tiny blobs to
01981     Purpose:    Renders the tiny blobs for this object
01982 
01983 ***********************************************************************************************/
01984 void NodeContour::RenderTinyBlobs(RenderRegion * pRender)
01985 {
01986     // Set the line colours etc as we need them
01987     pRender->SaveContext();
01988     pRender->SetLineColour(COLOUR_NONE);
01989     pRender->SetFillColour(COLOUR_UNSELECTEDBLOB);
01990 
01991     // Render the blobs on the path
01992     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01993 
01994     DocCoord Coord(BoundingRectangle.lo.x + (BlobMgr->GetBlobSize() / 2), 
01995         BoundingRectangle.hi.y - (BlobMgr->GetBlobSize() / 2));
01996     pRender->DrawBlob(Coord, BT_UNSELECTED);
01997     pRender->RestoreContext();
01998 }
01999 
02000 /********************************************************************************************
02001 
02002 >   virtual NodeCompound* NodeContour::GetParentController() const
02003 
02004     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02005     Created:    06/12/1999
02006     Returns:    Pointer to parent Contour controller (NULL if not found!)
02007     Purpose:    Returns a type correct pointer to the parent Contour controller
02008 
02009 ********************************************************************************************/
02010 NodeCompound* NodeContour::GetParentController() const
02011 {
02012     NodeContourController* pBob = (NodeContourController*)FindParent();
02013     if (pBob != NULL && !IS_A(pBob, NodeContourController))
02014     {
02015         ERROR3("Parent of Contour was not a NodeContourController!");
02016         pBob = NULL;
02017     }
02018 
02019     return pBob;
02020 }
02021 
02022 
02023 
02024 /********************************************************************************************
02025 
02026 >   virtual DocRect NodeContour::ValidateExtend(const ExtendParams& ExtParams)
02027 
02028     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02029     Created:    06/12/1999
02030     Inputs:     ExtParams       description parameters for the extension.
02031     Outputs:    
02032     Returns:    TRUE if extending this Nodewill be a reversible operation,
02033                 FALSE otherwise.
02034     Purpose:    Tests the reversibility of an Extend operation applied to this node.
02035 
02036                 A NodeContour cannot itself extend, so instead it asks its parent controller
02037                 to extend for it. Infinite recursion does not occur, as the controller node
02038                 ignores its child node, this NodeContour.
02039     Errors:     In debug builds, ERROR3 if this Node has no NodeContourController,
02040                 in release, we return TRUE.
02041     See also:   IsTypeExtendible(), Extend().
02042 
02043 ********************************************************************************************/
02044 DocRect NodeContour::ValidateExtend(const ExtendParams& ExtParams)
02045 {
02046     Node* pBob = GetParentController();
02047     if (pBob == NULL)
02048     {
02049         ERROR3("NodeContour::ValidateExtend- no controller Node found!");
02050         return DocRect(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX);
02051     }
02052     else
02053     {
02054         return pBob->ValidateExtend(ExtParams);
02055     }
02056 }
02057 
02058 
02059 
02060 /********************************************************************************************
02061 
02062 >   virtual void NodeContour::Extend(const ExtendParams& ExtParams)
02063 
02064     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02065     Created:    06/12/1999
02066     Inputs:     ExtParams       description parameters for the extension.
02067     Outputs:    Some of the child nodes of this ContourNode's NodeContourController may have
02068                 their dimensions altered.
02069     Returns:    
02070     Purpose:    Perform an Extend operation on this Node, and its children if appropriate.
02071 
02072                 A NodeContour cannot itself extend, so instead it asks its parent controller
02073                 to extend for it. Infinite recursion does not occur, as the controller node
02074                 ignores its child node, this NodeContour.
02075     Errors:     In debug builds, ERROR3 if this Node has no NodeContourController,
02076                 in release, we do nothing.
02077     See also:   
02078 
02079 ********************************************************************************************/
02080 void NodeContour::Extend(const ExtendParams& ExtParams)
02081 {
02082     Node* pBob = GetParentController();
02083     if (pBob == NULL)
02084     {
02085         ERROR3("NodeContour::ValidateExtend- no controller Node found!");
02086     }
02087     else
02088     {
02089         pBob->Extend(ExtParams);
02090     }
02091 
02092     return;
02093 }
02094 
02095 /********************************************************************************************
02096 
02097 >   virtual BOOL NodeContour::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
02098 
02099     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02100     Created:    7/2/2000
02101     Inputs:     pSpread     The spread in which things are happening
02102                 PointerPos  The Location of the mouse pointer at the time of the click
02103                 pMenu       The menu to which items should be added
02104     Returns:    BOOL - TRUE if the node claims the click as its own and FALSE if it is
02105                 not interested in the click
02106     Purpose:    Allows the shadow to respond to the click
02107 
02108 ********************************************************************************************/
02109 
02110 BOOL NodeContour::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
02111 {
02112     return pMenu->BuildCommand(TOOL_OPTOKEN_CONTOUR, TRUE);
02113 }
02114 
02115 
02116 
02117 
02118 
02119 
02121 // ContourBecomeA Implementation
02122 
02123 /***********************************************************************************************
02124 
02125 >   BOOL ContourBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,
02126                               CCAttrMap* pAttrMap)
02127 
02128     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02129     Created:    12/8/99
02130     Inputs:     See base class
02131     Purpose:    Sums all node paths' paths together into one path
02132 
02133 ***********************************************************************************************/
02134 BOOL ContourBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,
02135                               CCAttrMap* pAttrMap)
02136 {
02137     // output the path to a file
02138     ERROR2IF(pNewNode == NULL || pCreatedByNode == NULL, FALSE, "Parameter null");
02139     
02140     // if the new node isn't a node path then exit
02141     if (!pNewNode->IsNodePath())
02142     {
02143         // is it a compound node ?
02144         if (!pNewNode->IsCompound())
02145             return FALSE;
02146 
02147         // get the path out of the compound node
02148         NodePath * pPath = (NodePath *)(((NodeCompound *)pNewNode)->GetNodeToBlend()->PublicCopy());
02149 
02150         // delete the new node
02151         pNewNode->CascadeDelete();
02152         delete pNewNode;
02153         pNewNode = pPath;
02154 
02155     }
02156 
02157     // if the number of coordinates in the path is less than two exit
02158     if (((NodePath *)pNewNode)->InkPath.GetNumCoords() < 0)
02159         return TRUE;
02160 
02161     // variable to tell delete pAttrMap commands after this whether to do a delete attributes
02162     // on the map or not
02163     BOOL bDeleteAttrMap = TRUE;
02164     
02165     if (!pAttrMap)
02166     {
02167         // ok, set up the attribute map from the created by node
02168         pAttrMap = CCAttrMap::MakeAppliedAttrMap(pCreatedByNode);
02169         
02170         // and tell all deletes not to remove the attributes in this map when deleting it
02171         bDeleteAttrMap = FALSE;
02172     }
02173 
02174     DocRect br;
02175 //  double d = 0;
02176 //  double dWidth = 0;
02177 
02178     BOOL bStroke = FALSE;
02179     AttrLineWidth * pLineWidth = NULL;
02180     AttrStrokeColour * pStrokeColour = NULL;
02181     NodePath* pStrokedPath = NULL;
02182 
02183     if(pAttrMap)
02184     {
02185         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeColour), (void *&)pStrokeColour );
02186         
02187         if(pStrokeColour && !pStrokeColour->Value.GetStartColour()->IsTransparent())
02188             pStrokedPath = pCreatedByNode->GetSmoothVariableWidthStrokePath();
02189     }
02190     else
02191         pStrokedPath = pCreatedByNode->GetSmoothVariableWidthStrokePath();
02192 
02193     if(pStrokedPath)
02194     {
02195         // replace the path we got with the stroked path
02196         delete pNewNode;
02197         pNewNode = pStrokedPath;
02198         pStrokedPath = NULL;
02199         bStroke = TRUE;
02200         m_bIncludeLineWidths = FALSE; // stroke already incorporates line width
02201     
02202     }
02203 
02204     Path CopyPath;
02205     CopyPath.Initialise();
02206 
02207     Path ClipPath;
02208     ClipPath.Initialise();
02209 
02210 //  Node * pChild = NULL;
02211 //  Node * pNextNode = NULL;
02212 
02213     BOOL IsClosed = ((NodePath *)pNewNode)->InkPath.IsClosed();
02214 
02215     // only clip unstroked closed paths
02216     if (IsClosed && !bStroke)
02217     {
02218         ClipPath.CloneFrom(((NodePath *)pNewNode)->InkPath);
02219         ((NodePath *)pNewNode)->InkPath.ClearPath();
02220 
02221         if(CopyPath.ClipPathToPath(ClipPath, &(((NodePath *)pNewNode)->InkPath), 3, ClipPathToPath_Tolerance, m_Flatness, m_Flatness) < 0)
02222         {
02223             ERROR3("BAD PATH ALERT!");
02224         }
02225     }
02226 
02227     CopyPath.ClearPath();
02228     ClipPath.ClearPath();
02229     
02230 //  UINT32 LineWidth = 0;
02231 
02232     if (pNewNode->IS_KIND_OF(NodeBlendPath))
02233     {
02234         // don't do blend paths
02235         if (pAttrMap)
02236         {
02237             if (bDeleteAttrMap)
02238                 pAttrMap->DeleteAttributes();
02239             
02240             delete pAttrMap;
02241         }
02242         
02243         pNewNode->CascadeDelete();
02244         delete pNewNode;    
02245         return TRUE;        
02246     }
02247 
02248     if (pNewNode->IsNodePath())
02249     {
02250         BOOL bDontUseThisNodePath = FALSE;
02251 
02252         // Karim 29/08/2000
02253         // If the NodePath's path data contains less than two handles, then give up now.
02254         if (((NodePath*)pNewNode)->InkPath.GetNumCoords() < 2)
02255             bDontUseThisNodePath = TRUE;
02256 
02257         // do nothing for these nodes as well - indicates a 'fit text to curve'
02258         // object
02259         else if (pNewNode->FindFirstChild())
02260             bDontUseThisNodePath = TRUE;
02261 
02262         if (bDontUseThisNodePath)
02263         {
02264             if (pAttrMap)
02265             {
02266                 if (bDeleteAttrMap)
02267                     pAttrMap->DeleteAttributes();
02268 
02269                 delete pAttrMap;
02270                 pAttrMap = NULL;
02271             }
02272 
02273             pNewNode->CascadeDelete();
02274             delete pNewNode;
02275             pNewNode = NULL;
02276 
02277             return TRUE;
02278         }
02279     }
02280 
02281     JointType jt = m_Join;
02282 
02283     // get the line width & stroke colour from the attribute map
02284     // to decide if we need to stroke it first
02285     if (IsClosed)
02286     {
02287         if(m_bIncludeLineWidths && pAttrMap != NULL)
02288         {
02289             pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrLineWidth), (void *&)pLineWidth );
02290             pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeColour), (void *&)pStrokeColour );
02291 
02292             if (pStrokeColour && pLineWidth)
02293             {
02294                 UINT32 Width = pLineWidth->Value.LineWidth;
02295 
02296                 if (pStrokeColour->GetStartColour() && Width > 0)
02297                 {
02298                     if (!pStrokeColour->GetStartColour()->IsTransparent())
02299                     {
02300                         // stroke the path & add it to the original path node's path
02301                         Path OrigStrokedPath;
02302                         OrigStrokedPath.Initialise();
02303 
02304                         JoinStyles JoinS = (jt==MitreJoin) ? JOIN_MITER : (jt==RoundJoin) ? JOIN_ROUND : JOIN_BEVEL;
02305 
02306                         OrigStrokedPath.CloneFrom(((NodePath *)pNewNode)->InkPath);
02307                         ((NodePath *)pNewNode)->InkPath.ClearPath();
02308                             
02309                         OrigStrokedPath.InitializeContourValues(Width,JoinS,true,m_Flatness);
02310                         if(OrigStrokedPath.GetContourForStep(&((NodePath *)pNewNode)->InkPath,1.0) < 0)
02311                         {
02312                             ERROR3("BAD PATH ALERT!");
02313                         }
02314                     }
02315                 }
02316             }
02317         }
02318 
02319         ((NodePath *)pNewNode)->InkPath.TryToClose();
02320         ((NodePath *)pNewNode)->InkPath.IsFilled = TRUE;
02321         ((NodePath *)pNewNode)->InkPath.IsStroked = FALSE;
02322     }
02323     else
02324     {
02325         // turn the path into a closed path
02326         NodePath * pWholePath = ((NodePath *)pNewNode)->MakeNodePathFromAttributes(m_Flatness, pAttrMap);
02327 
02328         if (pWholePath)
02329         {
02330             // ok, delete the new node pointer & all its children
02331             // does the new node have pointers to child nodes ? if so, delete the child nodes
02332             // first, then delete the top level node
02333             pNewNode->CascadeDelete();
02334             delete pNewNode;
02335             pNewNode = pWholePath;
02336         }
02337     }
02338 
02339 
02340     // are the lists ok ?
02341     if (m_pPathList && m_pSummedPath)
02342     {
02343         // is the node a path (you can get groups out of BecomeA with NodePath)
02344         if (pNewNode->IsNodePath() && ((NodePath *)pNewNode)->InkPath.GetNumCoords() > 2)
02345         {
02346             if(m_Width != 0)
02347                 if(!GenerateContourPaths(&(((NodePath *)pNewNode)->InkPath), m_pPathList, m_Join, (m_bOuter == TRUE)))
02348                     return FALSE;
02349 
02350             m_pSummedPath->MergeTwoPaths(((NodePath *)pNewNode)->InkPath);
02351         }
02352     }
02353 
02354     if (pAttrMap)
02355     {
02356         if (bDeleteAttrMap)
02357             pAttrMap->DeleteAttributes();
02358         
02359         delete pAttrMap;
02360     }
02361     
02362     // does the new node have pointers to child nodes ? if so, delete the child nodes
02363     // first, then delete the top level node
02364     pNewNode->CascadeDelete();
02365     delete pNewNode;
02366     
02367     return TRUE;
02368 }
02369 
02370 /***********************************************************************************************
02371 
02372 >   BOOL ContourBecomeA::GenerateContourPaths(Path * pPathToContour, Path * pPathList
02373                                               JointType jt, bool Outer)
02374     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
02375     Created:    15/05/00
02376     Inputs:     See base class
02377     Purpose:    Contours the path & sums the output with the path list passed in
02378     Notes:      This is a totally rewritten version of the David Mc code!
02379                 It basically cuts out all the unnessasary clipping and uses the Gavin
02380                 Contouring code found in Path.cpp!
02381 
02382 ***********************************************************************************************/
02383 BOOL ContourBecomeA::GenerateContourPaths(Path* pPathToContour, Path* pPathList, JointType jt, bool Outer,
02384                                           bool UseContourMode, LineCapType ct)
02385 {
02386     // First create a join Style from the given JoinType!
02387     JoinStyles JoinS = (jt==MitreJoin) ? JOIN_MITER : (jt==RoundJoin) ? JOIN_ROUND : JOIN_BEVEL;
02388     CapStyles CapS = (ct==LineCapButt) ? CAPS_BUTT : (ct==LineCapRound) ? CAPS_ROUND : CAPS_SQUARE;
02389 
02390     // Now initialize the contour values for the path To process using the width & JoinStyle
02391     /*UINT32 Length =*/ pPathToContour->InitializeContourValues(m_Width * 2, JoinS, Outer, m_Flatness, true, UseContourMode, CapS);
02392 
02393     Path* pTempPath = NULL;
02394     double StepValue = 1.0;
02395 
02396     // Get the current profiler in order to alter the step values
02397     CProfileBiasGain Profile = m_Profile;
02398     Profile.SetBias(-Profile.GetBias());
02399 
02400     // Loop through all the steps getting the individual paths!
02401     for (INT32 i = m_NumSteps; i > 0; i--)
02402     {
02403         // Get the correct Step Value
02404         if (m_NumSteps >= 2)
02405             StepValue = (double)i / m_NumSteps;
02406 
02407         // Correct the step value with the current profile settings
02408         StepValue = (double)Profile.MapZeroToOne((AFp)StepValue);
02409 
02410         // Get the Next Stepped Path pointer
02411         pTempPath = &pPathList[i-1];
02412 
02413         // Now get the new contoured path using the current width and step value!
02414         if(pPathToContour->GetContourForStep(pTempPath, StepValue) < 0)
02415             ERROR2(FALSE,"Failed to Get Controur! NODECNTR");
02416     }
02417 
02418     return TRUE;
02419 }
02420 
02421 
02422 
02423 /********************************************************************************************
02424 
02425 >   virtual INT32 NodeContour::EstimateNodeComplexity (OpParam* details)
02426 
02427     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02428     Created:    6/09/2000
02429 
02430     Inputs:     details     any data that should be used for the calculation
02431 
02432     Outputs:    -
02433 
02434     Returns:    an estimate of the nodes complexity
02435 
02436     Purpose:    This function estimates a complexity value for the node.  The complexity
02437                 value is based upon the total length of all paths in the node.
02438 
02439     See Also:   OpBlendNodes::DeterminBlendObjectsProcessorHit ()
02440 
02441 ********************************************************************************************/
02442 
02443 INT32 NodeContour::EstimateNodeComplexity (OpParam* details)
02444 {
02445     INT32 total = 0;
02446     
02447     for (INT32 i = 0 ; i < m_NumPaths && m_pPathList; i++)
02448         total += m_pPathList[i].GetUsedSlots();
02449 
02450     return (total);
02451 }

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