brshcomp.cpp

Go to the documentation of this file.
00001 // $Id: brshcomp.cpp 1732 2006-09-03 14:08:16Z 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 // Brush Component and Brush Definition headers
00100 
00101 #include "camtypes.h"
00102 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00103 //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00104 #include "brshcomp.h"
00105 #include "linecomp.h"
00106 //#include "colormgr.h"
00107 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "cxftags.h"
00110 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "layer.h"
00112 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "brushref.h"
00114 #include "freehand.h"
00115 #include "freeinfo.h"
00116 //#include "xlong.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 #include "ppbrush.h"
00118 #include "brshattr.h"
00119 #include "sgline.h"
00120 //#include "loadbrsh.h"
00121 #include "fileutil.h"
00122 #include "clipint.h"
00123 #include "nodebldr.h"
00124 #include "bldbrdef.h"
00125 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00127 #include "lineattr.h"
00128 #include "brshdata.h"
00129 //#include "swfrndr.h"
00130 #include "brshname.h"
00131 //#include "resdll.h"
00132 #include "nodeshad.h"
00133 #include "nodecntr.h"
00134 #include "nodebev.h"
00135 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00136 //#include "opdrbrsh.h"
00137 #include "toollist.h"
00138 //#include "freeinfo.h"
00139 #include "brshbeca.h"
00140 //#include "brdlgres.h"
00141 #include "brushop.h"
00142 
00143 DECLARE_SOURCE("$Revision: 1732 $");
00144 
00145 CC_IMPLEMENT_DYNAMIC(BrushDefinition, LineDefinition)
00146 CC_IMPLEMENT_DYNAMIC(BrushComponent, LineComponent)
00147 CC_IMPLEMENT_DYNAMIC(BrushComponentClass, DocComponentClass)
00148 
00149 // Declare smart memory handling in Debug builds
00150 #define new CAM_DEBUG_NEW  
00151 
00152 BOOL BrushComponent::m_bCancelNewBrush = FALSE;
00153 String_32 BrushComponent::m_NewName = TEXT("Unnamed brush");
00154 
00155 
00156 const INT32 MAX_TRANSP_VALUE = 255;
00157 /********************************************************************************************
00158 
00159 >   BrushDefinition::BrushDefinition(Node *pBrushTree);
00160 
00161     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00162     Created:    27/2/97
00163 
00164     Inputs:     pBrushTree - A pointer to a Spread node which is the root of a clipart
00165                               subtree which should be used for this Brush definition. It
00166                               must obey these rules:
00167                                 * It must be a Spread
00168                                 * It must not be linked into any other parent tree structure
00169                                 * It should contain at least one ink node (or else the Brush
00170                                   will appear "blank"). 
00171                                 * It should be attribute complete, or close thereto
00172 
00173     Purpose:    Constructor
00174 
00175     SeeAlso:    BrushComponent::AddNewBrush
00176 
00177 ********************************************************************************************/
00178 
00179 BrushDefinition::BrushDefinition(Node* pBrushTree) 
00180 : LineDefinition(pBrushTree)
00181 {
00182     ResetMembers();
00183     InitialiseBrushArray(MAX_BRUSH_OBJECTS);
00184     if (!GenerateBrush())
00185     {
00186         // hmm, not sure what to do here, but you can detect if we failed to initialise
00187         // by calling IsActivated, if that fails you should delete me.
00188     }
00189     
00190 }
00191 
00192 
00193 /********************************************************************************************
00194 
00195 >   BrushDefinition::BrushDefinition()
00196 
00197     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00198     Created:    27/2/97
00199 
00200     Purpose:    Default constructor for the purposes of inherited classes, if you just want a
00201                 normal brushdef you should really use the constructor above.  
00202             
00203                 5/12/2000 Actually I've just read that it is a poor idea to initialise inside
00204                 constructors, so I think that a reorganisation should occur whereby the construcotr
00205                 should simply assign the brush tree and we should GenerateBrush after the constructor
00206                 returns.
00207 
00208 ********************************************************************************************/
00209 
00210 BrushDefinition::BrushDefinition()
00211 {
00212     ResetMembers();
00213 }
00214 
00215 
00216 /********************************************************************************************
00217 
00218 >   BrushDefinition::~BrushDefinition()
00219 
00220     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00221     Created:    27/2/97
00222 
00223     Purpose:    Destructor
00224 
00225 ********************************************************************************************/
00226 
00227 BrushDefinition::~BrushDefinition()  
00228 { 
00229     for( size_t i = 0; i < m_BrushRefPtrArray.size(); i++ )
00230     {
00231         // ask the brushref to delete the attribute maps it generated
00232         if (m_BrushRefPtrArray[i] != NULL)
00233         {
00234         //  m_BrushRefPtrArray[i]->DeleteAttributeMapsAndAttributes();
00235             delete m_BrushRefPtrArray[i];
00236         }
00237     }
00238     m_BrushRefPtrArray.clear();
00239 
00240     
00241 
00242 }
00243 
00244 
00245 
00246 /***********************************************************************************************
00247 
00248 >   void BrushDefinition::ResetMembers()
00249 
00250     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00251     Created:    6/10/99
00252     Inputs:     -
00253     Returns     -
00254     Purpose:    Initialises the member variables
00255 
00256 ***********************************************************************************************/
00257 
00258 void BrushDefinition::ResetMembers()
00259 {
00260     m_BrushSpacing          = 10000;
00261     m_BrushSpacingIncrProp  = 1.0;
00262     m_BrushSpacingIncrConst = 0;
00263     m_BrushSpacingMaxRand   = 0;
00264     m_BrushSpacingRandSeed  = 0;
00265 
00266     m_PathOffsetType      = OFFSET_NONE;
00267     m_PathOffsetValue     =  0;
00268     m_PathOffsetIncrProp  = 1.0;
00269     m_PathOffsetIncrConst = 0;
00270     m_OffsetTypeRandSeed  = 0;
00271     m_OffsetValueMaxRand  = 0;
00272     m_OffsetValueRandSeed = 0;
00273 
00274     m_bRotate             = TRUE;
00275     m_RotateAngle         = 0;
00276     m_RotationMaxRand     = 0;
00277     m_RotationRandSeed    = 0;
00278     m_RotAngleIncrConst   = 0;
00279     m_RotAngleIncrProp    = 1.0;
00280     m_RotationMaxPressure = 0;
00281 
00282     m_bTile               = TRUE;
00283 
00284     m_BrushScaling        = 1.0;
00285     m_BrushScalingIncr    = 1.0;
00286     m_BrushScalingIncrConst = 0.0;
00287     m_BrushScalingMaxRand   = 0;
00288     m_BrushScalingRandSeed  = 0;
00289     m_ScalingMaxPressure    = 35;
00290     
00291     m_BrushHueIncrement     = 0.0;
00292     m_BrushHueMaxRand       = 0;
00293     m_BrushHueRandSeed      = 1234;
00294 
00295     m_BrushSatIncrement     = 0.0;
00296     m_BrushSatMaxRand       = 0;
00297     m_BrushSatRandSeed      = 5432; 
00298 
00299     m_SequenceType        = SEQ_FORWARD;
00300     m_SequenceRandSeed    = 0;
00301 
00302     m_NumBrushObjects     = 0;
00303     m_bInitOk             = FALSE;
00304 
00305     m_LargestBoundingBox.MakeEmpty();
00306 
00307     m_Name = TEXT("Custom Brush");
00308 
00309     m_TimeStampPeriod = 0;
00310 
00311     m_BrushTransparency  = 100;
00312     m_TranspMaxPressure  = 0;
00313 
00314     m_DefaultFileID = BRUSHFILE_NONE;
00315 
00316     m_bActivated = FALSE;
00317 }
00318 
00319 
00320 
00321 /***********************************************************************************************
00322 
00323 >   BOOL BrushDefinition::GenerateBrush()
00324 
00325     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00326     Created:    13/12/99
00327     Inputs:     -
00328     Returns     TRUE if successful, FALSE otherwise
00329     Purpose:    goes through the input spread and calls AddNewObject() for each ink object found
00330                 We try to make some intelligent guesses about what to set the starting spacing and
00331                 offset values depending on the nature of the nodes.  The following rules apply:
00332                 - if there are multiple ink objects then the spacing is 1.1 * the largest dimension 
00333                 of the largest bounding box
00334                 - if there is only one object and it contains a positive line width spacing is as above
00335                 - if there is only one object with no line width then spacing is 10000 millipoint
00336                 - defaulf offset is always the largest dimension of the largest BBOX.
00337 
00338 ***********************************************************************************************/
00339 
00340 BOOL BrushDefinition::GenerateBrush()
00341 {
00342     if (m_pTree == NULL)
00343         return FALSE;
00344 
00345     // get a node that points at the first child of the layer
00346     Node* pNode = m_pTree->FindFirstChild();
00347     pNode       = pNode->FindFirstChild();
00348     
00349 
00350     AttrLineWidth* pLineWidth = NULL;
00351     while (pNode != NULL)
00352     {
00353         if (pNode->IsAnObject())
00354         {
00355             // it has been drawn to my attention that it is possible to end up with a brush object
00356             // that has no line width attribute, which leads to all kinds of problems.  So here we will
00357             // search for one and if we don't find one we will add one.
00358             ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), (NodeAttribute**)&pLineWidth);
00359             if (!pLineWidth)
00360             {
00361                 pLineWidth = new AttrLineWidth(pNode, LASTCHILD);
00362                 if (pLineWidth)
00363                 {
00364                     pLineWidth->Value.LineWidth = 501;
00365                     pLineWidth = NULL;
00366                 }
00367             }
00368 
00369             // we don't want to make brushes from brushes, instead we want to convert them
00370             // to shapes first, so use the routine to do that.
00371             NodeGroup* pGroup = NULL;
00372             ReplaceBrushWithGroup((NodeRenderableInk*)pNode, &pGroup);
00373             if (pGroup != NULL)
00374             {
00375                 // we got a group, so we have to delete the original node
00376                 pGroup->AttachNode(pNode, NEXT);
00377                 pGroup->FactorOutCommonChildAttributes();
00378                 // unhook from the tree
00379                 pNode->CascadeDelete();
00380 
00381                 // swap the pointers
00382                 Node* pDelete = pNode;
00383                 pNode = pGroup;
00384                 
00385                 delete pDelete;
00386             }
00387 
00388             if (!AddNewObject((NodeRenderableInk*)pNode))
00389                 return FALSE;
00390         }
00391         pNode = pNode->FindNext();
00392     }
00393     CalculateMaxScaling();
00394 
00395     m_bActivated = TRUE;
00396     return TRUE;
00397 
00398 }
00399 
00400 
00401 /***********************************************************************************************
00402 
00403 >   BOOL BrushDefinition::ReplaceBrushWithGroup(NodeRenderableInk* pInk, NodeGroup* pGroup)
00404 
00405     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00406     Created:    13/12/99
00407     Inputs:     pInk - the inknode that we are going to generate a brush from
00408     Outputs:    if pInk has a brush attribute applied to it then we will generate a new group 
00409                 from that brush
00410     Returns     TRUE if successful, FALSE otherwise
00411     Purpose:    We are no longer allowed to make brushes from brushes, because of problems
00412                 when saving and loading.  Instead if we have a brush then we will convert
00413                 it to shapes and use those instead.
00414 
00415     Note:       So you actually have to delete pInk yourself if this function outputs a group.
00416                 I had to do that to get the recursion to work.  Still I'm sure you can handle it.
00417 ***********************************************************************************************/
00418 
00419 BOOL BrushDefinition::ReplaceBrushWithGroup(NodeRenderableInk* pInk, NodeGroup** ppGroup)
00420 {
00421     ERROR2IF(pInk == NULL, FALSE, "Invalid inputs to BrushDefinition::ReplaceBrushWithGroups");
00422 
00423     BOOL  ok = TRUE;
00424     // if we're compound then search through all our children first
00425     if (pInk->IsCompound())
00426     {
00427         NodeGroup* pNodeGroup = NULL;
00428         Node* pChild = pInk->FindFirstChild();
00429         Node* pDelete = NULL;       
00430         
00431         while (pChild)
00432         {
00433             if (pChild->IsAnObject())
00434             {
00435                 if (ok) ok = ReplaceBrushWithGroup((NodeRenderableInk*)pChild, &pNodeGroup);
00436                 if (pNodeGroup)
00437                 {
00438                     // attach the new group next to the child
00439                     pNodeGroup->AttachNode(pChild, NEXT);
00440 
00441                     pNodeGroup->FactorOutCommonChildAttributes();
00442 
00443                     // unhook the child
00444                     pChild->CascadeDelete();
00445                     
00446                     // we're ready to delete the original child
00447                     pDelete =  pChild;
00448 
00449                     // swap the pointer
00450                     pChild = pNodeGroup;    
00451 
00452                     delete pDelete;
00453                 }
00454             }
00455             pChild = pChild->FindNext();
00456             pNodeGroup = NULL;
00457         }
00458     }
00459 
00460     AttrBrushType* pAttrBrush = NULL;
00461     pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), (NodeAttribute**)&pAttrBrush);
00462 
00463     if (pAttrBrush && pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush && ok)
00464     {
00465         //Turn the brush into a group with lots of nodepaths
00466         BrushBecomeAGroup BecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL);
00467         ok = pAttrBrush->DoBecomeA(&BecomeA, pInk);
00468 
00469         // the brush will create a group out of itself and we want to retrieve that
00470         NodeGroup* pBrushGroup = BecomeA.GetNodeGroup();
00471         pBrushGroup->FactorOutCommonChildAttributes();
00472         pBrushGroup->NormaliseAttributes();
00473         if (pBrushGroup != NULL && ok)
00474         {
00475             *ppGroup = pBrushGroup;
00476         }
00477     }
00478     return ok;
00479 }
00480 
00481 /***********************************************************************************************
00482 
00483 >   BOOL BrushDefinition::RegenerateBrush()
00484 
00485     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00486     Created:    13/12/99
00487     Inputs:     -
00488     Returns     TRUE if successful, FALSE otherwise
00489     Purpose:    This function deletes the existing brushrefs and recreates them from scratch from 
00490                 m_pTree.
00491 
00492     Notes:      This function is necessary because of the inaccuracies in our matrix transformation
00493                 system which make it impossible to repeatedly scale without either growing our objects.
00494                 Therefore it is recommended that after every render where an object is scaled this function
00495                 should be called to regenerate the brush.
00496                 Obviously the potential for disaster is very high for instance we run out of memory
00497                 in the middle of the operation.
00498 
00499 ***********************************************************************************************/
00500 
00501 BOOL BrushDefinition::RegenerateBrush()
00502 {
00503     /*  As we now always take copies of the brush objects, this is scheduled for demolition...
00504 
00505     // first we need to transform all the attributes back to where they began
00506     PreExportSubTree();
00507 
00508     // make a temporary array to hold the existing elements, as we do not wish 
00509     // to delete them until we are sure regeneration went ok
00510     CTypedPtrArray <CPtrArray, BrushRef*> TempArray;
00511     
00512     TempArray.SetSize(MAX_BRUSH_OBJECTS, -1);
00513 
00514     // fill the array with our existing objects
00515     BrushRef* pRef = NULL;
00516     INT32 i = 0;
00517     for (i = 0; i < TempArray.GetSize(); i++)
00518     {
00519         pRef = m_BrushRefPtrArray[i];
00520         TempArray.SetAt(i, pRef);
00521         m_BrushRefPtrArray.SetAt(i, NULL); // set existing pointer to null
00522     }
00523 
00524     m_NumBrushObjects = 0;
00525 
00526     // now remake ourselves
00527     if  (GenerateBrush() == TRUE)
00528     {
00529         // delete the existing brushrefs
00530         for (i = 0; i < TempArray.GetSize(); i++)
00531         {
00532             if (TempArray[i] != NULL)
00533             {
00534                 delete TempArray[i];
00535             }
00536         }
00537         TempArray.RemoveAll();
00538         
00539         return TRUE;
00540     }
00541 
00542     // ok so something went wrong, not much we can do except for restore the original objects
00543     // and hope for the best
00544     ERROR3("Uh oh - Regenerate Brush failed");
00545     pRef = NULL;
00546     i= 0;
00547     for (i = 0; i < TempArray.GetSize(); i++)
00548     {
00549         pRef = TempArray[i];
00550         m_BrushRefPtrArray.SetAt(i, pRef); // set existing pointer to null
00551         TempArray[i] = NULL;
00552     }
00553     */
00554     return TRUE;
00555 }
00556 
00557 
00558 
00559 /***********************************************************************************************
00560 
00561 >   BOOL BrushDefinition::IsActivated()
00562 
00563     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00564     Created:    13/12/99
00565     Inputs:     -
00566     Returns     the member indicating whether this definition is active
00567     Purpose:    To see if this definition was initialised ok or if it was subsequently deactivated
00568                 for some reason
00569 
00570 ***********************************************************************************************/
00571 
00572 BOOL BrushDefinition::IsActivated()
00573 {
00574     return m_bActivated;
00575 }
00576 
00577 
00578 /***********************************************************************************************
00579 
00580 >   void BrushDefinition::SetActivated(BOOL Value)
00581 
00582     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00583     Created:    13/12/99
00584     Inputs:     Value - the value to set
00585     Returns     -
00586     Purpose:    To set this definition activated or not
00587 
00588 ***********************************************************************************************/
00589 
00590 void BrushDefinition::SetActivated(BOOL Value)
00591 {
00592     m_bActivated = Value;
00593 }
00594 
00595 /***********************************************************************************************
00596 
00597 >   BOOL BrushDefinition::AddNewObject(NodeRenderableInk* pInkNode)
00598 
00599     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00600     Created:    13/12/99
00601     Inputs:     pInkNode - node to add
00602     Returns     TRUE if successful, FALSE otherwise
00603     Purpose:    creates a brushref object from the inknode and its attributes, then adds the
00604                 brushref to the member array
00605 
00606 ***********************************************************************************************/
00607 
00608 BOOL BrushDefinition::AddNewObject(NodeRenderableInk* pInkNode)
00609 {
00610 
00611     if (pInkNode == NULL)
00612     {
00613         ERROR3("Ink node is NULL");
00614         return FALSE;
00615     }
00616 
00617     if (m_NumBrushObjects >= MAX_BRUSH_OBJECTS)
00618     {
00619         ERROR3("Cannot exceed MAX_BRUSH_OBJECTS");
00620         return FALSE;
00621     }
00622 
00623     BrushRef *pNewBrushRef = new BrushRef;
00624 
00625     if (pNewBrushRef == NULL)
00626     {
00627         ERROR3("Failed to allocate brushref");
00628         return FALSE;
00629     }
00630     
00631 
00632     
00633     if (!pNewBrushRef->Initialise(pInkNode))
00634     {
00635         ERROR3("Failed to initialise brushref");
00636         return FALSE;
00637     }
00638 
00639     m_BrushRefPtrArray[m_NumBrushObjects++] = pNewBrushRef;
00640 
00641     return TRUE;
00642 
00643 }
00644 
00645 
00646 /***********************************************************************************************
00647 
00648 >   void BrushDefinition::InitialiseBrushArray(UINT32 NumObjects)
00649 
00650     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00651     Created:    6/10/99
00652     Inputs:     Number of objects to size the array at
00653     Returns:    -
00654     Purpose:    Clears out the m_BrushRefPtrArray if it is not empty, and sets the size 
00655 
00656 ***********************************************************************************************/
00657 
00658 void BrushDefinition::InitialiseBrushArray(UINT32 NumObjects) 
00659 {
00660     size_t              i = 0;
00661     while( i < m_BrushRefPtrArray.size() )
00662     {
00663         delete m_BrushRefPtrArray[i++];
00664     }
00665     m_BrushRefPtrArray.clear();
00666 
00667     m_BrushRefPtrArray.resize( NumObjects );
00668 
00669     // fill the array with NULL objects so that we can check later
00670     // to see if our allocations have worked
00671     i = 0;
00672     while( i < m_BrushRefPtrArray.size() )
00673     {
00674         m_BrushRefPtrArray[i++] = NULL;
00675     }
00676 
00677 }
00678 
00679 
00680 /********************************************************************************************
00681 
00682 >   BOOL BrushDefinition::IsDifferent(BrushDefinition *pOther)
00683 
00684     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00685     Created:    13/12/99
00686 
00687     Inputs:     pOther - the Brush to compare this Brush to
00688 
00689     Returns:    TRUE if they're different in any way,
00690                 FALSE if they are identical definitions
00691 
00692     Purpose:    Determine if 2 BrushDefinitions are considered different.
00693                 Used when adding Brushes to the global list, so that like Brushes
00694                 can be merged.
00695 
00696 ********************************************************************************************/
00697 
00698 BOOL BrushDefinition::IsDifferent(LineDefinition *pOther)
00699 {
00700     ERROR3IF(pOther == NULL, "Illegal NULL param");
00701 
00702 //  String_32* pName = pOther->GetLineName();
00703     if (!m_Name.IsIdentical(*pOther->GetLineName()))
00704         return(TRUE);
00705 
00706     if (pOther->GetLineTree() == NULL || m_pTree == NULL)
00707     {
00708         ERROR3("BrushDefinition has not been properly initialised");
00709         return(TRUE);
00710     }
00711 
00712     /*
00713     // --- Check to see if the brush bounds are equal
00714     DocRect OtherBounds = ((Spread *)(pOther->GetLineTree()))->GetBoundingRect();
00715     DocRect Bounds = ((Spread *)m_pTree)->GetBoundingRect();
00716     if (Bounds != OtherBounds)
00717         return(TRUE);
00718     */
00719     // --- Check the subtrees node-for-node to see if they are the same
00720     Node *pCurNode1 = m_pTree->FindFirstDepthFirst();
00721     Node *pCurNode2 = pOther->GetLineTree()->FindFirstDepthFirst();
00722 
00723     while (pCurNode1 != NULL && pCurNode2 != NULL)
00724     {
00725         // if they are fill attributes then we want to translate both attributes to the origin.
00726         // This prevents us getting duplication of brushes when we have the 
00727         // same thing in a different place, note that occasionally this fails due to rounding errors.
00728         if (pCurNode1->IsAFillAttr() && pCurNode2->IsAFillAttr() && 
00729             !((AttrFillGeometry*)pCurNode1)->IsAStrokeColour() && 
00730             !((AttrFillGeometry*)pCurNode1)->IsAStrokeTransp())
00731         {
00732             AttrFillGeometry* pFill1 = (AttrFillGeometry*)pCurNode1;
00733             AttrFillGeometry* pFill2 = (AttrFillGeometry*)pCurNode2;
00734             DocCoord* pStartPoint1 = pFill1->GetStartPoint();
00735             DocCoord* pStartPoint2 = pFill2->GetStartPoint();
00736         
00737             // translate them both to 0,0
00738             if (pStartPoint1 && pStartPoint2)
00739             {
00740                 Trans2DMatrix Trans1(-pStartPoint1->x, -pStartPoint1->y);
00741                 Trans2DMatrix Trans2(-pStartPoint2->x, -pStartPoint2->y);
00742                 pFill1->Transform(Trans1);
00743                 pFill2->Transform(Trans2);
00744                 BOOL Different = FALSE;
00745                 if (pCurNode1->IsDifferent(pCurNode2))
00746                     Different = TRUE;
00747 
00748                 Trans1.Invert();
00749                 Trans2.Invert();
00750                 pFill1->Transform(Trans1);
00751                 pFill2->Transform(Trans2);
00752                 if (Different == TRUE)
00753                     return TRUE;
00754             }
00755 
00756         }
00757         else
00758             // likewise if they are an object then translate from the centre of the bounding
00759             // rect to the origin
00760             if (pCurNode1->IsAnObject() && pCurNode2->IsAnObject())
00761             {
00762                 NodeRenderableInk* pInk1 = (NodeRenderableInk*)pCurNode1;
00763                 NodeRenderableInk* pInk2 = (NodeRenderableInk*)pCurNode2;
00764                 DocRect BRect1 = ((NodeRenderableInk*)pCurNode1)->GetBoundingRect();
00765                 DocRect BRect2 = ((NodeRenderableInk*)pCurNode2)->GetBoundingRect();
00766                 DocCoord Coord1 = BRect1.Centre();
00767                 DocCoord Coord2 = BRect2.Centre();
00768                 Trans2DMatrix Trans1(-Coord1.x, -Coord1.y);
00769                 Trans2DMatrix Trans2(-Coord2.x, -Coord2.y);
00770                 pInk1->Transform(Trans1);
00771                 pInk2->Transform(Trans2);
00772                 BOOL Different = FALSE;
00773                 if (pCurNode1->IsDifferent(pCurNode2))
00774                     Different = TRUE;
00775                 
00776                 Trans1.Invert();
00777                 Trans2.Invert();
00778                 pInk1->Transform(Trans1);
00779                 pInk2->Transform(Trans2);
00780                 if (Different ==TRUE)
00781                     return TRUE;
00782             }
00783         else 
00784         {
00785             // otherwise just use the regular == operator
00786             if (pCurNode1->IsDifferent(pCurNode2))
00787                 return(TRUE);
00788         }
00789 
00790         // And go to the next node in both brushes
00791         pCurNode1 = pCurNode1->FindNextDepthFirst(m_pTree);
00792         pCurNode2 = pCurNode2->FindNextDepthFirst(pOther->GetLineTree());
00793     }
00794 
00795     if (!AreBrushParametersIdentical((BrushDefinition*)pOther))
00796         return TRUE;
00797 
00798     // If we did the entire search and both pointers ended up NULL simultaneously, then
00799     // we have an exact match
00800     if (pCurNode1 == NULL && pCurNode2 == NULL)
00801         return(FALSE);
00802 
00803     return(TRUE);
00804 }
00805 
00806 
00807 /********************************************************************************************
00808 
00809 >   BOOL BrushDefinition::IsDifferent(BrushDefinition *pOther)
00810 
00811     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00812     Created:    13/12/99
00813 
00814     Inputs:     pOther - the Brush to compare this Brush to
00815 
00816     Returns:    TRUE if they're different 
00817                 FALSE if they are identical 
00818 
00819     Purpose:    Compares the member variables of the two brushes
00820 
00821 ********************************************************************************************/
00822 
00823 BOOL BrushDefinition::AreBrushParametersIdentical(BrushDefinition* pOther)
00824 {
00825     if (m_BrushSpacing          != pOther->m_BrushSpacing           ||
00826         m_BrushSpacingIncrProp  != pOther->m_BrushSpacingIncrProp   ||
00827         m_BrushSpacingIncrConst != pOther->m_BrushSpacingIncrConst  ||
00828         m_BrushSpacingMaxRand   != pOther->m_BrushSpacingMaxRand    ||
00829         m_BrushSpacingRandSeed  != pOther->m_BrushSpacingRandSeed   ||
00830         m_SpacingMaxPressure    != pOther->m_SpacingMaxPressure     ||
00831         
00832         m_bTile                 != pOther->m_bTile                  ||
00833         m_bRotate               != pOther->m_bRotate                ||
00834         
00835         m_RotateAngle           != pOther->m_RotateAngle            ||
00836         m_RotAngleIncrProp      != pOther->m_RotAngleIncrProp       ||
00837         m_RotAngleIncrConst     != pOther->m_RotAngleIncrConst      ||
00838         m_RotationMaxRand       != pOther->m_RotationMaxRand        ||
00839         m_RotationRandSeed      != pOther->m_RotationRandSeed       ||
00840         m_RotationMaxPressure   != pOther->m_RotationMaxPressure    ||
00841 
00842         m_PathOffsetType        != pOther->m_PathOffsetType         ||
00843         m_PathOffsetValue       != pOther->m_PathOffsetValue        ||
00844         m_PathOffsetIncrProp    != pOther->m_PathOffsetIncrProp     ||
00845         m_PathOffsetIncrConst   != pOther->m_PathOffsetIncrConst    ||
00846         m_OffsetTypeRandSeed    != pOther->m_OffsetTypeRandSeed     ||
00847         m_OffsetValueMaxRand    != pOther->m_OffsetValueMaxRand     ||
00848         m_OffsetValueRandSeed   != pOther->m_OffsetValueRandSeed    ||
00849         
00850         m_BrushScaling          != pOther->m_BrushScaling           ||
00851         m_BrushScalingIncr      != pOther->m_BrushScalingIncr       ||
00852         m_BrushScalingIncrConst != pOther->m_BrushScalingIncrConst  ||
00853         m_BrushScalingMaxRand   != pOther->m_BrushScalingMaxRand    ||
00854         m_BrushScalingRandSeed  != pOther->m_BrushScalingRandSeed   ||
00855         m_ScalingMaxPressure    != pOther->m_ScalingMaxPressure     ||
00856 
00857         m_BrushHueIncrement     != pOther->m_BrushHueIncrement      ||
00858         m_BrushHueMaxRand       != pOther->m_BrushHueMaxRand        ||
00859         m_BrushHueRandSeed      != pOther->m_BrushHueRandSeed       ||
00860         m_HueMaxPressure        != pOther->m_HueMaxPressure         ||
00861 
00862         m_BrushSatIncrement     != pOther->m_BrushSatIncrement      ||
00863         m_BrushSatMaxRand       != pOther->m_BrushSatMaxRand        ||
00864         m_BrushSatRandSeed      != pOther->m_BrushSatRandSeed       ||
00865         m_SatMaxPressure        != pOther->m_SatMaxPressure         ||
00866 
00867         m_SequenceType          != pOther->m_SequenceType           ||
00868         m_SequenceRandSeed      != pOther->m_SequenceRandSeed       ||
00869 
00870         m_TimeStampPeriod       != pOther->m_TimeStampPeriod)
00871         return FALSE;
00872     else
00873         return TRUE;
00874 }
00875 
00876 
00877 /********************************************************************************************
00878 
00879 >   BrushDefinition* BrushDefinition::Copy()
00880 
00881     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00882     Created:    22/5/2000
00883 
00884     Inputs:     -
00885 
00886     Returns:    Pointer to new brush definition if succesful, NULL if it fails
00887     Purpose:    Makes a copy of this definition, inknodes and all.
00888     
00889 ********************************************************************************************/
00890 
00891 BrushDefinition* BrushDefinition::Copy()
00892 {   
00893     // we need to make a range in order to copy the nodes to the new brush definition
00894     // so we need the start and end nodes of the range
00895     Node* pStart = NULL;
00896     Node* pNext = NULL;
00897     Node* pLast = NULL;
00898 
00899     // don't forget we start on a spread
00900     Node* pTree = GetLineTree();
00901     if (pTree != NULL)
00902         pTree = pTree->FindFirstChild();  // get the layer
00903     if (pTree != NULL)
00904             pTree = pTree->FindFirstChild();  // get the first ink object
00905     
00906     if (pTree == NULL)
00907     {
00908         ERROR3("Tree node is NULL in BrushDefinition::Copy()");
00909         return NULL;
00910     }
00911     
00912     pNext = pStart = pTree;
00913     while (pNext != NULL)
00914     {
00915         pNext->SetSelected(FALSE);  // we need to do this so that Range::FindFirst doesn't return NULL
00916         pLast = pNext;
00917         pNext = pLast->FindNext();
00918     }
00919 
00920 
00921     // make a range out of our subtree as thats the easiest way to copy all the nodes
00922     RangeControl rc;
00923     rc.Unselected = TRUE;
00924     rc.Selected = FALSE;
00925     Range BrushRange(pStart, pLast, rc);
00926     
00927     // make a new spread to attach our copy tree to
00928     Spread* pSpread = new Spread;
00929     if (pSpread == NULL)
00930     {
00931         ERROR3("Failed to allocate spread in BrushDefinition::Copy");
00932         return NULL;
00933     }
00934 
00935     Layer              *pLayer = new Layer( pSpread, FIRSTCHILD, String_256( TEXT("Diccon did this") ) );
00936     if (pLayer == NULL)
00937     {
00938         ERROR3("Failed to allocate layer in BrushDefinition::Copy");
00939         delete pSpread;
00940         return NULL;
00941     }
00942 
00943     // we need to reset our attributes in the same way that we need to with exporting.
00944     // If we don't do this then attributes with control points end up vanishing
00945     PreExportSubTree();
00946 
00947     if (!pLayer->CopyComplexRange(BrushRange))
00948     {
00949         ERROR3("Failed to copy range in BrushDefinition::Copy");
00950         delete pSpread;
00951         delete pLayer;
00952         return NULL;
00953     }
00954 
00955     PostExportSubTree();
00956 
00957     // lets allocate ourselves a new empty definition
00958     BrushDefinition* pNewBrushDef = new BrushDefinition(pSpread);
00959 
00960     if (pNewBrushDef == NULL)
00961     {
00962         ERROR3("Failed to allocate brush definition in BrushDefinition* BrushDefinition::Copy");
00963         delete pSpread;
00964         delete pLayer;
00965         return NULL;
00966     }
00967 
00968     if (!pNewBrushDef->IsActivated())
00969     {
00970         ERROR3("Brush definition failed to initialise");
00971         delete pNewBrushDef;
00972         delete pSpread;
00973         delete pLayer;
00974         return NULL;
00975     }
00976 
00977     return pNewBrushDef;
00978 }
00979 
00980 /***********************************************************************************************
00981 
00982 >   BOOL BrushDefinition::UsesPressure()
00983 
00984     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
00985     Created:    16/6/2000
00986     Inputs:     -
00987     Returns     TRUE if this brush has one of its pressure variables set
00988     Purpose:    as above
00989 ***********************************************************************************************/
00990 
00991 BOOL BrushDefinition::UsesPressure()
00992 {
00993     if (m_ScalingMaxPressure != 0  ||
00994         m_RotationMaxPressure != 0 ||
00995         m_TranspMaxPressure != 0)
00996         return TRUE;
00997 
00998     return FALSE;
00999 }
01000 
01001 
01002 /***********************************************************************************************
01003 
01004 >   static BOOL BrushDefinition::ObjectCanCreateBrush(NodeRenderableInk* pObject)
01005 
01006     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
01007     Created:    16/6/2000
01008     Inputs:     pObject - the object to test
01009     Returns     TRUE if we are allowed to make a brush out of this object, FALSE otherwise
01010     Purpose:    To determine if this ink node can be made into a brush, currently returns TRUE for
01011                 except for bevels, shadows, and contours, which are too damn awkward.
01012 ***********************************************************************************************/
01013 
01014 BOOL BrushDefinition::ObjectCanCreateBrush(NodeRenderableInk* pObject)
01015 {
01016     ERROR2IF(pObject == NULL, FALSE, "Object is NULL in BrushDefinition::ObjectCanCreateBrush");
01017 
01018     // only way to do it is some ugly IKO's
01019     if (pObject->IS_KIND_OF(NodeShadow))
01020         return FALSE;
01021     if (pObject->IS_KIND_OF(NodeContour))
01022         return FALSE;
01023     if (pObject->IS_KIND_OF(NodeBevel))
01024         return FALSE;
01025 
01026     // if its compound then check all the children
01027     if (pObject->IsCompound())
01028     {
01029         Node* pChild = pObject->FindFirstChild();
01030         while (pChild)
01031         {
01032             if (pChild->IsAnObject())
01033             {
01034                 if (!ObjectCanCreateBrush((NodeRenderableInk*)pChild))
01035                     return FALSE;
01036             }
01037             pChild = pChild->FindNext();
01038         }
01039     }
01040 
01041     return TRUE;
01042 
01043 }
01044 
01045 
01046 /*********************************************************************************************
01047 
01048 >    BOOL BrushDefinition::StartRender()
01049 
01050      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01051      Created:   4/9/2000
01052      Inputs:    -
01053      Outputs:   -
01054      Returns:   TRUE if successful, FALSE if not.  If we return FALSE then don't even think
01055                 about rendering with this brush.
01056 
01057      Purpose:   Basically due to imprecision in the arithmetic system we ended up corrupting the
01058                 brush data if we used it too much.  So to get around this we never transform
01059                 the original brush data, instead we make copies of it each time we want to render
01060                 a version of this brush.
01061                 This function generates the data copies that we need to render this brush, make 
01062                 sure you call StopRender() to get rid of them.
01063 **********************************************************************************************/
01064        
01065 BOOL BrushDefinition::StartRender()
01066 {
01067     BrushRef* pBrushRef = GetFirstBrushRef();
01068     BOOL ok = TRUE;
01069     while (pBrushRef != NULL)
01070     {
01071         if (ok)
01072             ok = pBrushRef->MakeCopiesForRendering();
01073         pBrushRef = GetNextBrushRef();
01074     }
01075 
01076     return ok;
01077     
01078 }
01079 
01080 
01081 
01082 /*********************************************************************************************
01083 
01084 >    void BrushDefinition::StopRender()
01085 
01086      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01087      Created:   4/9/2000
01088      Inputs:    -
01089      Outputs:   -
01090      Returns:   -
01091 
01092      Purpose:   Call this when you have finished rendering a node with this brush, it deletes
01093                 the copied data that we used for rendering
01094 **********************************************************************************************/
01095        
01096 void BrushDefinition::StopRender()
01097 {
01098     BrushRef* pBrushRef = GetFirstBrushRef();
01099     while (pBrushRef != NULL)
01100     {
01101         pBrushRef->DeleteRenderCopies();
01102         pBrushRef = GetNextBrushRef();
01103     }
01104 
01105     return;
01106 
01107 }
01108 
01109 /***********************************************************************************************
01110 
01111 >   void BrushDefinition::CalculateMaxScaling()
01112 
01113     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
01114     Created:    13/12/99
01115     Inputs:     -
01116     Returns     -
01117     Purpose:    Calculates the maximum scaling value possible by dividing the maximum size value by
01118                 the longest side of the bounding rect of the largest object
01119 ***********************************************************************************************/
01120 
01121 void BrushDefinition::CalculateMaxScaling()
01122 {
01123     DocRect BRect = GetLargestBoundingBox();
01124     if (BRect.IsEmpty())
01125     {
01126         //ERROR3("Bounding rect is empty in BrushDefinition::CalculateMaxScaling");
01127         return;
01128     }
01129     MILLIPOINT LongestSide = BRect.Height() > BRect.Width() ? BRect.Height() : BRect.Width();
01130     m_MaxScaling = MAX_BRUSH_SIZE  / LongestSide;
01131 
01132 }
01133 
01134 
01135 /***********************************************************************************************
01136 
01137 >   double BrushDefinition::GetMaxScaling()
01138 
01139     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
01140     Created:    13/12/99
01141     Inputs:     -
01142     Returns     the maximum scaling value
01143     Purpose:    as above
01144 ***********************************************************************************************/
01145 
01146 double BrushDefinition::GetMaxScaling()
01147 {
01148     return m_MaxScaling;
01149 }
01150 
01151 /********************************************************************************************
01152 
01153 >   BOOL BrushDefinition::SetBrushSpacing(double Spacing) 
01154 
01155     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01156     Created:    6/10/99
01157     Inputs:     the spacing value to set
01158     Returns:    TRUE if successful, FALSE if the spacing is invalid 
01159     Purpose:    To set the spacing between brush objects
01160     SeeAlso:    -
01161 
01162 ********************************************************************************************/
01163 
01164 BOOL BrushDefinition::SetSpacing(MILLIPOINT Spacing)
01165 {
01166     if (Spacing < MIN_BRUSH_SPACING || Spacing > MAX_BRUSH_SPACING)
01167         return FALSE;
01168     
01169     m_BrushSpacing = Spacing;
01170 
01171     return TRUE;
01172 
01173 }
01174 
01175 
01176 
01177 /********************************************************************************************
01178 
01179 >   MILLIPOINT BrushDefinition::GetBrushSpacing() 
01180 
01181     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01182     Created:    6/10/99
01183     Returns:    the spacing between the brush objects 
01184     Purpose:    As above
01185 
01186 ********************************************************************************************/
01187 
01188 MILLIPOINT BrushDefinition::GetSpacing()
01189 {
01190     return m_BrushSpacing;
01191 }
01192 
01193 
01194 
01195 /********************************************************************************************
01196 
01197 >   BOOL PathProcessorBrush::SetSpacingIncrProp(double Incr) 
01198 
01199     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01200     Created:    6/10/99
01201     Inputs:     the spacing increment value to set
01202     Returns:    TRUE if we can draw 10 objects, FALSE if the spacing is invalid 
01203     Purpose:    To set the proportional spacing increment between brush objects
01204     SeeAlso:    -
01205 
01206 ********************************************************************************************/
01207 
01208 BOOL BrushDefinition::SetSpacingIncrProp(double Incr)
01209 {
01210     if (Incr <= 0)
01211         return FALSE;
01212     
01213     if (Incr != 1.0)
01214     {
01215         double TenIncr = pow(Incr, 10.0);
01216         double TenthSpacing = m_BrushSpacing * TenIncr;
01217         if (TenthSpacing >= MAX_BRUSH_SPACING || TenthSpacing < MIN_BRUSH_SPACING)
01218         {
01219             ERROR3("Illegal increment value");
01220             return FALSE;
01221         }
01222     }
01223     m_BrushSpacingIncrProp = Incr;
01224     return TRUE;
01225 }
01226 
01227 
01228 
01229 /********************************************************************************************
01230 
01231 >   MILLIPOINT BrushDefinition::GetSpacingIncrProp() 
01232 
01233     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01234     Created:    6/10/99
01235     Returns:    the proportional spacing increment between the brush objects 
01236     Purpose:    As above
01237 
01238 ********************************************************************************************/
01239 
01240 double BrushDefinition::GetSpacingIncrProp()
01241 {
01242     return m_BrushSpacingIncrProp;
01243 }
01244 
01245 
01246 /********************************************************************************************
01247 
01248 >   BOOL PathProcessorBrush::SetSpacingIncrConst(MILLIPOINT Incr) 
01249 
01250     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01251     Created:    6/10/99
01252     Inputs:     the constant spacing increment value to set
01253     Returns:    TRUE if we can draw 10 objects, FALSE if the spacing is invalid 
01254     Purpose:    To set the proportional spacing increment between brush objects
01255     SeeAlso:    -
01256 
01257 ********************************************************************************************/
01258 
01259 BOOL BrushDefinition::SetSpacingIncrConst(MILLIPOIN