OpCombineShapes Class Reference

Base class op that does all the shape combining ops, like add, subtract and intersect,etc. More...

#include <combshps.h>

Inheritance diagram for OpCombineShapes:

SelOperation UndoableOperation Operation MessageHandler ListItem CCObject SimpleCCObject OpAddShapes OpIntersectShapes OpSliceShapes OpSubtractShapes List of all members.

Public Member Functions

 OpCombineShapes ()
 Default constructor.
 ~OpCombineShapes ()
 Default destructor - does nothing.
virtual void Do (OpDescriptor *)
 This is it! The routine that gets all the hard work underway.
virtual void GetOpName (String_256 *)
 The GetOpName fn is overridden so that we return back a description appropriate to the type of attribute that the operation applies.

Static Public Member Functions

static BOOL Init ()
 Declares a preference that allows you to clear memory in delete().
static OpState GetState (String_256 *, OpDescriptor *)
 Gives the op an oppertunity to say whether the op is executable or not.

Protected Attributes

CombineReason Reason

Private Member Functions

BOOL ConvertToShapes (Range *pRange, BOOL bCombineEffectBitmaps)
 This subtracts all the shapes in all the lists from all the lines in all the lists This subtracts all the shapes in all the lists from pLine This subtracts all the shapes in pSelObjPathList from pLine This generates the data structure that holds all the paths generated by the selected objects. NOTE! Pointers to paths in the tree are stored in the lists (unlike CreateSelObjPathLists).
BOOL CreateSelObjPathLists (BOOL bCombineEffectBitmaps)
 This generates the data structure that holds all the paths generated by the selected objects.
BOOL CreateSelObjPathList (Node *pNode, SelObjPathList *pSelObjPathList, BOOL IsFirstNode=TRUE, BOOL bCombineEffectBitmaps=TRUE, BecomeAReason reason=BECOMEA_PASSBACK)
 This generates the data structure that holds all the paths generated by a given selected object.
BOOL DoAddShapes ()
 This adds all the paths together to form a combined silhouette path of the selection.
BOOL AddPathsFromList (Path *pPath, SelObjPathList *pSelObjPathList)
 This adds all the paths in the list together to form a combined silhouette path of the selection.
BOOL AddPaths (Path *pPath1, Path *pPath2)
 This adds the two paths together using ClipPathToPath, storing the result in pPath1.
BOOL AddOrMergePaths (Path *pPath1, Path *pPath2)
 Helper function for DoAddShapes and AddPathsFromList.
BOOL DoCombineShapes (ClipStyle Style, BOOL bCombineEffectBitmaps)
 Firstly, this adds all the paths together in the last list to form the clip path. Then all the other paths in the other lists are combined with the clip path, using the method specified in 'Flags'.
BOOL CombinePathsFromList (ClipStyle Style, Path *pClipPath, Path *pStrokedClipPath, SelObjPathList *pSelObjPathList, BOOL bCombineEffectBitmaps=TRUE)
 This combines pClipPath with all the paths in pSelObjPathList.
BOOL CombinePaths (ClipStyle Style, Path *pClipPath, Path *pStrokedClipPath, Path *pSrcPath, Path *pDestPath)
 This combines pClipPath from pSrcPath using ClipPathToPath, storing the result in pDestPath.
BOOL DoHideListedNodes (BOOL bCombineEffectBitmaps)
 This hides all the nodes that were selected when the op was invoked.
BOOL DoSelectResultNodes (BOOL bCombineEffectBitmaps)
 This selects all the nodes in the results list.
BOOL ApplyAttributes (NodeRenderableInk *pSrcNode, NodeRenderableInk *pDestNode, CCAttrMap *pAttrMap, SelObjPathListItem *pListItem=NULL)
 This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.
BOOL ApplyEffects (SelObjPathListItem *pListItem, NodeRenderableInk *pDestNode, NodeRenderableInk *pParentEffect)
 To delete the extra attributes we allocated in AdjustAttributesForStroke This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.
BOOL StrokePathToPath (Path *pPath, Path *pDest)
 This picks out a subpath from Src and places it into pDest Strokes pPath, and places result in pDest.
BOOL ConvertClipLineToShape (Path *pClipPath, Path *pSrcPath, Path *pDestPath)
 Only call this if pClipPath is a line.
DocRect GetBoundingRect (Path *pPath)
 This gets the exact pure path bounds, using GDraw_CalcStrokeBBox.
BOOL DoInsertNewNode (NodeRenderableBounded *pNewNode)
 High-level interface to UndoableOperation::DoInsertNode().
UINT32 AdjustAttributesForStroke (CCAttrMap *pAttrMap, SelObjPathListItem *pListItem=NULL)
 If we are doing combine shapes on a stroked shape then we need to make the following changes: 1) Do not keep the AttrStrokeType and VariableWidth around, this is because their effects have been achieved via the DoBecomeA so if we keep them then effectively we are stroking our final output twice. 2) Set line width to zero. Once again the line width effect has been extracted from the DoBecomeA 3) Use the current stroke colour for the new fill colour. 4) Ditch the stroke colour?
BOOL BeginSlowJob ()
 Wrapper around BeginSlowJob() that sets up the hour glass counter correctly.
SelObjPathListGetFirstList ()
SelObjPathListGetLastList ()
SelObjPathListGetNextList (SelObjPathList *pItem)
ListRangeCopyRange (Range *pRange)
 Copy the specified range of nodes.

