GGSliceOpsImpl.mesa
Contents: This module is the outer interface to slice class procs
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Pier, July 18, 1991 7:46 pm PDT
Bier, December 2, 1991 11:13 am PST
DIRECTORY
Atom, Feedback, FeedbackTypes, GGCoreTypes, GGHistoryTypes, GGHistoryTypesOpaque, GGModelTypes, GGOutline, GGParent, GGParseIn, GGProps, GGSlice, GGSliceOps, GGTraj, Imager, ImagerTransformation, IO, Rope;
GGSliceOpsImpl: CEDAR PROGRAM
IMPORTS Atom, Feedback, GGOutline, GGParent, GGParseIn, GGProps, --GGScene,-- GGSlice, GGSliceOps, GGTraj, ImagerTransformation, IO, Rope
EXPORTS GGSliceOps, GGHistoryTypes = BEGIN
OPEN GGModelTypes;
noteNoMore: PUBLIC BOOLTRUE;
debugHalt: PUBLIC BOOLFALSE;
BoundBox: TYPE = GGCoreTypes.BoundBox;
Change: PUBLIC TYPE = GGHistoryTypesOpaque.Change; -- exported to GGHistoryTypes
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
HistoryProc: TYPE = GGHistoryTypes.HistoryProc;
SubEvent: TYPE = GGHistoryTypes.SubEvent;
SubEventObj: TYPE = GGHistoryTypes.SubEventObj;
StrokeEndRec: TYPE = RECORD [end: Imager.StrokeEnd]; -- TYPE = {square, butt, round}
StrokeJointRec: TYPE = RECORD [joint: Imager.StrokeJoint]; -- TYPE = {miter, bevel, round}
DashedRec: TYPE = RECORD [dashed: BOOLFALSE, pattern: SequenceOfReal, offset, length: REAL];
SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal;
ArrowRec: TYPE = RECORD [leftDown, rightUp: BOOLFALSE];
OrientationRec: TYPE = RECORD [or: Orientation];
TransformRec: TYPE = RECORD [transform: Imager.Transformation, editConstraints: EditConstraints];
DebugHalt: SIGNAL = CODE;
One-to-one correspondence between procs here and SliceClass procs. Clients should call through GGSliceOps instead of directly through class record.
GetType: PUBLIC PROC [slice: Slice] RETURNS [type: ATOM] = {RETURN[slice.class.type]};
GetBoundBox: PUBLIC SliceBoundBoxProc = {RETURN[slice.class.getBoundBox[slice, parts]];};
GetTransformedBoundBox: PUBLIC SliceTransformedBoundBoxProc = {RETURN[slice.class.getTransformedBoundBox[slice, selectedParts, movingParts, transform]];};
GetTightBox: PUBLIC SliceTightBoxProc = {RETURN[slice.class.getTightBox[slice, parts]];};
Copy: PUBLIC SliceCopyProc = {RETURN[slice.class.copy[slice, parts]];};
Restore: PUBLIC SliceRestoreProc = {from.class.restore[from: from, to: to];};
Unlink: PUBLIC SliceUnlinkProc = {slice.class.unlink[slice];};
BuildPath: PUBLIC SliceBuildPathProc = {slice.class.buildPath[slice, transformParts, transform, moveTo, lineTo, curveTo, conicTo, arcTo, editConstraints];};
DrawBorder: PUBLIC SliceDrawBorderProc = {slice.class.drawBorder[slice, drawParts, transformParts, transform, dc, camera, quick, editConstraints];};
DrawParts: PUBLIC SliceDrawPartsProc = {slice.class.drawParts[slice, parts, dc, camera, quick];};
DrawTransform: PUBLIC SliceDrawTransformProc = {slice.class.drawTransform[slice, parts, dc, camera, transform, editConstraints ];};
DrawSelectionFeedback: PUBLIC SliceDrawSelectionFeedbackProc = {slice.class.drawSelectionFeedback[slice, selectedParts, hotParts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];};
DrawAttractorFeedback: PUBLIC SliceDrawAttractorFeedbackProc = {slice.class.drawAttractorFeedback[slice, attractorParts, selectedParts, dragInProgress, dc, camera, editConstraints];};
AttractorFeedbackBoundBox: PUBLIC SliceAttractorFeedbackBoundBoxProc = {
IF slice.class.attractorFeedbackBoundBox = NIL
THEN RETURN GGSlice.NoOpAttractorFeedbackBoundBox[slice, attractorParts, selectedParts, dragInProgress, camera, editConstraints]
ELSE RETURN slice.class.attractorFeedbackBoundBox[slice, attractorParts, selectedParts, dragInProgress, camera, editConstraints];
};
SaveSelections: PUBLIC SliceSaveSelectionsProc = {slice.class.saveSelections[slice, parts, selectClass];};
RemakeSelections: PUBLIC SliceRemakeSelectionsProc = {
parts ← slice.class.remakeSelections[slice, selectClass];
};
Transform: PUBLIC SliceTransformProc = {
oldValue: REF TransformRec ← NEW[TransformRec];
oldValue.transform ← transform; -- defer inverting the transform until undo is performed
oldValue.editConstraints ← editConstraints;
slice.class.transform[slice, parts, transform, editConstraints, history];
GenericPropsUndo[op: $Transform, slice: slice, parts: parts, val: oldValue, history: history];
};
Describe: PUBLIC SliceDescribeProc = {RETURN[sliceD.slice.class.describe[sliceD]];};
DescribeHit: PUBLIC SliceDescribeHitProc = {RETURN[slice.class.describeHit[slice, hitData]];};
Fileout: PUBLIC SliceFileoutProc = {slice.class.fileout[slice, f];};
Filein: PUBLIC SliceFileinProc = {RETURN[slice.class.filein[f, version, router, camera]];};
IsEmptyParts: PUBLIC SliceIsEmptyPartsProc = { RETURN[sliceD.slice.class.isEmptyParts[sliceD]];};
IsCompleteParts: PUBLIC SliceIsCompletePartsProc = { RETURN[sliceD.slice.class.isCompleteParts[sliceD]];};
NewParts: PUBLIC SliceNewPartsProc = {RETURN[slice.class.newParts[slice, hitData, mode]];};
UnionParts: PUBLIC SliceUnionPartsProc = {RETURN[partsA.slice.class.unionParts[partsA, partsB]];};
DifferenceParts: PUBLIC SliceDifferencePartsProc = {
RETURN[partsA.slice.class.differenceParts[partsA, partsB]];};
MovingParts: PUBLIC SliceMovingPartsProc = {[background, overlay, rubber, drag] ← slice.class.movingParts[slice, selectedParts, editConstraints, bezierDrag];};
AugmentParts: PUBLIC SliceAugmentPartsProc = {
RETURN[sliceD.slice.class.augmentParts[sliceD, selectClass]];};
AlterParts: PUBLIC SliceAlterPartsProc = {
RETURN[sliceD.slice.class.alterParts[sliceD, action]];};
SetSelectedFields: PUBLIC SliceSetSelectedFieldsProc = {sliceD.slice.class.setSelectedFields[sliceD, selected, selectClass];};
PointsInDescriptor: PUBLIC SlicePointsInDescriptorProc = {
RETURN[sliceD.slice.class.pointsInDescriptor[sliceD]];};
WalkPointsInDescriptor: PUBLIC SliceWalkPointsInDescriptorProc = {
sliceD.slice.class.walkPointsInDescriptor[sliceD, walkProc];};
PointPairsInDescriptor: PUBLIC SlicePointPairsInDescriptorProc = {RETURN[sliceD.slice.class.pointPairsInDescriptor[sliceD]];};
SegmentsInDescriptor: PUBLIC SliceSegmentsInDescriptorProc = {RETURN[sliceD.slice.class.segmentsInDescriptor[sliceD]];};
WalkSegments: PUBLIC SliceWalkSegmentsProc = {RETURN[slice.class.walkSegments[slice, walkProc]];};
Calls the slice class walkSegments proc.
The walkProc is called with each segment in the slice. If walkProc returns TRUE, the part of the slice that corresponds to the segment will be in the SliceDescriptor returned by WalkSegments.
NextPoint: PUBLIC SliceNextPointProc = {RETURN[slice.class.nextPoint[slice, pointGen]];};
NextPointPair: PUBLIC SliceNextPointPairProc = {RETURN[slice.class.nextPointPair[slice, pointPairGen]];};
NextSegment: PUBLIC SliceNextSegmentProc = {[seg, transform] ← slice.class.nextSegment[slice, segGen];};
ClosestPoint: PUBLIC SliceClosestPointProc = {[bestPoint, bestDist, bestNormal, hitData, success] ← sliceD.slice.class.closestPoint[sliceD, testPoint, tolerance];};
ClosestPoint is an expensive procedure. GGMultiGravity and GGOutline call it directly.
ClosestJointToHitData: PUBLIC SliceClosestJointToHitDataProc = {[jointD, point, normal] ← sliceD.slice.class.closestJointToHitData[sliceD, mapPoint, testPoint, hitData];};
ClosestPointAndTangent: PUBLIC SliceClosestPointAndTangentProc = {[bestPoint, bestDist, tangent, hitData, success] ← sliceD.slice.class.closestPointAndTangent[sliceD, testPoint, tolerance];};
ClosestSegment: PUBLIC SliceClosestSegmentProc = {[bestPoint, bestDist, bestNormal, hitData, success] ← sliceD.slice.class.closestSegment[sliceD, testPoint, tolerance];};
ClosestSegment is an expensive procedure. GGMultiGravity and GGOutline call it directly.
FilledPathsUnderPoint: PUBLIC SliceFilledPathsUnderPointProc = {
Describes those paths of slice, with non-NIL fill color, that contain point. Used by GGMultiGravityImpl for faces-preferred gravity, which is used for selections. Paths are listed near to far in overlap order.
IF slice.class.filledPathsUnderPoint = NIL THEN RETURN GGSlice.NoOpFilledPathsUnderPoint[slice, point, tolerance]
ELSE {
RETURN slice.class.filledPathsUnderPoint[slice, point, tolerance];
};
};
LineIntersection: PUBLIC SliceLineIntersectionProc = {[points, pointCount] ← sliceD.slice.class.lineIntersection[sliceD, line];};
CircleIntersection: PUBLIC SliceCircleIntersectionProc = {[points, pointCount] ← sliceD.slice.class.circleIntersection[sliceD, circle];};
HitDataAsSimpleCurve: PUBLIC SliceHitDataAsSimpleCurveProc = {RETURN[slice.class.hitDataAsSimpleCurve[slice, hitData]];};
Style
SetDefaults: PUBLIC SliceSetDefaultsProc = {
PUNT on undo for now
slice.class.setDefaults[slice, parts, defaults, history];
};
SetStrokeWidth: PUBLIC SliceSetStrokeWidthProc = {
oldValue: REF REALNEW[REAL ← slice.class.getStrokeWidth[slice, parts].strokeWidth];
box ← slice.class.setStrokeWidth[slice, parts, strokeWidth, history];
GenericPropsUndo[op: $SetStrokeWidth, slice: slice, parts: parts, val: oldValue, history: history];
RETURN[box];
};
GetStrokeWidth: PUBLIC SliceGetStrokeWidthProc = {
RETURN slice.class.getStrokeWidth[slice, parts];
};
SetStrokeEnd: PUBLIC SliceSetStrokeEndProc = {
oldValue: REF StrokeEndRec ← NEW[StrokeEndRec];
oldValue.end ← slice.class.getStrokeEnd[slice, parts].strokeEnd;
slice.class.setStrokeEnd[slice, parts, strokeEnd, history];
GenericPropsUndo[op: $SetStrokeEnd, slice: slice, parts: parts, val: oldValue, history: history];
};
GetStrokeEnd: PUBLIC SliceGetStrokeEndProc = {
RETURN slice.class.getStrokeEnd[slice, parts];
};
SetStrokeJoint: PUBLIC SliceSetStrokeJointProc = {
oldValue: REF StrokeJointRec ← NEW[StrokeJointRec];
oldValue.joint ← slice.class.getStrokeJoint[slice, parts].strokeJoint;
slice.class.setStrokeJoint[slice, parts, strokeJoint, history];
GenericPropsUndo[op: $SetStrokeJoint, slice: slice, parts: parts, val: oldValue, history: history];
};
GetStrokeJoint: PUBLIC SliceGetStrokeJointProc = {
RETURN slice.class.getStrokeJoint[slice, parts];
};
SetStrokeColor: PUBLIC SliceSetStrokeColorProc = {
oldValue: Imager.Color ← slice.class.getStrokeColor[slice, parts].color;
slice.class.setStrokeColor[slice, parts, color, setHow, history];
GenericPropsUndo[op: $SetStrokeColor, slice: slice, parts: parts, val: oldValue, history: history];
};
GetStrokeColor: PUBLIC SliceGetStrokeColorProc = {
RETURN slice.class.getStrokeColor[slice, parts];
};
SetFillColor: PUBLIC SliceSetFillColorProc = {
oldValue: Imager.Color ← slice.class.getFillColor[slice, parts].color;
slice.class.setFillColor[slice, parts, color, setHow, history];
GenericPropsUndo[op: $SetFillColor, slice: slice, parts: parts, val: oldValue, history: history];
};
GetFillColor: PUBLIC SliceGetFillColorProc = {
RETURN slice.class.getFillColor[slice, parts];
};
SetArrows: PUBLIC SliceSetArrowsProc = {
oldValue: REF ArrowRec ← NEW[ArrowRec];
[oldValue.leftDown, oldValue.rightUp] ← slice.class.getArrows[slice];
slice.class.setArrows[slice, parts, leftDown, rightUp, history];
GenericPropsUndo[op: $SetArrows, slice: slice, parts: parts, val: oldValue, history: history];
};
GetArrows: PUBLIC SliceGetArrowsProc = {
RETURN slice.class.getArrows[slice];
};
SetDashed: PUBLIC SliceSetDashedProc = {
oldValue: REF DashedRec ← NEW[DashedRec];
[oldValue.dashed, oldValue.pattern, oldValue.offset, oldValue.length] ← slice.class.getDashed[slice, parts];
slice.class.setDashed[slice, parts, dashed, pattern, offset, length, history];
GenericPropsUndo[op: $SetDashed, slice: slice, parts: parts, val: oldValue, history: history];
};
GetDashed: PUBLIC SliceGetDashedProc = {
RETURN slice.class.getDashed[slice, parts];
};
SetOrientation: PUBLIC SliceSetOrientationProc = {
oldValue: REF OrientationRec ← NEW[OrientationRec];
oldValue.or ← slice.class.getOrientation[slice, parts].orientation;
success ← slice.class.setOrientation[slice, parts, orientation, history];
GenericPropsUndo[op: $SetOrientation, slice: slice, parts: NIL, val: oldValue, history: history];
};
GetOrientation: PUBLIC SliceGetOrientationProc = {
RETURN slice.class.getOrientation[slice, parts];
};
Procedures implemented as a thin veneer on top of the basic class procs
PropWalkProc: TYPE = GGProps.PropWalkProc;
ValFormat: TYPE = GGProps.ValFormat;
leftDelimiter: CHAR = 032C; -- Boxcar
rightDelimiter: CHAR = 007C; -- Bell
FileoutSlice: PUBLIC PROC [f: IO.STREAM, slice: Slice] = {
type: ATOM ← GGSliceOps.GetType[slice];
f.PutF["%g ", [rope[Atom.GetPName[type]]] ];
GGSliceOps.Fileout[slice, f];
IF NOT GGParent.IsParentType[type] THEN FileoutPList[f, slice];
};
FileoutPList: PROC [f: IO.STREAM, slice: Slice] = {
Property list format is:
( (key value) (key value) (key value) ... )
key is the printname of an ATOM,
value has two possible formats, distinguishable by their leading byte:
[count] countBytes
printable ascii string of any length
FileoutProp: PROC [key: ATOM, val: REF] RETURNS [done: BOOLFALSE] = {
r: Rope.ROPE;
vf: ValFormat;
[r, vf] ← GGProps.ToRope[key, val]; -- get val in rope form with format
SELECT TRUE FROM
Rope.Length[r]=0 => {
RETURN; -- NIL value. Don't write property.
};
vf=delimited => {
f.PutF["(%g ", [rope[Atom.GetPName[key]]] ]; -- write (keyName and SP
f.PutF["%g%g%g) ", [character[leftDelimiter]], [rope[r]], [character[rightDelimiter]] ];
};
vf=counted => {
c: INT ← Rope.Length[r];
f.PutF["(%g ", [rope[Atom.GetPName[key]]] ]; -- write (keyName and SP
f.PutF["[%g] %g) ", [integer[c]], [rope[r]] ];
write "[c]" then "SP" then c bytes from r then ")" then "SP".
};
ENDCASE => ERROR;
};
buttonClassName: ATOMNIL;
success: BOOLTRUE;
f.PutRope[" pList: ( "]; -- write property list label (pList: ) and starting leftParen
IF slice.props#NIL THEN [] ← GGProps.Walk[slice, GGSliceOps.NewParts[slice, NIL, slice], FileoutProp];
f.PutRope[")\n"] -- write closing rightParen of pList and CR
};
FileinSlice: PUBLIC PROC [f: IO.STREAM, version: REAL, router: FeedbackTypes.MsgRouter, camera: GGModelTypes. Camera, class: SliceClass ← NIL] RETURNS [slice: Slice] = {
className: Rope.ROPE ← "Traj"; -- needed for older files without classes
type: ATOM;
IF class=NIL THEN { -- read the class name
IF version >= 8705.14 THEN {
className ← GGParseIn.ReadWWord[f];
}
ELSE {
childCount: NAT;
GGParseIn.ReadWRope[f, "(class:"];
className ← GGParseIn.ReadWWord[f];
GGParseIn.ReadWRope[f, ")"];
GGParseIn.ReadWRope[f, "["];
childCount ← GGParseIn.ReadWNAT[f];
GGParseIn.ReadWRope[f, "]:"];
GGParseIn.ReadWRope[f, "Data:"];
};
};
type ← SliceClassFromRope[className];
class ← GGSlice.FetchSliceClass[type];
slice ← class.filein[f, version, router, camera];
IF version>=8906.16 AND slice # NIL AND NOT GGParent.IsParentType[GGSliceOps.GetType[slice]] THEN FileinPList[f, version, router, slice];
};
SliceClassFromRope: PROC [className: Rope.ROPE] RETURNS [type: ATOM] = {
RETURN[Atom.MakeAtom[className] ];
};
FileinPList: PROC [f: IO.STREAM, version: REAL, router: FeedbackTypes.MsgRouter, slice: Slice] = {
Property list format is:
( (key value) (key value) (key value) ... )
key is the printname of an ATOM,
value has two possible formats, distinguishable by their leading byte:
[count] countBytes
printable ascii string of any length
key: ATOM;
val: REF;
nextChar: CHAR;
keyName, valRope: Rope.ROPE;
success: BOOLTRUE;
GGParseIn.ReadWRope[f, "pList: ("]; -- read property list label pList: and starting leftParen
DO -- loop until end of property list
GGParseIn.ReadBlank[f]; -- advance to next property or end of pList
nextChar ← f.GetChar[];
IF nextChar=') THEN EXIT; -- reached end of pList
IF nextChar#'( THEN ERROR; -- do better than ERROR later
keyName ← GGParseIn.ReadWWord[f];
key ← Atom.MakeAtom[keyName];
nextChar ← f.GetChar[]; -- Can't use GGParseIn.ReadChar because it skips non-printing chars like leftDelimiter. This should read SP
nextChar ← f.GetChar[]; -- choices are leftDelimiter, [, or )
SELECT nextChar FROM
leftDelimiter => { -- read up to the right delimiter
StopProc: IO.BreakProc = {
RETURN[IF char=rightDelimiter THEN break ELSE other];
};
valRope ← f.GetTokenRope[StopProc].token;
nextChar ← f.GetChar[]; -- should be the rightDelimiter. Can't use GGParseIn.ReadChar because it skips non-printing chars
GGParseIn.ReadChar[f, ')]; -- SyntaxError raised if fails
};
'[ => { -- read byte count, then SP, then read "count" bytes
c: INT ← f.GetInt[];
GGParseIn.ReadWRope[f, "] " ]; -- better be a bracket and a single space
valRope ← f.GetRope[len: c, demand: TRUE];
GGParseIn.ReadChar[f, ')]; -- SyntaxError raised if fails
};
') => valRope ← NIL; -- already read the rightParen
ENDCASE => ERROR; -- do better than ERROR later
val ← IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope];
GGProps.Put[slice, NIL, key, val];
ENDLOOP;
};
PartsInBoundBox: PUBLIC PROC [slice: Slice, box: BoundBox] RETURNS [inParts: SliceDescriptor] = {
Returns a descriptor of those parts of slice that are within box.
SIGNAL Feedback.Problem[msg: "Not yet implemented"];
};
WithinBoundBox: PUBLIC PROC [slice: Slice, box: BoundBox] RETURNS [in: BOOL] = {
Returns TRUE if all parts of slice are within box.
epsilon: REAL = 0.072; -- 0.001 inches
Within: PROC [test, bound: BoundBox] RETURNS [BOOL] = {
RETURN [test.hiX <= bound.hiX+epsilon AND test.loX >= bound.loX-epsilon AND test.hiY <= bound.hiY+epsilon AND test.loY >= bound.loY-epsilon];
};
sliceBox: BoundBox ← slice.class.getTightBox[slice, NIL];
in ← Within[sliceBox, box];
};
IsOpen: PUBLIC PROC [slice: Slice] RETURNS [isOpen: BOOLFALSE] = {
IF GetType[slice] # $Traj THEN RETURN[FALSE];
RETURN[GGTraj.GetTrajRole[slice] = open];
};
GenericPropsUndo: PROC [op: ATOM, slice: Slice, parts: SliceParts, val: REF, history: HistoryEvent] = {
IF history#NIL THEN {
changeRef: REF Change.changingprops ← NEW[Change.changingprops ← [changingprops[op, slice, parts, val] ] ];
Note[history, UndoProps, changeRef];
};
};
Note: PUBLIC PROC [event: HistoryEvent, historyProc: HistoryProc, historyData: REF Change] = {
IF event = NIL THEN RETURN;
IF debugHalt THEN SIGNAL DebugHalt;
Refuse to append any more subevents to a capture type subevent
IF noteNoMore AND event.subevents#NIL AND event.subevents.first#NIL AND event.subevents.first.historyData.kind=capture THEN RETURN;
event.subevents ← CONS[NEW[SubEventObj ←
[historyProc, historyData]], event.subevents];
};
UndoProps: PROC [historyData: REF Change, currentEvent: HistoryEvent] = {
GGModelTypes.HistoryProc
This proc is called by the Undo mechanism. It is called with a history event (currentEvent) which it passes on to record its undo operations, making undo an event (and thus undoable) in itself.
propsData: REF Change.changingprops ← NARROW[historyData];
slice: Slice ← propsData.slice;
parts: SliceParts ← propsData.parts;
IF slice.class#NIL THEN {
SELECT propsData.op FROM
$SetStrokeWidth => {
oldValue: REF REALNARROW[propsData.oldValue];
[] ← GGSliceOps.SetStrokeWidth[slice, FixBogusParts[slice, parts], oldValue^, currentEvent]; -- restore old value
};
$SetStrokeEnd => {
oldValue: REF StrokeEndRec ← NARROW[propsData.oldValue];
GGSliceOps.SetStrokeEnd[slice, FixBogusParts[slice, parts], oldValue.end, currentEvent];
};
$SetStrokeJoint => {
oldValue: REF StrokeJointRec ← NARROW[propsData.oldValue];
GGSliceOps.SetStrokeJoint[slice, FixBogusParts[slice, parts], oldValue.joint, currentEvent];
};
$SetStrokeColor => {
oldValue: Imager.Color ← NARROW[propsData.oldValue];
GGSliceOps.SetStrokeColor[slice, FixBogusParts[slice, parts], oldValue, $Set, currentEvent];
};
$SetFillColor => {
oldValue: Imager.Color ← NARROW[propsData.oldValue];
GGSliceOps.SetFillColor[slice, parts, oldValue, $Set, currentEvent];
};
$SetArrows => {
oldValue: REF ArrowRec ← NARROW[propsData.oldValue];
GGSliceOps.SetArrows[slice, FixBogusParts[slice, parts], oldValue.leftDown, oldValue.rightUp, currentEvent];
};
$SetDashed => {
oldValue: REF DashedRec ← NARROW[propsData.oldValue];
GGSliceOps.SetDashed[slice, FixBogusParts[slice, parts], oldValue.dashed, oldValue.pattern, oldValue.offset, oldValue.length, currentEvent];
};
$SetOrientation => {
oldValue: REF OrientationRec ← NARROW[propsData.oldValue];
[] ← GGSliceOps.SetOrientation[slice, FixBogusParts[slice, parts], oldValue.or, currentEvent];
};
$Transform => {
oldValue: REF TransformRec ← NARROW[propsData.oldValue];
transform: Imager.Transformation ← ImagerTransformation.Invert[oldValue.transform]; -- lazily evaluate the inverse
[] ← GGSliceOps.Transform[slice, FixBogusParts[slice, parts], transform, oldValue.editConstraints, currentEvent];
};
ENDCASE;
};
};
FixBogusParts: PROC [slice: Slice, bogusParts: SliceParts] RETURNS [goodParts: SliceParts]= {
goodParts ← bogusParts; -- gonna fix it in place
IF forgetBogusParts THEN RETURN;
ERROR;
<<IF GGSliceOps.GetType[slice] = $Outline THEN {
outlineData: GGOutline.OutlineData ← NARROW[slice.data];
outlineChildren: LIST OF Slice ← outlineData.children;
outlineParts: LIST OF SliceDescriptor ← NARROW[bogusParts, GGOutline.OutlineParts].descriptors;
lastOutlineParts: LIST OF SliceDescriptor ← NIL;
FOR childList: LIST OF Slice ← outlineChildren, childList.rest UNTIL childList=NIL DO
IF outlineParts#NIL AND outlineParts.first#NIL THEN outlineParts.first.slice ← childList.first;
lastOutlineParts ← outlineParts;
outlineParts ← outlineParts.rest;
ENDLOOP;
IF lastOutlineParts#NIL THEN lastOutlineParts.rest ← NIL;
};>>
};
forgetBogusParts: BOOLTRUE;
Routines that operate on several classes, but aren't class procs
IsDescriptorOfEnd: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [BOOL] = {
jointNum: NAT;
partType: TrajPartType;
success: BOOL ← FALSE;
IF sliceD = NIL THEN RETURN[FALSE];
SELECT GGSliceOps.GetType[sliceD.slice] FROM
$Cluster => {
trajD: SliceDescriptor;
trajD ← GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, leaf, $Traj];
RETURN[IsDescriptorOfEnd[trajD]];
};
$Outline => {
trajD: SliceDescriptor;
[success, partType, trajD, ----, jointNum] ← GGOutline.UnpackSimpleDescriptor[sliceD];
RETURN[IF (NOT success OR partType#joint) THEN FALSE ELSE GGTraj.IsEndJoint[trajD.slice, jointNum]];
};
$Traj => {
[success, partType, ----, ----, jointNum] ← GGTraj.UnpackSimpleDescriptor[sliceD.slice, sliceD.parts];
RETURN[IF (NOT success OR partType#joint) THEN FALSE ELSE GGTraj.IsEndJoint[sliceD.slice, jointNum]];
};
ENDCASE => RETURN[FALSE];
};
UnpackJoint: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [success: BOOL, trajD: SliceDescriptor, jointNum: INT] = {
SELECT GGSliceOps.GetType[sliceD.slice] FROM
$Cluster => {
sliceD ← GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, leaf, $Outline];
[success, ----, trajD, ----, jointNum] ← GGOutline.UnpackSimpleDescriptor[sliceD];
};
$Outline => [success, ----, trajD, ----, jointNum] ← GGOutline.UnpackSimpleDescriptor[sliceD];
ENDCASE => RETURN [FALSE, NIL, -1];
};
UnpackSegment: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [success: BOOL, hitType: TrajPartType, traj: Slice, seg: Segment, segNum: NAT ← 999] = {
Used by GGEventImplC.AddControlPoint.
trajD: SliceDescriptor;
SELECT GGSliceOps.GetType[sliceD.slice] FROM
$Cluster => {
sliceD ← GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, leaf, $Outline];
[success, hitType, trajD, ----, ----, ----, ----, seg, segNum] ← GGOutline.UnpackSimpleDescriptor[sliceD];
traj ← trajD.slice;
};
$Outline => {
[success, hitType, trajD, ----, ----, ----, ----, seg, segNum] ← GGOutline.UnpackSimpleDescriptor[sliceD];
traj ← trajD.slice;
};
$Traj => {
[success, hitType, ----, ----, ----, ----, ----, seg, segNum] ← GGTraj.UnpackSimpleDescriptor[sliceD.slice, sliceD.parts];
};
ENDCASE => RETURN [FALSE, none, NIL, NIL, 999];
};
END.