// $Id: nodeblnd.cpp 1688 2006-08-10 12:05:20Z gerry $ /* @@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============================ */ /* */ #include "camtypes.h" #include "nodeblnd.h" //#include "markn.h" //#include "mario.h" //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "ophist.h" //#include "rndrgn.h" #include "aw_eps.h" #include "cameleps.h" #include "objchge.h" #include "nodebldr.h" //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "blobs.h" #include "contmenu.h" #include "blndtool.h" //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "ndbldpth.h" #include "attrmap.h" //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "cxftags.h" //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "rechblnd.h" #include "nodershp.h" //#include "opshadow.h" //#include "opbevel.h" #include "extender.h" #include "ngcore.h" // NameGallery, for stretching functionality #include "brshattr.h" #include "slicehelper.h" DECLARE_SOURCE("$Revision: 1688 $"); CC_IMPLEMENT_DYNAMIC(NodeBlend,NodeGroup) CC_IMPLEMENT_DYNCREATE(InitBlendAction,Action) CC_IMPLEMENT_DYNAMIC(BlendRecordHandler, CamelotRecordHandler) #define new CAM_DEBUG_NEW #define NUM_DEFAULT_BLENDSTEPS 5 BOOL NodeBlend::s_DefaultNotAntialiased = FALSE; NodeBlender* BlendRecordHandler::m_pLastInsertedNodeBlender = NULL; NodeBlend* BlendRecordHandler::m_pLastInsertedNodeBlend = NULL; NodeBlendPath* BlendRecordHandler::m_pLastNodeBlendPath = NULL; CProfileBiasGain BlendRecordHandler::m_LastObjectProfile; CProfileBiasGain BlendRecordHandler::m_LastAttrProfile; CProfileBiasGain BlendRecordHandler::m_LastPositionProfile; BOOL BlendRecordHandler::m_bLoadedProfiles = FALSE; /********************************************************************************************* > NodeBlend::NodeBlend() Author: Mark_Neves (Xara Group Ltd) Created: 6/10/94 Inputs: Outputs: Returns: Purpose: This constructor creates a NodeBlend linked to no other, with all status flags false and an uninitialised bounding rectangle. Errors: **********************************************************************************************/ NodeBlend::NodeBlend(): NodeGroup() { ResetVars(); } /*********************************************************************************************** > void NodeBlend::NodeBlend ( Node* ContextNode, AttachNodeDirection Direction, BOOL Locked = FALSE, BOOL Mangled = FALSE, BOOL Marked = FALSE, BOOL Selected = FALSE, ) Author: Mark_Neves (Xara Group Ltd) Created: 6/10/94 Inputs: ContextNode: Pointer to a node which this node is to be attached to. Direction: Specifies the direction in which this 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 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 ? Outputs: - Returns: - Purpose: This method initialises the node and links it to ContextNode in the direction specified by Direction. All necessary tree links are updated. Errors: An assertion error will occur if ContextNode is NULL ***********************************************************************************************/ NodeBlend::NodeBlend(Node* ContextNode, AttachNodeDirection Direction, BOOL Locked, BOOL Mangled, BOOL Marked, BOOL Selected ):NodeGroup(ContextNode, Direction, Locked, Mangled, Marked, Selected) { ResetVars(); } /********************************************************************************************* > void NodeBlend::ResetVars() Author: Mark_Neves (Xara Group Ltd) Created: 6/10/94 Inputs: Outputs: Returns: Purpose: This sets all the member vars to the state they should be in on construction. All constructors should call this func. Errors: **********************************************************************************************/ void NodeBlend::ResetVars() { m_NumBlendSteps = NUM_DEFAULT_BLENDSTEPS; m_OneToOne = FALSE; m_NotAntialiased = s_DefaultNotAntialiased; m_ColBlendType = COLOURBLEND_FADE; m_Tangential = FALSE; m_BlendedOnCurve = FALSE; m_Edit = EDIT_STEPS; m_DistanceEntered = -1; m_LastEdited = NONE; m_NumNodeBlendPaths = 0; objectProfileProcessing = FALSE; UpdateStepDistance(); } /******************************************************************************************** > virtual String NodeBlend::Describe(BOOL Plural, BOOL Verbose = TRUE) Author: Mark_Neves (Xara Group Ltd) Created: 6/10/94 Inputs: Plural: Flag indicating if the string description should be plural or singular. Outputs: - Retuns: Description of the blend node Purpose: To return a description of the Blend object in either the singular or the plural. This method is called by the DescribeRange method. The description will always begin with a lower case letter. Errors: - SeeAlso: - ********************************************************************************************/ String NodeBlend::Describe(BOOL Plural, BOOL Verbose) { if (Plural) return(String(_R(IDS_BLEND_DESCRP))); else return(String(_R(IDS_BLEND_DESCRS))); }; /*********************************************************************************************** > Node* NodeBlend::SimpleCopy() Author: Mark_Neves (Xara Group Ltd) Created: 6/10/94 Inputs: - Outputs: - Returns: A copy of the node, or NULL if memory has run out Purpose: This method returns a shallow copy of the node with all Node pointers NULL. The function is virtual, and must be defined for all derived classes. Errors: If memory runs out when trying to copy, then ERROR is called with an out of memory error and the function returns NULL. **********************************************************************************************/ Node* NodeBlend::SimpleCopy() { NodeBlend* pCopyOfNode = new NodeBlend(); ERROR1IF(pCopyOfNode == NULL,NULL,_R(IDE_NOMORE_MEMORY)); CopyNodeContents(pCopyOfNode); return (pCopyOfNode); } /*********************************************************************************************** > void NodeBlend::CopyNodeContents(Node* pCopyOfNode) Author: Mark_Neves (Xara Group Ltd) Created: 11/10/94 Inputs: pCopyOfNode - The node to copy data to Outputs: - Returns: - Purpose: Copies the data from 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: NodeGroup::CopyNodeContents ***********************************************************************************************/ void NodeBlend::CopyNodeContents(NodeBlend* pCopyOfNode) { NodeGroup::CopyNodeContents(pCopyOfNode); // Copy member vars here pCopyOfNode->m_NumBlendSteps = m_NumBlendSteps; pCopyOfNode->m_OneToOne = m_OneToOne; pCopyOfNode->m_NotAntialiased = m_NotAntialiased; pCopyOfNode->m_ColBlendType = m_ColBlendType; pCopyOfNode->m_Tangential = m_Tangential; pCopyOfNode->m_BlendedOnCurve = m_BlendedOnCurve; pCopyOfNode->m_Edit = m_Edit; pCopyOfNode->m_DistanceEntered = m_DistanceEntered; pCopyOfNode->m_NumNodeBlendPaths = m_NumNodeBlendPaths; pCopyOfNode->m_ObjectProfile = m_ObjectProfile; pCopyOfNode->m_AttrProfile = m_AttrProfile; pCopyOfNode->m_PositionProfile = m_PositionProfile; } /*********************************************************************************************** > void NodeBlend::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 NodeBlend::PolyCopyNodeContents(NodeRenderable* pNodeCopy) { ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node"); ENSURE(IS_A(pNodeCopy, NodeBlend), "PolyCopyNodeContents given wrong dest node type"); if (IS_A(pNodeCopy, NodeBlend)) CopyNodeContents((NodeBlend*)pNodeCopy); } /******************************************************************************************** > virtual UINT32 NodeBlend::GetNodeSize() const Author: Mark_Neves (Xara Group Ltd) Created: 6/10/94 Inputs: - Outputs: - Returns: The size of the node in bytes Purpose: For finding the size of the node SeeAlso: Node::GetSubtreeSize ********************************************************************************************/ UINT32 NodeBlend::GetNodeSize() const { return (sizeof(NodeBlend)); } /******************************************************************************************** > BOOL NodeBlend::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread ) Author: Mark_Neves (Xara Group Ltd) Created: 6/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: TRUE if the node claims the click as its own and FALSE if it is not interested in the click Purpose: Does nothing at the moment - Just returns FALSE. ********************************************************************************************/ BOOL NodeBlend::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread ) { // we did not use the click, so let someone else try return FALSE; } /*********************************************************************************************** > BOOL NodeBlend::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 NodeBlend::HidingNode() { // Call the base class first if (!NodeGroup::HidingNode()) return FALSE; BOOL ok = TRUE; Node* pNode = FindFirstChild(); while (pNode != NULL && ok) { ok = pNode->HidingNode(); pNode = pNode->FindNext(); } return ok; } /*********************************************************************************************** > BOOL NodeBlend::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 NodeBlend::ShowingNode() { // Call the base class first if (!NodeGroup::ShowingNode()) return FALSE; // This doesn't need to be done!!! // The caller of ShowingNode recurses through the subtree by itself // BOOL ok = TRUE; // Node* pNode = FindFirstChild(); // while (pNode != NULL && ok) // { // ok = pNode->ShowingNode(); // pNode = pNode->FindNext(); // } // // return ok; return TRUE; } /******************************************************************************************** > virtual BOOL NodeBlend::DoBecomeA(BecomeA* pBecomeA) Author: Mark_Neves (Xara Group Ltd) Created: 18/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. This converts all its children, and replaces itself in the tree with a NodeGroup Errors: - SeeAlso: Node::CanBecomeA ********************************************************************************************/ BOOL NodeBlend::DoBecomeA(BecomeA* pBecomeA) { if (!Reinit()) return FALSE; switch (pBecomeA->GetReason()) { case BECOMEA_REPLACE: { // ERROR2IF(pBecomeA->GetUndoOp() == NULL,FALSE,"Trying to replace a NodeBlend, but pUndoOp == NULL"); UndoableOperation* pUndoOp = pBecomeA->GetUndoOp(); // Make all attrs children of the child nodes if (pUndoOp) { if (!pUndoOp->DoLocaliseCommonAttributes(this)) return FALSE; } else { if (!LocaliseCommonAttributes()) return FALSE; } // If being repaced in the tree, deselect the node BOOL IsSelected = this->IsSelected(); SetSelected(FALSE); // When becoming a new type of node AND replacing ourselves in the tree, we must first ask all the // blender child nodes, then the rest of the children, in that order. // This is because the blender nodes generate new nodes from other children in the tree, so // they have to do their stuff before the other nodes get replaced and hence disappear. // We need a list of all non-blender child nodes std::list ListOfNodes; //(30); // Scan the children of the blend Node* pNode = FindFirstChild(); while (pNode != NULL) { Node* pThisNode = pNode; pNode = pNode->FindNext(); // If this node is a blender, call its DoBecomeA() method // otherwise add the node to the list of non-blender nodes to use later if (IS_A(pThisNode,NodeBlender)) pThisNode->DoBecomeA(pBecomeA); else ListOfNodes.push_back(pThisNode); } while( !ListOfNodes.empty() ) { // Here we must ask all the non-blender nodes to DoBecomeA() pNode = ListOfNodes.back(); ListOfNodes.pop_back(); // CGS: prevent NodeShadowControllers, NodeBevelControllers and NodeCountourControllers from // converting to paths when inside of a blend .... if (!pNode->IsController ()) { pNode->DoBecomeA(pBecomeA); } else { // BUT if we need them converted to paths, then do it .... if (pBecomeA->GetInsertComplexBlendStepsAsPaths ()) { pNode->DoBecomeA(pBecomeA); } } } // All the children have been delt with, so now's the time to deal with this blend node // When replacing the blend with shapes, hide the blend, create a group, and move all the children // so they become children of the new group node // Allocate a new NodeGroup node NodeGroup* pNodeGroup; ALLOC_WITH_FAIL(pNodeGroup, (new NodeGroup), pUndoOp); if (pNodeGroup == NULL) return FALSE; // Insert the NodeGroup where the NodeBlend used to be if (pUndoOp) { if (!pUndoOp->DoInsertNewNode(pNodeGroup,this,NEXT,FALSE,FALSE,FALSE,FALSE)) return FALSE; } else { pNodeGroup->AttachNode(this, NEXT); } // Select the group, but only if the caller wants us too (moulds don't, for example) if (IsSelected && pUndoOp) { if (!pUndoOp->DoSelectNode(pNodeGroup,FALSE)) return FALSE; } // Karim 15/05/2000 - rewrote to handle non-optimising attrs. // Move our children into the new group node. // // NOTE: The IsEffectAttribute function won't work when called from ShouldBeOptimized below // because it looks along the tree for bounded nodes and this function will have removed them // all by the time that call is made. // So bInEffectArea duplicates that logic by being set as soon as any bounded object // is met as we traverse the child list. Not nice... // BOOL ok = TRUE; BOOL bInEffectArea = FALSE; Node* pNextKid = NULL; Node* pThisKid = FindFirstChild(); while (ok && pThisKid != NULL) { pNextKid = pThisKid->FindNext(); if (pThisKid->IsBounded()) bInEffectArea = TRUE; // ink nodes just get moved. if (pThisKid->IsAnObject()) { if (pUndoOp) { CALL_WITH_FAIL(pUndoOp->DoMoveNode(pThisKid, pNodeGroup, LASTCHILD), pUndoOp, ok) } else { pThisKid->MoveNode(pNodeGroup, LASTCHILD); } } // non-optimising attrs just get copied, as DoMoveNode() doesn't like them. else if (pThisKid->IsAnAttribute() && (!((NodeAttribute*)pThisKid)->ShouldBeOptimized() || bInEffectArea) ) CALL_WITH_FAIL(pThisKid->CopyNode(pNodeGroup, LASTCHILD), pUndoOp, ok) pThisKid = pNextKid; } if (!ok) return FALSE; // Hide the blend node NodeHidden* pNodeHidden; if (pUndoOp) { if(!pUndoOp->DoHideNode(this, FALSE, &pNodeHidden, FALSE)) return FALSE; } else { CascadeDelete(); delete this; } // Factor out the attrs (needed after a call to DoLocaliseCommonAttributes if (pUndoOp) { if (!pUndoOp->DoFactorOutCommonChildAttributes(pNodeGroup)) return FALSE; } else { if (!pNodeGroup->FactorOutCommonChildAttributes()) return FALSE; } } break; case BECOMEA_PASSBACK: // the following case is executed when doing the blend of two grouped blends { // Sequentially ask the children of the blend to DoBecomeA // This is all that's required because the child objects are only passing back // the new node type, and NOT replacing themselves in the tree // This also ensures the receipient gets the list of paths in render order Node* pNode = FindFirstChild(); while (pNode != NULL) { if (!pNode->DoBecomeA(pBecomeA)) return FALSE; pNode = pNode->FindNext(); } } break; default: ERROR3_PF(("Unknown BecomeA reason %d",pBecomeA->GetReason())); break; } return TRUE; } /******************************************************************************************** > void NodeBlend::SetNumBlendSteps(UINT32 NumSteps) Author: Mark_Neves (Xara Group Ltd) Created: 4/11/94 Inputs: NumSteps = num steps to set in this blend Outputs: - Returns: - Purpose: Sets the number of blend steps in this blend SeeAlso: - ********************************************************************************************/ void NodeBlend::SetNumBlendSteps(UINT32 NumSteps) { m_NumBlendSteps = NumSteps; } /******************************************************************************************** > UINT32 NodeBlend::GetNumBlendSteps() Author: Mark_Neves (Xara Group Ltd) Created: 4/11/94 Inputs: - Outputs: - Returns: Num blend steps in this blend Purpose: Gets the number of blend steps in this blend SeeAlso: - ********************************************************************************************/ UINT32 NodeBlend::GetNumBlendSteps() { return m_NumBlendSteps; } /******************************************************************************************** > void NodeBlend::SetColourBlendType(ColourBlendType Type) Author: Mark_Neves (Xara Group Ltd) Created: 17/3/95 Inputs: Type = the way colours are blended (i.e. fade, rainbow, or alt rainbow) Outputs: - Returns: - Purpose: Sets up the way this blend blends colours SeeAlso: - ********************************************************************************************/ void NodeBlend::SetColourBlendType(ColourBlendType Type) { m_ColBlendType = Type; } /******************************************************************************************** > ColourBlendType NodeBlend::GetColourBlendType() Author: Mark_Neves (Xara Group Ltd) Created: 17/3/95 Inputs: - Outputs: - Returns: The way the blend blends colours Purpose: How exactly will this blend blend colours? Use this func to find out. SeeAlso: - ********************************************************************************************/ ColourBlendType NodeBlend::GetColourBlendType() { return m_ColBlendType; } /******************************************************************************************** > BOOL NodeBlend::IsOneToOne() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Inputs: - Outputs: - Returns: TRUE if blend should be done using one-to-one mapping Purpose: Get the state of the OneToOne flag for this blend SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::IsOneToOne() { return m_OneToOne; } /******************************************************************************************** > void NodeBlend::SetOneToOne(BOOL state) Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Inputs: state = TRUE if blend should be done using one-to-one Outputs: - Returns: - Purpose: Set the state of the OneToOne flag for this blend SeeAlso: - ********************************************************************************************/ void NodeBlend::SetOneToOne(BOOL state) { m_OneToOne = state; } /******************************************************************************************** > BOOL NodeBlend::IsNotAntialiased() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Inputs: - Outputs: - Returns: TRUE if the blend steps should NOT be rendered anti-aliased Purpose: Get the state of the NotAntialiased flag for this blend SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::IsNotAntialiased() { return m_NotAntialiased; } /******************************************************************************************** > void NodeBlend::SetNotAntialiased(BOOL state) Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Inputs: state = TRUE if the blend steps should NOT be rendered anti-aliased Outputs: - Returns: - Purpose: Set the state of the NotAntialiased flag for this blend SeeAlso: - ********************************************************************************************/ void NodeBlend::SetNotAntialiased(BOOL state) { m_NotAntialiased = state; s_DefaultNotAntialiased = state; } /******************************************************************************************** > BOOL NodeBlend::IsTangential() Author: Mark_Neves (Xara Group Ltd) Created: 20/5/99 Inputs: - Outputs: - Returns: TRUE if it blends on a curve tangentially Purpose: Get the state of the m_Tangential flag for this blend SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::IsTangential() { return m_Tangential; } /******************************************************************************************** > void NodeBlend::SetTangential(BOOL state) Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Inputs: state = TRUE if it blends on a curve tangentially Outputs: - Returns: - Purpose: Set the state of the m_Tangential flag for this blend SeeAlso: - ********************************************************************************************/ void NodeBlend::SetTangential(BOOL state) { m_Tangential = state; } /******************************************************************************************** > BOOL NodeBlend::IsOnACurve() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 23/9/99 Inputs: - Outputs: - Returns: TRUE if it blends on a curve Purpose: Get the state of the m_blendedoncurve flag SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::IsOnACurve() { return m_BlendedOnCurve; } /******************************************************************************************** > void NodeBlend::SetBlendedOncurve(BOOL state) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 23/9/99 Inputs: state = TRUE if it blends on a curve Outputs: - Returns: - Purpose: Set the state of the m_BlendedOnCurve flag for this blend SeeAlso: - ********************************************************************************************/ void NodeBlend::SetBlendedOnCurve(BOOL state) { m_BlendedOnCurve = state; } /******************************************************************************************** > EditState NodeBlend::GetEditState() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 27/9/99 Inputs: - Outputs: - Returns: The current state of the m_Edit flag Purpose: lets us know whether to edit the number of steps, or the distance between steps SeeAlso: - ********************************************************************************************/ EditState NodeBlend::GetEditState() { return m_Edit; } /******************************************************************************************** > void NodeBlend::SetEditType(EditType TypeToEdit) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 27/9/99 Inputs: the type to edit (can be EDIT_DISTANCE or EDIT_STEPS) Outputs: - Returns: - Purpose: sets the type to edit according to which button is selected on the infobar. Also initialises the m_DistanceEntered member if it has not already been initialised. This is in case the user selected distance edit but does not enter a value. In this case, if the user has not selected a previous value I assume that they wish to keep the value generated. SeeAlso: - ********************************************************************************************/ void NodeBlend::SetEditState(EditState State) { m_Edit = State; if (m_DistanceEntered == -1.0) { // first update in case something has changed UpdateStepDistance(); m_DistanceEntered = GetStepDistance(); } } /******************************************************************************************** > BOOL NodeBlend::IsChildOfGroup() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 21/9/99 Inputs: - Outputs: - Returns: TRUE if this is a child of a nodegroup, FALSE otherwise Purpose: to determine whether this nodeblend is part of a nodegroup SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::IsChildOfGroup() { Node* pParent = ((Node*)this)->FindParent(CC_RUNTIME_CLASS(NodeGroup)); if (pParent == NULL) { // ERROR3("This blend has no parent"); return FALSE; } return TRUE; /* if (pParent->IS_KIND_OF(NodeBevelController) || pParent->IS_KIND_OF(NodeContourController) || pParent->IS_KIND_OF(NodeShadowController)) return FALSE; else if (pParent->IS_KIND_OF(NodeGroup)) return TRUE return FALSE;*/ } /******************************************************************************************** > BOOL NodeBlend::ContainsBrushedNode() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 21/9/99 Inputs: - Outputs: - Returns: TRUE if this blend contains an object with a brush attribute Purpose: as above SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::ContainsBrushedNode() { Node* pNode = FindFirstChild(); NodeAttribute* pAttr = NULL; while (pNode != NULL) { if (pNode->IsAnObject()) { ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr); if (pAttr != NULL) { AttrBrushType* pAttrBrush = (AttrBrushType*)pAttr; if (pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush) return TRUE; } } pNode = pNode->FindNext(); } return FALSE; } /******************************************************************************************** > UINT32 NodeBlend::GetNumBlenders() Author: Mark_Neves (Xara Group Ltd) Created: 21/11/94 Inputs: - Outputs: - Returns: Num child NodeBlenders for this blend Purpose: This counts the number of child nodes that are NodeBlenders SeeAlso: - ********************************************************************************************/ UINT32 NodeBlend::GetNumBlenders() { UINT32 Count = 0; Node* pNode = FindFirstChild(); while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) Count++; pNode = pNode->FindNext(); } return Count; } /******************************************************************************************** > BOOL NodeBlend::Reinit(BOOL ProgressBar) Author: Mark_Neves (Xara Group Ltd) Created: 26/1/95 Inputs: ProgressBar = if TRUE, a progress bar will be displayed Outputs: - Returns: TRUE if all child blenders reinit OK Purpose: This calls Reinit() on all child blenders. SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::Reinit(BOOL ProgressBar) { Node* pNode = FindFirstChild(); while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) { if (!((NodeBlender*)pNode)->Reinit(NULL,NULL,ProgressBar)) return FALSE; } pNode = pNode->FindNext(); } return TRUE; } /******************************************************************************************** > void NodeBlend::Deinit(BOOL bNodesMayBeChanged = FALSE) Author: Mark_Neves (Xara Group Ltd) Created: 6/2/95 Inputs: - Outputs: - Returns: - Purpose: This calls Deinit() on all child blenders. SeeAlso: - ********************************************************************************************/ void NodeBlend::Deinit(BOOL bNodesMayBeChanged) { Node* pNode = FindFirstChild(); while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) ((NodeBlender*)pNode)->Deinit(bNodesMayBeChanged); pNode = pNode->FindNext(); } } /******************************************************************************************** > BOOL NodeBlend::PostImport() Author: Mark_Neves (Xara Group Ltd) Created: 23/5/95 Inputs: - Outputs: - Returns: - Purpose: This is called after the entire doc has been imported It first deinits the blend (in case they are initialised currently), then reinits the blend, returning the result. SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::PostImport() { Deinit(); InvalidateBlendBoundingRect(); return Reinit(FALSE); } /******************************************************************************************** > void NodeBlend::InvalidateBlendBoundingRect() Author: Mark_Neves (Xara Group Ltd) Created: 9/2/95 Inputs: - Outputs: - Returns: - Purpose: This calls InvalidateBoundingRect() on all bounded children of this blend. SeeAlso: - ********************************************************************************************/ void NodeBlend::InvalidateBlendBoundingRect() { Node* pNode = FindFirstChild(); while (pNode != NULL) { if (pNode->IsBounded()) ((NodeRenderableBounded*)pNode)->InvalidateBoundingRect(); pNode = pNode->FindNext(); } } /******************************************************************************************** > DocRect NodeBlend::GetBlobBoundingRect() Author: Mark_Neves (Xara Group Ltd) Created: 6/3/95 Returns: DocRect - The bounding rect of the node and its blobs Purpose: This calls the base classes GetBlobBoundingRect(), and inflates the result by the width of a blob ********************************************************************************************/ DocRect NodeBlend::GetBlobBoundingRect() { #if !defined(EXCLUDE_FROM_RALPH) // Find the base class blob bounding rectangle DocRect Rect = NodeGroup::GetBlobBoundingRect(); // inflate it by the width of a blob (plus a bit) DocRect TempRect; GetApplication()->GetBlobManager()->GetBlobRect(Rect.lo,&TempRect); INT32 Delta = ((TempRect.hi.x - TempRect.lo.x)*1)/1; Rect.Inflate(Delta); return Rect; #else return DocRect(0,0,0,0); #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 NodeBlend::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest) { if (IsBoundingRectValid && !DontUseAttrs) return BoundingRectangle; // NOTE! NodeGroup:;GetBoundingRect doesn't set BoundingRectangle if DontUseAttrs is TRUE // It returns a computed value instead. // So this routine must use the return value from calling NodeGroup::GetBoundingRectangle // to return a correct value. return NodeGroup::GetBoundingRect(DontUseAttrs, HitTest); } /******************************************************************************************** > void NodeBlend::RenderObjectBlobs(RenderRegion* pRender) Author: Mark_Neves (Xara Group Ltd) Created: 6/3/95 Inputs: pRender - The region to draw the blobs in Purpose: Renders the object blobs ********************************************************************************************/ void NodeBlend::RenderObjectBlobs(RenderRegion* pRegion) { #if !defined(EXCLUDE_FROM_RALPH) // Find out about the groups bounding rect DocRect BoundingRect = GetBoundingRect(); // Inflate the bounds by the width of a blob DocRect TempRect; GetApplication()->GetBlobManager()->GetBlobRect(BoundingRect.lo,&TempRect); INT32 Delta = ((TempRect.hi.x - TempRect.lo.x)*3)/4; BoundingRect.Inflate(Delta); // Find out where to draw the blobs DocCoord Low = BoundingRect.LowCorner(); DocCoord High = BoundingRect.HighCorner(); // Set the colours of the blobs pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB); pRegion->SetLineColour(COLOUR_NONE); // Draw all the blobs pRegion->DrawBlob(Low, BT_UNSELECTED); pRegion->DrawBlob(High, BT_UNSELECTED); pRegion->DrawBlob(DocCoord(Low.x, High.y), BT_UNSELECTED); pRegion->DrawBlob(DocCoord(High.x, Low.y), BT_UNSELECTED); /* NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(); if (pNodeBlendPath) pNodeBlendPath->RenderObjectBlobs(pRegion); */ #endif } /******************************************************************************************** > virtual void NodeBlend::RenderBlendBlobs(pRender) Author: Mark_Neves (Xara Group Ltd) Created: 9/11/94 Inputs: pRender = render region to render into Outputs: - Returns: - Purpose: This renders all the blobs associated with this blend node This node doesn't do any rendering itself. All it does is ask its child blender nodes to render their blend blobs SeeAlso: - ********************************************************************************************/ void NodeBlend::RenderBlendBlobs(RenderRegion* pRender) { #if !defined(EXCLUDE_FROM_RALPH) // The problem we have with this routine is that two adjacent blenders will reference the same // object, hence if we ask all blenders to render blobs for both its objects, blobs will get // rendered twice, EORing themselves off again. // So, we ask all but the last blender to just render the start object, then get the last one // to render both. Node* pLast = NULL; // Find the last blender Node* pNode = FindFirstChild(); while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) pLast = pNode; pNode = pNode->FindNext(); } // Render the blender blobs pNode = FindFirstChild(); // altered by Diccon 3/9/99 to render the blobs of a blend on a path differently // so that it is apparent that the position of the end objects can be altered. // probably deserves a separate function but for now.. if (m_BlendedOnCurve == FALSE) { while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) { BOOL RenderStart = TRUE; BOOL RenderEnd = FALSE; if (pNode == pLast) RenderEnd = TRUE; ((NodeBlender*)pNode)->RenderBlendBlobs(pRender,RenderStart,RenderEnd); } pNode = pNode->FindNext(); } } else RenderBlendOnCurveBlobs(pRender); #endif } /******************************************************************************************** > virtual void NodeBlend::RenderBlendOnCurveBlobs(RenderRegion* pRender) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 21/9/99 Inputs: pRender = render region to render into Outputs: - Returns: - Purpose: This renders all the blobs associated with this blend node if it is on a curve. It needs to render blobs on the editable nodes, i.e. if the whole thing is on a curve then the first and last nodes are editable. if a blend on curve is blended to a straight blend then only the end on the curve is editable. SeeAlso: - ********************************************************************************************/ void NodeBlend::RenderBlendOnCurveBlobs(RenderRegion* pRegion) { NodeBlender* pNodeBlender = FindFirstBlender(); while (pNodeBlender != NULL) { NodeBlender* pNextBlender = FindNextBlender(pNodeBlender); if (pNodeBlender->IsBlendedOnCurve()) { Node* pStart = pNodeBlender->GetNodeStart(); Node* pEnd = pNodeBlender->GetNodeEnd(); if (pStart == NULL || pEnd == NULL) { ERROR3("This blender has no start and end"); return; } else { BOOL bFinish = FALSE; Node* pNodeToRender = pStart; DocCoord FirstPoint(0,0); while (bFinish == FALSE) { if (pNodeToRender->IsABaseTextClass()) { pRegion->SetLineColour(COLOUR_BEZIERBLOB); pRegion->SetFillColour(COLOUR_BEZIERBLOB); } else { pRegion->SetLineColour(COLOUR_UNSELECTEDBLOB); pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB); } // Draw a blob at the centre point DocRect BlobSize; BlobManager* pBlobMgr = GetApplication()->GetBlobManager(); if (pBlobMgr != NULL) { DocRect BoundingRect = ((NodeRenderableBounded*)pNodeToRender)->GetBoundingRect(); DocCoord Point = BoundingRect.Centre(); // we may be blended on a circle in which case the start and end nodes will be in the same // place. In this case only do the first one if (pNodeToRender == pStart) FirstPoint = Point; else { // in fact as it turns out they may not be exactly in the same place, but very close double Distance = Point.Distance((Coord)FirstPoint); if (Distance < 10) break; } pBlobMgr->GetBlobRect(Point, &BlobSize); if (pNodeToRender->IsABaseTextClass()) { pRegion->DrawBlob(Point, BT_UNSELECTED); } else { pRegion->DrawLine(DocCoord(BlobSize.hi.x, BlobSize.hi.y), DocCoord(BlobSize.lo.x, BlobSize.lo.y)); pRegion->DrawLine(DocCoord(BlobSize.lo.x, BlobSize.hi.y), DocCoord(BlobSize.hi.x, BlobSize.lo.y)); pRegion->DrawPixel(DocCoord(BlobSize.hi.x, BlobSize.lo.y)); pRegion->DrawPixel(DocCoord(BlobSize.lo.x, BlobSize.lo.y)); } } if (pNodeToRender == pStart && pNextBlender == NULL) pNodeToRender = pEnd; else bFinish = TRUE; } } // end while } // End if on curve pNodeBlender = pNextBlender; } return; } /* NodeBlender* pFirstBlender = NULL; NodeBlender* pLastBlender = NULL; BOOL ok = FindFirstAndLastBlenders(&pFirstBlender, &pLastBlender); if (!ok) { ERROR3("Couldn't get first and last blenders"); return; } Node* pFirstNode = NULL; Node* pLastNode = NULL; ok = GetStartAndEndNodes(&pFirstNode, &pLastNode); if (!ok) { ERROR3("Couldn't get first and last nodes"); return; } // aah, the poetry of C++ NodeBlender* pBlenderToRender = pFirstBlender; Node* pNodeToRender = pFirstNode; if (pBlenderToRender == NULL || pNodeToRender == NULL) return; BOOL bFinish = FALSE; while (bFinish == FALSE) { if (pBlenderToRender->IsBlendedOnCurve()) { // if it is a regular shape then that class can do it for us if (IS_A(pNodeToRender, NodeRegularShape)) ((NodeRegularShape*)pNodeToRender)->RenderObjectBlobs(pRegion); // if its a group or other type we need to do it by hand. If it is text then we want // a red rectangle instead of a black cross else if (pNodeToRender->IsCompound()) { if (pNodeToRender->IsABaseTextClass()) { pRegion->SetLineColour(COLOUR_BEZIERBLOB); pRegion->SetFillColour(COLOUR_BEZIERBLOB); } else { pRegion->SetLineColour(COLOUR_UNSELECTEDBLOB); pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB); } // Draw a blob at the centre point DocRect BlobSize; BlobManager* pBlobMgr = GetApplication()->GetBlobManager(); if (pBlobMgr != NULL) { DocRect BoundingRect = ((NodeRenderableBounded*)pNodeToRender)->GetBoundingRect(); DocCoord Point = BoundingRect.Centre(); pBlobMgr->GetBlobRect(Point, &BlobSize); if (pNodeToRender->IsABaseTextClass()) { pRegion->DrawBlob(Point, BT_UNSELECTED); } else { pRegion->DrawLine(DocCoord(BlobSize.hix, BlobSize.hiy), DocCoord(BlobSize.lox, BlobSize.loy)); pRegion->DrawLine(DocCoord(BlobSize.lox, BlobSize.hiy), DocCoord(BlobSize.hix, BlobSize.loy)); pRegion->DrawPixel(DocCoord(BlobSize.hix, BlobSize.loy)); pRegion->DrawPixel(DocCoord(BlobSize.lox, BlobSize.loy)); } } } } if (pNodeToRender == pFirstNode) pNodeToRender = pLastNode; else bFinish = TRUE; } }*/ /******************************************************************************************** > BOOL NodeBlend::IsPointOverBlob( DocCoord* pPointerPos, BlendPath** ppBlendPath, INT32* pIndex, BOOL* pAStart, UINT32* pRemapRef) 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 bool indicating start or end object flag pRemapRef = ptr to place remap ref that's passed into NodeBlend::Remap() Outputs: *ppBlendPath & *pIndex & *pAStart & *pRemapRef updated if TRUE returned, undefined otherwise Returns: TRUE if match found, FALSE otherwise Purpose: This asks all its child blenders 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 blob belongs to start node, FALSE if end node. Also, if found,*pRemapRef contains a value that should be passed into NodeBlend::Remap() 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 NodeBlend::IsPointOverBlob(DocCoord* pPointerPos, BlendPath** ppBlendPath, INT32* pIndex, BOOL* pAStart, UINT32* pRemapRef) { if (pPointerPos == NULL) return FALSE; #if !defined(EXCLUDE_FROM_RALPH) BOOL Found = FALSE; Node* pNode = FindFirstChild(); while (pNode != NULL && !Found) { if (IS_A(pNode,NodeBlender)) { Found = ((NodeBlender*)pNode)->IsPointOverBlob(pPointerPos,ppBlendPath,pIndex,pAStart); if (Found) *pRemapRef = pNode->GetTag(); } pNode = pNode->FindNext(); } return Found; #else return FALSE; #endif } /******************************************************************************************** > BOOL NodeBlend::Remap(UINT32 RemapRef,DocCoord PosStart,DocCoord PosEnd, DocCoord * pInvPosStart,DocCoord * pInvPosEnd) Author: Mark_Neves (Xara Group Ltd) Created: 10/11/94 Inputs: RemapRef = special remap ref value allowing blend to identify blender to remap 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: This will ask the child blender referenced by RemapRef to remap its blend paths with the given params. If the child blender manages to remap a pair of blend paths, then this will return TRUE. SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::Remap(UINT32 RemapRef,DocCoord PosStart,DocCoord PosEnd,DocCoord* pInvPosStart,DocCoord* pInvPosEnd) { BOOL Mapped = FALSE; Node* pNode = FindFirstChild(); while (pNode != NULL && !Mapped) { if (pNode->GetTag() == RemapRef && IS_A(pNode,NodeBlender)) Mapped = ((NodeBlender*)pNode)->Remap(PosStart,PosEnd,pInvPosStart,pInvPosEnd); pNode = pNode->FindNext(); } return Mapped; } //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Export code... /********************************************************************************************* > void NodeBlend::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 blend" command. Supports ArtWorks EPS and Camelot EPS Errors: **********************************************************************************************/ void NodeBlend::PreExportRender(RenderRegion* pRegion) { #ifdef DO_EXPORT if (IS_A(pRegion, CamelotEPSRenderRegion)) // We ust want the paths in EPS. return; if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion)) { EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC(); if (pRegion->IS_KIND_OF(CamelotEPSRenderRegion)) { // Output the Camelot format for blends pDC->OutputValue(INT32(0)); // Reserved INT32 1 pDC->OutputValue(INT32(0)); // Reserved INT32 2 pDC->OutputValue(INT32(0)); // Reserved INT32 3 pDC->OutputValue(INT32(m_ColBlendType)); // Colour blend type (fade, rainbow or alt rainbow) pDC->OutputValue(INT32(m_OneToOne)); // The "blend using a one to one mapping" flag pDC->OutputValue(INT32(m_NotAntialiased)); // The "don't antialias blend steps" flag pDC->OutputValue(INT32(m_NumBlendSteps)); // Num blend steps pDC->OutputValue(INT32(1)); // Version (Important that this is the last param!!!) pDC->OutputToken(_T("csbd")); // Camelot "start blend" token pDC->OutputNewLine(); } else { // Use a cached flag set up by the next call to IsArtWorksEPSCompatible() // The same function is called by every child blender, so we don't want to do this (possibly) // time consuming task more than once per blend. m_AWEPSCompatibleCache = FALSE; if (IsArtWorksEPSCompatible()) { // Output the AW format for blends, if this blend is ArtWorks-compatible pDC->OutputValue(INT32(1)); // Version pDC->OutputValue(INT32(0)); // Expanded flag pDC->OutputValue(GetNumInkObjects()); // Num objects we are blending pDC->OutputToken(_T("asbd")); // ArtWorks "start blend" token pDC->OutputNewLine(); } } } #endif } /********************************************************************************************* > BOOL NodeBlend::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 afterCamelotEPSFilter this node and all of its children have been rendered to the export region. This outputs the "end blend" command. Supports ArtWorks EPS and Camelot EPS Errors: **********************************************************************************************/ BOOL NodeBlend::ExportRender(RenderRegion* pRegion) { #ifdef DO_EXPORT if (IS_A(pRegion, CamelotEPSRenderRegion)) // We just want the paths in EPS. return FALSE; if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion)) { EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC(); if (pRegion->IS_KIND_OF(CamelotEPSRenderRegion)) { pDC->OutputToken(_T("cebd")); // Camelot "end blend" token pDC->OutputNewLine(); } else if (IsArtWorksEPSCompatible()) { pDC->OutputToken(_T("aebd")); // ArtWorks "end blend" 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 blend is AW compatible Purpose: This asks all the child node blenders if they are AW compatible, and returns TRUE if ALL of them are AW compatible. If the m_AWEPSCompatibleCache flag is TRUE, the current setting of m_AWEPSCompatible is returned, else it is recalculated (setting m_AWEPSCompatibleCache to TRUE in the process) A NodeBlend is only AWEPS compatible if the colour blend type is FADE (i.e. not rainbow or alt rainbow) See NodeBlender::IsArtWorksEPSCompatible() for the other things that must be true for this blend to be AWEPS compatible Errors: SeeAlso: NodeBlender::IsArtWorksEPSCompatible() **********************************************************************************************/ BOOL NodeBlend::IsArtWorksEPSCompatible() { if (!m_AWEPSCompatibleCache) { m_AWEPSCompatible = (m_ColBlendType == COLOURBLEND_FADE); Node* pNode = FindFirstChild(); while (pNode != NULL && m_AWEPSCompatible) { if (IS_A(pNode,NodeBlender)) m_AWEPSCompatible = ((NodeBlender*)pNode)->IsArtWorksEPSCompatible(); pNode = pNode->FindNext(); } m_AWEPSCompatibleCache = TRUE; } return m_AWEPSCompatible; } /********************************************************************************************* > INT32 NodeBlend::GetNumInkObjects() Author: Mark_Neves (Xara Group Ltd) Created: 28/11/94 Inputs: Outputs: Returns: Returns the number of child ink objects that are NOT blender nodes Purpose: Used by the AW EPS blend export code to store the number of shapes this blend is blending. Errors: **********************************************************************************************/ INT32 NodeBlend::GetNumInkObjects() { INT32 Count = 0; Node* pNode = FindFirstChild(); while (pNode != NULL) { if (!IS_A(pNode,NodeBlender) && pNode->IS_KIND_OF(NodeRenderableInk)) Count++; pNode = pNode->FindNext(); } return Count; } //---------------------------------------------------------------------------- /******************************************************************************************** > virtual BOOL NodeBlend::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState = TRUE, BOOL DoPreTriggerEdit = TRUE) Author: Ed_Cornes (Xara Group Ltd) ; Karim 20/01/2000 Created: 6/5/95 Inputs: pParam = describes the way an op wants to change the node SetOpPermissionState = if TRUE the Op permission state of this node will be set according to the outcome of the call DoPreTriggerEdit = if TRUE then calls NameGallery::PreTriggerEdit. *Must* be TRUE if the calling Op may make any nodes change their bounds, eg move, line width, cut. Use TRUE if unsure. Outputs: - Returns: TRUE means the node and all its parents are happy with this op, FALSE means don't do it Purpose: In this instance, the func gives the blend a chance to stop an op from happening to one of its children. Current implementation specs that blends will only allow you to change the appearence of a blend's child (e.g. change colour,translate, edit path). This must be called *before* the op is performed. SeeAlso: Node::AllowOp(),GetOpPermission(),SetOpPermission(); ********************************************************************************************/ BOOL NodeBlend::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState, BOOL DoPreTriggerEdit) { ERROR2IF(pParam==NULL,FALSE,"NodeBlend::AllowOp() - pParam==NULL"); // clean out the calling-child ptr, so it doesn't get passed around unintentionally. pParam->SetCallingChild(NULL); // decide if we allow it ... (and if we'll still exist after the op) BOOL allowed=TRUE; ObjChangeFlags Flags=pParam->GetChangeFlags(); if (pParam->GetDirection()==OBJCHANGE_CALLEDBYCHILD) { if (Flags.DeleteNode || Flags.MultiReplaceNode || Flags.MoveNode) { pParam->SetReasonForDenial(_R(IDS_BLEND_CANT_OP_ON_CHILDREN)); allowed=FALSE; } } // if we allow it, (and our parents weren't calling us) see if our parents do ... if (allowed && pParam->GetDirection()!=OBJCHANGE_CALLEDBYPARENT && Parent!=NULL) { ObjChangeDirection OldDirection=pParam->GetDirection(); pParam->SetCallingChild(this); pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD); allowed=Parent->AllowOp(pParam,SetOpPermissionState,DoPreTriggerEdit); pParam->SetDirection(OldDirection); } if (allowed && pParam->GetDirection() == OBJCHANGE_CALLEDBYCHILD) { // Ilan 5/4/00 // Inform attrs attached at compound level that children have changed UndoableOperation* pChangeOp = pParam->GetOpPointer(); BOOL InformGeomLinkedAttrs = SetOpPermissionState && pChangeOp && pChangeOp->MayChangeNodeBounds(); if(InformGeomLinkedAttrs) { NodeAttribute* pNA = FindFirstGeometryLinkedAttr(); while(pNA) { pNA->LinkedNodeGeometryHasChanged(pChangeOp); pNA = pNA->FindNextGeometryLinkedAttr(); } } } // if setting permissions ... (and possibly cause post-op code to be called) if (SetOpPermissionState) { if (allowed) { if (Parent!=NULL) Parent->SetOpPermission(PERMISSION_ALLOWED); // if post process required, ensure our OnChildChange is called (by setting permission on ourself), // insert an inverse action in the undo if (pParam->GetDirection()==OBJCHANGE_CALLEDBYCHILD || Flags.Attribute || Flags.RegenerateNode) { SetOpPermission(PERMISSION_ALLOWED); UndoableOperation* pOp=pParam->GetOpPointer(); if (pOp!=NULL) { if (allowed) allowed=(InitBlendAction::Init(pOp,pOp->GetUndoActionList(),this)!=AC_FAIL); if (allowed) allowed=pOp->DoInvalidateNodeRegion(this, TRUE, FALSE, FALSE, !pParam->GetRetainCachedData()); } } } else SetOpPermission(PERMISSION_DENIED,TRUE); } // if the op was allowed, inform our children by calling their AllowOp()s // this must be done after we've inserted our undo actions so that any child undo actions will be undone first! if (allowed && pParam->GetDirection()!=OBJCHANGE_CALLEDBYCHILD) AllowOp_AccountForCompound(pParam, SetOpPermissionState, DoPreTriggerEdit); if (allowed && Flags.Attribute && SetOpPermissionState) Deinit(); // if we're ok so far and were asked to do a PreTriggerEdit, then // determine whether the Op may change the bounds of some nodes. // If it may, then call NameGallery::PreTriggerEdit. if (allowed && DoPreTriggerEdit) { // if the Op is non-NULL then query its MayChangeNodeBounds() method. UndoableOperation* pChangeOp = pParam->GetOpPointer(); if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds()) { if (NameGallery::Instance()) allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this); } } // return result (directly, or indirectly via a child AllowOp()) to op return allowed; } /******************************************************************************************** > virtual ChangeCode NodeBlend::OnChildChange(ObjChangeParam* pParam) Author: Mark_Neves (Xara Group Ltd) Created: 06/02/95 Inputs: pParam = pointer to a object change parameter class Returns: CC_OK if we have successfully processed the change. CC_FAIL if we cannot handle this particular change and must prevent the child from continuing Purpose: This function should be overridden in derived object classes. Composite objects can use this function to respond to one of their children undergoing a change. They should return CC_FAIL whenever they are unable to cope with the change. Blends deinitialise, then reinitialise themselves. Hopefully that should do the trick. SeeAlso: WarnParentOfChange(),AllowOp(); ********************************************************************************************/ ChangeCode NodeBlend::OnChildChange(ObjChangeParam* pParam) { ERROR2IF(pParam == NULL,CC_FAIL,"pParam == NULL"); ObjChangeType cType = pParam->GetChangeType(); ObjChangeFlags cFlags = pParam->GetChangeFlags(); if (cType == OBJCHANGE_FINISHED) { UndoableOperation* pUndoOp = pParam->GetOpPointer(); // Node* pNodeChanging = pParam->GetChangeObj(); UINT32 NBPCounter = 0; NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(NBPCounter++); // BOOL NodeBlendPathEdited = (pNodeChanging != NULL && pNodeChanging == pNodeBlendPath); // if the op has finished, and we're either the parent of a selected node OR an attribute // was applied OR our node blend path has been edited, deinit the blend in an undoable way while (pNodeBlendPath != NULL) { pNodeBlendPath->OnEdited(pParam); if (!TransformBlendEndObjects(pUndoOp)) return CC_FAIL; pNodeBlendPath = GetNodeBlendPath(NBPCounter++); } if (pUndoOp != NULL) { if (InitBlendAction::Init(pUndoOp,pUndoOp->GetUndoActionList(),this, FALSE, FALSE, cFlags.RegenerateNode) == AC_FAIL) return CC_FAIL; if (!pUndoOp->DoInvalidateNodeRegion(this, TRUE, FALSE, FALSE, !pParam->GetRetainCachedData())) return CC_FAIL; } else { // If no undo op ptr, deinit the blend in a non-undoable way InvalidateBlendBoundingRect(); Deinit(cFlags.RegenerateNode); DocRect rect = GetUnionBlobBoundingRect(); Document* pDoc = (Document*)FindOwnerDoc(); Spread* pSpread = FindParentSpread(); if (pDoc != NULL && pSpread != NULL && pDoc->IS_KIND_OF(Document)) pDoc->ForceRedraw(pSpread, rect, FALSE, this); } } return CC_OK; } /******************************************************************************************** > virtual BOOL NodeBlend::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu) Author: Mark_Neves (Xara Group Ltd) Created: 12/5/95 Inputs: pSpread The spread in which things are happening PointerPos The Location of the mouse pointer at the time of the click pMenu The menu to which items should be added 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 Blend to respond to pop up menu clicks on itself. ********************************************************************************************/ BOOL NodeBlend::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu) { //WEBSTER-ranbirr-01/12/96 #ifndef WEBSTER #if !defined(EXCLUDE_FROM_RALPH) BOOL ok = TRUE; ok = ok && pMenu->BuildCommand(TOOL_OPTOKEN_BLEND, TRUE); // ok = ok && pMenu->BuildCommand(OPTOKEN_ADDBLENDPATH, TRUE); // ok = ok && pMenu->BuildCommand(OPTOKEN_REMOVEBLEND, FALSE); // ok = ok && pMenu->BuildCommand(OPTOKEN_BLENDONETOONE, FALSE); // ok = ok && pMenu->BuildCommand(OPTOKEN_BLENDANTIALIAS, TRUE); return ok; #else return FALSE; #endif #else //webster return FALSE; #endif //webster } /******************************************************************************************** > NodeBlendPath* NodeBlend::GetNodeBlendPath(UINT32 Index) Author: Mark_Neves (Xara Group Ltd) Created: 29/4/99, changed DY 23/9/99 Inputs: Index - number of the NodeBlendPath that you want Returns: Ptr to the NodeBlendPath associated with this blend, or NULL if it aint got one Purpose: Finds the path the blend with try and follow, if it's got one ********************************************************************************************/ NodeBlendPath* NodeBlend::GetNodeBlendPath(UINT32 Index) { Node* pNode = FindFirstChild(); UINT32 NBPCounter = 0; while (pNode) { if (IS_A(pNode,NodeBlendPath)) { //if (((NodeBlendPath*)pNode)->GetPathIndex() == Index) if (NBPCounter == Index) return (NodeBlendPath*)pNode; NBPCounter++; } pNode = pNode->FindNext(); } return NULL; } /******************************************************************************************** > BOOL NodeBlend::BlendAlongThisPath(NodePath* pPath,CCAttrMap* pAttrMap,UndoableOperation* pUndoOp); Author: Mark_Neves (Xara Group Ltd) Created: 29/4/99 Inputs: pPath = ptr to the definition of the path to blend alone pAttrMap = ptr to attrs to apply to given path (can be NULL) pUndoOp = ptr to undo op performing th operation. Returns: TRUE if all went to plan FALSE otherwise Purpose: Makes this blend object blend along the given path It creates a NodeBlendPath from the given NodePath, and inserts it a the last child of the blend ********************************************************************************************/ BOOL NodeBlend::BlendAlongThisPath(NodePath* pNodePath,CCAttrMap* pAttrMap,UndoableOperation* pUndoOp, BOOL BlendPathOnTop) { ERROR2IF(pUndoOp == NULL,FALSE,"I need an undoable op to perform this task"); ERROR2IF(pNodePath == NULL,FALSE,"Er, where is the path then?"); BOOL ok = TRUE; // DY at this point there should only be one nodeblendpath, hence the zero index NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(0); if (pNodeBlendPath != NULL) ok = pUndoOp->DoHideNode(pNodeBlendPath,TRUE); if (ok) pNodeBlendPath = new NodeBlendPath(); if (ok) ok = (pNodeBlendPath != NULL); if (ok) ok = pNodeBlendPath->CopyNodePath(pNodePath); if (ok) ok = pUndoOp->DoLocaliseCommonAttributes(this); if (ok) //DY 16/9/99, previously blendpath was always inserted as last child { if (BlendPathOnTop) ok = pUndoOp->DoInsertNewNode(pNodeBlendPath,this,LASTCHILD,FALSE,FALSE,FALSE,FALSE); else ok = pUndoOp->DoInsertNewNode(pNodeBlendPath,this,FIRSTCHILD,FALSE,FALSE,FALSE,FALSE); } if (ok) ok = pNodeBlendPath->ApplyAttributes(pAttrMap); if (ok) ok = pUndoOp->DoFactorOutCommonChildAttributes(this); if (ok) ok = InitBlenderPathProportions(pUndoOp); if (ok) ok = TransformBlendEndObjects(pUndoOp); // Dont' need to do these two steps as the call to AllowOp() will take care of these things. // Deinit(); // InvalidateBlendBoundingRect(); return ok; } BOOL NodeBlend::InitBlenderPathProportions(UndoableOperation* pUndoOp) { BOOL ok = TRUE; UINT32 NumBlenders = GetNumBlenders(); UINT32 CurBlender = 0; double CurProp = 0.0; double PropIncrement = 1.0 / NumBlenders; NodeBlender* pNodeBlender = FindFirstBlender(); while (ok && pNodeBlender != NULL) { CurBlender++; pNodeBlender->SetBlendedOnCurve(TRUE); pNodeBlender->SetProportionOfPathDistStart(CurProp); CurProp += PropIncrement; pNodeBlender->SetProportionOfPathDistEnd(CurProp); pNodeBlender = FindNextBlender(pNodeBlender); } return ok; } BOOL NodeBlend::RotateBlendEndObjectsBack(UndoableOperation* pUndoOp) { BOOL ok = TRUE; Node* pLast = NULL; NodeBlender* pNodeBlender = FindFirstBlender(); while (ok && pNodeBlender != NULL) { NodeRenderableInk* pStart = pNodeBlender->GetNodeStart(); NodeRenderableInk* pEnd = pNodeBlender->GetNodeEnd(); if (pStart && pStart != pLast) { double AngleChange = -pNodeBlender->GetAngleStart(); ok = TransformNodeToPoint(pStart,NULL,pUndoOp,AngleChange); if (ok && pUndoOp && AngleChange != 0.0) { ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLESTART); ChangeParam.m_NewAngleStart = 0.0; ActionCode Ac = ChangeBlenderAction::Init( pUndoOp,pUndoOp->GetUndoActionList(), pNodeBlender,ChangeParam); ok = (Ac !=AC_FAIL); } } if (pEnd && ok) { double AngleChange = -pNodeBlender->GetAngleEnd(); ok = TransformNodeToPoint(pEnd,NULL,pUndoOp,AngleChange); if (ok && pUndoOp && AngleChange != 0.0) { ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLEEND); ChangeParam.m_NewAngleEnd = 0.0; ActionCode Ac = ChangeBlenderAction::Init( pUndoOp,pUndoOp->GetUndoActionList(), pNodeBlender,ChangeParam); ok = (Ac !=AC_FAIL); } pLast = pEnd; } pNodeBlender = FindNextBlender(pNodeBlender); } return ok; } BOOL NodeBlend::TransformBlendEndObjects(UndoableOperation* pUndoOp) { BOOL ok = TRUE; Node* pLast = NULL; NodeBlender* pNodeBlender = FindFirstBlender(); while (ok && pNodeBlender != NULL) { NodeRenderableInk* pStart = pNodeBlender->GetNodeStart(); NodeRenderableInk* pEnd = pNodeBlender->GetNodeEnd(); ERROR3IF(pStart == NULL,"No start node"); ERROR3IF(pEnd == NULL,"No end node"); if ((pStart == NULL) || (pEnd == NULL)) { return (FALSE); } DocCoord Point; double Angle = 0.0; if (pStart && pStart != pLast) { if (pNodeBlender->GetPointOnNodeBlendPath(0.0,&Point,&Angle)) { double AngleStart = pNodeBlender->GetAngleStart(); double AngleChange = Angle - AngleStart; ok = TransformNodeToPoint(pStart,&Point,pUndoOp,AngleChange); if (ok && pUndoOp && AngleChange != 0.0) { ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLESTART); ChangeParam.m_NewAngleStart = AngleStart + AngleChange; ActionCode Ac = ChangeBlenderAction::Init( pUndoOp,pUndoOp->GetUndoActionList(), pNodeBlender,ChangeParam); ok = (Ac !=AC_FAIL); } } } if (pEnd && ok) { if (pNodeBlender->GetPointOnNodeBlendPath(1.0,&Point,&Angle)) { double AngleEnd = pNodeBlender->GetAngleEnd(); double AngleChange = Angle - AngleEnd; ok = TransformNodeToPoint(pEnd,&Point,pUndoOp,AngleChange); ERROR3IF (!ok, "one does not find this amusing!"); if (ok && pUndoOp && AngleChange != 0.0) { ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLEEND); ChangeParam.m_NewAngleEnd = AngleEnd + AngleChange; ActionCode Ac = ChangeBlenderAction::Init( pUndoOp,pUndoOp->GetUndoActionList(), pNodeBlender,ChangeParam); ok = (Ac !=AC_FAIL); } } pLast = pEnd; } pNodeBlender = FindNextBlender(pNodeBlender); } return ok; } BOOL NodeBlend::TransformNodeToPoint(NodeRenderableInk* pNode,DocCoord* pPoint,UndoableOperation* pUndoOp,double Angle) { ERROR2IF(pNode == NULL,FALSE,"Null node ptr"); BOOL ok = TRUE; // Get the node's bounds pNode->InvalidateBoundingRect(TRUE); DocRect Rect = pNode->GetBoundingRect(); // Work out the translation that will move the centre of the bounds onto the given point DocCoord centre = Rect.Centre(); //(Rect.lox + (Rect.Width()/2),Rect.loy + (Rect.Height()/2)); INT32 dx = 0; INT32 dy = 0; if (pPoint) { dx = pPoint->x - centre.x; dy = pPoint->y - centre.y; } if (pUndoOp) { // DoTransformNode() needs an instance of the matrix. It gets deleted when the action is destroyed if (Angle != 0.0) { Trans2DMatrix* pRotate = new Trans2DMatrix(centre,Angle); if (ok) { RangeControl rc(TRUE,TRUE); rc.SiblingsOnly = TRUE; // Don't look at children - stops children getting // transformed twice when select-inside is present Range r(pNode,pNode, rc); ok = pUndoOp->DoTransformNodes(r,pRotate); } } if (dx != 0 || dy != 0) { Trans2DMatrix* pTrans = new Trans2DMatrix(dx,dy); if (ok) { RangeControl rc(TRUE,TRUE); rc.SiblingsOnly = TRUE; // Don't look at children - stops children getting // transformed twice when select-inside is present Range r(pNode,pNode, rc); ok = pUndoOp->DoTransformNodes(r,pTrans); } } } else { // Non-undoable form of the transform if (Angle != 0.0) { Trans2DMatrix Rotate(centre,Angle); pNode->Transform(Rotate); } if (dx != 0 || dy != 0) { Trans2DMatrix Trans(dx,dy); pNode->Transform(Trans); } } return ok; } /********************************************************************************************* > BOOL NodeBlend::DetachNodeBlendPath(Node* pContextNode, AttachNodeDirection AttDir,UndoableOperation* pUndoOp) Author: Mark_Neves (Xara Group Ltd) Created: 29/4/99 //changed Diccon 9/99 Inputs: - Outputs: - Returns: - Purpose: Removes all nodeblendpaths **********************************************************************************************/ BOOL NodeBlend::DetachNodeBlendPath(Node* pContextNode, AttachNodeDirection AttDir,UndoableOperation* pUndoOp) { ERROR2IF(pContextNode == NULL,FALSE,"NULL pContextNode"); ERROR2IF(pUndoOp == NULL,FALSE,"NULL pUndoOp"); BOOL ok = TRUE; UINT32 NBPCounter = 0; NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(NBPCounter++); while (pNodeBlendPath != NULL) { NodePath* pNodePath = pNodeBlendPath->CreateNodePathCopy(); if (pNodePath != NULL) { CCAttrMap* pAttrMap = new CCAttrMap(30); ok = (pAttrMap != NULL); if (ok) ok = pNodeBlendPath->FindAppliedAttributes(pAttrMap); if (ok) ok = pUndoOp->DoInsertNewNode(pNodePath,pContextNode,AttDir,FALSE,FALSE,FALSE,FALSE); if (ok) ok = pUndoOp->DoHideNode(pNodeBlendPath,TRUE); if (ok) ok = pUndoOp->DoSelectNode(pNodePath); // Select it, because it's nicer that way if (ok) pNodePath->ApplyAttributes(pAttrMap,TRUE); if (pAttrMap != NULL) { delete pAttrMap; pAttrMap = NULL; } } // go through all the blenders and set their index flags to -1 // NOTE - need to make this undoable NodeBlender* pBlender = FindFirstBlender(); while (pBlender != NULL) { pBlender->SetNodeBlendPathIndex(-1); pBlender = FindNextBlender(pBlender); } pNodeBlendPath = GetNodeBlendPath(NBPCounter++); } return ok; } /********************************************************************************************* > NodeBlender* NodeBlend::FindFirstBlender() Author: Mark_Neves (Xara Group Ltd) Created: 29/4/99 Inputs: - Outputs: - Returns: Pointer to the first NodeBlender belonging to this blend object or NULL if there aren't any Purpose: Convenience function. A drive-thru NodeBlender takeaway service SeeAlso: FindNextBlender() **********************************************************************************************/ NodeBlender* NodeBlend::FindFirstBlender() { Node* pNode = FindFirstChild(); while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) return ((NodeBlender*)pNode); pNode = pNode->FindNext(); } return NULL; } /********************************************************************************************* > NodeBlender* NodeBlend::FindNextBlender(NodeBlender* pNodeBlender) Author: Mark_Neves (Xara Group Ltd) Created: 29/4/99 Inputs: pNodeBlender = ptr to previous NodeBlender found Outputs: - Returns: Pointer to the next NodeBlender belonging to this blend object, in rendering order or NULL if there aren't any Purpose: Convenience function. A drive-thru NodeBlender takeaway service SeeAlso: FindFirstBlender() **********************************************************************************************/ NodeBlender* NodeBlend::FindNextBlender(NodeBlender* pNodeBlender) { if (pNodeBlender) { Node* pNode = pNodeBlender->FindNext(); while (pNode != NULL) { if (IS_A(pNode,NodeBlender)) return ((NodeBlender*)pNode); pNode = pNode->FindNext(); } } return NULL; } /********************************************************************************************* > NodeBlender* NodeBlend::FindLastBlender() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 16/9/99 Inputs: - Outputs: - Returns: Pointer to the last NodeBlender belonging to this blend object, in rendering order or NULL if there aren't any Purpose: Convenience function. A drive-thru NodeBlender takeaway service SeeAlso: FindFirstBlender(), FindNextBlender() **********************************************************************************************/ NodeBlender* NodeBlend::FindLastBlender() { Node* pNode = FindLastChild(); while (pNode != NULL) { if (pNode->IS_KIND_OF(NodeBlender)) { return (NodeBlender*)pNode; } pNode = pNode->FindPrevious(); } return NULL; } /********************************************************************************************* > BOOL NodeBlend::FindFirstAndLastBlender(NodeBlender** pFirstBlender, NodeBlender** ppLastBlender) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 16/9/99 Inputs: - Outputs: pFirstBlender, pLastBlender, as the names suggest Returns: TRUE if successful, FALSE otherwise Purpose: Convenience function. A drive-thru NodeBlender takeaway service SeeAlso: FindFirstBlender(), FindNextBlender() **********************************************************************************************/ BOOL NodeBlend::FindFirstAndLastBlenders(NodeBlender** ppFirstBlender, NodeBlender** ppLastBlender) { NodeBlender* pTempBlender = FindFirstBlender(); if (pTempBlender == NULL) return FALSE; else *ppFirstBlender = pTempBlender; pTempBlender = NULL; pTempBlender = FindLastBlender(); if (pTempBlender == NULL) return FALSE; *ppLastBlender = pTempBlender; return TRUE; } /********************************************************************************************* > Node* NodeBlend::HasEditableChild(CCRuntimeClass* ChildClass, Node* pPreviousChild) Author: Mark_Neves (Xara Group Ltd) Created: 29/4/99 Inputs: ChildClass = the runtime class of the editable object pPreviousChild = a pointer to the previous editable child returned by 'this' node, NULL if this is the first call to this node. Outputs: - Returns: A node pointer, to an object which forms part of the editable surface of its parent (this node). Purpose: This function returns our edit node (the NodeBlendPath) **********************************************************************************************/ Node* NodeBlend::HasEditableChild(CCRuntimeClass* ChildClass, Node* pPreviousChild) { if (ChildClass != CC_RUNTIME_CLASS(NodePath)) return NULL; NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(0); // check to see if this has already been asked for once if (((Node*)pNodeBlendPath) == pPreviousChild) return NULL; return pNodeBlendPath; } /********************************************************************************************* > BOOL NodeBlend::GetBlendDistance(BOOL FullDistance, double* Distance) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: boolean indicating whether to return the full length of the blendpath or just the length that is currently occupied by the blend Outputs: length of the blend Returns: TRUE if successful, FALSE otherwise Purpose: To find out how long the blend is. **********************************************************************************************/ BOOL NodeBlend::GetBlendDistance( BOOL FullDistance, double* Distance) { NodeBlender* pBlender = FindFirstBlender(); double TempDistance = 0.0; while (pBlender != NULL) { if (pBlender->IsBlendedOnCurve()) { NodeBlendPath* pPath = pBlender->GetNodeBlendPath(); if (pPath == NULL) return FALSE; double PathDistance = pPath->GetPathLength(); double StartProportion = 0.0; double EndProportion = 1.0; if (FullDistance ==FALSE) { // the blend may only go part way along the curve, calculate how // much distance the actual blend takes up StartProportion = pBlender->GetProportionOfPathDistStart(); EndProportion = pBlender->GetProportionOfPathDistEnd(); } TempDistance += (PathDistance * (EndProportion - StartProportion)); } else { TempDistance += pBlender->GetLinearDistance(); } pBlender = FindNextBlender(pBlender); } ERROR2IF((TempDistance < 0), FALSE, "Negative path length"); *Distance = TempDistance; return TRUE; } /********************************************************************************************* > BOOL NodeBlend::GetNumStepsFromDistance(double StepDistance, UINT32* NumSteps) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: the desired distance between steps Outputs: number of steps required to achieve the distance closest to the specified value Returns: TRUE if successful, FALSE otherwise Purpose: To determine the number of steps that will achieve a distance between steps close to the value passed. **********************************************************************************************/ BOOL NodeBlend::GetNumStepsFromDistance(double Distance, UINT32* NumSteps) { ASSERT(Distance); double BlendLength = 0.0; BOOL Valid = GetBlendDistance(TRUE, &BlendLength); if (!Valid) return FALSE; UINT32 TempNumSteps = (UINT32)(BlendLength / Distance); ERROR3IF((TempNumSteps < 0), "Negative number of blend steps"); *NumSteps = TempNumSteps; return TRUE; } /********************************************************************************************* > double NodeBlend::GetStepDistance() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: - Outputs: - Returns: the distance between steps of the blend Purpose: works out the distance between each step in the blend **********************************************************************************************/ double NodeBlend::GetStepDistance() { double BlendLength = 0.0; GetBlendDistance(FALSE, &BlendLength); double StepDistance = BlendLength/m_NumBlendSteps; ERROR2IF((StepDistance < 0), 0, "Negative step distance"); return StepDistance; } /********************************************************************************************* > void NodeBlend::UpdateStepDistance() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 26/9/99 Inputs: - Outputs: - Returns: - Purpose: works out the distance between each step in the blend and updates the member variable **********************************************************************************************/ void NodeBlend::UpdateStepDistance() { double BlendLength = 0.0; GetBlendDistance(FALSE, &BlendLength); m_StepDistance = BlendLength/m_NumBlendSteps; } /********************************************************************************************* > double NodeBlend::GetDistanceEntered() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 7/9/99 Inputs: - Outputs: - Returns: the last distance that the user entered in the edit box, this will be initialised to -1 so if the user has not entered any value then -1 will be returned Purpose: to return the last distance between blend steps that user entered **********************************************************************************************/ double NodeBlend::GetDistanceEntered() { return m_DistanceEntered; } /********************************************************************************************* > void NodeBlend::SetDistanceEntered(double Distance) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 7/9/99 Inputs: Distance between steps to set Outputs: - Returns: - Purpose: to set the distance between blend steps that user entered **********************************************************************************************/ void NodeBlend::SetDistanceEntered(double Distance) { m_DistanceEntered = Distance; } /********************************************************************************************* > EndObject NodeBlend::GetLastEdited() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: - Outputs: - Returns: the last object edited, either FIRST, LAST or NONE. Purpose: Access function, lets you know which end object of a blend on a path was last edited. **********************************************************************************************/ EndObject NodeBlend::GetLastEdited() { return m_LastEdited; } /********************************************************************************************* > void NodeBlend::SetLastEdited(EndObject ObjectEdited) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: Object last edited Outputs: - Returns: - Purpose: Access function **********************************************************************************************/ void NodeBlend::SetLastEdited(EndObject LastEdited) { m_LastEdited = LastEdited; } /********************************************************************************************* > UINT32 NodeBlend::GetNumNodeBlendPaths() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 23/9/99 Inputs: - Outputs: - Returns: the number of nodeblend paths this blend is using Purpose: Access function **********************************************************************************************/ UINT32 NodeBlend::GetNumNodeBlendPaths() { UINT32 NBPCounter = 0; Node* pNode = FindFirstChild(); while (pNode != NULL) { if (pNode->IS_KIND_OF(NodeBlendPath)) NBPCounter++; pNode = pNode->FindNext(); } return NBPCounter; } /********************************************************************************************* > void NodeBlend::SetNumNodeBlendPathsInc(BOOL Increase) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 23/9/99 Inputs: Increase - TRUE if you wish to add, FALSE if you want to take away Outputs: - Returns: - Purpose: Record a change in the number of nodeblendpaths Errors: If m_NumNodeBlendPaths = 0 and Increment = FALSE **********************************************************************************************/ void NodeBlend::SetNumNodeBlendPathsInc(BOOL Increase) { if (Increase) m_NumNodeBlendPaths++; else { if (m_NumNodeBlendPaths == 0) { ERROR3("Blend has no nodeblend paths"); return; } m_NumNodeBlendPaths--; } } /********************************************************************************************* > void NodeBlend::SetNumNodeBlendPaths(INT32 Index) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 23/9/99 Inputs: Index - number that you wish to set Outputs: - Returns: - Purpose: Record a change in the number of nodeblendpaths Errors: If m_NumNodeBlendPaths = 0 and Increment = FALSE **********************************************************************************************/ void NodeBlend::SetNumNodeBlendPaths(INT32 Index) { if (Index < 0) { ERROR3("Index is less than zero"); return; } else m_NumNodeBlendPaths = Index; } /********************************************************************************************* > BOOL NodeBlend::GetStartAndEndProportions(double* StartProp, double* EndProp) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: - Outputs: StartProp - the proportion along the nodeblendpath where the first blend object is EndProp - the proportion along the nodeblendpath where the last blend object is Returns: TRUE if successful, FALSE if not, or if the blend is not on a path Purpose: To find the proportions along the path where the first and last objects are located **********************************************************************************************/ BOOL NodeBlend::GetStartAndEndProportions(double *StartProp, double* EndProp) { if (m_BlendedOnCurve == FALSE) { ERROR3("Blend is not on a curve"); return FALSE; } NodeBlender* pNodeBlender = FindFirstBlender(); if (pNodeBlender == NULL) { ERROR3("Can't find first blender"); return FALSE; } double TempStartProp = pNodeBlender->GetProportionOfPathDistStart(); if (TempStartProp < 0.0 || TempStartProp > 1.0) { ERROR3("Invalid value for start proportion"); return FALSE; } double TempEndProp = 1.0; while (pNodeBlender != NULL) { TempEndProp = pNodeBlender->GetProportionOfPathDistEnd(); pNodeBlender = FindNextBlender(pNodeBlender); } if (TempEndProp < 0.0 || TempEndProp > 1.0) { ERROR3("Invalid value for end proportion"); return FALSE; } *StartProp = TempStartProp; *EndProp = TempEndProp; return TRUE; } /********************************************************************************************* > BOOL NodeBlend::GetStartAndEndNodes(Node** ppStart, Node** ppEnd) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: - Outputs: pStart - the first blend object pEnd - the last blend object Returns: TRUE if successful, FALSE if not Purpose: To find the first and last objects in a blend **********************************************************************************************/ BOOL NodeBlend::GetStartAndEndNodes(Node** ppStart, Node** ppEnd) { NodeBlender* pFirstBlender = FindFirstBlender(); if (pFirstBlender == NULL) { ERROR3("No first blender"); return FALSE; } *ppStart = pFirstBlender->GetNodeStart(); if (*ppStart == NULL) { ERROR3("No start node"); return FALSE; } NodeBlender* pLastBlender = FindLastBlender(); if (pLastBlender == NULL) { ERROR3("No last blender"); return FALSE; } *ppEnd = pLastBlender->GetNodeEnd(); if (*ppEnd == NULL) { ERROR3("No last node"); return FALSE; } return TRUE; } /********************************************************************************************* > BOOL NodeBlend::NonLinearObjectProfile() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 4/10/99 Inputs: - Outputs: - Returns: TRUE if the blend currently has a non-linear object profile, otherwise false Purpose: as above **********************************************************************************************/ BOOL NodeBlend::NonLinearObjectProfile() { if (m_ObjectProfile.GetBias() != 0.0) return TRUE; if (m_ObjectProfile.GetGain() != 0.0) return TRUE; return FALSE; } /********************************************************************************************* > BOOL NodeBlend::HitOnEndDragBlob(DocCoord Point, Node** ppHitNode) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 20/9/99 Inputs: Point - the point to test Outputs: ppHitNode - the node that was hit, if there was one Returns: TRUE if Point is within the central drag blob of an end node, otherwise false also FALSE if blend is not on a curve Purpose: To determine whether or not Point is over the central blob of one of the end objects of a blend on a curve. **********************************************************************************************/ BOOL NodeBlend::HitOnEndDragBlob(DocCoord Point, Node** ppHitNode) { NodeBlender* pBlender = FindFirstBlender(); BOOL ok = FALSE; while (pBlender != NULL) { /*NodeBlender* pNextBlender =*/ FindNextBlender(pBlender); // only the end objects of blends on curves are editable if (pBlender->IsBlendedOnCurve()) { Node* pFirstNode = pBlender->GetNodeStart(); Node* pLastNode = pBlender->GetNodeEnd(); if (pFirstNode == NULL || pLastNode == NULL) { return FALSE; } ok = HitOnDragBlob((NodeRenderableInk*)pFirstNode, Point); if (ok) { *ppHitNode = pFirstNode; return TRUE; } ok = HitOnDragBlob((NodeRenderableInk*)pLastNode, Point); if (ok) { *ppHitNode = pLastNode; return TRUE; } } pBlender = FindNextBlender(pBlender); } return ok; } /********************************************************************************************* > BOOL NodeBlend::HitOnDragBlob(NodeRenderableInk* pInk, DocCoord PointerPos) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: pInk - the node to test PointerPos - the point to test Outputs: - Returns: TRUE if PointerPos is within the central blob of pink, else FALSE Purpose: To find out if the pointer position is within the bounds of the blob in the middle of this node. **********************************************************************************************/ BOOL NodeBlend::HitOnDragBlob(NodeRenderableInk* pInk, DocCoord PointerPos) { if (pInk == NULL) { ERROR3("Ink node is NULL"); return FALSE; } BOOL ok = FALSE; // find the bounding rect of the centre blob DocRect BlobSize; BlobManager* pBlobMgr = GetApplication()->GetBlobManager(); if (pBlobMgr != NULL) { DocRect BoundingRect = pInk->GetBoundingRect(); DocCoord CentrePoint = BoundingRect.Centre(); pBlobMgr->GetBlobRect(CentrePoint, &BlobSize); ok = BlobSize.ContainsCoord(PointerPos); } return ok; } /******************************************************************************************** > virtual INT32 NodeBlend::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 NodeBlend::EstimateNodeComplexity (OpParam* details) { NodeBlender* pBlender = FindFirstBlender(); INT32 total = 0; while (pBlender) { total += pBlender->EstimateNodeComplexity (details); pBlender = FindNextBlender(pBlender); } return (total); } /********************************************************************************************* > NodeBlender* NodeBlend::NodeIsPartOfBlender( Node* pNode) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: pNode - the node to look for Outputs: - Returns: the nodeblender which uses pNode at the specified position Purpose: To find out if pNode is used by a blender in this nodeblend at the position specified. Useful if you are changing the node through a different blender and wish to update related blends **********************************************************************************************/ NodeBlender* NodeBlend::NodeIsPartOfBlender(Node* pNode, BOOL FirstNode) { if (pNode == NULL) { ERROR3("Node is NULL"); return NULL; } NodeBlender* pNodeBlender = FindFirstBlender(); if (pNodeBlender == NULL) { ERROR3("No nodeblender"); return NULL; } while (pNodeBlender != NULL) { if (FirstNode) { Node* pCheckFirstNode = pNodeBlender->GetNodeStart(); if (pCheckFirstNode == pNode) return pNodeBlender; } else { Node* pCheckLastNode = pNodeBlender->GetNodeEnd(); if (pCheckLastNode == pNode) return pNodeBlender; } pNodeBlender = FindNextBlender(pNodeBlender); } return NULL; } /********************************************************************************************* > NodeBlender* NodeBlend::NodeIsPartOfBlender( Node* pNode, Nodeblender* pBlender, BOOL* First) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: pNode - the node to look for pBlender - the blender that we already know pNode belongs to Outputs: First, whether the node is first on last in the newly found blender Returns: the nodeblender which uses pNode and is not pBlender Purpose: Overridden version of above, now that we have multi stage blends which can go to and from arbitrary end points we can't always rely on knowing the end position **********************************************************************************************/ NodeBlender* NodeBlend::NodeIsPartOfBlender(Node* pNode, NodeBlender* pBlender, BOOL* First) { if (pNode == NULL) { ERROR3("Node is NULL"); return NULL; } NodeBlender* pNodeBlender = FindFirstBlender(); if (pNodeBlender == NULL) { ERROR3("No nodeblender"); return NULL; } while (pNodeBlender != NULL) { if (pNodeBlender != pBlender) { Node* pStart = pNodeBlender->GetNodeStart(); Node* pEnd = pNodeBlender->GetNodeEnd(); if (pStart == NULL || pEnd == NULL) { ERROR3("Start or end node is NULL"); return NULL; } if (pStart == pNode) { *First = TRUE; return pNodeBlender; } else if (pEnd == pNode) { *First = FALSE; return pNodeBlender; } } pNodeBlender = FindNextBlender(pNodeBlender); } return NULL; } /********************************************************************************************* > virtual NodeCompound* NodeBlend::GetParentController() const Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15/9/99 Inputs: - Outputs: - Returns: Controller node if there is one, or NULL Purpose: If the blend is part of a bevel, shadow, or contour then this returns the parent controller **********************************************************************************************/ NodeCompound* NodeBlend::GetParentController() const { Node* pParent = (Node*)this->FindParent(); if (pParent == NULL) { ERROR3("This blend has no parents"); return NULL; } while ((pParent != NULL) && (!pParent->IsLayer())) { if (pParent->IsCompoundClass() && pParent->IsController()) return static_cast(pParent); pParent = pParent->FindParent(); } return NULL; } //----------------------------------------------- /******************************************************************************************** > InitBlendAction::InitBlendAction() Author: Mark_Neves (Xara Group Ltd) Created: 8/2/95 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action Errors: - SeeAlso: - ********************************************************************************************/ InitBlendAction::InitBlendAction() { m_pNodeBlend = NULL; m_pNodeBlender = NULL; m_pStartNode = NULL; m_pEndNode = NULL; m_bReverse = FALSE; m_bStoreStartAndEndNodes = FALSE; m_enInitType = IBAT_INVALID; } /******************************************************************************************** > ActionCode InitBlendAction::Init(UndoableOperation* pOp,ActionList* pActionList,NodeBlend* pThisNodeBlend, BOOL StoreStartAndEndNodes, BOOL bReverse) Author: Mark_Neves (Xara Group Ltd) Created: 8/2/95 Inputs: pOp = ptr to the operation to which this action belongs pThisNodeBlend = ptr to blend to init. pActionList = ptr to action list to which this action should be added StoreStartAndEndNodes = Whether to store the start & end nodes or not bReverse = Whether to insert this action into the head or the tail of the action list (if FALSE it's treated normally) Outputs: - Returns: ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL Purpose: This is the function which creates an instance of this action. If there is no room in the undo buffer (which is determined by the base class Init function called within) the function will either return AC_NO_RECORD which means the operation can continue, but no undo information needs to be stored, or AC_OK which means the operation should continue AND record undo information. If the function returns AC_FAIL, there was not enough memory to record the undo information, and the user has decided not to continue with the operation. Records an action that, when executed, will Deinit() then Reinit() the blend, then invalidate its bounds. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode InitBlendAction::Init(UndoableOperation* pOp,ActionList* pActionList,NodeBlend* pThisNodeBlend, BOOL StoreStartAndEndNodes, BOOL bReverse, BOOL bNodesMayBeChanged) { UINT32 ActSize = sizeof(InitBlendAction); InitBlendAction* pNewAction; ActionCode Ac = Action::Init(pOp,pActionList,ActSize,CC_RUNTIME_CLASS(InitBlendAction),(Action**)&pNewAction); NodeBlender * pBlender = (NodeBlender *)pThisNodeBlend->FindFirstChild(CC_RUNTIME_CLASS(NodeBlender)); if (pNewAction) pNewAction->m_enInitType = IBAT_INIT; if (Ac != AC_FAIL && pNewAction != NULL) { pNewAction->m_pNodeBlend = pThisNodeBlend; if (StoreStartAndEndNodes && pBlender) { pNewAction->m_pStartNode = pBlender->GetNodeStart(); pNewAction->m_pEndNode = pBlender->GetNodeEnd(); } pNewAction->m_bReverse = bReverse; pNewAction->m_bStoreStartAndEndNodes = StoreStartAndEndNodes; pNewAction->m_bNodesMayBeChanged = bNodesMayBeChanged; if (bReverse) { // reverse this action in the action list pActionList->RemoveItem(pNewAction); pActionList->AddHead(pNewAction); } pThisNodeBlend->InvalidateBlendBoundingRect(); pThisNodeBlend->Deinit(bNodesMayBeChanged); // if (!pOp->DoInvalidateNodeRegion(pThisNodeBlend,TRUE)) // Ac = AC_FAIL; } return Ac; } ActionCode InitBlendAction::InitOnBlender(UndoableOperation* pOp,ActionList* pActionList, NodeBlender* pThisNodeBlender, BOOL StoreStartAndEndNodes, BOOL bReverse) { UINT32 ActSize = sizeof(InitBlendAction); InitBlendAction* pNewAction; ActionCode Ac = Action::Init(pOp,pActionList,ActSize,CC_RUNTIME_CLASS(InitBlendAction),(Action**)&pNewAction); NodeBlend* pThisNodeBlend = (NodeBlend *)pThisNodeBlender->FindParent(CC_RUNTIME_CLASS(NodeBlend)); pNewAction->m_enInitType = IBAT_INITONBLENDER; pNewAction->m_pNodeBlender = pThisNodeBlender; if (Ac != AC_FAIL && pNewAction != NULL) { pNewAction->m_pNodeBlend = pThisNodeBlend; if (StoreStartAndEndNodes && pThisNodeBlender) { pNewAction->m_pStartNode = pThisNodeBlender->GetNodeStart(); pNewAction->m_pEndNode = pThisNodeBlender->GetNodeEnd(); } pNewAction->m_bReverse = bReverse; pNewAction->m_bStoreStartAndEndNodes = StoreStartAndEndNodes; pNewAction->m_bNodesMayBeChanged = FALSE; if (bReverse) { // reverse this action in the action list pActionList->RemoveItem(pNewAction); pActionList->AddHead(pNewAction); } pThisNodeBlend->InvalidateBlendBoundingRect(); pThisNodeBlend->Deinit(); // if (!pOp->DoInvalidateNodeRegion(pThisNodeBlend,TRUE)) // Ac = AC_FAIL; } return Ac; } /******************************************************************************************** > ActionCode InitBlendAction::Execute(); Author: Mark_Neves (Xara Group Ltd) Created: 8/2/95 Inputs: - Outputs: - Returns: ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL Purpose: Executes the action. It calls Deinit() then Reinit() on the blend, then invalidate its bounds. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode InitBlendAction::Execute() { if (pOperation->IS_KIND_OF(UndoableOperation)) { switch (m_enInitType) { case IBAT_INIT: { if (InitBlendAction::Init((UndoableOperation*)pOperation,pOppositeActLst,m_pNodeBlend, m_bStoreStartAndEndNodes, m_bReverse, m_bNodesMayBeChanged) != AC_OK) return AC_FAIL; } break; case IBAT_INITONBLENDER: { if (InitBlendAction::InitOnBlender((UndoableOperation*)pOperation,pOppositeActLst,m_pNodeBlender, m_bStoreStartAndEndNodes, m_bReverse) != AC_OK) return AC_FAIL; } break; default: { ERROR3 ("Er - I don't know what to do!"); return (AC_FAIL); } break; } // DMc // ok, if we've got either the start or the end nodes then re-init if (m_pStartNode != NULL && m_pEndNode != NULL) { NodeBlender * pBlender = NULL; switch (m_enInitType) { case IBAT_INIT: { pBlender = (NodeBlender *)m_pNodeBlend->FindFirstChild(CC_RUNTIME_CLASS(NodeBlender)); } break; case IBAT_INITONBLENDER: { pBlender = m_pNodeBlender;//(NodeBlender *)m_pNodeBlend->FindFirstChild(CC_RUNTIME_CLASS(NodeBlender)); } break; default: { ERROR3 ("Er - I don't know what to do!"); return (AC_FAIL); } break; } if (pBlender) { pBlender->Reinit((NodeRenderableInk *)m_pStartNode, (NodeRenderableInk *)m_pEndNode, FALSE); } } return AC_OK; } else { ERROR3("InitBlendAction::Execute() called with op that's not an undoable op"); return AC_FAIL; } } InitBlendAction::~InitBlendAction() { } //------------------------------------------------------------------------- //------------------------------------------------------------------------- //------------------------------------------------------------------------- /******************************************************************************************** > virtual BOOL NodeBlend::WritePreChildren(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 blend record to the filter SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::WritePreChildren(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); BOOL ok = TRUE; // Diccon 9/99 added profile record if (ok) { CXaraFileRecord ProfileRec(TAG_BLENDPROFILES, TAG_BLENDPROFILES_SIZE); ok = ProfileRec.Init(); if (ok) ok = ProfileRec.WriteDOUBLE(m_ObjectProfile.GetBias()); if (ok) ok = ProfileRec.WriteDOUBLE(m_ObjectProfile.GetGain()); if (ok) ok = ProfileRec.WriteDOUBLE(m_AttrProfile.GetBias()); if (ok) ok = ProfileRec.WriteDOUBLE(m_AttrProfile.GetGain()); if (ok) ok = ProfileRec.WriteDOUBLE(/*m_PositionProfile.GetBias()*/0.0); if (ok) ok = ProfileRec.WriteDOUBLE(/*m_PositionProfile.GetGain()*/0.0); if (ok) ok = pFilter->Write(&ProfileRec); } CXaraFileRecord Rec(TAG_BLEND,TAG_BLEND_SIZE); UINT16 NumSteps = UINT16(m_NumBlendSteps); BYTE Flags =( m_OneToOne << 0) | ((m_NotAntialiased == 0) << 1) | ( m_Tangential << 2); ERROR3IF(BYTE(m_ColBlendType) > (1 << TAG_BLEND_COLEFFECT_SHIFT),"Col blend type will not fit into the flags byte"); BYTE ColEffect = (BYTE(m_ColBlendType) << TAG_BLEND_COLEFFECT_SHIFT) & TAG_BLEND_COLEFFECT_MASK; Flags = Flags | ColEffect; if (ok) ok = Rec.Init(); if (ok) ok = Rec.WriteUINT16(NumSteps); if (ok) ok = Rec.WriteBYTE(Flags); if (ok) ok = pFilter->Write(&Rec); return ok; #else return FALSE; #endif } /******************************************************************************************** > virtual BOOL NodeBlend::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 blend record to the filter WEBSTER - markn 29/1/97 Changed so that it reacts to the "Convert blends to outlines" switch SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::WritePreChildrenWeb(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT if (pFilter->GetConvertBlendsToOutlines()) return pFilter->WriteNodeAsOutlines(this); return WritePreChildren(pFilter); #else return FALSE; #endif } //-------------------------------------------------------------- // See NodeBlend::WritePreChildrenWeb(BaseCamelotFilter* pFilter) // BOOL NodeBlend::WritePreChildrenNative(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT if (pFilter->IsCompactNativeFilter()) { if (pFilter->GetConvertBlendsToOutlines()) return pFilter->WriteNodeAsOutlines(this); } return WritePreChildren(pFilter); #else return FALSE; #endif } /******************************************************************************************** > virtual BOOL NodeBlend::CanWriteChildrenWeb(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 29/1/97 Inputs: pFilter = ptr to the filter Returns: TRUE if record is written, FALSE if not Purpose: Determines whether the blend's children are to be saved or not. WEBSTER - markn 29/1/97 SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::CanWriteChildrenWeb(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT if (pFilter->GetConvertBlendsToOutlines()) return FALSE; return TRUE; #else return FALSE; #endif } /******************************************************************************************** > virtual BOOL NodeBlend::CanWriteChildrenNative(BaseCamelotFilter* pFilter) Author: Mark_Neves (Xara Group Ltd) Created: 29/1/97 Inputs: pFilter = ptr to the filter Returns: TRUE if record is written, FALSE if not Purpose: Determines whether the blend's children are to be saved or not. WEBSTER - markn 29/1/97 SeeAlso: - ********************************************************************************************/ BOOL NodeBlend::CanWriteChildrenNative(BaseCamelotFilter* pFilter) { #ifdef DO_EXPORT if (pFilter->IsCompactNativeFilter()) { if (pFilter->GetConvertBlendsToOutlines()) return FALSE; } return TRUE; #else return FALSE; #endif } /******************************************************************************************** > BOOL NodeBlend::ReinitialiseOnChildChanged( UndoableOperation* pOp, NodeRenderableInk* pOldChild, NodeRenderableInk* pNewChild ) Author: Karim_MacDonald (Xara Group Ltd) Created: 08 September 2000 Inputs: pOp we need an undoable operation to do this undoably. pOldChild the node which the blend used to (currently) point to. pNewChild the node which the blend should now point to. Returns: TRUE if success, FALSE otherwise. Purpose: Used by the shadowing code to reinitialise the blend when one of the blended nodes has had a shadow applied to it. In this case, the tree structure has changed, and the blend framework needs to know this. Errors: ERROR2 if any parameters are NULL. ********************************************************************************************/ BOOL NodeBlend::ReinitialiseOnChildChanged( UndoableOperation* pOp, NodeRenderableInk* pOldChild, NodeRenderableInk* pNewChild ) { // validate input params. ERROR2IF(pOp == NULL || pOldChild == NULL || pNewChild == NULL, FALSE, "NodeBlend::ReinitialiseOnChildChanged; NULL input parameters!"); // iterate over our NodeBlenders, looking for a ptr to the old child. NodeBlender* pBlender = FindFirstBlender(); while (pBlender != NULL) { // reinitialise the first blended node. if (pBlender->GetNodeStart() == pOldChild) { if (InitBlendAction::InitOnBlender(pOp, pOp->GetUndoActionList(), pBlender, TRUE, TRUE) == AC_FAIL) { ERROR3("NodeBlend::ReinitialiseOnChildChanged; couldn't init blend action"); } pBlender->Reinit(pNewChild, NULL, FALSE); } // reinitialise the second blended node. // (should be 'else if' here, but for robustness, we'll just use 'if'). if (pBlender->GetNodeEnd() == pOldChild) { if (InitBlendAction::InitOnBlender(pOp, pOp->GetUndoActionList(), pBlender, TRUE, TRUE) == AC_FAIL) { ERROR3("NodeBlend::ReinitialiseOnChildChanged; couldn't init blend action"); } pBlender->Reinit(NULL, pNewChild, FALSE); } pBlender = FindNextBlender(pBlender); } return TRUE; } /******************************************************************************************** > virtual DocRect NodeBlend::ValidateExtend(const ExtendParams& ExtParams) Author: Karim_MacDonald (Xara Group Ltd) Created: 26/11/1999 Inputs: ExtParams extend description parameters. Outputs: Returns: TRUE if this node and its children cannot be validly extended, FALSE otherwise. Purpose: Checks whether or not performing an extend operation on this node will leave it in a state from which it would be impossible to return. In the case of a NodeBlend, we consider its children, which will be the ones to move under a blend. Under extension, the choice of which child nodes to extend is fairly complicated - see NodeBlend::FindExtendingChildren for more information. Errors: See also: NodeBlend::Extend, NodeBlend::FindExtendingChildren. ********************************************************************************************/ DocRect NodeBlend::ValidateExtend(const ExtendParams& ExtParams) { // the routine for finding extending nodes may take a while, so check first // whether we're actually doing any extending, and if not, leave now. if (!(ExtParams.fExtendFlags & X_EXTEND) && !(ExtParams.fExtendFlags & Y_EXTEND)) return DocRect(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX); // okay, compile lists of child nodes to validate for extension. std::list lpInks; std::list lpBlendPaths; FindExtendingChildren(lpInks, lpBlendPaths); // under non-stretch extension, a blend's child nodes behave in two ways. // if the child node is a blend-path, it is extended, and requires validation, as a path. // we test blend-paths first, as these are more likely to invalidate the extension. DocRect drMinExtend(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drThisMinExtend; for ( std::list::iterator iterPath = lpBlendPaths.begin(); iterPath != lpBlendPaths.end(); iterPath ++ ) { drThisMinExtend = ((NodeBlendPath*)(*iterPath))->ValidateExtend(ExtParams); if (drMinExtend.lo.x > drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x; if (drMinExtend.lo.y > drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y; if (drMinExtend.hi.x > drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x; if (drMinExtend.hi.y > drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y; } // Karim 17/04/2000 // We're changing things so that blended objects actually extend, rather than translate. // This _may_ cause some havoc with blend-on-a-path... /* // if it is any other ink node, it is translated as a whole, so its centre needs testing. INT32 i = 0; INT32 numInks = lpInks.size(); DocCoord* doccArray = new DocCoord[numInks]; for ( std::list::iterator iterInk = lpInks.begin(); iterInk != lpInks.end(); iterInk ++ ) { doccArray[i ++] = ((NodeRenderableInk*)(*iterInk))->FindExtendCentre(); } drThisMinExtend = Extender::ValidateControlPoints(numInks, doccArray, ExtParams); if (drMinExtend.lox > drThisMinExtend.lox) drMinExtend.lox = drThisMinExtend.lox; if (drMinExtend.loy > drThisMinExtend.loy) drMinExtend.loy = drThisMinExtend.loy; if (drMinExtend.hix > drThisMinExtend.hix) drMinExtend.hix = drThisMinExtend.hix; if (drMinExtend.hiy > drThisMinExtend.hiy) drMinExtend.hiy = drThisMinExtend.hiy; delete doccArray; */ // if it is any other ink node, it is extended as if it is not even in a blend, // so we validate it as normal. /*size_t numInks = */lpInks.size(); for ( std::list::iterator iterInk = lpInks.begin(); iterInk != lpInks.end(); iterInk ++ ) { drThisMinExtend = ((NodeRenderableInk*)(*iterInk))->ValidateExtend(ExtParams); if (drMinExtend.lo.x > drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x; if (drMinExtend.lo.y > drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y; if (drMinExtend.hi.x > drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x; if (drMinExtend.hi.y > drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y; } return drMinExtend; } /******************************************************************************************** > virtual void NodeBlend::Extend(const ExtendParams& ExtParams) Author: Karim_MacDonald (Xara Group Ltd) Created: 26/11/1999 Inputs: ExtParams extend description parameters. Outputs: This node and its children may undergo change in the process of extension. Returns: Purpose: Applies an Extend operation to this node. If we're just stretching, then we simply do a stretch transform on ourself. If the extend operation involves any actual extension, we must determine which of the nodes lying below us we must extend. The child nodes which are extended will fall into two categories: * NodeBlendPaths will take care of themselves, via their Extend() fn. * Nodes which are blended between will be translated appropriately. (Karim 17/04/2000 - behaviour has changed so that Nodes instead have their Extend() methods called) Note however that the choice of which nodes to translate is a little complex; look at FindExtendingChildren() for info. Errors: See also: NodeBlend::FindExtendingChildren() ********************************************************************************************/ void NodeBlend::Extend(const ExtendParams& ExtParams) { // try to perform a stretch first of all. TransformStretchObject(ExtParams); // figuring out which children to do what to is complicated and may take time, // so only continue if we _have_ to extend along either axis, otherwise quit now. if ( !(ExtParams.fExtendFlags & (X_EXTEND | Y_EXTEND)) ) return; // okay, compile lists of child nodes which will be extended. std::list lpInks; std::list lpBlendPaths; FindExtendingChildren(lpInks, lpBlendPaths); // extend the blend-paths. for ( std::list::iterator iterPath = lpBlendPaths.begin(); iterPath != lpBlendPaths.end(); iterPath ++ ) { ((NodeBlendPath*)(*iterPath))->Extend(ExtParams); } // Karim 17/04/2000 // We're changing things so that blended objects actually extend, rather than translate. // This _may_ cause some havoc with blend-on-a-path... // translate the objects. for ( std::list::iterator iterInk = lpInks.begin(); iterInk != lpInks.end(); iterInk ++ ) { // ((NodeRenderableInk*)(*iterInk))->TransformTranslateObject(ExtParams); ((NodeRenderableInk*)(*iterInk))->Extend(ExtParams); } } /******************************************************************************************** > void NodeBlend::FindExtendingChildren( std::list& lpInks, std::list& lpBlendPaths ) Author: Karim_MacDonald (Xara Group Ltd) Created: 26/11/1999 Inputs: lpInks a NodeRenderableInk list, to fill with translating objects. lpBlendPaths a NodeBlendPath list, to fill with extending blend-paths. Outputs: The two lists passed will be filled with pointers to Nodes in the tree. Returns: Purpose: Performs a search of the children below this blend node, returning a list of object nodes which should be translated in an extension, and another list of blend-path nodes, which should be extended appropriately. We travel down the tree, building a list of objects which belong to normal blender nodes but do not also belong to blender-on-a-path nodes, and a list of blender-on-a-path nodes also. This is a recursive algorithm, as blends can lie within other blends. This recursion is carried out in a loop within this function. Errors: See also: ********************************************************************************************/ void NodeBlend::FindExtendingChildren( std::list& lpInks, std::list& lpBlendPaths ) { std::list lpBlends; // a stack of blends, used for recursion. std::list lpGroups; // a stack of groups, used for recursion. // push ourself onto the blend stack, to start all the fun! lpBlends.push_back(this); NodeBlend* pNodeBlend = NULL; // NodeGroup* pNodeGroup = NULL; // NodeRenderableInk* pInk = NULL; // NodeBlendPath* pNodeBlendPath = NULL; // // loop until both stacks are empty. while (!lpBlends.empty() || !lpGroups.empty()) { // deal with the blends first, as these are the complicated beasties. if (!lpBlends.empty()) { // pull the next blend off our blend stack, for processing. pNodeBlend = lpBlends.front(); lpBlends.pop_front(); // process each of the blend's blender objects. // in our first pass, we add each blender's paths and objects to separate // lists. we need to discard any objects belonging to both a path-blender // and a normal blender, hence the two object-lists below. std::list lpBlendedObjects; std::list lpBlendPathObjects; NodeBlender* pBlender = pNodeBlend->FindFirstBlender(); while (pBlender != NULL) { // does this blender follow a path? pNodeBlendPath = pBlender->GetNodeBlendPath(); if (pNodeBlendPath != NULL) { // yes - stick its path on the list of paths to extend. lpBlendPaths.push_back(pNodeBlendPath); // note down its nodes, so they don't land on the extending list later. pInk = pBlender->GetNodeStart(); if (pInk != NULL) // should never happen, but test anyway. lpBlendPathObjects.push_back(pInk); pInk = pBlender->GetNodeEnd(); if (pInk != NULL) // should never happen, but test anyway. lpBlendPathObjects.push_back(pInk); } // no - ok, remember its nodes in our short-list of blended objects. else { pInk = pBlender->GetNodeStart(); if (pInk != NULL) // should never happen, but test anyway. lpBlendedObjects.push_back(pInk); pInk = pBlender->GetNodeEnd(); if (pInk != NULL) // should never happen, but test anyway. lpBlendedObjects.push_back(pInk); } pBlender = pNodeBlend->FindNextBlender(pBlender); } // ok, we now have a list of blended objects, some of which we must discard // if they are also blended along a path. lpBlendPathObjects.sort(); lpBlendedObjects.sort(); std::list::iterator iterNode = lpBlendedObjects.begin(); while (!lpBlendPathObjects.empty() && !lpBlendPathObjects.empty()) { // pop the next object to discard from the front of its list. pInk = lpBlendPathObjects.front(); lpBlendPathObjects.pop_front(); // move through the object-list until we find a pointer whose value // is not less than our discard-pointer. while (iterNode != lpBlendedObjects.end() && (*iterNode) < pInk) iterNode ++; // discard any elements from the object list whose pointer value // matches our discard pointer. while (iterNode != lpBlendedObjects.end() && (*iterNode) == pInk) iterNode = lpBlendedObjects.erase(iterNode); } // we move the remaining objects onto our group, blend and object lists. // if there were two or more blenders, then some nodes may occur twice // in our lists, so we test for these and only move them across once. pInk = NULL; while (!lpBlendedObjects.empty()) { if (pInk == lpBlendedObjects.front()) { lpBlendedObjects.pop_front(); continue; } pInk = lpBlendedObjects.front(); lpBlendedObjects.pop_front(); if (IS_A(pInk, NodeGroup)) // another group on the groups stack. lpGroups.push_front((NodeGroup*)pInk); else if (IS_A(pInk, NodeBlend)) // don't think a blender can hold lpBlends.push_front((NodeBlend*)pInk); // a blend directly, but only 99% // certain, so we check for it. else if (pInk->IsAnObject()) // wahey! got another object to extend. lpInks.push_back(pInk); } } // now groups - we add any renderable-ink children to the objects list, // and any group or blend children to the appropriate recursion list. if (!lpGroups.empty()) { // we needn't worry about avoiding hidden nodes in this loop, // as we explicitly check the type of all nodes iterated over. pNodeGroup = lpGroups.front(); lpGroups.pop_front(); Node* pChild = pNodeGroup->FindFirstChild(); while (pChild != NULL) { if (IS_A(pChild, NodeGroup)) // another group on the groups stack. lpGroups.push_front((NodeGroup*)pChild); else if (IS_A(pChild, NodeBlend)) // another blend on the blends stack. lpBlends.push_front((NodeBlend*)pChild); else if (pChild->IsAnObject()) // wahey! got another object to extend. lpInks.push_back((NodeRenderableInk*)pChild); pChild = pChild->FindNext(); } } } } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- /******************************************************************************************** > virtual UINT32* BlendRecordHandler::GetTagList() Author: Mark_Neves (Xara Group Ltd) Created: 19/6/96 Inputs: - Returns: Ptr to a list of tag values, terminated by CXFRH_TAG_LIST_END Purpose: Provides the record handler system with a list of records handled by this handler SeeAlso: - ********************************************************************************************/ UINT32* BlendRecordHandler::GetTagList() { static UINT32 TagList[] = { TAG_BLEND, TAG_BLENDER, TAG_BLENDER_CURVEPROP, TAG_BLENDER_CURVEANGLES, TAG_BLEND_PATH, TAG_BLENDPROFILES, TAG_BLENDERADDITIONAL, TAG_NODEBLENDPATH_FILLED, CXFRH_TAG_LIST_END}; return (UINT32*)&TagList; } /******************************************************************************************** > virtual BOOL BlendRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord) Author: Mark_Neves (Xara Group Ltd) Created: 19/6/96 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); BOOL ok = TRUE; switch (pCXaraFileRecord->GetTag()) { case TAG_BLEND: ok = HandleBlendRecord(pCXaraFileRecord); break; case TAG_BLENDER: ok = HandleBlenderRecord(pCXaraFileRecord); break; case TAG_BLENDER_CURVEPROP: ok = HandleBlenderCurvePropRecord(pCXaraFileRecord); break; case TAG_BLENDER_CURVEANGLES: ok = HandleBlenderCurveAnglesRecord(pCXaraFileRecord); break; case TAG_BLEND_PATH: ok = HandleBlendPathRecord(pCXaraFileRecord); break; case TAG_NODEBLENDPATH_FILLED: ok = HandleBlendPathFlags(pCXaraFileRecord); break; case TAG_BLENDPROFILES: ok = HandleBlendProfileRecord(pCXaraFileRecord); break; case TAG_BLENDERADDITIONAL: ok = HandleBlenderExtraRecord(pCXaraFileRecord); break; default: ok = FALSE; ERROR3_PF(("I don't handle records with the tag (%d)\n",pCXaraFileRecord->GetTag())); break; } return ok; } /******************************************************************************************** BOOL BlendRecordHandler::HandleBlendRecord(CXaraFileRecord* pCXaraFileRecord) Author: Mark_Neves (Xara Group Ltd) Created: 19/6/96 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blend record SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlendRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLEND,FALSE,"I don't handle this tag type"); UINT16 NumSteps = 0; BYTE Flags = 0; BOOL ok = pCXaraFileRecord->ReadUINT16(&NumSteps); if (ok) ok = pCXaraFileRecord->ReadBYTE(&Flags); if (ok) { NodeBlend* pBlend = new NodeBlend; if (pBlend != NULL) { BOOL OneToOne = Flags & TAG_BLEND_FLAG_ONETOONE; BOOL Antialias = Flags & TAG_BLEND_FLAG_ANTIALIAS; BOOL Tangential= Flags & TAG_BLEND_FLAG_TANGENTIAL; BYTE ColEffect = (Flags & TAG_BLEND_COLEFFECT_MASK) >> TAG_BLEND_COLEFFECT_SHIFT; pBlend->SetOneToOne(OneToOne != 0); pBlend->SetNotAntialiased(Antialias == 0); pBlend->SetTangential(Tangential != 0); pBlend->SetNumBlendSteps(NumSteps); pBlend->SetColourBlendType(ColourBlendType(ColEffect)); ok = InsertNode(pBlend); if (ok) SetLastInsertedNodeBlend(pBlend); // load the profiles, if we have some if (m_bLoadedProfiles && ok) { CProfileBiasGain* pObjProfile = pBlend->GetObjectProfile(); if (pObjProfile == NULL) ok = FALSE; else { pObjProfile->SetBiasGain(m_LastObjectProfile.GetBias(), m_LastObjectProfile.GetGain()); pBlend->RequestObjectProfileProcessing (TRUE); } if (ok) { CProfileBiasGain* pAttrProfile = pBlend->GetAttrProfile(); if (pAttrProfile == NULL) ok = FALSE; else { pAttrProfile->SetBiasGain(m_LastAttrProfile.GetBias(), m_LastAttrProfile.GetGain()); pBlend->RequestObjectProfileProcessing (TRUE); } } if (ok) { CProfileBiasGain* pPosProfile = pBlend->GetPositionProfile(); if (pPosProfile == NULL) ok = FALSE; else pPosProfile->SetBiasGain(m_LastPositionProfile.GetBias(), m_LastPositionProfile.GetGain()); } } } else ok = FALSE; } m_bLoadedProfiles = FALSE; return ok; } /******************************************************************************************** BOOL BlendRecordHandler::HandleBlenderRecord(CXaraFileRecord* pCXaraFileRecord) Author: Mark_Neves (Xara Group Ltd) Created: 19/6/96 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blender record SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlenderRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDER,FALSE,"I don't handle this tag type"); INT32 PathIndexStart= -1; INT32 PathIndexEnd = -1; BOOL ok = pCXaraFileRecord->ReadINT32(&PathIndexStart); if (ok) ok = pCXaraFileRecord->ReadINT32(&PathIndexEnd); if (ok) { NodeBlender* pBlender = new NodeBlender; if (pBlender != NULL) { pBlender->SetPathIndexStart(PathIndexStart); pBlender->SetPathIndexEnd(PathIndexEnd); ok = InsertNode(pBlender); if (ok) SetLastInsertedNodeBlender(pBlender); } else ok = FALSE; } return ok; } /******************************************************************************************** > BOOL BlendRecordHandler::HandleBlenderCurvePropRecord(CXaraFileRecord* pCXaraFileRecord); Author: Mark_Neves (Xara Group Ltd) Created: 12/5/99 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blender curve proportion record SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlenderCurvePropRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDER_CURVEPROP,FALSE,"I don't handle this tag type"); BOOL ok = TRUE; NodeBlender* pNodeBlender = GetLastInsertedNodeBlender(); if (pNodeBlender != NULL) { double ProportionOfPathDistStart = -1.0; double ProportionOfPathDistEnd = -1.0; ok = pCXaraFileRecord->ReadDOUBLE(&ProportionOfPathDistStart); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&ProportionOfPathDistEnd); if (ok) { pNodeBlender->SetProportionOfPathDistStart(ProportionOfPathDistStart); pNodeBlender->SetProportionOfPathDistEnd(ProportionOfPathDistEnd); } } return ok; } /******************************************************************************************** > BOOL BlendRecordHandler::HandleBlenderCurveAnglesRecord(CXaraFileRecord* pCXaraFileRecord); Author: Mark_Neves (Xara Group Ltd) Created: 2/6/99 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blender curve angles record SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlenderCurveAnglesRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDER_CURVEANGLES,FALSE,"I don't handle this tag type"); BOOL ok = TRUE; NodeBlender* pNodeBlender = GetLastInsertedNodeBlender(); if (pNodeBlender != NULL) { double AngleStart = 0.0; double AngleEnd = 0.0; ok = pCXaraFileRecord->ReadDOUBLE(&AngleStart); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AngleEnd); if (ok) { pNodeBlender->SetAngleStart(AngleStart); pNodeBlender->SetAngleEnd(AngleEnd); } } return ok; } /******************************************************************************************** > BOOL BlendRecordHandler::HandleBlendPathRecord(CXaraFileRecord* pCXaraFileRecord) Author: Mark_Neves (Xara Group Ltd) Created: 12/5/99 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blend path record SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlendPathRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLEND_PATH,FALSE,"I don't handle this tag type"); BOOL ok = FALSE; NodeBlendPath* pNodeBlendPath = new NodeBlendPath; if (pNodeBlendPath != NULL && pNodeBlendPath->SetUpPath()) { ok = pCXaraFileRecord->ReadPath(&pNodeBlendPath->InkPath); if (ok) pNodeBlendPath->InkPath.InitialiseFlags(); // Init the path flags array to something sensible if (ok) ok = InsertNode(pNodeBlendPath); if (ok) { SetLastNodePathInserted(pNodeBlendPath); SetLastInsertedNodeBlendPath(pNodeBlendPath); } if (ok) { Node* pParent = pNodeBlendPath->FindParent(); if ((pParent != NULL) && (pParent->IS_KIND_OF(NodeBlend))) { NodeBlend* pNodeBlend = (NodeBlend*)pParent; pNodeBlend->SetBlendedOnCurve(TRUE); pNodeBlend->SetNumNodeBlendPathsInc(TRUE); // this deals with the case of older files that contain a blend on a curve. // these blends will contain only one NBP and all blenders will refer to that // curve. // We can tell the old files because their Index values will not have been changed // from the default value of -2. NodeBlender* pBlender = pNodeBlend->FindFirstBlender(); while (pBlender != NULL) { if (pBlender->GetNodeBlendPathIndex() == -2) pBlender->SetNodeBlendPathIndex(0); pBlender = pNodeBlend->FindNextBlender(pBlender); } } } } return ok; } /******************************************************************************************** > BOOL BlendRecordHandler::HandleNodeBlendPathFlags(CXaraFileRecord* pCXaraFileRecord) Author: Mark_Neves (Xara Group Ltd) Created: 12/5/99 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blend path filled record SeeAlso: - ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlendPathFlags(CXaraFileRecord* pXFRecord) { ERROR2IF(pXFRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pXFRecord->GetTag() != TAG_NODEBLENDPATH_FILLED,FALSE,"I don't handle this tag type"); INT32 Flag; BOOL ok = pXFRecord->ReadINT32(&Flag); if (ok) { NodeBlendPath* pNodeBlendPath = GetLastInsertedNodeBlendPath(); ERROR2IF(pNodeBlendPath == NULL, FALSE, "No NodeBlendPath"); pNodeBlendPath->InkPath.IsFilled = Flag; } return ok; } /******************************************************************************************** > BOOL BlendRecordHandler::HandleBlenderExtraRecord(CXaraFileRecord* pCXaraFileRecord) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 20/9/99 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a a blender extra info record (exact contents yet to be finalised). SeeAlso: - Notes: Karim 05/02/2001 Added BitField parameter to extra blend record. This stores whether or not a blend-on-path is reversed. Unfortunately, the extra information needs to go at the end of an existing record, so must use the dangerous ReadBYTEnoError() fn. ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlenderExtraRecord(CXaraFileRecord* pRecord) { ERROR2IF(pRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pRecord->GetTag() != TAG_BLENDERADDITIONAL,FALSE,"I don't handle this tag type"); INT32 Flag; INT32 Index; INT32 ObjIndexStart; INT32 ObjIndexEnd; BYTE BitField; BOOL ok = pRecord->ReadINT32(&Flag); if (ok) ok = pRecord->ReadINT32(&Index); if (ok) ok = pRecord->ReadINT32(&ObjIndexStart); if (ok) ok = pRecord->ReadINT32(&ObjIndexEnd); if (ok) { // if this fn fails, it means we read in an old file without BitField // info, so we arbitrarily set it to 0. if (!pRecord->ReadBYTEnoError(&BitField)) BitField = 0; } if (ok) { NodeBlender* pBlender = GetLastInsertedNodeBlender(); ERROR2IF(pBlender == NULL, FALSE, "No NodeBlender"); pBlender->SetBlendedOnCurve(Flag); pBlender->SetNodeBlendPathIndex(Index); pBlender->SetObjIndexStart(ObjIndexStart); pBlender->SetObjIndexEnd(ObjIndexEnd); pBlender->SetReversed((BitField & 1) == 1); NodeBlend* pNodeBlend = pBlender->GetNodeBlend(); ERROR2IF(pNodeBlend == NULL, FALSE, "Parent is not NodeBlend"); INT32 CurrentNodeBlendPaths = (INT32)pNodeBlend->GetNumNodeBlendPaths(); if (Index > CurrentNodeBlendPaths) pNodeBlend->SetNumNodeBlendPaths(Index+1); } return ok; } /******************************************************************************************** > BOOL BlendRecordHandler::HandleBlendProfileRecord(CXaraFileRecord* pCXaraFileRecord) Author: Diccon_Yamanaka (Xara Group Ltd) Created: 20/9/99 Inputs: pCXaraFileRecord = ptr to record to handle Returns: TRUE if handled successfuly FALSE otherwise Purpose: Handles the given record. The record has to be a blend profile record SeeAlso: - NOTE: 12/7/2000 in order to fix a bug loading v.3 blends into the browser plugin I had to reverse the order in which blend data was saved. Thus in order to load it in again we have to cache the profile data and wait for our next blend to be loaded. The cause of the plug-in problem is still unresolved... ********************************************************************************************/ BOOL BlendRecordHandler::HandleBlendProfileRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDPROFILES,FALSE,"I don't handle this tag type"); double ObjBias = 0.0; double ObjGain = 0.0; double AttrBias = 0.0; double AttrGain = 0.0; double PosBias = 0.0; double PosGain = 0.0; BOOL ok = pCXaraFileRecord->ReadDOUBLE(&ObjBias); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&ObjGain); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AttrBias); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AttrGain); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&PosBias); if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&PosGain); m_LastObjectProfile.SetBiasGain(ObjBias, ObjGain); m_LastAttrProfile.SetBiasGain(AttrBias, AttrGain); m_LastPositionProfile.SetBiasGain(PosBias, PosGain); m_bLoadedProfiles = TRUE; return ok; } /******************************************************************************************** > virtual void BlendRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr) Author: Mark_Neves (Xara Group Ltd) Created: 19/6/96 Inputs: pRecord = ptr to a record pStr = ptr to string to update Returns: - Purpose: This provides descriptions for the blend records. Errors: - SeeAlso: - ********************************************************************************************/ #ifdef XAR_TREE_DIALOG void BlendRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr) { if (pStr == NULL || pRecord == NULL) return; TCHAR s[256]; // Call base class first CamelotRecordHandler::GetRecordDescriptionText(pRecord,pStr); switch (pRecord->GetTag()) { case TAG_BLEND: { UINT16 NumSteps = 0; BYTE Flags = 0; pRecord->ReadUINT16(&NumSteps); pRecord->ReadBYTE(&Flags); camSprintf(s,_T("NumSteps\t= %hu\r\n"),NumSteps); (*pStr) += s; camSprintf(s,_T("Flags\t\t= %d\r\n"),INT32(Flags)); (*pStr) += s; (*pStr) += _T("\r\n"); camSprintf(s,_T("One to One\t= %d\r\n"),Flags & TAG_BLEND_FLAG_ONETOONE); (*pStr) += s; camSprintf(s,_T("Antialiased\t= %d\r\n"),Flags & TAG_BLEND_FLAG_ANTIALIAS); (*pStr) += s; camSprintf(s,_T("Tangential\t= %d\r\n"),Flags & TAG_BLEND_FLAG_TANGENTIAL); (*pStr) += s; BYTE ColEffect = (Flags & TAG_BLEND_COLEFFECT_MASK) >> TAG_BLEND_COLEFFECT_SHIFT; camSprintf(s,_T("Colour Effect\t= %d\r\n"),INT32(ColEffect)); (*pStr) += s; } break; case TAG_BLENDER: { INT32 PathIndexStart = -1; INT32 PathIndexEnd = -1; pRecord->ReadINT32(&PathIndexStart); pRecord->ReadINT32(&PathIndexEnd); camSprintf(s,_T("PathIndexStart\t= %d\r\n"),PathIndexStart); (*pStr) += s; camSprintf(s,_T("PathIndexEnd\t= %d\r\n"),PathIndexEnd); (*pStr) += s; } break; case TAG_BLENDER_CURVEPROP: { double PropStart = -1.0; double PropEnd = -1.0; pRecord->ReadDOUBLE(&PropStart); pRecord->ReadDOUBLE(&PropEnd); camSprintf(s,_T("Proportion of path distance start\t= %f\r\n"),PropStart); (*pStr) += s; camSprintf(s,_T("Proportion of path distance end\t= %f\r\n"),PropEnd); (*pStr) += s; } break; case TAG_BLENDER_CURVEANGLES: { double AngleStart = -1.0; double AngleEnd = -1.0; pRecord->ReadDOUBLE(&AngleStart); pRecord->ReadDOUBLE(&AngleEnd); camSprintf(s,_T("Angle start\t= %f\r\n"),AngleStart); (*pStr) += s; camSprintf(s,_T("Angle end\t= %f\r\n"),AngleEnd); (*pStr) += s; } break; case TAG_BLEND_PATH: // Use standard base class func for describing the path textually DescribePath(pRecord,pStr); break; case TAG_BLENDERADDITIONAL: { INT32 BlendedOnCurve = FALSE; pRecord->ReadINT32(&BlendedOnCurve); camSprintf(s,_T("BlendedOnCurve\t= %f\r\n"),BlendedOnCurve); (*pStr) += s; } break; case TAG_NODEBLENDPATH_FILLED: { INT32 Filled = FALSE; pRecord->ReadINT32(&Filled); camSprintf(s,_T("Filled \t= %f\r\n"),Filled); (*pStr) += s; } break; case TAG_BLENDPROFILES: double ObjBias = 0.0; double ObjGain = 0.0; double AttrBias = 0.0; double AttrGain = 0.0; double PosBias = 0.0; double PosGain = 0.0; BOOL ok = pRecord->ReadDOUBLE(&ObjBias); if (ok) { camSprintf(s,_T("Object Bias\t= %f\r\n"),ObjBias); (*pStr) += s; ok = pRecord->ReadDOUBLE(&ObjGain); } if (ok) { camSprintf(s,_T("Object Gain\t= %f\r\n"),ObjGain); (*pStr) += s; ok = pRecord->ReadDOUBLE(&AttrBias); } if (ok) { camSprintf(s,_T("Attribute Bias\t= %f\r\n"),AttrBias); (*pStr) += s; ok = pRecord->ReadDOUBLE(&AttrGain); } if (ok) { camSprintf(s,_T("Attribute Gain\t= %f\r\n"),AttrGain); (*pStr) += s; ok = pRecord->ReadDOUBLE(&PosBias); } if (ok) { camSprintf(s,_T("Position Bias\t= %f\r\n"),PosBias); (*pStr) += s; ok = pRecord->ReadDOUBLE(&PosGain); } if (ok) { camSprintf(s,_T("Position Gain\t= %f\r\n"),PosGain); (*pStr) += s; } break; } } #endif