Private Attributes

List ListOfSelObjPathLists
ListRangepListOfResults
DocRect OriginalBoundingRect
SelRangepSelRange
INT32 NumPaths
INT32 JobCount
INT32 NodeInsertCount
NodepContextNode
AttachNodeDirection AttachDir

Detailed Description

Base class op that does all the shape combining ops, like add, subtract and intersect,etc.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
11/1/95

Definition at line 232 of file combshps.h.


Constructor & Destructor Documentation

OpCombineShapes::OpCombineShapes  ) 
 

Default constructor.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
-

Definition at line 167 of file combshps.cpp.

00168 {
00169     Reason      = COMBINE_NONE;
00170     pSelRange   = NULL;
00171     NumPaths    = 0;
00172     JobCount    = 0;
00173 
00174     pContextNode = NULL;
00175     pListOfResults = NULL;
00176 }

OpCombineShapes::~OpCombineShapes  ) 
 

Default destructor - does nothing.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
-

Definition at line 193 of file combshps.cpp.

00194 {
00195     ListOfSelObjPathLists.DeleteAll();
00196     if (pListOfResults)
00197     {
00198         delete pListOfResults;
00199         pListOfResults = NULL;
00200     };
00201 }


Member Function Documentation

BOOL OpCombineShapes::AddOrMergePaths Path pPath1,
Path pPath2
[private]
 

Helper function for DoAddShapes and AddPathsFromList.

Author:
Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com>
Date:
08/02/2005
Parameters:
pPath1 - pointer to a Path [INPUTS] pPath2 - pointer to a Path
Returns:
TRUE if ok, FALSE if bother

Definition at line 749 of file combshps.cpp.

00750 {
00751     BOOL ok = TRUE;
00752     // if num coords == 0, the path is effectively empty, so just copy this node path's path
00753     if (pPath1->GetNumCoords() == 0)
00754         ok = pPath1->MergeTwoPaths(*pPath2);
00755     else
00756     {
00757         // Otherwise actually 'add' the paths together, using ClipPathToPath
00758         ok = AddPaths(pPath1,pPath2);
00759 
00760         // If this fails, we can just merge the two paths together as this achieves the desired
00761         // silhouette effect
00762         if (!ok) ok = pPath1->MergeTwoPaths(*pPath2);
00763 
00764     }
00765     return(ok);
00766 }

BOOL OpCombineShapes::AddPaths Path pPath1,
Path pPath2
[private]
 

This adds the two paths together using ClipPathToPath, storing the result in pPath1.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
pPath1 = ptr to first path, and also the place to store resultant path [INPUTS] pPath2 = ptr to second path, i.e. the path to add to pPath1
*pPath1 contains the result of adding the two paths (if TRUE returned). [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
OpCombineShapes::AddPathsFromList()

Definition at line 785 of file combshps.cpp.

00786 {
00787     ERROR2IF(pPath1 == NULL,FALSE,"pPath1 == NULL");
00788     ERROR2IF(pPath2 == NULL,FALSE,"pPath2 == NULL");
00789 
00790     // check for a zero-bounding box path
00791     DocRect dr = pPath2->GetBoundingRect();
00792 
00793     if (dr.Width() <= 1 && dr.Height() <= 1)
00794     {
00795         ERROR3("We have a zero bounding rect path");
00796         return FALSE;
00797     }
00798 
00799     Path StrokedPath2;
00800     if (!StrokedPath2.Initialise()) return FALSE;
00801 
00802     // is path 2 a line?
00803     if (pPath2->GetPathType() == PATHTYPE_LINE)
00804     {
00805         if (!StrokePathToPath(pPath2,&StrokedPath2))
00806             return FALSE;
00807     }
00808 
00809     INT32 len = CombinePaths(CLIP_STYLE_ADD,pPath2,&StrokedPath2,pPath1,pPath1);
00810 
00811     return (len > 0);
00812 }

BOOL OpCombineShapes::AddPathsFromList Path pPath,
SelObjPathList pSelObjPathList
[private]
 

This adds all the paths in the list together to form a combined silhouette path of the selection.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
pPath = ptr to path to place the result [INPUTS] pSelObjPathList = ptr to list of paths
pPath contains the union of all the paths in pSelObjPathList [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This can be thought of as a logical OR of the paths
Returns:
Errors: -
See also:
OpCombineShapes::Do()

Definition at line 671 of file combshps.cpp.

00672 {
00673     // Check our input params
00674     ERROR3IF(pPath == NULL,"pPath == NULL");
00675     ERROR3IF(pSelObjPathList == NULL,"pSelObjPathList== NULL");
00676     if (pPath == NULL || pSelObjPathList == NULL) return FALSE;
00677 
00678     NodePath* pTempPath = new NodePath;
00679     if (pTempPath == NULL)       return FALSE;
00680     if (!pTempPath->SetUpPath()) return FALSE;
00681 
00682     // Get the first path in this list
00683     SelObjPathListItem* pSelObjPathListItem = pSelObjPathList->GetFirstItem();
00684 
00685     BOOL ok = TRUE;
00686 
00687     while (ok && pSelObjPathListItem != NULL)
00688     {
00689         // Get a pointer to the actual node path in this list item
00690         Path* pSrcPath = pSelObjPathListItem->GetPath();
00691 
00692         if (pSrcPath != NULL)
00693         {
00694             // Add this path into the TempPath
00695             ok = AddOrMergePaths(&(pTempPath->InkPath), pSrcPath);
00696 
00697             // If the TempPath is now too large
00698             if (ok && pTempPath->InkPath.GetNumCoords() > AddPathGranularity)
00699             {
00700                 // Combine the TempPath with the output path
00701                 ok = AddOrMergePaths(pPath, &(pTempPath->InkPath));
00702                 // And clear the TempPath
00703                 if (ok) ok = pTempPath->InkPath.ClearPath(FALSE);
00704             }
00705             if (ok) pSelObjPathList->SetProducedPaths(TRUE);
00706         }
00707         else
00708         {
00709             // What? No Src path? Surely not!
00710             ERROR3("pSrcPath == NULL");
00711             ok = FALSE;
00712         }
00713 
00714         if (ok)
00715             ok = ::ContinueSlowJob(++JobCount);
00716 
00717         // Get the next item in the selected object path list.
00718         pSelObjPathListItem = pSelObjPathList->GetNextItem(pSelObjPathListItem);
00719     }
00720 
00721     // If the TempPath has anything in
00722     if (ok && pTempPath->InkPath.GetNumCoords() > 0)
00723     {
00724         // Combine the TempPath with the output path
00725         ok = AddOrMergePaths(pPath, &(pTempPath->InkPath));
00726     }
00727 
00728     delete pTempPath;
00729 
00730     return (ok);
00731 }

UINT32 OpCombineShapes::AdjustAttributesForStroke CCAttrMap pAttrMap,
SelObjPathListItem pListItem = NULL
[private]
 

If we are doing combine shapes on a stroked shape then we need to make the following changes: 1) Do not keep the AttrStrokeType and VariableWidth around, this is because their effects have been achieved via the DoBecomeA so if we keep them then effectively we are stroking our final output twice. 2) Set line width to zero. Once again the line width effect has been extracted from the DoBecomeA 3) Use the current stroke colour for the new fill colour. 4) Ditch the stroke colour?

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
4/9/2000
Parameters:
pAttrMap - the attribute map that we are about to apply to our combined node [INPUTS]
swaps a few things around in the map [OUTPUTS]
Returns:
0 - if we do not have an active stroke attribute (therefore do nothing) 1 - if we do have an active stroke and everything went well 2 - if we have an active stroke and something went wrong (e.g. out of memory)
We must actually make new nodes for these attributes as changing the existing ones will mess up our undo. These attributes must be deleted after they are applied by calling DeleteExtraAttributesForStroke()
Returns:
Errors: -
See also:
- Notes: Phil, 16/11/2005 We know that we own the attributes referred to by the Atrtibute Map so we can party on them without worrying about affecting other data structures. This allows us to remove attributes from the map completely, leaving it in a consistent state.

