Extender Class Reference

Preliminary container class for Extend() function. This performs automatic extension and stretching of nodes. More...

#include <extender.h>

List of all members.

Static Public Member Functions

static DocRect CheckValidExtend (NodeRenderableInk *pNode, BYTE fExtendFlags, const DocRect &drTriggerRelation, const DocRect &drTargetRelation, const DocRect &drThisTrigger, const DocRect &drLastTrigger, const DocRect &drLastTarget, BOOL *pOK=NULL, const BOOL ExtendAroundTarget=FALSE)
 This function tests whether or not a call to the extend function will extend an object in a valid way. For example, a path cannot be shrunken so that its control points cross its centre, as this operation is irreversible - if the path was extended back again, these points would extend the wrong direction. Similarly, an invalid extend occurs when a trigger set is shrunk so that its centre happens to move over the centre of a target node which is not moving in this particular extension; undoing this operation would end up dragging the target node back with the trigger.
static void Extend (NodeRenderableInk *pNode, BYTE fExtendFlags, const DocRect &drTriggerRelation, const DocRect &drTargetRelation, const DocRect &drNewTrigger, const DocRect &drLastTrigger, const DocRect &drLastTarget, const DocRect *pdrDifference=NULL, const BOOL ExtendAroundTarget=FALSE, UndoableOperation *pOp=NULL)
 Perform an extend operation on the given Node and its children, using the given rectangles and flags to determine how the Node should transform.
static BOOL ConvertQuickShapesInSelRangeToPaths (UndoableOperation *pUndoOp, Range *pRange)
 Iterates over the given SelRange looking for any NodeRegularShapes, ie QuickShapes. If we find any then they get zapped, and all that is left is a smoking pair of shoes! ... erm, sorry - a NodePath.
static BOOL CheckValidExtend (NodeRenderableInk *pNode, DocRect *pStartRect, DocRect *pEndRect, BYTE fStretchType, DocRect *pOldStartRect=NULL)
 ** DEPRECATED - DO NOT CALL **
static void Extend (NodeRenderableInk *pNode, DocRect *pStartRect, DocRect *pEndRect, BYTE fStretchType, DocRect *pOldStartRect=NULL)
 ** DEPRECATED - DO NOT CALL **
static DocRect ValidateControlPoints (INT32 numPoints, const DocCoord *doccArray, const ExtendParams &ExtParams)
 Validate the given points, using the provided extend parameters. If extending the points is invalid, then return a rectangle containing the largest distances which the points can be shrunk in by.
static void CalculateExtendParams (ExtendParams *pEPS, BYTE fExtendFlags, const DocRect &drTriggerRelation, const DocRect &drTargetRelation, const DocRect &drNewTrigger, const DocRect &drLastTrigger, const DocRect &drLastTarget, const DocRect *pdrDifference=NULL, const BOOL ExtendAroundTarget=FALSE)
 Given the rectangles and flags which define an extension, this function derives the bare information which a Node requires to perform the extension on itself.

Private Member Functions

 CC_DECLARE_MEMDUMP (Extender)

Static Private Member Functions

static INT32 CheckInvalidShrinkingPoints (INT32 nCentre, INT32 nDelta, INT32 nOffset, INT32 nNumPoints, const DocCoord *doccArray, BOOL bExamineX)
 Check whether any of the points in the given array will be shrunk past the given limiting value. If all the points check out, we return an all-clear value, which is negative. Otherwise, the maximum safe shrinkage is given.
static INT32 CheckInvalidExpandingPoints (INT32 nCentre, INT32 nDelta, INT32 nOffset, INT32 nNumPoints, const DocCoord *doccArray, BOOL bExamineX)
 Two checks:.
static INT32 CheckLimits (const INT32 limit, const INT32 delta, const INT32 value)
 Checks that the given value may be reduced by the amount delta without going below the given limit value. If it cannot, we return the most which the given value can be safely reduced by.


Detailed Description

Preliminary container class for Extend() function. This performs automatic extension and stretching of nodes.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
10/09/1999
Stretching is defined as application of a matrix transformation to control points, causing the points to scale and translate. The distance a point is moved depends on its own distance from some centre - think 'x -> ax+b'.

Extension is defined as translation of control points, depending on their initial position. All points are moved by the same fixed amount, although the direction* follows the same rules as for stretching - 'x -> x +/- delta'. See also:

Definition at line 203 of file extender.h.


Member Function Documentation

void Extender::CalculateExtendParams ExtendParams pEPS,
BYTE  fExtendFlags,
const DocRect drTriggerRelation,
const DocRect drTargetRelation,
const DocRect drNewTrigger,
const DocRect drLastTrigger,
const DocRect drLastTarget,
const DocRect pdrDifference = NULL,
const BOOL  ExtendAroundTarget = FALSE
[static]
 

Given the rectangles and flags which define an extension, this function derives the bare information which a Node requires to perform the extension on itself.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
06/12/1999
Parameters:
pEPS pointer to an ExtendParams object to fill. [INPUTS] fExtendFlags bitwise flags, describing any combination of x- or y- stretching or extending, except that you may not extend and stretch along an axis at the same time. Values:
X_EXTEND the node should be extended along the x-axis Y_EXTEND the node should be extended along the y-axis X_STRETCH the node should be stretched along the x-axis Y_STRETCH the node should be stretched along the y-axis

drTriggerRelation relationship rectangle - the bounding box of the trigger sets when the relationship is defined. drTargetRelation relationship rectangle - the bounding box of the target sets when the relationship is defined. drNewTrigger bounding box of sets of triggers which started the call to this function. drLastTrigger the bounding box of the trigger set(s) before it changed. drLastTarget the bounding box of the target set before it is extended. pdrDifference a rectangle containing four difference parameters which may be used to alter the extend. ok to omit or be NULL.

Parameters:
*pEPS will be set up to contain extend information derived from the inputs. [OUTPUTS]
Returns:
Errors: ERROR3 in debug builds if pEPS is NULL. See also: Extender::Extend().

Definition at line 987 of file extender.cpp.

