// $Id: shapeops.cpp 1282 2006-06-09 09:46:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ /* */ #include "camtypes.h" #include "shapeops.h" //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "blobs.h" #include "csrstack.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodepath.h" #include "nodershp.h" #include "pathedit.h" //#include "peter.h" #include "progress.h" //#include "resource.h" //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "viewrc.h" #include "objchge.h" //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] #define PATRACE if(IsUserName("Peter")) TRACE // Useful macro, given a pointer to a CCObject (or an object derived // from it) and a string format, it will output the class name of the object. // Example usage PACLASSNAME(CurrentNode,"Node class = %s\n"); // N.B. No checking is node or parameters. Note semi-colon on end. #define PACLASSNAME(a,b) CCRuntimeClass* Blob; Blob = a->GetRuntimeClass(); PATRACE(b,Blob->m_lpszClassName) DECLARE_SOURCE( "$Revision: 1282 $" ); CC_IMPLEMENT_MEMDUMP(EditRegularShapeParam, OpParam) CC_IMPLEMENT_DYNCREATE(OpEditRegularShape, SelOperation) CC_IMPLEMENT_DYNCREATE(OpDragRegularShape, SelOperation) CC_IMPLEMENT_DYNCREATE(OpReformShapeEdge, SelOperation) CC_IMPLEMENT_DYNCREATE(ChangeShapeDataAction, Action) CC_IMPLEMENT_DYNCREATE(ChangeShapePointAction, Action) CC_IMPLEMENT_DYNCREATE(ChangeShapePathAction, Action) CC_IMPLEMENT_DYNAMIC(ShapeEditedMsg, Msg) CC_IMPLEMENT_DYNCREATE(OpShapeToggleBase, SelOperation) CC_IMPLEMENT_DYNCREATE(OpShapeToggleElipPoly, OpShapeToggleBase) CC_IMPLEMENT_DYNCREATE(OpShapeToggleStellation, OpShapeToggleBase) CC_IMPLEMENT_DYNCREATE(OpShapeToggleCurvature, OpShapeToggleBase) CC_IMPLEMENT_DYNCREATE(OpShapeMakeRegular, SelOperation) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides, SelOperation ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides3, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides4, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides5, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides6, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides7, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides8, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides9, OpShapeToggleSetNumSides ) CC_IMPLEMENT_DYNCREATE( OpShapeToggleSetNumSides10, OpShapeToggleSetNumSides ) // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW const DocCoord DontCare (0,0); /******************************************************************************************** > ShapeEditedMsg::ShapeEditedMsg(NodeRegularShape* pChangeShape, Spread* pParentSpread) Author: Peter_Arnold (Xara Group Ltd) Created: 17/1/95 Inputs: pChangeShape - pointer to the edited shape pParentSpread - pointer to the parent spread of the object Purpose: initilise a ShapeEditedMsg SeeAlso: ShapeEditedMsg ********************************************************************************************/ ShapeEditedMsg::ShapeEditedMsg(NodeRegularShape* pChangeShape, Spread* pParentSpread) { pShape = pChangeShape; pSpread = pParentSpread; } ////////////////////////////////////////////////////////////////////////////////////////////// // EditRegularShapeParam // ////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > EditRegularShapeParam::EditRegularShapeParam(NodeRegularShape* ChangeShapes) Author: Peter_Arnold (Xara Group Ltd) Created: 08/12/94 Inputs: ChangeShapes - pointer to the shape to change OR NULL to change all selected shapes Purpose: EditRegularShapeParam constructor - sets all member items to unchange. After constructing the object, the caller should set those items that it wants OpEditRegularShape to change. ********************************************************************************************/ EditRegularShapeParam::EditRegularShapeParam(NodeRegularShape* ChangeShapes) { ShapeToChange = ChangeShapes; ShapesToAffect = AFFECT_ALL; NewEdgePath1 = NULL; NewEdgePath2 = NULL; NewNumSides = 0; NewCircular = CHANGE_DONTCARE; NewStellated = CHANGE_DONTCARE; NewPrimaryCurvature = CHANGE_DONTCARE; NewStellationCurvature = CHANGE_DONTCARE; NewStellRadiusToPrimary = -1.0; NewPrimaryCurveToPrimary = -1.0; NewStellCurveToStell = -1.0; NewStellOffsetRatio = -1000.0; // NewCentrePoint = DontCare; // NewMajorAxes = DontCare; // NewMinorAxes = DontCare; TranslateTo = DontCare; TranslateBy = DontCare; SetRadiusLength = -1; AddRadiusLength = -1; RotateTo = -1000.0; RotateBy = -1000.0; SetStellationLength = -1; AddStellationLength = -1; SetStellationOffset = -1000.0; AddStellationOffset = -1000.0; AddPrimaryCurvature = 0.0; AddStellationCurvature = 0.0; SetMajorAxisLength = 0; AddMajorAxisLength = 0; SetMinorAxisLength = 0; AddMinorAxisLength = 0; } ////////////////////////////////////////////////////////////////////////////////////////////// // OpEditRegularShape // ////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > OpEditRegularShape::OpEditRegularShape() Author: Peter_Arnold (Xara Group Ltd) Created: 08/12/94 Purpose: OpEditRegularShape constructor - does nothing itself ********************************************************************************************/ OpEditRegularShape::OpEditRegularShape(): SelOperation() { } /******************************************************************************************** > BOOL OpEditRegularShape::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 08/12/94 Returns: TRUE if the operation could be successfully initialised FALSE if no more memory could be allocated Purpose: OpEditRegularShape initialiser method Errors: ERROR will be called if there was insufficient memory to allocate the operation. SeeAlso: - ********************************************************************************************/ BOOL OpEditRegularShape::Init() { BOOL Success = RegisterOpDescriptor(0, _R(IDS_EDITREGULARSHAPEOP), CC_RUNTIME_CLASS(OpEditRegularShape), OPTOKEN_EDITREGULARSHAPE, OpEditRegularShape::GetState); if (Success) Success = OpShapeToggleElipPoly::Init(); if (Success) Success = OpShapeToggleStellation::Init(); if (Success) Success = OpShapeToggleCurvature::Init(); if (Success) Success = OpShapeToggleSetNumSides::Init(); if (Success) Success = OpShapeMakeRegular::Init(); return Success; } /******************************************************************************************** > OpState OpEditRegularShape::GetState(String_256*, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 08/12/94 Returns: The state of the OpEditRegularShape Purpose: For finding the OpEditRegularShape's state. SeeAlso: - ********************************************************************************************/ OpState OpEditRegularShape::GetState(String_256* UIDescription, OpDescriptor*) { OpState OpSt; return OpSt; } /******************************************************************************************** > void OpEditRegularShape::DoWithParam(OpDescriptor* pOpDesc, OpParam* pEntryOpParam) Author: Peter_Arnold (Xara Group Ltd) Created: 08/12/94 Inputs: OpDescriptor (unused) pOpParam - pointer to the parameter object. Purpose: Performs the edit regular shapes operation. Errors: - SeeAlso: EditRegularShapeParam, OpEditRegularShape::CarryOut ********************************************************************************************/ void OpEditRegularShape::DoWithParam(OpDescriptor* pOpDesc, OpParam* pEntryOpParam) { EditRegularShapeParam* pOpParam = (EditRegularShapeParam*)pEntryOpParam; // Tell the operation system its kickoff time. if (DoStartSelOp(FALSE)) { BOOL Complete = TRUE; // Data used by AllowOp() to ask the node if it's ok to do the op ObjChangeFlags cFlags; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); // Either change just one shape or all selected shapes. if (pOpParam->ShapeToChange != NULL) { if (ShouldDoThisShape(pOpParam->ShapeToChange, pOpParam->ShapesToAffect)) { if (pOpParam->ShapeToChange->AllowOp(&ObjChange)) // Will node allow the op? Complete = CarryOut(pOpParam->ShapeToChange, pOpParam); } } else { // Obtain the current selections and the first node in the selection SelRange* Selected = GetApplication()->FindSelection(); Node* pNode = Selected->FindFirst(); while ((pNode != NULL) && Complete) { if (IS_A(pNode,NodeRegularShape)) { if (ShouldDoThisShape((NodeRegularShape*)pNode, pOpParam->ShapesToAffect)) { if (pNode->AllowOp(&ObjChange)) // Will node allow the op? Complete = CarryOut((NodeRegularShape*)pNode, pOpParam); } } pNode = Selected->FindNext(pNode); } } GetApplication()->FindSelection()->Update(FALSE); if (Complete) { // updated effected parents ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); Complete = UpdateChangedNodes(&ObjChange); } // See if anything failed if (!Complete) { FailAndExecute(); } } else { FailAndExecute(); } End(); } /******************************************************************************************** > BOOL OpEditRegularShape::CarryOut(NodeRegularShape* ShapeToEdit, EditRegularShapeParam* NewShapeData) Author: Peter_Arnold (Xara Group Ltd) Created: 08/12/94 Inputs: ShapeToEdit points to the Shape to change. NewShapeData points to the parameter object with the changes data. Purpose: Performs the edit regular shapes operation on one particular shape Errors: ERROR2 if there is no current DocView SeeAlso: EditRegularShapeParam, OpEditRegularShape::DoWithParam ********************************************************************************************/ BOOL OpEditRegularShape::CarryOut(NodeRegularShape* ShapeToEdit, EditRegularShapeParam* NewShapeData) { // Create an action to will cause the shape to recalc its bounds if (RecalcBoundsAction::DoRecalc(this, &UndoActions, ShapeToEdit) == AC_FAIL) { return FALSE; } // See if we should change the polygon/circular flag if (NewShapeData->NewCircular != EditRegularShapeParam::CHANGE_DONTCARE) { if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_CIRCULAR, ShapeToEdit->IsCircular() ? 1.0 : 0.0) == AC_FAIL) { return FALSE; } ShapeToEdit->SetCircular(NewShapeData->NewCircular == EditRegularShapeParam::CHANGE_SETTRUE); } // See if we should change the stellated flag if (NewShapeData->NewStellated != EditRegularShapeParam::CHANGE_DONTCARE) { /* // If were turning off stellation them we may need to fix the primary curvature ratio if (NewShapeData->NewStellated == EditRegularShapeParam::CHANGE_SETFALSE) { if (ShapeToEdit->GetPrimaryCurveToPrimary() > 0.5) NewShapeData->NewPrimaryCurveToPrimary = 0.2; } else { // If we've turned on stellation them we may need to fix both curvature ratios if (ShapeToEdit->GetPrimaryCurveToPrimary()+ShapeToEdit->GetStellCurveToStell() > 1.0) { NewShapeData->NewPrimaryCurveToPrimary = 0.2; NewShapeData->NewStellCurveToStell = 0.2; } }*/ if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLATED, ShapeToEdit->IsStellated() ? 1.0 : 0.0) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellated(NewShapeData->NewStellated == EditRegularShapeParam::CHANGE_SETTRUE); } // See if we should change the primary curvature flag if (NewShapeData->NewPrimaryCurvature != EditRegularShapeParam::CHANGE_DONTCARE) { if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_PRIMARYCURVATURE, ShapeToEdit->IsPrimaryCurvature() ? 1.0 : 0.0) == AC_FAIL) { return FALSE; } ShapeToEdit->SetPrimaryCurvature(NewShapeData->NewPrimaryCurvature == EditRegularShapeParam::CHANGE_SETTRUE); } // See if we should change the stellation curvature flag if (NewShapeData->NewStellationCurvature != EditRegularShapeParam::CHANGE_DONTCARE) { if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLATIONCURVATURE, ShapeToEdit->IsStellationCurvature() ? 1.0 : 0.0) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellationCurvature(NewShapeData->NewStellationCurvature == EditRegularShapeParam::CHANGE_SETTRUE); } // See if we should change the stellation offset if (NewShapeData->NewStellOffsetRatio != -1000.0) { if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLATIONOFFSET, ShapeToEdit->GetStellationRatio()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellationRatio(NewShapeData->NewStellOffsetRatio); } // See if we should change the number of sides if (NewShapeData->NewNumSides != 0) { if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_NUMSIDES, ShapeToEdit->GetNumSides()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetNumSides(NewShapeData->NewNumSides); } // See if we should change the stellation radius ratio if (NewShapeData->NewStellRadiusToPrimary != -1.0) { if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLRADIUSTOPRIMARY, ShapeToEdit->GetStellRadiusToPrimary()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellRadiusToPrimary(NewShapeData->NewStellRadiusToPrimary); } // See if we should change the primary curvature ratio if (NewShapeData->NewPrimaryCurveToPrimary != -1.0) { double NewRatio = NewShapeData->NewPrimaryCurveToPrimary; /* if (ShapeToEdit->IsStellated()) { double StellRatio = ShapeToEdit->GetStellCurveToStell(); if (NewShapeData->NewStellCurveToStell != -1.0) StellRatio = NewShapeData->NewStellCurveToStell; NewRatio = __min(1.0-StellRatio, NewShapeData->NewPrimaryCurveToPrimary); } else { NewRatio = __min(0.5, NewShapeData->NewPrimaryCurveToPrimary); }*/ if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, ShapeToEdit->GetPrimaryCurveToPrimary()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetPrimaryCurveToPrimary(NewRatio); } // See if we should bump the primary curvature ratio if (NewShapeData->AddPrimaryCurvature != -0.0) { double NewRatio = ShapeToEdit->GetPrimaryCurveToPrimary() + NewShapeData->AddPrimaryCurvature; /* if (ShapeToEdit->IsStellated()) { NewRatio = __min(1.0-ShapeToEdit->GetStellCurveToStell(), NewRatio); } else { NewRatio = __min(0.5, NewRatio); }*/ NewRatio = max( 0.0, NewRatio ); if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, ShapeToEdit->GetPrimaryCurveToPrimary()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetPrimaryCurveToPrimary(NewRatio); } // See if we should change the stellation curvature ratio if (NewShapeData->NewStellCurveToStell != -1.0) { double NewRatio = NewShapeData->NewStellCurveToStell; // NewRatio = __min (1.0-ShapeToEdit->GetPrimaryCurveToPrimary(), NewShapeData->NewStellCurveToStell); if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLCURVETOSTELL, ShapeToEdit->GetStellCurveToStell()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellCurveToStell(NewRatio); } // See if we should bump the stellation curvature ratio if (NewShapeData->AddStellationCurvature != 0.0) { double NewRatio = ShapeToEdit->GetStellCurveToStell() + NewShapeData->AddStellationCurvature; // NewRatio = __min(1.0-ShapeToEdit->GetPrimaryCurveToPrimary(), NewRatio); NewRatio = max( 0.0, NewRatio ); if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, ShapeToEdit->GetStellCurveToStell()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellCurveToStell(NewRatio); } // See if we should change the minor axes /* if (NewShapeData->NewMinorAxes != DontCare) { if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapePointAction::CHANGE_MINOR, ShapeToEdit->GetMinorAxes()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetMinorAxes(NewShapeData->NewMinorAxes); } // See if we should change the major axes if (NewShapeData->NewMajorAxes != DontCare) { if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapePointAction::CHANGE_MAJOR, ShapeToEdit->GetMajorAxes()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetMajorAxes(NewShapeData->NewMajorAxes); }*/ // See if we should change the size of the shape to a new length // OR see if we should change the size of the shape by a specified ammount if ((NewShapeData->SetRadiusLength != -1) || (NewShapeData->AddRadiusLength != -1) ) { // Get scaling factor fixed16 ScaleFactor; const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMajorAxes()) ; if (CurrentLength != 0.0) { if (NewShapeData->SetRadiusLength != -1) { double RequiredLength = NewShapeData->SetRadiusLength; ScaleFactor = RequiredLength / CurrentLength; } else { double RequiredAddition = NewShapeData->AddRadiusLength; ScaleFactor = (RequiredAddition + CurrentLength) / CurrentLength; } } else ScaleFactor = 1.0; // Translate to origin, scale and translate back Trans2DMatrix* TransOriginMat = new Trans2DMatrix(-ShapeToEdit->GetCentrePoint().x, -ShapeToEdit->GetCentrePoint().y); Trans2DMatrix* ScaleMat = new Trans2DMatrix(Matrix(ScaleFactor,ScaleFactor)); Trans2DMatrix* TransBackMat = new Trans2DMatrix(ShapeToEdit->GetCentrePoint().x, ShapeToEdit->GetCentrePoint().y); if (!(DoTransformNode(ShapeToEdit, TransOriginMat) && DoTransformNode(ShapeToEdit, ScaleMat) && DoTransformNode(ShapeToEdit, TransBackMat) )) { return FALSE; } } // See if we should change the MajorAxis of the shape to a new length // OR see if we should change the MajorAxis of the shape by a specified ammount if ((NewShapeData->SetMajorAxisLength != 0) || (NewShapeData->AddMajorAxisLength != 0) ) { // Get scaling factor fixed16 ScaleFactor; const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMajorAxes()) ; if (CurrentLength != 0.0) { if (NewShapeData->SetMajorAxisLength != 0) { double RequiredLength = NewShapeData->SetMajorAxisLength; ScaleFactor = RequiredLength / CurrentLength; } else { double RequiredAddition = NewShapeData->AddMajorAxisLength; ScaleFactor = (RequiredAddition + CurrentLength) / CurrentLength; } } else ScaleFactor = 1.0; // Translate to origin, rotate so major axis flat, scale, then rotate and translate back Matrix TransMat(-ShapeToEdit->GetCentrePoint().x, -ShapeToEdit->GetCentrePoint().y); DocCoord Offset = ShapeToEdit->GetMajorAxes() - ShapeToEdit->GetCentrePoint(); double Angle = atan2((double)Offset.y, (double)Offset.x)*(180/PI); TransMat*=Matrix(-ANGLE(Angle)); TransMat*=Matrix(ScaleFactor, 1.0); TransMat*=Matrix(ANGLE(Angle)); TransMat*=Matrix(ShapeToEdit->GetCentrePoint().x, ShapeToEdit->GetCentrePoint().y); Trans2DMatrix* Trans2DMat = new Trans2DMatrix(TransMat); if (Trans2DMat == NULL) return FALSE; if (!DoTransformNode(ShapeToEdit, Trans2DMat)) return FALSE; } // See if we should change the MinorAxis of the shape to a new length // OR see if we should change the MinorAxis of the shape by a specified ammount if ((NewShapeData->SetMinorAxisLength != 0) || (NewShapeData->AddMinorAxisLength != 0) ) { // Get scaling factor fixed16 ScaleFactor; const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMinorAxes()) ; if (CurrentLength != 0.0) { if (NewShapeData->SetMinorAxisLength != 0) { double RequiredLength = NewShapeData->SetMinorAxisLength; ScaleFactor = RequiredLength / CurrentLength; } else { double RequiredAddition = NewShapeData->AddMinorAxisLength; ScaleFactor = (RequiredAddition + CurrentLength) / CurrentLength; } } else ScaleFactor = 1.0; // Translate to origin, rotate so minor axis flat, scale, then rotate and translate back Matrix TransMat(-ShapeToEdit->GetCentrePoint().x, -ShapeToEdit->GetCentrePoint().y); DocCoord Offset = ShapeToEdit->GetMinorAxes() - ShapeToEdit->GetCentrePoint(); double Angle = atan2((double)Offset.y, (double)Offset.x)*(180/PI); TransMat*=Matrix(-ANGLE(Angle)); TransMat*=Matrix(ScaleFactor, 1.0); TransMat*=Matrix(ANGLE(Angle)); TransMat*=Matrix(ShapeToEdit->GetCentrePoint().x, ShapeToEdit->GetCentrePoint().y); Trans2DMatrix* Trans2DMat = new Trans2DMatrix(TransMat); if (Trans2DMat == NULL) return FALSE; if (!DoTransformNode(ShapeToEdit, Trans2DMat)) return FALSE; } // See if we should change the size of the stellation radius to a new length // OR see if we should change the size of the stellation radius by a specified ammount if ((NewShapeData->SetStellationLength != -1) || (NewShapeData->AddStellationLength != -1) ) { const double CurrentLength = ShapeToEdit->GetCentrePoint().Distance(ShapeToEdit->GetMajorAxes()) ; double NewRatio; if (NewShapeData->SetStellationLength != -1) { // Get the current length of the shape and get the ratio from that NewRatio = double( min( fabs( NewShapeData->SetStellationLength / CurrentLength ), 1.0 ) ); } else { double NewLength = fabs(CurrentLength - NewShapeData->AddStellationLength); NewRatio = double( min( fabs( ( NewLength / CurrentLength ) - 1 + ShapeToEdit->GetStellRadiusToPrimary() ), 1.0 ) ); } if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLRADIUSTOPRIMARY, ShapeToEdit->GetStellRadiusToPrimary()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellRadiusToPrimary(NewRatio); } // See if we should rotate the shape to a specific angle // OR see if we should rotate the shape by a specified ammount if ((NewShapeData->RotateTo != -1000.0) || (NewShapeData->RotateBy != -1000.0) ) { // Get rotate matrix ANGLE RotateBy; const DocCoord MajorOrigin = ShapeToEdit->GetMajorAxes() - ShapeToEdit->GetCentrePoint(); const double CurrentAngle = atan2((double)MajorOrigin.y, (double)MajorOrigin.x) ; if (NewShapeData->RotateTo != -1000.0) { RotateBy = -(CurrentAngle - NewShapeData->RotateTo) * (180/PI); } else { RotateBy = NewShapeData->RotateBy * (180/PI); } Trans2DMatrix* RotateMat = new Trans2DMatrix(ShapeToEdit->GetCentrePoint(), RotateBy); if (!DoTransformNode(ShapeToEdit, RotateMat)) { return FALSE; } } // See if we should change the stellation offset to a new angle // OR see if we should change the stellation offset by a specified ammount if ((NewShapeData->SetStellationOffset != -1000.0) || (NewShapeData->AddStellationOffset != -1000.0) ) { const UINT32 NumSides = ShapeToEdit->GetNumSides(); const double OppositeRatio = 180.0/(360.0/NumSides); double NewRatio; if (NewShapeData->SetStellationOffset != -1000.0) { NewRatio = OppositeRatio * (NewShapeData->SetStellationOffset / 180.0); } else { double NewOffset = (ShapeToEdit->GetStellationRatio()/OppositeRatio)*180.0 + NewShapeData->AddStellationOffset; if (NewOffset < -180.0) NewOffset = 180.0 + (NewOffset + 180.0); if (NewOffset > 180.0) NewOffset = -180.0 + (NewOffset - 180.0); NewRatio = OppositeRatio * (NewOffset / 180.0); } if (ChangeShapeDataAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapeDataAction::CHANGE_STELLATIONOFFSET, ShapeToEdit->GetStellationRatio()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetStellationRatio(NewRatio); } // See if we should change the centre point /* if (NewShapeData->NewCentrePoint != DontCare) { if (ChangeShapePointAction::DoToggle(this, &UndoActions, ShapeToEdit, ChangeShapePointAction::CHANGE_CENTRE, ShapeToEdit->GetCentrePoint()) == AC_FAIL) { return FALSE; } ShapeToEdit->SetCentrePoint(NewShapeData->NewCentrePoint); }*/ // See if we should translate the shape to an exact location if (NewShapeData->TranslateTo != DontCare) { DocCoord Offset = NewShapeData->TranslateTo - ShapeToEdit->GetCentrePoint() ; Trans2DMatrix* TransMat = new Trans2DMatrix(Offset.x, Offset.y); if (!DoTransformNode(ShapeToEdit, TransMat)) return FALSE; } // See if we should translate the shape by an offset if (NewShapeData->TranslateBy != DontCare) { Trans2DMatrix* TransMat = new Trans2DMatrix(NewShapeData->TranslateBy.x, NewShapeData->TranslateBy.y); if (!DoTransformNode(ShapeToEdit, TransMat)) return FALSE; } // See if we should change edge path one if (NewShapeData->NewEdgePath1 != NULL) { if (ChangeShapePathAction::DoReshape(this, &UndoActions, ShapeToEdit, ChangeShapePathAction::CHANGE_PATH1, &(ShapeToEdit->EdgePath1)) == AC_FAIL) { return FALSE; } ShapeToEdit->EdgePath1.ClearPath(); ShapeToEdit->EdgePath1.CopyPathDataFrom(NewShapeData->NewEdgePath1); } // See if we should change edge path two if (NewShapeData->NewEdgePath2 != NULL) { if (ChangeShapePathAction::DoReshape(this, &UndoActions, ShapeToEdit, ChangeShapePathAction::CHANGE_PATH2, &(ShapeToEdit->EdgePath2)) == AC_FAIL) { return FALSE; } ShapeToEdit->EdgePath2.ClearPath(); ShapeToEdit->EdgePath2.CopyPathDataFrom(NewShapeData->NewEdgePath2); } // Invalidate the shapes bounding rect as it's changed ShapeToEdit->InvalidateBoundingRect(); ShapeToEdit->InvalidateCache(); // Cause a redraw of the shape with its end bounds if (RecordBoundsAction::DoRecord(this, &UndoActions, ShapeToEdit) == AC_FAIL) { return FALSE; } return TRUE; } /******************************************************************************************** > BOOL OpEditRegularShape::ShouldDoThisShape(NodeRegularShape* pShape, EditRegularShapeParam::AffectShape Allowance) Author: Peter_Arnold (Xara Group Ltd) Created: 26/03/95 Inputs: pShape - points to a regular shape Allowance - the current shape allowance mode Outputs: - Returns: TRUE if this shape should be changed in this operation FALSE if this shape should not be changed Purpose: Do decide wether or not the shape should be edited. The decision is based upon the Allowance mode. Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpEditRegularShape::ShouldDoThisShape(NodeRegularShape* pShape, EditRegularShapeParam::AffectShape Allowance) { ERROR2IF(pShape == NULL, FALSE, "Can't edit a NULL shape!"); switch (Allowance) { case EditRegularShapeParam::AFFECT_ALL: return TRUE; break; case EditRegularShapeParam::AFFECT_ELLIPSES: return pShape->IsCircular(); break; case EditRegularShapeParam::AFFECT_RECTANGLES: return (!pShape->IsCircular() && (pShape->GetNumSides() == 4) ); break; default: ERROR3("Allowance was not known"); return FALSE; } } /////////////////////////////////////////////////////////////////////////////////////////////// // The ChangeShapeDataAction class // /////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > ChangeShapeDataAction::ChangeShapeDataAction() Author: Peter_Arnold (Xara Group Ltd) Created: 10/12/94 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action to toggle a data item of a regular state. Initialises the data members to sensible defaults. Errors: - SeeAlso: - ********************************************************************************************/ ChangeShapeDataAction::ChangeShapeDataAction() { pToggleShape = NULL; ChangeItemID = CHANGE_NUMSIDES; NewValue = 0.0; } /******************************************************************************************** > ActionCode ChangeShapeDataAction::Init( Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, double NewData, Action** NewAction) Author: Peter_Arnold (Xara Group Ltd) Created: 10/12/94 Inputs: pOp is the pointer to the operation to which this action belongs pShape is a pointer to the NodeRegularShape to change NewItem identifes the data item that shoudl be changed NewData is the value this action should set the data item to pActionList is the action list to which this action should be added Outputs: NewAction is a pointer to a pointer to an action, allowing the function to return a pointer to the created action 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. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode ChangeShapeDataAction::Init( Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, double NewData, Action** NewAction) { UINT32 ActSize = sizeof(ChangeShapeDataAction); ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeShapeDataAction), NewAction); if ((Ac == AC_OK) && (*NewAction != NULL)) { ((ChangeShapeDataAction*)*NewAction)->pToggleShape = pShape; ((ChangeShapeDataAction*)*NewAction)->ChangeItemID = NewItem; ((ChangeShapeDataAction*)*NewAction)->NewValue = NewData; } return Ac; } /******************************************************************************************** > ActionCode ChangeShapeDataAction::DoToggle(Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, double NewData) Author: Peter_Arnold (Xara Group Ltd) Created: 10/12/94 Inputs: pOp is the currently running operation. pActionList is a pointer ot the action list to which the action should be appended. pShape is a pointer to the NodeRegularShape to change. NewItem identifes the data item that shoudl be changed NewData is the value this action should set the data item to Outputs: - Returns: Action code which indicates success or failure to create the action Purpose: This static function makes it a little easier to use this action. It creates an instance of this action and appends it to the action list. Errors: - SeeAlso: - ********************************************************************************************/ ActionCode ChangeShapeDataAction::DoToggle(Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, double NewData) { // Get the runtime class info on this object and create a pointer to another object of the same type ChangeShapeDataAction* NewAction; // Now call the init function to set everything up ActionCode Act = ChangeShapeDataAction::Init(pOp, pActionList, pShape, NewItem, NewData, (Action**)&NewAction); return Act; } /******************************************************************************************** > ActionCode ChangeShapeDataAction::Execute() Author: Peter_Arnold (Xara Group Ltd) Created: 09/12/94 Inputs: - Outputs: - Returns: ActionCode, either AC_OK, AC_NORECORD or AC_FAIL Purpose: Changes a data item in a Regular shape, createing another ChangeShapeDataAction to undo the change. Errors: ERROR3 if the internal shape pointer was NULL. ERROR2 checks on individual changes. SeeAlso: - ********************************************************************************************/ ActionCode ChangeShapeDataAction::Execute() { ERROR3IF(pToggleShape == NULL, "Pointer to shape was NULL. Did you call Init/DoToggle OR handle their failure?"); // Get the runtime class info on this object and create a pointer to another object of the same type ChangeShapeDataAction *ReAction; ActionCode Act = AC_FAIL; double ReData = 0.0; switch (ChangeItemID) { case CHANGE_NUMSIDES: ReData = pToggleShape->GetNumSides(); break; case CHANGE_CIRCULAR: ReData = pToggleShape->IsCircular() ? 1.0 : 0.0; break; case CHANGE_STELLATED: ReData = pToggleShape->IsStellated() ? 1.0 : 0.0; break; case CHANGE_STELLRADIUSTOPRIMARY: ReData = pToggleShape->GetStellRadiusToPrimary(); break; case CHANGE_PRIMARYCURVATURE: ReData = pToggleShape->IsPrimaryCurvature() ? 1.0 : 0.0; break; case CHANGE_STELLATIONCURVATURE: ReData = pToggleShape->IsStellationCurvature() ? 1.0 : 0.0; break; case CHANGE_PRIMARYCURVETOPRIMARY: ReData = pToggleShape->GetPrimaryCurveToPrimary(); break; case CHANGE_STELLCURVETOSTELL: ReData = pToggleShape->GetStellCurveToStell(); break; case CHANGE_STELLATIONOFFSET: ReData = pToggleShape->GetStellationRatio(); break; default: ERROR2(Act, "What was that Change ID?!"); break; } // Create a redo action for this action if (pToggleShape != NULL) { Act = ChangeShapeDataAction::Init(pOperation, pOppositeActLst, pToggleShape, ChangeItemID, ReData, (Action**)(&ReAction)); if (Act == AC_FAIL) return AC_FAIL; // Now do the actual action switch (ChangeItemID) { case CHANGE_NUMSIDES: pToggleShape->SetNumSides((UINT32)NewValue); break; case CHANGE_CIRCULAR: pToggleShape->SetCircular(NewValue == 1.0 ? TRUE : FALSE); break; case CHANGE_STELLATED: pToggleShape->SetStellated(NewValue == 1.0 ? TRUE : FALSE); break; case CHANGE_STELLRADIUSTOPRIMARY: pToggleShape->SetStellRadiusToPrimary(NewValue); break; case CHANGE_PRIMARYCURVATURE: pToggleShape->SetPrimaryCurvature(NewValue == 1.0 ? TRUE : FALSE); break; case CHANGE_STELLATIONCURVATURE: pToggleShape->SetStellationCurvature(NewValue == 1.0 ? TRUE : FALSE); break; case CHANGE_PRIMARYCURVETOPRIMARY: pToggleShape->SetPrimaryCurveToPrimary(NewValue); break; case CHANGE_STELLCURVETOSTELL: pToggleShape->SetStellCurveToStell(NewValue); break; case CHANGE_STELLATIONOFFSET: pToggleShape->SetStellationRatio(NewValue); break; default: ERROR3("What was that Change ID?!"); break; } pToggleShape->InvalidateBoundingRect(); pToggleShape->InvalidateCache(); } return Act; } /////////////////////////////////////////////////////////////////////////////////////////////// // The ChangeShapePointAction class // /////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > ChangeShapePointAction::ChangeShapePointAction() Author: Peter_Arnold (Xara Group Ltd) Created: 13/12/94 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action to change a point in a regular shape. Initialises the data members to sensible defaults. Errors: - SeeAlso: - ********************************************************************************************/ ChangeShapePointAction::ChangeShapePointAction() { pToggleShape = NULL; ChangeItemID = CHANGE_MAJOR; NewValue = DocCoord(0,0); } /******************************************************************************************** > ActionCode ChangeShapePointAction::Init( Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, DocCoord NewData, Action** NewAction) Author: Peter_Arnold (Xara Group Ltd) Created: 10/12/94 Inputs: pOp is the pointer to the operation to which this action belongs pShape is a pointer to the NodeRegularShape to change NewItem identifes the data item that shoudl be changed NewData is the value this action should set the data item to pActionList is the action list to which this action should be added Outputs: NewAction is a pointer to a pointer to an action, allowing the function to return a pointer to the created action 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. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode ChangeShapePointAction::Init( Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, DocCoord NewData, Action** NewAction) { UINT32 ActSize = sizeof(ChangeShapePointAction); ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeShapePointAction), NewAction); if ((Ac == AC_OK) && (*NewAction != NULL)) { ((ChangeShapePointAction*)*NewAction)->pToggleShape = pShape; ((ChangeShapePointAction*)*NewAction)->ChangeItemID = NewItem; ((ChangeShapePointAction*)*NewAction)->NewValue = NewData; } return Ac; } /******************************************************************************************** > ActionCode ChangeShapePointAction::DoToggle(Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, DocCoord NewData) Author: Peter_Arnold (Xara Group Ltd) Created: 10/12/94 Inputs: pOp is the currently running operation. pActionList is a pointer ot the action list to which the action should be appended. pShape is a pointer to the NodeRegularShape to change. NewItem identifes the data item that shoudl be changed NewData is the value this action should set the data item to Outputs: - Returns: Action code which indicates success or failure to create the action Purpose: This static function makes it a little easier to use this action. It creates an instance of this action and appends it to the action list. Errors: - SeeAlso: - ********************************************************************************************/ ActionCode ChangeShapePointAction::DoToggle(Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, DocCoord NewData) { // Get the runtime class info on this object and create a pointer to another object of the same type ChangeShapePointAction* NewAction; // Now call the init function to set everything up ActionCode Act = ChangeShapePointAction::Init(pOp, pActionList, pShape, NewItem, NewData, (Action**)&NewAction); return Act; } /******************************************************************************************** > ActionCode ChangeShapePointAction::Execute() Author: Peter_Arnold (Xara Group Ltd) Created: 09/12/94 Inputs: - Outputs: - Returns: ActionCode, either AC_OK, AC_NORECORD or AC_FAIL Purpose: Changes a data item in a Regular shape, createing another ChangeShapePointAction to undo the change. Errors: ERROR3 if the internal shape pointer was NULL. ERROR2 checks on individual changes. SeeAlso: - ********************************************************************************************/ ActionCode ChangeShapePointAction::Execute() { ERROR3IF(pToggleShape == NULL, "Pointer to shape was NULL. Did you call Init/DoToggle OR handle their failure?"); // Get the runtime class info on this object and create a pointer to another object of the same type ChangeShapePointAction *ReAction; ActionCode Act = AC_FAIL; DocCoord ReData(0,0); switch (ChangeItemID) { case CHANGE_MINOR: ReData = pToggleShape->GetMinorAxes(); break; case CHANGE_MAJOR: ReData = pToggleShape->GetMajorAxes(); break; case CHANGE_CENTRE: ReData = pToggleShape->GetCentrePoint(); break; default: ERROR2(Act, "What was that Change ID?!"); break; } // Create a redo action for this action if (pToggleShape != NULL) { Act = ChangeShapePointAction::Init(pOperation, pOppositeActLst, pToggleShape, ChangeItemID, ReData, (Action**)(&ReAction)); if (Act == AC_FAIL) return AC_FAIL; // Now do the actual action switch (ChangeItemID) { case CHANGE_MINOR: pToggleShape->SetMinorAxes(NewValue); break; case CHANGE_MAJOR: pToggleShape->SetMajorAxes(NewValue); break; case CHANGE_CENTRE: pToggleShape->SetCentrePoint(NewValue); break; } pToggleShape->InvalidateBoundingRect(); pToggleShape->InvalidateCache(); } return Act; } /////////////////////////////////////////////////////////////////////////////////////////////// // The ChangeShapePathAction class // /////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > ChangeShapePathAction::ChangeShapePathAction() Author: Peter_Arnold (Xara Group Ltd) Created: 21/12/94 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action to change an edge path of a regular shape. Initialises the data members to sensible defaults. Errors: - SeeAlso: - ********************************************************************************************/ ChangeShapePathAction::ChangeShapePathAction() { pShape = NULL; ChangeItemID = CHANGE_PATH1; } /******************************************************************************************** > ActionCode ChangeShapePathAction::Init( Operation* pOp, ActionList* pActionList, NodeRegularShape* pChangeShape, enum ChangeItem NewItem, Path* NewPath, Action** NewAction) Author: Peter_Arnold (Xara Group Ltd) Created: 21/12/94 Inputs: pOp is the pointer to the operation to which this action belongs pShape is a pointer to the NodeRegularShape to change NewItem identifes the data item that shoudl be changed NewPath points to the path to store as undo infomation pActionList is the action list to which this action should be added Outputs: NewAction is a pointer to a pointer to an action, allowing the function to return a pointer to the created action 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. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode ChangeShapePathAction::Init( Operation* pOp, ActionList* pActionList, NodeRegularShape* pChangeShape, enum ChangeItem NewItem, Path* NewPath, Action** NewAction) { UINT32 ActSize = sizeof(ChangeShapePathAction); ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeShapePathAction), NewAction); if ((Ac == AC_OK) && (*NewAction != NULL)) { ((ChangeShapePathAction*)*NewAction)->pShape = pChangeShape; ((ChangeShapePathAction*)*NewAction)->ChangeItemID = NewItem; if (!((ChangeShapePathAction*)*NewAction)->NewEdge.Initialise(NewPath->GetNumCoords())) return AC_FAIL; ((ChangeShapePathAction*)*NewAction)->NewEdge.CopyPathDataFrom(NewPath); } return Ac; } /******************************************************************************************** > ActionCode ChangeShapePathAction::DoReshape(Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, Path* NewData) Author: Peter_Arnold (Xara Group Ltd) Created: 21/12/94 Inputs: pOp is the currently running operation. pActionList is a pointer ot the action list to which the action should be appended. pShape is a pointer to the NodeRegularShape to change. NewItem identifes the data item that shoudl be changed NewData poits to the path that this action should store Outputs: - Returns: Action code which indicates success or failure to create the action Purpose: This static function makes it a little easier to use this action. It creates an instance of this action and appends it to the action list. Errors: - SeeAlso: - ********************************************************************************************/ ActionCode ChangeShapePathAction::DoReshape(Operation* pOp, ActionList* pActionList, NodeRegularShape* pShape, enum ChangeItem NewItem, Path* NewData) { // Get the runtime class info on this object and create a pointer to another object of the same type ChangeShapePathAction* NewAction; // Now call the init function to set everything up ActionCode Act = ChangeShapePathAction::Init(pOp, pActionList, pShape, NewItem, NewData, (Action**)&NewAction); return Act; } /******************************************************************************************** > ActionCode ChangeShapePathAction::Execute() Author: Peter_Arnold (Xara Group Ltd) Created: 21/12/94 Inputs: - Outputs: - Returns: ActionCode, either AC_OK, AC_NORECORD or AC_FAIL Purpose: Changes a data item in a Regular shape, createing another ChangeShapePathAction to undo the change. Errors: ERROR2 if the internal shape pointer was NULL. SeeAlso: - ********************************************************************************************/ ActionCode ChangeShapePathAction::Execute() { ERROR2IF(pShape == NULL, AC_FAIL, "Pointer to shape was NULL. Did you call Init/DoToggle OR handle their failure?"); // Get the runtime class info on this object and create a pointer to another object of the same type ChangeShapePathAction *ReAction; ActionCode Act = AC_FAIL; Path* ReData; switch (ChangeItemID) { case CHANGE_PATH1: ReData = &(pShape->EdgePath1); break; case CHANGE_PATH2: ReData = &(pShape->EdgePath2); break; default: ERROR2(Act, "What was that Change ID?!"); break; } // Create a redo action for this action Act = ChangeShapePathAction::Init(pOperation, pOppositeActLst, pShape, ChangeItemID, ReData, (Action**)(&ReAction)); if (Act == AC_FAIL) return AC_FAIL; // Now do the actual action switch (ChangeItemID) { case CHANGE_PATH1: pShape->EdgePath1.CopyPathDataFrom(&NewEdge); break; case CHANGE_PATH2: pShape->EdgePath2.CopyPathDataFrom(&NewEdge); break; } pShape->InvalidateBoundingRect(); pShape->InvalidateCache(); return Act; } //////////////////////////////////////////////////////////////////////////////////////////// // OpDragRegularShape // //////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > OpDragRegularShape::OpDragRegularShape() Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Purpose: Constructor. This sets a few of the operation variables. ********************************************************************************************/ OpDragRegularShape::OpDragRegularShape() { pCursor = NULL; StartSpread = NULL; OriginalShape = NULL; EditShape = NULL; } /******************************************************************************************** > void OpDragRegularShape::DoStartDragEdit( NodeRegularShape* pShape, DocCoord Anchor, Spread *pSpread, DragType DragThing, BOOL QuickShape) Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Inputs: pShape - Pointer to the shape we are about to edit Anchor - The position of the mouse at the start of the Drag pSpread - The spread that the shape is on DragThing - the object on the shape that should be dragged QuickShape - TRUE to drag about the centres of shapes, FALSE to do special shape drags Purpose: This is called to start a drag operation on a regular shape Errors: If there was not enough memory for the setup then the user is informed and the op is aborted ********************************************************************************************/ void OpDragRegularShape::DoStartDragEdit( NodeRegularShape* pShape, DocCoord Anchor, Spread *pSpread, DragType DragThing, BOOL QuickShape) { // Adjust anchor point so this is the actual point on the shape that the drag starts from INT32 DragPointNumber = -1; pShape->DetermineClickEffect(&Anchor, pSpread, &DragPointNumber); // Take note of the drag parameters DragObject = DragThing; LastMousePos = Anchor; StartMousePos = Anchor; StartSpread = pSpread; OriginalShape = pShape; DragAroundCentre = QuickShape; // We also need to make a version of the shape that we can change EditShape = (NodeRegularShape*) OriginalShape->SimpleCopy(); BOOL Success = (EditShape != NULL); if (Success) { // Create the cursors and display it (if it was created ok) pCursor = new Cursor(TOOLID_BEZTOOL, _R(IDC_MOVEBEZIERCURSOR)); if ( pCursor != NULL && pCursor->IsValid()) { // Select the cursor CurrentCursorID = CursorStack::GPush(pCursor); } else { // Set cursor pointer to NULL to indicate its non appearence if (pCursor != NULL) delete pCursor; pCursor = NULL; } // And tell the Dragging system that we need drags to happen Success = StartDrag(DRAGTYPE_AUTOSCROLL, NULL, &LastMousePos, FALSE); // Render the initial shape if (Success) RenderDragBlobs(DocRect(), pSpread, FALSE); } // Do special rectangle setup if (Success && (DragObject == DRAG_PRIMARY) && OriginalShape->IsARectangle()) { if (DragPointNumber == -1) { ERROR3("Couldn't find the rectangle drag point"); Success = FALSE; } // Get the significant points DocCoord* PointsArray = NULL; INT32 NumberPoints = 0; Success = OriginalShape->BuildPolygonPoints(&PointsArray, &NumberPoints); // Transform the significant points to document space if (Success) { // Get the shape matrix and its inverse Matrix ShapeMat; OriginalShape->GetTransformMatrix(&ShapeMat); ShapeMat.transform((Coord*)PointsArray,NumberPoints); } // Get the points opposite to the drag point, the stationary point INT32 Opposite = 0; if (Success) Opposite = (DragPointNumber + ((NumberPoints-2)/2)) % (NumberPoints-2); // Copy the points into the array. ) is the opposite point, 2 is the drag point if (Success) { INT32 Increment = (NumberPoints-2)/4; INT32 Copy = 0; for (INT32 i = Opposite; Copy < 4; i+=Increment) RectangleArray[Copy++] = PointsArray[i%(NumberPoints-2)]; // Make the array go clockwise DocCoord temp = RectangleArray[1]; RectangleArray[1] = RectangleArray[3]; RectangleArray[3] = temp; } delete PointsArray; } if (!Success) { InformError(); FailAndExecute(); End(); } } /******************************************************************************************** > void OpDragRegularShape::DoStartDragEditCurve(NodeRegularShape* pShape, DocCoord Anchor, Spread *pSpread, DragType DragThing, DocCoord Start, DocCoord End) Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Inputs: pShape - Pointer to the shape we are about to edit Anchor - The position of the mouse at the start of the Drag pSpread - The spread that the shape is on DragThing - the object on the shape that should be dragged Start - the start of the line along which the curvature point is to be dragged End - the end of the line along which the curvature point is to be dragged. Purpose: This is called to start a drag operation on a regular shape Errors: If there was not enought memory for the setup then the user is informed and the op is aborted ********************************************************************************************/ void OpDragRegularShape::DoStartDragEditCurve( NodeRegularShape* pShape, DocCoord Anchor, Spread *pSpread, DragType DragThing, DocCoord Start, DocCoord End) { DoStartDragEdit(pShape, Anchor, pSpread, DragThing, TRUE); // Take note of the additional drag parameters LineStart = Start; LineEnd = End; } /******************************************************************************************** > virtual BOOL OpDragRegularShape::SnappingDrag() Author: Ed_Cornes (Xara Group Ltd) Created: 26/10/95 Returns: FALSE if the current drag is not of the snapping flavor ********************************************************************************************/ BOOL OpDragRegularShape::SnappingDrag() { BOOL NonSnappingDrag = (DragObject==DRAG_PRIMARYCURVE || DragObject==DRAG_STELLATIONCURVE); return !NonSnappingDrag; } /******************************************************************************************** > void OpDragRegularShape::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag) Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Inputs: PointerPos - The current position of the mouse in Doc Coords ClickMods - Which key modifiers are being pressed pSpread - The spread containing PointerPos Purpose: This is called every time the mouse moves, during a drag. We need to render off the shape in its current position, calculate any changes to apply to it then render it back on SeeAlso: ClickModifiers ********************************************************************************************/ void OpDragRegularShape::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag) { // If the PointerPois is the same as the last one, then dont bother doing anything if (PointerPos == LastMousePos) return; // If drag has moved onto a different spread, convert the coord to be relative to the // original spread. if (pSpread != StartSpread) PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos); // Rub out the old EORed version of the path RenderDragBlobs( DocRect(), StartSpread, bSolidDrag); // Now apply the changes to the EditShape, depending on the drag type BOOL Success = FALSE; switch (DragObject) { case DRAG_CENTRE: { Success = ProcessCentre(PointerPos, ClickMods); break; } case DRAG_ELLIPSE: { if (DragAroundCentre) Success = ProcessPrimary(PointerPos, ClickMods); else Success = ProcessEllipse(PointerPos, ClickMods); break; } case DRAG_PRIMARY: { if (EditShape->IsARectangle() && !DragAroundCentre) Success = ProcessRectangle(PointerPos, ClickMods); else Success = ProcessPrimary(PointerPos, ClickMods); break; } case DRAG_STELLATION: { Success = ProcessStellation(PointerPos, ClickMods); break; } case DRAG_PRIMARYCURVE: { Success = ProcessPrimaryCurve(PointerPos); break; } case DRAG_STELLATIONCURVE: { Success = ProcessStellationCurve(PointerPos); break; } default: ERROR3("What was the drag type?"); break; } if (Success) { // Draw in the new version of the path RenderDragBlobs( DocRect(), StartSpread, bSolidDrag); // Update the Last Mouse Position LastMousePos = PointerPos; // Tell anyone that's interested BROADCAST_TO_ALL(ShapeEditedMsg(EditShape, pSpread)); } else { InformError(); EndDrag(); FailAndExecute(); End(); } } /******************************************************************************************** > void OpDragRegularShape::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, BOOL Success, BOOL bSolidDrag) Author: Peter_Arnold (Xara Group Ltd) Created: 12/14/94 Inputs: PointerPos - The position of the mouse at the end of the drag ClickMods - the key modifiers being pressed Success - TRUE if the drag was terminated properly, FALSE if it was ended with the escape key being pressed Purpose: This is called when a drag operation finishes. If the drag was sucessful then the OriginalShape in the document is updated. SeeAlso: ClickModifiers ********************************************************************************************/ void OpDragRegularShape::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success, BOOL bSolidDrag) { // Do some tidying up RenderDragBlobs( DocRect(), StartSpread, bSolidDrag ); if (pCursor != NULL) { CursorStack::GPop(CurrentCursorID); delete pCursor; CurrentCursorID = 0; pCursor = NULL; } EndDrag(); BeginSlowJob(); if ( Success ) { BOOL Failed = !DoStartSelOp(FALSE, TRUE, FALSE, TRUE); //DocView* pDocView = DocView::GetSelected(); //ERROR3IF( pDocView == NULL, "There was no selected docview when editing a shape" ); // Will the original shape allow the op to happen? ObjChangeFlags cFlags; cFlags.TransformNode = TRUE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,OriginalShape,this); Failed = !OriginalShape->AllowOp(&ObjChange); if (!Failed) Failed = (RecalcBoundsAction::DoRecalc(this, &UndoActions, OriginalShape) == AC_FAIL); // Now update the shape depending on the dragtype if (!Failed) { switch (DragObject) { case DRAG_CENTRE: { // Construct a transformation matrix to move the shape Trans2DMatrix* TransMat = new Trans2DMatrix(EditShape->GetCentrePoint().x - OriginalShape->GetCentrePoint().x, EditShape->GetCentrePoint().y - OriginalShape->GetCentrePoint().y); DoTransformNode(OriginalShape, TransMat); break; } case DRAG_ELLIPSE: case DRAG_PRIMARY: { // Transform shape using the matrix Trans2DMatrix* tm = new Trans2DMatrix(transMat); Failed = ! DoTransformNode(OriginalShape, tm) ; break; } case DRAG_STELLATION: Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, ChangeShapeDataAction::CHANGE_STELLRADIUSTOPRIMARY, OriginalShape->GetStellRadiusToPrimary() ); OriginalShape->SetStellRadiusToPrimary(EditShape->GetStellRadiusToPrimary()); Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, ChangeShapeDataAction::CHANGE_STELLATIONOFFSET, OriginalShape->GetStellationRatio() ); OriginalShape->SetStellationRatio(EditShape->GetStellationRatio()); break; case DRAG_PRIMARYCURVE: Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, ChangeShapeDataAction::CHANGE_PRIMARYCURVETOPRIMARY, OriginalShape->GetPrimaryCurveToPrimary() ); OriginalShape->SetPrimaryCurveToPrimary(EditShape->GetPrimaryCurveToPrimary()); break; case DRAG_STELLATIONCURVE: Failed = !ChangeShapeDataAction::DoToggle(this, &UndoActions, OriginalShape, ChangeShapeDataAction::CHANGE_STELLCURVETOSTELL, OriginalShape->GetStellCurveToStell() ); OriginalShape->SetStellCurveToStell(EditShape->GetStellCurveToStell()); break; default: ERROR3("What was that drag type?"); break; } } // Recalculate the path's bounding box OriginalShape->InvalidateBoundingRect(); OriginalShape->InvalidateCache(); // tell the world that something in the selection has changed so that selection bounds are updated SelRange *Sel = GetApplication()->FindSelection(); Sel->Update(); // Try to expand the pasteboard if necessary to include the new bounds of the object DocRect NewBounds = Sel->GetBoundingRect(); ERROR3IF(pSpread == NULL, "Unexpectedly NULL Spread pointer"); pSpread->ExpandPasteboardToInclude(NewBounds); if (!Failed) { // Update effected parents ObjChange.Define(OBJCHANGE_FINISHED,cFlags,OriginalShape,this); Failed = !UpdateChangedNodes(&ObjChange); } if (!Failed && OriginalShape->FindParentSpread()!=NULL) Failed = (RecordBoundsAction::DoRecord(this, &UndoActions, OriginalShape) == AC_FAIL) ; // If any of the above failed the kill the op if (Failed) { // PATRACE( _T("An action failed!\n")); FailAndExecute(); } } else { // Set up the flags that say it all went wrong FailAndExecute(); } delete (EditShape); End(); } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessPrimaryCurve( DocCoord PointerPos ) Author: Peter_Arnold (Xara Group Ltd) Created: 4/1/94 Inputs: PointerPos - The current position of the mouse in Doc Coords Purpose: This is called to update the EditShape after a mouse movement in a primary curvature point drag. SeeAlso: OpDragRegularShape::DragPointerMove ********************************************************************************************/ BOOL OpDragRegularShape::ProcessPrimaryCurve( DocCoord PointerPos ) { // Untransform the various things Matrix Mat; EditShape->GetTransformMatrix(&Mat); Mat = Mat.Inverse(); DocCoord UTLineStart = LineStart; DocCoord UTLineEnd = LineEnd; DocCoord UTPointerPos = PointerPos; Mat.transform(&UTLineStart); Mat.transform(&UTLineEnd); Mat.transform(&UTPointerPos); // Move the point along the edge line to get new ratio double c = UTLineStart.Distance(UTPointerPos); double b = fabs(DocCoord::DistanceFromLine(UTLineStart, UTLineEnd, UTPointerPos)); double a = sqrt(c*c - b*b); double NewRatio = a/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint()); // Detect a zero ratio case (otherwise the point moves back along the line) if (!IsJoinAcute(&UTLineStart, &UTLineEnd, &UTPointerPos)) { NewRatio = 0.0; } else { // Restrict to fit with along the edge if (EditShape->IsStellated()) { // Get the position of the stellation curvature point and its distance const double OuterMajorLength = OriginalShape->GetMajorRadiusSize(); const double StellationCurvatureLength = OuterMajorLength * OriginalShape->GetStellCurveToStell(); const double AvailableLength = (UTLineStart.Distance(UTLineEnd)) - StellationCurvatureLength; double top = a; if ( (AvailableLength - a) < 1.0) top = AvailableLength; NewRatio = top/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint()); } else { // We need to limit the drag to halfway along the drag line const double MaxLength = UTLineStart.Distance(UTLineEnd)/2.0; if (a > MaxLength) NewRatio = MaxLength/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint()); } } EditShape->SetPrimaryCurveToPrimary(NewRatio); return TRUE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessStellationCurve( DocCoord PointerPos ) Author: Peter_Arnold (Xara Group Ltd) Created: 6/1/94 Inputs: PointerPos - The current position of the mouse in Doc Coords Purpose: This is called to update the EditShape after a mouse movement in a stellation curvature point drag. SeeAlso: OpDragRegularShape::DragPointerMove ********************************************************************************************/ BOOL OpDragRegularShape::ProcessStellationCurve( DocCoord PointerPos ) { // Untransform the various things Matrix Mat; EditShape->GetTransformMatrix(&Mat); Mat = Mat.Inverse(); DocCoord UTLineStart = LineStart; DocCoord UTLineEnd = LineEnd; DocCoord UTPointerPos = PointerPos; Mat.transform(&UTLineStart); Mat.transform(&UTLineEnd); Mat.transform(&UTPointerPos); // Move the point along the edge line to get new ratio double c = UTLineStart.Distance(UTPointerPos); double b = fabs(DocCoord::DistanceFromLine(UTLineStart, UTLineEnd, UTPointerPos)); double a = sqrt(c*c - b*b); double NewRatio = a/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint()); // Detect a zero ratio case (otherwise the point moves back along the line) if (!IsJoinAcute(&UTLineStart, &UTLineEnd, &UTPointerPos)) { NewRatio = 0.0; } else { // Restrict to fit with along the edge const double OuterMajorLength = OriginalShape->GetMajorRadiusSize(); const double PrimaryCurvatureLength = OuterMajorLength * OriginalShape->GetPrimaryCurveToPrimary(); const double AvailableLength = (UTLineStart.Distance(UTLineEnd)) - PrimaryCurvatureLength; double top = a; if ( (AvailableLength - a) < 1.0) top = AvailableLength; NewRatio = top/EditShape->GetUTMajorAxes().Distance(EditShape->GetUTCentrePoint()); } EditShape->SetStellCurveToStell(NewRatio); return TRUE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessStellation( DocCoord PointerPos, ClickModifiers Mods ) Author: Peter_Arnold (Xara Group Ltd) Created: 9/1/94 Inputs: PointerPos - The current position of the mouse in Doc Coords Purpose: This is called to update the EditShape after a mouse movement in a stellation point drag. SeeAlso: OpDragRegularShape::DragPointerMove ********************************************************************************************/ BOOL OpDragRegularShape::ProcessStellation( DocCoord PointerPos, ClickModifiers Mods) { // Constrain the mouse movement if (Mods.Constrain) { WheelConstrain(&PointerPos, StartMousePos); } // Snap the mouse point to the active grid DocView::SnapCurrent(StartSpread,&PointerPos); // Untransform the current pointer pos DocCoord UTPointerPos = GetUTPosition(PointerPos); DocCoord UTStartPos = GetUTPosition(StartMousePos); // Set the new stellation radius length double PrimaryRadiusLength = OriginalShape->GetUTMajorAxes().Distance(OriginalShape->GetUTCentrePoint()); double CurrentRadiusLength = UTPointerPos.Distance(OriginalShape->GetUTCentrePoint()); double NewRadius = min( CurrentRadiusLength / PrimaryRadiusLength, 1.0 ); EditShape->SetStellRadiusToPrimary(NewRadius); // Now set the new stellation point offset // Get angle between the Major axes point and the pointer pos DocCoord OldPos = UTStartPos - OriginalShape->GetUTCentrePoint(); DocCoord NewPos = UTPointerPos - OriginalShape->GetUTCentrePoint(); if (NewPos != DocCoord(0,0)) { double OrigAngle = atan2((double)OldPos.y, (double)OldPos.x); double NewAngle = atan2((double)NewPos.y, (double)NewPos.x); double OppositeRatio = 180.0/(360.0/EditShape->GetNumSides()) ; double SettingRatio = ((NewAngle-OrigAngle) / ((2*PI)/EditShape->GetNumSides()) ) + OriginalShape->GetStellationRatio(); while (SettingRatio > OppositeRatio*2) SettingRatio -= OppositeRatio*2; while (SettingRatio < -OppositeRatio*2) SettingRatio += OppositeRatio*2; if (SettingRatio > OppositeRatio) SettingRatio = -OppositeRatio + (SettingRatio-OppositeRatio); if (SettingRatio < -OppositeRatio) SettingRatio = OppositeRatio + (SettingRatio+OppositeRatio); EditShape->SetStellationRatio(SettingRatio); } else EditShape->SetStellationRatio(0.0); return TRUE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessPrimary( DocCoord PointerPos, ClickModifiers Mods ) Author: Peter_Arnold (Xara Group Ltd) Created: 9/1/94 Inputs: PointerPos - The current position of the mouse in Doc Coords Purpose: This is called to update the EditShape after a mouse movement in a primary point drag. SeeAlso: OpDragRegularShape::DragPointerMove, OpDragRegularShape::ProcessRadius ********************************************************************************************/ BOOL OpDragRegularShape::ProcessPrimary( DocCoord PointerPos, ClickModifiers Mods ) { if (Mods.Constrain) { WheelConstrain(&PointerPos, StartMousePos); } return ProcessRadius(PointerPos, Mods); } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessEllipse( DocCoord PointerPos, ClickModifiers Mods ) Author: Peter_Arnold (Xara Group Ltd) Created: 08/04/95 Inputs: PointerPos - The current position of the mouse in Doc Coords Mods - the drag modifiers Purpose: This is called to update the ellipitical EditShape after a mouse movement during a drag. SeeAlso: OpDragRegularShape::DragPointerMove, OpDragRegularShape::ProcessRectangle ********************************************************************************************/ BOOL OpDragRegularShape::ProcessEllipse( DocCoord PointerPos, ClickModifiers Mods ) { // If this is an adjust drag then work around the centre if (Mods.Adjust) { if (Mods.Constrain) WheelConstrain(&PointerPos, StartMousePos); return ProcessRadius(PointerPos, Mods); } // Snap the mouse point to the active grid DocView::SnapCurrent(StartSpread,&PointerPos); // Untransform the current pointer pos DocCoord UTPointerPos = GetUTPosition(PointerPos); const DocCoord UTCentre = OriginalShape->GetUTCentrePoint(); const DocCoord Centre = OriginalShape->GetCentrePoint(); Matrix OriginalMat; OriginalShape->GetTransformMatrix(&OriginalMat); DocCoord UTStartPos = GetUTPosition(StartMousePos); DocCoord UTOppositePos = DocCoord::PositionPointFromRatio(UTCentre, UTStartPos, -1.0); DocCoord OppositePos = UTOppositePos; OriginalMat.transform(&OppositePos); // Get rotation of original and current dragging points DocCoord StartDragPoint = StartMousePos - OppositePos; double OrigRotation = atan2((double)StartDragPoint.y, (double)StartDragPoint.x); // Get the scaling factor const double OriginalLength = OppositePos.Distance(StartMousePos); const double Ax = (StartMousePos.x-OppositePos.x); const double Ay = (StartMousePos.y-OppositePos.y); const double Bx = (PointerPos.x-OppositePos.x); const double By = (PointerPos.y-OppositePos.y); const double NewLength = ((Ax*Bx) + (Ay*By)) / sqrt((Ax*Ax) + (Ay*Ay)); double ScaleFactor = NewLength/OriginalLength; // Transform to origin, rotate so drag point to opposite lies on the origin, // scale, rotate back to current rotation, then transform back Matrix Transform(-OppositePos.x, -OppositePos.y); Transform*= Matrix(ANGLE(-(OrigRotation*(180/PI)))); Transform*= Matrix(fixed16(ScaleFactor),fixed16(1.0)); Transform*= Matrix(ANGLE(OrigRotation*(180/PI))); Transform*= Matrix(OppositePos.x, OppositePos.y); Trans2DMatrix Trans(Transform); // Copy the original shape then apply the transform delete EditShape; EditShape = (NodeRegularShape*)OriginalShape->SimpleCopy(); if (EditShape != NULL) { EditShape->Transform(Trans); transMat = Transform; return TRUE; } else return FALSE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessRectangle( DocCoord PointerPos, ClickModifiers Mods ) Author: Peter_Arnold (Xara Group Ltd) Created: 08/04/95 Inputs: PointerPos - The current position of the mouse in Doc Coords Mods - the drag modifiers Purpose: This is called to update a rectangular EditShape after a mouse movement during a drag on one if its corners SeeAlso: OpDragRegularShape::DragPointerMove, OpDragRegularShape::ProcessEllipse ********************************************************************************************/ BOOL OpDragRegularShape::ProcessRectangle( DocCoord PointerPos, ClickModifiers Mods ) { // If this is an adjust drag then work around the centre if (Mods.Adjust) { if (Mods.Constrain) WheelConstrain(&PointerPos, StartMousePos); return ProcessRadius(PointerPos, Mods); } // See which line the mouse is nearest to determine the type of the drag if (Mods.Constrain) { double Diagonal = DocCoord::DistanceFromLine(RectangleArray[2], RectangleArray[0], PointerPos); double Prev = DocCoord::DistanceFromLine(RectangleArray[2], RectangleArray[1], PointerPos); double Next = DocCoord::DistanceFromLine(RectangleArray[2], RectangleArray[3], PointerPos); if ((Diagonal <= Prev) && (Diagonal <= Next)) return ProcessRectangleDiagonal(PointerPos, Mods); else { if (Prev <= Next) return ProcessRectangleEdge(PointerPos, RectangleArray[1]); else return ProcessRectangleEdge(PointerPos, RectangleArray[3]); } } else return ProcessRectangleDiagonal(PointerPos, Mods); } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessRectangleDiagonal( DocCoord PointerPos, ClickModifiers Mods ) Author: Peter_Arnold (Xara Group Ltd) Created: 12/12/95 Inputs: PointerPos - The current position of the mouse in Doc Coords Mods - the drag modifiers Purpose: This is called to update a rectangular EditShape after a mouse movement during a drag on one if its corners. The rectangle is resized about the opposite corner to the drag corner. SeeAlso: OpDragRegularShape::ProcessRectangleEdge ********************************************************************************************/ BOOL OpDragRegularShape::ProcessRectangleDiagonal( DocCoord PointerPos, ClickModifiers Mods ) { // Constrain the mouse so the aspect of the shape is maintained if (Mods.Constrain) { // Get the scaling factor const double RectDiagonal = RectangleArray[0].Distance(RectangleArray[2]); const double Ax = (RectangleArray[2].x-RectangleArray[0].x); const double Ay = (RectangleArray[2].y-RectangleArray[0].y); const double Bx = (PointerPos.x-RectangleArray[0].x); const double By = (PointerPos.y-RectangleArray[0].y); const double NewLength = ((Ax*Bx) + (Ay*By)) / sqrt((Ax*Ax) + (Ay*Ay)); double ratio = NewLength/RectDiagonal; // reposition the pointer PointerPos = DocCoord::PositionPointFromRatio(RectangleArray[0], RectangleArray[2], ratio); } // Snap the mouse point to the active grid DocView::SnapCurrent(StartSpread,&PointerPos); // Get the shape matrix and its inverse Matrix ShapeMat; OriginalShape->GetTransformMatrix(&ShapeMat); Matrix InvShapeMat = ShapeMat; InvShapeMat = InvShapeMat.Inverse(); // Copy the vertext array for changing DocCoord VertexArray[4]; for (INT32 loop = 0; loop < 4; loop++) VertexArray[loop] = RectangleArray[loop]; // Get the matrix to take the original shape down to the unit square double OrigA = RectangleArray[3].x - RectangleArray[0].x; double OrigB = RectangleArray[3].y - RectangleArray[0].y; double OrigC = RectangleArray[1].x - RectangleArray[0].x; double OrigD = RectangleArray[1].y - RectangleArray[0].y; double OrigE = RectangleArray[0].x; double OrigF = RectangleArray[0].y; // Invert this double Det = OrigA*OrigD - OrigB*OrigC; ERROR2IF(Det==0.0, FALSE, "Matrix Inversion Failed"); double InvertA = OrigD / Det; double InvertB = -OrigB / Det; double InvertC = -OrigC / Det; double InvertD = OrigA / Det; double InvertE = ((OrigC*OrigF) - (OrigE*OrigD))/Det; double InvertF = -(((OrigA*OrigF) - (OrigE*OrigB))/Det); // Now transform the vertex points // Calculate the offsets from the old point and the fixed point double dx0 = PointerPos.x - RectangleArray[2].x; double dy0 = PointerPos.y - RectangleArray[2].y; double dx3 = RectangleArray[1].x - RectangleArray[0].x; double dy3 = RectangleArray[1].y - RectangleArray[0].y; double dx4 = RectangleArray[3].x - RectangleArray[0].x; double dy4 = RectangleArray[3].y - RectangleArray[0].y; // Calculate the bits that we need to know double dx1 = dx3 * ((dy0*dx4-dy4*dx0) / (dx4*dy3-dy4*dx3)); double dy1 = dy3 * ((dy0*dx4-dy4*dx0) / (dx4*dy3-dy4*dx3)); double dx2 = dx4 * ((dx3*dy0-dy3*dx0) / (dy4*dx3-dx4*dy3)); double dy2 = dy4 * ((dx3*dy0-dy3*dx0) / (dy4*dx3-dx4*dy3)); // Change the parallelogram VertexArray[1].x = RectangleArray[1].x + (INT32)dx1; VertexArray[1].y = RectangleArray[1].y + (INT32)dy1; VertexArray[3].x = RectangleArray[3].x + (INT32)dx2; VertexArray[3].y = RectangleArray[3].y + (INT32)dy2; VertexArray[2] = PointerPos; // Get the array from unit to the new position double NewA = VertexArray[3].x - VertexArray[0].x; double NewB = VertexArray[3].y - VertexArray[0].y; double NewC = VertexArray[1].x - VertexArray[0].x; double NewD = VertexArray[1].y - VertexArray[0].y; double NewE = VertexArray[0].x; double NewF = VertexArray[0].y; // multiply the two matrices double FinalA = (InvertA * NewA) + (InvertB * NewC); double FinalB = (InvertA * NewB) + (InvertB * NewD); double FinalC = (InvertC * NewA) + (InvertD * NewC); double FinalD = (InvertC * NewB) + (InvertD * NewD); INT32 FinalE = (INT32)(NewA*InvertE + NewC*InvertF + NewE); INT32 FinalF = (INT32)(NewB*InvertE + NewD*InvertF + NewF); // apply to the rectangle Trans2DMatrix Transform(Matrix(FinalA, FinalB, FinalC, FinalD, FinalE, FinalF)); delete EditShape; EditShape = new (NodeRegularShape); if (EditShape != NULL) OriginalShape->CopyNodeContents(EditShape); else return FALSE; EditShape->Transform(Transform); transMat = Transform.GetMatrix(); return TRUE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessRectangleEdge( DocCoord PointerPos, DocCoord OtherEnd ) Author: Peter_Arnold (Xara Group Ltd) Created: 12/12/95 Inputs: PointerPos - The current position of the mouse in Doc Coords OtherEnd - the other corner of the rectangle from the corner being dragged, the drag is constrained along this line Purpose: This is called during a rectangle drag to resize the rectangle about either edge SeeAlso: OpDragRegularShape::ProcessRectangleDiagonally ********************************************************************************************/ BOOL OpDragRegularShape::ProcessRectangleEdge( DocCoord PointerPos, DocCoord OtherEnd ) { // Work in untransformed space to remove skew const DocCoord DraggingEnd = GetUTPosition(RectangleArray[2]); OtherEnd = GetUTPosition(OtherEnd); PointerPos = GetUTPosition(PointerPos); Matrix TransMat; OriginalShape->GetTransformMatrix(&TransMat); TransMat = TransMat.Inverse(); // Translate rectangle so other (stationary) corner is on origin TransMat *= Matrix(-OtherEnd.x, -OtherEnd.y); // Rotate rectangle so drag edge lies along x-axis double RotAngle = atan2((double)DraggingEnd.y-OtherEnd.y, (double)DraggingEnd.x-OtherEnd.x); TransMat *= Matrix(ANGLE(-RotAngle*(180/PI))); // Scale along x-axis double OrigDragEdgeLength = OtherEnd.Distance(DraggingEnd); double c = OtherEnd.Distance(PointerPos); double a = DocCoord::DistanceFromLine(OtherEnd, DraggingEnd, PointerPos); double NewDragEdgeLength = sqrt((c*c) - (a*a)); double ScaleFactor = (OrigDragEdgeLength == 0.0) ? 1.0 : NewDragEdgeLength/OrigDragEdgeLength; if (!IsJoinAcute(&OtherEnd, &DraggingEnd, &PointerPos)) ScaleFactor = -ScaleFactor; TransMat *= Matrix (FIXED16(ScaleFactor), FIXED16(1.0)); // Rotate back TransMat *= Matrix(ANGLE(RotAngle*(180/PI))); // Translate back TransMat *= Matrix(OtherEnd.x, OtherEnd.y); // And finally apply the original transform back Matrix OrigMatrix; OriginalShape->GetTransformMatrix(&OrigMatrix); TransMat *= OrigMatrix; // Apply this to the edit shape delete EditShape; EditShape = new (NodeRegularShape); if (EditShape != NULL) { OriginalShape->CopyNodeContents(EditShape); Trans2DMatrix matrix( TransMat ); EditShape->Transform( matrix ); transMat = TransMat; return TRUE; } else return FALSE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessCentre( DocCoord PointerPos, ClickModifiers ClickMods ) Author: Peter_Arnold (Xara Group Ltd) Created: 15/12/94 Inputs: PointerPos - The current position of the mouse in Doc Coords ClickMods - Which key modifiers are being pressed Purpose: This is called to update the EditShape after a mouse movement in a centre point drag. The shape is being translated. Constrain works from angles from the original centre point of the shape. SeeAlso: OpDragRegularShape::DragPointerMove ********************************************************************************************/ BOOL OpDragRegularShape::ProcessCentre( DocCoord PointerPos, ClickModifiers ClickMods ) { // Constrain and snap the mouse point if required DocCoord OldCentre = OriginalShape->GetCentrePoint(); if (ClickMods.Constrain) DocView::ConstrainToAngle(OldCentre, &PointerPos); DocView::SnapCurrent(StartSpread,&PointerPos); // Translate shape based on the movement. DocCoord Offset = PointerPos - EditShape->GetCentrePoint(); Trans2DMatrix Trans(Offset.x, Offset.y); EditShape->Transform(Trans); return TRUE; } /******************************************************************************************** > BOOL OpDragRegularShape::ProcessRadius( DocCoord PointerPos, ClickModifiers ClickMods) Author: Peter_Arnold (Xara Group Ltd) Created: 23/2/95 Inputs: PointerPos - The current position of the mouse in Doc Coords ClickMods - Which key modifiers are being pressed Purpose: This is called to process drags on the primary points and on the major axes. SeeAlso: - ********************************************************************************************/ BOOL OpDragRegularShape::ProcessRadius(DocCoord Pos, ClickModifiers Mods) { // Snap the mouse point to the active grid DocView::SnapCurrent(StartSpread,&Pos); // Untransform the current pointer pos DocCoord UTPointerPos = GetUTPosition(Pos); const DocCoord UTCentre = OriginalShape->GetUTCentrePoint(); const DocCoord Centre = OriginalShape->GetCentrePoint(); // Get rotation of original and current primary points DocCoord CurrentPrimary = StartMousePos - Centre; DocCoord NewPrimary = Pos - Centre; double OrigPrimaryRotation = atan2((double)CurrentPrimary.y, (double)CurrentPrimary.x); double NewPrimaryRotation = atan2((double)NewPrimary.y, (double)NewPrimary.x); ANGLE RotBy(-(OrigPrimaryRotation - NewPrimaryRotation)*(180/PI)); // Get the scaling factor double NewLength = Pos.Distance(Centre); double CurrentLength = StartMousePos.Distance(Centre); double ScaleFactor = (CurrentLength != 0.0) ? (NewLength/CurrentLength) : 0.001; // Transform to origin, scale, rotate then transform back Matrix Transform(-Centre.x, -Centre.y); Transform*= Matrix(RotBy); Transform*= Matrix(fixed16(ScaleFactor),fixed16(ScaleFactor)); Transform*= Matrix(Centre.x, Centre.y); Trans2DMatrix Trans(Transform); // Copy the original shape then apply the transform delete EditShape; EditShape = (NodeRegularShape*)OriginalShape->SimpleCopy(); if (EditShape != NULL) { EditShape->Transform(Trans); transMat = Transform; return TRUE; } else return FALSE; } /******************************************************************************************** > void OpDragRegularShape::WheelConstrain(DocCoord* PointerPos, const DocCoord StartPos) Author: Peter_Arnold (Xara Group Ltd) Created: 10/1/94 Inputs: PointerPos - The current position of the mouse in Doc Coords StartPos - The outer constrain location. Outputs: PointerPos is constrained to the shape described below Returns: - Purpose: This is called to constrain a point to a steering-wheel shape. The wheel is centred on the centre of OriginalShape. It has radius of the distance from the centre to StartPos. The other constrain line is the line passing through the centre point and StartPos. SeeAlso: - ********************************************************************************************/ void OpDragRegularShape::WheelConstrain(DocCoord* PointerPos, const DocCoord StartPos) { // Obtain perpendicular distance from StartPos DocCoord LineConstrainPoint = StartPos; DocCoord RotStartPos = StartPos; Matrix Mat(-OriginalShape->GetCentrePoint().x, -OriginalShape->GetCentrePoint().y); Mat *= Matrix(ANGLE(90)); Mat *= Matrix(OriginalShape->GetCentrePoint().x, OriginalShape->GetCentrePoint().y); Mat.transform(&RotStartPos); double LineDistance1 = DocCoord::DistanceFromLine(OriginalShape->GetCentrePoint(), StartPos, *PointerPos); double LineDistance2 = DocCoord::DistanceFromLine(OriginalShape->GetCentrePoint(), RotStartPos, *PointerPos); BOOL UseRotated = FALSE; double LineDistance = LineDistance1; if (LineDistance2 < LineDistance) { UseRotated = TRUE; LineDistance = LineDistance2; } // Now obtain distance the pointer position is from the circumference of the circle double Radius = OriginalShape->GetCentrePoint().Distance(StartPos); const double CurrentRadius = OriginalShape->GetCentrePoint().Distance(*PointerPos); const double CircleDistance = fabs(Radius - CurrentRadius); Radius = OriginalShape->GetCentrePoint().Distance(StartPos); /* // Now if the pointer is past the centre point on the other side of the shape from StartPos // then we want the distance from the centre point if (!OriginalShape->IsCircular()) { DocCoord StartOrig = StartPos - OriginalShape->GetCentrePoint(); DocCoord PointerOrig = *PointerPos - OriginalShape->GetCentrePoint(); double StartAngle = atan2((double)StartOrig.y, (double)StartOrig.x); double PointerAngle = atan2((double)PointerOrig.y, (double)PointerOrig.x); if ((PointerAngle < StartAngle-(PI/2)) || (PointerAngle > StartAngle+(PI/2)) ) LineDistance = CircleDistance*2; }*/ // Now constrain to either the line or the circumference if ((CircleDistance < LineDistance) && (CurrentRadius >= 0.0001)) { // Constrain to the circle circumference *PointerPos = DocCoord::PositionPointFromRatio(OriginalShape->GetCentrePoint(), *PointerPos, Radius/CurrentRadius); } else { // Constrain to the line if (UseRotated) *PointerPos = DocCoord::PositionPointFromRatio(OriginalShape->GetCentrePoint(), RotStartPos, CurrentRadius/Radius); else *PointerPos = DocCoord::PositionPointFromRatio(OriginalShape->GetCentrePoint(), StartPos, CurrentRadius/Radius); } } /******************************************************************************************** > void OpDragRegularShape::GetUTPosition(DocCoord TransedPos) Author: Peter_Arnold (Xara Group Ltd) Created: 23/2/95 Inputs: TransedPos - A position in the OriginalShape's translated space Outputs: - Returns: The position untranslated back to the OriginalShape Purpose: This is called to convert a current mouse postion to untransform back to the coordinates used by the untranslated OriginalShape. SeeAlso: - ********************************************************************************************/ DocCoord OpDragRegularShape::GetUTPosition(DocCoord TransedPos) { Matrix TransMat; OriginalShape->GetTransformMatrix(&TransMat); TransMat = TransMat.Inverse(); TransMat.transform(&TransedPos); return TransedPos; } /******************************************************************************************** > void OpDragRegularShape::RenderDragBlobs( DocRect Rect, SPread* pSpread, BOOL bSolidDrag ) Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Purpose: Renders the current version of the shape to the window. ********************************************************************************************/ void OpDragRegularShape::RenderDragBlobs( DocRect Rect, Spread *pSpread, BOOL bSolidDrag ) { RenderRegion* pRegion = DocView::RenderOnTop(NULL, pSpread, ClippedEOR ); while ( pRegion ) { // Set the line colour pRegion -> SetFillColour(COLOUR_NONE); pRegion -> SetLineColour(COLOUR_XOREDIT); // And draw an EORed version of how the shape will turn out EditShape->RenderEorDrag( pRegion ); // Get the Next render region pRegion = DocView::GetNextOnTop(NULL); } } /******************************************************************************************** > BOOL OpDragRegularShape::IsJoinAcute(const DocCoord* pJoin, const DocCoord* pOtherEndOfJoin, const DocCoord* pOtherPoint); Author: Peter_Arnold (Xara Group Ltd) Created: 13/12/95 Inputs: pJoin - the coordinate where two lines join pOtherEndOfJoin - another point that with pJoin defines a line pOtherPoint - another point that with pJoin defines another line Outputs: - Returns: TRUE if the angle between the two lines is an acute angle Purpose: For seeing the perpendicual distance from pOtherPoint to the line pJoin-pOtherEndOfJoin end on the line or before pJoin. Used when dragging along a line to see if the drag point has gon back beyond the start of the line. ********************************************************************************************/ BOOL OpDragRegularShape::IsJoinAcute(const DocCoord* pJoin, const DocCoord* pOtherEndOfJoin, const DocCoord* pOtherPoint) { return DocCoord::IsJoinAcute(pJoin, pOtherEndOfJoin, pOtherPoint); } /******************************************************************************************** > BOOL OpDragRegularShape::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Returns: TRUE if all went OK, False otherwise Purpose: Adds the operation to the list of all known operations ********************************************************************************************/ BOOL OpDragRegularShape::Init() { return (RegisterOpDescriptor( 0, _R(IDS_EDITREGULARSHAPEOP), CC_RUNTIME_CLASS(OpDragRegularShape), OPTOKEN_DRAGREGULARSHAPE, OpDragRegularShape::GetState)); } /******************************************************************************************** > OpState OpDragRegularShape::GetState(String_256* Description, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 14/12/94 Outputs: - Returns: The state of the operation, so that menu items (ticks and greying can be done properly Purpose: Find out the state of the operation at the specific time ********************************************************************************************/ OpState OpDragRegularShape::GetState(String_256* Description, OpDescriptor*) { OpState Blobby; return Blobby; } /******************************************************************************************** > OpReformShapeEdge::OpReformShapeEdge() Author: Peter_Arnold (Xara Group Ltd) Created: 19/12/94 Inputs: - Outputs: - Returns: - Purpose: Constructor - set member variables to default values. ********************************************************************************************/ OpReformShapeEdge::OpReformShapeEdge() { StartSpread = NULL; EditShape = NULL; EditEdge = NULL; } /******************************************************************************************** > void OpReformShapeEdge::DoStartDragEdit(NodeRegularShape* OrigShape, DocCoord Anchor, Spread *pSpread, INT32 SegmentIndex, double pdist) Author: Peter_Arnold (Xara Group Ltd) - from Jim code in OpReshapeOrAddPoint Created: 19/12/95 Inputs: OrigShape - The original shape object Anchor - The position of the mouse at the start of the Drag pSpread - the spread that the drag started on SegmentIndex - The Coord number of the control point that we are dragging pdist - parametric distance along the curve we're dragging from (0 to 1) Purpose: This is called when a Drag operation has been started on the edge of a shape. It will try and reshape the edge as it's dragged according to the maths in the function RecalculatePath SeeAlso: OpReformShapeEdge::RecalculatePath ********************************************************************************************/ void OpReformShapeEdge::DoStartDragEdit(NodeRegularShape* OrigShape, BOOL EdgeOne, DocCoord Anchor, Spread *pSpread, INT32 SegmentIndex) { // We had better take a note of the starting point of the drag StartMousePos = Anchor; UTStartMousePos = Anchor; // We'll untransform this later in BuildEditShape StartSpread = pSpread; OriginalShape = OrigShape; Index = SegmentIndex; EdgeDrag = EdgeOne ? DRAG_EDGE1 : DRAG_EDGE2; // We need to make a version of the shape that we can change if (!BuildEditEdge()) { // Inform the person doing the clicking that life is not looking so good InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) ); End(); return; } // Render the bits of the path that are different DocRect EditPathBBox = EditShape->GetBoundingRect(); RenderDragBlobs(EditPathBBox, pSpread, FALSE); // And tell the Dragging system that we need drags to happen StartDrag(DRAGTYPE_AUTOSCROLL, &EditPathBBox, &StartMousePos); } /******************************************************************************************** > void OpReformShapeEdge::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag) Author: Peter_Arnold (Xara Group Ltd) Created: 19/12/94 Inputs: PointerPos - The current position of the mouse in Doc Coords ClickMods - Which key modifiers are being pressed pSpread - the spread that the cursor is over now Purpose: This is called every time the mouse moves, during a drag. SeeAlso: ClickModifiers ********************************************************************************************/ void OpReformShapeEdge::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag) { // If drag has moved onto a different spread, convert the coord to be relative to the // original spread. if (pSpread != StartSpread) PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos); // Now snap it to the current grid DocView::SnapCurrent(StartSpread,&PointerPos); // Rub out the old EORed version of the path RenderDragBlobs( EditShape->GetBoundingRect(), StartSpread, bSolidDrag ); // This is the bit where we go off and re-calculate the edge path, // based on how much the mouse has moved. We need to work in untransfomed coordinates DocCoord Offset = PointerPos; Matrix TransMat; EditShape->GetTransformMatrix(&TransMat); TransMat = TransMat.Inverse(); TransMat.transform(&Offset); Offset = Offset-UTStartMousePos; RecalculateEdge( Offset ); if (!BuildEditShape(ClickMods.Constrain || !OriginalShape->IsStellated(), ClickMods.Adjust)) { InformError(); End(); } // Draw in the new version of the path and update the Last Mouse Position RenderDragBlobs( EditShape->GetBoundingRect(), StartSpread, bSolidDrag ); } /******************************************************************************************** > void OpReformShapeEdge::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success, BOOL bSolidDrag) Author: Peter_Arnold (Xara Group Ltd) Created: 21/12/94 Inputs: PointerPos - The position of the mouse at the end of the drag ClickMods - the key modifiers being pressed pSpread - pointer to the spared the drag ended on. Success - TRUE if the drag was terminated properly, FALSE if it was ended with the escape key being pressed Purpose: This is called when a drag operation finishes. SeeAlso: ClickModifiers ********************************************************************************************/ void OpReformShapeEdge::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success, BOOL bSolidDrag) { RenderDragBlobs( EditEdge->GetBoundingRect(), StartSpread, bSolidDrag ); EndDrag(); BeginSlowJob(); BOOL Failed = FALSE; if ( Success ) { // Start the undo ball rolling Failed = !DoStartSelOp(FALSE, TRUE, FALSE, TRUE); if (!Failed) { Failed = RecalcBoundsAction::DoRecalc(this, &UndoActions, OriginalShape) == AC_FAIL; } // Will the original shape allow the op to happen? ObjChangeFlags cFlags; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,OriginalShape,this); if (!Failed) Failed = !OriginalShape->AllowOp(&ObjChange); if (!Failed) { if ((EdgeDrag == DRAG_EDGE1) || ClickMods.Constrain) { Failed = ChangeShapePathAction::DoReshape(this, &UndoActions, OriginalShape, ChangeShapePathAction::CHANGE_PATH1, &(OriginalShape->EdgePath1)) == AC_FAIL ; } if ((EdgeDrag == DRAG_EDGE2) || ClickMods.Constrain || !OriginalShape->IsStellated()) { if (!Failed) { Failed = ChangeShapePathAction::DoReshape(this, &UndoActions, OriginalShape, ChangeShapePathAction::CHANGE_PATH2, &(OriginalShape->EdgePath2)) == AC_FAIL ; } } } // Now do the change if (!Failed) { if ((EdgeDrag == DRAG_EDGE1) || ClickMods.Constrain ) { OriginalShape->EdgePath1.ClearPath(); OriginalShape->EdgePath1.CopyPathDataFrom(&(EditShape->EdgePath1)); } if ((EdgeDrag == DRAG_EDGE2) || ClickMods.Constrain || !OriginalShape->IsStellated() ) { OriginalShape->EdgePath2.ClearPath(); OriginalShape->EdgePath2.CopyPathDataFrom(&(EditShape->EdgePath2)); } // Recalculate the path's bounding box OriginalShape->InvalidateBoundingRect(); OriginalShape->InvalidateCache(); // tell the world that something in the selection has changed // so that selection bounds are updated GetApplication()->FindSelection()->Update(TRUE); Failed = RecordBoundsAction::DoRecord(this, &UndoActions, OriginalShape) == AC_FAIL ; } if (!Failed) { // Update effected parents ObjChange.Define(OBJCHANGE_FINISHED,cFlags,OriginalShape,this); Failed = !UpdateChangedNodes(&ObjChange); } } else { // Set up the flags that say it all went wrong Failed = TRUE; } delete (EditShape); delete (EditEdge); if (Failed) FailAndExecute(); End(); } /******************************************************************************************** > BOOL OpReformShapeEdge::BuildEditEdge() Author: Peter_Arnold (Xara Group Ltd) Created: 19/12/94 Returns: TRUE if it managed to build the path, FALSE if it failed Purpose: Builds a copy of the shape and the edge path that we can edit, without destroying the original data Errors: If it runs out of memory then it will return FALSE ********************************************************************************************/ BOOL OpReformShapeEdge::BuildEditEdge() { // Copy the shape if ( (EditShape = new (NodeRegularShape)) == NULL) return FALSE; OriginalShape->CopyNodeContents(EditShape); // Copy the edge path if ( (EditEdge = new (Path)) == NULL) return FALSE; // We need to make a copy of the edge path as it appears in the shape. INT32 Count1 = OriginalShape->EdgePath1.GetNumCoords(); INT32 Count2 = OriginalShape->EdgePath2.GetNumCoords(); if (Count1 == 2) Count1 = 4; if (Count2 == 2) Count2 = 4; Path* RenderPath; if (!OriginalShape->BuildShapePath(&RenderPath)) return FALSE; // Run through the path makeing linetos into straight curvetos BOOL FoundLine = TRUE; while (FoundLine) { INT32 NumCoords = RenderPath->GetNumCoords(); // find a lineto INT32 loop = 0; while ((loop < NumCoords) && ((RenderPath->GetVerbArray()[loop] & ~PT_CLOSEFIGURE) != PT_LINETO)) { loop ++; } if (loop < NumCoords) { DocCoord* EdgeCoords = RenderPath->GetCoordArray(); DocCoord CP1 (EdgeCoords[loop-1].x + (EdgeCoords[loop].x-EdgeCoords[loop-1].x)/3, EdgeCoords[loop-1].y + (EdgeCoords[loop].y-EdgeCoords[loop-1].y)/3); DocCoord CP2 (EdgeCoords[loop].x + (EdgeCoords[loop-1].x-EdgeCoords[loop].x)/3, EdgeCoords[loop].y + (EdgeCoords[loop-1].y-EdgeCoords[loop].y)/3); DocCoord End = EdgeCoords[loop]; RenderPath->SetPathPosition(loop); RenderPath->InsertCurveTo(CP1,CP2,End); RenderPath->DeleteSection(loop+3,1); } else FoundLine = FALSE; } // Now calculate paramdist RenderPath->SqrDistanceToPoint(StartMousePos, &Index, ¶mdist); // Now set Index to point to the first element in the edge PathFlags* RenderFlags = RenderPath->GetFlagArray(); while ((!RenderFlags[Index].IsEndPoint) && (Index >= 0) ) { Index--; } INT32 EndCount = EdgeDrag==DRAG_EDGE1 ? Count1 : Count2; // Ask for a path with the required number of free slots in it if (!EditEdge->Initialise(EndCount, 1)) return FALSE; // now copy the data from the appropitae place in RenderPath into EdgeEdge if (!EditEdge->CopySectionFrom(*RenderPath, Index, EndCount)) return FALSE; // Set the first verb to be a MoveTo and clear any closefigure from the last verb PathVerb* EdgeVerbs = EditEdge->GetVerbArray(); DocCoord* EdgeCoords = EditEdge->GetCoordArray(); EdgeVerbs[0] = PT_MOVETO; EdgeVerbs[EditEdge->GetNumCoords()-1] = EdgeVerbs[EditEdge->GetNumCoords()-1] & ~PT_CLOSEFIGURE; // Untransform the path Matrix TransMat; EditShape->GetTransformMatrix(&TransMat); TransMat = TransMat.Inverse(); TransMat.transform((Coord*)EdgeCoords, EditEdge->GetNumCoords()); // We also need an untransformed starting point of the drag TransMat.transform(&UTStartMousePos); // Record the original positions of the two control points of the drag segment // NOTE Assumes that the edit path consists of one segment if (EdgeVerbs[1] == PT_BEZIERTO) { OrigControl1 = EdgeCoords[1]; OrigControl2 = EdgeCoords[2]; } Index = 1; return TRUE; } /******************************************************************************************** > BOOL OpReformShapeEdge::BuildEditShape(BOOL CopyToBoth, BOOL ShouldMirror) Author: Peter_Arnold (Xara Group Ltd) Created: 20/12/94 Inputs: CopyToBoth - TRUE if the status of EdgeDrag is to be ignored and the new edge should be applied to both edges of the shape. Outputs: - Returns: TRUE if the build succeded, FALSE if it failed Purpose: Builds a copy of the shape into EditShape based upon the current edge path Errors: - ********************************************************************************************/ BOOL OpReformShapeEdge::BuildEditShape(BOOL CopyToBoth, BOOL ShouldMirror) { ERROR2IF(EditShape == NULL, FALSE, "Can't build into a non-existant shape!"); BOOL Success = TRUE; if (CopyToBoth) { Path* RevPath = new Path; Path* Path1; Path* Path2; if ((RevPath == NULL) || !ReversePath(RevPath, ShouldMirror)) { if (RevPath != NULL) delete RevPath; RevPath = EditEdge; } if (EdgeDrag == DRAG_EDGE1) { Path1 = EditEdge; Path2 = RevPath; } else { Path1 = RevPath; Path2 = EditEdge; } Success = EditShape->EdgePath1.CopyPathDataFrom(Path1); if (Success) Success = EditShape->EdgePath2.CopyPathDataFrom(Path2); if (RevPath != EditEdge) delete RevPath; } else { if (EdgeDrag == DRAG_EDGE1) { Success = EditShape->EdgePath1.CopyPathDataFrom(EditEdge); if (Success) Success = EditShape->EdgePath2.CopyPathDataFrom(&(OriginalShape->EdgePath2)); } else { Success = EditShape->EdgePath2.CopyPathDataFrom(EditEdge); if (Success) Success = EditShape->EdgePath1.CopyPathDataFrom(&(OriginalShape->EdgePath1)); } } if (!Success) { Error::SetError(_R(IDS_OUT_OF_MEMORY)); } EditShape->InvalidateCache(); return Success; } /******************************************************************************************** > void OpReformShapeEdge::RecalculateEdge( DocCoord Offset ) Author: Peter_Arnold (Xara Group Ltd) (stolen from Jim code) Created: 20/12/94 Inputs: Offset - The Amount the pointer has moved from the *original* position (i.e. not the last position). Purpose: Will reshape the edge being edited using Jims curve distorting method SeeAlso: OpReshapeOrAddPoint::RecalculatePath ********************************************************************************************/ void OpReformShapeEdge::RecalculateEdge( DocCoord Offset ) { const double factor = 0.656875; PathVerb* Verbs = EditEdge->GetVerbArray(); DocCoord* Coords = EditEdge->GetCoordArray(); if (Verbs[Index] == PT_BEZIERTO) // first ctrl pt won't have closefigure set { DocCoord ControlOffset; ControlOffset.x = (INT32)(OrigControl1.x + (Offset.x) * factor / paramdist); ControlOffset.y = (INT32)(OrigControl1.y + (Offset.y) * factor / paramdist); ControlOffset.x -= Coords[Index].x; ControlOffset.y -= Coords[Index].y; Coords[Index].x += ControlOffset.x; Coords[Index].y += ControlOffset.y; ControlOffset.x = (INT32)(OrigControl2.x + (Offset.x) * factor / (1.0-paramdist)); ControlOffset.y = (INT32)(OrigControl2.y + (Offset.y) * factor / (1.0-paramdist)); ControlOffset.x -= Coords[Index+1].x; ControlOffset.y -= Coords[Index+1].y; Coords[Index+1].x += ControlOffset.x; Coords[Index+1].y += ControlOffset.y; } } /******************************************************************************************** > void OpReformShapeEdge::ReversePath(Path* NewPath, BOOL ShouldMirror) Author: Peter_Arnold (Xara Group Ltd) Created: 13/1/95 Inputs: NewPath - Points to an uninitialsed path. ShouldMirror - TRUE if the path should be mirrored Outputs: NewPath contains a mirror image of EditEdge Returns: TRUE for success, FALSE for failure Purpose: Use to reverse an edit path before copying it to the other edge path NOTE: This function assumes that the edge path consists of a MoveTo-BezierTo SeeAlso: - ********************************************************************************************/ BOOL OpReformShapeEdge::ReversePath(Path* NewPath, BOOL ShouldMirror) { const INT32 NumCoords = EditEdge->GetNumCoords(); ERROR3IF(NumCoords != 4, "Edit edge was not a single Bezier section"); if (!( NewPath->Initialise(NumCoords,4) && NewPath->CopyPathDataFrom(EditEdge) )) return FALSE; if (!ShouldMirror) NewPath->Reverse(); /* if (ShouldMirror) { DocCoord* Points = NewPath->GetCoordArray(); // Transform point 1 double angle1 = atan2((double)Points[3].y-Points[0].y, (double)Points[3].x-Points[0].x) * (180/PI); double angle2 = atan2((double)Points[1].y-Points[0].y, (double)Points[1].x-Points[0].x) * (180/PI); Matrix Trans(-Points[0].x, -Points[0].x); Trans *= Matrix(-ANGLE((angle2-angle1))); Trans *= Matrix(Points[0].x, Points[0].x); Trans.transform(&(Points[1])); // Transform point 2 angle1 = atan2((double)Points[0].y-Points[3].y, (double)Points[0].x-Points[3].x) * (180/PI); angle2 = atan2((double)Points[2].y-Points[3].y, (double)Points[2].x-Points[3].x) * (180/PI); Trans = Matrix(-Points[3].x, -Points[3].x); Trans *= Matrix(-ANGLE((angle2-angle1))); Trans *= Matrix(Points[3].x, Points[3].x); Trans.transform(&(Points[2])); }*/ return TRUE; } /******************************************************************************************** > BOOL OpReformShapeEdge::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 16/12/94 Returns: TRUE if all went OK, False otherwise Purpose: Adds the operation to the list of all known operations ********************************************************************************************/ BOOL OpReformShapeEdge::Init() { return (RegisterOpDescriptor( 0, _R(IDS_EDITREGULARSHAPEOP), CC_RUNTIME_CLASS(OpReformShapeEdge), OPTOKEN_RESHAPESHAPEEDGE, OpReformShapeEdge::GetState)); } /******************************************************************************************** > OpState OpReformShapeEdge::GetState(String_256* Description, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 16/12/94 Outputs: Description - GetState fills this string with an approriate description of the current state of the selector tool Returns: The state of the operation, so that menu items (ticks and greying can be done properly Purpose: Find out the state of the operation at the specific time ********************************************************************************************/ OpState OpReformShapeEdge::GetState(String_256* Description, OpDescriptor*) { OpState Blobby; return Blobby; } /******************************************************************************************** > virtual void OpReformShapeEdge::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag ) Author: Peter_Arnold (Xara Group Ltd) Created: 19/12/94 Inputs: Rect - the clipping rectangle pSpread - the spread to draw onto Outputs: - Returns: - Purpose: Renders the drag blobs of this operation. ********************************************************************************************/ void OpReformShapeEdge::RenderDragBlobs( DocRect Rect, Spread* pSpread, BOOL bSolidDrag ) { // If being called from DocView::RenderView, then the spread could be wrong - so // convert the rectangle if necessary. if (pSpread != StartSpread) { Rect.lo = MakeRelativeToSpread(StartSpread, pSpread, Rect.lo); Rect.hi = MakeRelativeToSpread(StartSpread, pSpread, Rect.hi); } ERROR3IF(EditShape == NULL, "Can't render a non-existant shape!"); if (EditShape == NULL) return; // start a rendering loop RenderRegion* pRegion = DocView::RenderOnTop(NULL, pSpread, ClippedEOR); while (pRegion) { // Set the line colour pRegion -> SetFillColour(COLOUR_NONE); pRegion -> SetLineColour(COLOUR_XOREDIT); // Draw the outline EditShape->RenderEorDrag(pRegion); // Get the Next render region pRegion = DocView::GetNextOnTop(NULL); } } /////////////////////////////////////////////////////////////////////////////////////////// // OpShapeToggleBase /******************************************************************************************** > OpShapeToggleBase::OpShapeToggleBase(): SelOperation() Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Purpose: Constructs an OpShapeToggleBase object. ********************************************************************************************/ OpShapeToggleBase::OpShapeToggleBase(): SelOperation() { } /******************************************************************************************** > void OpShapeToggleBase::Do(OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Inputs: An Operation Descriptor (unused) Outputs: - Returns: - Purpose: Runs through all selected Quickshapes, toggling their one of their member states Errors: - SeeAlso: - ********************************************************************************************/ void OpShapeToggleBase::Do(OpDescriptor*) { // Start the operation BOOL Success = DoStartSelOp(FALSE, FALSE, FALSE, FALSE); // Check with our parent nodes ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(),NULL,this); // Run through the selected nodes if (Success) { SelRange* pSelection = GetApplication()->FindSelection(); Node* pNode = pSelection->FindFirst(); while ((pNode != NULL) && Success) { if (IS_A(pNode, NodeRegularShape)) { NodeRegularShape* pShape = (NodeRegularShape*)pNode; // Convert this shape if it's parents allow if (pShape->AllowOp(&ObjChange)) { // Create an action to will cause the shape to recalc its bounds if (Success) Success = (RecalcBoundsAction::DoRecalc(this, &UndoActions, pShape) != AC_FAIL); // Toggle its ellipseness if (Success) Success = DoAction(pShape); // Invalidate the shapes bounding rect as it's changed if (Success) { pShape->InvalidateBoundingRect(); pShape->InvalidateCache(); } // And redraw its final state if (Success) Success = (RecordBoundsAction::DoRecord(this, &UndoActions, pShape) != AC_FAIL); // Tell our parents we have finished if (Success) { ObjChange.Define(OBJCHANGE_FINISHED,ObjChangeFlags(),NULL,this); Success = UpdateChangedNodes(&ObjChange); } } } // Get the next selected thing pNode = pSelection->FindNext(pNode); } } // Clean up after any failure if (!Success) { InformError(); FailAndExecute(); } End(); } BOOL OpShapeToggleBase::DoAction(NodeRegularShape* pShape) { ERROR3("Should not call OpShapeToggleBase::DoAction"); return FALSE; } /////////////////////////////////////////////////////////////////////////////////////////// // OpShapeToggleElipPoly /******************************************************************************************** > OpShapeToggleElipPoly::OpShapeToggleElipPoly(): OpShapeToggleBase() Author: Peter_Arnold (Xara Group Ltd) Created: 16/04/95 Purpose: Constructs an OpShapeToggleElipPoly object. ********************************************************************************************/ OpShapeToggleElipPoly::OpShapeToggleElipPoly(): OpShapeToggleBase() { } /******************************************************************************************** > OpState OpShapeToggleElipPoly::GetState(String_256* UIDescription, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 16/04/95 Inputs: UIDescription - the name of the opertaion to display OpDescriptor - To help update the state Outputs: UIDescription - The name of the operation Returns: The state of the operation Purpose: Find the state of the OpShapeToggleElipPoly operation. Errors: - SeeAlso: - ********************************************************************************************/ OpState OpShapeToggleElipPoly::GetState(String_256* UIDescription, OpDescriptor*) { OpState OpSt; OpSt.Greyed = TRUE; BOOL FoundEllipse = FALSE; BOOL FoundPolygon = FALSE; ObjChangeFlags cFlags; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL); SelRange* pSelection = GetApplication()->FindSelection(); Node* pNode = pSelection->FindFirst(); while (pNode != NULL) { if (IS_A(pNode, NodeRegularShape)) { NodeRegularShape* pShape = (NodeRegularShape*)pNode; // Convert this shape if it's parents allow if (pShape->AllowOp(&ObjChange)) { if (pShape->IsCircular()) FoundEllipse = TRUE; else FoundPolygon = TRUE; } } // Get the next selected thing pNode = pSelection->FindNext(pNode); } if (FoundEllipse || FoundPolygon) OpSt.Greyed = FALSE; if (FoundEllipse && !FoundPolygon) UIDescription->Load(_R(IDS_OPSHAPETOGGLE_ELIP_PO)); if (!FoundEllipse && FoundPolygon) UIDescription->Load(_R(IDS_OPSHAPETOGGLE_ELIP_EP)); return(OpSt); } /******************************************************************************************** > BOOL OpShapeToggleElipPoly::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: - Outputs: - Returns: TRUE/FALSE for successful init Purpose: Initialise the toggle ellipseness operation by registering it Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleElipPoly::Init() { return(RegisterOpDescriptor(0, _R(IDS_OPSHAPETOGGLE_ELIP), CC_RUNTIME_CLASS(OpShapeToggleElipPoly), OPTOKEN_TOGGLEELIPPOLY, OpShapeToggleElipPoly::GetState)); } /******************************************************************************************** > BOOL OpShapeToggleElipPoly::DoAction(NodeRegularShape* pShape) Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: pShape - points to the shape to toggle Outputs: - Returns: TRUE/FALSE for successful toggle Purpose: Toggles the ellipse/polygon state of a shape Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleElipPoly::DoAction(NodeRegularShape* pShape) { BOOL Success = TRUE; if (Success) Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape, ChangeShapeDataAction::CHANGE_CIRCULAR, pShape->IsCircular() ? 1.0 : 0.0) != AC_FAIL); if (Success) pShape->SetCircular(!pShape->IsCircular()); return Success; } /////////////////////////////////////////////////////////////////////////////////////////// // OpShapeToggleCurvature /******************************************************************************************** > OpShapeToggleStellation::OpShapeToggleStellation(): OpShapeToggleBase() Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Purpose: Constructs an OpShapeToggleStellation object. ********************************************************************************************/ OpShapeToggleStellation::OpShapeToggleStellation(): OpShapeToggleBase() { } /******************************************************************************************** > OpState OpShapeToggleStellation::GetState(String_256* UIDescription, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Inputs: UIDescription - the name of the opertaion to display OpDescriptor - To help update the state Outputs: UIDescription - The name of the operation Returns: The state of the operation Purpose: Find the state of the OpShapeToggleStellation operation. Errors: - SeeAlso: - ********************************************************************************************/ OpState OpShapeToggleStellation::GetState(String_256* UIDescription, OpDescriptor*) { OpState OpSt; OpSt.Greyed = TRUE; BOOL FoundStellated = FALSE; BOOL FoundNotStellated = FALSE; BOOL AllEllipses = TRUE; ObjChangeFlags cFlags; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL); SelRange* pSelection = GetApplication()->FindSelection(); Node* pNode = pSelection->FindFirst(); while (pNode != NULL) { if (IS_A(pNode, NodeRegularShape)) { NodeRegularShape* pShape = (NodeRegularShape*)pNode; AllEllipses = AllEllipses && pShape->IsCircular(); // Convert this shape if it's parents allow if (pShape->AllowOp(&ObjChange)) { if (pShape->IsStellated()) FoundStellated = TRUE; else FoundNotStellated = TRUE; } } // Get the next selected thing pNode = pSelection->FindNext(pNode); } if (AllEllipses) UIDescription->Load(_R(IDS_REGSHAPE_ALLELLIPSES)); else { if (FoundStellated || FoundNotStellated) OpSt.Greyed = FALSE; if (FoundStellated && !FoundNotStellated) UIDescription->Load(_R(IDS_OPSHAPETOGGLE_STELL_REM)); if (!FoundStellated && FoundNotStellated) UIDescription->Load(_R(IDS_OPSHAPETOGGLE_STELL_ADD)); } return(OpSt); } /******************************************************************************************** > BOOL OpShapeToggleStellation::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: - Outputs: - Returns: TRUE/FALSE for successful init Purpose: Initialise the toggle stellation operation by registering it Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleStellation::Init() { return(RegisterOpDescriptor(0, _R(IDS_OPSHAPETOGGLE_STELL), CC_RUNTIME_CLASS(OpShapeToggleStellation), OPTOKEN_TOGGLESTELLATION, OpShapeToggleStellation::GetState)); } /******************************************************************************************** > BOOL OpShapeToggleStellation::DoAction(NodeRegularShape* pShape) Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: pShape - points to the shape to toggle Outputs: - Returns: TRUE/FALSE for successful toggle Purpose: Toggles the stellation state of a shape Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleStellation::DoAction(NodeRegularShape* pShape) { BOOL Success = TRUE; if (Success) Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape, ChangeShapeDataAction::CHANGE_STELLATED, pShape->IsStellated() ? 1.0 : 0.0) != AC_FAIL); if (Success) pShape->SetStellated(!pShape->IsStellated()); return Success; } /////////////////////////////////////////////////////////////////////////////////////////// // OpShapeToggleCurvature /******************************************************************************************** > OpShapeToggleCurvature::OpShapeToggleCurvature(): OpShapeToggleBase() Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Purpose: Constructs an OpShapeToggleCurvature object. ********************************************************************************************/ OpShapeToggleCurvature::OpShapeToggleCurvature(): OpShapeToggleBase() { } /******************************************************************************************** > OpState OpShapeToggleCurvature::GetState(String_256* UIDescription, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Inputs: UIDescription - the name of the opertaion to display OpDescriptor - To help update the state Outputs: UIDescription - The name of the operation Returns: The state of the operation Purpose: Find the state of the OpShapeToggleCurvature operation. Errors: - SeeAlso: - ********************************************************************************************/ OpState OpShapeToggleCurvature::GetState(String_256* UIDescription, OpDescriptor*) { OpState OpSt; OpSt.Greyed = TRUE; BOOL FoundCurved = FALSE; BOOL FoundNotCurved = FALSE; BOOL AllEllipses = TRUE; ObjChangeFlags cFlags; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL); SelRange* pSelection = GetApplication()->FindSelection(); Node* pNode = pSelection->FindFirst(); while (pNode != NULL) { if (IS_A(pNode, NodeRegularShape)) { NodeRegularShape* pShape = (NodeRegularShape*)pNode; AllEllipses = AllEllipses && pShape->IsCircular(); // Convert this shape if it's parents allow if (pShape->AllowOp(&ObjChange)) { if (pShape->IsPrimaryCurvature() && pShape->IsStellationCurvature()) FoundCurved = TRUE; else FoundNotCurved = TRUE; } } // Get the next selected thing pNode = pSelection->FindNext(pNode); } if (AllEllipses) UIDescription->Load(_R(IDS_REGSHAPE_ALLELLIPSES)); else { if (FoundCurved || FoundNotCurved) OpSt.Greyed = FALSE; if (FoundCurved && !FoundNotCurved) UIDescription->Load(_R(IDS_OPSHAPETOGGLE_CURVE_REM)); if (!FoundCurved && FoundNotCurved) UIDescription->Load(_R(IDS_OPSHAPETOGGLE_CURVE_ADD)); } return(OpSt); } /******************************************************************************************** > BOOL OpShapeToggleCurvature::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: - Outputs: - Returns: TRUE/FALSE for successful init Purpose: Initialise the toggle curvature operation by registering it Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleCurvature::Init() { return(RegisterOpDescriptor(0, _R(IDS_OPSHAPETOGGLE_CURVE), CC_RUNTIME_CLASS(OpShapeToggleCurvature), OPTOKEN_TOGGLECURVATURE, OpShapeToggleCurvature::GetState)); } /******************************************************************************************** > BOOL OpShapeToggleCurvature::DoAction(NodeRegularShape* pShape) Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: pShape - points to the shape to toggle Outputs: - Returns: TRUE/FALSE for successful toggle Purpose: Toggles the curvature state of a shape Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleCurvature::DoAction(NodeRegularShape* pShape) { BOOL Success = TRUE; // Do the primary curvature if (Success) Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape, ChangeShapeDataAction::CHANGE_PRIMARYCURVATURE, pShape->IsPrimaryCurvature() ? 1.0 : 0.0) != AC_FAIL); if (Success) pShape->SetPrimaryCurvature(!pShape->IsPrimaryCurvature()); // Do the stellation curvature if (Success) Success = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape, ChangeShapeDataAction::CHANGE_STELLATIONCURVATURE, pShape->IsStellationCurvature() ? 1.0 : 0.0) != AC_FAIL); if (Success) pShape->SetStellationCurvature(!pShape->IsStellationCurvature()); return Success; } /////////////////////////////////////////////////////////////////////////////////////////// // OpShapeToggleSetNumSides /******************************************************************************************** > OpShapeToggleSetNumSides::OpShapeToggleSetNumSides(): SelOperation() Author: Peter_Arnold (Xara Group Ltd) Created: 22/04/95 Purpose: Constructs an OpShapeToggleSetNumSides object. ********************************************************************************************/ OpShapeToggleSetNumSides::OpShapeToggleSetNumSides(): SelOperation() { } /******************************************************************************************** > OpState OpShapeToggleSetNumSides::GetState(String_256* UIDescription, OpDescriptor*) Author: Peter_Arnold (Xara Group Ltd) Created: 17/04/95 Inputs: UIDescription - the name of the opertaion to display OpDescriptor - To help update the state Outputs: UIDescription - The name of the operation Returns: The state of the operation Purpose: Find the state of the OpShapeToggleCurvature operation. Errors: - SeeAlso: - ********************************************************************************************/ OpState OpShapeToggleSetNumSides::GetState(String_256* UIDescription, OpDescriptor*) { OpState OpSt; BOOL FoundEditable = FALSE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(),NULL,NULL); SelRange* pSelection = GetApplication()->FindSelection(); Node* pNode = pSelection->FindFirst(); while (pNode != NULL) { if (IS_A(pNode, NodeRegularShape)) { NodeRegularShape* pShape = (NodeRegularShape*)pNode; // Convert this shape if it's parents allow if (pNode->AllowOp(&ObjChange) && !pShape->IsCircular()) FoundEditable = TRUE; } // Get the next selected thing pNode = pSelection->FindNext(pNode); } if (!FoundEditable) OpSt.Greyed = TRUE; return OpSt; } /******************************************************************************************** > BOOL OpShapeToggleSetNumSides::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: - Outputs: - Returns: TRUE/FALSE for successful init Purpose: Initialise the set number of sides operation by registering it Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleSetNumSides::Init() { BOOL ok = TRUE; ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides), OPTOKEN_QUICKSHAPE_NUMBERSIDES, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES3), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides3), OPTOKEN_QUICKSHAPE_NUMBERSIDES3, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES4), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides4), OPTOKEN_QUICKSHAPE_NUMBERSIDES4, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES5), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides5), OPTOKEN_QUICKSHAPE_NUMBERSIDES5, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES6), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides6), OPTOKEN_QUICKSHAPE_NUMBERSIDES6, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES7), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides7), OPTOKEN_QUICKSHAPE_NUMBERSIDES7, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES8), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides8), OPTOKEN_QUICKSHAPE_NUMBERSIDES8, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES9), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides9), OPTOKEN_QUICKSHAPE_NUMBERSIDES9, OpShapeToggleSetNumSides::GetState); ok = ok && RegisterOpDescriptor(0, _R(IDS_QUICKSHAPES_NUMBERSIDES10), CC_RUNTIME_CLASS(OpShapeToggleSetNumSides10), OPTOKEN_QUICKSHAPE_NUMBERSIDES10, OpShapeToggleSetNumSides::GetState); return ok; } /******************************************************************************************** > BOOL OpShapeToggleSetNumSides::DoAction(INT32 NumSides) Author: Peter_Arnold (Xara Group Ltd) Created: 16/4/95 Inputs: NumSides - the number of sides to set the shape to Outputs: - Returns: TRUE/FALSE for successful toggle Purpose: Sets the number of sides of a shape Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeToggleSetNumSides::DoAction(INT32 NumSides) { BOOL ok = DoStartSelOp(FALSE,FALSE); ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(), NULL, this); SelRange* Selection = GetApplication()->FindSelection(); Node* pNode = Selection->FindFirst(); while (ok && (pNode != NULL)) { if (IS_A(pNode,NodeRegularShape)) { NodeRegularShape* pShape = (NodeRegularShape*)pNode; if (pShape->AllowOp(&ObjChange) && !pShape->IsCircular()) { // Record the bounds if (ok) ok = (RecalcBoundsAction::DoRecalc(this, &UndoActions, pShape) != AC_FAIL); // Change the shape ok = (ChangeShapeDataAction::DoToggle(this, &UndoActions, pShape, ChangeShapeDataAction::CHANGE_NUMSIDES, pShape->GetNumSides()) != AC_FAIL); if (ok) pShape->SetNumSides(NumSides); // Invalidate the shapes bounding rect as it's changed if (ok) { pShape->InvalidateBoundingRect(); pShape->InvalidateCache(); } // And redraw its final state if (ok) ok = (RecordBoundsAction::DoRecord(this, &UndoActions, pShape) != AC_FAIL); // Say we have finished if (ok) { ObjChange.Define(OBJCHANGE_FINISHED,ObjChangeFlags(),NULL,this); ok = UpdateChangedNodes(&ObjChange); } } } pNode = Selection->FindNext(pNode); } if (!ok) { InformError(); FailAndExecute(); } End(); return ok; } void OpShapeToggleSetNumSides3::Do(OpDescriptor* pOpDesc) { if (!DoAction(3)) InformError(); } void OpShapeToggleSetNumSides4::Do(OpDescriptor* pOpDesc) { if (!DoAction(4)) InformError(); } void OpShapeToggleSetNumSides5::Do(OpDescriptor* pOpDesc) { if (!DoAction(5)) InformError(); } void OpShapeToggleSetNumSides6::Do(OpDescriptor* pOpDesc) { if (!DoAction(6)) InformError(); } void OpShapeToggleSetNumSides7::Do(OpDescriptor* pOpDesc) { if (!DoAction(7)) InformError(); } void OpShapeToggleSetNumSides8::Do(OpDescriptor* pOpDesc) { if (!DoAction(8)) InformError(); } void OpShapeToggleSetNumSides9::Do(OpDescriptor* pOpDesc) { if (!DoAction(9)) InformError(); } void OpShapeToggleSetNumSides10::Do(OpDescriptor* pOpDesc) { if (!DoAction(10)) InformError(); } ///////////////////////////////////////////////////////////////////////////////////////////// // OpShapeMakeRegular /******************************************************************************************** > static OpState OpShapeMakeRegular::GetState(String_256* UIDesc, OpDescriptor* OpDesc) Author: Peter_Arnold (Xara Group Ltd) Created: 23/4/95 Inputs: UIDesc - OpDesc - Outputs: - Returns: The tick/grey state of this op Purpose: Returns the state of this operation Errors: - SeeAlso: - ********************************************************************************************/ OpState OpShapeMakeRegular::GetState(String_256* UIDesc, OpDescriptor* OpDesc) { OpState Blobby; return Blobby; } /******************************************************************************************** > static BOOL OpShapeMakeRegular::Init() Author: Peter_Arnold (Xara Group Ltd) Created: 23/4/95 Inputs: - Outputs: - Returns: TRUE/FALSE for successful initialise Purpose: Registers this op Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpShapeMakeRegular::Init() { return RegisterOpDescriptor(0, _R(IDS_QUICKSHAPE_MAKEREGULAR), CC_RUNTIME_CLASS(OpShapeMakeRegular), OPTOKEN_QUICKSHAPE_MAKEREGULAR, OpShapeMakeRegular::GetState); } /******************************************************************************************** > void OpShapeMakeRegular::DoMakeRegular(NodeRegularShape* pShape, DocCoord RegularPoint) Author: Peter_Arnold (Xara Group Ltd) Created: 23/4/95 Inputs: pShape - points to the shape to make regular RegularPoint - defines the new size of the shape, from the centre to this point Outputs: - Returns: - Purpose: Makes a QuickShape regular, ie 100% aspect ratio, no skew, shear etc... Errors: Assorted pointer checks SeeAlso: - ********************************************************************************************/ void OpShapeMakeRegular::DoMakeRegular(NodeRegularShape* pShape, DocCoord RegularPoint) { if (!DoActions(pShape, RegularPoint)) { InformError(); FailAndExecute(); } End(); } /******************************************************************************************** > BOOL OpShapeMakeRegular::DoActions(NodeRegularShape* pShape, DocCoord RegularPoint) Author: Peter_Arnold (Xara Group Ltd) Created: 23/4/95 Inputs: pShape - points to the shape to make regular RegularPoint - defines the new size of the shape, from the centre to this point Outputs: - Returns: - Purpose: Makes a QuickShape regular, ie 100% aspect ratio, no skew, shear etc... Errors: Assorted pointer checks SeeAlso: - ********************************************************************************************/ BOOL OpShapeMakeRegular::DoActions(NodeRegularShape* pShape, DocCoord RegularPoint) { ERROR2IF(pShape == NULL, FALSE, "Shape pointer was NULL"); ERROR3IF(!pShape->IsCircular(), "Make regular not implemented for non-circular QuickShapes"); BOOL ok = DoStartSelOp(FALSE, FALSE); // Get required variables DocCoord ShapeCentre = pShape->GetCentrePoint(); DocCoord UTShapeCentre = pShape->GetUTCentrePoint(); const double RequiredLength = ShapeCentre.Distance(RegularPoint); const double MajorLength = pShape->GetUTMajorAxes().Distance(UTShapeCentre); const double MinorLength = pShape->GetUTMinorAxes().Distance(UTShapeCentre); const double RequiredAngle = atan2((double)(RegularPoint.y-ShapeCentre.y),(double)(RegularPoint.x-ShapeCentre.x)); const double MajorAxisAngle = atan2((double)(pShape->GetUTMajorAxes().y-UTShapeCentre.y), (double)(pShape->GetUTMajorAxes().x-UTShapeCentre.x)); Matrix ShapeMat; pShape->GetTransformMatrix(&ShapeMat); Trans2DMatrix* Translation = new Trans2DMatrix(ShapeMat); Translation->Invert(); // Translate shape to origin if (ok) *Translation *= Trans2DMatrix(-UTShapeCentre.x, -UTShapeCentre.y); // Rotate so major axis points along +ve x-axis (not y!) if (ok) *Translation *= Trans2DMatrix(Matrix(-ANGLE(MajorAxisAngle*(180/PI)))); // Scale it so it is regular if (ok) *Translation *= Trans2DMatrix(Matrix(FIXED16(RequiredLength/MajorLength), FIXED16(RequiredLength/MinorLength))); // Rotate so major axis is under click point if (ok) *Translation *= Trans2DMatrix(Matrix(ANGLE(RequiredAngle*(180/PI)))); // Translate shape to old centre point if (ok) *Translation *= Trans2DMatrix(ShapeCentre.x, ShapeCentre.y); ObjChangeParam ObjChange(OBJCHANGE_STARTING,ObjChangeFlags(), NULL, this); if (pShape->AllowOp(&ObjChange)) { // Record the bounds if (ok) ok = (RecalcBoundsAction::DoRecalc(this, &UndoActions, pShape) != AC_FAIL); // Now actually transform the shape in the document if (ok) ok = DoTransformNode(pShape, Translation); // And redraw its final state if (ok) ok = (RecordBoundsAction::DoRecord(this, &UndoActions, pShape) != AC_FAIL); GetApplication()->FindSelection()->Update(); // Say we have finished if (ok) { ObjChange.Define(OBJCHANGE_FINISHED,ObjChangeFlags(),NULL,this); ok = UpdateChangedNodes(&ObjChange); } } return ok; }