Definition at line 2135 of file combshps.cpp.

02136 {
02137     if (pAttrMap == NULL)
02138         return 0;
02139 
02140     // first see if we have an active stroke, if not we can just quit already
02141     AttrStrokeType* pStroke = NULL;
02142     pAttrMap->Lookup(CC_RUNTIME_CLASS(AttrStrokeType), (void*&)pStroke);
02143     
02144     if (pStroke == NULL || !pStroke->HasPathProcessor())
02145         return 0;
02146 
02147     AttrVariableWidth* pVarWidth = NULL;
02148     pAttrMap->Lookup(CC_RUNTIME_CLASS(AttrVariableWidth), (void*&)pVarWidth);
02149     
02150     // CGS:  we should only really continue IF we have an active value function!  Otherwise, we seem to run
02151     // into problems when we have give new objects most recent attributes turned on ....
02152     if ((pVarWidth == NULL) || !pVarWidth->HasActiveValueFunction ())
02153         return (0);
02154 
02155     // right so we want to set the line width to zero
02156     AttrLineWidth* pLineWidth = new AttrLineWidth;
02157     if (pLineWidth != NULL)
02158     {
02159         pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrLineWidth));
02160         pLineWidth->Value.LineWidth = 0;
02161         pAttrMap->SetAt(CC_RUNTIME_CLASS(AttrLineWidth), (void*)pLineWidth);
02162     }
02163 
02164     // now get the stroke colour and use it to make a fill colour
02165     AttrStrokeColour * pColour = NULL;
02166 
02167     // look up the stroke colour attribute          
02168     pAttrMap->Lookup(CC_RUNTIME_CLASS(AttrStrokeColour),
02169                         (void *&)pColour);
02170     
02171 
02172     // make a new flat fill attribute and apply this to the node
02173     if (pColour)
02174     {
02175         if (!pListItem || pListItem->GetStrokeCreatedPassbackPath ())
02176         {
02177             StrokeColourAttribute * pAttrVal = (StrokeColourAttribute *)pColour->GetAttributeValue();
02178 
02179             if (pAttrVal)
02180             {
02181                 AttrFlatColourFill* pFill = new AttrFlatColourFill;
02182                 
02183                 if (pFill)
02184                 {
02185                     DocColour Col = pAttrVal->Colour;
02186                     pFill->SetStartColour(&Col);
02187                     
02188                     // set back in the map
02189                     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrFillGeometry));
02190                     pAttrMap->SetAt(CC_RUNTIME_CLASS(AttrFillGeometry), (void*)pFill);
02191                 }
02192                 else
02193                     return 2;
02194             }
02195         }
02196         // now make a new stroke colour and set it transparent      
02197         AttrStrokeColour* pNewStrokeCol = new AttrStrokeColour;
02198         if (pNewStrokeCol)
02199         {
02200             pNewStrokeCol->Value.Colour = DocColour(COLOUR_NONE);
02201             pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrStrokeColour));
02202             pAttrMap->SetAt(CC_RUNTIME_CLASS(AttrStrokeColour), (void*)pNewStrokeCol);
02203         }
02204     }
02205 
02206     // now remove the stroke and var width attributes, and any brushes whilst we're here
02207     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrVariableWidth));
02208     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrStrokeType));
02209     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrBrushType));
02210 
02211     return 1;
02212 }

