// $Id: nodebldr.cpp 1315 2006-06-14 09:51:34Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // NodeBlender implementation /* */ #include "camtypes.h" //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "osrndrgn.h" #include "nodepath.h" #include "ophist.h" //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "lineattr.h" #include "blendatt.h" #include "qualattr.h" //#include "markn.h" #include "gblend.h" #include "gwinding.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "grnddib.h" //#include "grndclik.h" #include "ndbldpth.h" //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "aw_eps.h" #include "nativeps.h" // The old style EPS native filter, used in v1.1 #include "cameleps.h" #include "attrmap.h" #include "progress.h" #include "cxftags.h" //#include "cxfdefs.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodeblnd.h" #include "nodebldr.h" //#include "bitmap.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "oilbitmap.h" #include "brshattr.h" #include "blndhelp.h" #include "fthrattr.h" #include "nodecont.h" #include "brshbeca.h" #include "strkattr.h" DECLARE_SOURCE("$Revision: 1315 $"); // Declare smart memory handling in Debug builds //#define Swap(a,b,t) { t=a; a=b; b=t; } #define SWAP(type,a,b) { type x=a; a=b; b=x; } #define DELPTR(p) if (p != NULL) { delete p; p = NULL; } //----------------------------------------------------------- void ListStepper::Init(UINT32 NumInStart,UINT32 NumInEnd,UINT32 Start) { if (NumInStart > NumInEnd) { Min = NumInEnd; pNextInMin = &NextInEnd; Max = NumInStart; pNextInMax = &NextInStart; } else { Min = NumInStart; pNextInMin = &NextInStart; Max = NumInEnd; pNextInMax = &NextInEnd; } NextInStart = NextInEnd = Inc = 0; for (INT32 dummy;Start > 0;Start--) GetNext(&dummy,&dummy); } void ListStepper::GetNext(INT32* pNextInStart,INT32* pNextInEnd) { if ((*pNextInMax) < Max) { *pNextInStart = NextInStart; *pNextInEnd = NextInEnd; Inc += Min; if (Inc >= Max) { Inc -= Max; (*pNextInMin)++; } (*pNextInMax)++; } else { *pNextInStart = -1; *pNextInEnd = -1; } } /*********************************************************************************************** > class BlendSubRenderContext: public SubRenderContext Author: Mark_Neves (Xara Group Ltd) Created: 30/11/94 Purpose: Used during rendering to store the render context of a blender node if it has taken too long to render so far. ***********************************************************************************************/ class BlendSubRenderContext: public SubRenderContext { CC_DECLARE_DYNCREATE(BlendSubRenderContext); public: BlendSubRenderContext() { BlendStep = 1; } UINT32 BlendStep; }; CC_IMPLEMENT_DYNCREATE(BlendSubRenderContext,SubRenderContext); //----------------------------------------------------------- // Put my version Number into the About box DECLARE_SOURCE( "$Revision: 1315 $" ); CC_IMPLEMENT_DYNCREATE(NodeBlender, NodeRenderableInk) CC_IMPLEMENT_MEMDUMP(BlendRef,CC_CLASS_MEMDUMP) CC_IMPLEMENT_MEMDUMP(BlendPath,ListItem) CC_IMPLEMENT_MEMDUMP(BecomeA,CC_CLASS_MEMDUMP) CC_IMPLEMENT_MEMDUMP(BlendBecomeA,BecomeA) CC_IMPLEMENT_MEMDUMP(HandleBecomeA,CC_CLASS_MEMDUMP) CC_IMPLEMENT_MEMDUMP(NodeCompoundBlendBecomeA,BecomeA) // Declare smart memory handling in Debug builds // #define new CAM_DEBUG_NEW #define MAX_NUM_BLEND_STEPS 999 #define FLATNESS 1024 /*********************************************************************************************** > NodeBlender::NodeBlender(Node* ContextNode, AttachNodeDirection Direction, const DocRect& BoundingRect, BOOL Locked = FALSE, BOOL Mangled = FALSE, BOOL Marked = FALSE, BOOL Selected = FALSE, BOOL Renderable = FALSE ) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: ContextNode: Pointer to a node which this node is to be attached to. MonoOn Direction: MonoOff Specifies the direction in which the node is to be attached to the ContextNode. The values this variable can take are as follows: PREV : Attach node as a previous sibling of the context node NEXT : Attach node as a next sibling of the context node FIRSTCHILD: Attach node as the first child of the context node LASTCHILD : Attach node as a last child of the context node BoundingRect: Bounding rectangle The remaining inputs specify the status of the node: Locked: Is node locked ? Mangled: Is node mangled ? Marked: Is node marked ? Selected: Is node selected ? Purpose: This constructor initialises the nodes flags and links it to ContextNode in the direction specified by Direction. All neccesary tree links are updated. Note: Initialise() must be called before the NodeBlender is in a state in which it can be used. SeeAlso: Initialise() Errors: An assertion error will occur if ContextNode is NULL ***********************************************************************************************/ NodeBlender::NodeBlender(Node* ContextNode, AttachNodeDirection Direction, BOOL Locked, BOOL Mangled, BOOL Marked, BOOL Selected ):NodeRenderableInk(ContextNode, Direction, Locked, Mangled, Marked, Selected ) { ResetVars(); } /********************************************************************************************* > NodeBlender::NodeBlender() Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Purpose: This constructor creates a NodeBlender linked to no other with all status flags false and an uninitialized bounding rectangle. Note: Initialise() must be called before the NodeBlender is in a state in which it can be used. SeeAlso: Initialise() **********************************************************************************************/ /* Technical Notes: The default constructor is required so that SimpleCopy will work */ NodeBlender::NodeBlender(): NodeRenderableInk() { ResetVars(); } /********************************************************************************************* > NodeBlender::~NodeBlender() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Purpose: Default deconstructor **********************************************************************************************/ NodeBlender::~NodeBlender() { // Call PostBlend() in case we're trying to be deleted in the middle of rendering. PostBlend(); // Delete the two blend references DELPTR(m_pRefStart); DELPTR(m_pRefEnd); } /********************************************************************************************* > void NodeBlender::ResetVars() Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Purpose: Resets all the member vars to default values. Should only be called by NodeBlender constructors **********************************************************************************************/ void NodeBlender::ResetVars() { m_pRefStart = NULL; m_pRefEnd = NULL; m_pNodeStart = NULL; m_pNodeEnd = NULL; m_ObjIndexStart = -1; m_ObjIndexEnd = -1; m_PathIndexStart = -1; m_PathIndexEnd = -1; m_Initialised = FALSE; m_TempArraySize = 0; m_pTempCoords = NULL; m_pTempVerbs = NULL; m_pTempFlags = NULL; m_GBlendBuffSize = 0; m_pGBlendBuff = NULL; m_ProportionOfPathDistStart = -1.0; m_ProportionOfPathDistEnd = -1.0; m_AngleStart = 0.0; m_AngleEnd = 0.0; m_Reversed = FALSE; m_BlendedOnCurve = FALSE; m_NodeBlendPathIndex = -2; //-2 to deal with the case of the previous file format - see // HandleBlendPathRecord for details } /*********************************************************************************************** > virtual Node* NodeBlender::SimpleCopy() Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Returns: Pointer to a Node Purpose: Makes a copy of all the data in the node ***********************************************************************************************/ Node* NodeBlender::SimpleCopy() { // Make a new NodeBlender and then copy things into it NodeBlender* pCopyOfNode = new NodeBlender(); if (pCopyOfNode) CopyNodeContents(pCopyOfNode); return pCopyOfNode; } /*********************************************************************************************** > void NodeBlender::CopyNodeContents(NodeBlender* pCopyOfNode) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: pCopyOfNode - The node to copy data to Purpose: Copies the data in this node to pCopyOfNode by first calling the base class to get it to copy its stuff, and then copying its own stuff Scope: protected SeeAlso: NodeRenderableInk::CopyNodeContents ***********************************************************************************************/ void NodeBlender::CopyNodeContents(NodeBlender* pCopyOfNode) { NodeRenderableInk::CopyNodeContents(pCopyOfNode); // Copy contents specific to derived class here pCopyOfNode->m_ObjIndexStart = m_ObjIndexStart; pCopyOfNode->m_ObjIndexEnd = m_ObjIndexEnd; pCopyOfNode->m_PathIndexStart = m_PathIndexStart; pCopyOfNode->m_PathIndexEnd = m_PathIndexEnd; pCopyOfNode->m_ProportionOfPathDistStart = m_ProportionOfPathDistStart; pCopyOfNode->m_ProportionOfPathDistEnd = m_ProportionOfPathDistEnd; pCopyOfNode->m_NodeBlendPathIndex = m_NodeBlendPathIndex; pCopyOfNode->m_BlendedOnCurve = m_BlendedOnCurve; pCopyOfNode->SetReversed(m_Reversed); pCopyOfNode->m_AngleEnd = m_AngleEnd; pCopyOfNode->m_AngleStart = m_AngleStart; pCopyOfNode->SetUninitialised(); } /*********************************************************************************************** > void NodeBlender::PolyCopyNodeContents(NodeRenderable* pNodeCopy) Author: Phil_Martin (Xara Group Ltd) Created: 18/12/2003 Outputs: - Purpose: Polymorphically copies the contents of this node to another Errors: An assertion failure will occur if NodeCopy is NULL Scope: protected ***********************************************************************************************/ void NodeBlender::PolyCopyNodeContents(NodeRenderable* pNodeCopy) { ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node"); ENSURE(IS_A(pNodeCopy, NodeBlender), "PolyCopyNodeContents given wrong dest node type"); if (IS_A(pNodeCopy, NodeBlender)) CopyNodeContents((NodeBlender*)pNodeCopy); } /*********************************************************************************************** > void NodeBlender::ShowDebugTreeDetails() const Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Purpose: Displays debugging info of the tree SeeAlso: NodeRenderableInk::ShowDebugTreeDetails ***********************************************************************************************/ #ifdef _DEBUG void NodeBlender::ShowDebugTreeDetails() const { // Display a bit of debugging info // For now, we will just call the base class version NodeRenderableInk::ShowDebugTreeDetails(); } #endif /******************************************************************************************** > void NodeBlender::GetDebugDetails( StringBase* Str ) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Outputs: Str: String giving debug info about the node Purpose: For obtaining debug information about the Node ********************************************************************************************/ void NodeBlender::GetDebugDetails( StringBase* Str ) { #ifdef _DEBUG // Call base class NodeRenderableInk::GetDebugDetails( Str ); (*Str) += TEXT( "\r\nBlender Data Dump\r\n" ); TCHAR buf[500]; camSnprintf( buf, 500, _T("Blend steps = %ld\r\n") _T("m_ObjIndex start = %ld\r\n") _T("m_ObjIndex end = %ld\r\n") _T("m_ProportionOfPathDistStart = %f\r\n") _T("m_ProportionOfPathDistEnd = %f\r\n") _T("m_AngleStart = %f\r\n") _T("m_AngleEnd = %f\r\n") _T("m_NodeBlendPathIndex = %ld\r\n") _T("\r\n"), GetNumBlendSteps(), m_ObjIndexStart, m_ObjIndexEnd, m_ProportionOfPathDistStart, m_ProportionOfPathDistEnd, m_AngleStart, m_AngleEnd, m_NodeBlendPathIndex ); *Str += buf; *Str += _T("--------- Start\r\n\r\n"); GetDebugDetails(Str,m_pRefStart); *Str += _T("--------- End\r\n\r\n"); GetDebugDetails(Str,m_pRefEnd); #endif } /******************************************************************************************** > void NodeBlender::GetDebugDetails( StringBase* Str,BlendRef* pBlendRef ) Author: Mark_Neves (Xara Group Ltd) Created: 22/2/95 Outputs: Str: String giving debug info about the node Purpose: For obtaining debug information about the blend reference ********************************************************************************************/ void NodeBlender::GetDebugDetails( StringBase* Str, BlendRef* pBlendRef ) { #ifdef _DEBUG String_256 TempStr; TempStr._MakeMsg( _T("Num blend paths = #1%d\r\n"),pBlendRef->GetNumBlendPaths()); *Str += TempStr; BlendPath* pBlendPath = pBlendRef->GetFirstBlendPath(); while (pBlendPath != NULL) { Path* pPath = pBlendPath->GetPath(); PathVerb* pVerbs = pPath->GetVerbArray(); PathFlags* pFlags = pPath->GetFlagArray(); DocCoord* pCoords = pPath->GetCoordArray(); TempStr._MakeMsg( _T("Original Mapping = #1%d\r\n"), pBlendPath->GetOrigMapping()); *Str += TempStr; for (INT32 i=0; iGetNumCoords(); i++) { // Add the info to the string TempStr._MakeMsg( TEXT("#1%d.\t#2%d\t#3%ld,\t#4%ld\t"), i, pVerbs[i], pCoords[i].x, pCoords[i].y ); (*Str) += TempStr; if (pFlags[i].IsEndPoint) { TempStr._MakeMsg( _T(": E\t") ); (*Str) += TempStr; } *Str += _T("\r\n"); } pBlendPath = pBlendRef->GetNextBlendPath(pBlendPath); } #endif } /******************************************************************************************** > virtual void NodeBlender::Transform( TransformBase& Trans ) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: Trans - The transform Object Purpose: Transforms all the paths attached to this blender node SeeAlso: NodeRenderableInk::Transform() ********************************************************************************************/ void NodeBlender::Transform( TransformBase& Trans ) { InvalidateBoundingRect(); Deinit(); /* if (!Initialised) return; // The blender's bounding rect is no longer valid InvalidateBoundingRect(); // Transform all the BlendPaths in the two blend references if (pRefStart != NULL) pRefStart->Transform(Trans); if (pRefEnd != NULL) pRefEnd ->Transform(Trans); */ } /*********************************************************************************************** > BOOL NodeBlender::HidingNode() Author: Mark_Neves (Xara Group Ltd) Created: 14/11/94 Inputs: - Outputs: - Purpose: Called whenever the node gets hidden. It calls the Deinit() member function ***********************************************************************************************/ BOOL NodeBlender::HidingNode() { PostBlend(); Deinit(); return TRUE; } /*********************************************************************************************** > BOOL NodeBlender::ShowingNode() Author: Mark_Neves (Xara Group Ltd) Created: 14/11/94 Inputs: - Outputs: - Purpose: Called whenever the node gets shown after it's been hidden. It calls the Reinit() member function ***********************************************************************************************/ BOOL NodeBlender::ShowingNode() { return TRUE; // return (Reinit()); } /*********************************************************************************************** > void NodeBlender::Render(RenderRegion* pRender) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: Pointer to a render region Purpose: Will render the blended steps of this blender node ***********************************************************************************************/ void NodeBlender::Render(RenderRegion* pRender) { pRender->SaveContext(); CreateBlends(pRender,NULL); // Only supply a render region, because we're only rendering pRender->RestoreContext(); } /*********************************************************************************************** > BOOL NodeBlender::CallBeginBlendStep(BlendNodeParam * pParam) Author: David_McClarnon (Xara Group Ltd) Created: 7/3/2000 Inputs: pParam - the bevel node parameter to pass to all nodes Outputs: - Returns: - Purpose: Calls all begin blend step functions on appropriate nodes ***********************************************************************************************/ BOOL NodeBlender::CallBeginBlendStep(BlendNodeParam * pParam, BOOL* usingSumAllPathsPathProcessor) { if (pParam->GetRenderRegion() == NULL) { // CGS: if the RenderRegion == NULL, then we might be doing a convert to shapes // test whether or not we are if (!pParam->GetHandleBecomeA ()) { return TRUE; // er - there ain't much we can do } // else we are in a convert to editable shapes - continue } NodeRenderableInk * pCompoundStep = pParam->GetStartBlendPath()->GetBlendNode(); if (!pCompoundStep->IsCompound()) { *usingSumAllPathsPathProcessor = FALSE; return TRUE; } if (((NodeCompound *)pCompoundStep)->IsStartBlendGroupNode()) { if (!((NodeCompound *)pCompoundStep)->BeginBlendStep(pParam)) { return FALSE; } else { // we are using a path processor to do our funky stuff .... *usingSumAllPathsPathProcessor = TRUE; } } Node * pStep = pCompoundStep->FindFirstChild(); while (pStep) { if (pStep->IsCompound()) { NodeCompound * pCompound = (NodeCompound *)pStep; if (pCompound->IsStartBlendGroupNode()) { if (!pCompound->BeginBlendStep(pParam)) { return FALSE; } else { *usingSumAllPathsPathProcessor = TRUE; } } } pStep = pStep->FindNext(); } return TRUE; } /*********************************************************************************************** > BOOL NodeBlender::CallEndBlendStep(BlendNodeParam * pParam) Author: David_McClarnon (Xara Group Ltd) Created: 7/3/2000 Inputs: pCompound - the root of the tree to use pParam - the bevel node parameter to pass to all nodes Outputs: - Returns: - Purpose: Calls all end blend step functions on appropriate nodes ***********************************************************************************************/ BOOL NodeBlender::CallEndBlendStep(BlendNodeParam * pParam) { // we need to do this bottom up if (pParam->GetRenderRegion() == NULL) { // CGS: if the RenderRegion == NULL, then we might be doing a convert to shapes // test whether or not we are (or were) if (!pParam->GetHandleBecomeA ()) { return TRUE; // er - there ain't much we can do } // else we are in a convert to editable shapes - continue } NodeRenderableInk * pCompoundStep = pParam->GetStartBlendPath()->GetBlendNode(); // View * pView = View::GetCurrent(); if (!pCompoundStep->IsCompound()) return TRUE; Node * pStep = pCompoundStep->FindFirstDepthFirst(); while (pStep && pStep != pCompoundStep) { if (pStep->IsCompound()) { NodeCompound * pCompound = (NodeCompound *)pStep; if (pCompound->IsEndBlendGroupNode()) { if (!pCompound->EndBlendStep(pParam)) { return FALSE; } } } pStep = pStep->FindNextDepthFirst(pCompoundStep); } if (((NodeCompound *)pCompoundStep)->IsEndBlendGroupNode()) { if (!((NodeCompound *)pCompoundStep)->EndBlendStep(pParam)) { return FALSE; } } return TRUE; } /*********************************************************************************************** > void NodeBlender::CreateBlends(RenderRegion* pRender,HandleBecomeA* pHandleBecomeA) Author: Mark_Neves (Xara Group Ltd) Created: 17/10/94 Inputs: pRender = ptr to a render region (Can be NULL) pHandleBecomeA = ptr to class to pass each created blended path to (can be NULL) Outputs: - Returns: - Purpose: Will generate the blended steps of this blender node. If pRender != NULL, it will render each blend step to it. if pHandleBecomeA != NULL, it will pass each blend step to pHandleBecomeA->PassBack() as it's generated ***********************************************************************************************/ void NodeBlender::CreateBlends(RenderRegion* pRender,HandleBecomeA* pHandleBecomeA) { TRACEUSER( "ChrisS", _T("Entering create blends\n")); // Ensure that we are initialised (if it can be done!!) if (!Reinit()) return; if (!m_Initialised) return; // we MUST handle the this case! (blending NULL to NULL etc.) // Check the important member vars, to be on the safe side if (m_pRefStart == NULL || m_pRefEnd == NULL) { ERROR3("Dodgey pRef params"); return; } if (m_pNodeStart == NULL || m_pNodeEnd == NULL) { ERROR3("Dodgey m_pNodeStart and/or m_pNodeEnd params"); return; } // If the render region's quality does not state full blend rendering, // proceed as if we are not rendering at all (this is done by pretending we haven't // got a render region if (pRender != NULL && pRender->RRQuality.GetBlendQuality() != Quality::FullBlend) pRender = NULL; // If we're not rendering and not handling becoming paths, exit now. if (pRender == NULL && pHandleBecomeA == NULL) return; // Only allow background rendering when going to a DIB BOOL AllowBackgroundRender = (pRender!=NULL && ( IS_A(pRender,GRenderDIB) || pRender->IsHitDetect() ) ); // If somebody else has locked the sub render context, then we cannot BG render without stepping on their toes. if (AllowBackgroundRender && pRender->IsSubRenderStateLocked()) AllowBackgroundRender = FALSE; // This should stop any problems with blends not fully rendering in separation views if (AllowBackgroundRender && pRender->IsPrinting()) AllowBackgroundRender = FALSE; // If we are rendering (i.e. pRender != NULL) do some work with the quality settings if (pRender != NULL) { // If this blend is set for non-antialiased blend steps AND the quality is set for full antialiasing // we need to render an attr that means "do everything except antialias" if (IsNotAntialiased() && pRender->RRQuality.GetAntialiasQuality() == Quality::FullAntialias) { // Create a non-antialias quality attr to render Quality Qual; QualityAttribute *pQualAttrVal = new QualityAttribute(Qual); if (pQualAttrVal != NULL) { // Render the attribute, and mark it Temporary so it will be auto-deleted // when it is finished with. pRender->SetQuality(pQualAttrVal, TRUE); } } } // Get the start and end bounds so we can do fast blended bounds intersection tests // on the render region before going ahead with the heavy stuff DocRect BoundsStart = m_pNodeStart->GetBoundingRect(); DocRect BoundsEnd = m_pNodeEnd ->GetBoundingRect(); UINT32 NumBlendSteps = GetNumBlendSteps(); // Check for silly numbers of blend steps if (NumBlendSteps > MAX_NUM_BLEND_STEPS) NumBlendSteps = MAX_NUM_BLEND_STEPS; m_BlendStep = IsReversed() ? NumBlendSteps : 1; BlendSubRenderContext* pSubRenderContext = NULL; if (pRender != NULL) { pSubRenderContext = (BlendSubRenderContext*)pRender->GetSubRenderState(); // ERROR3IF(pSubRenderContext != NULL && !IS_A(pSubRenderContext,BlendSubRenderContext),"Sub render context is of wrong type"); // If we don't own the S.R.C, then we won't treat it as our own if (pSubRenderContext != NULL && !IS_A(pSubRenderContext,BlendSubRenderContext)) pSubRenderContext = NULL; if (pSubRenderContext != NULL) { m_BlendStep = pSubRenderContext->BlendStep; } } // Find num blend paths in start and end, and keep hold of the MAX value UINT32 NumPathsInStart = m_pRefStart->GetNumBlendPaths(); UINT32 NumPathsInEnd = m_pRefEnd ->GetNumBlendPaths(); UINT32 MaxNumPaths = max(NumPathsInStart,NumPathsInEnd); // If either start or end has 0 blend paths, return. // DMc - not any more ! if (min(NumPathsInStart,NumPathsInEnd) == 0) { return; } // Let the HandleBecomeA class know the max num paths in the start and end objects if (pHandleBecomeA != NULL) pHandleBecomeA->SetNumPathsPerStep(MaxNumPaths); // This object is used to step along the the two lists of objects choosing a pair to go and blend. // It mainly comes into play when there are different numbers of objects in the two lists. ListStepper Stepper; // BlendRatio - always a value between 0.0 and 1.0; // BlendRatioStep - the value that is added to BlendRatio on every blend step double BlendRatio; double BlendRatioStep = 1.0/(NumBlendSteps+1); Path BlendedPath; if (!BlendedPath.Initialise()) return; // Do some stuff before we start blending (if this is the first time this node has been asked to render) if (pSubRenderContext == NULL) { PreBlend(); } // This holds blended attributes that are generated during the blend CCAttrMap BlendedAttrMap(30); // If we will use background rendering, we must make sure nobody inside the blend tries // to do backgrounding as well, as we can only store one sub-render context. This bodge // locks the sub render state so that nobody else is allowed to use it. (Quick bodge // by Jason to allow strokes to background render, but not crash when they're inside // a blend - in this case, individual strokes become non-interruptible, and leave // the background render support entirely to the containing blend) // We must take care to see that we unlock the state when we are done rendering. if (AllowBackgroundRender) pRender->LockSubRenderState(TRUE); // ContinueRender is set to FALSE if we should stop rendering the blend BOOL ContinueRender = TRUE; INT32 Counter = 1; UINT32 StopBlendStep = NumBlendSteps +1 ; if (IsReversed()) { Counter = -1; StopBlendStep = 0; } // NodeRenderableInk * pLastCreatedByNode = NULL; // NodeRenderableInk * pLastBlendStartNode = NULL; // the parameter for the blending of nodes BlendNodeParam BNParam; // Graeme (21-11-00) - Tightened up the check on what was the for loop (and now a while // loop), so that it stops when the blend step gets to be equal to, or greater than, the // StopBlendStep value. (Or less than if it's a reversed blend.) // // The original code stopped when the current blend step equalled the stop value, which // meant that if the m_BlendStep value leapfrogged StopBlendStep an infinite loop could // occur. (The BlendRatio would get out of bounds pretty quick, though, and that would // stop the loop within a couple more iterations.) while ( ContinueRender && ( !IsReversed() && m_BlendStep < StopBlendStep ) || ( IsReversed() && m_BlendStep > StopBlendStep ) ) { // Calc the blend ratio using the current blend step double LinearBlendRatio = BlendRatioStep * m_BlendStep; BlendRatio = MapObjectBlendRatio(LinearBlendRatio); // Get the stepper object ready for the current blend step INT32 NextPathStart,NextPathEnd; Stepper.Init(NumPathsInStart,NumPathsInEnd,0); // Reset the ptrs and subpath IDs of the previously blended blendpath pairs. BlendPath* pPrevBlendPathStart = NULL; BlendPath* pPrevBlendPathEnd = NULL; UINT32 PrevSubPathIDStart = 0; UINT32 PrevSubPathIDEnd = 0; // Get the first pair of objects to blend Stepper.GetNext(&NextPathStart,&NextPathEnd); // The first pair of blend paths become the current blend path pair // Get ptrs to these, plus the subpath IDs of these BlendPath* pCurrBlendPathStart = m_pRefStart->GetBlendPath(NextPathStart); BlendPath* pCurrBlendPathEnd = m_pRefEnd ->GetBlendPath(NextPathEnd ); UINT32 CurrSubPathIDStart = pCurrBlendPathStart->GetSubPathID(); UINT32 CurrSubPathIDEnd = pCurrBlendPathEnd ->GetSubPathID(); // We also need info on the next blend path pair, so get some vars ready BlendPath* pNextBlendPathStart; BlendPath* pNextBlendPathEnd; UINT32 NextSubPathIDStart; UINT32 NextSubPathIDEnd; // Check to see if the blended bounds intersect with the render region's clip rect BOOL BoundsIntersect = TRUE; if (pRender != NULL && pHandleBecomeA == NULL) BoundsIntersect = CheckBoundsIntersect(pRender,BoundsStart,BoundsEnd,BlendRatio); DocRect RawBlendedBounds = BlendBounds(BoundsStart,BoundsEnd,BlendRatio,FALSE); DocCoord BBCentre = RawBlendedBounds.Centre(); // CGS: I have to allocate this here - because otherwise memory becomes toast within // subsequent iterations of the following loop .... SumAllPathsPathProcessor * ptrProc = new SumAllPathsPathProcessor (FALSE); BOOL* usingSumAllPathsPathProcessor = new BOOL; *usingSumAllPathsPathProcessor = FALSE; // and whether we are using it or not // this is reset within each call to // BeginBlendStep (). This allows us to select between conventional passback and // new SumAllPathsPathProcessor stuff when blending. // This thereby allows us to handles (.e.g): // simple shape to simple shape // shadow to shadow // grouped shadow(s) to shadow // shadowed group to shadow // shadow to simple shape // grouped shadow(s) to simple shape // shadowed group to simple shape // grouped shadow(s) to grouped shadow(s) // shadowed group to shadowed group // shadowed group to grouped shadow // you get the general gist .... // We need to blend all the blend paths together, so do MaxNumPaths blends for (UINT32 NextPath = 0; ContinueRender && BoundsIntersect && (NextPath < MaxNumPaths); NextPath++) { // Get the next pair of objects to blend Stepper.GetNext(&NextPathStart,&NextPathEnd); // Ensure GLA's which applied indiviually to child objects (ie individual BlendPaths within // either BlendRef) are removed from the BlendAttrMap when changing to next BlendPath // If this doesn't occur then blended GLA left from Blend of previous BlendPath will be // applied to subsequent BlendPaths if they're not replaced (ie if subsequent BlendPaths // don't have a valid GLA applied) BlendedAttrMap.RemoveGeometryLinkedAttrs(); if (NextPathStart >= 0 && NextPathEnd >= 0) { // There's another pair to blend, so get ptrs to them, plus their subpath IDs pNextBlendPathStart = m_pRefStart->GetBlendPath(NextPathStart); pNextBlendPathEnd = m_pRefEnd ->GetBlendPath(NextPathEnd ); NextSubPathIDStart = pNextBlendPathStart->GetSubPathID(); NextSubPathIDEnd = pNextBlendPathEnd ->GetSubPathID(); } else { // No more blend pairs after this one pNextBlendPathStart = NULL; pNextBlendPathEnd = NULL; NextSubPathIDStart = 0; NextSubPathIDEnd = 0; } // Check the ptrs to be on the safe side // DMc - we don't anymore - since if either are null this means we'll // go into the new code if (pCurrBlendPathStart == NULL || pCurrBlendPathEnd == NULL) { if (AllowBackgroundRender) pRender->LockSubRenderState(FALSE); // Unlock the sub-render state return; } double AttrBlendRatio = MapAttrBlendRatio(LinearBlendRatio); NodeRenderableInk * pStartBlendNode = NULL; NodeRenderableInk * pEndBlendNode = NULL; if (LinearBlendRatio <= 0.5) // CGS: 27/7/2000 We used to use BlendRatio for // this comparison. We now use LinearBlendRatio since // this avoids the problem of 'compound jumping' when // blending from a compound to a non-compound { BNParam.Init(GetNodeBlend (), pRender, pCurrBlendPathStart, pCurrBlendPathEnd, BlendRatio, AttrBlendRatio, m_AngleStart, m_AngleEnd, GetObjectRatio(), GetInvertedAttributeRatio(), GetColourBlendType(), pCurrBlendPathStart->GetBlendNode(), pCurrBlendPathEnd->GetBlendNode(), IsOneToOne(), GetNodeBlendPath(), pHandleBecomeA, ptrProc); pStartBlendNode = pCurrBlendPathStart->GetBlendNode(); pEndBlendNode = pCurrBlendPathEnd->GetBlendNode(); } else { BNParam.Init(GetNodeBlend (), pRender, pCurrBlendPathEnd, pCurrBlendPathStart, 1.0 - BlendRatio, 1.0 - AttrBlendRatio, m_AngleStart, m_AngleEnd, 1.0 - GetObjectRatio(), 1.0 - GetInvertedAttributeRatio(), GetColourBlendType(), pCurrBlendPathEnd->GetBlendNode(), pCurrBlendPathStart->GetBlendNode(), IsOneToOne(), GetNodeBlendPath(), pHandleBecomeA, ptrProc); pStartBlendNode = pCurrBlendPathEnd->GetBlendNode(); pEndBlendNode = pCurrBlendPathStart->GetBlendNode(); } if (!CallBeginBlendStep(&BNParam, usingSumAllPathsPathProcessor)) { return ; } if (BlendAttributes(pRender,pCurrBlendPathStart,pCurrBlendPathEnd,&BlendedAttrMap,AttrBlendRatio, BlendRatio, GetNodeBlend ()->GetObjectProfileProcessing ())) { // Blend the paths together, putting the blended path in BlendedPath if (BlendPaths(pCurrBlendPathStart,pCurrBlendPathEnd,BlendRatio)) { // The blended path will be filled if either of the paths are filled // The blended path will be stroked if either of the paths are stroked BOOL Filled = pCurrBlendPathStart->IsFilled() || pCurrBlendPathEnd->IsFilled(); BOOL Stroked = pCurrBlendPathStart->IsStroked() || pCurrBlendPathEnd->IsStroked(); // If the current subpath ID is the same as the previous one, and the current path is // different to the previous path, dont clear the blended path // (this applies to the start OR the end path) BOOL DontClearPath = ((CurrSubPathIDStart == PrevSubPathIDStart && pCurrBlendPathStart != pPrevBlendPathStart) || (CurrSubPathIDEnd == PrevSubPathIDEnd && pCurrBlendPathEnd != pPrevBlendPathEnd )); if (!DontClearPath) BlendedPath.ClearPath(FALSE); if (!BlendedPath.MakeSpaceInPath(m_ArrayLength)) goto ExitRender; BlendedPath.MergeTwoPaths(m_pTempCoords,m_pTempVerbs,m_pTempFlags,m_ArrayLength,Filled); BlendedPath.IsFilled = Filled; BlendedPath.IsStroked = Stroked; // We haven't yet got a complete path to render or pass back if next path has the same ID and is // a different path // (this applies to the start OR the end path) BOOL NotACompletePath = ((CurrSubPathIDStart == NextSubPathIDStart && pCurrBlendPathStart != pNextBlendPathStart) || (CurrSubPathIDEnd == NextSubPathIDEnd && pCurrBlendPathEnd != pNextBlendPathEnd )); if (!NotACompletePath) { // DY 9/99 added this condition to enable us to blend to // and from blends on paths. if (m_BlendedOnCurve) { DocCoord PointOnPath; double Angle = 0.0; if (GetPointOnNodeBlendPath(BlendRatio,&PointOnPath,&Angle)) { Trans2DMatrix Translate(PointOnPath.x-BBCentre.x,PointOnPath.y-BBCentre.y); Trans2DMatrix Rotate(BBCentre,Angle); if (Angle != 0.0) { Rotate.Transform( (DocCoord*)BlendedPath.GetCoordArray(), BlendedPath.GetNumCoords() ); BlendedAttrMap.Transform(Rotate); } Translate.Transform( (DocCoord*)BlendedPath.GetCoordArray(), BlendedPath.GetNumCoords() ); // not a regular transform for the attributes, as we may want to tile BlendedAttrMap.TransformForBrush(Translate); } } // Ilan 4/5/00 // Call the PostDynCreateInit functions for the attributes which require this // NB attrs which don't succeed in init'ing will mark themselves so that they don't NeedToRender() // ie the AttrMap can safely be rendered - the united attrs simply won't do their job BlendedAttrMap.PostBlendInit(&BlendedPath, GetRuntimeClass()); // CGS: Now are we doing a convert to editable shapes ???? if (pHandleBecomeA != NULL) { // we are - so test if we have a SumAllPathsPathProcessor .... if (!(*usingSumAllPathsPathProcessor)) { // we don't - so we are not dealing with compound nodes // Pass the path back (as usual) ContinueRender = pHandleBecomeA->PassBack(&BlendedPath,&BlendedAttrMap,m_BlendStep); } else { // we do - so we are dealing with compound nodes .... if ((pHandleBecomeA->GetBecomeA ()->GetReason () == BECOMEA_REPLACE) || (pHandleBecomeA->GetBecomeA ()->GetReason () == BECOMEA_PASSBACK) ) { Path* pNewBlendedPath = new Path; pNewBlendedPath->Initialise (BlendedPath.GetNumCoords ()); pNewBlendedPath->CopyPathDataFrom (&BlendedPath); CCAttrMap* pNewBlendedAttrMap = BlendedAttrMap.Copy (); SumAllPathsElem * pElem = new SumAllPathsElem(pNewBlendedPath, pNewBlendedAttrMap); ptrProc->InsertSumAllPathsElem (pElem); ContinueRender = TRUE; delete (pNewBlendedPath); } } } if (pRender != NULL) { pRender->SaveContext(); // CGS: if (!(*usingSumAllPathsPathProcessor)) { RenderAttributes(pRender,&BlendedAttrMap); pRender->DrawPath(&BlendedPath); } else { Path* pNewBlendedPath = new Path; pNewBlendedPath->Initialise (BlendedPath.GetNumCoords ()); pNewBlendedPath->CopyPathDataFrom (&BlendedPath); CCAttrMap* pNewBlendedAttrMap = BlendedAttrMap.Copy (); SumAllPathsElem * pElem = new SumAllPathsElem(pNewBlendedPath, pNewBlendedAttrMap); ptrProc->InsertSumAllPathsElem (pElem); ContinueRender = TRUE; delete (pNewBlendedPath); } pRender->RestoreContext(); } // Ilan 4/5/00 // Deinitialise the attrs which allocated memory in the PostBlendInit() fn call BlendedAttrMap.PostBlendDeinit(); } } } TRACEUSER( "ChrisS", _T("Blend step == %f\n"), BlendRatio); // restore the blend paths if (!CallEndBlendStep(&BNParam)) { return ; } // Set up the vars for the next pair of blend paths // The Current vars become the previous vars PrevSubPathIDStart = CurrSubPathIDStart; PrevSubPathIDEnd = CurrSubPathIDEnd; pPrevBlendPathStart = pCurrBlendPathStart; pPrevBlendPathEnd = pCurrBlendPathEnd; // The next vars become the current vars CurrSubPathIDStart = NextSubPathIDStart; CurrSubPathIDEnd = NextSubPathIDEnd; pCurrBlendPathStart = pNextBlendPathStart; pCurrBlendPathEnd = pNextBlendPathEnd; } if (usingSumAllPathsPathProcessor != NULL) { delete (usingSumAllPathsPathProcessor); // clear out the boolean that was allocated } if (ptrProc != NULL) { delete (ptrProc); // clear out the path processor if we allocated one } // This section communicates with the render region (if there is one) to see if // we have taken too long to render this step, and hence need to update the screen during // background redraw. // Have we got a render region and have we actually rendered anything on this step? if (pRender != NULL && BoundsIntersect) { View* pView = pRender->GetRenderView(); if (pView != NULL) { if (AllowBackgroundRender && (pRender->IsHitDetect() || !pRender->RenderTreeCanContinue())) { // CanContinue() has returned FALSE, so we need to try and save the sub render // context so that the render region can call us back next time // Have we got a sub render context object? if (pSubRenderContext == NULL) { // If not, create a new BlendSubRenderContext object and make this the one // the render region should use via SetSubRenderState() pSubRenderContext = new BlendSubRenderContext; if (pSubRenderContext != NULL && pRender->GetSubRenderState() != pSubRenderContext) { // Turn off the sub render lock now, or my silly ERROR3 will go off! pRender->LockSubRenderState(FALSE); pRender->SetSubRenderState(pSubRenderContext); } } if (pSubRenderContext != NULL) { // If we have a sub render context object, set it up and exit this render // call at an appropriate point pSubRenderContext->BlendStep = m_BlendStep+Counter; // Render the next blend step next time goto ExitRender; } } } } // Increment the counter. m_BlendStep+= Counter; } // end while if (pRender != NULL && pSubRenderContext != NULL) { // Here we have finished rendering the whole blender node // We must make sure we delete the sub render context object, and set the render region's // sub render state to NULL delete pSubRenderContext; pSubRenderContext = NULL; pRender->SetSubRenderState(NULL); } // Do some stuff after the blend PostBlend(); ExitRender: // delete any attributes that are left in the blended attr map BlendedAttrMap.DeleteAttributes(); // Finally, unlock the sub-render context so others may use it (now that it is relatively safe) if (AllowBackgroundRender) pRender->LockSubRenderState(FALSE); TRACEUSER( "ChrisS", _T("Exiting create blends\n")); } /******************************************************************************************** > BOOL NodeBlender::CheckBoundsIntersect(RenderRegion* pRender,DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio) Author: Mark_Neves (Xara Group Ltd) Created: 2/12/94 Inputs: pRender = ptr to the current render region BoundsStart = the blob bounds of start object BoundsEnd = the blob bounds of end object BlendRatio = current blend point (between 0.0 and 1.0) Outputs: - Returns: TRUE if blended bounds intersect with the render region's clip rect. Purpose: Finds the blended bounds that lies at point BlendRatio and tests for intersection. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::CheckBoundsIntersect(RenderRegion* pRender,DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio) { BOOL Clip = TRUE; if (pRender != NULL) { DocRect BlendedBounds = BlendBounds(BoundsStart,BoundsEnd,BlendRatio,TRUE); Clip = BlendedBounds.IsIntersectedWith(pRender->GetClipRect()); //pRender->DrawRect(&BlendedBounds); } return Clip; } /******************************************************************************************** > DocRect NodeBlender::BlendBounds(DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio) Author: Mark_Neves (Xara Group Ltd) Created: 5/5/99 Inputs: BoundsStart = the blob bounds of start object BoundsEnd = the blob bounds of end object BlendRatio = current blend point (between 0.0 and 1.0) Outputs: - Returns: The blended bounds. Purpose: Finds the blended bounds that lies at point BlendRatio and tests for intersection. Takes into account the path the blend is following, if it has one. SeeAlso: - ********************************************************************************************/ DocRect NodeBlender::BlendBounds(DocRect& BoundsStart,DocRect& BoundsEnd,double BlendRatio,BOOL UseBlendPath) { DocRect BlendedBounds,OldBoundsStart,OldBoundsEnd; if (UseBlendPath) { OldBoundsStart = BoundsStart; OldBoundsEnd = BoundsEnd; RotateBounds(BoundsStart,-m_AngleStart); RotateBounds(BoundsEnd,-m_AngleEnd); } BlendedBounds.lo.x = INT32(BoundsStart.lo.x + ((BoundsEnd.lo.x-BoundsStart.lo.x)*BlendRatio)); BlendedBounds.lo.y = INT32(BoundsStart.lo.y + ((BoundsEnd.lo.y-BoundsStart.lo.y)*BlendRatio)); BlendedBounds.hi.x = INT32(BoundsStart.hi.x + ((BoundsEnd.hi.x-BoundsStart.hi.x)*BlendRatio)); BlendedBounds.hi.y = INT32(BoundsStart.hi.y + ((BoundsEnd.hi.y-BoundsStart.hi.y)*BlendRatio)); if (UseBlendPath) { DocCoord PointOnPath; double Angle = 0.0; if (GetPointOnNodeBlendPath(BlendRatio,&PointOnPath,&Angle)) { DocCoord BBCentre = BlendedBounds.Centre(); BlendedBounds.Translate(PointOnPath.x-BBCentre.x,PointOnPath.y-BBCentre.y); RotateBounds(BlendedBounds,Angle); } } if (UseBlendPath) { BoundsStart = OldBoundsStart; BoundsEnd = OldBoundsEnd; } return BlendedBounds; } /******************************************************************************************** > void NodeBlender::RotateBounds(DocRect& Bounds,double Angle) Author: Mark_Neves (Xara Group Ltd) Created: 21/5/99 Inputs: Bounds = the bounds to rotate Angle = the angle in degrees by which to rotate it by Outputs: - Returns: - Purpose: The bounds are updated to contain the rotated version SeeAlso: - ********************************************************************************************/ void NodeBlender::RotateBounds(DocRect& Bounds,double Angle) { if (Angle != 0.0) { DocCoord BBCentre = Bounds.Centre(); DocCoord Coords[4]; Coords[0].x = Bounds.lo.x; Coords[0].y = Bounds.lo.y; Coords[1].x = Bounds.hi.x; Coords[1].y = Bounds.lo.y; Coords[2].x = Bounds.hi.x; Coords[2].y = Bounds.hi.y; Coords[3].x = Bounds.lo.x; Coords[3].y = Bounds.hi.y; Trans2DMatrix Rotate(BBCentre,Angle); Rotate.Transform(Coords,4); Bounds.lo.x = min(min(Coords[0].x,Coords[1].x), min(Coords[2].x,Coords[3].x)); Bounds.hi.x = max(max(Coords[0].x,Coords[1].x), max(Coords[2].x,Coords[3].x)); Bounds.lo.y = min(min(Coords[0].y,Coords[1].y), min(Coords[2].y,Coords[3].y)); Bounds.hi.y = max(max(Coords[0].y,Coords[1].y), max(Coords[2].y,Coords[3].y)); } } /******************************************************************************************** > Trans2DMatrix* NodeBlender::GetRotateMatrix(NodeRenderableBounded* pNode,double Angle) Author: Mark_Neves (Xara Group Ltd) Created: 25/5/99 Inputs: pNode = the node who's bounds define the centre of rotation Angle = Angle to rotate by, in degrees Outputs: - Returns: ptr to the matrix that will perform the rotation or NULL if no rotation is necessary (i.e. Angle % 360.0 == 0.0) Purpose: Function that returns a matrix that will rotate around the centre of the bounds of the given node SeeAlso: - ********************************************************************************************/ Trans2DMatrix* NodeBlender::GetRotateMatrix(NodeRenderableBounded* pNode,double Angle) { Trans2DMatrix* pMatrix = NULL; if (pNode != NULL && fmod(Angle,360.0) != 0.0) { DocRect BoundsStart = pNode->GetBoundingRect(); DocCoord Centre = BoundsStart.Centre(); pMatrix = new Trans2DMatrix(Centre,Angle); } return pMatrix; } /******************************************************************************************** > DocRect NodeBlender::GetUnionBlendedBounds(DocRect& BoundsStart,DocRect& BoundsEnd) Author: Mark_Neves (Xara Group Ltd) Created: 5/5/99 Inputs: - Outputs: - Returns: The union of blended bounds. Purpose: Finds the blended bounds that lies at point BlendRatio and tests for intersection. Takes into account the path the blend is following, if it has one. SeeAlso: - ********************************************************************************************/ DocRect NodeBlender::GetUnionBlendedBounds(DocRect& BoundsStart,DocRect& BoundsEnd) { DocRect UnionBlendedBounds(0,0,0,0); UINT32 NumBlendSteps = GetNumBlendSteps(); double BlendRatio = 0.0; double BlendRatioStep = 1.0/(NumBlendSteps+1); for (UINT32 BlendStep = 1; BlendStep <= NumBlendSteps; BlendStep++) { // Calc the blend ratio using the current blend step BlendRatio = BlendRatioStep * BlendStep; DocRect BlendedBounds = BlendBounds(BoundsStart,BoundsEnd,BlendRatio,TRUE); if (BlendStep == 1) UnionBlendedBounds = BlendedBounds; else UnionBlendedBounds = UnionBlendedBounds.Union(BlendedBounds); } return UnionBlendedBounds; } /******************************************************************************************** > void NodeBlender::RenderAttributes(RenderRegion* pRender,CCAttrMap* pAttrMap) Author: Mark_Neves (Xara Group Ltd) Created: 20/10/94 Inputs: pRender = ptr to render region pAttrMap = ptr to map of ttributes Outputs: - Returns: - Purpose: Renders the attributes to the given render region SeeAlso: - ********************************************************************************************/ void NodeBlender::RenderAttributes(RenderRegion* pRender,CCAttrMap* pAttrMap) { // Check entry params BOOL ok = (pRender != NULL && pAttrMap != NULL); ERROR3IF(!ok,"One or more NULL entry params"); if (!ok) return; CCRuntimeClass* pType; void* pVal; // iterating all (key, value) pairs for (CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); Pos != pAttrMap->GetEndPosition();) { // Get attr at CMapPtrToPtr::iterator Pos pAttrMap->GetNextAssoc(Pos,pType,pVal); // Don't render quality attributes, to ensure steps are rendered with the same quality // as the start and end objects. if (pType != CC_RUNTIME_CLASS(AttrQuality)) { // pVal is actually an attribute, so get a ptr to it and render it NodeAttribute* pNodeAttr = (NodeAttribute *)pVal; // *********+**** BODGE (removed 1/12/94) **************** // Ignore these two attrs until I get Wills attr changes, involving unimplemented Restore() funcs // if (!pNodeAttr->IS_KIND_OF(AttrDashPattern) && !pNodeAttr->IS_KIND_OF(AttrTranspFillMapping)) // pNodeAttr->Render(pRender); if (pNodeAttr->IsABrush()) { //TRACEUSER( "Diccon", _T("Blend step %d, rendering Brush\n"), m_BlendStep); PathProcessor* pPathProc = pRender->GetFirstPathProcessor(); if (pPathProc != NULL && pPathProc->IsAPathProcessorBrush()) continue; } pNodeAttr->Render(pRender); } } } /******************************************************************************************** > BOOL NodeBlender::BlendAttributes(RenderRegion *pRender, BlendPath* pBlendPathStart, BlendPath* pBlendPathEnd,CCAttrMap* pBlendedAttrMap, double BlendRatio, double objectRatio, BOOL objectProfileProcessing) Author: Mark_Neves (Xara Group Ltd) Created: 20/10/94 Inputs: pRender = The Render Region into which we are rendering the blend, or NULL if not rendering (i.e. doing a make shapes) pBlendPathStart = ptr to blend path to blend from pBlendPathEnd = ptr to blend path to blend to pBlendedAttrMap = ptr to map to store the blended attributes in BlendRatio = amount to blend by (0.0 <= BlendPath <= 1.0) objectRatio = the corresponding object ratio (CGS: allows us to blend object stuff with attributes) objectProfileProcessing = whether we are allowed to blend object (i.e. CMapPtrToPtr::iterator) data of attributes Outputs: - Returns: TRUE if successful, FALSE otherwise Purpose: Blends the attributes of the two BlendPath objects by the amount specified in BlendRatio SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::BlendAttributes(RenderRegion *pRender, BlendPath* pBlendPathStart, BlendPath* pBlendPathEnd,CCAttrMap* pBlendedAttrMap, double BlendRatio, double objectRatio, BOOL objectProfileProcessing) { // Check entry params BOOL ok = (pBlendPathStart != NULL && pBlendPathEnd != NULL && pBlendedAttrMap != NULL); ERROR3IF(!ok,"One or more NULL entry params"); if (!ok) return FALSE; // Find the attributes that are applied to the blend paths CCAttrMap* pAttrMapStart = m_pRefStart->FindAppliedAttributes(pBlendPathStart); CCAttrMap* pAttrMapEnd = m_pRefEnd ->FindAppliedAttributes(pBlendPathEnd); // If either are NULL, return FALSE if (pAttrMapStart == NULL || pAttrMapEnd == NULL) return FALSE; //#ifdef _DEBUG // AttrTranspFillGeometry* pLineWidth = NULL; // pMap->Lookup((void *)CC_RUNTIME_CLASS(AttrLineWidth), (void *&)pIndent); // //UINT32* pCol = pLineWidth->GetStartTransp (); // // pAttrMapEnd->Lookup((void*) CC_RUNTIME_CLASS(AttrTranspFillGeometry),(void* &)pLineWidth); // //ol = pLineWidth->GetStartTransp (); //#endif pAttrMapStart->attrMapCreator = pBlendPathStart->GetBlendNode (); pAttrMapEnd->attrMapCreator = pBlendPathEnd->GetBlendNode (); if (GetNodeBlendPath() != NULL) { Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pNodeStart,360.0 - m_AngleStart); Trans2DMatrix* pRotateEnd = GetRotateMatrix(m_pNodeEnd ,360.0 - m_AngleEnd ); if (pRotateStart) pAttrMapStart->Transform(*pRotateStart); if (pRotateEnd) pAttrMapEnd ->Transform(*pRotateEnd); DELPTR(pRotateStart); DELPTR(pRotateEnd); } // Diccon 24/5/2000 Brush attributes need to know about stroke colour, so we need to // keep track of both as we find them AttrBrushType* pBlendedAttrBrush = NULL; AttrStrokeColour* pBlendedStrokeColour = NULL;; // These vars are used as params to the CCAttrMap funcs CCRuntimeClass* pTypeStart; void* pValStart; void* pValEnd; double OldBlendRatio = BlendRatio; // Process each attribute in turn CMapPtrToPtr::iterator PosStart = pAttrMapStart->GetStartPosition(); while ( PosStart != pAttrMapStart->GetEndPosition() ) { // Get a ptr to the attr at CMapPtrToPtr::iterator PosStart in the start node's attr map pAttrMapStart->GetNextAssoc(PosStart,pTypeStart,pValStart); NodeAttribute* pNodeAttrStart = (NodeAttribute *)pValStart; BlendRatio = OldBlendRatio; // Diccon 10/99 When using non-linear profiles for the objects those attributes // that make use of control points were not being profiled, making the objects look strange. // to avoid this those attributes now share the same profiles as the objects. if (pNodeAttrStart->IsAGradFill()) { if (!((AttrFillGeometry*)pNodeAttrStart)->IsAColourFill()) { BlendRatio = GetObjectRatio(); } else { BlendRatio = GetInvertedAttributeRatio(); } } if (pNodeAttrStart->IsAFlatFill() || (pNodeAttrStart->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrLineWidth))) { BlendRatio = GetInvertedAttributeRatio(); } // Get a blended attribute NodeAttribute* pBlendedNodeAttr = NULL; // Find an attr of the same type in the end object's attr list, // and blend the two attrs together if (pAttrMapEnd->Lookup(pTypeStart,pValEnd)) { // We've found a matching end attr, so try to blend it with the start attr // Set up the param object to pass to the start attr's blend method BlendAttrParam BlendParam; // Initialise the BlendParam with the end attr and blend ratio if (BlendParam.Init(pRender, (NodeAttribute *)pValEnd,BlendRatio,objectRatio,objectProfileProcessing,GetColourBlendType(), pAttrMapStart, pAttrMapEnd) ) { // Successfully initialised, so now try blending the attributes if (pNodeAttrStart->Blend(&BlendParam)) { // Attrs successfully blended, now get a ptr to the new attr. // Once we get the blended attr ptr, it belongs to us, so we have // to delete it when it is not needed pBlendedNodeAttr = BlendParam.GetBlendedAttr(); } else { #ifdef _DEBUG void *tmp = NULL; ENSURE(!pBlendedAttrMap->Lookup(pNodeAttrStart->GetRuntimeClass(),tmp),"Didn't blend attr, but there's still one in pBlendedAttrMap"); #endif } } } // If we have a blended attr, pBlendedNodeAttr != NULL if (pBlendedNodeAttr != NULL) { // Get the type of the blended attr CCRuntimeClass* pTypeBlend = pBlendedNodeAttr->GetAttributeType(); void* pValBlend; // If we already have an attr in the blended attr map of the same type, // remove it and delete it, before inserting a new attr of this type if (pBlendedAttrMap->Lookup(pTypeBlend,pValBlend)) { if (pValBlend != NULL) { pBlendedAttrMap->RemoveKey(pTypeBlend); delete (NodeAttribute*)pValBlend; } } // add it to the blend map pBlendedAttrMap->SetAt(pTypeBlend,pBlendedNodeAttr); // find out if we are a stroke colour or a brush if (pBlendedNodeAttr->IsAStrokeColour()) pBlendedStrokeColour = (AttrStrokeColour*)pBlendedNodeAttr; if (pBlendedNodeAttr->IsABrush()) pBlendedAttrBrush = (AttrBrushType*)pBlendedNodeAttr; } } if (GetNodeBlendPath() != NULL) { Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pNodeStart,m_AngleStart); Trans2DMatrix* pRotateEnd = GetRotateMatrix(m_pNodeEnd ,m_AngleEnd ); if (pRotateStart) pAttrMapStart->Transform(*pRotateStart); if (pRotateEnd) pAttrMapEnd ->Transform(*pRotateEnd); DELPTR(pRotateStart); DELPTR(pRotateEnd); } // if we got a stroke colour and a brush then tell the brush what the stroke colour is if (pBlendedAttrBrush != NULL && pBlendedStrokeColour != NULL) pBlendedAttrBrush->SetBlendedStrokeColour(&(pBlendedStrokeColour->Value.Colour)); return TRUE; } /******************************************************************************************** > BOOL NodeBlender::BlendPaths(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio) Author: Mark_Neves (Xara Group Ltd) Created: 14/10/94 Inputs: pBlendPathStart = ptr to blend path to blend from pBlendPathEnd = ptr to blend path to blend to BlendRatio = amount to blend by (0.0 <= BlendPath <= 1.0) Outputs: The blended path is stored in three arrays: the coords, the verbs, and the flags. The arrays are: pTempCoords pTempVerbs pTempFlags ArrayLength = the length of all three arrays This allows the caller to decide what to do with the blended path in a very flexible way. Returns: TRUE if successful, FALSE otherwise Purpose: Blends two BlendPath objects by the amount specified in BlendRatio SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::BlendPaths(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio) { // Check entry params BOOL ok = (pBlendPathStart != NULL && pBlendPathEnd != NULL); if (ok) ok = (pBlendPathStart->GetBlendNode() != NULL && pBlendPathEnd->GetBlendNode() != NULL); ERROR3IF(!ok,"One or more NULL entry params"); if (!ok) return FALSE; // Get the types of the two paths PathTypeEnum PathTypeStart = pBlendPathStart->GetPathType(); PathTypeEnum PathTypeEnd = pBlendPathEnd ->GetPathType(); // The blended path will be closed if either of the paths is a shape BOOL Closed = (PathTypeStart == PATHTYPE_SHAPE) || (PathTypeEnd == PATHTYPE_SHAPE); Path * pPathStart = NULL; // Find the paths associated with the start and end blend paths if (pBlendPathStart->GetBlendNode()->IsNodePath()) { pPathStart = &(((NodePath *)pBlendPathStart->GetBlendNode())->InkPath); } else { pPathStart = &(((NodePath *)((NodeCompound *)pBlendPathStart->GetBlendNode())->GetNodeToBlend())->InkPath); } Path * pPathEnd = NULL; if (pBlendPathEnd->GetBlendNode()->IsNodePath()) { pPathEnd = &(((NodePath *)pBlendPathEnd->GetBlendNode())->InkPath); } else { pPathEnd = &(((NodePath *)((NodeCompound *)pBlendPathEnd->GetBlendNode())->GetNodeToBlend())->InkPath); } // Calculate how large the arrays have to be to store the blended path definition INT32 DestPathSize = ((pPathStart->GetNumCoords()+pPathEnd->GetNumCoords())*3)+500; // Get some arrays used to hold the blended path data, and error if any are NULL DocCoord* pDestCoords = GetCoordArray(DestPathSize); PathVerb* pDestVerbs = GetVerbArray(DestPathSize); PathFlags* pDestFlags = GetFlagArray(DestPathSize); UINT32* pBuff = GetGBlendBuff(DestPathSize); if (pDestCoords == NULL || pDestVerbs == NULL || pDestFlags == NULL || pBuff == NULL) return FALSE; // This section copes with the case when blending a line with a shape. // In this case we need to get a temp path the is actually a shape version of the line. // The line is simply reversed back onto itself to form a shape that would look identical to the // line if rendered. This allows the line to appear to open up to the shape when blended. Path Shape; if (PathTypeStart != PathTypeEnd) { BOOL ok = FALSE; if (!Shape.Initialise()) return FALSE; // if going from a line to a shape, convert the start path to a shape if (PathTypeStart == PATHTYPE_LINE && PathTypeEnd == PATHTYPE_SHAPE) { ok = NodeBlender::ConvertLineToShape(pPathStart,&Shape); pPathStart = &Shape; } // if going from a shape to a line, convert the end path to a shape if (PathTypeStart == PATHTYPE_SHAPE && PathTypeEnd == PATHTYPE_LINE) { ok = NodeBlender::ConvertLineToShape(pPathEnd,&Shape); pPathEnd = &Shape; } if (!ok) return FALSE; } // The blend should do a one-to-one mapping when the OneToOne flag is set AND both paths // have the same number of elements BOOL OneToOne = FALSE; if (IsOneToOne()) OneToOne = (pBlendPathStart->GetNumElements() == pBlendPathEnd->GetNumElements()); // Now actually blend the two paths GBlend GBlendObj; // Define the blend GBlendObj.Define( (PPOINT)pPathStart->GetCoordArray(), // Specify the start path pPathStart->GetVerbArray(), pPathStart->GetNumCoords(), (PPOINT)pPathEnd ->GetCoordArray(), // Specify the end path pPathEnd ->GetVerbArray(), pPathEnd ->GetNumCoords(), OneToOne, // The one-to-one flag FLATNESS, // Flatness pBuff, // Buffer for GBlend to use DestPathSize*sizeof(UINT32)); // The buffer size // Blend the paths m_ArrayLength = GBlendObj.Blend(BlendRatio, // The blend ratio, 0.0 < BlendRatio < 1.0 (PPOINT)pDestCoords, // Array to store blended coords pDestVerbs, // Array to store blended verbs DestPathSize); // The num elements in the two arrays // If we're blending a line to another line, we need to make sure that the blended line // is going in a direction that corresponds to the source lines. This ensures attributes // that depend on this direction (e.g. start and end arrows) look correct. // // When creating blend paths of lines, we can detect if the blend path has been reversed, // in relation to the original path, by the original mapping value. // If it's 0 it has NOT been reversed, otherwise it's been reversed. // // If the blend ratio is <=0.5, the blended path is closest to the start blend path, // so we look at the start blend path's original mapping. // // If blend ration > 0.5, look at the end blend path's original mapping. // // The (BlendRation <= 0.5) cut-off point is the same as the cut-off point used in the blending // of attributes. if (pBlendPathStart->IsLine() && pBlendPathEnd->IsLine()) { BlendPath* pBlendPath; if (BlendRatio <= 0.5) pBlendPath = pBlendPathStart; else pBlendPath = pBlendPathEnd; if (pBlendPath->GetOrigMapping() != 0) ReversePath(pDestCoords,pDestVerbs,m_ArrayLength); } // We need to do some work on the blended path if (!ProcessBlendedPath(pDestCoords,pDestVerbs,pDestFlags,m_ArrayLength,Closed)) return FALSE; return TRUE; } /******************************************************************************************** > void NodeBlender::ReversePath(DocCoord* pCoords,PathVerb* pVerbs,UINT32 Len) Author: Mark_Neves (Xara Group Ltd) Created: 24/1/96 Inputs: pCoords = ptr to coords array pVerbs = ptr to verbs array Len = the length of above arrays Outputs: - Returns: - Purpose: This will reverse the path definition given in the two arrays. Path flags are not delt with as these are specific to the Path class. This function is designed to process raw paths returned from Gavin. SeeAlso: - ********************************************************************************************/ void NodeBlender::ReversePath(DocCoord* pCoords,PathVerb* pVerbs,UINT32 Len) { // First we will reverse all the Coords UINT32 LastPos = Len-1; UINT32 FirstPos = 0; while (FirstPos void NodeBlender::UpdateBlendStartAngles (Trans2DMatrix& trans) Author: Chris_Snook (Xara Group Ltd) Created: 25/7/2000 Inputs: trans = the transformation matrix Outputs: - Returns: - Purpose: Updates a blend on curves start angles after we have rotated the blend. SeeAlso: - NOTE: If problems arise the function should be modified to store undo information. However at present, this seems to be generated for us somewhere else; which is fine by me. ********************************************************************************************/ void NodeBlender::UpdateBlendStartAngles (Trans2DMatrix& trans) { // only update if these are non-zero if ((m_AngleStart != 0.0 || m_AngleEnd != 0.0)) { double ang = (trans.GetRotation ()).MakeDouble (); m_AngleStart += ang; m_AngleEnd += ang; } } /******************************************************************************************** > virtual INT32 NodeBlender::EstimateNodeComplexity (OpParam* details) Author: Chris_Snook (Xara Group Ltd) Created: 6/09/2000 Inputs: details any data that should be used for the calculation Outputs: - Returns: an estimate of the nodes complexity Purpose: This function estimates a complexity value for the node. The complexity value is based upon the total length of all paths in the node. See Also: OpBlendNodes::DeterminBlendObjectsProcessorHit () ********************************************************************************************/ INT32 NodeBlender::EstimateNodeComplexity (OpParam* details) { //m_pNodeStart,&m_pRefStart if ((m_pNodeStart == NULL) || (m_pNodeEnd == NULL)) { return (-1); } INT32 complexityEstimate1 = m_pNodeStart->EstimateNodeComplexity (details); INT32 complexityEstimate2 = m_pNodeEnd->EstimateNodeComplexity (details); UINT32 NumBlendStepsby2 = 0; if (!details) { NumBlendStepsby2 = GetNumBlendSteps()/2; } else { NumBlendStepsby2 = (UINT32) (INT32) details->Param1; } INT32 generatedPathsEstimate = NumBlendStepsby2+1*complexityEstimate1 + NumBlendStepsby2*complexityEstimate2; return (generatedPathsEstimate); } /******************************************************************************************** > BOOL NodeBlender::ConvertLineToShape(Path* pPath,Path* pShapePath) Author: Mark_Neves (Xara Group Ltd) Created: 17/11/94 Inputs: pPath = ptr to the line to convert pShapePath = ptr to path to store the shape version of the line Outputs: - Returns: TRUE if OK, FALSE if it goes bang Purpose: This takes a line and creates a shape out of it, placing the shape in pShapePath. It does this: A copy of the line is made, and reversed. The reversed copy has its start movetto element removed. A shape is formed by merging the line with the reversed copy. This allows us to blend a line with a shape, giving the impression that the line opens out into the shape. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::ConvertLineToShape(Path* pPath,Path* pShapePath) { ERROR3IF(pPath == NULL || pShapePath == NULL,"One or more NULL entry params"); if (pPath == NULL || pShapePath == NULL) return FALSE; Path ReversePath; if (!ReversePath.Initialise(pPath->GetNumCoords())) return FALSE; if (!ReversePath.CopyPathDataFrom(pPath)) return FALSE; PathVerb* pRevVerb = ReversePath.GetVerbArray(); PathVerb* pShapeVerb = pShapePath->GetVerbArray(); ReversePath.Reverse(); ReversePath.FindStartOfPath(); ReversePath.DeleteElement(); pRevVerb = ReversePath.GetVerbArray(); pShapeVerb = pShapePath->GetVerbArray(); pShapePath->ClearPath(FALSE); if (!pShapePath->MergeTwoPaths(*pPath)) return FALSE; if (!pShapePath->MergeTwoPaths(ReversePath)) return FALSE; return TRUE; } /******************************************************************************************** > BOOL NodeBlender::ProcessBlendedPath(DocCoord* pCoords,PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len,BOOL Closed) Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: pCoords = ptr to coords of the blended path pVerbs = ptr to verbs of blended path pFlags = ptr to uninitialised flags array Len = num verbs in pVerbs Closed = The resultant path should be closed Outputs: pCoords array contains a valid set of coords pVerbs array contains a valid set of verbs pFlags array contains valid flags for each of the verbs in pVerbs Returns: TRUE if OK, FALSE if it goes bang Purpose: This does any processing necessary on the path generated by GBlend. The three arrays can be used to generate renderable paths, and also legal paths that can be used to create NodePath objects on the tree. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::ProcessBlendedPath(DocCoord* pCoords,PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len,BOOL Closed) { if (Len < 1) return FALSE; // Generate a legal path flags array SetPathFlags(pVerbs,pFlags,Len); // If closed, make sure there's a PT_CLOSEFIGURE on the last end point if (Closed) pVerbs[Len-1] = pVerbs[Len-1] | PT_CLOSEFIGURE; return TRUE; } /******************************************************************************************** > void NodeBlender::SetPathFlags(PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len) Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: pVerbs = ptr to path verbs pFlags = ptr to flags Len = num verbs in pVerbs Outputs: pFlags array contains valid flags for each of the verbs in pVerbs Returns: - Purpose: This generates a legal path flags array based on the given verbs. All it does is set the end point flags correctly, leaving all other flags FALSE SeeAlso: - ********************************************************************************************/ void NodeBlender::SetPathFlags(PathVerb* pVerbs,PathFlags* pFlags,UINT32 Len) { UINT32 n; // Reset all the path flags to default values PathFlags DefaultPathFlags; for (n=0; n < Len; n++) pFlags[n] = DefaultPathFlags; // Scan the verbs, so that the end point flags can be set correctly for (n=0; n < Len; n++) { PathVerb Verb = pVerbs[n] & ~PT_CLOSEFIGURE; switch (Verb) { case PT_LINETO: case PT_MOVETO: pFlags[n].IsEndPoint = TRUE; break; case PT_BEZIERTO: n += 2; ERROR3IF(n>=Len,"Found a PT_BEZIERTO, but third pt is off the end of the array"); pFlags[n].IsEndPoint = TRUE; break; default: ERROR3_PF(("Illegal path verb found : %c",Verb)); break; } } } /******************************************************************************************** > Matrix NodeBlender::MakeMatrix(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio) Author: Mark_Neves (Xara Group Ltd) (clever bit by Mike) Created: 20/10/94 Inputs: pBlendPathStart = ptr to blend path to blend from pBlendPathEnd = ptr to blend path to blend to BlendRation = amount to blend by (0.0 <= BlendPath <= 1.0) Outputs: - Returns: A matrix Purpose: Makes the matrix for the transfrom that should be applied to blend paths and attributes SeeAlso: - ********************************************************************************************/ Matrix NodeBlender::MakeMatrix(BlendPath* pBlendPathStart,BlendPath* pBlendPathEnd,double BlendRatio) { // Check entry params BOOL ok = (pBlendPathStart != NULL && pBlendPathEnd != NULL); if (ok) ok = (pBlendPathStart->GetCreatedByNode() != NULL && pBlendPathEnd->GetCreatedByNode() != NULL); ERROR3IF(!ok,"One or more NULL entry params"); if (!ok) { Matrix M; return M; } // Get the bounds of the objects that generated the paths DocRect BoundsStart = pBlendPathStart->GetCreatedByNode()->GetBoundingRect(TRUE); DocRect BoundsEnd = pBlendPathEnd ->GetCreatedByNode()->GetBoundingRect(TRUE); // The following creates a matrix that transforms the start rect to the end rect // dependent on the blend ratio, courtesy of Mike (thanks!) double Sx0 = BoundsStart.lo.x; double Sy0 = BoundsStart.lo.y; double Sx1 = BoundsStart.hi.x; double Sy1 = BoundsStart.hi.y; double Dx0 = BoundsEnd.lo.x; double Dy0 = BoundsEnd.lo.y; double Dx1 = BoundsEnd.hi.x; double Dy1 = BoundsEnd.hi.y; double Ex0 = Sx0 + BlendRatio*(Dx0-Sx0); double Ey0 = Sy0 + BlendRatio*(Dy0-Sy0); double Ex1 = Sx1 + BlendRatio*(Dx1-Sx1); double Ey1 = Sy1 + BlendRatio*(Dy1-Sy1); double t0 = (Ex1-Ex0)/(Sx1-Sx0); double t1 = 0; double t2 = 0; double t3 = (Ey1-Ey0)/(Sy1-Sy0); FIXED16 a(t0); FIXED16 b(t1); FIXED16 c(t2); FIXED16 d(t3); INT32 e = (INT32) (Ex0-Sx0*t0); INT32 f = (INT32) (Ey0-Sy0*t3); Matrix M(a,b,c,d,e,f); return (M); } /******************************************************************************************** > double NodeBlender::MapObjectBlendRatio(double BlendRatio) Author: Mark_Neves (Xara Group Ltd) Created: 1/6/99 Inputs: BlendRatio - the linear blend ration, between 0.0 and 1.0 Returns: The mapped blend ratio to use for object blending Purpose: Function that works out what blend ratio to use when creating a blended bath. Allows non-linear processing of blends SeeAlso: NodePath::Render; NodeRenderableInk::RenderEorDrag ********************************************************************************************/ double NodeBlender::MapObjectBlendRatio(double BlendRatio) { NodeBlend* pNodeBlend = GetNodeBlend(); if (pNodeBlend) { IProfile* pProfile = pNodeBlend->GetObjectProfile(); if (pProfile) return pProfile->MapZeroToOne(BlendRatio); } return BlendRatio; } /******************************************************************************************** > double NodeBlender::MapAttrBlendRatio(double BlendRatio) Author: Mark_Neves (Xara Group Ltd) Created: 1/6/99 Inputs: BlendRatio - the linear blend ration, between 0.0 and 1.0 Returns: The mapped blend ratio to use for attribute blending Purpose: Function that works out what blend ratio to use when blending attributes Allows non-linear processing of blend attributes SeeAlso: NodePath::Render; NodeRenderableInk::RenderEorDrag ********************************************************************************************/ double NodeBlender::MapAttrBlendRatio(double BlendRatio) { NodeBlend* pNodeBlend = GetNodeBlend(); if (pNodeBlend) { IProfile* pProfile = pNodeBlend->GetAttrProfile(); if (pProfile) return pProfile->MapZeroToOne(BlendRatio); } return BlendRatio; } /******************************************************************************************** > double NodeBlender::MapCMapPtrToPtr::iteratorBlendRatio(double BlendRatio) Author: Mark_Neves (Xara Group Ltd) Created: 1/6/99 Inputs: BlendRatio - the linear blend ration, between 0.0 and 1.0 Returns: The mapped blend ratio to use for CMapPtrToPtr::iteratoring the blended shapes Purpose: Function that works out what blend ratio to use when CMapPtrToPtr::iteratoring the blended shapes Allows non-linear processing of blend shape CMapPtrToPtr::iteratoring SeeAlso: NodePath::Render; NodeRenderableInk::RenderEorDrag ********************************************************************************************/ double NodeBlender::MapPositionBlendRatio(double BlendRatio) { NodeBlend* pNodeBlend = GetNodeBlend(); if (pNodeBlend) { IProfile* pProfile = pNodeBlend->GetPositionProfile(); if (pProfile) return pProfile->MapZeroToOne(BlendRatio); } return BlendRatio; } /******************************************************************************************** > double NodeBlender::GetObjectRatio() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 1/10/99 Inputs: - Returns: The mapped blend ratio to use for blend objects Purpose: returns the current mapping of objects ********************************************************************************************/ double NodeBlender::GetObjectRatio() { UINT32 NumSteps = GetNumBlendSteps() + 1; double StepRatio = 1.0/NumSteps; double LinearRatio = StepRatio * m_BlendStep; CProfileBiasGain* ObjectProfile = GetNodeBlend()->GetObjectProfile(); return ObjectProfile->MapZeroToOne(LinearRatio); } /******************************************************************************************** > double NodeBlender::GetInvertedAttributeRatio() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 1/10/99 Inputs: - Returns: The mapped blend ratio to use for blend attributes Purpose: Function that inverts the current attribute profile and returns the mapped value This is needed to fix the problem with profiles and transparencies and other fills that use control points. SeeAlso: NodePath::Render; NodeRenderableInk::RenderEorDrag ********************************************************************************************/ double NodeBlender::GetInvertedAttributeRatio() { UINT32 NumSteps = GetNumBlendSteps() + 1; double StepRatio = 1.0/NumSteps; double LinearRatio = StepRatio * m_BlendStep; CProfileBiasGain* AttributeProfile = GetNodeBlend()->GetAttrProfile(); CProfileBiasGain InvertedProfile; InvertedProfile.SetBias(-1*(AttributeProfile->GetBias())); InvertedProfile.SetGain(-1*(AttributeProfile->GetGain())); /*double Test =*/ AttributeProfile->MapZeroToOne(LinearRatio); return InvertedProfile.MapZeroToOne(LinearRatio); } /******************************************************************************************** > void NodeBlender::RenderEorDrag( RenderRegion* pRender ) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: pRender - A Pointer to the current RenderRegion Purpose: Renders a version of the blender node for EORed dragging of blends. SeeAlso: NodePath::Render; NodeRenderableInk::RenderEorDrag ********************************************************************************************/ void NodeBlender::RenderEorDrag( RenderRegion* pRender ) { if (!Reinit()) return; } /******************************************************************************************** > virtual void NodeBlender::RenderBlendBlobs(RenderRegion* pRender,BOOL RenderStart,BOOL RenderEnd) Author: Mark_Neves (Xara Group Ltd) Created: 9/11/94 Inputs: pRender = region to render blobs into RenderStart = TRUE if blobs of start of blend should be rendered RenderEnd = TRUE if blobs of end of blend should be rendered Outputs: - Returns: - Purpose: Renders the blobs of the blend paths associated with the given blender NOTE: This only renders blobs if there's only one blend path in both the start and the end of the blend SeeAlso: - ********************************************************************************************/ void NodeBlender::RenderBlendBlobs(RenderRegion* pRender,BOOL RenderStart,BOOL RenderEnd) { #if !defined(EXCLUDE_FROM_RALPH) if (!Reinit()) return; if (m_pRefStart == NULL || m_pRefEnd == NULL) return; if (m_pRefStart->GetNumBlendPaths() == 1 && m_pRefEnd->GetNumBlendPaths() == 1) { Trans2DMatrix* pRotateStart= NULL; Trans2DMatrix* pRotateEnd = NULL; if (GetNodeBlendPath() != NULL) { pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),m_AngleStart); pRotateEnd = GetRotateMatrix(m_pRefEnd ->GetNode(),m_AngleEnd ); } if (RenderStart) m_pRefStart->RenderBlendBlobs(pRender,pRotateStart); if (RenderEnd) m_pRefEnd ->RenderBlendBlobs(pRender,pRotateEnd); BlendPath* pBlendPathStart = m_pRefStart->GetFirstBlendPath(); BlendPath* pBlendPathEnd = m_pRefEnd ->GetFirstBlendPath(); if (pBlendPathStart != NULL && pBlendPathEnd != NULL) { DocCoord Start = pBlendPathStart->GetPathCoord(0); DocCoord End = pBlendPathEnd ->GetPathCoord(0); if (pRotateStart) pRotateStart->Transform(&Start,1); if (pRotateEnd) pRotateEnd ->Transform(&End ,1); // Get the scaled pixel size for the view FIXED16 ScaledPixelWidth, ScaledPixelHeight; // Not used pRender->GetRenderView()->GetScaledPixelSize(&ScaledPixelWidth, &ScaledPixelHeight); pRender->SetLineWidth((ScaledPixelWidth.MakeLong())*2); pRender->SetLineColour(COLOUR_BLACK); pRender->DrawLine(Start,End); } DELPTR(pRotateStart); DELPTR(pRotateEnd); } #endif } /******************************************************************************************** > DocRect NodeBlender::GetBoundingRect(BOOL DontUseAttrs=FALSE, BOOL HitTest=FALSE) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: DontUseAttrs - TRUE if we should ignore the nodes attributes. Defaults to FALSE HitTest - TRUE if being called during HitTest Returns: The nodes bounding rect Purpose: if the bounding rect is valid it is returned, if not, it is recalculated and then returned. SeeAlso: NodeBlender::GetBlobBoundingRect ********************************************************************************************/ DocRect NodeBlender::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest) { if (Parent != NULL && IS_A(Parent,NodeBlend) && !((NodeBlend*)Parent)->AreBoundsValid()) IsBoundingRectValid = FALSE; if (!IsBoundingRectValid || DontUseAttrs) { // We need ptrs to the start and end objects this blender is blending NodeRenderableInk* pStart = NULL; NodeRenderableInk* pEnd = NULL; // If we are initialised, get the start and end ink nodes if (m_pRefStart != NULL) pStart = m_pRefStart->GetNode(); if (m_pRefEnd != NULL) pEnd = m_pRefEnd ->GetNode(); if (pStart == NULL) pStart = m_pNodeStart; if (pEnd == NULL) pEnd = m_pNodeEnd; // If we don't have ptrs to the start and end objects of this blend, have a look for them if (pStart == NULL) pStart = (NodeRenderableInk*)FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk)); if (pEnd == NULL) pEnd = (NodeRenderableInk*)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk)); // Check the ink nodes to ensure that they are what we expect. ERROR3IF(pStart == NULL,"pStart == NULL"); ERROR3IF(pEnd == NULL,"pEnd == NULL"); ERROR3IF(!pStart->IS_KIND_OF(NodeRenderableInk),"pStart not a NodeRenderableInk"); ERROR3IF(!pEnd ->IS_KIND_OF(NodeRenderableInk),"pEnd not a NodeRenderableInk"); ERROR3IF(pStart->IS_KIND_OF(NodeBlender),"pStart is a NodeBlender"); ERROR3IF(pEnd ->IS_KIND_OF(NodeBlender),"pEnd not a NodeBlender"); // Continue only if we have nodes that we expect BOOL ok = (pStart != NULL) && (pEnd != NULL) && (pStart->IS_KIND_OF(NodeRenderableInk)) && (pEnd ->IS_KIND_OF(NodeRenderableInk)) && (!pStart->IS_KIND_OF(NodeBlender)) && (!pEnd ->IS_KIND_OF(NodeBlender)); if (ok) { // The bounds for the entire blender is the union of the bounds of the start and end objects blended. // Assume here that blended paths can't lie outside this bounding rect. DocRect BoundsStart = pStart->GetBoundingRect(DontUseAttrs,HitTest); DocRect BoundsEnd = pEnd ->GetBoundingRect(DontUseAttrs,HitTest); DocRect NewBounds; NewBounds = BoundsStart; NewBounds = NewBounds.Union(BoundsEnd); if (GetNodeBlendPath() != NULL) NewBounds = NewBounds.Union(GetUnionBlendedBounds(BoundsStart,BoundsEnd)); // If the caller didn't want bounds included, then return the bounds immediately, // and do not cache the bounds rectangle. if (DontUseAttrs) return(NewBounds); // Cache the new bounding rectangle IsBoundingRectValid = TRUE; BoundingRectangle = NewBounds; } } /* if (!IsBoundingRectValid && Reinit()) { if (pRefStart != NULL && pRefStart->GetNode() != NULL && pRefEnd != NULL && pRefEnd ->GetNode() != NULL) { BoundingRectangle = pRefStart->GetNode()->GetBoundingRect(DontUseAttrs); BoundingRectangle = BoundingRectangle.Union(pRefEnd->GetNode()->GetBoundingRect(DontUseAttrs)); IsBoundingRectValid = TRUE; } } */ // return the current state of the bounding rect return BoundingRectangle; } /******************************************************************************************** > DocRect NodeBlender::GetBlobBoundingRect() Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Returns: DocRect - Returns the bounding rect of the blender node and its blobs Purpose: At the moment, blender nodes don't have any blobs, so just the standard bounding box is returned ********************************************************************************************/ DocRect NodeBlender::GetBlobBoundingRect() { return (GetBoundingRect()); } /******************************************************************************************** > virtual UINT32 NodeBlender::GetNodeSize() const Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Returns: The size of the node in bytes Purpose: For finding the size of the node SeeAlso: Node::GetSubtreeSize ********************************************************************************************/ UINT32 NodeBlender::GetNodeSize() const { return (sizeof(NodeBlender)); } /******************************************************************************************** > BOOL NodeBlender::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread ) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: PointerPos - The Location of the mouse pointer at the time of the click Click - The type of click received (single, double, drag etc) ClickMods - The modifiers to the click (eg shift, control etc ) Returns: BOOL - TRUE if the node claims the click as its own and FALSE if it is not interested in the click Purpose: Allows the Node to respond to clicks by selecting its blobs or starting drags etc. This functions should be overridden in the all the NodeRenderableInk classes so that this version never gets called. Eg the NodePath class might claim the click if it happened over one of its unselected blobs. ********************************************************************************************/ BOOL NodeBlender::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread ) { if (!Reinit()) return FALSE; // we did not use the click, so let someone else try return FALSE; } /******************************************************************************************** > virtual BOOL NodeBlender::CanBecomeA(BecomeA* pBecomeA) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: InkClass: The class of object pNumObjects = ptr to place number of objects of type pClass that will be created (Note: can be NULL). *pNumObects in undefined on entry Returns: TRUE if the node, or any of its children can transmogrify themselves to become an InkClass object Purpose: This function is used by the convert to shapes operation. It determines if the node or any of its children can convert themselves into an InkClass object. The number you put into pNumObjects (if it's not NULL) should exactly equal the total number of pClass objects you create. It should NOT contain any additional objects you may produce such as group objects for containing the pClass object, or attributes. Also, the entry value of *pNumObjects cannot be assumed to be 0. ********************************************************************************************/ BOOL NodeBlender::CanBecomeA(BecomeA* pBecomeA) { // The NodeBlender can become a NodePath if (pBecomeA->BAPath()) { if (pBecomeA->IsCounting()) { if (!Reinit()) return FALSE; // Check that we have a valid blender if (m_pRefStart == NULL || m_pRefEnd == NULL) return FALSE; UINT32 NumBlendSteps = GetNumBlendSteps(); // Find num blend paths in start and end, and keep hold of the MAX value // The max value is the number of individual paths created for each blend step UINT32 NumPathsInStart = m_pRefStart->GetNumBlendPaths(); UINT32 NumPathsInEnd = m_pRefEnd ->GetNumBlendPaths(); UINT32 MaxNumPaths = max(NumPathsInStart,NumPathsInEnd); // The number of objects // *pNumObjects = NumBlendSteps * MaxNumPaths; pBecomeA->AddCount(NumBlendSteps * MaxNumPaths); } return TRUE; } return FALSE; } /******************************************************************************************** > virtual BOOL NodeBlender::DoBecomeA(CCRuntimeClass* InkClass, UndoableOperation* pOp) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: pBecomeA = ptr to a class that contains all the info needed to become a new type of node. Outputs: - Returns: TRUE if the object has been transformed, FALSE if we run out of memory Purpose: Transforms the object into another type of object. SeeAlso: NodeBlender::CanBecomeA ********************************************************************************************/ BOOL NodeBlender::DoBecomeA(BecomeA* pBecomeA) { if (!Reinit()) return TRUE; if (!m_Initialised) return TRUE; // we MUST handle this for future versions ! // Check for a NULL entry param ERROR2IF(pBecomeA == NULL,FALSE,"pBecomeA is NULL"); // This lump checks that the Reason is one that we understand // It also makes sure that we don't have a NULL UndoOp ptr BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK); ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason())); // Set up a HandleBecomeA object and call CreateBlends(). // This mechanism means the the same code is used to both render blends and create paths // from blends. HandleBecomeA HBecomeA(this,this,pBecomeA); CreateBlends(NULL,&HBecomeA); switch (pBecomeA->GetReason()) { case BECOMEA_REPLACE : { // It's a BECOMEA_REPLACE, so hide this node in an undoable way // Can't do it in an undoable way without an Undo Op // ERROR2IF(pBecomeA->GetUndoOp() == NULL,FALSE,"GetUndoOp() returned NULL"); UndoableOperation* pOp = pBecomeA->GetUndoOp(); // We need to make sure that all the groups we may have produced have their attrs correctly // factored out. This is so the final factoring out done by the NodeBlend node will be able // to complete the job. // Without this step, leaf nodes can have common child attrs that should be factored up the // to a higher level. Node* pNode = m_pNodeStart->FindNext(); while (pNode != NULL && pNode != m_pNodeEnd) { //if (IS_A (pNode, NodeGroup)) if (pNode->IsCompoundClass()) { if (pOp) { if (!pOp->DoFactorOutCommonChildAttributes((NodeCompound*)pNode)) return FALSE; } else { if (!((NodeCompound*)pNode)->FactorOutCommonChildAttributes()) return(FALSE); } } pNode = pNode->FindNext(); } NodeHidden* pHiddenNodeBlender = NULL; // hide this blender node if (pOp) { if (!pOp->DoHideNode(this, TRUE, &pHiddenNodeBlender)) return FALSE; } else { CascadeDelete(); delete this; } } break; case BECOMEA_PASSBACK : // Don't do anything for this reason break; default: break; } return TRUE; } /*********************************************************************************************** > BOOL NodeBlender::Snap(DocCoord* pDocCoord) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: pDocCoord = a coord in Spread coords Outputs: Returns: TRUE - the DocCoord has been snapped . FALSE - the DocCoord has not been processed. Purpose: Snaps to given coord to the nearest point on the blender node. Just returns FALSE currently Errors: Scope: public **********************************************************************************************/ BOOL NodeBlender::Snap(DocCoord* pDocCoord) { return FALSE; } /*********************************************************************************************** > BOOL NodeBlender::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: pDocCoord - the rectangle to snap StartDrag - Start coord of drag EndDrag - End coord of drag Outputs: Returns: TRUE - the DocRect been snapped. FALSE - the DocRect has not been processed. Purpose: Snaps the given rect to the nearest CMapPtrToPtr::iterator on the grid, preserving its width and height. The coords of the rect used for the snapping are determined by the PrevCoord and CurCoord coords supplied. This is done to allow the user to control how a selection rectangle is snapped to the grid by the direction of his/her last mouse movement. To force the bottom left hand corner of the rect to be snapped, supply PrevCoord=(0,0) and CurCoord(-1,-1). ALWAYS RETURNS FALSE currently. Scope: public **********************************************************************************************/ BOOL NodeBlender::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord) { return FALSE; } /******************************************************************************************** > BOOL NodeBlender::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,BOOL* pAStart); Author: Mark_Neves (Xara Group Ltd) Created: 10/11/94 Inputs: pPointerPos = point to check against ppBlendPath= ptr to place to store blend path ptr if a match is found pIndex = ptr to place to store index if match is found pAStart = ptr to place to store start or end flag Outputs: *ppBlendPath & *pIndex & *pAStart updated if TRUE returned, undefined otherwise Returns: TRUE if match found, FALSE otherwise Purpose: This sees if the point given lies on a selected blob. If a match is found, the ptr to the blend path and index to blob element is returned. Also, if found,*pAStart = TRUE if it belongs to the start path, FALSE for the end path Markn 26-5-99: Modified so that if the function returns TRUE, *pPointerPos is updated so that it contains the coords of the blob it is over SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,BOOL* pAStart) { #if !defined(EXCLUDE_FROM_RALPH) if (m_pRefStart == NULL || m_pRefEnd == NULL || pPointerPos == NULL) return FALSE; BOOL Found = FALSE; if (m_pRefStart->GetNumBlendPaths() == 1 && m_pRefEnd->GetNumBlendPaths() == 1) { Trans2DMatrix* pRotateStart= NULL; Trans2DMatrix* pRotateEnd = NULL; if (GetNodeBlendPath() != NULL) { pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),m_AngleStart); pRotateEnd = GetRotateMatrix(m_pRefEnd ->GetNode(),m_AngleEnd ); } if (m_pRefStart->IsPointOverBlob(pPointerPos,ppBlendPath,pIndex,pRotateStart)) { Found = TRUE; *pAStart = TRUE; } if (m_pRefEnd->IsPointOverBlob(pPointerPos,ppBlendPath,pIndex,pRotateEnd)) { Found = TRUE; *pAStart = FALSE; } DELPTR(pRotateStart); DELPTR(pRotateEnd); } return Found; #else return FALSE; #endif } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- /*********************************************************************************************** > BOOL NodeBlender::Initialise( NodeRenderableInk* pNodeStart, NodeRenderableInk* pNodeEnd, INT32 ThisPathIndexStart, INT32 ThisPathIndexEnd, UndoableOperation* pUndoOp, Progress* pProgress, BOOL IgnoreEscape) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pNodeStart = ptr to the start of the blend (can be NULL - see later) pNodeEnd = ptr to the end of the blend (can be NULL - see later) ThisPathIndexStart = the index into the start path of the blob the user blended from (-1 means none) ThisPathIndexEnd = the index into the end path of the blob the user blended to (-1 means none) pUndoOp = ptr to an undoable op so that initialisation can be done in an undoable way (can be NULL) pProgress = ptr to progress object for maintaining hour glass IgnoreEscape = TRUE if you don't want this process abortable via the Escape key Outputs: - Returns: TRUE if successfully initialises, FALSE otherwise Purpose: This initialises the blender node so that it is in a state in which it can blend the two nodes together. This should be called AFTER this NodeBlender and its start and end nodes have been placed under the corresponding NodeBlend node. This is so that it can get a reference to the nodes relative to the parent NodeBlend node. If pNodeStart == NULL, then the previous node is chosen. If there isn't a previous node or the node is not a NodeRenderableInk, FALSE will be returned. If pNodeEnd == NULL, then the next node is chosen. If there isn't a next node or the node is not a NodeRenderableInk, FALSE will be returned. ***********************************************************************************************/ BOOL NodeBlender::Initialise( NodeRenderableInk* pThisNodeStart, NodeRenderableInk* pThisNodeEnd, INT32 ThisPathIndexStart, INT32 ThisPathIndexEnd, UndoableOperation* pUndoOp, Progress* pProgress, BOOL IgnoreEscape) { // Copy the ink node ptrs into member vars m_pNodeStart = pThisNodeStart; m_pNodeEnd = pThisNodeEnd; // Copy the path index params into member vars m_PathIndexStart = ThisPathIndexStart; m_PathIndexEnd = ThisPathIndexEnd; // If the ink nodes are NULL, search for appropriate ink nodes if (m_pNodeStart == NULL) m_pNodeStart = (NodeRenderableInk*)FindPrevious(); if (m_pNodeEnd == NULL) m_pNodeEnd = (NodeRenderableInk*)FindNext(); // Check the ink nodes ERROR2IF(m_pNodeStart == NULL,FALSE,"m_pNodeStart == NULL"); ERROR2IF(m_pNodeEnd == NULL,FALSE,"m_pNodeEnd == NULL"); ERROR2IF(!m_pNodeStart->IS_KIND_OF(NodeRenderableInk),FALSE,"m_pNodeStart not a NodeRenderableInk"); ERROR2IF(!m_pNodeEnd ->IS_KIND_OF(NodeRenderableInk),FALSE,"m_pNodeEnd not a NodeRenderableInk"); ERROR2IF(m_pNodeStart->IS_KIND_OF(NodeBlender),FALSE,"m_pNodeStart is a NodeBlender"); ERROR2IF(m_pNodeEnd ->IS_KIND_OF(NodeBlender),FALSE,"m_pNodeEnd not a NodeBlender"); // Delete old BlendRefs we may have (we might be reinitialising) DELPTR(m_pRefStart); DELPTR(m_pRefEnd); // Reset the member vars before we try and initialise the blender m_pRefStart = NULL; m_pRefEnd = NULL; m_ObjIndexStart = -1; m_ObjIndexEnd = -1; m_Initialised = FALSE; Trans2DMatrix* pRotateStart= NULL; Trans2DMatrix* pRotateEnd = NULL; if (GetNodeBlendPath() != NULL) { pRotateStart = GetRotateMatrix(m_pNodeStart,360.0 - m_AngleStart); pRotateEnd = GetRotateMatrix(m_pNodeEnd ,360.0 - m_AngleEnd ); } BOOL ok = TRUE; // CGS: we can have funky blends on paths. Everything works fine if they both have a path. // However, if they do NOT both have paths - then Mr Blender (incorrectly) blends the NodeBlendPath // into the paths generated by the other blend! // we SHOULD prevent this from happening .... if (ok) ok = InitBlendRef(m_pNodeStart,&m_pRefStart,m_PathIndexStart,pUndoOp,pProgress,IgnoreEscape,pRotateStart); if (ok) ok = InitBlendRef(m_pNodeEnd ,&m_pRefEnd ,m_PathIndexEnd ,pUndoOp,pProgress,IgnoreEscape,pRotateEnd); if (ok) { m_pRefStart->StripRedundantNodeBlendPaths (m_pRefEnd); } if (ok) ok = CalcObjIndex(m_pNodeStart,&m_ObjIndexStart); if (ok) ok = CalcObjIndex(m_pNodeEnd ,&m_ObjIndexEnd); // Ensure these indexes are kept up to date (needed for cut/copy/paste, export, etc). // Note that the following code dumps the indexes computed by the calls to InitBlendRef // above and recomputes them before assigning them to m_PathIndexStart and m_PathIndexEnd // (A lot of DoBecomeA work to compute two numbers...) if (((m_pRefStart->GetNumBlendPaths () > 1) && (m_pRefEnd->GetNumBlendPaths () == 1)) || ((m_pRefStart->GetNumBlendPaths () == 1) && (m_pRefEnd->GetNumBlendPaths () > 1))) { m_PathIndexStart = -1; m_PathIndexEnd = -1; DELPTR(m_pRefStart); DELPTR(m_pRefEnd); if (ok) ok = InitBlendRef(m_pNodeStart,&m_pRefStart,m_PathIndexStart,pUndoOp,pProgress,IgnoreEscape,pRotateStart); if (ok) ok = InitBlendRef(m_pNodeEnd ,&m_pRefEnd ,m_PathIndexEnd ,pUndoOp,pProgress,IgnoreEscape,pRotateEnd); if (ok) { m_pRefStart->StripRedundantNodeBlendPaths (m_pRefEnd); } } if (ok) m_PathIndexStart = m_pRefStart->GetOrigMapping(); if (ok) m_PathIndexEnd = m_pRefEnd ->GetOrigMapping(); // Make sure the Initialised flag is TRUE only if we have done everything successfully. if (ok) CheckFullyInitialised(); if (ok) ok = m_Initialised; DELPTR(pRotateStart); DELPTR(pRotateEnd); // Don't need to delete the BlendRefs if things go wrong. // They'll get deleted next time it's initialised, or by the deconstructor return (ok); } /*********************************************************************************************** > BOOL NodeBlender::InitBlendRef(NodeRenderableInk* pNode,BlendRef** ppRef,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pNode = ptr to a node in the tree ppRef = ptr to place to store a new BlendRef structure Index = index into path of blob clicked on (-1 means ignore) pUndoOp = ptr to an undoable op so that initialisation can be done in an undoable way (can be NULL) pProgress = ptr to progress object for maintaining hour glass IgnoreEscape= TRUE if you don't want this process abortable via the Escape key pMatrix = transformation to apply to each blend path (can be NULL) Outputs: *ppRef = ptr to an initialised blend reference Returns: TRUE if successfully initialises, FALSE otherwise Purpose: This creates and initialises a blend reference. ***********************************************************************************************/ BOOL NodeBlender::InitBlendRef(NodeRenderableInk* pNode,BlendRef** ppRef,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix) { ERROR2IF(ppRef == NULL,FALSE,"ppRef == NULL"); ALLOC_WITH_FAIL(*ppRef,new BlendRef,pUndoOp); BOOL ok = (*ppRef != NULL); if (ok) ok = (*ppRef)->Initialise(pNode,Index,pUndoOp,pProgress,IgnoreEscape,pMatrix); return (ok); } /******************************************************************************************** > BOOL NodeBlender::CalcObjIndex(NodeRenderableInk* pInkNode,INT32* pObjIndex) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pInkNode = ptr to a node that this blender will blend pObjIndex = ptr to place to store the relative reference to this node Outputs: *pObjIndex will contain the reference if TRUE is returned, -1 if FALSE is returned Returns: TRUE if successful, FALSE otherwise Purpose: Firstly, this will check that this node and pInkNode have the same NodeBlend parent node. If all's well, it will scan the children of the NodeBlend, starting with the first child, counting until it comes to pInkNode. If pInkNode is the first child, *pObjIndex == 0. If the second child, *pObjIndex == 1, etc SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::CalcObjIndex(NodeRenderableInk* pInkNode,INT32* pObjIndex) { ERROR2IF(pInkNode == NULL,FALSE,"pInkNode is NULL"); ERROR2IF(pObjIndex == NULL,FALSE,"pObjIndex is NULL"); Node* pParent = pInkNode->FindParent(); /*if (pInkNode->IsNodeHidden ()) { ERROR3 ("Node is hidden!"); }*/ ERROR2IF(pParent == NULL,FALSE,"pInkNode has no parent"); if (!pParent->IsController ()) // CGS: we can now have compound nodes in here! { ERROR2IF(!IS_A(pParent,NodeBlend),FALSE,"pInkNode parent is not a NodeBlend"); ERROR2IF(pParent != this->FindParent(),FALSE,"pInkNode has different parent to this node"); } else { // compound might be shadowed - if it is, find the shadow, and check that its parent is the NodeBlend .... if (IS_A (pParent->FindParent (), NodeShadowController)) { pParent = pParent->FindParent ()->FindParent (); } ERROR2IF(pParent->FindParent () != this->FindParent(),FALSE,"pInkNode has different parent to this node"); } INT32 Index = 0; Node* pNode = pParent->FindFirstChild(); while (pNode != NULL && pNode != pInkNode) { if (pNode->IS_KIND_OF(NodeRenderableInk)) Index++; if (pNode->IS_KIND_OF(NodeBlendPath)) Index--; pNode = pNode->FindNext(); } if (pNode == pInkNode) *pObjIndex = Index; else *pObjIndex = -1; return (pNode == pInkNode); } /******************************************************************************************** > NodeRenderableInk* NodeBlender::FindObjIndexedNode(INT32 ObjIndex) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Adjusted Diccon Yamanaka 16/9/99 Inputs: ObjIndex = Relative ref to the object under the parent of this node Outputs: - Returns: Ptr to the node. NULL if couldn't be found or node is of wrong type Purpose: This scans the children of the parent of this blender, until it finds the ObjIndex'th NodeRenderableInk. I.e. if ObjIndex == 0, it returns the first child NodeRenderableInk. if ObjIndex == 1, returns the second child NodeRenderableInk. Fails if: ObjIndex >= number of children the parent has The node found is not a NodeRenderableInk or is a NodeBlender SeeAlso: - ********************************************************************************************/ NodeRenderableInk* NodeBlender::FindObjIndexedNode(INT32 ObjIndex) { ERROR3IF_PF(ObjIndex < 0,("ObjIndex is -ve!! (%ld)",ObjIndex)); if (ObjIndex < 0) return NULL; Node* pParent = this->FindParent(); Node* pFoundNode = NULL; ERROR3IF(pParent == NULL,"pInkNode has no parent"); ERROR3IF(!IS_A(pParent,NodeBlend),"parent is not a NodeBlend"); if (pParent != NULL && IS_A(pParent,NodeBlend)) { Node* pNode = pParent->FindFirstChild(); while (pNode != NULL && ObjIndex >= 0) { if (pNode->IS_KIND_OF(NodeRenderableInk)) { //DY 16/9/99 fix to make blending along a curve possible // as we insert NodeBlendPath during that operation. if (!pNode->IS_KIND_OF(NodeBlendPath)) { if (ObjIndex == 0) pFoundNode = pNode; ObjIndex--; } } pNode = pNode->FindNext(); } } if (pFoundNode != NULL) { if (IS_A(pFoundNode,NodeBlender)) { ERROR3("Found node is a NodeBlender"); pFoundNode = NULL; } else if (!pFoundNode->IS_KIND_OF(NodeRenderableInk)) { ERROR3("Found node is not a NodeRenderableInk"); pFoundNode = NULL; } } else ERROR3("Couldn't find ref node"); return ((NodeRenderableInk*)pFoundNode); } /******************************************************************************************** > virtual BOOL NodeBlender::CanSelectAsSimple() Author: Phil_Martin (Xara Group Ltd) Created: 06/01/95 Inputs: - Outputs: - Returns: TRUE if the node will allow itself to be selected when it has been "hit" FALSE otherwise Purpose: Ask a node whether or not it's prepared to become selected when it's clicked on. This function is called in the FindSimple routines when they have just detected a "hit" on a node. This virtual base function is overridden here to indicate that the NodeBlender object is definitely NOT prepared to become selected! Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint ********************************************************************************************/ BOOL NodeBlender::CanSelectAsSimple() { return FALSE; } /******************************************************************************************** > void NodeBlender::CheckFullyInitialised() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: - Outputs: - Returns: - Purpose: This will set the object's Initialise member to TRUE if it is fully initialised, i.e. pRefStart & pRefEnd != NULL, and ObjIndexStart and ObjIndexEnd and >= 0 SeeAlso: - ********************************************************************************************/ void NodeBlender::CheckFullyInitialised() { m_Initialised = ((m_pRefStart != NULL) && (m_pRefEnd != NULL) && (m_ObjIndexStart >= 0) && (m_ObjIndexEnd >= 0)); } /******************************************************************************************** > BOOL NodeBlender::Reinit(NodeRenderableInk* pInkNodeStart = NULL,NodeRenderableInk* pInkNodeEnd = NULL,BOOL ProgressBar = TRUE) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 adjusted Diccon Yamanaka 16/9/99 Inputs: pInkNodeStart = ptr to the node to blend from (can be NULL) pInkNodeEnd = ptr to the node to blend to (can be NULL) ProgressBar = if TRUE a progress bar will be displayed. Outputs: - Returns: TRUE if successful, FALSE otherwise Purpose: This will try and reinitialise the blender by using either the relative reference values ObjIndexStart and ObjIndexEnd (if input params are NULL) or the nodes provided. If using the indexes, it finds the actual pointers to the nodes that they reference, and calls Initialise() If already initialised, it returns TRUE immediately SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::Reinit(NodeRenderableInk* pInkNodeStart,NodeRenderableInk* pInkNodeEnd,BOOL ProgressBar) { if (GetNodeBlend()) { CProfileBiasGain* pObjProfile = GetNodeBlend()->GetObjectProfile(); if (pObjProfile) { // this ensures that blend object profile processing is ON if we need it .... GetNodeBlend()->RequestObjectProfileProcessing(TRUE); } } if (m_Initialised) return TRUE; /* Diccon 9/99 changes to permit blending between two blends using arbitrary start and end points mean that NodeBlenders are no longer necessarily CMapPtrToPtr::iteratored in between the two nodes that they are blending. This means that the third method of finding start and end nodes is now likely to yield incorrect results, with the sole exception of when you are loading an old format file */ if (pInkNodeStart == NULL) pInkNodeStart = m_pNodeStart; if (pInkNodeEnd == NULL) pInkNodeEnd = m_pNodeEnd; // If still no start and end node, use the ObjIndex value to find them if (pInkNodeStart == NULL && m_ObjIndexStart >= 0) pInkNodeStart = FindObjIndexedNode(m_ObjIndexStart); if (pInkNodeEnd == NULL && m_ObjIndexEnd >= 0) pInkNodeEnd = FindObjIndexedNode(m_ObjIndexEnd); // the only time we should be using this method is if we are loading old format files with blends in them if (pInkNodeStart == NULL) pInkNodeStart = (NodeRenderableInk*)FindPrevious(CC_RUNTIME_CLASS(NodeRenderableInk)); if (pInkNodeEnd == NULL) pInkNodeEnd = (NodeRenderableInk*)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk)); while (pInkNodeEnd->IS_KIND_OF(NodeBlendPath)) pInkNodeEnd = (NodeRenderableInk*)pInkNodeEnd->FindNext(CC_RUNTIME_CLASS(NodeRenderableInk)); // In debug builds, inform the user, as something's gone wrong ERROR3IF(pInkNodeStart == NULL,"pInkNodeStart is NULL"); ERROR3IF(pInkNodeEnd == NULL,"pInkNodeEnd is NULL"); if (pInkNodeStart == NULL || pInkNodeEnd == NULL) return TRUE; if (ProgressBar) { Progress Hourglass(_R(IDS_REINTBLEND)); return (Initialise(pInkNodeStart,pInkNodeEnd,m_PathIndexStart,m_PathIndexEnd,NULL,&Hourglass,TRUE)); } else return (Initialise(pInkNodeStart,pInkNodeEnd,m_PathIndexStart,m_PathIndexEnd,NULL,NULL,TRUE)); } /******************************************************************************************** > void NodeBlender::Deinit(BOOL bNodesMayBeChanged = FALSE) Author: Mark_Neves (Xara Group Ltd) Created: 4/11/94 Inputs: - Outputs: - Returns: - Purpose: This deinits the node, setting its internal Initialised flag to FALSE, and deleting any memory that is created during initialisation. It's like a combination of the constructor and destructor, leaving it in a state similar to defualt construction. The state is such that a call to Reinit() can exactly recreate the blender node. Used when hiding NodeBlenders to free up some memory. SeeAlso: - ********************************************************************************************/ void NodeBlender::Deinit(BOOL bNodesMayBeChanged) { // CGS: we must now delete any intermediate blend step generated stuff .... // NOTE: these lists will only contains items when we have been asked to do // a blend of a grouped blend to another grouped blend. Node* node = FindPrevious (); NodeBlend* node2 = (NodeBlend*) node->FindFirstChild (CC_RUNTIME_CLASS (NodeBlend)); NodeGroup::KillAllBlendBecomeAConsListItem (node2); if (bNodesMayBeChanged) { m_pNodeStart = NULL; m_pNodeEnd = NULL; } if (m_Initialised) { DELPTR(m_pRefStart); DELPTR(m_pRefEnd); m_Initialised = FALSE; } // if we are on a curve then destroy the cached points of the curve NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(); if (pNodeBlendPath != NULL) pNodeBlendPath->DestroyCachedInformation(); } /******************************************************************************************** > UINT32 NodeBlender::GetNumBlendSteps() Author: Mark_Neves (Xara Group Ltd) Created: 4/11/94 Inputs: - Outputs: - Returns: Num blend steps for this blender Purpose: Gets the number of blend steps for this blender, by asking the parent blend node for its num blend steps SeeAlso: - ********************************************************************************************/ UINT32 NodeBlender::GetNumBlendSteps() { UINT32 NumBlendSteps = 5; NodeBlend* pNode = GetNodeBlend(); if (pNode != NULL) NumBlendSteps = pNode->GetNumBlendSteps(); else { ERROR3("Parent of blender is not there, or is not a NodeBlend!!!"); } return NumBlendSteps; } /******************************************************************************************** > ColourBlendType NodeBlender::GetColourBlendType() Author: Mark_Neves (Xara Group Ltd) Created: 17/3/95 Inputs: - Outputs: - Returns: How colours are blended Purpose: Gets the way colours are blended by asking the parent blend node SeeAlso: - ********************************************************************************************/ ColourBlendType NodeBlender::GetColourBlendType() { ColourBlendType ColBlendType = COLOURBLEND_FADE; NodeBlend* pNode = GetNodeBlend(); if (pNode != NULL) ColBlendType = pNode->GetColourBlendType(); else { ERROR3("Parent of blender is not there, or is not a NodeBlend!!!"); } return ColBlendType; } /******************************************************************************************** > BOOL NodeBlender::IsOneToOne() Author: Mark_Neves (Xara Group Ltd) Created: 30/11/94 Inputs: - Outputs: - Returns: The one-to-one flag state Purpose: Gets the one-to-one blend mapping state for this blender, by asking the parent blend node SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::IsOneToOne() { BOOL OneToOne = FALSE; NodeBlend* pNode = GetNodeBlend(); if (pNode != NULL) OneToOne = pNode->IsOneToOne(); else { ERROR3("Parent of blender is not there, or is not a NodeBlend!!!"); } return OneToOne; } /******************************************************************************************** > BOOL NodeBlender::IsNotAntialiased() Author: Mark_Neves (Xara Group Ltd) Created: 30/11/94 Inputs: - Outputs: - Returns: The NotAntialiased flag state Purpose: Gets the NotAntialiased state for this blender, by asking the parent blend node SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::IsNotAntialiased() { BOOL NotAntialiased = FALSE; NodeBlend* pNode = GetNodeBlend(); if (pNode != NULL) NotAntialiased = pNode->IsNotAntialiased(); else { ERROR3("Parent of blender is not there, or is not a NodeBlend!!!"); } return NotAntialiased; } /******************************************************************************************** > BOOL NodeBlender::IsTangential() Author: Mark_Neves (Xara Group Ltd) Created: 21/5/96 Inputs: - Returns: TRUE if this blend should follow the curve tangentially Purpose: See above SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::IsTangential() { NodeBlend* pNode = GetNodeBlend(); if (pNode != NULL) return pNode->IsTangential(); return FALSE; } /******************************************************************************************** > INT32 NodeBlender::GetNodeBlendPathIndex() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: - Returns: the index to the nodeblendpath to which this blender refers, or -1 if it is not on a curve Purpose: See above SeeAlso: - ********************************************************************************************/ INT32 NodeBlender::GetNodeBlendPathIndex() { return m_NodeBlendPathIndex; } /******************************************************************************************** > void NodeBlender::SetNodeBlendPathIndex(INT32 Index) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: the index to the nodeblendpath to which this blender refers, or -1 if it is not on a curve Returns: - Purpose: See above SeeAlso: - ********************************************************************************************/ void NodeBlender::SetNodeBlendPathIndex(INT32 Index) { if (Index < -2) { ERROR3("Invalid index value"); return; } m_NodeBlendPathIndex = Index; return; } /******************************************************************************************** > BOOL NodeBlender::ReverseEnds() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 29/9/99 Inputs: - Returns: TRUE if successfull, FALSE otherwise Purpose: reverses the blend so that the start node becomes the end node and vice versa SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::ReverseEnds() { NodeRenderableInk* pTemp = NULL; pTemp = m_pNodeStart; m_pNodeStart = m_pNodeEnd; m_pNodeEnd = pTemp; SetReversed(!IsReversed()); INT32 Temp = m_ObjIndexStart; m_ObjIndexStart = m_ObjIndexEnd; m_ObjIndexEnd = Temp; return TRUE; } /******************************************************************************************** > BOOL NodeBlender::PreBlend() Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: - Outputs: - Returns: TRUE if it's OK to continue, FALSE otherwise Purpose: This should be called just before blending occurs. At the moment it does the following - Initialises stuff so you can call GetCoordArray(), GetVerbArray() and GetFlagArray() SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::PreBlend() { // Clean up in case PostBlend() wasn't called after the last PreBlend() call PostBlend(); if (m_pRefStart != NULL) m_pRefStart->PreBlend(); if (m_pRefEnd != NULL) m_pRefEnd ->PreBlend(); // Allocate the arrays return (ReallocTempBuffers(1000)); } /******************************************************************************************** > void NodeBlender::PostBlend() Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: - Outputs: - Returns: - Purpose: This should be called just after blending occurs. At the moment it does the following - Releases memory allocated for the temp path arrays SeeAlso: - ********************************************************************************************/ void NodeBlender::PostBlend() { if (m_pRefStart != NULL) m_pRefStart->PostBlend(); if (m_pRefEnd != NULL) m_pRefEnd ->PostBlend(); DeallocTempBuffers(); } /******************************************************************************************** > BOOL NodeBlender::ReallocTempBuffers(UINT32 Size) Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: Size = Size the temp arrays should be Outputs: - Returns: - Purpose: Allocates memory for the temp path arrays, and sets the size var to 0 You can use calls to GetCoordArray(), GetVerbArray() and GetFlagArray() to get the alloced arrays. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::ReallocTempBuffers(UINT32 Size) { DeallocTempBuffers(); // Allocate the arrays m_TempArraySize = Size; m_pTempCoords = (DocCoord*) CCMalloc(Size*sizeof(DocCoord)); m_pTempVerbs = (PathVerb*) CCMalloc(Size*sizeof(PathVerb)); m_pTempFlags = (PathFlags*) CCMalloc(Size*sizeof(PathFlags)); // If any of the arrays are not allocated, dealloc the alloced ones, and return FALSE if (m_pTempCoords == NULL || m_pTempVerbs == NULL || m_pTempFlags == NULL) { DeallocTempBuffers(); return FALSE; } // It's all OK, so return TRUE return TRUE; } /******************************************************************************************** > void NodeBlender::DeallocTempBuffers() Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: - Outputs: - Returns: - Purpose: Releases memory allocated for the temp path arrays, and sets the size var to 0 SeeAlso: - ********************************************************************************************/ void NodeBlender::DeallocTempBuffers() { if (m_pTempCoords != NULL) { CCFree(m_pTempCoords); m_pTempCoords = NULL; } if (m_pTempVerbs != NULL) { CCFree(m_pTempVerbs); m_pTempVerbs = NULL; } if (m_pTempFlags != NULL) { CCFree(m_pTempFlags); m_pTempFlags = NULL; } m_TempArraySize = 0; if (m_pGBlendBuff != NULL) { CCFree(m_pGBlendBuff); m_pGBlendBuff = NULL; } m_GBlendBuffSize = 0; } /******************************************************************************************** > DocCoord* NodeBlender::GetCoordArray(UINT32 MinSize); Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: MinSize = min size the array should be Outputs: - Returns: Ptr to the array, or NULL if can't get memory Purpose: Used to get an array you can write to. SeeAlso: - ********************************************************************************************/ DocCoord* NodeBlender::GetCoordArray(UINT32 MinSize) { MinSize++; if (m_TempArraySize >= MinSize) return m_pTempCoords; if (ReallocTempBuffers(MinSize)) return m_pTempCoords; else return NULL; } /******************************************************************************************** > PathVerb* NodeBlender::GetVerbArray(UINT32 MinSize); Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: MinSize = min size the array should be Outputs: - Returns: Ptr to the array, or NULL if can't get memory Purpose: Used to get an array you can write to. SeeAlso: - ********************************************************************************************/ PathVerb* NodeBlender::GetVerbArray(UINT32 MinSize) { MinSize++; if (m_TempArraySize >= MinSize) return m_pTempVerbs; if (ReallocTempBuffers(MinSize)) return m_pTempVerbs; else return NULL; } /******************************************************************************************** > PathFlags* NodeBlender::GetFlagArray(UINT32 MinSize); Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: MinSize = min size the array should be Outputs: - Returns: Ptr to the array, or NULL if can't get memory Purpose: Used to get an array you can write to. SeeAlso: - ********************************************************************************************/ PathFlags* NodeBlender::GetFlagArray(UINT32 MinSize) { MinSize++; if (m_TempArraySize >= MinSize) return m_pTempFlags; if (ReallocTempBuffers(MinSize)) return m_pTempFlags; else return NULL; } /******************************************************************************************** > UINT32* NodeBlender::GetGBlendBuff(UINT32 MinSize); Author: Mark_Neves (Xara Group Ltd) Created: 7/11/94 Inputs: MinSize = min size the buffer should be Outputs: - Returns: Ptr to the buffer, or NULL if can't get memory Purpose: Used to get a buffer you can write to. SeeAlso: - ********************************************************************************************/ UINT32* NodeBlender::GetGBlendBuff(UINT32 MinSize) { MinSize++; if (m_GBlendBuffSize >= MinSize) return m_pGBlendBuff; if (m_pGBlendBuff != NULL) CCFree(m_pGBlendBuff); m_pGBlendBuff = (UINT32*) CCMalloc(MinSize*sizeof(UINT32)); if (m_pGBlendBuff != NULL) m_GBlendBuffSize = MinSize; else m_GBlendBuffSize = 0; return m_pGBlendBuff; } /******************************************************************************************** > BOOL NodeBlender::Remap(DocCoord PosStart,DocCoord PosEnd,DocCoord* pInvPosStart,DocCoord* pInvPosEnd) Author: Mark_Neves (Xara Group Ltd) Created: 10/11/94 Inputs: PosStart = Coord of element to remap start node to PosEnd = Coord of element to remap end node to pInvPosStart= ptr to place to put the coord to use to invert the start mapping pInvPosEnd = ptr to place to put the coord to use to invert the end mapping Outputs: - Returns: TRUE if remapped, FALSE otherwise Purpose: If this blender manages to remap a pair of blend paths, then this will return TRUE. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::Remap(DocCoord PosStart,DocCoord PosEnd,DocCoord* pInvPosStart,DocCoord* pInvPosEnd) { BOOL Mapped = FALSE; if (m_pRefStart != NULL && m_pRefEnd != NULL) { BlendPath* pBlendPathStart = m_pRefStart->GetFirstBlendPath(); BlendPath* pBlendPathEnd = m_pRefEnd ->GetFirstBlendPath(); if (pBlendPathStart != NULL && pBlendPathEnd != NULL) { if (GetNodeBlendPath() != NULL) { Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),360.0 - m_AngleStart); Trans2DMatrix* pRotateEnd = GetRotateMatrix(m_pRefEnd ->GetNode(),360.0 - m_AngleEnd ); if (pRotateStart) pRotateStart->Transform(&PosStart,1); if (pRotateEnd) pRotateEnd ->Transform(&PosEnd ,1); DELPTR(pRotateStart); DELPTR(pRotateEnd); } *pInvPosStart = pBlendPathStart->MapPath(PosStart); *pInvPosEnd = pBlendPathEnd ->MapPath(PosEnd); Mapped = TRUE; if (GetNodeBlendPath() != NULL) { Trans2DMatrix* pRotateStart = GetRotateMatrix(m_pRefStart->GetNode(),m_AngleStart); Trans2DMatrix* pRotateEnd = GetRotateMatrix(m_pRefEnd ->GetNode(),m_AngleEnd ); if (pRotateStart) pRotateStart->Transform(pInvPosStart,1); if (pRotateEnd) pRotateEnd ->Transform(pInvPosEnd ,1); DELPTR(pRotateStart); DELPTR(pRotateEnd); } } // Keep the path mapping index up to date (necessary for cut/copy/paste, export, etc). m_PathIndexStart = m_pRefStart->GetOrigMapping(); m_PathIndexEnd = m_pRefEnd ->GetOrigMapping(); } return Mapped; } /******************************************************************************************** > BOOL NodeBlender::ConvertAWPathIndexesToCamelot(INT32 *pPathIndexStart,INT32* pPathIndexEnd) Author: Mark_Neves (Xara Group Ltd) Created: 25/11/94 Inputs: pPathIndexStart = index of element in start node pPathIndexEnd = index of element in end node Outputs: *pPathIndexStart = possibly modified index for start node *pPathIndexEnd = possibly modified index for end node Returns: TRUE if ok, FALSE otherwise Purpose: This converts ArtWorks path indexes to a Camelot path indexes, for this blender. An AW index is just an index to the nth end point, so all that's done is the nth end point in the path is found. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::ConvertAWPathIndexesToCamelot(INT32 *pPathIndexStart,INT32* pPathIndexEnd) { BOOL ok = FindPathEndPoint(FindPrevious(),pPathIndexStart); if (ok) ok = FindPathEndPoint(FindNext(), pPathIndexEnd); return ok; } /******************************************************************************************** > BOOL NodeBlender::FindPathEndPoint(Node* pNodePath,INT32* pIndex) Author: Mark_Neves (Xara Group Ltd) Created: 25/11/94 Inputs: pNodePath = ptr to a NodePath pIndex = ptr to INT32 - must contain the number of the endpoint desired (N) Outputs: *pIndex = the index to the N'th endpoint. Returns: TRUE if ok, FALSE otherwise Purpose: This finds the N'th end point in the given NodePath. This is used to convert AW end point indexes into Camelot end point indexes. If *pIndex <=0 on entry, *pIndex = 0 on exit SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::FindPathEndPoint(Node* pNodePath,INT32* pIndex) { ERROR3IF(pIndex == NULL,"pIndex == NULL"); ERROR3IF(pNodePath == NULL,"pNodePath == NULL"); if (pIndex == NULL || pNodePath == NULL) return FALSE; // ERROR3IF(!pNodePath->IS_KIND_OF(NodePath),"pNodePath is not kind of NodePath"); if (!pNodePath->IS_KIND_OF(NodePath)) *pIndex = 0; // if *pIndex < 0 then this has a special meaning so preserve it and return TRUE // (e.g. -1 means find bottom left element) if (*pIndex < 0) return TRUE; Path* pPath = &(((NodePath*)pNodePath)->InkPath); INT32 Index=0; for (INT32 n=*pIndex;n>0;n--) { if (!pPath->FindNextEndPoint(&Index)) return FALSE; } *pIndex = Index; return TRUE; } //------------------------------------------------------------------------------- //------------------------------------------------------------------------------- //------------------------------------------------------------------------------- /*********************************************************************************************** > BlendRef::BlendRef() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: - Outputs: - Returns: - Purpose: Default constructor. ***********************************************************************************************/ BlendRef::BlendRef() { m_pNode = NULL; m_NumBlendPaths = 0; m_pBlendPathAttrMap = NULL; } /*********************************************************************************************** > BlendRef::~BlendRef() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: - Outputs: - Returns: - Purpose: Default destructor. ***********************************************************************************************/ BlendRef::~BlendRef() { m_BlendPathList.DeleteAll(); DELPTR(m_pBlendPathAttrMap); } /*********************************************************************************************** > BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pThisNode = ptr to the node in the tree this BlendRef refers to. Index = index into path of blob clicked on (-1 means ignore) pUndoOp = ptr to an undoable op so that initialisation can be done in an undoable way (can be NULL) pProgress = ptr to progress object for maintaining hour glass IgnoreEscape= TRUE if you don't want this process abortable via the Escape key pMatrix = transformation to apply to each blend path (can be NULL) Outputs: - Returns: TRUE if successfully initialises, FALSE otherwise Purpose: This inits the BlendRef object with the given node. ***********************************************************************************************/ /* BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape,Trans2DMatrix* pMatrix) { ERROR2IF(pThisNode == NULL,FALSE,"pThisNode == NULL"); ERROR2IF(!pThisNode->CanBecomeA(CC_RUNTIME_CLASS(NodePath)),FALSE,"pThisNode can't become a NodePath!"); // Reset the member vars of this blend reference m_pNode = pThisNode; m_NumBlendPaths = 0; m_BlendPathList.DeleteAll(); // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode. BlendBecomeA ParamBecomeA( BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), pUndoOp, this, Index, pProgress, IgnoreEscape, pMatrix); // Get pNode to generate its paths for us BOOL ok = m_pNode->DoBecomeA(&ParamBecomeA); if (!ok) { // tidy up if things go wrong m_pNode = NULL; m_NumBlendPaths = 0; m_BlendPathList.DeleteAll(); } return (ok); } */ /*********************************************************************************************** > BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress, BOOL IgnoreEscape,Trans2DMatrix* pMatrix, AttrBrushType* pAttrBrush) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pThisNode = ptr to the node in the tree this BlendRef refers to. Index = index into path of blob clicked on (-1 means ignore) pUndoOp = ptr to an undoable op so that initialisation can be done in an undoable way (can be NULL) pProgress = ptr to progress object for maintaining hour glass IgnoreEscape= TRUE if you don't want this process abortable via the Escape key pMatrix = transformation to apply to each blend path (can be NULL) pAttrBrush = the brush attribute applying to pThisNode, using this parameter indicates that you wish to extract paths from the brush. Outputs: - Returns: TRUE if successfully initialises, FALSE otherwise Purpose: This inits the BlendRef object with the given node. ***********************************************************************************************/ BOOL BlendRef::Initialise(NodeRenderableInk* pThisNode,INT32 Index,UndoableOperation* pUndoOp,Progress* pProgress,BOOL IgnoreEscape, Trans2DMatrix* pMatrix, AttrBrushType* pAttrBrush) { ERROR2IF(pThisNode == NULL,FALSE,"pThisNode == NULL"); // Reset the member vars of this blend reference m_pNode = pThisNode; m_NumBlendPaths = 0; m_BlendPathList.DeleteAll(); // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode. BlendBecomeA ParamBecomeA( BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), pUndoOp, this, Index, pProgress, IgnoreEscape, pMatrix); ERROR2IF(!pThisNode->CanBecomeA(&ParamBecomeA), FALSE, "pThisNode can't become a NodePath!"); BOOL ok = FALSE; if (pAttrBrush != NULL) { // check to see if it is default, if it is not then let the attribute do the work if (pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush) ok = pAttrBrush->DoBecomeA(&ParamBecomeA, pThisNode); else ok = m_pNode->DoBecomeA(&ParamBecomeA); } else // Get pNode to generate its paths for us ok = m_pNode->DoBecomeA(&ParamBecomeA); if (!ok) { // tidy up if things go wrong m_pNode = NULL; m_NumBlendPaths = 0; m_BlendPathList.DeleteAll(); } return (ok); } /*********************************************************************************************** > BOOL BlendRef::InitialiseForBrush(NodeRenderableInk* pThisNode) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pThisNode = ptr to the node in the tree this BlendRef refers to. Outputs: - Returns: TRUE if successfully initialises, FALSE otherwise Purpose: This inits the BlendRef object with the given node. ***********************************************************************************************/ BOOL BlendRef::InitialiseForBrush(NodeRenderableInk* pThisNode) { ERROR2IF(pThisNode == NULL,FALSE,"pThisNode == NULL"); // Reset the member vars of this blend reference m_pNode = pThisNode; m_NumBlendPaths = 0; m_BlendPathList.DeleteAll(); // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode. BrushBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL, this); ERROR2IF(!pThisNode->CanBecomeA(&MyBecomeA), FALSE, "pThisNode can't become a NodePath!"); // if we've got a stroke attribute applied then we must make a special case /* AttrStrokeType* pStroke = NULL; AttrVariableWidth* pVarWidth = NULL; pThisNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke); pThisNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth); BOOL ok = FALSE; if (pStroke && pVarWidth && pVarWidth->HasActiveValueFunction()) ok = pStroke->DoBecomeA(&BecomeA, m_pNode); else */ BOOL ok = m_pNode->DoBecomeA(&MyBecomeA); if (!ok) { // tidy up if things go wrong m_pNode = NULL; m_NumBlendPaths = 0; m_BlendPathList.DeleteAll(); } return (ok); } /*********************************************************************************************** > BOOL BlendRef::AddBlendPath(BlendPath* pBlendPath) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pBlendPath = ptr to blend path to add Outputs: - Returns: TRUE if successfully initialises, FALSE otherwise Purpose: Add the blend path object to this blend ref NOTE: Once you add a BlendPath to a BlendRef, it becomes property of the BlendRef which can alter/delete it at any time. DO NOT KEEP A COPY OF THE PTR pBlendPath!!!! It may not be there next time use it. ***********************************************************************************************/ BOOL BlendRef::AddBlendPath(BlendPath* pBlendPath) { m_BlendPathList.AddTail(pBlendPath); m_NumBlendPaths++; return TRUE; } /*********************************************************************************************** > BlendPath* BlendRef::GetFirstBlendPath() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: - Outputs: - Returns: First blend path in the list, or NULL if the list is empty Purpose: Lets you get the first blend path in the list ***********************************************************************************************/ BlendPath* BlendRef::GetFirstBlendPath() { return ((BlendPath*)m_BlendPathList.GetHead()); } /*********************************************************************************************** > BlendPath* BlendRef::GetNextBlendPath(BlendPath* pCurrentBlendPath) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pCurrentBlendPath = ptr the current blend path in the list Outputs: - Returns: Returns the next blend path after pCurrentBlendPath, or NULL if there isn't a next one Purpose: Lets you get the next blend path in the list ***********************************************************************************************/ BlendPath* BlendRef::GetNextBlendPath(BlendPath* pCurrentBlendPath) { return ((BlendPath*)m_BlendPathList.GetNext(pCurrentBlendPath)); } /*********************************************************************************************** > BlendPath* BlendRef::GetBlendPath(INT32 Index) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: Index = index of the list item (0 is the first item) Outputs: - Returns: Returns the blend path who's index is Index, or NULL if there isn't a next one Purpose: Lets you get the nth blend path in the list SeeAlso: GetNumBlendPaths(); ***********************************************************************************************/ BlendPath* BlendRef::GetBlendPath(INT32 Index) { if (m_NumBlendPaths <= 0) return NULL; if (Index >= INT32(m_NumBlendPaths)) { ERROR3("Index too big"); Index = m_NumBlendPaths-1; } if (Index < 0) { ERROR3("Index is -ve "); Index = 0; } BlendPath* pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL && Index > 0) { pBlendPath = GetNextBlendPath(pBlendPath); Index--; } ERROR3IF(pBlendPath==NULL,"pBlendPath == NULL"); return (pBlendPath); } /******************************************************************************************** > void BlendRef::Transform( TransformBase& Trans ) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: Trans - The transform Object Purpose: Transforms all the BlendPaths attached to this blend reference ********************************************************************************************/ void BlendRef::Transform( TransformBase& Trans ) { BlendPath* pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL) { pBlendPath->Transform(Trans); pBlendPath = GetNextBlendPath(pBlendPath); } } /******************************************************************************************** > void BlendRef::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix) Author: Mark_Neves (Xara Group Ltd) Created: 9/11/94 Inputs: pRender = region to render blobs into. pMatrix = matrix to apply to the blob coords Outputs: - Returns: - Purpose: Renders all the blend blobs associated with all the blend paths in this blend ref ********************************************************************************************/ void BlendRef::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix) { BlendPath* pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL) { pBlendPath->RenderBlendBlobs(pRender,pMatrix); pBlendPath = GetNextBlendPath(pBlendPath); } } /******************************************************************************************** > BOOL BlendRef::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,Trans2DMatrix* pMatrix); Author: Mark_Neves (Xara Group Ltd) Created: 10/11/94 Inputs: pPointerPos = point to check against ppBlendPath = ptr to place to store blend path ptr if a match is found pIndex = ptr to place to store index if match is found pMatrix = matrix to apply to the path points before testing with pointer pos (can be NULL) Outputs: *ppBlendPath & *pIndex, updated if TRUE returned, undefined otherwise Returns: TRUE if match found, FALSE otherwise Purpose: This sees if the point given lies on a selected blob. If a match is found, the ptr to the blend path and index to blob element is returned. Markn 26-5-99: Modified so that if the function returns TRUE, *pPointerPos is updated so that it contains the coords of the blob it is over SeeAlso: - ********************************************************************************************/ BOOL BlendRef::IsPointOverBlob(DocCoord* pPointerPos,BlendPath** ppBlendPath,INT32* pIndex,Trans2DMatrix* pMatrix) { if (pPointerPos == NULL) return FALSE; if (GetNumBlendPaths() != 1) return FALSE; BOOL Found = FALSE; BlendPath* pBlendPath = GetFirstBlendPath(); if (pBlendPath != NULL) { Found = pBlendPath->IsPointOverBlob(pPointerPos,pIndex,pMatrix); if (Found) *ppBlendPath = pBlendPath; } return Found; } /******************************************************************************************** > INT32 BlendRef::GetOrigMapping() Author: Mark_Neves (Xara Group Ltd) Created: 11/11/94 Inputs: - Outputs: - Returns: The mapping that should be applied to the original path Purpose: The path in question could well have been remapping (i.e. rotated to change the el at index 0) many times. This func returns the mapping (i.e. the index of the el that should be at index 0) that should be appied to the original path in order to get the same result. If the ref has more than one blend path, -1 is returned, meaning choose a default element as the start node (e.g. left most, bottom most element) SeeAlso: - ********************************************************************************************/ INT32 BlendRef::GetOrigMapping() { INT32 Mapping = -1; if (GetNumBlendPaths() == 1) { BlendPath* pBlendPath = GetFirstBlendPath(); if (pBlendPath != NULL) Mapping = pBlendPath->GetOrigMapping(); } return Mapping; } /******************************************************************************************** > void BlendRef::PreBlend() Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: - Outputs: - Returns: - Purpose: Called just before a blender node is about to do its blend. It asks all its blend paths to find their applied attributes. ********************************************************************************************/ void BlendRef::PreBlend() { // If we haven't got a blend path attr map yet, make one (don't worry if we fail) if (m_pBlendPathAttrMap == NULL && m_NumBlendPaths > 0) PORTNOTE("other","Maps aren't hash bashed, so have no initial size") m_pBlendPathAttrMap = new CMapPtrToPtr(); // (m_NumBlendPaths); } /******************************************************************************************** > void BlendRef::PostBlend() Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: - Outputs: - Returns: - Purpose: Called just after a blender node has done its blend. It asks all its blend paths to free their applied attributes. ********************************************************************************************/ void BlendRef::PostBlend() { // Free up any memory used to hold applied attribute tables in the blend paths BlendPath* pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL) { pBlendPath->FreeAppliedAttributes(); pBlendPath = GetNextBlendPath(pBlendPath); } // Remove all the items in the map of blend path attr maps if (m_pBlendPathAttrMap != NULL) m_pBlendPathAttrMap->clear(); } /******************************************************************************************** > CCAttrMap* BlendRef::FindAppliedAttributes(BlendPath* pBlendPath) Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: pBlendPath = the blend path that has the attrs you want Outputs: - Returns: - Purpose: Use this to get the attrs of a blend path that belongs to this blend reference. It keeps its own map keyed by the node which created the blend path, so that FindAppliedAttributes() is only called once for each node that created blend paths, rather than once for every blend path produced by the node (potentially many). ********************************************************************************************/ CCAttrMap* BlendRef::FindAppliedAttributes(BlendPath* pBlendPath) { // If the attrs applied to this blend path are unique to the blend path (i.e. not the same // as the attrs applid to the node that created the blend path) return the unique attrs if (pBlendPath->UniqueAppliedAttrs()) return (pBlendPath->FindAppliedAttributes()); // If we haven't got a map of attr maps yet, try the blend path directly if (m_pBlendPathAttrMap == NULL) return (pBlendPath->FindAppliedAttributes()); // pAttrMap will hold the attrs applied to pBlendPath CCAttrMap* pAttrMap = NULL; // the map of attr maps is keyed by the ptr to the node that created the blend path NodeRenderableInk* pCreatedByNode = pBlendPath->GetCreatedByNode(); if (pCreatedByNode->IsCompound()) { pCreatedByNode = ((NodeCompound *)pCreatedByNode)->GetNodeToBlend(); } if (pCreatedByNode != NULL) { // Do we have an attr map for pCreatedByNode? CMapPtrToPtr::iterator iter = m_pBlendPathAttrMap->find( pCreatedByNode ); if( m_pBlendPathAttrMap->end() != iter ) pAttrMap = (CCAttrMap *)iter->second; else { // If not, get the blend path to return us an attr map // and put the ptr in our map of attr maps, keyed by pCreatedByNode. pAttrMap = pBlendPath->FindAppliedAttributes(); if (pAttrMap != NULL) (*m_pBlendPathAttrMap)[pCreatedByNode] = pAttrMap; } } return pAttrMap; } /******************************************************************************************** > DocRect BlendRef::GetBoundingRect() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 24/11/99 Inputs: - Outputs: - Returns: the bounding rect of all the blendpaths in the blendref Purpose: ********************************************************************************************/ DocRect BlendRef::GetBoundingRect() { DocRect Rect; BlendPath *pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL) { DocRect PathRect = pBlendPath->m_pPath->GetBoundingRect(); Rect = Rect.Union(PathRect); pBlendPath = GetNextBlendPath(pBlendPath); } return Rect; } /******************************************************************************************** > void BlendRef::StripRedundantNodeBlendPaths (BlendRef* spouse) Author: Chris_Snook (Xara Group Ltd) Created: 25/7/2000 Inputs: - Outputs: - Returns: - Purpose: Strips redundant NodeBlendPaths from the blend refs. This is needed in the case that we are required to blend a blend on a both to a non-blend on a path (and vice versa). ********************************************************************************************/ void BlendRef::StripRedundantNodeBlendPaths (BlendRef* spouse) { INT32 numbBlendPathInited = 0, spouseNumbBlendPathInited = 0; BlendPath *pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL) { if (pBlendPath->GetCreatedViaNodeBlendPath ()) { numbBlendPathInited++; } pBlendPath = GetNextBlendPath(pBlendPath); } pBlendPath = spouse->GetFirstBlendPath(); while (pBlendPath != NULL) { if (pBlendPath->GetCreatedViaNodeBlendPath ()) { spouseNumbBlendPathInited++; } pBlendPath = spouse->GetNextBlendPath(pBlendPath); } // only strip in this case .... if (numbBlendPathInited != spouseNumbBlendPathInited) { if ((numbBlendPathInited == 0) && (spouseNumbBlendPathInited > 0)) { // strip our spouse .... pBlendPath = spouse->GetFirstBlendPath(); while (pBlendPath != NULL) { if (pBlendPath->GetCreatedViaNodeBlendPath ()) { BlendPath* remover = pBlendPath; pBlendPath = spouse->GetNextBlendPath(pBlendPath); delete (spouse->m_BlendPathList.RemoveItem (remover)); spouse->m_NumBlendPaths--; } else { pBlendPath = spouse->GetNextBlendPath(pBlendPath); } } } else if ((spouseNumbBlendPathInited == 0) && (numbBlendPathInited > 0)) { // strip ourselves .... pBlendPath = GetFirstBlendPath(); while (pBlendPath != NULL) { if (pBlendPath->GetCreatedViaNodeBlendPath ()) { BlendPath* remover = pBlendPath; pBlendPath = GetNextBlendPath(pBlendPath); delete (m_BlendPathList.RemoveItem (remover)); m_NumBlendPaths--; } else { pBlendPath = GetNextBlendPath(pBlendPath); } } } // else - oh well } } /******************************************************************************************** > BOOL BlendRef::RemoveLastBlendPath() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 24/11/99 Inputs: - Outputs: - Returns: TRUE if successful, FALSE if not Purpose: Not a function that most people will have cause to use, removes the tail item in the blendpath list. ********************************************************************************************/ BOOL BlendRef::RemoveLastBlendPath() { if (m_BlendPathList.IsEmpty()) return FALSE; m_BlendPathList.RemoveTail(); m_NumBlendPaths--; return TRUE; } //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- /*********************************************************************************************** > BlendPath::BlendPath() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: - Outputs: - Returns: - Purpose: Default constructor ***********************************************************************************************/ BlendPath::BlendPath() { m_pBlendNode = NULL; m_pPath = NULL; m_pCreatedByNode= NULL; m_pAppliedAttrs = NULL; m_CloseFigure = 0; m_OrigMapping = 0; m_PathType = PATHTYPE_NONE; m_SubPathID = 0; m_DirChanged = FALSE; m_NumElements = -1; m_UniqueAttrs = FALSE; m_bDeleteAll = FALSE; m_bFreeAttrMapOnDestruct = FALSE; m_AppliedLineWidth = -1; m_bCreatedViaNodeBlendPath = FALSE; m_pCopyPath = NULL; m_pCopyAttrs = NULL; } /*********************************************************************************************** > BlendPath::~BlendPath() Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: - Outputs: - Returns: - Purpose: Default destructor Notes: This destructor has actually gotten quite complicated as it Blendpaths are an important part of the brush attribute and there are a number of different cases to deal with: - The normal case is where the blendpath is part of a blend, in this case it may wish to delete its applied attributes, but more than often will not, as they are still used in the tree. The brush has two deletion scenarios: - normally the m_bFreeAttributes flag is set, as we wish to ensure the attribute map is deleted but we do not wish to delete the attributes (as they are probably shared) - if this blendpath has been created specially as part of a blend between two brush attributes, then the m_bDeleteAll flag will be set, which asks that we delete everything ***********************************************************************************************/ BlendPath::~BlendPath() { // Delete our copies DeleteCopyPathAndAttributes(); // Only delete the applied attr map if this is the first sub path that was generated from the // source path supplied when making shapes. // This assumes that all sub-paths within a given path have the same attributes (far assumption) if (m_SubPathNum == 0 || m_bDeleteAll) { if ((m_UniqueAttrs || m_bDeleteAll) && m_pAppliedAttrs != NULL) { CCRuntimeClass *pType; void *pVal; // delete all attrs CMapPtrToPtr::iterator Pos = m_pAppliedAttrs->GetStartPosition(); CMapPtrToPtr::iterator End = m_pAppliedAttrs->GetEndPosition(); for(; Pos != End;) { // Get attr at CMapPtrToPtr::iterator Pos m_pAppliedAttrs->GetNextAssoc( Pos, pType, pVal ); if (pVal != NULL) delete (NodeAttribute*)pVal; } } if (m_pAppliedAttrs != NULL) { delete m_pAppliedAttrs; m_pAppliedAttrs = NULL; } } // m_bFreeAttributes should only really be set by brushing code, so unless you are a // brush you should never enter the following section if (m_bFreeAttrMapOnDestruct) { if (m_pAppliedAttrs != NULL) { delete m_pAppliedAttrs; m_pAppliedAttrs = NULL; } } if (m_pBlendNode) { m_pBlendNode->CascadeDelete(); delete m_pBlendNode; } if (m_bDeleteAll) { if (m_pPath != NULL) delete m_pPath; } } /*********************************************************************************************** > BOOL BlendPath::Initialise( NodeRenderableInk* pThisBlendNode, INT32 Index, NodeRenderableInk* pThisCreatedByNode, UINT32 ThisSubPathID, CCAttrMap* pAttrMap) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: pThisBlendNode = ptr to the node that this BlendPath will use (should NOT be linked in the tree) Index = index to path element the path should start on (-1 means bottom left most el) pThisCreatedByNode = ptr to the node that created pThisNodePath ThisSubPathID = ID for this subpath (all blend paths generated from the same path have the same subpath ID). Must be >=1 ThisSubPathNum = The sub path number specifying which sub path of the original path this represents. The first sub path is numbered 0. pAttrMap = ptr to applied attrs (or NULL if to use attrs applied to pThisCreatedByNode) Outputs: - Returns: TRUE if Successful, FALSE otherwise Purpose: Inits the BlendPath object to use the given NodePath. NOTE: pThisNodePath should point to isolated instance, and not to a used NodePath linked into the tree! NOTE: Once you give a BlendPath a NodePath, this becomes property of the BlendPath and can be altered and/or deleted by the BlendPath at any time. DO NOT KEEP A COPY OF THE PTR pThisNodePath!!! If you want a copy of the NodePath at pThisNodePath, make a new copy of the node. ***********************************************************************************************/ BOOL BlendPath::Initialise( NodeRenderableInk* pThisBlendNode, INT32 Index, NodeRenderableInk* pThisCreatedByNode, UINT32 ThisSubPathID, UINT32 ThisSubPathNum, CCAttrMap* pAttrMap, BOOL CheckDirection) { if (pThisBlendNode == NULL || pThisCreatedByNode == NULL) return FALSE; if (pThisBlendNode->IsNodePath()) ERROR3IF(ThisSubPathID == 0,"Invalid subpath ID"); DELPTR(m_pBlendNode); m_pBlendNode = pThisBlendNode; if (m_pBlendNode->IsNodePath()) { m_pPath = &(((NodePath *)m_pBlendNode)->InkPath); } else { // get the node to blend out of this path node & use this if (pThisBlendNode->IsCompound()) { NodeRenderableInk * pInk = ((NodeCompound *)pThisBlendNode)->GetNodeToBlend(); if (pInk) { if (pInk->IsNodePath()) { m_pPath = &(((NodePath *)pInk)->InkPath); } else { ERROR3("Node to blend isn't a node path"); } } else { ERROR3("No node to blend"); } } } m_pCreatedByNode= pThisCreatedByNode; m_SubPathID = ThisSubPathID; m_SubPathNum = ThisSubPathNum; m_pAppliedAttrs = pAttrMap; m_UniqueAttrs = (pAttrMap != NULL); if (m_pPath) { m_NumElements = m_pPath->GetNumElements(); m_PathType = m_pPath->GetPathType(); } else { m_NumElements = 0; m_PathType = PATHTYPE_SHAPE; } // If we don't have an start element index (i.e. == -1) and its a shape (not a line) // then pick the bottom left element if (m_pPath && CheckDirection) { if (Index < 0 && m_PathType == PATHTYPE_SHAPE) Index = FindBottomLeft(); ProcessVerbs(); MapPath(Index); CorrectDirection(Index); } // record the initial CMapPtrToPtr::iterator (used for loading and saving brushes so that we don't get // the control coordinates of fill attributes wrong) DocRect BRect; if (m_pPath) { BRect = m_pPath->GetBoundingRect(); } else { BRect = m_pBlendNode->GetBoundingRect(FALSE, FALSE); } m_BrushStartCoord = BRect.Centre(); return TRUE; } /*********************************************************************************************** > BOOL BlendPath::Initialise( BlendPath *pBlendPath, NodeRenderableInk * pNodeToBlend) Author: David_McClarnon (Xara Group Ltd) Created: 6/3/2000 Inputs: pBlendPath - the blend path to copy from Outputs: - Returns: TRUE if Successful, FALSE otherwise Purpose: Inits this blend path, replacing the blend node with the one given ***********************************************************************************************/ BOOL BlendPath::Initialise( BlendPath *pBlendPath, NodeRenderableInk * pNodeToBlend) { m_pPath = pBlendPath->GetPath(); pNodeToBlend->NodeCopy((Node **)(&m_pBlendNode)); m_pCreatedByNode = pBlendPath->GetCreatedByNode(); m_OrigMapping = pBlendPath->GetOrigMapping(); m_PathType = pBlendPath->GetPathType(); m_SubPathID = pBlendPath->GetSubPathID(); m_SubPathNum = pBlendPath->GetSubPathNum(); m_NumElements = pBlendPath->GetNumElements(); m_UniqueAttrs = pBlendPath->UniqueAppliedAttrs(); m_DirChanged = pBlendPath->m_DirChanged; m_BrushStartCoord = pBlendPath->m_BrushStartCoord; m_BrushCurrentCoord = pBlendPath->m_BrushCurrentCoord; if (m_pAppliedAttrs) m_pAppliedAttrs = pBlendPath->m_pAppliedAttrs->Copy(); else m_pAppliedAttrs = NULL; return TRUE; } /******************************************************************************************** > void BlendPath::CorrectDirection(INT32 Index) Author: Mark_Neves (Xara Group Ltd) Created: 9/11/94 Inputs: Index = index of the node the blend was attempted from Outputs: pPath potentially reversed so it is rendered in the correct direction Returns: - Purpose: This ensures that the path is going in the correct direction. It doesn't matter which direction, as long as the direction is the same for all blend paths. ********************************************************************************************/ void BlendPath::CorrectDirection(INT32 Index) { switch (m_PathType) { case PATHTYPE_SHAPE: { // Dummy vars for the GWinding() call UINT32 l; POINT p; // Get the direction of the path BOOL Direction = GWinding((PPOINT)m_pPath->GetCoordArray(),m_pPath->GetVerbArray(),m_pPath->GetNumCoords(),FLATNESS,l,p); // If clockwise, reverse it so it's anticlockwise (this is ArtWorks compatible!!) if (Direction) m_pPath->Reverse(); m_DirChanged = Direction; } break; case PATHTYPE_LINE: { // Get num coords INT32 NumCoords = m_pPath->GetNumCoords(); if (Index != 0 && Index != (NumCoords-1)) { // Get the coords array of the line DocCoord* pCoords = m_pPath->GetCoordArray(); // Find the coords of the two end points of the line DocCoord Coord1 = pCoords[0]; DocCoord Coord2 = pCoords[NumCoords-1]; // if the start of the line is less bottom-left than the end, reverse the line. if ((Coord1.x + Coord1.y) > (Coord2.x + Coord2.y)) { m_pPath->Reverse(); m_OrigMapping = NumCoords-1; // Pretend that user mapped to end of line } } } break; default : ERROR3("Unknown path type"); break; } } /******************************************************************************************** > INT32 BlendPath::MapPath(INT32 Index) Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: Index = index of element you want the path to start from Outputs: pPath potentially rotated so it's first el is the one that was indexed by Index OrigMapping will be the mapping that should be applied to the original path to get the same effect. Returns: The mapping to apply to invert this mapping Purpose: This will ensure that the path's first element is the element at Index. It does this by rotating the path until element at index 'Index' is at index 0. The value returned can be used to undo the mapping, i.e. to apply an inverse mapping ********************************************************************************************/ INT32 BlendPath::MapPath(INT32 Index) { INT32 NumCoords = m_pPath->GetNumCoords(); INT32 InverseMapping = 0; // Make sure Index is in range if (Index > 0 && Index < NumCoords) { // Check that we're trying to remap an end point PathFlags* pFlags = m_pPath->GetFlagArray(); PathVerb* pVerbs = m_pPath->GetVerbArray(); DocCoord* pCoords = m_pPath->GetCoordArray(); ERROR3IF(!pFlags[Index].IsEndPoint,"Index not on end point"); if (!pFlags[Index].IsEndPoint) { // If index is not on an end point, choose the index of the end point that // appears before this index. INT32 OldIndex = Index; INT32 PrevIndex = 0; while (PrevIndex < OldIndex) { Index = PrevIndex; if (!m_pPath->FindNextEndPoint(&PrevIndex)) { Index = 0; break; } } } switch (m_PathType) { case PATHTYPE_SHAPE: if (m_DirChanged) { DocCoord Pos = pCoords[Index]; m_pPath->Reverse(); m_pPath->FindNearestPoint(Pos,POINTFLAG_ENDPOINTS,&Index); } // Change the path so the el at index 'Index' is now at index 0 m_pPath->ChangeStartElement(Index); // The above call will introduce a PT_CLOSEFIGURE flag into the end verb, so // we need to clear this flag from the last verb pVerbs[NumCoords-1] &= ~PT_CLOSEFIGURE; // Work out a mapping that can be applied to the original path in order to get the same effect // This is used when the blender reinitialises after its been deinitialised m_OrigMapping += Index; if (m_OrigMapping >= (NumCoords-1)) m_OrigMapping = (m_OrigMapping+1) % NumCoords; InverseMapping = NumCoords-Index-1; if (m_DirChanged) { DocCoord Pos = pCoords[InverseMapping]; m_pPath->Reverse(); m_pPath->FindNearestPoint(Pos,POINTFLAG_ENDPOINTS,&InverseMapping); } break; case PATHTYPE_LINE: // If it's a line then we will only try and remap if the index is the end element // In this case, this just means reversing the path if (Index == (NumCoords-1)) { // Make the end of the line the start of the line m_pPath->Reverse(); // The mapping to apply to the orig path to get the same effect if the opposite end point index if (m_OrigMapping == 0) m_OrigMapping = NumCoords-1; else m_OrigMapping = 0; InverseMapping = NumCoords-1; } break; default : ERROR3("Unknown path type"); break; } } return InverseMapping; } /******************************************************************************************** > DocCoord BlendPath::MapPath(DocCoord Pos) Author: Mark_Neves (Xara Group Ltd) Created: 23/2/95 Inputs: Pos = coord of element you want the path to start from Outputs: pPath potentially rotated so it's first el is the one that was closest to 'Pos' OrigMapping will be the mapping that should be applied to the original path to get the same effect. Returns: The mapping to apply to invert this mapping Purpose: This will ensure that the path's first element is the element closest to Pos. It does this by rotating the path until element at Pos is at index 0. The value returned can be used to undo the mapping, i.e. to apply an inverse mapping ********************************************************************************************/ DocCoord BlendPath::MapPath(DocCoord Pos) { DocCoord InversePos(0,0); ERROR2IF(m_pPath == NULL,InversePos,"m_pPath == NULL"); DocCoord* pCoords = m_pPath->GetCoordArray(); ERROR2IF(pCoords == NULL,InversePos,"pCoords == NULL"); // The inverse of this op is to remap the element currently at index 0 InversePos = pCoords[0]; INT32 Index = 0; INT32 InvIndex = 0; /* { StringBase Str; if (Str.Alloc(50000)) { Str = ""; pNodePath->GetDebugDetails(&Str); TRACE((TCHAR*)Str); } } */ if (m_pPath->FindNearestPoint(Pos,POINTFLAG_ENDPOINTS,&Index)) InvIndex = MapPath(Index); InversePos = pCoords[InvIndex]; /* { StringBase Str; if (Str.Alloc(50000)) { Str = ""; pNodePath->GetDebugDetails(&Str); TRACE((TCHAR*)Str); } } */ return InversePos; } /******************************************************************************************** > INT32 BlendPath::FindBottomLeft() Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: - Outputs: - Returns: The index of the bottom left most element (or -1 if something when wrong) Purpose: This scans the path for the bottom left most element, and returns the index to it ********************************************************************************************/ INT32 BlendPath::FindBottomLeft() { DocCoord* pCoords = m_pPath->GetCoordArray(); if (pCoords == NULL) return -1; INT32 BottomLeftIndex = -1; INT32 Index; BOOL ok; DocCoord VeryMostBottomLeft(0x7fffffff,0x7fffffff); double SmallestDist = 1.0e100,ThisDist; for (Index=0,ok=TRUE;ok;) { if (pCoords[Index].x < VeryMostBottomLeft.x) VeryMostBottomLeft.x = pCoords[Index].x; if (pCoords[Index].y < VeryMostBottomLeft.y) VeryMostBottomLeft.y = pCoords[Index].y; ok = m_pPath->FindNextEndPoint(&Index); } for (Index=0,ok=TRUE;ok;) { ThisDist = VeryMostBottomLeft.Distance(pCoords[Index]); if (ThisDist < SmallestDist) { BottomLeftIndex = Index; SmallestDist = ThisDist; } ok = m_pPath->FindNextEndPoint(&Index); } // return BottomLeftIndex; UINT32 u; POINT p; // Get the direction of the path GWinding((PPOINT)m_pPath->GetCoordArray(),m_pPath->GetVerbArray(),m_pPath->GetNumCoords(),FLATNESS,u,p); // TRACEUSER( "Markn", _T("\n")); INT32 GIndex=-1; DocCoord Coord(p.x,p.y); SmallestDist = 1.0e100; for (Index=0,ok=TRUE;ok;) { ThisDist = Coord.Distance(pCoords[Index]); if ((ThisDist-SmallestDist) < 5.0) { // This point is close enough, so remember it's index GIndex = Index; // Only update the smallest dist if it is indeed the smallest so far if (ThisDist < SmallestDist) SmallestDist = ThisDist; } //TRACEUSER( "Markn", _T("(%d) %f - "),Index,ThisDist); ok = m_pPath->FindNextEndPoint(&Index); } // TRACEUSER( "Markn", _T("\n")); // TRACEUSER( "Markn", _T("GIndex = %ld\n"),GIndex); // TRACEUSER( "Markn", _T("BottomLeftIndex = %ld\n"),BottomLeftIndex); // if (GIndex != BottomLeftIndex) // { // TRACEUSER( "Markn", _T("*^*^*^*^*^*^*^*^*^*^*^*^*^*^")); // } // ERROR3IF_PF(GIndex != BottomLeftIndex,("GIndex != BottomLeftIndex: GIndex = %ld, BottomLeftIndex = %ld",GIndex,BottomLeftIndex)); return GIndex; // return BottomLeftIndex; } /******************************************************************************************** > void BlendPath::ProcessVerbs() Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: - Outputs: Contents of pPath's verb array potentially changed CloseFigure = PT_CLOSEFIGURE if one or more PT_CLOSEFIGURE flags are found in the verbs 0 otherwise Returns: - Purpose: This processes the verbs so the GBlend can cope with them. It clears any PT_CLOSEFIGURE flags it finds. If it finds one or more PT_CLOSEFIGURE, CloseFigure = PT_CLOSEFIGURE ********************************************************************************************/ void BlendPath::ProcessVerbs() { PathVerb* pPathVerb = m_pPath->GetVerbArray(); INT32 n = m_pPath->GetNumCoords()-1; m_CloseFigure = 0; for (;n>=0;n--) { // Get the next verb PathVerb Verb = pPathVerb[n]; // Have we found a close figure flag yet? if (m_CloseFigure == 0) m_CloseFigure = Verb & PT_CLOSEFIGURE; // Remove any wierd flags from the verb, as GBlend doesn't like them. pPathVerb[n] = Verb & ~PT_CLOSEFIGURE; } } /******************************************************************************************** > void BlendPath::Transform( TransformBase& Trans ) Author: Mark_Neves (Xara Group Ltd) Created: 12/10/94 Inputs: Trans - The transform Object Purpose: Transforms the NodePath attached to this blend path, plus any applied attrs it may have ********************************************************************************************/ void BlendPath::Transform( TransformBase& Trans ) { if (m_pBlendNode != NULL) m_pBlendNode->Transform(Trans); if (m_UniqueAttrs && m_pAppliedAttrs != NULL) { CCRuntimeClass *pType; void *pVal; // iterating all (key, value) pairs for( CMapPtrToPtr::iterator Pos = m_pAppliedAttrs->GetStartPosition(); Pos != m_pAppliedAttrs->GetEndPosition();) { // Get attr at CMapPtrToPtr::iterator Pos m_pAppliedAttrs->GetNextAssoc(Pos,pType,pVal); // pVal is actually an attribute, so get a ptr to it and render it NodeAttribute* pNodeAttr = (NodeAttribute *)pVal; pNodeAttr->Transform(Trans); } } } /******************************************************************************************** > void BlendPath::Transform( Matrix* pTransMatrix,BOOL TransformLines, BOOL Tile) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 12/1/2000 Inputs: TransMatrix - the matrix describing the transformation TransformLineWidth - do we transform the line width Tile - flag to say whether to tile or not. If set this means that attributes such as bitmap fills etc. are only rendered once Purpose: Transforms the NodePath attached to this blend path, plus any applied attrs it may have Plus it also records transforms in the member variable, so that later they may be undone This is specifically required by brush attributes. ********************************************************************************************/ void BlendPath::Transform( Matrix* pTransMatrix, BOOL TransformLines, BOOL Tile ) { Trans2DMatrix Trans(*pTransMatrix); if (m_pBlendNode != NULL) m_pBlendNode->Transform(Trans); else { // added this for the case where we create the blendpath manually, i.e. when blending // brush attributes DocCoord* pCoords = m_pPath->GetCoordArray(); INT32 NumCoords = m_pPath->GetNumCoords(); if (pCoords != NULL && NumCoords > 0) Trans.Transform(pCoords, NumCoords); else ERROR3("No coordinates in BlendPath::Transform"); } CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap != NULL) { if (Tile) pAttrMap->TransformForBrush(Trans); else pAttrMap->Transform(Trans); } else { ERROR3("No applied attributes"); } return; } /******************************************************************************************** > BOOL BlendPath::TransformBrushAttributesBeforeSave() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 12/1/2000 Inputs: - Returns: TRUE if successful, FALSE otherwise Purpose: Undoes the transformations performed on the attributes of this blendpath. This function must be called prior to saving a brush attribute. If you wish to keep using the attribute after the save call TransformBrushAttributesAfterSave or else your attributes will be all over the place. ********************************************************************************************/ BOOL BlendPath::TransformBrushAttributesBeforeSave() { if (m_pPath == NULL) { ERROR2(FALSE, "This blendpath has no path"); return FALSE; } DocRect BRect = m_pPath->GetBoundingRect(); m_BrushCurrentCoord = BRect.Centre(); Trans2DMatrix Trans(m_BrushStartCoord.x - m_BrushCurrentCoord.x, m_BrushStartCoord.y - m_BrushCurrentCoord.y); CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap == NULL) { ERROR3("No applied attributes"); return FALSE; } // quick pre-release bodge fix - fractal fills seem to be getting wrongly transformed // when we do this, so exclude them AttrFractalFill* pFrac = NULL; if( pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrFractalFill), (void*&)pFrac ) ) pAttrMap->RemoveKey( CC_RUNTIME_CLASS(AttrFractalFill) ); pAttrMap->Transform(Trans); return TRUE; } /******************************************************************************************** > void BlendPath::TransformBrushAttributesAfterSave() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 12/1/2000 Inputs: - Returns: TRUE if successful , FALSE otherwise Purpose: Undoes the transformations performed on the attributes by the previous function. ********************************************************************************************/ BOOL BlendPath::TransformBrushAttributesAfterSave() { Trans2DMatrix Trans(m_BrushCurrentCoord.x - m_BrushStartCoord.x, m_BrushCurrentCoord.y - m_BrushStartCoord.y); CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap == NULL) { ERROR3("No applied attributes"); return FALSE; } pAttrMap->TransformForBrush(Trans); return TRUE; } /******************************************************************************************** > BOOL BlendPath::IsFilled() Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: - Outputs: - Returns: TRUE if the associated path is filled Purpose: Find out if the path is filled ********************************************************************************************/ BOOL BlendPath::IsFilled() { if (m_pPath != NULL) return (m_pPath->IsFilled); else return FALSE; } /******************************************************************************************** > BOOL BlendPath::IsStroked() Author: Mark_Neves (Xara Group Ltd) Created: 8/11/94 Inputs: - Outputs: - Returns: TRUE if the associated path is Stroked Purpose: Find out if the path is Stroked ********************************************************************************************/ BOOL BlendPath::IsStroked() { if (m_pPath != NULL) return (m_pPath->IsStroked); else return FALSE; } /******************************************************************************************** > DocCoord BlendPath::GetPathCoord(INT32 Index) Author: Mark_Neves (Xara Group Ltd) Created: 10/11/94 Inputs: Index = index into the path's coord array of coord required If index < 0 or index >= num coords in array, an value of 0 used Outputs: - Returns: The DocCoord at index Index Purpose: Access to path's coord array ********************************************************************************************/ DocCoord BlendPath::GetPathCoord(INT32 Index) { DocCoord Result(0,0); if (m_pPath != NULL) { if (m_DirChanged) m_pPath->Reverse(); DocCoord* pCoords = m_pPath->GetCoordArray(); INT32 n = m_pPath->GetNumCoords(); if (Index >= n || Index < 0) Index = 0; if (Index < n && pCoords != NULL) Result = pCoords[Index]; if (m_DirChanged) m_pPath->Reverse(); } return Result; } /******************************************************************************************** > void BlendPath::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix) Author: Mark_Neves (Xara Group Ltd) Created: 9/11/94 Inputs: pRender = region to render blobs into. pMatrix = matrix to apply to blob coords Outputs: - Returns: - Purpose: Renders the blend blobs by asking the pNodePath's Path to render its path blobs ********************************************************************************************/ void BlendPath::RenderBlendBlobs(RenderRegion* pRender,Trans2DMatrix* pMatrix) { #if !defined(EXCLUDE_FROM_RALPH) if (m_pPath != NULL) { // PathVerb* pVerbs = m_pPath->GetVerbArray(); DocCoord* pCoords = m_pPath->GetCoordArray(); INT32 n = m_pPath->GetNumCoords(); INT32 Index = 0; pRender->SetLineColour(COLOUR_NONE); pRender->SetFillColour(COLOUR_UNSELECTEDBLOB); INT32 i=0; DocCoord* pCoordList = (DocCoord*) malloc(n*sizeof(DocCoord)); if (pCoordList) { switch (m_PathType) { case PATHTYPE_SHAPE: // while (m_pPath->FindNextEndPoint(&Index)) // pRender->DrawBlob(pCoords[Index], BT_UNSELECTED); while (m_pPath->FindNextEndPoint(&Index)) pCoordList[i++] = pCoords[Index]; break; case PATHTYPE_LINE: // pRender->DrawBlob(pCoords[0] , BT_UNSELECTED); // pRender->DrawBlob(pCoords[n-1], BT_UNSELECTED); pCoordList[i++] = pCoords[0]; pCoordList[i++] = pCoords[n-1]; break; default: ERROR3("Unknown pathtype"); break; } if (pMatrix && i > 0) pMatrix->Transform(pCoordList,i); for (;i>0;i--) pRender->DrawBlob(pCoordList[i-1], BT_UNSELECTED); } DELPTR(pCoordList); } #endif } /******************************************************************************************** > BOOL BlendPath::IsPointOverBlob(DocCoord* pPointerPos,INT32* pIndex,Trans2DMatrix* pMatrix) Author: Mark_Neves (Xara Group Ltd) Created: 10/11/94 Inputs: pPointerPos = point to check against pIndex = ptr to place to store index if match is found pMatrix = matrix to apply to the path points before testing with pointer pos Outputs: *pIndex, updated if TRUE returned, undefined otherwise Returns: TRUE if match found, FALSE otherwise Purpose: This sees if the point given lies on a selected blob. If a match is found, the index to blob element is updated Markn 26-5-99: Modified so that if the function returns TRUE, *pPointerPos is updated so that it contains the coords of the blob it is over SeeAlso: - ********************************************************************************************/ BOOL BlendPath::IsPointOverBlob(DocCoord* pPointerPos,INT32* pIndex,Trans2DMatrix* pMatrix) { #if !defined(EXCLUDE_FROM_RALPH) BOOL Found = FALSE; if (m_pPath != NULL && pIndex != NULL) { Path* pPath = m_pPath; if (pMatrix) { pPath = new Path; if (pPath != NULL && pPath->Initialise()) { pPath->MergeTwoPaths(*m_pPath); pMatrix->Transform((DocCoord*)(pPath->GetCoordArray()), pPath->GetNumCoords() ); } } Found = pPath->FindNearestPoint(*pPointerPos,POINTFLAG_ENDPOINTS,pIndex); if (Found) { if (m_PathType == PATHTYPE_LINE) Found = ((*pIndex == 0) || (*pIndex == (pPath->GetNumCoords()-1))); else if (m_DirChanged) { pPath->Reverse(); pPath->FindNearestPoint(*pPointerPos,POINTFLAG_ENDPOINTS,pIndex); pPath->Reverse(); } if (Found) { *pPointerPos = GetPathCoord(*pIndex); if (pMatrix) pMatrix->Transform(pPointerPos,1); } } if (pMatrix) DELPTR(pPath); } return Found; #else return FALSE; #endif } /******************************************************************************************** > CCAttrMap* BlendPath::FindAppliedAttributes() Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: - Outputs: - Returns: Ptr to the attributes applied to this blend path NULL if unable to get attributes Purpose: This calls FindAppliedAttributes() on the node that created this blend path. It will only do this if this is the first call since the last FreeAppliedAttributes() or is the first ever call (in other words, it is cached). SeeAlso: - ********************************************************************************************/ CCAttrMap* BlendPath::FindAppliedAttributes() { if (m_pAppliedAttrs == NULL) { m_pAppliedAttrs = new CCAttrMap(30); if (m_pAppliedAttrs != NULL) { // Ilan 4/5/00 // Ensure geometry linked attributes which are applied to the entire Blend compound // aren't found if (!m_pCreatedByNode->FindAppliedAttributes(m_pAppliedAttrs,5000,NULL,TRUE)) { delete m_pAppliedAttrs; m_pAppliedAttrs = NULL; } AttrTranspFillGeometry* pTransp = NULL; m_pCreatedByNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrTranspFillGeometry), (NodeAttribute**)&pTransp); } } return m_pAppliedAttrs; } /******************************************************************************************** > void BlendPath::FreeAppliedAttributes() Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: - Outputs: - Returns: - Purpose: This will free any memory used to store the applied attributes of this blend path SeeAlso: - ********************************************************************************************/ void BlendPath::FreeAppliedAttributes() { if (!m_UniqueAttrs && m_pAppliedAttrs != NULL) { delete m_pAppliedAttrs; m_pAppliedAttrs = NULL; } } /******************************************************************************************** > BOOL BlendPath::MakeAttributeMap() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 11/9/2000 Inputs: - Outputs: - Returns: A copy of the applied attributes of this blendpath, or NULL if we fail Purpose: To make a complete copy of the applied attribute map SeeAlso: - ********************************************************************************************/ CCAttrMap* BlendPath::MakeAttributeMapCopy() { // easiest thing to do is get the attribute pointers the normal way // and then copy them one by one CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap == NULL) { ERROR3("Unable to find attributes in BlendPath::MakeAttributeMap"); return NULL; } // make a new map CCAttrMap* pCopyMap = new CCAttrMap(30); if (pCopyMap == NULL) return NULL; // now copy each one and set it back to the map CCRuntimeClass *pType; void *pVal; NodeAttribute* pNodeAttr = NULL; NodeAttribute* pAttrCopy = NULL; // iterating all (key, value) pairs for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); Pos != pAttrMap->GetEndPosition();) { // Get attr at CMapPtrToPtr::iterator Pos pAttrMap->GetNextAssoc(Pos,pType,pVal); // pVal is actually an attribute, so get a ptr to it and render it pNodeAttr = (NodeAttribute *)pVal; pAttrCopy = (NodeAttribute*)pNodeAttr->SimpleCopy(); if (pAttrCopy == NULL) { ERROR3("Unable to make attribute copy in BlendPath::MakeAttributeMap"); DeleteAttributeMapAndAttributes(pCopyMap); return NULL; } pCopyMap->SetAt( pAttrCopy->GetAttributeType(), (void *)pAttrCopy ); pAttrCopy = NULL; } return pCopyMap; } /******************************************************************************************** > CCAttrMap* BlendPath::UpdateAttributeMapCopy(CCAttrMap* pCopyMap) Author: Phil_Martin (Xara Group Ltd) Created: 14/01/2004 Inputs: - Outputs: - Returns: A copy of the applied attributes of this blendpath, or NULL if we fail Purpose: To make a complete copy of the applied attribute map SeeAlso: - ********************************************************************************************/ CCAttrMap* BlendPath::UpdateAttributeMapCopy(CCAttrMap* pCopyMap) { // easiest thing to do is get the attribute pointers the normal way // and then copy them one by one CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap == NULL) { ERROR3("Unable to find attributes in BlendPath::UpdateAttributeMapCopy"); return NULL; } ERROR2IF(pCopyMap==NULL, NULL, "Unable to find attribute copies in BlendPath::UpdateAttributeMapCopy"); // now copy the contents of attributes from the original map back into the copy map CCRuntimeClass *pType; void *pVal; void *pCopyVal; NodeAttribute *pNodeAttr = NULL; NodeAttribute *pAttrCopy = NULL; // iterating all (key, value) pairs for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); Pos != pAttrMap->GetEndPosition();) { // Get attr at CMapPtrToPtr::iterator Pos pAttrMap->GetNextAssoc(Pos, pType, pVal); pNodeAttr = (NodeAttribute*)pVal; BOOL bFound = pCopyMap->Lookup( pType, pCopyVal ); if (bFound) { pAttrCopy = (NodeAttribute*)pCopyVal; pNodeAttr->PolyCopyNodeContents(pAttrCopy); } } return pCopyMap; } /******************************************************************************************** > void BlendPath::DeleteAttributeMapAndAttributes() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 11/9/2000 Inputs: - Outputs: - Returns: - Purpose: Counterpart to MakeAttributeMap, deletes all the copied attributes and then the map. SeeAlso: - ********************************************************************************************/ void BlendPath::DeleteAttributeMapAndAttributes(CCAttrMap* pMap) { if (pMap != NULL) { pMap->DeleteAttributes(); delete pMap; pMap = NULL; } } /******************************************************************************************** > void BlendPath::SetFreeAttributeFlag(BOOL Value) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 10/5/2000 Inputs: Value to set the flag to Outputs: - Returns: - Purpose: Sets the flag which indicates whether or not we definitely want to delete the attribute map upon destruction of this blendpath. This was designed pretty specifically for use with the Brush code so if you want to use it for other purposes you should be careful. SeeAlso: - ********************************************************************************************/ void BlendPath::SetFreeAttributeFlag(BOOL Value) { m_bFreeAttrMapOnDestruct = TRUE; } /******************************************************************************************** > void BlendPath::SetAppliedAttributes(CCAttrMap* pMap) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: pointer to an attribute map to set for Outputs: - Returns: - Purpose: Designed specifically for use when blending brush attributes, which need to generate blendpaths and attribute maps by hand,so to speak. This function allows you to set an external attribute map for this blendpath. SeeAlso: - ********************************************************************************************/ void BlendPath::SetAppliedAttributes(CCAttrMap* pMap) { if (pMap == NULL) return; // get rid of any existing attributes FreeAppliedAttributes(); m_pAppliedAttrs = pMap; } /******************************************************************************************** > void BlendPath::SetPath(Path* pPath) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: pointer to a path to set Outputs: - Returns: - Purpose: Designed specifically for use when blending brush attributes, which need to generate blendpaths and attribute maps by hand,so to speak. This function allows you to set an external path for this blendpath. SeeAlso: - ********************************************************************************************/ void BlendPath::SetPath(Path* pPath) { if (pPath != NULL) { m_pPath = pPath; m_NumElements = m_pPath->GetNumElements(); m_PathType = m_pPath->GetPathType(); m_SubPathNum = m_pPath->GetNumSubpaths(); } } /******************************************************************************************** > void BlendPath::DeleteAttributesAndPath() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: - Purpose: Sets m_SubPathID to zero thus ensuring that all the attributes and the attribute map are deleted. We need to do this for blendpaths created by blending brush attributes as mentioned in the previous two functions. I don't recommend use of this function under normal circumstances. SeeAlso: - ********************************************************************************************/ void BlendPath::DeleteAttributesAndPath() { m_bDeleteAll = TRUE; } /******************************************************************************************** > BitmapFillAttribute* BlendPath::GetAppliedBitmapFill() Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: - Outputs: - Returns: the bitmap colour fill attribute applying to this blendpath, if there is one Purpose: as above SeeAlso: - ********************************************************************************************/ AttrBitmapFill* BlendPath::GetAppliedBitmapColourFill() { // Get our applied attribute map CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap == NULL) return NULL; // loop through the map until we find an bitmap fill CCRuntimeClass *pType; void *pVal; NodeAttribute* pNodeAttr = NULL; // iterating all (key, value) pairs for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); Pos != pAttrMap->GetEndPosition();) { // Get attr at CMapPtrToPtr::iterator Pos pAttrMap->GetNextAssoc(Pos,pType,pVal); // pVal is actually an attribute, so get a ptr to it pNodeAttr = (NodeAttribute *)pVal; if (pNodeAttr->IsABitmapFill() &&!pNodeAttr->IsATranspFill()) break; pNodeAttr = NULL; } // first get the attribute value from the attribute node if (pNodeAttr != NULL) { return (AttrBitmapFill*)pNodeAttr; } return NULL; } /******************************************************************************************** > BitmapFillAttribute* BlendPath::GetAppliedBitmapTranspFill() Author: Mark_Neves (Xara Group Ltd) Created: 16/11/94 Inputs: - Outputs: - Returns: the bitmapfill transparent attribute applying to this blendpath, if there is one Purpose: as above SeeAlso: - ********************************************************************************************/ AttrBitmapFill* BlendPath::GetAppliedBitmapTranspFill() { // Get our applied attribute map CCAttrMap* pAttrMap = FindAppliedAttributes(); if (pAttrMap == NULL) return NULL; // loop through the map until we find an bitmap fill CCRuntimeClass *pType; void *pVal; NodeAttribute* pNodeAttr = NULL; // iterating all (key, value) pairs for( CMapPtrToPtr::iterator Pos = pAttrMap->GetStartPosition(); Pos != pAttrMap->GetEndPosition();) { // Get attr at CMapPtrToPtr::iterator Pos pAttrMap->GetNextAssoc(Pos,pType,pVal); // pVal is actually an attribute, so get a ptr to it pNodeAttr = (NodeAttribute *)pVal; if (pNodeAttr->IsABitmapFill() &&pNodeAttr->IsATranspFill()) break; pNodeAttr = NULL; } // first get the attribute value from the attribute node if (pNodeAttr != NULL) { return (AttrBitmapFill*)pNodeAttr; } return NULL; } /******************************************************************************************** > void BlendPath::DeleteAttributesAndPath() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: the applied linewidth, or -1 indicating that no line width was found Purpose: Returns the cached line width applying to this blendpath SeeAlso: - ********************************************************************************************/ MILLIPOINT BlendPath::GetAppliedLineWidth() { // find the line width attribute NodeAttribute* pAttr = NULL; m_pCreatedByNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), &pAttr); if (pAttr != NULL) m_AppliedLineWidth = ((AttrLineWidth*)pAttr)->Value.LineWidth; return m_AppliedLineWidth; } /******************************************************************************************** > BOOL BlendPath::MakeCopyPathAndAttributes() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: TRUE if successful, FALSE otherwise Purpose: Because of all the imprecision problems we've been having I've decided it is best if we never ever access the orignal blendpath path and attribute data for rendering. Instead what we will do is generate copies of this data and supply it to anyone who wants to render with this brush. The copies will then be destroyed after we have finished rendering. This function makes copies of the paths and attributes and and assigns them to our member pointers. SeeAlso: - ********************************************************************************************/ BOOL BlendPath::MakeCopyPathAndAttributes() { // if we've no path then fail ERROR2IF(m_pPath == NULL, FALSE, "Path is NULL in BlendPath::MakeCopyPathAndAttributes"); // likewise we need the attributes CCAttrMap* pAttrMap = FindAppliedAttributes(); ERROR2IF(pAttrMap == NULL, FALSE, "Unable to find attributes in BlendPath::MakeCopyPathAndAttributes"); // if we've already got them then get rid of the existing ones if (m_pCopyPath != NULL || m_pCopyAttrs != NULL) DeleteCopyPathAndAttributes(); // first copy the path m_pCopyPath = new Path; if (m_pCopyPath == NULL) return FALSE; if (!m_pCopyPath->Initialise(m_pPath->GetNumCoords())) { delete m_pCopyPath; m_pCopyPath = NULL; return FALSE; } if (!m_pCopyPath->CopyPathDataFrom(m_pPath)) { delete m_pCopyPath; m_pCopyPath = NULL; return FALSE; } // get a copy of our attributes m_pCopyAttrs = MakeAttributeMapCopy(); if (m_pCopyAttrs == NULL) { delete m_pCopyPath; m_pCopyPath = NULL; return FALSE; } return TRUE; } /******************************************************************************************** > BOOL BlendPath::UpdateCopyPathAndAttributes() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: TRUE if successful, FALSE otherwise Purpose: Because of all the imprecision problems we've been having I've decided it is best if we never ever access the orignal blendpath path and attribute data for rendering. Instead what we will do is generate copies of this data and supply it to anyone who wants to render with this brush. The copies will then be destroyed after we have finished rendering. This function makes copies of the paths and attributes and and assigns them to our member pointers. SeeAlso: - ********************************************************************************************/ BOOL BlendPath::UpdateCopyPathAndAttributes() { // if we've no path then fail ERROR2IF(m_pPath == NULL, FALSE, "Path is NULL in BlendPath::UpdateCopyPathAndAttributes"); ERROR2IF(m_pCopyPath == NULL, FALSE, "CopyPath is NULL in BlendPath::UpdateCopyPathAndAttributes"); // likewise we need the attributes ERROR2IF(m_pAppliedAttrs==NULL, FALSE, "No applied attrs in BlendPath::UpdateCopyPathAndAttributes"); ERROR2IF(m_pPath->GetNumCoords()!=m_pCopyPath->GetNumCoords(), FALSE, "Mismatching paths in BlendPath::UpdateCopyPathAndAttributes"); if (!m_pCopyPath->Initialise(m_pPath->GetNumCoords())) { delete m_pCopyPath; m_pCopyPath = NULL; return FALSE; } if (!m_pCopyPath->CopyPathDataFrom(m_pPath)) { delete m_pCopyPath; m_pCopyPath = NULL; return FALSE; } // get a copy of our attributes m_pCopyAttrs = UpdateAttributeMapCopy(m_pCopyAttrs); if (m_pCopyAttrs == NULL) { delete m_pCopyPath; m_pCopyPath = NULL; return FALSE; } return TRUE; } /******************************************************************************************** > void BlendPath::DeleteCopyPathAndAttributes() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: - Purpose: Deletes the copies we made of the paths and attributes SeeAlso: - ********************************************************************************************/ void BlendPath::DeleteCopyPathAndAttributes() { if (m_pCopyPath != NULL) { delete m_pCopyPath; m_pCopyPath = NULL; } if (m_pCopyAttrs != NULL) { DeleteAttributeMapAndAttributes(m_pCopyAttrs); m_pCopyAttrs = NULL; } } /******************************************************************************************** > Path* BlendPath::GetCopyPath() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: the copy we made of our path Purpose: access SeeAlso: - ********************************************************************************************/ Path* BlendPath::GetCopyPath() { return m_pCopyPath; } /******************************************************************************************** > Path* BlendPath::GetCopyPath() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 28/3/2000 Inputs: - Outputs: - Returns: the copy we made of our attribute map Purpose: access SeeAlso: - ********************************************************************************************/ CCAttrMap* BlendPath::GetCopyAttributes() { return m_pCopyAttrs; } //------------------------------------------- //------------------------------------------- //------------------------------------------- //------------------------------------------- BecomeA::BecomeA(BecomeAReason ThisReason, CCRuntimeClass* pClass, UndoableOperation* pOp /* = NULL */, BOOL sel /*= TRUE*/, BOOL First /*= FALSE*/ ) : Reason(ThisReason), pClassToBecome(pClass), pUndoOp(pOp) { Select = sel; pCallNode = NULL; AmFirstNode = First; fSilhouette = FALSE; fShadowSilhouettes = FALSE; insertComplexBlendStepsAsPaths = FALSE; m_Count = 0; m_bIsCounting = FALSE; m_bBecomeANodePath = (pClassToBecome==CC_RUNTIME_CLASS(NodePath)); m_bInPlace = FALSE; m_bSecondary = FALSE; m_bPathsOnly = FALSE; } BOOL BecomeA::BAPath() const {return m_bBecomeANodePath;} // This function should be called when Reason == BECOMEA_PASSBACK // (see above for a description of this reason code) BOOL BecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap /*=NULL*/) { if (pAttrMap) { pAttrMap->DeleteAttributes(); delete pAttrMap; } return FALSE; } /*********************************************************************************************** > NodeRenderableInk * BlendBecomeA::CreateWrapNode(NodeRenderableInk * pNode, NodeCompound *pTree, CCAttrMap * pMap) Author: David_McClarnon (Xara Group Ltd) Created: 15/3/2000 Inputs: pNode - the node to wrap up pTree - the root of the tree to use to wrap the node pAttrMap - the attribute map to use in the wrapping Outputs: - Returns: TRUE if Successful, FALSE otherwise Pupose: Recursively wraps up the given node using the tree specified ***********************************************************************************************/ NodeRenderableInk * BlendBecomeA::CreateWrapNode(NodeRenderableInk * pNode, NodeCompound *pTree, CCAttrMap * pMap) { NodeCompound * pCompound = (NodeCompound *)pTree->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound)); if (pCompound) { // create a wrapped node for the compound node pNode = CreateWrapNode(pNode, pCompound, pMap); } // create the wrapped node from the given compound node return pTree->CreateTreeFromNodeToBlend(pNode, pMap); } /*********************************************************************************************** > BOOL BlendBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap) Author: Mark_Neves (Xara Group Ltd) Created: 18/10/94 Inputs: pNewNode = ptr to Node that been generated by a node via its DoBecomeA() method pCreatedByNode = ptr to the node that created pNewNode pAttrMap = ptr to attr map (or NULL if to use applied attrs of pCreatedByNode) Outputs: - Returns: TRUE if Successful, FALSE otherwise Purpose: This is called by each node that is blended. When the blend tool asks an object to turn itself into loads of paths, it gives the object a BlendBecomeA ptr. The object calls the PassBack() method of BlendBecomeA (i.e. this routine) for each path that is generated. This generates a BlendPath for each sub-path in the given NodePath, and adds it to a blend reference object. NOTE: pNewNode should point to isolated instance, and not to a used NodePath linked into the tree! NOTE: Once you give a BlendPath a NodePath, this becomes property of the BlendPath and can be altered and/or deleted by the BlendPath at any time. DO NOT KEEP A COPY OF THE PTR pThisNodePath!!! If you want a copy of the NodePath at pThisNodePath, make a new copy of the node. NOTE: If it fails, pNewNode will have to be deleted by the caller ***********************************************************************************************/ BOOL BlendBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap) { ERROR2IF(pBlendRef == NULL,FALSE,"pBlendRef == NULL"); ERROR2IF(pNewNode == NULL,FALSE,"pNewNode == NULL"); if (pProgress != NULL) { if (!pProgress->Update(pProgress->GetCount(1),TRUE) && !IgnoreEscape) return FALSE; } Path* pInkPath = NULL; Node * pPathNode = NULL; // DMc // we have a game of let's pretend in here // because all passback nodes can only have 1 path node in them, // then let's get this node & set up the path appropriately if (pNewNode->IsNodePath()) { pInkPath = &(((NodePath*)pNewNode)->InkPath); pPathNode = (NodePath *)pNewNode; } else if (pNewNode->IsCompound()) { NodeRenderableInk * pNodeToBlend = ((NodeCompound *)pNewNode)->GetNodeToBlend(); if (pNodeToBlend) { if (pNodeToBlend->IsNodePath()) { pInkPath = &(((NodePath *)pNodeToBlend)->InkPath); pPathNode = (NodePath *)pNodeToBlend; } } } INT32 NumCoords = pInkPath->GetNumCoords(); INT32 StartIndex=0,EndIndex=0; UINT32 NumSubPaths=0; Path TempPath; BOOL ok = TempPath.Initialise(NumCoords); // All subpaths generated by this call will have the same subpath ID SubPathID++; NodeRenderableInk * pTreeNode = NULL; // This loop interates for each subpath in pInkPath. while (ok && StartIndex < NumCoords) { // Get the start and end indexes for the current subpath EndIndex = StartIndex; pInkPath->FindEndElOfSubPath(&EndIndex); ERROR2IF(EndIndex >= NumCoords,FALSE,"EndIndex gone beyond num coords in path"); // Clear the temp path we've set up, and copy the subpath into it TempPath.ClearPath(FALSE); ok = pInkPath->CopySectionTo(&TempPath,StartIndex,(EndIndex-StartIndex)+1); if (ok) { // Make sure the filled and stroked flags are correct TempPath.IsFilled = pInkPath->IsFilled; TempPath.IsStroked = pInkPath->IsStroked; if (m_pMatrix != NULL) m_pMatrix->Transform((DocCoord*)TempPath.GetCoordArray(), TempPath.GetNumCoords() ); // Create a new NodePath for this subpath NodePath* pNewNodePath = new NodePath; ok = (pNewNodePath != NULL); if (ok) { // Copy the subpath in TempPath into the new NodePath's path object Path* pNewPath = &(pNewNodePath->InkPath); ok = pNewPath->Initialise(TempPath.GetNumCoords()); if (ok) ok = pNewPath->CopyPathDataFrom(&TempPath); if (ok) { // Create a new BlendPath obect BlendPath* pBlendPath = new BlendPath; ok = (pBlendPath != NULL); // now, have we been passed a compound node ? // if so, wrap up the given node with the new node if (pNewNode->IsCompound()) { NodeRenderableInk * pWrapNode = CreateWrapNode(pNewNodePath, (NodeCompound *)pNewNode, pAttrMap); if (!pWrapNode) return FALSE; // set up its created by node ((NodeCompound *)pWrapNode)->SetBlendCreatedByNode(((NodeCompound *)pNewNode)->GetBlendCreatedByNode()); // now, we can only have start blend nodes on first blend path, and we can // only have end blend nodes on the last blend path if (NumSubPaths != 0) { pTreeNode = pWrapNode; while (pTreeNode && pTreeNode->IsCompound()) { if (((NodeCompound *)pTreeNode)->IsStartBlendGroupNode()) { ((NodeCompound *)pTreeNode)->SetStartBlendGroupNode(FALSE); } pTreeNode = (NodeCompound *)pTreeNode->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound)); } } // reset end blend nodes if (EndIndex != NumCoords-1) { pTreeNode = pWrapNode; while (pTreeNode && pTreeNode->IsCompound()) { if (((NodeCompound *)pTreeNode)->IsEndBlendGroupNode()) { ((NodeCompound *)pTreeNode)->SetEndBlendGroupNode(FALSE); } pTreeNode = (NodeCompound *)pTreeNode->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound)); } } if (pWrapNode->IsCompound()) { if (((NodeCompound *)pWrapNode)->IsStartBlendGroupNode()) { TRACEUSER( "ChrisS", _T("BlendBecomeA::Start node\n")); } if (((NodeCompound *)pWrapNode)->IsEndBlendGroupNode()) { TRACEUSER( "ChrisS", _T("BlendBecomeA::End node\n")); } } if (ok) ok = pBlendPath->Initialise(pWrapNode,Index,pCreatedByNode,SubPathID,NumSubPaths,NULL); if (ok) ok = pBlendRef->AddBlendPath(pBlendPath); } else { // Initialise the BlendPath object with the generated NodePath // and add the BlendPath to the BlendRef if (ok) ok = pBlendPath->Initialise(pNewNodePath,Index,pCreatedByNode,SubPathID,NumSubPaths,pAttrMap); if (ok) ok = pBlendRef->AddBlendPath(pBlendPath); if (IS_A (pNewNode, NodeBlendPath)) { pBlendPath->SetCreatedViaNodeBlendPath (TRUE); } } } } } Index = -1; StartIndex = EndIndex+1; // the start of the next subpath begins after the end of the last one // Get the next sub path NumSubPaths++; } if (pNewNode->IsCompound()) { pAttrMap->DeleteAttributes(); delete pAttrMap; } if (ok) { pBlendRef->SetAWComplex(NumSubPaths > 1); // This is the 'complex' flag used when exporting to AW EPS pNewNode->CascadeDelete(); delete pNewNode; pNewNode = NULL; } return (ok); } /*********************************************************************************************** > BOOL HandleBecomeA::PassBack(Path* pBlendedPath,CCAttrMap* pBlendedAttrMap,UINT32 Step) Author: Mark_Neves (Xara Group Ltd) Created: 17/10/94 Inputs: pBlendedPath = ptr to a blended path object pBlendedAttrMap = ptr to the blended attributes for this path Step = The step number this path belongs to Outputs: - Returns: TRUE if successful, FALSE otherwise Purpose: This takes a path and an attribute map generated by a NodeBlender object, and generates a NodePath object, if it wants to become a path object If pBecomeA->Reason == BECOMEA_REPLACE, the NodePath and attributes are added to the tree. If pBecomeA->Reason == BECOMEA_PASSBACK, the NodePath is passed to the routine. ***********************************************************************************************/ BOOL HandleBecomeA::PassBack(Path* pBlendedPath,CCAttrMap* pBlendedAttrMap,UINT32 Step) { // Check BecomeA mechanism has been set up ERROR3IF(pBecomeA == NULL,"pBecomeA is NULL"); ERROR3IF(pNodeBlender == NULL,"pNodeBlender is NULL"); if (pBecomeA == NULL || pNodeBlender == NULL) return FALSE; // Check for a NULL entry params ERROR3IF(pBlendedPath == NULL,"pBlendedPath is NULL"); ERROR3IF(pBlendedAttrMap == NULL,"pBlendedAttrMap is NULL"); if (pBlendedPath == NULL || pBlendedAttrMap == NULL) return FALSE; // This lump checks that the Reason is one that we understand // It also makes sure that we don't have a NULL UndoOp ptr BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK); ERROR3IF_PF(!ValidReason,("Unkown BecomeA reason %d",pBecomeA->GetReason())); if (!ValidReason) return FALSE; // pBecomeA->Reason is one that we understand. BOOL Success = TRUE; // Our success flag (Important that this defaults to TRUE) NodePath* pNewNodePath = NULL; // Ptr to a new NodePath, if we get to make one. if (pBecomeA->BAPath()) { // We need to create a new NodePath, no matter what the reason. // Allocate a new NodePath node ALLOC_WITH_FAIL(pNewNodePath, (new NodePath), pBecomeA->GetUndoOp()); Success = (pNewNodePath != NULL); // Initialise the path if (Success) CALL_WITH_FAIL(pNewNodePath->InkPath.Initialise(pBlendedPath->GetNumCoords(),12), pBecomeA->GetUndoOp(), Success); if (Success) CALL_WITH_FAIL(pNewNodePath->InkPath.CopyPathDataFrom(pBlendedPath), pBecomeA->GetUndoOp(), Success); // If Success is TRUE, then we now have a new NodePath object that contains this shape's path if (Success) { switch (pBecomeA->GetReason()) { case BECOMEA_REPLACE : { // It's a BECOMEA_REPLACE, so put the new NodePath in the tree in an undoable way // Can't do it in an undoable way without an Undo Op // ERROR2IF_PF(pBecomeA->GetUndoOp() == NULL,FALSE,("GetUndoOp() returned NULL")); // Can't do it if we don't have a context node ERROR2IF(pContextNode == NULL,FALSE,"pContextNode is NULL"); // By default, attach the new path as the previous child of the context node Node* pAttachNode = pContextNode; AttachNodeDirection AttachDir = PREV; // By default, create a hide node action for the new path we're attaching to the tree BOOL HideNode = TRUE; Node* pNodeToHide = pNewNodePath; // Should we be placing all created paths inside a group? if (MakeGroupPerStep()) { // Do we need to create a new group? I.e. is this the first path created for this step of the blend? if (Step != LastStep) { // Remember this step value so we can tell if the next path we receive belongs to the // same blend step LastStep = Step; // Create a new group and place it in the tree next to the context node pNodeGroup = new NodeGroup; Success = (pNodeGroup != NULL); if (Success) { // Put group into the tree pNodeGroup->AttachNode(pContextNode,PREV); // Make a hide node action for this group HideNode = TRUE; pNodeToHide = pNodeGroup; } } else HideNode = FALSE; // We created a hide node action when this group was created // If we have a group node, make sure the new path gets placed in the tree as the // group's last child if (Success) { pAttachNode = pNodeGroup; AttachDir = LASTCHILD; } } if (Success) { ERROR2IF(pAttachNode == NULL,FALSE,"The attach node is NULL!!!"); // Insert the new NodePath into the tree pNewNodePath->AttachNode(pAttachNode,AttachDir); // Apply all the attributes to the new path pNewNodePath->ApplyAttributes(pBlendedAttrMap,TRUE); // Set the bounds pNewNodePath->ValidateBoundingRect(); pNewNodePath->InvalidateBoundingRect(); // Create a hide node action to hide the node when we undo if (HideNode) { ERROR2IF(pNodeToHide == NULL,FALSE,"pNodeToHide is NULL"); if (pBecomeA->GetUndoOp()) { HideNodeAction* UndoHideNodeAction; Success = (HideNodeAction::Init(pBecomeA->GetUndoOp(), pBecomeA->GetUndoOp()->GetUndoActionList(), pNodeToHide, TRUE, // Include subtree size ( Action**)(&UndoHideNodeAction)) != AC_FAIL); } } CCAttrMap* pNewAttrMap = pBlendedAttrMap->Copy(); if (pNewAttrMap != NULL) pBecomeA->PassBack(pNewNodePath, pNodeBlender, pNewAttrMap); } } break; case BECOMEA_PASSBACK : { // We need to create a copy of the blended attributes to give to the caller // as they don't exist in the tree, and the ones in pBlendedAttrMap will be // deleted eventually. CCAttrMap* pNewAttrMap = pBlendedAttrMap->Copy(); if (pNewAttrMap != NULL) Success = pBecomeA->PassBack(pNewNodePath,pNodeBlender,pNewAttrMap); else Success = FALSE; break; } default: break; } } } if (!Success) { if (pNodeGroup != NULL) { // Delete all the groups children (if it has any) and unlink it from the tree (if it's linked) // This is all done by CascadeDelete() pNodeGroup->CascadeDelete(); delete pNodeGroup; pNodeGroup = NULL; } else if (pNewNodePath != NULL) { // Delete all the NodePath's children (if it has any) and unlink it from the tree (if it's linked) // This is all done by CascadeDelete() pNewNodePath->CascadeDelete(); delete pNewNodePath; pNewNodePath = NULL; } } return Success; } //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ // Export code... /********************************************************************************************* > void NodeBlender::PreExportRender(RenderRegion* pRegion) Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: pRegion = ptr to the export render region to export to Outputs: Returns: Purpose: Called before this node or any of its children have been rendered to the export region. This outputs the "start blender" command. Supports ArtWorks EPS and Camelot EPS Errors: **********************************************************************************************/ void NodeBlender::PreExportRender(RenderRegion* pRegion) { #ifdef DO_EXPORT // Ensure that we are initialised (if it can be done!!) if (!Reinit()) return; if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion)) { EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC(); if (IS_A(pRegion, CamelotEPSRenderRegion)) { // only want renderable stuff in EPS return; } else if (pRegion->IS_KIND_OF(NativeRenderRegion)) { // Output the Camelot format for blenders pDC->OutputValue(INT32(0)); // Reserved INT32 1 pDC->OutputValue(INT32(0)); // Reserved INT32 2 pDC->OutputValue(INT32(0)); // Reserved INT32 3 pDC->OutputValue(INT32(0)); // Reserved INT32 4 pDC->OutputValue(m_ObjIndexStart); // Index to object at start of blend pDC->OutputValue(m_ObjIndexEnd); // Index to object at end of blend pDC->OutputValue(m_PathIndexStart); // Path index to el num of start path pDC->OutputValue(m_PathIndexEnd); // Path index to el num of end path pDC->OutputToken(_T("csbr")); // Camelot "start blender" token pDC->OutputNewLine(); } else { NodeBlend* pNodeBlend = GetNodeBlend(); if (pNodeBlend != NULL) { if (pNodeBlend->IsArtWorksEPSCompatible()) { // Output the AW format for blenders, if this blender is ArtWorks-compatible pDC->OutputValue(INT32(0)); // Expanded flag pDC->OutputValue(INT32(IsComplex())); // Complex flag pDC->OutputValue(INT32(IsOneToOne())); // The OneToOne flag pDC->OutputValue(INT32(GetNumBlendSteps()+1)); // The number of blend steps (+1 for AW) pDC->OutputValue(GetAWStartPathIndex()); // Get start path index pDC->OutputValue(GetAWEndPathIndex()); // Get end path index pDC->OutputToken(_T("asbr")); // ArtWorks "start blender" token pDC->OutputNewLine(); // Blend is done return; } else { // Blend cannot be done as AW - just render intermediate shapes. Render(pRegion); } } else { ERROR3("Parent is not a NodeBlend"); } } } // These two lines will output the first pair of blendable paths - useful for testing //pRefStart->GetFirstBlendPath()->GetNodePath()->Render(pRegion); //pRefEnd ->GetFirstBlendPath()->GetNodePath()->Render(pRegion); #endif } /********************************************************************************************* > BOOL NodeBlender::ExportRender(RenderRegion* pRegion) Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: pRegion = ptr to the export render region to export to Outputs: Returns: TRUE if ok, FALSE if something went wrong Purpose: Called after this node and all of its children have been rendered to the export region. This outputs the "end blender" command. Supports ArtWorks EPS and Camelot EPS Errors: **********************************************************************************************/ BOOL NodeBlender::ExportRender(RenderRegion* pRegion) { #ifdef DO_EXPORT // Ensure that we are initialised (if it can be done!!) if (!Reinit()) return FALSE; if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion)) { EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC(); if (IS_A(pRegion, CamelotEPSRenderRegion)) { // only want renderable stuff in EPS return FALSE; // render this as paths. } else if (pRegion->IS_KIND_OF(NativeRenderRegion)) { pDC->OutputToken(_T("cebr")); // Camelot "end blender" token pDC->OutputNewLine(); } else { NodeBlend* pNodeBlend = GetNodeBlend(); if (pNodeBlend != NULL) { if (pNodeBlend->IsArtWorksEPSCompatible()) { pDC->OutputToken(_T("aebr")); // ArtWorks "end blender" token pDC->OutputNewLine(); } } } // Tell caller we rendered ourselves ok return TRUE; } #endif // Render this node in the normal way return FALSE; } /********************************************************************************************* > BOOL NodeBlend::IsArtWorksEPSCompatible() Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: Outputs: Returns: TRUE if this blender is AW compatible Purpose: A blender is AW EPS compatible if ALL the following conditions are TRUE: 1) This node is between two path nodes. 2) The blender is blending the two path nodes it lies between 3) The two path nodes contain the same number of sub paths. This is called by the parent blend's IsArtWorksEPSCompatible() func. Errors: **********************************************************************************************/ BOOL NodeBlender::IsArtWorksEPSCompatible() { ERROR2IF(m_pRefStart == NULL,FALSE,"m_pRefStart == NULL"); ERROR2IF(m_pRefEnd == NULL,FALSE,"m_pRefEnd == NULL"); Node* pPrevNode = FindPrevious(); Node* pNextNode = FindNext(); return (pPrevNode == m_pNodeStart && pNextNode == m_pNodeEnd && pPrevNode != NULL && pNextNode != NULL && IS_A(pPrevNode,NodePath) && IS_A(pNextNode,NodePath) && m_pRefStart->GetNumBlendPaths() == m_pRefEnd->GetNumBlendPaths()); } /********************************************************************************************* > BOOL NodeBlend::IsComplex() Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: Outputs: Returns: TRUE if this blender is complex Purpose: The blender is "complex" as far as AW EPS is concerned, if either the start path or the end path of the blend has more that one sub-path. Errors: **********************************************************************************************/ BOOL NodeBlender::IsComplex() { ERROR2IF(m_pRefStart == NULL,FALSE,"m_pRefStart == NULL"); ERROR2IF(m_pRefEnd == NULL,FALSE,"m_pRefEnd == NULL"); return (m_pRefStart->GetAWComplex() || m_pRefEnd->GetAWComplex()); } /********************************************************************************************* > INT32 NodeBlender::GetAWStartPathIndex() Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: Outputs: Returns: AW path index based on the PathIndexStart member var Purpose: This converts PathIndexStart into an index AW understands (compatible with RISCOS path format) Errors: **********************************************************************************************/ INT32 NodeBlender::GetAWStartPathIndex() { ERROR2IF(m_pRefStart == NULL,-1,"m_pRefStart == NULL"); return (GetAWPathIndex(m_pRefStart,m_PathIndexStart)); } /********************************************************************************************* > INT32 NodeBlender::GetAWEndPathIndex() Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: Outputs: Returns: AW path index based on the PathIndexEnd member var Purpose: This converts PathIndexEnd into an index AW understands (compatible with RISCOS path format) Errors: **********************************************************************************************/ INT32 NodeBlender::GetAWEndPathIndex() { ERROR2IF(m_pRefEnd == NULL,-1,"m_pRefEnd == NULL"); return (GetAWPathIndex(m_pRefEnd,m_PathIndexEnd)); } /********************************************************************************************* > INT32 NodeBlender::GetAWPathIndex(BlendRef *pRef,INT32 PathIndex) Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: pRef = ptr to a blend ref PathIndex = index into the blend ref's first path Outputs: Returns: AW path index based on PathIndex and the first path in the blend ref Purpose: This converts the index into an index AW understands (compatible with RISCOS path format) Errors: **********************************************************************************************/ INT32 NodeBlender::GetAWPathIndex(BlendRef *pRef,INT32 PathIndex) { BlendPath* pBlendPath = pRef->GetFirstBlendPath(); if (pBlendPath == NULL || pBlendPath->GetPath() == NULL) return -1; Path* pPath = pBlendPath->GetPath(); INT32 AWPathIndex=0,Index=0; while (Index < PathIndex) { pPath->FindNextEndPoint(&Index); AWPathIndex++; } return AWPathIndex; } //------------------------------------------------------------------------- //------------------------------------------------------------------------- //------------------------------------------------------------------------- /******************************************************************************************** > virtual BOOL NodeBlender::WritePreChildrenWeb(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 30/5/96 Inputs: pFilter = ptr to the filter Returns: TRUE if record is written, FALSE if not Purpose: Writes the blender record to the filter SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::WritePreChildrenWeb(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); BOOL ok = TRUE; CXaraFileRecord Rec(TAG_BLENDER,TAG_BLENDER_SIZE); if (ok) ok = Rec.Init(); if (ok) ok = Rec.WriteINT32(m_PathIndexStart); if (ok) ok = Rec.WriteINT32(m_PathIndexEnd); if (ok) ok = pFilter->Write(&Rec); // Diccon 9/99 added extra record CXaraFileRecord Additional(TAG_BLENDERADDITIONAL, TAG_BLENDERADDITIONAL_SIZE); if (ok) ok = Additional.Init(); if (ok) ok = Additional.WriteINT32(m_BlendedOnCurve); if (ok) { if (m_NodeBlendPathIndex == -2) ok = Additional.WriteINT32(-1); else ok = Additional.WriteINT32(m_NodeBlendPathIndex); } if (ok) ok = Additional.WriteINT32(m_ObjIndexStart); if (ok) ok = Additional.WriteINT32(m_ObjIndexEnd); // Karim 05/02/2001 // Extra bitfield tacked onto the end of the record. // Currently this stores only our reversed-on-path flag. if (ok) { BYTE BitField = IsReversed() ? 1 : 0; ok = Additional.WriteBYTE(BitField); } if (ok) ok = pFilter->Write(&Additional); return ok; #else return FALSE; #endif } //-------------------------------------------------------------- // See NodeBlender::WritePreChildrenWeb(BaseCamelotFilter* pFilter) // BOOL NodeBlender::WritePreChildrenNative(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT return WritePreChildrenWeb(pFilter); #else return FALSE; #endif } /******************************************************************************************** > BOOL NodeBlender::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 11/5/99 Inputs: pFilter = ptr to the filter to write to Outputs: - Returns: TRUE ok, FALSE otherwise Purpose: Begins the child record sequence for the blender Blenders need to write out curve CMapPtrToPtr::iteratoring information if the blend is on a curve SeeAlso: WritePreChildrenNative() ********************************************************************************************/ BOOL NodeBlender::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); if (GetNodeBlendPath() == NULL) return NodeRenderableInk::WriteBeginChildRecordsNative(pFilter); // First thing to do is write out the Down record BOOL ok = (pFilter->WriteZeroSizedRecord(TAG_DOWN)); // Write out the blender's curve CMapPtrToPtr::iterator proportions { CamelotFileRecord PropRec(pFilter,TAG_BLENDER_CURVEPROP,TAG_BLENDER_CURVEPROP_SIZE); if (ok) ok = PropRec.Init(); if (ok) ok = PropRec.WriteDOUBLE(m_ProportionOfPathDistStart); if (ok) ok = PropRec.WriteDOUBLE(m_ProportionOfPathDistEnd); if (ok) ok = pFilter->Write(&PropRec); } // Write out the blender's curve angles if (m_AngleStart != 0.0 || m_AngleEnd != 0.0) { CamelotFileRecord AngleRec(pFilter,TAG_BLENDER_CURVEANGLES,TAG_BLENDER_CURVEANGLES_SIZE); if (ok) ok = AngleRec.Init(); if (ok) ok = AngleRec.WriteDOUBLE(m_AngleStart); if (ok) ok = AngleRec.WriteDOUBLE(m_AngleEnd); if (ok) ok = pFilter->Write(&AngleRec); } return ok; #else return TRUE; #endif //DO_EXPORT } /******************************************************************************************** > BOOL NodeBlender::WriteEndChildRecordsNative(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 11/5/99 Inputs: pFilter = ptr to the filter to write to Outputs: - Returns: TRUE ok, FALSE otherwise Purpose: Ends the child record sequence for the blend Only does anything if the blend is on a curve SeeAlso: WritePreChildrenNative() ********************************************************************************************/ BOOL NodeBlender::WriteEndChildRecordsNative(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); if (GetNodeBlendPath() == NULL) return NodeRenderableInk::WriteEndChildRecordsNative(pFilter); return pFilter->WriteZeroSizedRecord(TAG_UP); #else return FALSE; #endif } /******************************************************************************************** > BOOL NodeBlender::WriteBeginChildRecordsWeb(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 11/5/99 Inputs: pFilter = ptr to the filter to write to Outputs: - Returns: TRUE ok, FALSE otherwise Purpose: Same as the Native version SeeAlso: WritePreChildrenNative() ********************************************************************************************/ BOOL NodeBlender::WriteBeginChildRecordsWeb(BaseCamelotFilter* pFilter) { return WriteBeginChildRecordsNative(pFilter); } /******************************************************************************************** > BOOL NodeBlender::WriteEndChildRecordsWeb(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 11/5/99 Inputs: pFilter = ptr to the filter to write to Outputs: - Returns: TRUE ok, FALSE otherwise Purpose: Same as the Native version SeeAlso: WritePreChildrenNative() ********************************************************************************************/ BOOL NodeBlender::WriteEndChildRecordsWeb(BaseCamelotFilter* pFilter) { return WriteEndChildRecordsNative(pFilter); } //-------------------------------------------------------------- //-------------------------------------------------------------- //-------------------------------------------------------------- /******************************************************************************************** > NodeBlend* NodeBlender::GetNodeBlend() Author: Mark_Neves (Xara Group Ltd) Created: 21/5/99 Inputs: - Returns: ptr to the blend that this blender belongs to, or NULL if it's all gone pear shaped Purpose: Central NodeBlend "getter" SeeAlso: - ********************************************************************************************/ NodeBlend* NodeBlender::GetNodeBlend() { Node* pNode = FindParent(); if (pNode != NULL && IS_A(pNode,NodeBlend)) return (NodeBlend*)pNode; return NULL; } /******************************************************************************************** > NodeBlendPath* NodeBlender::GetNodeBlendPath() Author: Mark_Neves (Xara Group Ltd) Created: 30/4/99 Inputs: - Returns: ptr to the blend path for this blender, or NULL if it fails Purpose: It gets the node blend path from its parent SeeAlso: - ********************************************************************************************/ NodeBlendPath* NodeBlender::GetNodeBlendPath() { if (m_NodeBlendPathIndex > -1) { NodeBlend* pNode = GetNodeBlend(); if (pNode != NULL) return pNode->GetNodeBlendPath(m_NodeBlendPathIndex); } return NULL; } /******************************************************************************************** > BOOL NodeBlender::GetPointOnNodeBlendPath(DocCoord* pPoint,double BlendRatio,double* pAngle) Author: Mark_Neves (Xara Group Ltd) Created: 4/5/99 Inputs: BlendRatio = the blend ratio to use Outputs: pPoint = ptr to store coord pAngle = angle at this point in degrees (ptr can be NULL) Returns: TRUE if it could find the point on the NodeBlendPath FALSE if there's no path to blend along, or the point-on-path function fails Purpose: Access function to make it easy to get the correct point in the path for this NodeBlender with the given blend ratio SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::GetPointOnNodeBlendPath(double BlendRatio,DocCoord* pPoint,double* pAngle) { ERROR2IF(pPoint == NULL,FALSE,"NULL coord ptr"); BOOL ok = FALSE; NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(); if (pNodeBlendPath) { double PropStart = GetProportionOfPathDistStart(); double PropEnd = GetProportionOfPathDistEnd(); double PathLen = pNodeBlendPath->GetPathLength(); double PropLen = PropEnd-PropStart; double PathLenOffset = PathLen*PropStart; double Tangent = 0.0; MILLIPOINT Dist = MILLIPOINT(((PathLen*PropLen) * BlendRatio)+PathLenOffset); ok = pNodeBlendPath->GetPointAtDistance(Dist,pPoint,&Tangent); if (ok && pAngle) { if (IsTangential()) *pAngle = Tangent/PI*180; else *pAngle = 0.0; } } return ok; } /******************************************************************************************** > BOOL NodeBlender::GetPointFromDistance(double Distance, DocCoord8 pPoint, double* pAngle) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 17/9/99 Inputs: Distance - the distance along the nodeblendpath Outputs: pPoint = ptr to store coord pAngle = angle at this point in degrees (ptr can be NULL) Returns: TRUE if it could find the point on the NodeBlendPath FALSE if there's no path to blend along, or the point-on-path function fails Purpose: Access function to make it easy to get the correct point in the path for this NodeBlender with the given distance along the path. SeeAlso: - ********************************************************************************************/ BOOL NodeBlender::GetPointFromDistance(double Distance, DocCoord* pPoint, double* pAngle) { ERROR2IF(pPoint == NULL,FALSE,"NULL coord ptr"); BOOL ok = FALSE; NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(); if (pNodeBlendPath) { double Tangent = 0.0; ok = pNodeBlendPath->GetPointAtDistance((INT32)Distance,pPoint,&Tangent); if (ok && pAngle) { if (IsTangential()) *pAngle = Tangent/PI*180; else *pAngle = 0.0; } } return ok; } double NodeBlender::GetProportionOfPathDistStart() { if (m_ProportionOfPathDistStart < 0.0) return 0.0; return m_ProportionOfPathDistStart; } double NodeBlender::GetProportionOfPathDistEnd() { if (m_ProportionOfPathDistEnd < 0.0) return 1.0; return m_ProportionOfPathDistEnd; } void NodeBlender::SetProportionOfPathDistStart(double p) { if (p < 0.0) p = 0.0; if (p > 1.0) p = 1.0; if (p > GetProportionOfPathDistEnd()) p = GetProportionOfPathDistEnd(); m_ProportionOfPathDistStart = p; } void NodeBlender::SetProportionOfPathDistEnd(double p) { if (p < 0.0) p = 0.0; if (p > 1.0) p = 1.0; if (p < GetProportionOfPathDistStart()) p = GetProportionOfPathDistStart(); m_ProportionOfPathDistEnd = p; } double NodeBlender::GetAngleStart() { return m_AngleStart; } double NodeBlender::GetAngleEnd() { return m_AngleEnd; } void NodeBlender::SetAngleStart(double Angle) { m_AngleStart = fmod(Angle,360.0); } void NodeBlender::SetAngleEnd(double Angle) { m_AngleEnd = fmod(Angle,360.0); } /******************************************************************************************** > double NodeBlender::GetBlendDistance() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: - Returns: distance between the centre of the start and end nodes Purpose: to get the distance between the start and end nodes of the blender when the blend is not on a curve. Note that it will return an incorrect value if the blend is on a curve. SeeAlso: NodeBlendPath::GetPathLength(); ********************************************************************************************/ double NodeBlender::GetLinearDistance() { DocCoord StartCentre; DocCoord EndCentre; BOOL Valid = GetBlendObjectCentres(&StartCentre, &EndCentre); if (!Valid) return FALSE; Coord pStart = (Coord)StartCentre; Coord pEnd = (Coord)EndCentre; double Distance = pStart.Distance(pEnd); return Distance; } /******************************************************************************************** > BOOL NodeBlender::GetBlendDirection(double* Gradient) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 10/9/99 Inputs: - Outputs: Gradient of the line that the blend follows Returns: TRUE if successful, FALSE otherwise Purpose: To get a vector of the direction of a linear blend. ********************************************************************************************/ BOOL NodeBlender::GetBlendDirection(double* Gradient) { if (IsBlendedOnCurve()) { ERROR3("Trying to get linear direction of blend on a curve"); return FALSE; } DocCoord StartCentre; DocCoord EndCentre; BOOL Valid = GetBlendObjectCentres(&StartCentre, &EndCentre); if (!Valid) return FALSE; double XDiff = EndCentre.x - StartCentre.x; double YDiff = EndCentre.y - StartCentre.y; *Gradient = YDiff/XDiff; return TRUE; } /******************************************************************************************** > BOOL NodeBlender::GetBlendObjectCentres(DocCoord* pFirstCentre, DocCoord* pLastCentre) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 10/9/99 Inputs: - Outputs: Coordinates of the centres of the first and last objects of this blender Returns: TRUE if successful, FALSE otherwise Purpose: To get the centres of the objects that are blended between ********************************************************************************************/ BOOL NodeBlender::GetBlendObjectCentres(DocCoord* pFirstCentre, DocCoord* pEndCentre) { // get the start and end nodes NodeRenderableInk* pBlendStart = GetNodeStart(); NodeRenderableInk* pBlendEnd = GetNodeEnd(); if (pBlendStart ==NULL || pBlendEnd == NULL) { ERROR3IF(pBlendStart ==NULL, "Blender has no start nodes"); ERROR3IF(pBlendEnd ==NULL, "Blender has no end nodes"); return FALSE; } DocRect StartRect = pBlendStart->GetBoundingRect(); DocRect EndRect = pBlendEnd->GetBoundingRect(); DocCoord StartCentre = StartRect.Centre(); DocCoord EndCentre = EndRect.Centre(); *pFirstCentre = StartCentre; *pEndCentre = EndCentre; return TRUE; }