00992 {
00993     // validate input.
00994     ERROR3IF(pEPS == NULL, "Extender::CalculateExtendParams- pEPS is NULL!");
00995 
00996     // use the relationship rects and the new trigger rect to calculate the new target rect.
00997     DocRect drNewTarget(drNewTrigger);
00998 
00999     // x-extend - the new target rect must be x-offset from new-trigger on each
01000     // side, by the same amount as target-relation from trigger-relation.
01001     if (fExtendFlags & X_EXTEND)
01002     {
01003         // if the trigger and target-relation rects do not overlap in the x-direction,
01004         // then the new target rect must be x-offset from new-trigger on its near-side,
01005         // by the same amount as target-relation from trigger-relation.
01006         if (drTargetRelation.lo.x > drTriggerRelation.hi.x)
01007         {
01008             drNewTarget.lo.x  = drNewTrigger.hi.x + (drTargetRelation.lo.x - drTriggerRelation.hi.x);
01009             drNewTarget.hi.x  = drNewTrigger.hi.x + (drTargetRelation.hi.x - drTriggerRelation.hi.x);
01010         }
01011         else if (drTargetRelation.hi.x < drTriggerRelation.lo.x)
01012         {
01013             drNewTarget.lo.x = drNewTrigger.lo.x - (drTriggerRelation.lo.x - drTargetRelation.lo.x);
01014             drNewTarget.hi.x = drNewTrigger.lo.x - (drTriggerRelation.lo.x - drTargetRelation.hi.x);
01015         }
01016 
01017         // normal extend behaviour - distances between corresponding sides are kept constant.
01018         else
01019         {
01020             drNewTarget.lo.x += drTargetRelation.lo.x - drTriggerRelation.lo.x;
01021             drNewTarget.hi.x += drTargetRelation.hi.x - drTriggerRelation.hi.x;
01022 
01023             // if the target's new sides are coincident or invalid, then
01024             // they need to be moved back into valid positions.
01025             if (drNewTarget.lo.x >= drNewTarget.hi.x)
01026             {
01027                 drNewTarget.hi.x = drNewTarget.lo.x + 2;
01028             }
01029         }
01030     }
01031     else if (fExtendFlags & X_STRETCH)
01032     {
01033         double a = drNewTrigger.Width() / (double)drTriggerRelation.Width();
01034         INT32 b = drNewTrigger.hi.x - (INT32)(a * drTriggerRelation.hi.x);
01035 
01036         drNewTarget.lo.x = b + (INT32)(a * drTargetRelation.lo.x);
01037         drNewTarget.hi.x = b + (INT32)(a * drTargetRelation.hi.x);
01038     }
01039 
01040     // y-extend - the new target rect must be y-offset from new-trigger on top and
01041     // bottom, by the same amount as target-relation from trigger-relation.
01042     if (fExtendFlags & Y_EXTEND)
01043     {
01044         // if the trigger and target-relation rects do not overlap in the y-direction,
01045         // then the new target rect must be y-offset from new-trigger on its near-side,
01046         // by the same amount as target-relation from trigger-relation.
01047         if (drTargetRelation.lo.y > drTriggerRelation.hi.y)
01048         {
01049             drNewTarget.lo.y  = drNewTrigger.hi.y + (drTargetRelation.lo.y - drTriggerRelation.hi.y);
01050             drNewTarget.hi.y  = drNewTrigger.hi.y + (drTargetRelation.hi.y - drTriggerRelation.hi.y);
01051         }
01052         else if (drTargetRelation.hi.y < drTriggerRelation.lo.y)
01053         {
01054             drNewTarget.lo.y = drNewTrigger.lo.y - (drTriggerRelation.lo.y - drTargetRelation.lo.y);
01055             drNewTarget.hi.y = drNewTrigger.lo.y - (drTriggerRelation.lo.y - drTargetRelation.hi.y);
01056         }
01057 
01058         // normal extend behaviour - distances between corresponding sides are kept constant.
01059         else
01060         {
01061             drNewTarget.lo.y += drTargetRelation.lo.y - drTriggerRelation.lo.y;
01062             drNewTarget.hi.y += drTargetRelation.hi.y - drTriggerRelation.hi.y;
01063 
01064             // if the target's new top and bottom are coincident or invalid, then
01065             // they need to be moved back into valid positions.
01066             if (drNewTarget.lo.y >= drNewTarget.hi.y)
01067             {
01068                 drNewTarget.hi.y = drNewTarget.lo.y + 2;
01069             }
01070         }
01071     }
01072     else if (fExtendFlags & Y_STRETCH)
01073     {
01074         double a = drNewTrigger.Height() / (double)drTriggerRelation.Height();
01075         INT32 b = drNewTrigger.hi.y - (INT32)(a * drTriggerRelation.hi.y);
01076 
01077         drNewTarget.lo.y = b + (INT32)(a * drTargetRelation.lo.y);
01078         drNewTarget.hi.y = b + (INT32)(a * drTargetRelation.hi.y);
01079     }
01080 
01081     // use the old and new target rectangles to calculate our extension working values.
01082 
01083     // extend flags.
01084     pEPS->fExtendFlags = fExtendFlags;
01085 
01086     // the start- and end- centres of extension + the offset between them.
01087     // Karim 14/03/2000 - changed to use the trigger set bounds if their old centre
01088     // lies within the bounds of the old target set.
01089     if (!ExtendAroundTarget && drLastTarget.ContainsCoord(drLastTrigger.Centre()))
01090     {
01091         pEPS->doccStartCentre = drLastTrigger.Centre();
01092         pEPS->doccEndCentre = drNewTrigger.Centre();
01093         pEPS->doccOffset = pEPS->doccEndCentre - pEPS->doccStartCentre;
01094     }
01095     else
01096     {
01097         pEPS->doccStartCentre = drLastTarget.Centre();
01098         pEPS->doccEndCentre = drNewTarget.Centre();
01099         pEPS->doccOffset = pEPS->doccEndCentre - pEPS->doccStartCentre;
01100     }
01101 
01102     // magnitudes of extension. note that under extension, objects are first offset
01103     // from start- to end- centre, then extended. this makes extension mirror-symmetric
01104     // so that xinc == xdec (+/- 1).
01105     pEPS->xinc = drNewTarget.hi.x - (drLastTarget.hi.x + pEPS->doccOffset.x);
01106     pEPS->xdec = (drLastTarget.lo.x + pEPS->doccOffset.x) - drNewTarget.lo.x;
01107     pEPS->yinc = drNewTarget.hi.y - (drLastTarget.hi.y + pEPS->doccOffset.y);
01108     pEPS->ydec = (drLastTarget.lo.y + pEPS->doccOffset.y) - drNewTarget.lo.y;
01109 
01110     // scale factors, if automatic _stretching_ is required.
01111     pEPS->xscale = drNewTarget.Width() / (double)drLastTarget.Width();
01112     pEPS->yscale = drNewTarget.Height() / (double)drLastTarget.Height();
01113 
01114     // if our difference rect is non-NULL, then we may need to substitute
01115     // xinc, xdec, yinc, ydec with negative copies of some of its values.
01116     if (pdrDifference != NULL)
01117     {
01118         if (pdrDifference->lo.x != INT32_MAX) pEPS->xdec = -pdrDifference->lo.x;
01119         if (pdrDifference->lo.y != INT32_MAX) pEPS->ydec = -pdrDifference->lo.y;
01120         if (pdrDifference->hi.x != INT32_MAX) pEPS->xinc = -pdrDifference->hi.x;
01121         if (pdrDifference->hi.y != INT32_MAX) pEPS->yinc = -pdrDifference->hi.y;
01122     }
01123 
01124     // TODO: should probably not use these, but the equivalent of b (used above) instead.
01125     pEPS->doccScaleStart = drLastTarget.lo;
01126     pEPS->doccScaleEnd = drNewTarget.lo;
01127 
01128     // set the various dead-zones; these depend on the signs of the expansions in each dirn.
01129     pEPS->xincExtendBuffer = (pEPS->xinc >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER;
01130     pEPS->xdecExtendBuffer = (pEPS->xdec >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER;
01131     pEPS->yincExtendBuffer = (pEPS->yinc >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER;
01132     pEPS->ydecExtendBuffer = (pEPS->ydec >= 0) ? EXT_EXPAND_BUFFER : EXT_SHRINK_BUFFER;
01133 }

Extender::CC_DECLARE_MEMDUMP Extender   )  [private]
 

INT32 Extender::CheckInvalidExpandingPoints INT32  nCentre,
INT32  nDelta,
INT32  nOffset,
INT32  nNumPoints,
const DocCoord doccArray,
BOOL  bExamineX
[static, private]
 

Two checks:.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/01/2000
Parameters:
nCentre the centre x- or y- value from which point are expanding. [INPUTS] nDelta (currently unused) the distance by which the points are moving. nOffset an offset which should be applied to the points before testing. nNumPoints the number of points to test. doccArray the array of points to test. bExamineX whether to examine x-coords (TRUE) or y-coords (FALSE).
[OUTPUTS] 
Returns:
Negative value if all points can expand safely away from nCentre, or Zero if any of the points cannot be safely moved away.
No points may lie inside the dead-zone.

Due to rounding errors, the centre-point of an object depends on whether its width is even or odd. There is a potential for error here, if a point lying one millipoint outside the dead zone is moved out from the centre, and the operation then reversed. If the centre-point changes position due to a new width then the point may be moving back *inside* the dead zone, which will be disallowed -> irreversible operation.

This method checks for these points. (boy, what a mouthful!)

Assumptions: That the extension being checked is symmetric - ie all points are moving away from the centre point, regardless of their position relative to it.

Returns:
Errors: See also:

Definition at line 1174 of file extender.cpp.

01177 {
01178     // return with an all-clear if delta is zero.
01179     if (nDelta == 0)
01180         return -1;
01181 
01182     // each point should be offset by nOffset, so instead we 'un-offset' the centre.
01183 //  INT32 myCentre = nCentre - nOffset;
01184 
01185 /*
01186  *  Karim 11/05/2000
01187  *  Commented out, as this test is being moved into the actual extension code - rather than
01188  *  have invariant points limit the extension, they will have no effect, and will instead
01189  *  just not themselves extend.
01190  *
01191     // The test we do is simple - ensure that no points lie within one millipoint of the
01192     // dead zone. We only need one invalid point for the test to fail.
01193     BOOL bPassedTest = TRUE;
01194     if (bExamineX)
01195     {
01196         for (INT32 i = 0; i < nNumPoints && bPassedTest; i ++)
01197         {
01198             bPassedTest =   (doccArray[i].x > (myCentre + EXT_EXPAND_BUFFER)) ||
01199                             (doccArray[i].x < (myCentre - EXT_EXPAND_BUFFER));
01200         }
01201     }
01202     else
01203     {
01204         for (INT32 i = 0; i < nNumPoints && bPassedTest; i ++)
01205         {
01206             bPassedTest =   (doccArray[i].y > (myCentre + EXT_EXPAND_BUFFER)) ||
01207                             (doccArray[i].y < (myCentre - EXT_EXPAND_BUFFER));
01208         }
01209     }
01210 
01211     return bPassedTest ? -1 : 0;
01212  */
01213 
01214     return -1;
01215 }

INT32 Extender::CheckInvalidShrinkingPoints INT32  nCentre,
INT32  nDelta,
INT32  nOffset,
INT32  nNumPoints,
const DocCoord doccArray,
BOOL  bExamineX
[static, private]
 

Check whether any of the points in the given array will be shrunk past the given limiting value. If all the points check out, we return an all-clear value, which is negative. Otherwise, the maximum safe shrinkage is given.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
07/12/1999
Parameters:
nCentre the limiting x or y centre-value, to which points are shrinking. [INPUTS] nDelta the distance by which the points are being shrunk. nOffset an offset which should be applied to the points before testing. nNumPoints the number of points to test. doccArray the array of points to test. bExamineX whether to examine x-coords (TRUE) or y-coords (FALSE).
[OUTPUTS] 
Returns:
The maximum safe distance that points can be extended towards nCentre by. This value will be non-negative if any invalid points were found, or negative if all points passed the test.
Karim 11/05/2000 With the addition of invariant points, this method only checks those points which would move under extension. If a point is invariant, ie it is so close to nCentre that it would not be moved, it is disregarded. It is up to the various extend methods to ensure that these invariant points actually *stay* invariant!
Returns:
Errors: See also:

Definition at line 824 of file extender.cpp.

00827 {
00828     // return with an all-clear if delta is zero.
00829     if (nDelta == 0)
00830         return -1;
00831 
00832     // test the points we were given, and determine the furthest we can move them without
00833     // dragging any into the dead zone, defined as the limit value +/- our buffer size.
00834     // this value will be the least of all the maximum distances that each point can
00835     // shrink by.
00836     INT32 maxShrink, smallest_maxShrink = INT32_MAX;
00837 
00838     // each point should be offset by nOffset so for testing, we 'un-offset' the centre.
00839     INT32 myCentre = nCentre - nOffset;
00840 
00841     // nDelta < 0 means we are being dragged down or left.
00842     // we're checking that each point can stretch by nDelta down towards nCentre.
00843     // if it can't, we see how far it _can_ go.
00844     if (nDelta < 0)
00845     {
00846         // validate the x-component of the coordinates.
00847         if (bExamineX)
00848         {
00849             for (INT32 i = 0; i < nNumPoints; i ++)
00850             {
00851                 maxShrink = CheckLimits(myCentre, nDelta, doccArray[i].x);
00852                 if (maxShrink >= 0 && smallest_maxShrink > maxShrink)
00853                     smallest_maxShrink = maxShrink;
00854             }
00855         }
00856 
00857         // validate the y-component of the coordinates.
00858         else
00859         {
00860             for (INT32 i = 0; i < nNumPoints; i ++)
00861             {
00862                 maxShrink = CheckLimits(myCentre, nDelta, doccArray[i].y);
00863                 if (maxShrink >= 0 && smallest_maxShrink > maxShrink)
00864                     smallest_maxShrink = maxShrink;
00865             }
00866         }
00867     }
00868 
00869     // nDelta > 0 means we are being dragged up or right.
00870     // we're checking that each point can stretch by nDelta up towards nCentre.
00871     // if it can't, we see how far it _can_ go.
00872     else
00873     {
00874         // validate the x-component of the coordinates.
00875         if (bExamineX)
00876         {
00877             for (INT32 i = 0; i < nNumPoints; i ++)
00878             {
00879                 maxShrink = CheckLimits(myCentre, -nDelta, myCentre - doccArray[i].x + myCentre);
00880                 if (maxShrink >= 0 && smallest_maxShrink > maxShrink)
00881                     smallest_maxShrink = maxShrink;
00882             }
00883         }
00884 
00885         // validate the y-component of the coordinates.
00886         else
00887         {
00888             for (INT32 i = 0; i < nNumPoints; i ++)
00889             {
00890                 maxShrink = CheckLimits(myCentre, -nDelta, myCentre - doccArray[i].y + myCentre);
00891                 if (maxShrink >= 0 && smallest_maxShrink > maxShrink)
00892                     smallest_maxShrink = maxShrink;
00893             }
00894         }
00895     }
00896 
00897     return smallest_maxShrink == INT32_MAX ? -1 : smallest_maxShrink;
00898 }

INT32 Extender::CheckLimits const INT32  limit,
const INT32  delta,
const INT32  value
[inline, static, private]
 

Checks that the given value may be reduced by the amount delta without going below the given limit value. If it cannot, we return the most which the given value can be safely reduced by.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
07/12/1999
Parameters:
limit the limiting value to use. [INPUTS] delta the amount to attempt to shrink by - should always be negative. value the value to validate.
[OUTPUTS] 
Returns:
-1 if the validation was successful, non-negative integer value otherwise.
A preset buffer value is applied, defining a 'dead zone' around the limit, into which the value may not be reduced. All calculations performed within this method take account of this dead zone. Also note that if a value begins within the dead zone, then an all-clear value of -1 will be returned, as we don't want this invariant point to interfere with shrinking of other values. The actual extension code should always check for and not alter these points.

Returns:
Errors: ERROR3 if delta is non-negative (ie >= 0). See also:

Definition at line 930 of file extender.cpp.

00931 {
00932     // data validation.
00933     ERROR3IF(delta >= 0, "Extender::CheckLimits called with delta non-negative!");
00934 
00935     // we return an all-clear if the test value starts in the buffer zone.
00936     if (value >= limit - EXT_SHRINK_BUFFER && value <= limit + EXT_SHRINK_BUFFER)
00937         return -1;
00938 
00939     // give the all-clear if the test value lies below the limit value already.
00940     // also give the all-clear if the test value will not be drawn down into the dead-zone.
00941     if (value < limit - EXT_SHRINK_BUFFER || value + delta > limit + EXT_SHRINK_BUFFER)
00942         return -1;
00943 
00944     // ok, we're invalid - return the safe shrinkage distance for the test value.
00945     return value - (limit + EXT_SHRINK_BUFFER + 1);
00946 }

BOOL Extender::CheckValidExtend NodeRenderableInk pNode,
DocRect pStartRect,
DocRect pEndRect,
BYTE  fExtendFlags,
DocRect pOldStartRect = NULL
[static]
 

** DEPRECATED - DO NOT CALL **

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
17/11/1999
Parameters:
[INPUTS] 
[OUTPUTS] 
Returns:
FALSE

Errors: ERROR2 always. See also:

Definition at line 186 of file extender.cpp.

00191 {
00192     ERROR2( FALSE,
00193             "This implementation of Extender::CheckValidExtend is no longer used or valid" );
00194 }

DocRect Extender::CheckValidExtend NodeRenderableInk pNode,
BYTE  fExtendFlags,
const DocRect drTriggerRelation,
const DocRect drTargetRelation,
const DocRect drNewTrigger,
const DocRect drLastTrigger,
const DocRect drLastTarget,
BOOL *  pOK = NULL,
const BOOL  ExtendAroundTarget = FALSE
[static]
 

This function tests whether or not a call to the extend function will extend an object in a valid way. For example, a path cannot be shrunken so that its control points cross its centre, as this operation is irreversible - if the path was extended back again, these points would extend the wrong direction. Similarly, an invalid extend occurs when a trigger set is shrunk so that its centre happens to move over the centre of a target node which is not moving in this particular extension; undoing this operation would end up dragging the target node back with the trigger.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
06/12/1999
Parameters:
pNode pointer to a NodeRenderableInk which will be extended. [INPUTS] fExtendFlags bitwise flags, describing any combination of x- or y- stretching or extending, except that you may not extend and stretch along an axis at the same time. Values:
X_EXTEND the node should be extended along the x-axis Y_EXTEND the node should be extended along the y-axis X_STRETCH the node should be stretched along the x-axis Y_STRETCH the node should be stretched along the y-axis

drTriggerRelation relationship rectangle - the bounding box of the trigger sets when the relationship is defined. drTargetRelation relationship rectangle - the bounding box of the target sets when the relationship is defined. drNewTrigger bounding box of sets of triggers which started the call to this function. drLastTrigger the bounding box of the trigger set(s) before it changed. drLastTarget the bounding box of the target set before it is extended.

This function should always be called on all objects sharing the same name, before* Extender::Extend is called to actually perform the extension on those objects, to make sure that no irreversible operation is performed.

Returns:
A DocRect, whose values will all be set to INT32_MAX if the test was passed. For each side on which the test failed, the corresponding value will be set to how far in it is safe to extend the node, eg if the node could only be shrunk inwards by 10 millipoints on the right-hand side, then the hix value of the DocRect will contain 10, while all other values will be INT32_MAX.

Errors: ERROR2 with FALSE if fExtendFlags holds contradictory flags or any parameters are NULL.

See also: Extender::CheckValidExtend() and for more information, see: \Earth V3 Objects - Mechanism.doc

NOTE: A call to Extender::CheckValidExtend must *always* be made before performing a corresponding Extender::Extend process.

Definition at line 436 of file extender.cpp.

00440 {
00441     // validate parameters.
00442     ERROR2IF(   pNode == NULL, DocRect(0, 0, 0, 0),
00443                 "NULL parameters passed to Extender::CheckValidExtend" );
00444 
00445     ERROR2IF(   ((fExtendFlags & X_EXTEND) && (fExtendFlags & X_STRETCH)) ||
00446                 ((fExtendFlags & Y_EXTEND) && (fExtendFlags & Y_STRETCH)),
00447                 DocRect(0, 0, 0, 0),
00448                 "Extender::CheckValidExtend called with contradictory flags in fExtendFlags" );
00449 
00450     if (pOK)
00451         *pOK = TRUE;
00452     // quit immediately if any of the bounding rectangles is invalid,
00453     // or is of zero width or height.
00454     if(         !drTriggerRelation.IsValid() ||
00455                 !drTargetRelation.IsValid() ||
00456                 !drNewTrigger.IsValid() ||
00457                 !drLastTrigger.IsValid() ||
00458                 !drLastTarget.IsValid() ||
00459                 drTriggerRelation.Height() == 0 ||
00460                 drTriggerRelation.Width() == 0 ||
00461                 drTargetRelation.Height() == 0 ||
00462                 drTargetRelation.Width() == 0 ||
00463                 drNewTrigger.Height() == 0 ||
00464                 drNewTrigger.Width() == 0 ||
00465                 drLastTrigger.Height() == 0 ||
00466                 drLastTrigger.Width() == 0 ||
00467                 drLastTarget.Height() == 0 ||
00468                 drLastTarget.Width() == 0)
00469     {
00470         if (pOK)
00471             *pOK = FALSE;
00472 
00473         return DocRect(0, 0, 0, 0);
00474     }
00475 
00476 
00477 
00478     // determine extension working values.
00479     ExtendParams eps;
00480     CalculateExtendParams(  &eps, fExtendFlags, drTriggerRelation, drTargetRelation,
00481                                                 drNewTrigger, drLastTrigger, drLastTarget, NULL, ExtendAroundTarget );
00482 
00483     // check whether pNode, or any child of pNode, is extendible.
00484     // also, check that extending all of these Nodes is a valid operation.
00485     // if this is the case, recursively extend pNode and its children.
00486     DocRect drMinExtend = pNode->ValidateExtend(eps);
00487     BOOL bValidExtend = (   drMinExtend.lo.x == INT32_MAX &&
00488                             drMinExtend.lo.y == INT32_MAX &&
00489                             drMinExtend.hi.x == INT32_MAX &&
00490                             drMinExtend.hi.y == INT32_MAX );
00491 
00492 // DEBUG:
00493 #ifdef _DEBUG
00494     TRACEUSER( "Karim", _T("%5s ValidateExtend() on %s\n"), bValidExtend ? _T("TRUE") : _T("FALSE"),
00495                                                         pNode->Name());
00496 #endif
00497 
00498     BOOL bExtendible = TRUE;
00499 
00500     if (!bExtendible || !bValidExtend)
00501     {
00502 //#ifdef _DEBUG
00503 //      TRACEUSER( "Karim", _T("Invalid Extend on %s; Ext: %d, Valid: %d\n"),
00504 //                          pNode->Name(), bExtendible, bValidExtend);
00505 //#endif
00506         return drMinExtend;
00507     }
00508 
00509     // return the outcome of the test, in the form of drMinExtend.
00510     return drMinExtend;
00511 }

BOOL Extender::ConvertQuickShapesInSelRangeToPaths UndoableOperation pUndoOp,
Range pRange
[static]
 

Iterates over the given SelRange looking for any NodeRegularShapes, ie QuickShapes. If we find any then they get zapped, and all that is left is a smoking pair of shoes! ... erm, sorry - a NodePath.

Notes: This is all done undoably. There is however a problem in that we do not necessarily want to completely unwind the UndoOp we were given if anything goes wrong. But if we don't do that then what _can_ we do??? Currently, if things go wrong while converting to shapes, we just try to convert as many shapes to paths as possible and return FALSE at the end.

We don't mind if pSel is empty or has no QuickShapes.

Our search of the SelRange is deep - we look at children of nodes in the range too.

Returns:
Errors: ERROR2 with FALSE if we get invalid parameters. See also:

Definition at line 235 of file extender.cpp.

00237 {
00238     // validate params.
00239     ERROR2IF(pUndoOp == NULL || pRange == NULL, FALSE, "Invalid parameter(s)");
00240 
00241 //  TODO: May need to set the range's RangeControl to avoid promoting to parents.
00242 
00243     // iterate over the range and build a list of QuickShapes.
00244     // we do a depth-first search from every node in the range,
00245     // looking everywhere for those those pesky QuickShapes :)
00246     BOOL bQuickShapesConverted = FALSE;
00247     std::list<NodeRegularShape*> lpShapes;
00248     Node* pCurrentRangeNode = pRange->FindFirst();
00249     while (pCurrentRangeNode != NULL)
00250     {
00251         Node* pNextNode = NULL;
00252         Node* pCurrentNode = pCurrentRangeNode->FindFirstDepthFirst();
00253         while (pCurrentNode != NULL)
00254         {
00255             pNextNode = pCurrentNode->FindNextDepthFirst(pCurrentRangeNode);
00256             if (pCurrentNode->IsARegularShape())
00257             {
00258                 if (!(pCurrentNode->FindParent (CC_RUNTIME_CLASS (NodeBlend))))
00259                 {
00260                     // note thate we're *saying* we're going to replace with only one node.
00261                     // this isn't necessarily true - DoBecomeA on a brushed path results in
00262                     // many ungrouped nodes. it's just the best we can do, considering blends.
00263                     ObjChangeFlags ocf;
00264                     ocf.ReplaceNode = TRUE;
00265                     ObjChangeParam ObjChange(OBJCHANGE_STARTING, ocf, pCurrentNode, pUndoOp);
00266                     if (pCurrentNode->AllowOp(&ObjChange, FALSE, FALSE))
00267                         lpShapes.push_front((NodeRegularShape*)pCurrentNode);
00268                 }
00269                 /*else
00270                 {
00271                     // note thate we're *saying* we're going to replace with only one node.
00272                     // this isn't necessarily true - DoBecomeA on a brushed path results in
00273                     // many ungrouped nodes. it's just the best we can do, considering blends.
00274                     ObjChangeFlags ocf;
00275                     ocf.ReplaceNode = TRUE;
00276                     ObjChangeParam ObjChange(OBJCHANGE_STARTING, ocf, pCurrentNode, pUndoOp);
00277                     if (pCurrentNode->AllowOp(&ObjChange, TRUE, FALSE))
00278                         lpShapes.push_front((NodeRegularShape*)pCurrentNode);
00279                 }*/
00280             }
00281             pCurrentNode = pNextNode;
00282         }
00283         pCurrentRangeNode = pRange->FindNext(pCurrentRangeNode, TRUE);
00284     }
00285 
00286     // okay, if our list isn't empty then we have work to do.
00287     if (!lpShapes.empty())
00288     {
00289         // invalidate the region of the selection.
00290         if (pUndoOp->DoInvalidateNodesRegions(*pRange, TRUE))
00291         {
00292             // change all the QuickShapes into NodePaths.
00293             NodeRegularShape* pCurrentShape = NULL;
00294             while (!lpShapes.empty())
00295             {
00296                 pCurrentShape = lpShapes.front();
00297                 lpShapes.pop_front();
00298                 BecomeA BecomeAPath( BECOMEA_REPLACE,
00299                                      CC_RUNTIME_CLASS(NodePath),
00300                                      pUndoOp,
00301                                      pCurrentShape->IsSelected() );
00302 
00303                 if (pCurrentShape->CanBecomeA(&BecomeAPath))
00304                 {
00305                     if (!(pCurrentShape->FindParent (CC_RUNTIME_CLASS (NodeBlend))))
00306                     {
00307                         if (pCurrentShape->DoBecomeA(&BecomeAPath))
00308                         {
00309                             pCurrentShape->DeSelect(FALSE);
00310                             bQuickShapesConverted = TRUE;
00311                         }
00312                     }
00313                     /*else
00314                     {
00315                         NodeBlend* ptrBlend = (NodeBlend*) pCurrentShape->FindParent (CC_RUNTIME_CLASS (NodeBlend));
00316                         BecomeA BecomeAPath( BECOMEA_REPLACE,
00317                                              CC_RUNTIME_CLASS(NodePath),
00318                                              pUndoOp,
00319                                              pCurrentShape->IsSelected() );
00320                         if (pCurrentShape->DoBecomeA(&BecomeAPath))
00321                         {
00322                             pCurrentShape->DeSelect(FALSE);
00323                             bQuickShapesConverted = TRUE;
00324                         }                                               
00325                         
00326                         // nastiness - to reinitialise the blend, I need a ptr to the converted path
00327                         // BUT there is no easy way of doing this!  This is a hack - which seems to
00328                         // hold up well ....  NOTE:  assumes that the last action executed was a
00329                         // HideNodeAction (i.e.  hide pCurrentShape)
00330 
00331                         ActionList* actList = pUndoOp->GetUndoActionList ();    // get undo history
00332                         ListItem* pItem = actList->GetTail ();                  // get last op
00333                         ERROR3IF (!IS_A (pItem, HideNodeAction), "Assumed HideNodeAction is not a HideNodeAction!");
00334                         HideNodeAction* hnAct = (HideNodeAction*) pItem;
00335                         NodeRenderableInk* newNode = (NodeRenderableInk*) hnAct->GetNode ();
00336                         ERROR3IF (!IS_A (ptrBlend, NodeBlend), "NodeBlend is not a NodeBlend!");
00337 
00338                         BOOL done = FALSE;
00339                         NodeBlender* ptrNode = ptrBlend->FindFirstBlender ();
00340 
00341                         while (!done)
00342                         {
00343                             ptrNode->Deinit ();
00344                             
00345                             if (ptrNode->GetNodeStart () == pCurrentShape)
00346                             {
00347                                 ptrNode->Reinit(newNode, NULL, FALSE);
00348                             }
00349                             if (ptrNode->GetNodeEnd () == pCurrentShape)
00350                             {
00351                                 ptrNode->Reinit(NULL, newNode, FALSE);
00352                             }
00353 
00354                             ptrNode = ptrBlend->FindNextBlender (ptrNode);
00355 
00356                             if (!ptrNode)
00357                             {
00358                                 done = TRUE;
00359                             }
00360                         }
00361                     }*/
00362                 }
00363             }
00364         }
00365         if (bQuickShapesConverted)
00366             pRange->Update();
00367     }
00368 
00369     return bQuickShapesConverted;
00370 }

void Extender::Extend NodeRenderableInk pNode,
DocRect pStartRect,
DocRect pEndRect,
BYTE  fExtendFlags,
DocRect pOldStartRect = NULL
[static]
 

** DEPRECATED - DO NOT CALL **

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
10/09/1999
Parameters:
[INPUTS] 
[OUTPUTS] 
Returns:
Errors: ERROR2 always. See also:

Definition at line 157 of file extender.cpp.

00162 {
00163     ERROR2RAW("This implementation of Extender::Extend is no longer used or valid");
00164 }

void Extender::Extend NodeRenderableInk pNode,
BYTE  fExtendFlags,
const DocRect drTriggerRelation,
const DocRect drTargetRelation,
const DocRect drNewTrigger,
const DocRect drLastTrigger,
const DocRect drLastTarget,
const DocRect pdrDifference = NULL,
const BOOL  ExtendAroundTarget = FALSE,
UndoableOperation pOp = NULL
[static]
 

Perform an extend operation on the given Node and its children, using the given rectangles and flags to determine how the Node should transform.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
03/12/1999
Parameters:
pNode pointer to a NodeRenderableInk which will be extended. [INPUTS] fExtendFlags bitwise flags, describing any combination of x- or y- stretching or extending, except that you may not extend and stretch along an axis at the same time. Values:
X_EXTEND the node should be extended along the x-axis Y_EXTEND the node should be extended along the y-axis X_STRETCH the node should be stretched along the x-axis Y_STRETCH the node should be stretched along the y-axis

drTriggerRelation relationship rectangle - the bounding box of the trigger sets when the relationship is defined. drTargetRelation relationship rectangle - the bounding box of the target sets when the relationship is defined. drNewTrigger bounding box of sets of triggers which started the call to this function. drLastTrigger the bounding box of the trigger set(s) before it changed. drLastTarget the bounding box of the target set before it is extended. pdrDifference a rectangle containing four difference parameters which may be used to alter the extend. ok to omit or be NULL.

Parameters:
pNode may be extended or stretched horizontally [OUTPUTS] or vertically in any combination.
Returns:
Errors: ERROR2 if fExtendFlags holds contradictory flags or any parameters are NULL.
See also: Extender::CheckValidExtend() and for more information, see: \Earth V3 Objects - Mechanism.doc

NOTE: A call to Extender::CheckValidExtend must always be made before performing a corresponding Extender::Extend process.

Definition at line 560 of file extender.cpp.

00565 {
00566     // validate parameters.
00567     if (pNode == NULL)
00568     {
00569         ERROR2RAW("NULL parameters passed to Extender::Extend.");
00570         return;
00571     }
00572 
00573     if (((fExtendFlags & X_EXTEND) && (fExtendFlags & X_STRETCH)) ||
00574         ((fExtendFlags & Y_EXTEND) && (fExtendFlags & Y_STRETCH)))
00575     {
00576         ERROR2RAW("Extender::Extend called with contradictory flags in fExtendFlags");
00577         return;
00578     }
00579 
00580     // quit immediately if any of the bounding rectangles is invalid,
00581     // or is of zero width or height.
00582     if (!drTriggerRelation.IsValid() ||
00583         !drTargetRelation.IsValid() ||
00584         !drNewTrigger.IsValid() ||
00585         !drLastTrigger.IsValid() ||
00586         !drLastTarget.IsValid() ||
00587         drTriggerRelation.Height() == 0 ||
00588         drTriggerRelation.Width() == 0 ||
00589         drTargetRelation.Height() == 0 ||
00590         drTargetRelation.Width() == 0 ||
00591         drNewTrigger.Height() == 0 ||
00592         drNewTrigger.Width() == 0 ||
00593         drLastTrigger.Height() == 0 ||
00594         drLastTrigger.Width() == 0 ||
00595         drLastTarget.Height() == 0 ||
00596         drLastTarget.Width() == 0)
00597     {
00598         ERROR2RAW("Extender::Extend; Invalid or zero-width rectangle");
00599         return;
00600     }
00601 
00602     // determine extension working values.
00603     ExtendParams eps;
00604     eps.pOp = pOp;
00605     CalculateExtendParams(  &eps, fExtendFlags, drTriggerRelation, drTargetRelation,
00606                                                 drNewTrigger, drLastTrigger,
00607                                                 drLastTarget, pdrDifference, ExtendAroundTarget );
00608 
00609     // this hasn't really changed size so do nothing
00610     if (!eps.xdec && !eps.xinc && !eps.ydec && !eps.yinc && eps.xscale == 1.0 && eps.yscale == 1.0
00611         && eps.doccOffset.x == 0 && eps.doccOffset.y == 0)
00612         return;
00613 
00614     // invalidate the old image of the object, perform the extension
00615     // and flag that the object should be redrawn.
00616     // we must look above this node, to detect whether we are the child
00617     // of a node whose on-screen bounds are greater than ours.
00618     // if this is the case, we need to invalidate *that* node and
00619     // get it to redraw.
00620     // TODO: there may be a better way of doing this (is it necessary at all with the AllowOp mechanism?)
00621     BOOL bFoundBigParent = FALSE;
00622     Node* pInvalidateNode = pNode->FindParent();
00623     while (pInvalidateNode != NULL && !IS_A(pInvalidateNode, Layer) && !bFoundBigParent)
00624     {
00625         if (IS_A(pInvalidateNode, NodeBevelController) ||
00626             IS_A(pInvalidateNode, NodeShadowController) ||
00627             IS_A(pInvalidateNode, NodeContourController))
00628             bFoundBigParent = TRUE;
00629         else
00630             pInvalidateNode = pInvalidateNode->FindParent();
00631     }
00632 
00633     if (!bFoundBigParent)
00634         pInvalidateNode = pNode;
00635 
00636     ((NodeRenderableInk*)pInvalidateNode)->RedrawObject();
00637     ((NodeRenderableInk*)pInvalidateNode)->InvalidateBoundingRect();
00638     pNode->Extend(eps);
00639 
00640 // DEBUG:
00641 //#ifdef _DEBUG
00642 //  TRACEUSER( "Karim", _T("Extend() on %s\n"), pNode->Name());
00643 //#endif
00644 
00645     ((NodeRenderableInk*)pInvalidateNode)->RedrawObject();
00646 }

DocRect Extender::ValidateControlPoints INT32  numPoints,
const DocCoord doccArray,
const ExtendParams ExtParams
[static]
 

Validate the given points, using the provided extend parameters. If extending the points is invalid, then return a rectangle containing the largest distances which the points can be shrunk in by.

Author:
Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
Date:
07/12/1999
Parameters:
numPoints number of coordinates to validate. [INPUTS] doccArray the array of coordinates. ExtParams extend parameters.
Returns:
A DocRect containing maximum safe shrink distances for the points:
lox maximum shrinkage in from the left. hix maximum shrinkage in from the right. loy maximum shrinkage in from the bottom. hiy maximum shrinkage in from the top.

If shrinking in a particular direction is ok, the corresponding DocRect value will be set to INT32_MAX.

Returns:
Errors: ERROR3 if doccArray is NULL. See also:

Definition at line 677 of file extender.cpp.

00678 {
00679     // validate data
00680     ERROR3IF(doccArray == NULL, "Extender::ValidateControlPoints- NULL doccArray passed!");
00681 
00682     DocRect drMaxSafeShrink(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drCheckExtend;
00683 
00684     // this check is only necessary if extension will be occurring along an
00685     // axis; if the extension type is stretch, or no action, no validation
00686     // is required.
00687 
00688     // for each of the four directions of displacement, and only if we shrank, check that
00689     // none of the points lie in a position to which they would not be returned under the
00690     // inverse extend operation. if they do, we record the largest safe amount we could
00691     // shrink them in by.
00692 
00693     // providing the extend operation involves displacing all points by the difference
00694     // between start- and end- centres, followed by the extension, these invalid points
00695     // will be only those which would be dragged inwards, to cross over the end-centre.
00696 
00697     if (ExtParams.fExtendFlags & X_EXTEND)
00698     {
00699         if (ExtParams.xinc < 0)
00700         {
00701             INT32 minXshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.x,
00702                                                             ExtParams.xinc,
00703                                                             ExtParams.doccOffset.x,
00704                                                             numPoints, doccArray, TRUE );
00705             if (minXshrink >= 0)    // invalid, so record the return value.
00706                 drMaxSafeShrink.hi.x = minXshrink;
00707         }
00708 
00709         // we do a different test if we're expanding.
00710         else if (ExtParams.xinc > 0)
00711         {
00712             INT32 minXshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.x,
00713                                                             ExtParams.xinc,
00714                                                             ExtParams.doccOffset.x,
00715                                                             numPoints, doccArray, TRUE );
00716             if (minXshrink >= 0)    // invalid, so record the return value.
00717                 drMaxSafeShrink.hi.x = minXshrink;
00718         }
00719 
00720         if (ExtParams.xdec < 0)
00721         {
00722             INT32 minXshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.x,
00723                                                             -ExtParams.xdec,
00724                                                             ExtParams.doccOffset.x,
00725                                                             numPoints, doccArray, TRUE );
00726             if (minXshrink >= 0)    // invalid, so record the return value.
00727                 drMaxSafeShrink.lo.x = minXshrink;
00728         }
00729 
00730         // we do a different test if we're expanding.
00731         else if (ExtParams.xdec > 0)
00732         {
00733             INT32 minXshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.x,
00734                                                             -ExtParams.xdec,
00735                                                             ExtParams.doccOffset.x,
00736                                                             numPoints, doccArray, TRUE );
00737             if (minXshrink >= 0)    // invalid, so record the return value.
00738                 drMaxSafeShrink.lo.x = minXshrink;
00739         }
00740     }
00741 
00742     if (ExtParams.fExtendFlags & Y_EXTEND)
00743     {
00744         if (ExtParams.yinc < 0)
00745         {
00746             INT32 minYshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.y,
00747                                                             ExtParams.yinc,
00748                                                             ExtParams.doccOffset.y,
00749                                                             numPoints, doccArray, FALSE );
00750             if (minYshrink >= 0)    // invalid, so record the return value.
00751                 drMaxSafeShrink.hi.y = minYshrink;
00752         }
00753 
00754         // we do a different test if we're expanding.
00755         else if (ExtParams.yinc > 0)
00756         {
00757             INT32 minYshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.y,
00758                                                             ExtParams.yinc,
00759                                                             ExtParams.doccOffset.y,
00760                                                             numPoints, doccArray, FALSE );
00761             if (minYshrink >= 0)    // invalid, so record the return value.
00762                 drMaxSafeShrink.hi.y = minYshrink;
00763         }
00764 
00765         if (ExtParams.ydec < 0)
00766         {
00767             INT32 minYshrink = CheckInvalidShrinkingPoints( ExtParams.doccEndCentre.y,
00768                                                             -ExtParams.ydec,
00769                                                             ExtParams.doccOffset.y,
00770                                                             numPoints, doccArray, FALSE );
00771             if (minYshrink >= 0)    // invalid, so record the return value.
00772                 drMaxSafeShrink.lo.y = minYshrink;
00773         }
00774 
00775         // we do a different test if we're expanding.
00776         else if (ExtParams.ydec > 0)
00777         {
00778             INT32 minYshrink = CheckInvalidExpandingPoints( ExtParams.doccEndCentre.y,
00779                                                             -ExtParams.ydec,
00780                                                             ExtParams.doccOffset.y,
00781                                                             numPoints, doccArray, FALSE );
00782             if (minYshrink >= 0)    // invalid, so record the return value.
00783                 drMaxSafeShrink.lo.y = minYshrink;
00784         }
00785     }
00786 
00787     return drMaxSafeShrink;
00788 }


The documentation for this class was generated from the following files:
Generated on Sat Nov 10 03:54:07 2007 for Camelot by  doxygen 1.4.4