BOOL OpCombineShapes::ApplyAttributes NodeRenderableInk pSrcNode,
NodeRenderableInk pDestNode,
CCAttrMap pAttrMap,
SelObjPathListItem pListItem = NULL
[private]
 

This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
pSrcNode = ptr to node to get attrs from [INPUTS] pDestNode = ptr to node to apply pSrcNode's attrs to pAttrMap = ptr to attr map (NULL means get attrs applied to pSrcNode) pListItem = so that the can control processing based upon the values of this
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
-

Definition at line 2063 of file combshps.cpp.

02064 {
02065     ERROR3IF(pSrcNode  == NULL,"pSrcNode == NULL");
02066     ERROR3IF(pDestNode == NULL,"pDestNode == NULL");
02067     if (pSrcNode == NULL || pDestNode == NULL) return FALSE;
02068 
02069     BOOL ok = TRUE;             // Set to TRUE if we successfully apply attrs to pDestNode
02070     BOOL DeleteAttrs = FALSE;   // TRUE if we need to delete attr map before returning
02071 
02072 
02073     if (pAttrMap == NULL)
02074     {
02075         // Find the currently applied attributes and make a local copy of the map (like most
02076         // passback functions do)
02077         CCAttrMap AppliedMap(30);
02078         ok = pSrcNode->FindAppliedAttributes(&AppliedMap);
02079         pAttrMap = AppliedMap.Copy();
02080         DeleteAttrs = TRUE;
02081     }
02082 
02083     // if we have a stroked object we need to do some attribute juggling
02084     if (AdjustAttributesForStroke(pAttrMap, pListItem) == 2)
02085         return FALSE;
02086 
02087     // If all's OK, apply attrs to pDestNode
02088     ok = pDestNode->ApplyAttributes(pAttrMap,TRUE);
02089 
02090     // Delete the copied attr map if we have to
02091     if (DeleteAttrs)
02092     {
02093         pAttrMap->DeleteAttributes();
02094         delete pAttrMap;
02095     }
02096 
02097     return ok;
02098 }

BOOL OpCombineShapes::ApplyEffects SelObjPathListItem pListItem,
NodeRenderableInk pDestNode,
NodeRenderableInk pParentEffect
[private]
 

To delete the extra attributes we allocated in AdjustAttributesForStroke This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.

Author:
Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
Date:
04/02/2005
Parameters:
pListItem = so that the can control processing based upon the values of this [INPUTS] pDestNode = ptr to node to apply pSrcNode's attrs to
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
-

Definition at line 2297 of file combshps.cpp.

02298 {
02299     BOOL ok = TRUE;
02300     PORTNOTE("other", "Removed OpCombineShapes::ApplyEffects");
02301 #ifndef EXCLUDE_FROM_XARALX
02302 
02303     ERROR3IF(pListItem  == NULL, "pListItem == NULL");
02304     ERROR3IF(pDestNode == NULL, "pDestNode == NULL");
02305 
02306     NodeRenderableInk* pSrcNode = pListItem->GetCreatedByNode();
02307     ERROR3IF(pSrcNode  == NULL, "pSrcNode == NULL");
02308 
02309     // Get the effect stack for the object which produced this path (pSrcNode)
02310     // If the source node had effects applied to it
02311     // And that stack is different than the stack above the context node
02312     // Then we must copy the effect stack onto the destination node
02313     ListRange* pStack = pListItem->GetEffectStack();
02314     Node* pEffect = NULL;
02315     if (pStack)
02316         pEffect = pStack->FindFirst();
02317     if (pEffect)
02318     {
02319         if (pParentEffect==NULL || pParentEffect!=pEffect)
02320         {
02321             // We need to copy the effect stack onto this object
02322             OpLiveEffect::DoCopyEffectsStack(this, pDestNode, pStack, GetWorkingDoc(), GetWorkingDoc());
02323         }
02324     }
02325 #endif
02326     return ok;
02327 }

BOOL OpCombineShapes::BeginSlowJob  )  [private]
 

Wrapper around BeginSlowJob() that sets up the hour glass counter correctly.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK

Errors: -

See also:
-

Definition at line 382 of file combshps.cpp.

00383 {
00384     NumPaths = 0;
00385     JobCount = 0;
00386     SelObjPathList* pList = GetFirstList();
00387     while (pList != NULL)
00388     {
00389         NumPaths += pList->GetCount();
00390         pList = GetNextList(pList);
00391     }   
00392 
00393     INT32 SlowJobCount = NumPaths;
00394     if (Reason == COMBINE_SLICE)
00395         SlowJobCount *= 2;
00396     String_64 Str(_R(IDS_COMBINE_SLOWJOB));
00397     return (::BeginSlowJob(SlowJobCount,FALSE,&Str));
00398 }

BOOL OpCombineShapes::CombinePaths ClipStyle  Style,
Path pClipPath,
Path pStrokedClipPath,
Path pSrcPath,
Path pDestPath
[private]
 

This combines pClipPath from pSrcPath using ClipPathToPath, storing the result in pDestPath.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
Style = High level style of clipping [INPUTS] pClipPath = ptr to the path to combine with pSrcPath pStrokedClipPath = ptr to stroked version of pClipPath ONLY if pClipPath is a line pSrcPath = ptr to path that will be combined with pClipPath pDestPath = ptr to path to place the result in
*pDestPath contains the result of combining pCliPath with pSrcPath(if TRUE returned). [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
OpCombineShapes::CombinePathsFromList()

Definition at line 1136 of file combshps.cpp.

01137 {
01138     // Check those entry params
01139     ERROR2IF(pClipPath          == NULL,FALSE,"pClipPath == NULL");
01140     ERROR2IF(pStrokedClipPath   == NULL,FALSE,"pStrokedClipPath == NULL");
01141     ERROR2IF(pSrcPath           == NULL,FALSE,"pSrcPath == NULL");
01142     ERROR2IF(pDestPath          == NULL,FALSE,"pDestPath == NULL");
01143 
01144     UINT32 Flags = 0;   // Flags passed to ClipPathToPath()
01145     BOOL Clip = TRUE;   // If this is TRUE, ClipPathToPath() is called at the end of this func
01146     INT32 len = -1;     // This is the length of the path produced by ClipPathToPath
01147 
01148     BOOL SrcIsLine  = (pSrcPath->GetPathType()  == PATHTYPE_LINE);  // TRUE if src path is a line
01149     BOOL ClipIsLine = (pClipPath->GetPathType() == PATHTYPE_LINE);  // TRUE if clip path is a line
01150 
01151     Path DestPath;      // Used when using a line to slice the source path up
01152 
01153     // If the clip path is a line and the op is slice, try and make a closed path using pClipPath
01154     // and the bounding rect of pSrcPath
01155     if (ClipIsLine && Reason == COMBINE_SLICE)
01156     {
01157         if (DestPath.Initialise() && ConvertClipLineToShape(pClipPath,pSrcPath,&DestPath))
01158         {
01159             // If successfully created a closed path, then pretend func was entered with the new path
01160             // by pointing pClipPath to it. Also the clip path is no longer a line
01161             pClipPath = &DestPath;
01162             ClipIsLine = FALSE;
01163         }
01164     }
01165     
01166     switch (Style)
01167     {
01168         case CLIP_STYLE_SUBTRACT:
01169             Flags = 1;  // 1 is Source AND NOT Clip
01170             if (SrcIsLine)  Flags |= CLIPPING_IS_STROKED;   // If source is a line, set the stroked flag
01171 
01172             if (ClipIsLine)
01173             {
01174                 // If slicing with a line, don't bother doing the subtraction stage 
01175                 if (Reason == COMBINE_SLICE)
01176                     Clip = FALSE;
01177                 else
01178                 // Otherwise subtract with a stroked version of the line.
01179                 // If the user's mad enough to try it, then we'd better try our best
01180                 { pClipPath = pStrokedClipPath; Flags |= CLIPPING_CLIP_WINDING; }
01181             }
01182             break;
01183 
01184         case CLIP_STYLE_INTERSECT:
01185             // If src is a line, Flags = 0 | stroked flag, else Flags = Source AND Clip
01186             if (SrcIsLine) Flags = CLIPPING_IS_STROKED; else Flags = 2; 
01187             if (ClipIsLine)
01188             {
01189                 // If slicing with a line, don't bother doing the intersection stage 
01190                 if (Reason == COMBINE_SLICE)
01191                     Clip = FALSE;
01192                 else
01193                 // Otherwise intersect with a stroked version of the line.
01194                 // If the user's mad enough to try it, then we'd better try our best
01195                 { pClipPath = pStrokedClipPath; Flags |= CLIPPING_CLIP_WINDING; }
01196             }
01197             break;
01198 
01199         case CLIP_STYLE_ADD:
01200             Flags = 7;  // 7 is Source OR Clip
01201             // convert lines into paths by stroking them
01202             // Also we must use a Non-zero winding rule to avoid cross segment lines taking effect
01203             if (SrcIsLine)  { StrokePathToPath(pSrcPath,pSrcPath);   Flags |= CLIPPING_SOURCE_WINDING; }
01204             if (ClipIsLine) { pClipPath = pStrokedClipPath; Flags |= CLIPPING_CLIP_WINDING; }
01205             break;
01206     }
01207 
01208     if (Clip)
01209     {
01210         // Clip those paths together
01211         // Mark Howitt 31/10/00
01212         // I`ve removed the ClipPathToPathWithAutoFlatness function and replaced it with
01213         // the function that returns you a flatness value to use with the ClipPath function
01214         double ClippingFlatness = pClipPath->CalculateFlatnessValueFromPath(750.0, 2.0, 375.0);
01215         double SourceFlatness = pSrcPath->CalculateFlatnessValueFromPath(750.0, 2.0, 375.0);
01216 
01217         len  = pClipPath->ClipPathToPath(*pSrcPath, pDestPath, Flags, DefaultTolerance, ClippingFlatness, SourceFlatness);
01218 /*
01219         // show debugging info about the path we have clipped
01220         INT32 NumCoords = pDestPath->GetNumCoords();
01221         PathVerb * pVerbs = pDestPath->GetVerbArray();
01222         DocCoord * pCoord = pDestPath->GetCoordArray();
01223 
01224         for (INT32 i = 0; i < NumCoords; i++)
01225         {
01226             if (pVerbs[i] == PT_MOVETO)
01227                 TRACE( _T("MOVE TO "));
01228             else
01229             if (pVerbs[i] == PT_BEZIERTO)
01230                 TRACE( _T("BEZIER TO "));
01231             else
01232             if (pVerbs[i] == PT_LINETO)
01233                 TRACE( _T("LINE TO "));
01234 
01235             TRACE( _T("%d, %d\n"), pCoord[i].x, pCoord[i].y);
01236         }
01237         TRACE( _T("end line\n"));
01238 */
01239     }
01240 
01241     return (len > 0);
01242 }

BOOL OpCombineShapes::CombinePathsFromList ClipStyle  Style,
Path pClipPath,
Path pStrokedClipPath,
SelObjPathList pSelObjPathList,
BOOL  bCombineEffectBitmaps = TRUE
[private]
 

This combines pClipPath with all the paths in pSelObjPathList.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
Style = High level style of clipping [INPUTS] pClipPath = ptr to path to combine with the paths in pSelObjPathList pStrokedClipPath= ptr to stroked version of pClipPath ONLY if pClipPath is a line pSelObjPathList = ptr to list of paths
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
For each path it tries to combine it with pClipPath. A new NodePath is generated and placed in the tree at the place the selected object lives.
Returns:
Errors: -
See also:
OpCombineShapes::DoCombineShapes()

Definition at line 1009 of file combshps.cpp.

01014 {
01015     // Check our input params
01016     ERROR3IF(pClipPath == NULL,"pClipPath == NULL");
01017     ERROR3IF(pSelObjPathList == NULL,"pSelObjPathList == NULL");
01018     if (pClipPath == NULL || pSelObjPathList == NULL) return FALSE;
01019 
01020     // Get the first path in this list
01021     SelObjPathListItem* pFirstItem = pSelObjPathList->GetFirstItem();
01022     SelObjPathListItem* pSelObjPathListItem = pFirstItem;
01023     BOOL ok = TRUE;
01024 
01025     while (ok && pSelObjPathListItem != NULL)
01026     {
01027         // Get a pointer to the actual node path in this list item
01028         NodePath* pNodePath = pSelObjPathListItem->GetNodePath();
01029         NodePath* pNewNodePath = new NodePath;
01030         NodeRenderableInk* pCreatedByNode = pSelObjPathListItem->GetCreatedByNode();
01031 
01032         if (pNodePath != NULL && pNewNodePath != NULL && pNewNodePath->SetUpPath())
01033         {
01034             //pNewNodePath->InkPath.Initialise(pNodePath->InkPath.GetNumCoords(), 64);
01035             //if    (pNewNodePath->InkPath.CopyPathDataFrom(&(pNodePath->InkPath)))
01036             if (CombinePaths(Style, pClipPath, pStrokedClipPath, &(pNodePath->InkPath), &(pNewNodePath->InkPath)))
01037             {
01038                 if (bCombineEffectBitmaps)
01039                     ok = DoInsertNewNode(pNewNodePath);
01040                 else
01041                 {
01042                     if (pSelObjPathListItem==pFirstItem)
01043                     {
01044                         // The first path produced can live where it's originating node lived
01045                         // inside whatever Effect stack was in place
01046                         ok = UndoableOperation::DoInsertNewNode(pNewNodePath, pNodePath, NEXT, TRUE, FALSE, FALSE, TRUE);
01047                     }
01048                     else
01049                     {
01050                         // If more than one path is produced under an Effect node
01051                         // Then we must make a new Effect Stack for it
01052                         //
01053                         // BUT NOTE! I don't think any multiple set of nodes are produced outside
01054                         // a group at the moment - so the effect applciation code found here
01055                         // has never yet been tested!
01056                         Node* pInsertNode = pNodePath;
01057                         ListRange* pStack = EffectsStack::GetEffectsStackFromNode(pNodePath, FALSE, TRUE, TRUE);    // Include locked effects
01058                         if (pStack)
01059                             pInsertNode = pStack->FindLast();
01060                         ok = UndoableOperation::DoInsertNewNode(pNewNodePath, pInsertNode, NEXT, TRUE, FALSE, FALSE, TRUE);
01061                         pInsertNode = pNewNodePath;
01062                         delete pStack;
01063 
01064                         // Now copy the effects stack for this new child...
01065                         if (ok) ok = ApplyEffects(pSelObjPathListItem, pNewNodePath, NULL);
01066 
01067                     }
01068 
01069                     // Select the resulting path now
01070                     if (ok && pCreatedByNode->IsSelected())
01071                         pNewNodePath->Select(FALSE);
01072 
01073                     if (ok)
01074                     {
01075                         pListOfResults->AddNode(pNewNodePath);
01076                         NodeInsertCount++;
01077                     }
01078                 }
01079 
01080                 // Find out who created the path, and apply its attrs to the new NodePath
01081                 CCAttrMap*          pAttrMap        = pSelObjPathListItem->GetAttrMap();
01082                 
01083                 if (ok) ok = ApplyAttributes(pCreatedByNode, pNewNodePath, pAttrMap, pSelObjPathListItem);
01084 
01085                 // Set up a set of default path flags to enable the bezier tool to edit this node
01086                 if (ok) pNewNodePath->InkPath.InitialiseFlags();
01087 
01088                 if (ok) pSelObjPathList->SetProducedPaths(TRUE);
01089             }
01090             else
01091             {
01092                 if (pNewNodePath!=NULL) { delete pNewNodePath; pNewNodePath = NULL; }
01093 
01094                 // Don't worry if the actual combine fails.  We'll just not put anything in the tree
01095                 // because, hmmm, I wonder....
01096             }
01097         }
01098         else
01099         {
01100             // What? Error? Surely not!
01101             ERROR3("pNodePath == NULL || pNewNodePath == NULL || !pNewNodePath->SetUpPath())");
01102             ok = FALSE;
01103 
01104             if (pNewNodePath!=NULL) { delete pNewNodePath; pNewNodePath = NULL; }
01105         }
01106 
01107         if (ok)
01108             ok = ::ContinueSlowJob(++JobCount);
01109 
01110         // Get the next item in the selected object path list.
01111         pSelObjPathListItem = pSelObjPathList->GetNextItem(pSelObjPathListItem);
01112     }
01113 
01114     return (ok);
01115 }

BOOL OpCombineShapes::ConvertClipLineToShape Path pClipPath,
Path pSrcPath,
Path pDestPath
[private]
 

Only call this if pClipPath is a line.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
pClipPath = ptr to the path to convert [INPUTS] pSrcPath = ptr to path that will be combined with pClipPath pDestPath = ptr to path to place the result in
*pDestPath contains the result of converting pCliPath with regards to pSrcPath (if TRUE returned). [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This creates a shape out of pClipPath such that the path from the start point of pClipPath to the end point of pClipPath does NOT intersect with pSrcPath. It can only do this if the start and end point of pClipPath are outside the coord bounds of pSrcPath. If either pClipPath ends are inside pSrcPath's bounds, FALSE is returned.

The shape is created by tacking on parts of the bounding rectangle to pClipPath, then closing the resultant path. Note: pDestPath must not point to pClipPath OR pSrcPath (i.e. you'll need three paths)

Returns:
Errors: -
See also:
OpCombineShapes::CombinePaths()

Definition at line 1272 of file combshps.cpp.

01273 {
01274     // Check for stupid entry parama
01275     ERROR2IF(pClipPath == NULL,FALSE,"pClipPath == NULL");
01276     ERROR2IF(pSrcPath  == NULL,FALSE,"pSrcPath == NULL");
01277     ERROR2IF(pDestPath == NULL,FALSE,"pClipPath == NULL");
01278 
01279     // Get the coord bounds of the source path.  Inflate it slightly so that we don't run into tolerance
01280     // problems when calling ClipPathToPath()
01281     //DocRect Rect = GetBoundingRect(pSrcPath);
01282 
01283     // we must be consistant as to the rect we use to get a consistant shape
01284     // otherwise some items will all get cut along the line but it is luck
01285     // which side of the cut the shapes appear in (sjk 15/11/00)
01286 //  DocRect Rect = GetApplication()->FindSelection()->GetBoundingRect(); // use a consistant rect for all the shapes
01287     DocRect Rect = OriginalBoundingRect; // use a consistant rect for all the shapes
01288 
01289     Rect.Inflate((DefaultTolerance*2)+10);
01290     
01291     INT32       NumClipCoords = pClipPath->GetNumCoords();
01292     DocCoord*   pClipCoords   = pClipPath->GetCoordArray();
01293     DocCoord    ClipStart     = pClipCoords[0];                 // Start point of pClipPath
01294     DocCoord    ClipEnd       = pClipCoords[NumClipCoords-1];   // End   point of pClipPath
01295     DocCoord    ExtraClipStart= pClipCoords[0];                 // Start point of pClipPath
01296     DocCoord    ExtraClipEnd  = pClipCoords[NumClipCoords-1];   // End   point of pClipPath
01297     BOOL        NeedExtraClipStart  = FALSE;
01298     BOOL        NeedExtraClipEnd    = FALSE;
01299 
01300     Path        FlatPath;
01301 //  BOOL        SetFlatPath = FALSE;
01302 //  double      FLATTNESS =  1024;  // arbitry figure of how flattened the curve will be
01303                                     // only used for test for extra cuts
01304 
01305     // We have to choose a corner point of the bounding rect for both the start and end points
01306     // of pClipPath.
01307 
01308     DocCoord ClipCornerStart,ClipCornerEnd;
01309 
01310     DocCoord mid = Rect.Centre();
01311 
01312     if (ClipStart.x < mid.x)
01313         ClipCornerStart.x = Rect.lo.x;
01314     else
01315         ClipCornerStart.x = Rect.hi.x;
01316 
01317     if (ClipStart.y < mid.y)
01318         ClipCornerStart.y = Rect.lo.y;
01319     else
01320         ClipCornerStart.y = Rect.hi.y;
01321 
01322     if (ClipEnd.x < mid.x)
01323         ClipCornerEnd.x = Rect.lo.x;
01324     else
01325         ClipCornerEnd.x = Rect.hi.x;
01326 
01327     if (ClipEnd.y < mid.y)
01328         ClipCornerEnd.y = Rect.lo.y;
01329     else
01330         ClipCornerEnd.y = Rect.hi.y;
01331 
01332     // start clip point in the rect?
01333     // move it to the nearest edge
01334     if (Rect.ContainsCoord(ClipStart))
01335     {
01336         NeedExtraClipStart = TRUE;
01337         DocCoord v = ClipStart - pClipCoords[1];
01338         INT32 giveup = 250; // number of times to try
01339 
01340         // extrapperlate the last part of the line until it breaks out of the bounding rect
01341         // give up if it fails to break out after an arbitry number of goes
01342         while (Rect.ContainsCoord(ExtraClipStart) && giveup > 0)
01343         {
01344             ExtraClipStart.x += v.x * 10;
01345             ExtraClipStart.y += v.y * 10;
01346             giveup--;
01347         }
01348 
01349         // failled to leave the shape
01350         if (giveup == 0) 
01351             return FALSE;
01352 
01353         // moving the start may change which is the nearest corner to go for
01354         DocCoord mid = Rect.Centre();
01355 
01356         if (ExtraClipStart.x < mid.x)
01357             ClipCornerStart.x = Rect.lo.x;
01358         else
01359             ClipCornerStart.x = Rect.hi.x;
01360 
01361         if (ExtraClipStart.y < mid.y)
01362             ClipCornerStart.y = Rect.lo.y;
01363         else
01364             ClipCornerStart.y = Rect.hi.y;
01365     }
01366 
01367     // perform all the same calcualtions for the other end of the clip line
01368     if (Rect.ContainsCoord(ClipEnd))
01369     {
01370         NeedExtraClipEnd = TRUE;
01371         DocCoord v = ClipEnd - pClipCoords[NumClipCoords-2];
01372         INT32 giveup = 250; // number of times to try
01373 
01374         // extrapperlate the last part of the line until it breaks out of the bounding rect
01375         // give up if it fails to break out after an arbitry number of goes
01376         while (Rect.ContainsCoord(ExtraClipEnd) && giveup > 0)
01377         {
01378             ExtraClipEnd.x += v.x * 10;
01379             ExtraClipEnd.y += v.y * 10;
01380             giveup--;
01381         }
01382 
01383         // failled to leave the shape
01384         if (giveup == 0) 
01385             return FALSE;
01386 
01387         // moving the end may change which is the nearest corner to go for
01388         DocCoord mid = Rect.Centre();
01389 
01390         if (ExtraClipEnd.x < mid.x)
01391             ClipCornerEnd.x = Rect.lo.x;
01392         else
01393             ClipCornerEnd.x = Rect.hi.x;
01394 
01395         if (ExtraClipEnd.y < mid.y)
01396             ClipCornerEnd.y = Rect.lo.y;
01397         else
01398             ClipCornerEnd.y = Rect.hi.y;            
01399     }
01400 
01401     // We now have two sensible corners of Rect to connect pClipPath's end points to
01402     // and if required also optimal points on this Rect boundry to that are
01403     // in direct line with the ends of the cutting line
01404 
01405     // Firstly, copy pClipPath to pDestPath
01406     pDestPath->ClearPath(FALSE);
01407     if (!pDestPath->MergeTwoPaths(*pClipPath)) return FALSE;
01408 
01409     // add on an extra end part to make this end of the clip path outside the bounds of the selection
01410     if (NeedExtraClipEnd)
01411     {
01412         if (!pDestPath->AddLineTo(ExtraClipEnd)) return FALSE;
01413     }
01414 
01415     // Connect the end of the line to the end corner point
01416     if (!pDestPath->AddLineTo(ClipCornerEnd)) return FALSE;
01417 
01418     // add in the corner nearest the end
01419     // If end corner x != start corner x, we need to connect these up
01420     if (ClipCornerEnd.x != ClipCornerStart.x)
01421     {
01422         ClipCornerEnd.x = ClipCornerStart.x;
01423         if (!pDestPath->AddLineTo(ClipCornerEnd)) return FALSE;
01424     }
01425 
01426     // if the start and end corners are opposing we need to use an intermediate corner to avoid the selection
01427     // If end corner y != start corner y, we need to connect these up
01428     if (ClipCornerEnd.y != ClipCornerStart.y)
01429     {
01430         ClipCornerEnd.y = ClipCornerStart.y;
01431         if (!pDestPath->AddLineTo(ClipCornerEnd)) return FALSE;
01432     }
01433 
01434     // add in the corner nearest the start
01435     // add on an extra end part to make this end of the clip path outside the bounds of the selection
01436     if (NeedExtraClipStart)
01437     {
01438         if (!pDestPath->AddLineTo(ExtraClipStart)) return FALSE;
01439     }
01440     
01441     // Finally, close the path up by connecting the start corner point to the start of the line
01442     // and setting PT_CLOSEFIGURE in the last verb
01443     if (!pDestPath->AddLineTo(ClipStart)) return FALSE;
01444 
01445     INT32 n = pDestPath->GetNumCoords();
01446     PathVerb* pVerbs = pDestPath->GetVerbArray();
01447     pVerbs[n-1] |= PT_CLOSEFIGURE;
01448 
01449     return TRUE;
01450 }