GGOutlineImplA.mesa
Contents: Implementations of the Parent Meta-Class and the Outline class.
Copyright Ó 1986, 1987, 1988, 1989 by Xerox Corporation. All rights reserved.
Pier, August 15, 1991 9:36 pm PDT
Eisenman, October 2, 1987 9:22:03 pm PDT
Bier, June 1, 1992 3:38 pm PDT
Doug Wyatt, December 18, 1989 2:48:02 pm PST
DIRECTORY
Feedback, FeedbackTypes, FS, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGParseIn, GGParseOut, GGScene, GGSegmentTypes, GGSequence, GGShapes, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, ImagerPath, ImagerTransformation, IO, Rope, TextNode, TiogaImager;
GGOutlineImplA:
CEDAR
PROGRAM
IMPORTS Feedback, FS, GGBoundBox, GGCoreOps, GGOutline, GGParent, GGParseIn, GGParseOut, GGScene, GGSequence, GGShapes, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, IO, Rope
EXPORTS GGSlice, GGOutline, GGParent = BEGIN
AlignBag: TYPE = GGInterfaceTypes.AlignBag;
BitVector: TYPE = GGBasicTypes.BitVector;
BoundBox: TYPE = GGCoreTypes.BoundBox;
Camera: TYPE = GGModelTypes.Camera;
Circle: TYPE = GGBasicTypes.Circle;
Color: TYPE = Imager.Color;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
DefaultData: TYPE = GGModelTypes.DefaultData;
EditConstraints: TYPE = GGModelTypes.EditConstraints;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
MsgRouter: TYPE = FeedbackTypes.MsgRouter;
GGData: TYPE = GGInterfaceTypes.GGData;
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
HitType: TYPE = GGModelTypes.TrajPartType;
Joint: TYPE = GGSegmentTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Line: TYPE = GGCoreTypes.Line;
OutlineData: TYPE = GGOutline.OutlineData;
OutlineDataObj: TYPE = GGOutline.OutlineDataObj;
OutlineHitData: TYPE = GGOutline.OutlineHitData;
OutlineHitDataObj: TYPE = GGOutline.OutlineHitDataObj;
OutlineParts: TYPE = GGOutline.OutlineParts;
OutlinePartsObj: TYPE = GGOutline.OutlinePartsObj;
Point: TYPE = GGBasicTypes.Point;
PointAndDone: TYPE = GGModelTypes.PointAndDone;
PointGenerator: TYPE = GGModelTypes.PointGenerator;
PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj;
PointPairAndDone: TYPE = GGModelTypes.PointPairAndDone;
PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator;
PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj;
PointWalkProc: TYPE = GGModelTypes.PointWalkProc;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
SelectMode: TYPE = GGModelTypes.SelectMode;
SequenceGenerator: TYPE = GGSequence.SequenceGenerator;
SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal;
Slice: TYPE = GGModelTypes.Slice;
SliceClass: TYPE = GGModelTypes.SliceClass;
SliceClassObj: TYPE = GGModelTypes.SliceClassObj;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceObj: TYPE = GGModelTypes.SliceObj;
SliceParts: TYPE = GGModelTypes.SliceParts;
StrokeEnd: TYPE = Imager.StrokeEnd;
StrokeJoint: TYPE = Imager.StrokeJoint;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajParts: TYPE = GGModelTypes.TrajParts;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
Transformation: TYPE = ImagerTransformation.Transformation;
TriggerBag: TYPE = GGInterfaceTypes.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
WalkProc: TYPE = GGModelTypes.WalkProc;
MoveToProc: TYPE = ImagerPath.MoveToProc;
LineToProc: TYPE = ImagerPath.LineToProc;
CurveToProc: TYPE = ImagerPath.CurveToProc;
ConicToProc: TYPE = ImagerPath.ConicToProc;
ArcToProc: TYPE = ImagerPath.ArcToProc;
ClusterData: TYPE = REF ClusterDataObj;
ClusterDataObj: TYPE = GGSlice.ClusterDataObj;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
BuildOutlineSliceClass:
PUBLIC
PROC []
RETURNS [class: SliceClass] = {
class ←
NEW[SliceClassObj ← [
type: $Outline,
unlink: GGParent.Unlink,
Fundamentals
getBoundBox: GGParent.GetBoundBox,
getTransformedBoundBox: GGSlice.GenericTransformedBoundBox,
getTightBox: GGParent.GetTightBox,
copy: OutlineCopy,
restore: GGParent.Restore,
Drawing
buildPath: OutlineBuildPath,
drawBorder: OutlineDrawBorder,
drawParts: OutlineDrawParts,
drawTransform: OutlineDrawTransform,
drawSelectionFeedback: OutlineDrawSelectionFeedback,
drawAttractorFeedback: GGParent.DrawAttractorFeedback,
attractorFeedbackBoundBox: GGParent.AttractorFeedbackBoundBox,
saveSelections: GGParent.SaveSelections,
remakeSelections: GGParent.RemakeSelections,
Transforming
transform: GGParent.Transform,
Textual Description
describe: OutlineDescribe,
describeHit: GGParent.DescribeHit,
fileout: OutlineFileout,
filein: OutlineFilein,
Parts
isEmptyParts: GGParent.IsEmptyParts,
isCompleteParts: GGParent.IsCompleteParts,
newParts: GGParent.NewParts,
unionParts: GGParent.UnionParts,
differenceParts: GGParent.DifferenceParts,
movingParts: OutlineMovingParts,
augmentParts: GGParent.AugmentParts,
alterParts: GGParent.AlterParts,
setSelectedFields: GGParent.SetSelectedFields,
Part Generators
pointsInDescriptor: GGParent.PointsInDescriptor,
walkPointsInDescriptor: GGParent.WalkPointsInDescriptor,
pointPairsInDescriptor: GGParent.PointPairsInDescriptor,
segmentsInDescriptor: GGParent.SegmentsInDescriptor,
walkSegments: GGParent.WalkSegments,
nextPoint: GGParent.NextPoint,
nextPointPair: GGParent.NextPointPair,
nextSegment: GGParent.NextSegment
<<,
Hit Testing
Hit Testing moved to GGOutlineImplB
closestPoint: GGParent.ClosestPoint,
closestJointToHitData: GGParent.ClosestJointToHitData,
closestPointAndTangent: GGSlice.NoOpClosestPointAndTangent,
closestSegment: GGParent.ClosestSegment,
lineIntersection: GGParent.LineIntersection,
circleIntersection: GGParent.CircleIntersection,
hitDataAsSimpleCurve: GGParent.HitDataAsSimpleCurve,
Style
Style moved to GGOutlineImplB
setDefaults: GGParent.SetDefaults,
setStrokeWidth: GGParent.SetStrokeWidth,
getStrokeWidth: GGParent.GetStrokeWidth,
setStrokeEnd: GGParent.SetStrokeEnd,
getStrokeEnd: GGParent.GetStrokeEnd,
setStrokeJoint: GGParent.SetStrokeJoint,
getStrokeJoint: GGParent.GetStrokeJoint,
setStrokeColor: GGParent.SetStrokeColor,
getStrokeColor: GGParent.GetStrokeColor,
setFillColor: GGParent.SetFillColor,
getFillColor: OutlineGetFillColor,
setArrows: GGSlice.NoOpSetArrows,
getArrows: GGSlice.NoOpGetArrows,
setDashed: GGParent.SetDashed,
getDashed: GGParent.GetDashed
>>
]];
GGSlice.BuildMoreOutlineSliceClass[class];
};
BuildClusterSliceClass:
PUBLIC
PROC []
RETURNS [class: SliceClass] = {
class ←
NEW[SliceClassObj ← [
type: $Cluster,
unlink: GGParent.Unlink,
Fundamentals
getBoundBox: GGParent.GetBoundBox,
getTransformedBoundBox: GGSlice.GenericTransformedBoundBox,
getTightBox: GGParent.GetTightBox,
copy: ClusterCopy,
restore: GGParent.Restore,
Drawing
buildPath: GGSlice.NoOpBuildPath,
drawBorder: GGSlice.NoOpDrawBorder,
drawParts: GGParent.DrawParts,
drawTransform: GGParent.DrawTransform,
drawSelectionFeedback: ClusterDrawSelectionFeedback,
drawAttractorFeedback: DrawAttractorFeedback,
attractorFeedbackBoundBox: AttractorFeedbackBoundBox,
saveSelections: GGParent.SaveSelections,
remakeSelections: GGParent.RemakeSelections,
Transforming
transform: GGParent.Transform,
Textual Description
describe: ClusterDescribe,
describeHit: GGParent.DescribeHit,
fileout: ClusterFileout,
filein: ClusterFilein,
Parts
isEmptyParts: GGParent.IsEmptyParts,
isCompleteParts: GGParent.IsCompleteParts,
newParts: GGParent.NewParts,
unionParts: GGParent.UnionParts,
differenceParts: GGParent.DifferenceParts,
movingParts: ClusterMovingParts,
augmentParts: GGParent.AugmentParts,
alterParts: GGParent.AlterParts,
setSelectedFields: GGParent.SetSelectedFields,
Part Generators
pointsInDescriptor: GGParent.PointsInDescriptor,
walkPointsInDescriptor: GGParent.WalkPointsInDescriptor,
pointPairsInDescriptor: GGParent.PointPairsInDescriptor,
segmentsInDescriptor: GGParent.SegmentsInDescriptor,
walkSegments: GGParent.WalkSegments,
nextPoint: GGParent.NextPoint,
nextPointPair: GGParent.NextPointPair,
nextSegment: GGParent.NextSegment
<<,
Hit Testing
Hit Testing moved to GGOutlineImplB
closestPoint: GGParent.ClosestPoint,
closestJointToHitData: GGParent.ClosestJointToHitData,
closestPointAndTangent: GGSlice.NoOpClosestPointAndTangent,
closestSegment: GGParent.ClosestSegment,
filledPathsUnderPoint: ClusterFilledPathsUnderPoint,
lineIntersection: GGParent.LineIntersection,
circleIntersection: GGParent.CircleIntersection,
hitDataAsSimpleCurve: GGParent.HitDataAsSimpleCurve,
Style
Style moved to GGOutlineImplB
setDefaults: GGParent.SetDefaults,
setStrokeWidth: GGParent.SetStrokeWidth,
getStrokeWidth: GGParent.GetStrokeWidth,
setStrokeEnd: GGParent.SetStrokeEnd,
getStrokeEnd: GGParent.GetStrokeEnd,
setStrokeJoint: GGParent.SetStrokeJoint,
getStrokeJoint: GGParent.GetStrokeJoint,
setStrokeColor: GGParent.SetStrokeColor,
getStrokeColor: GGParent.GetStrokeColor,
setFillColor: GGParent.SetFillColor,
getFillColor: GGParent.GetFillColor,
setArrows: GGSlice.NoOpSetArrows,
getArrows: GGSlice.NoOpGetArrows,
setDashed: GGParent.SetDashed,
getDashed: GGParent.GetDashed
>>
]];
GGSlice.BuildMoreClusterSliceClass[class];
};
OutlineFromChildList:
PROC [childList:
LIST
OF Slice, fillColor: Color, oddWrap:
BOOL ←
TRUE, fillText: TextNode.Ref, screenStyle:
BOOL, version:
REAL]
RETURNS [outline: Slice] = {
data: OutlineData ← NEW[OutlineDataObj ← [oddWrap: oddWrap, fillColor: fillColor, children: childList] ];
outline ←
NEW[SliceObj ← [
class: GGSlice.FetchSliceClass[$Outline],
parent: NIL,
data: data,
selectedInFull: [FALSE, FALSE, FALSE],
tightBox: GGBoundBox.NullBoundBox[],
boundBox: GGBoundBox.NullBoundBox[],
boxValid: FALSE]];
FOR tL:
LIST
OF Slice ← childList, tL.rest
UNTIL tL=
NIL
DO
tL.first.parent ← outline;
ENDLOOP;
outline.nullDescriptor ← GGSlice.DescriptorFromParts[outline, NIL];
IF version<8802.04
THEN {
-- have to calculate the forward bits from scratch. In older versions, trajectory direction was not important.
[] ← GGSliceOps.SetOrientation[childList.first, NIL, ccw, NIL]; -- first child is ccw by convention
FOR holeList:
LIST
OF Slice ← childList.rest, holeList.rest
UNTIL holeList=
NIL
DO
[] ← GGSliceOps.SetOrientation[holeList.first, NIL, cw, NIL]; -- other children are cw by convention
ENDLOOP;
};
GGOutline.SetFillText[outline, fillText, screenStyle, NIL];
};
ClusterFromChildList:
PROC [childList:
LIST
OF Slice, frozen:
BOOL, version:
REAL]
RETURNS [parent: Slice] = {
data: OutlineData ← NEW[OutlineDataObj ← [oddWrap: FALSE, fillColor: NIL, children: childList, subclassData: NEW[ClusterDataObj ← [frozen: frozen]] ]];
parent ←
NEW[SliceObj ← [
class: GGSlice.FetchSliceClass[$Cluster],
parent: NIL,
data: data,
selectedInFull: [FALSE, FALSE, FALSE],
tightBox: GGBoundBox.NullBoundBox[],
boundBox: GGBoundBox.NullBoundBox[],
boxValid: FALSE]];
FOR tL:
LIST
OF Slice ← childList, tL.rest
UNTIL tL=
NIL
DO
tL.first.parent ← parent;
ENDLOOP;
parent.nullDescriptor ← GGSlice.DescriptorFromParts[parent, NIL];
};
Textual Description
OutlineDescribe:
PROC [sliceD: SliceDescriptor]
RETURNS [rope: Rope.
ROPE] = {
RETURN[GGParent.Describe[sliceD, "outline"]];
};
ClusterDescribe:
PROC [sliceD: SliceDescriptor]
RETURNS [rope: Rope.
ROPE] = {
RETURN[GGParent.Describe[sliceD, "cluster"]];
};
The Cluster Class
Fundamentals
ClusterCopy:
PROC [slice: Slice, parts: SliceParts ←
NIL]
RETURNS [copy:
LIST
OF Slice] = {
DoCreateCluster:
PROC [newSlice: Slice]
RETURNS [parent: Slice] = {
newData: OutlineData;
parent ← GGSlice.CreateCluster[];
GGSlice.AddChildToCluster[parent, newSlice, -1];
newData ← NARROW[parent.data];
newData.subclassData ← NEW[ClusterDataObj ← [frozen: oldClusterData.frozen]];
};
outlineParts: OutlineParts ← NARROW[parts];
oldData: OutlineData ← NARROW[slice.data];
oldClusterData: ClusterData ← NARROW[oldData.subclassData];
copy ← Copy[slice, parts, DoCreateCluster];
};
Drawing
ClusterDrawSelectionFeedback:
PUBLIC PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick:
BOOL] = {
slowNormal, slowHot, completeNormal, completeHot: BOOL ← FALSE;
normalOutlineParts: OutlineParts ← NARROW[selectedParts];
hotOutlineParts: OutlineParts ← NARROW[hotParts];
boxDrawn: BOOL ← FALSE;
IF caretIsMoving OR dragInProgress THEN RETURN;
IF selectedParts=NIL AND hotParts=NIL THEN RETURN;
completeNormal ← normalOutlineParts#NIL AND IsComplete[normalOutlineParts];
completeHot ← hotOutlineParts#NIL AND IsComplete[hotOutlineParts];
slowNormal ← selectedParts#NIL AND NOT (quick AND completeNormal);
slowHot ← hotParts#NIL AND NOT (quick AND completeHot);
GGParent.DrawSelectionFeedback[slice, selectedParts, hotParts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
Draw the big boxes for speed, if you can.
IF
NOT slowNormal
AND completeNormal
THEN {
boundBox: BoundBox ← GetBoundBox[slice, NIL];
GGShapes.DrawBoundBox[dc, boundBox, camera.cpScale];
GGShapes.DrawQuickSelectedJoint[dc, [boundBox.loX, boundBox.loY], normal, camera.cpScale];
boxDrawn ← TRUE;
};
IF
NOT slowHot
AND completeHot
THEN {
boundBox: BoundBox ← GetBoundBox[slice, NIL];
GGShapes.DrawBoundBox[dc, boundBox, camera.cpScale];
GGShapes.DrawQuickSelectedJoint[dc, [boundBox.loX, boundBox.loY], hot, camera.cpScale];
boxDrawn ← TRUE;
};
IF
NOT boxDrawn
AND (
NOT IsEmpty[normalOutlineParts]
OR
NOT IsEmpty[hotOutlineParts])
THEN {
boundBox: BoundBox ← GetBoundBox[slice, NIL];
GGShapes.DrawBoundBox[dc, boundBox, camera.cpScale];
};
};
Textual Description
ClusterFileout:
PUBLIC PROC [slice: Slice, f:
IO.
STREAM] = {
outlineData: OutlineData ← NARROW[slice.data];
clusterData: ClusterData ← NARROW[outlineData.subclassData];
f.PutF["frozen: "]; GGParseOut.WriteBool[f, clusterData.frozen];
f.PutChar[IO.CR];
GGParent.Fileout[slice, f];
};
ClusterFilein:
PUBLIC PROC [f:
IO.
STREAM, version:
REAL, router: MsgRouter, camera: Camera]
RETURNS [slice: Slice] = {
frozen, good: BOOL ← TRUE;
childList: LIST OF Slice;
GGParseIn.ReadWRope[f, "frozen:"];
[frozen, good] ← GGParseIn.ReadBool[f, version];
childList ← GGParent.Filein[f, version, router, camera];
IF childList#NIL THEN slice ← ClusterFromChildList[childList, frozen, version];
};
Parts
ClusterMovingParts:
PROC [slice: Slice, selectedParts: SliceParts, editConstraints: EditConstraints, bezierDrag: GGInterfaceTypes.BezierDragRecord]
RETURNS [background, overlay, rubber, drag: SliceDescriptor] = {
outlineData: OutlineData ← NARROW[slice.data];
RETURN GGParent.MovingParts[slice, selectedParts, TRUE, editConstraints, bezierDrag];
};
The Parent Meta-Class
Unlink:
PUBLIC PROC [slice: Slice] = {
IF slice.data#
NIL
THEN {
outlineData: OutlineData ← NARROW[slice.data];
GGSlice.UnlinkSlice[slice];
FOR childList:
LIST
OF Slice ← outlineData.children, childList.rest
UNTIL childList=
NIL
DO
IF childList.first.data#NIL THEN GGSliceOps.Unlink[childList.first]; -- unlink circular structures. IMPORTANT
ENDLOOP;
outlineData.children ← NIL;
};
};
GetBoundBox:
PUBLIC PROC [slice: Slice, parts: SliceParts]
RETURNS [box: BoundBox] = {
GGModelTypes.SliceBoundBoxProc
IF slice.boxValid AND parts=NIL THEN RETURN[slice.boundBox] -- fast case
ELSE {
outlineParts: OutlineParts ← NARROW[parts];
outlineData: OutlineData ← NARROW[slice.data];
boundBoxList: LIST OF BoundBox;
thisBoundBox: BoundBox;
IF parts =
NIL
THEN
FOR childList:
LIST
OF Slice ← outlineData.children, childList.rest
UNTIL childList =
NIL
DO
bound box of all the children
thisBoundBox ← GGSliceOps.GetBoundBox[childList.first];
boundBoxList ← CONS[thisBoundBox, boundBoxList];
ENDLOOP
ELSE
FOR list:
LIST
OF SliceDescriptor ← outlineParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first#
NIL
THEN {
thisBoundBox ← GGSliceOps.GetBoundBox[list.first.slice, list.first.parts];
boundBoxList ← CONS[thisBoundBox, boundBoxList];
};
ENDLOOP;
box ← GGBoundBox.BoundBoxOfBoxes[boundBoxList];
IF parts=
NIL
THEN {
-- set up cache for fast case next time around
GGSlice.KillBoundBoxOnly[slice.parent]; -- invalidate ancestor caches
slice.boundBox ← box;
slice.boxValid ← TRUE;
};
};
};
GetTightBox:
PUBLIC PROC [slice: Slice, parts: SliceParts]
RETURNS [box: BoundBox] = {
GGModelTypes.SliceBoundBoxProc
IF slice.tightBoxValid AND parts=NIL THEN RETURN[slice.tightBox] -- fast case
ELSE {
outlineParts: OutlineParts ← NARROW[parts];
outlineData: OutlineData ← NARROW[slice.data];
boundBoxList: LIST OF BoundBox;
thisBoundBox: BoundBox;
IF parts =
NIL
THEN
FOR childList:
LIST
OF Slice ← outlineData.children, childList.rest
UNTIL childList =
NIL
DO
bound box of all the children
thisBoundBox ← GGSliceOps.GetTightBox[childList.first];
boundBoxList ← CONS[thisBoundBox, boundBoxList];
ENDLOOP
ELSE
FOR list:
LIST
OF SliceDescriptor ← outlineParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first#
NIL
THEN {
thisBoundBox ← GGSliceOps.GetTightBox[list.first.slice, list.first.parts];
boundBoxList ← CONS[thisBoundBox, boundBoxList];
};
ENDLOOP;
box ← GGBoundBox.BoundBoxOfBoxes[boundBoxList];
IF parts=
NIL
THEN {
-- set up cache for fast case next time around
GGSlice.KillTightBoxOnly[slice.parent]; -- invalidate ancestor caches
slice.tightBox ← box;
slice.tightBoxValid ← TRUE;
};
};
};
OutlineCopy:
PROC [slice: Slice, parts: SliceParts ←
NIL]
RETURNS [copy:
LIST
OF Slice] = {
DoCreateOutline:
PROC [newSlice: Slice]
RETURNS [parent: Slice] = {
newData: OutlineData;
parent ← GGOutline.CreateOutline[newSlice, GGCoreOps.CopyColor[oldData.fillColor]];
newData ← NARROW[parent.data];
newData.oddWrap ← oldData.oddWrap;
newData.fillText ← oldData.fillText;
newData.screenStyle ← oldData.screenStyle;
};
oldData: OutlineData ← NARROW[slice.data];
oldParts: OutlineParts ← NARROW[parts];
copy ← Copy[slice, parts, DoCreateOutline];
IF parts=
NIL
OR IsComplete[oldParts]
THEN {
-- if the whole thing is selected
GGOutline.SetFillText[copy.first, oldData.fillText, oldData.screenStyle, NIL];
};
};
Copy:
PUBLIC
PROC [slice: Slice, parts: SliceParts ←
NIL, create:
PROC [newSlice: Slice]
RETURNS [parent: Slice]]
RETURNS [copy:
LIST
OF Slice] = {
This PROC assumes that only Trajs can be copied in part. All other classes are copied in entirety.
newParent, newSlice: Slice;
oldData, newData: OutlineData;
ptr, holeList: LIST OF Slice;
outlineParts: OutlineParts ← NARROW[parts];
IF parts=
NIL
OR IsComplete[outlineParts]
THEN {
-- if the whole thing is selected
AddChild:
PROC [child: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
IF newSlice =
NIL
THEN {
newSlice ← GGSliceOps.Copy[child].first;
Since outlineParts represents a complete cluster or outline, copying a given child produces a single slice.
newParent ← create[newSlice];
Creates the parent and adds the child to its list of children.
newData ← NARROW[newParent.data];
}
ELSE {
newSlice ← GGSliceOps.Copy[child].first;
Since outlineParts represents a complete cluster or outline, copying a given child produces a single slice.
[holeList, ptr] ← GGUtility.AddSlice[newSlice, holeList, ptr];
newSlice.parent ← newParent;
};
};
oldData ← NARROW[slice.data];
[holeList, ptr] ← GGUtility.StartSliceList[];
[] ← GGParent.WalkChildren[slice, first, AddChild];
IF holeList#
NIL
THEN newData.children ← GGUtility.AppendSliceList[newData.children, holeList];
Add the rest of the children to the list.
copy ← LIST[newParent];
}
ELSE {
-- each run of each selected child becomes a new slice.
CopyWholeTraj:
PROC [child: Slice, enclose:
BOOL ←
FALSE] = {
thisFillColor: Imager.Color ← GGCoreOps.CopyColor[GGSliceOps.GetFillColor[slice, NIL].color];
newChild: Slice ← GGSliceOps.Copy[child].first;
IF enclose THEN newChild ← GGOutline.CreateOutline[newChild, thisFillColor];
copy ← GGUtility.AppendSliceList[copy, LIST[newChild]];
};
VisitChild:
PROC [childD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
nextChild: Slice ← childD.slice;
childParts: SliceParts ← childD.parts;
IF GGSliceOps.GetType[nextChild]#$Traj
THEN {
copy ← GGUtility.AppendSliceList[copy, GGSliceOps.Copy[nextChild, childParts]];
}
ELSE
{
nextChildParts: TrajParts ← NARROW[childParts];
IF GGSequence.IsComplete[nextChildParts] THEN CopyWholeTraj[nextChild, TRUE]
ELSE {
nextRuns: GGSequence.SequenceGenerator ← GGSequence.RunsInSequence[nextChildParts].seqGen;
FOR nextSeq: TrajParts ← GGSequence.NextSequence[nextRuns], GGSequence.NextSequence[nextRuns]
UNTIL nextSeq=
NIL
DO
IF
NOT GGSequence.ContainsSomeSegment[nextSeq]
THEN {
Feedback.PutFByName[$Gargoyle, oneLiner, $Complaint, ". . . Cannot Fetch Joints or CPs by themselves"];
LOOP;
};
newSlice ← GGTraj.CopyTrajFromRun[nextChild, nextSeq];
newSlice ← GGOutline.CreateOutline[newSlice, fillColor];
copy ← GGUtility.AppendSliceList[copy, LIST[newSlice]];
ENDLOOP;
};
};
};
fillColor: Imager.Color ← GGCoreOps.CopyColor[GGSliceOps.GetFillColor[slice, NIL].color];
[] ← GGParent.WalkIncludedChildren[slice, parts, first, VisitChild];
};
};
Restore:
PUBLIC PROC [from: Slice, to: Slice]
= {
GGModelTypes.SliceRestoreProc
from and to must be nearly identically structured slices, probably because from was made as a copy of to. Restore the contents of "to", getting all non-REF values from "from". Good luck.
IF to=NIL OR from=NIL THEN ERROR;
IF to.class#from.class THEN ERROR;
<<IF to.class.type#$Outline THEN ERROR;
IF to.parent#NIL OR from.parent#NIL THEN ERROR;>>
BEGIN
fromSize: INT ← 0;
fromData: OutlineData ← NARROW[from.data];
fromChildren: LIST OF Slice ← fromData.children;
toSize: INT ← 0;
toData: OutlineData ← NARROW[to.data];
toChildren: LIST OF Slice ← toData.children;
lastToChildren: LIST OF Slice ← NIL;
toData.oddWrap ← fromData.oddWrap;
toData.fillColor ← fromData.fillColor;
toData.fillText ← fromData.fillText;
toData.screenStyle ← fromData.screenStyle;
FOR fromList:
LIST
OF Slice ← fromChildren, fromList.rest
UNTIL fromList
=NIL DO
fromSize ← fromSize+1;
ENDLOOP;
FOR toList:
LIST
OF Slice ← toChildren, toList.rest
UNTIL toList
=NIL DO
toSize ← toSize+1;
ENDLOOP;
IF GGSlice.
copyRestore
THEN
IF fromSize>=toSize
THEN {
UNTIL toChildren=
NIL
DO
GGSliceOps.Restore[from: fromChildren.first, to: toChildren.first];
toChildren.first.parent ← to; -- must be restored in case it was reused at top level
lastToChildren ← toChildren;
fromChildren ← fromChildren.rest;
toChildren ← toChildren.rest;
ENDLOOP;
IF lastToChildren#
NIL
THEN {
-- put back the remaining "from" children.
CAN THIS LEAD TO OBSOLETE DESCRIPTORS??
lastToChildren.rest ← fromChildren;
-- hook up the lists
Reparent !!
FOR adoptees:
LIST
OF Slice ← fromChildren, adoptees.rest
UNTIL adoptees=
NIL
DO
adoptees.first.parent ← to;
ENDLOOP;
}
}
ELSE {
-- fromSize<toSize
UNTIL fromChildren=
NIL
DO
GGSliceOps.Restore[from: fromChildren.first, to: toChildren.first];
toChildren.first.parent ← to; -- must be restored in case it was reused at top level
lastToChildren ← toChildren;
fromChildren ← fromChildren.rest;
toChildren ← toChildren.rest;
ENDLOOP;
IF lastToChildren#NIL THEN lastToChildren.rest ← NIL; -- cut off the remaining "to" children. CAN THIS LEAD TO OBSOLETE DESCRIPTORS??
}
ELSE {
Reparent !!
FOR adoptees:
LIST
OF Slice ← fromData.children, adoptees.rest
UNTIL adoptees=
NIL
DO
adoptees.first.parent ← to;
ENDLOOP;
toData.children ← fromData.children; -- swing the pointer
};
to.selectedInFull ← from.selectedInFull; -- RECORD of BOOL
to.normalSelectedParts ← NIL; -- caller must reselect
to.hotSelectedParts ← NIL; -- caller must reselect
to.activeSelectedParts ← NIL; -- caller must reselect
to.matchSelectedParts ← NIL; -- caller must reselect
to.nullDescriptor is always valid
to.fullDescriptor is unused
to.tightBox^ ← from.tightBox^;
to.tightBoxValid ← from.tightBoxValid;
to.boundBox^ ← from.boundBox^;
to.boxValid ← from.boxValid;
to.onOverlay ← from.onOverlay;
to.extraPoints ← from.extraPoints;
to.priority ← from.priority;
to.historyTop ← from.historyTop;
END;
};
OutlineBuildPath:
PUBLIC PROC [slice: Slice, transformParts: SliceParts, transform: Transformation, moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc, editConstraints: EditConstraints ← none] = {
GGModelTypes.SliceBuildPathProc
transformParts=NIL => complete parts. transform=NIL => don't transform any parts
outlineData: OutlineData ← NARROW[slice.data];
outlineTransformParts: OutlineParts ← NARROW[transformParts];
noTransform: BOOL ← transform=NIL;
transformAll: BOOL ← transform#NIL AND transformParts=NIL;
pathTransformParts: LIST OF SliceDescriptor ← IF transformParts=NIL THEN NIL ELSE outlineTransformParts.descriptors;
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
isClosed: BOOL ← GGSliceOps.GetType[children.first]#$Traj OR NARROW[children.first.data, TrajData].role#open;
IF isClosed
THEN {
-- next child needs some path building
IF noTransform THEN GGSliceOps.BuildPath[children.first, NIL, NIL, moveTo, lineTo, curveTo, conicTo, arcTo, editConstraints]
ELSE IF transformAll THEN GGSliceOps.BuildPath[children.first, NIL, transform, moveTo, lineTo, curveTo, conicTo, arcTo, editConstraints]
ELSE GGSliceOps.BuildPath[children.first, IF pathTransformParts.first=NIL THEN NIL ELSE pathTransformParts.first.parts, IF pathTransformParts.first=NIL THEN NIL ELSE transform, moveTo, lineTo, curveTo, conicTo, arcTo, editConstraints];
};
IF pathTransformParts#NIL THEN pathTransformParts ← pathTransformParts.rest;
ENDLOOP;
};
OutlineDrawBorder:
PUBLIC PROC [slice: Slice, drawParts: SliceParts, transformParts: SliceParts, transform: Transformation, dc: Imager.Context, camera: Camera, quick:
BOOL ←
FALSE, editConstraints: EditConstraints ← none] = {
GGModelTypes.SliceDrawBorderProc
drawParts=NIL => draw ALL parts. transformParts=NIL => transform ALL parts. transform=NIL => don't transform any parts.
outlineData: OutlineData ← NARROW[slice.data];
outlineDrawParts: OutlineParts ← NARROW[drawParts];
outlineTransformParts: OutlineParts ← NARROW[transformParts];
noTransform: BOOL ← transform=NIL;
transformAll: BOOL ← transform#NIL AND transformParts=NIL;
drawAll: BOOL ← drawParts=NIL;
pathTransformParts: LIST OF SliceDescriptor ← IF transformParts=NIL THEN NIL ELSE outlineTransformParts.descriptors;
pathDrawParts: LIST OF SliceDescriptor ← IF drawAll THEN NIL ELSE outlineDrawParts.descriptors;
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
IF drawAll
OR pathDrawParts.first#
NIL
THEN {
-- next child needs some drawing
IF noTransform THEN GGSliceOps.DrawBorder[children.first, IF drawAll THEN NIL ELSE pathDrawParts.first.parts, NIL, NIL, dc, camera, quick, editConstraints]
ELSE IF transformAll THEN GGSliceOps.DrawBorder[children.first, IF drawAll THEN NIL ELSE pathDrawParts.first.parts, NIL, transform, dc, camera, quick, editConstraints]
ELSE GGSliceOps.DrawBorder[children.first, IF drawAll THEN NIL ELSE pathDrawParts.first.parts, IF pathTransformParts.first=NIL THEN NIL ELSE pathTransformParts.first.parts, IF pathTransformParts.first=NIL THEN NIL ELSE transform, dc, camera, quick, editConstraints];
};
IF pathTransformParts#NIL THEN pathTransformParts ← pathTransformParts.rest;
IF pathDrawParts#NIL THEN pathDrawParts ← pathDrawParts.rest;
ENDLOOP;
};
OutlineDrawParts:
PUBLIC PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: Camera, quick:
BOOL] = {
This routine assumes it is called without needing to transform any of its parts or needing to take EditConstraints into account.
Currently draws entire outline.
BuildOutline: Imager.PathProc = {
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
isOpen: BOOL ← GGSliceOps.IsOpen[children.first];
IF NOT isOpen THEN GGSliceOps.BuildPath[children.first, NIL, NIL, moveTo, lineTo, curveTo, conicTo, arcTo, none]; -- draw, without transforming
ENDLOOP;
};
outlineData: OutlineData ← NARROW[slice.data];
Draw the fill.
IF outlineData.fillColor#
NIL
THEN {
GGCoreOps.SetColor[dc, outlineData.fillColor];
Imager.MaskFill[dc, BuildOutline, outlineData.oddWrap];
};
Draw the strokes.
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
GGSliceOps.DrawBorder[children.first, NIL, NIL, NIL, dc, camera, FALSE, none];
ENDLOOP;
Draw the fill text.
BEGIN
RenderChild:
PROC [child: Slice]
RETURNS [ done:
BOOL ←
FALSE] = {
GGSlice.RenderText[dc, child, camera, quick];
};
[] ← GGParent.WalkChildren[slice, leaf, RenderChild, $Box];
END;
};
DrawParts:
PUBLIC
PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: Camera, quick:
BOOL] = {
Currently draws entire cluster.
Draw children back to front.
outlineData: OutlineData ← NARROW[slice.data];
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
GGSliceOps.DrawParts[children.first, NIL, dc, camera, quick];
ENDLOOP;
};
OutlineDrawTransform:
PUBLIC PROC [slice: Slice, parts: SliceParts ←
NIL, dc: Imager.Context, camera: Camera, transform: Transformation, editConstraints: EditConstraints] = {
"parts" describes which children of slice are to be transformed (and which are to be drawn in place). If parts is NIL, then all children are to be transformed (or not if transform = NIL);
BuildOutline: Imager.PathProc = {
pathParts: LIST OF SliceDescriptor;
transformParts: SliceParts;
pathParts ← IF outlineParts = NIL THEN NIL ELSE outlineParts.descriptors;
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
transformParts ← IF pathParts = NIL OR pathParts.first = NIL THEN NIL ELSE pathParts.first.parts;
t ← IF pathParts = NIL OR pathParts.first#NIL THEN transform ELSE NIL;
IF
NOT GGSliceOps.IsOpen[children.first]
THEN GGSliceOps.BuildPath[children.first, transformParts, t, moveTo, lineTo, curveTo, conicTo, arcTo, editConstraints];
IF pathParts # NIL THEN pathParts ← pathParts.rest;
ENDLOOP;
};
outlineData: OutlineData ← NARROW[slice.data];
outlineParts: OutlineParts ← NARROW[parts];
borderParts: LIST OF SliceDescriptor;
t: Transformation;
borderParts ← IF outlineParts = NIL THEN NIL ELSE outlineParts.descriptors;
Fill in the outline if appropriate.
IF outlineData.fillColor#
NIL
THEN {
IF IsComplete[outlineParts] THEN GGCoreOps.SetColor[dc, outlineData.fillColor, transform]
ELSE GGCoreOps.SetColor[dc, outlineData.fillColor];
Imager.MaskFill[dc, BuildOutline, outlineData.oddWrap];
};
Draw the strokes.
BEGIN
transformParts: SliceParts;
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
transformParts ← IF borderParts=NIL OR borderParts.first=NIL THEN NIL ELSE borderParts.first.parts;
t ← IF borderParts = NIL OR borderParts.first#NIL THEN transform ELSE NIL;
GGSliceOps.DrawBorder[children.first, NIL, transformParts, t, dc, camera, FALSE, editConstraints];
IF borderParts # NIL THEN borderParts ← borderParts.rest;
ENDLOOP;
END;
};
DrawTransform:
PUBLIC
PROC [slice: Slice, parts: SliceParts ←
NIL, dc: Imager.Context, camera: Camera, transform: Transformation, editConstraints: EditConstraints] = {
parts=NIL => transform entire slice
outlineData: OutlineData ← NARROW[slice.data];
outlineParts: OutlineParts ← NARROW[parts];
childrenD: LIST OF SliceDescriptor;
thisChild: Slice;
theseParts: SliceParts;
t: ImagerTransformation.Transformation;
childrenD ← IF outlineParts = NIL THEN NIL ELSE outlineParts.descriptors;
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children =
NIL
DO
thisChild ← children.first;
IF childrenD =
NIL
THEN {
-- transform the whole cluster
theseParts ← NIL;
t ← transform;
}
ELSE
IF childrenD.first#
NIL
THEN {
-- transform this child
theseParts ← childrenD.first.parts;
t ← transform;
}
ELSE {
-- don't transform this child
theseParts ← NIL;
t ← NIL;
};
GGSliceOps.DrawTransform[thisChild, theseParts, dc, camera, t, editConstraints];
IF childrenD # NIL THEN childrenD ← childrenD.rest;
ENDLOOP;
};
DrawSelectionFeedback:
PUBLIC PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick:
BOOL] = {
If some joints are both normal and hot selected, a special symbol is drawn at them, and separate symbols are drawn at other joints. If the parent is entirely selected or entirely hot, and we are allowed to try fast feedback, a single large box is drawn for that selection type for the entire slice.
normalList, hotList: LIST OF SliceDescriptor;
slowNormal, slowHot, completeNormal, completeHot: BOOL ← FALSE;
normalOutlineParts: OutlineParts ← NARROW[selectedParts];
hotOutlineParts: OutlineParts ← NARROW[hotParts];
IF caretIsMoving OR dragInProgress THEN RETURN;
IF selectedParts=NIL AND hotParts=NIL THEN RETURN;
completeNormal ← normalOutlineParts#NIL AND IsComplete[normalOutlineParts];
completeHot ← hotOutlineParts#NIL AND IsComplete[hotOutlineParts];
slowNormal ← selectedParts#NIL AND NOT (quick AND completeNormal);
slowHot ← hotParts#NIL AND NOT (quick AND completeHot);
IF slowNormal
AND slowHot
THEN {
hotList ← hotOutlineParts.descriptors;
FOR normalList ← normalOutlineParts.descriptors, normalList.rest
UNTIL normalList=
NIL
DO
IF normalList.first#
NIL
THEN
-- normal parts. May also be hot parts. One call does it all
GGSliceOps.DrawSelectionFeedback[normalList.first.slice, normalList.first.parts, IF hotList.first=NIL THEN NIL ELSE hotList.first.parts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]
ELSE
IF hotList.first#
NIL
THEN
-- hot parts only. Need a different call
GGSliceOps.DrawSelectionFeedback[hotList.first.slice, NIL, hotList.first.parts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
hotList ← hotList.rest;
ENDLOOP;
}
ELSE
IF slowNormal
THEN {
FOR normalList ← normalOutlineParts.descriptors, normalList.rest
UNTIL normalList =
NIL
DO
IF normalList.first # NIL THEN GGSliceOps.DrawSelectionFeedback[normalList.first.slice, normalList.first.parts, NIL, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
ENDLOOP;
}
ELSE
IF slowHot
THEN {
FOR hotList ← hotOutlineParts.descriptors, hotList.rest
UNTIL hotList =
NIL
DO
IF hotList.first # NIL THEN GGSliceOps.DrawSelectionFeedback[hotList.first.slice, NIL, hotList.first.parts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
ENDLOOP;
};
};
OutlineDrawSelectionFeedback:
PUBLIC PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick:
BOOL] = {
slowNormal, slowHot, completeNormal, completeHot: BOOL ← FALSE;
firstJoint: Point;
normalOutlineParts: OutlineParts ← NARROW[selectedParts];
hotOutlineParts: OutlineParts ← NARROW[hotParts];
IF caretIsMoving OR dragInProgress THEN RETURN;
IF selectedParts=NIL AND hotParts=NIL THEN RETURN;
completeNormal ← normalOutlineParts#NIL AND IsComplete[normalOutlineParts];
completeHot ← hotOutlineParts#NIL AND IsComplete[hotOutlineParts];
slowNormal ← selectedParts#NIL AND NOT (quick AND completeNormal);
slowHot ← hotParts#NIL AND NOT (quick AND completeHot);
GGParent.DrawSelectionFeedback[slice, selectedParts, hotParts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
Draw the big boxes for speed, if you can.
IF (
NOT slowNormal
AND completeNormal)
OR (
NOT slowHot
AND completeHot)
THEN {
firstChildD: SliceDescriptor ← IF completeNormal THEN normalOutlineParts.descriptors.first ELSE hotOutlineParts.descriptors.first;
pointGen: PointGenerator ← GGSliceOps.PointsInDescriptor[firstChildD];
firstJoint ← GGSliceOps.NextPoint[slice: firstChildD.slice, pointGen: pointGen].point;
};
IF
NOT slowNormal
AND completeNormal
THEN
GGShapes.DrawQuickSelectedJoint[dc, firstJoint, normal, camera.cpScale];
IF
NOT slowHot
AND completeHot
THEN
GGShapes.DrawQuickSelectedJoint[dc, firstJoint, hot, camera.cpScale];
};
DrawAttractorFeedback:
PUBLIC PROC [slice: Slice, attractorParts: SliceParts, selectedParts: SliceParts, dragInProgress:
BOOL, dc: Imager.Context, camera: Camera, editConstraints: EditConstraints] = {
The selectedParts are a hint as to what is being dragged. Attractor feedback for the moving part is suppressed if the attractor is part of something that is moving.
success: BOOL ← FALSE;
childD: SliceDescriptor;
childSelectedParts: SliceParts;
childD ← GGParent.FirstIncludedChild[slice, attractorParts, first]; -- first level because ChildPartsFromParts only goes one level. Expect a recursive call to here.
IF childD = NIL THEN RETURN;
Find out which parts of the child should not be highlighted (because they are moving).
childSelectedParts ← ChildPartsFromParts[slice, selectedParts, childD.slice];
GGSliceOps.DrawAttractorFeedback[childD.slice, childD.parts, childSelectedParts, dragInProgress, dc, camera, editConstraints];
};
AttractorFeedbackBoundBox:
PUBLIC PROC [slice: Slice, attractorParts: SliceParts, selectedParts: SliceParts, dragInProgress:
BOOL, camera: Camera, editConstraints: EditConstraints]
RETURNS [box: BoundBox] = {
The selectedParts are a hint as to what is being dragged. Attractor feedback for the moving part is suppressed if the attractor is part of something that is moving.
success: BOOL ← FALSE;
childD: SliceDescriptor;
childSelectedParts: SliceParts;
childD ← GGParent.FirstIncludedChild[slice, attractorParts, first]; -- there should only be one child mentioned in the attractorParts
IF childD = NIL THEN RETURN;
Find out which parts of the child should not be highlighted (because they are moving).
childSelectedParts ← ChildPartsFromParts[slice, selectedParts, childD.slice];
box ← GGSliceOps.AttractorFeedbackBoundBox[childD.slice, childD.parts, childSelectedParts, dragInProgress, camera, editConstraints];
};
ChildPartsFromParts:
PROC [slice: Slice, parts: SliceParts, child: Slice]
RETURNS [childParts: SliceParts] = {
Return which parts of child are mentioned in parts.
outlineParts: OutlineParts ← NARROW[parts];
IF child=NIL OR parts=NIL THEN RETURN[NIL];
FOR descriptors:
LIST
OF SliceDescriptor ← outlineParts.descriptors, descriptors.rest
UNTIL descriptors=
NIL
DO
IF descriptors.first#NIL AND descriptors.first.slice=child THEN RETURN[descriptors.first.parts];
ENDLOOP;
};
SaveSelections:
PUBLIC PROC [slice: Slice, parts: SliceParts, selectClass: SelectionClass] = {
GGModelTypes.SliceSaveSelectionsProc
Ya gotta clear out them bits first because there may have been NO SELECTION!
children: LIST OF Slice ← NARROW[slice.data, OutlineData].children;
FOR nextChildList:
LIST
OF Slice ← children, nextChildList.rest
UNTIL nextChildList=
NIL
DO
GGSliceOps.SaveSelections[nextChildList.first, NIL, selectClass]; -- clear every selection bit
ENDLOOP;
IF parts#
NIL
THEN {
-- then ya gotta set them bits
outlineParts: OutlineParts ← NARROW[parts];
childrenParts: LIST OF SliceDescriptor;
IF outlineParts = NIL THEN RETURN;
childrenParts ← outlineParts.descriptors;
FOR nextChildParts:
LIST
OF SliceDescriptor ← childrenParts, nextChildParts.rest
UNTIL nextChildParts=
NIL
DO
IF nextChildParts.first#NIL THEN GGSliceOps.SaveSelections[nextChildParts.first.slice, nextChildParts.first.parts, selectClass];
ENDLOOP;
};
};
RemakeSelections:
PUBLIC PROC [slice: Slice, selectClass: SelectionClass]
RETURNS [parts: SliceParts] = {
GGModelTypes.SliceRemakeSelectionsProc
childD: SliceDescriptor;
outlineParts: OutlineParts ← NEW[OutlinePartsObj];
partsFound: BOOL ← FALSE;
ptr: LIST OF SliceDescriptor;
children: LIST OF Slice ← NARROW[slice.data, OutlineData].children;
[outlineParts.descriptors, ptr] ← GGUtility.StartSliceDescriptorList[];
FOR nextChildren:
LIST
OF Slice ← children, nextChildren.rest
UNTIL nextChildren=
NIL
DO
nextChildPart: SliceParts ← GGSliceOps.RemakeSelections[nextChildren.first, selectClass];
IF nextChildPart=NIL THEN childD ← NIL
ELSE {
childD ← GGSlice.DescriptorFromParts[nextChildren.first, nextChildPart];
partsFound ← TRUE;
};
[outlineParts.descriptors, ptr] ←
GGUtility.AddSliceDescriptor[childD, outlineParts.descriptors, ptr];
ENDLOOP;
IF partsFound THEN parts ← outlineParts ELSE parts ← NIL;
};
Transform:
PUBLIC PROC [slice: Slice, parts: SliceParts ←
NIL, transform: ImagerTransformation.Transformation, editConstraints: EditConstraints, history: HistoryEvent] = {
outlineParts: OutlineParts ← NARROW[parts];
outlineData: OutlineData ← NARROW[slice.data];
Call GGSliceOps.Transform on each child, with history=NIL so the children will not make history list entries. The outline will make one instead.
IF outlineParts =
NIL
THEN {
FOR list:
LIST
OF Slice ← outlineData.children, list.rest
UNTIL list =
NIL
DO
GGSliceOps.Transform[list.first, NIL, transform, editConstraints, NIL];
ENDLOOP;
}
ELSE {
FOR list:
LIST
OF SliceDescriptor ← outlineParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first#NIL THEN GGSliceOps.Transform[list.first.slice, list.first.parts, transform, editConstraints, NIL];
ENDLOOP;
};
GGSlice.KillBoundBox[slice];
IF outlineParts = NIL OR IsComplete[outlineParts] THEN outlineData.fillColor ← GGCoreOps.TransformColor[outlineData.fillColor, transform];
IF outlineData.fillText #
NIL
THEN {
FirstChildProc:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
-- walk every Box
This proc assumes that every Box in the outline is filled if any are filled
[loc, screenStyle] ← GGSlice.GetBoxText[slice];
IF loc.node=NIL THEN RETURN [FALSE]; -- no fill. Keep walking
GGSlice.SetBoxText[slice, loc, screenStyle, history]; -- reformat first filled child
recentlyFilledChild ← slice; -- remember most recently filled child
RETURN[TRUE]; -- and stop walking
};
RestChildProc:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
-- walk every Box
This proc assumes that every Box in the outline is filled if any are filled
IF slice#recentlyFilledChild
THEN {
-- skip the first (already reformatted) child
siblingNodes: TiogaImager.FormattedNodes ← GGSlice.GetBoxNodes[recentlyFilledChild];
resumeLoc: TextNode.Location ← siblingNodes.resume;
GGSlice.SetBoxText[slice, resumeLoc, screenStyle, history];
recentlyFilledChild ← slice;
};
};
recentlyFilledChild: Slice;
foundFilledBox, screenStyle: BOOL ← FALSE;
loc: TextNode.Location;
foundFilledBox ← GGParent.WalkChildren[slice, leaf, FirstChildProc, $Box];
IF foundFilledBox THEN [] ← GGParent.WalkChildren[slice, leaf, RestChildProc, $Box];
};
};
OneChildPartOnly:
PROC [realParts: OutlineParts]
RETURNS [theChild: SliceDescriptor] = {
IF realParts describes exactly one child, return that child. Otherwise return NIL.
FOR list:
LIST
OF SliceDescriptor ← realParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first#
NIL
THEN {
IF theChild#NIL THEN RETURN[NIL] -- more than one child
ELSE theChild ← list.first;
};
ENDLOOP;
};
Describe:
PUBLIC
PROC [sliceD: SliceDescriptor, className: Rope.
ROPE]
RETURNS [rope: Rope.
ROPE] = {
realParts: OutlineParts;
theChild: SliceDescriptor;
IF sliceD.parts = NIL THEN RETURN["a Slice"];
realParts ← NARROW[sliceD.parts];
IF (theChild ← OneChildPartOnly[realParts])#
NIL
THEN {
rope ← GGSliceOps.Describe[theChild];
}
ELSE {
FOR list:
LIST
OF SliceDescriptor ← realParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first#NIL THEN RETURN[Rope.Concat["several parts of a multi-slice ", className]];
ENDLOOP;
rope ← Rope.Cat["No ", className, " parts to speak of."];
};
};
DescribeHit:
PUBLIC PROC [slice: Slice, hitData:
REF
ANY]
RETURNS [rope: Rope.
ROPE] = {
outlineData: OutlineData ← NARROW[slice.data];
outlineHitData: OutlineHitData ← NARROW[hitData];
count: NAT ← 0;
An outlineHitData is now [child: Slice, childHitData: REF ANY];
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children=
NIL
DO
child: Slice ← children.first;
IF child=outlineHitData.child
THEN {
clusterRope: Rope.ROPE;
IF GGScene.IsTopLevel[slice] THEN clusterRope ← IO.PutFR["child %g", [integer[count]]]
ELSE clusterRope ← IO.PutFR["/%g", [integer[count]]];
IF NOT GGParent.IsParent[child] THEN clusterRope ← Rope.Concat[clusterRope, ": "];
rope ← Rope.Concat[clusterRope, GGSliceOps.DescribeHit[child, outlineHitData.childHitData]];
RETURN;
};
count ← count + 1;
ENDLOOP;
SIGNAL Problem["Unmatched outline and hitData in OutlineDescribeHit"];
};
Fileout:
PUBLIC PROC [slice: Slice, f:
IO.
STREAM] = {
outlineData: OutlineData ← NARROW[slice.data];
count: NAT ← 0;
FOR childList:
LIST
OF Slice ← outlineData.children, childList.rest
UNTIL childList =
NIL
DO
count ← count + 1;
ENDLOOP;
f.PutF["Children: [%g]\n", [integer[count]]];
FOR childList:
LIST
OF Slice ← outlineData.children, childList.rest
UNTIL childList =
NIL
DO
GGSliceOps.FileoutSlice[f, childList.first]; -- not the class proc Fileout, but FileoutSlice
ENDLOOP;
};
Filein: PUBLIC PROC [f: IO.STREAM, version: REAL, router: MsgRouter, camera: Camera] RETURNS [children: LIST OF Slice] = {
8803.24, 8810.24
count: NAT ← 0;
class: SliceClass;
className: Rope.ROPE;
someChild, nextChild: Slice;
childList, ptr: LIST OF Slice;
IF version >= 8810.24 THEN GGParseIn.ReadWRope[f, "Children:"]
ELSE GGParseIn.ReadWRope[f, "Trajectories:"];
GGParseIn.ReadWRope[f, "["];
count ← GGParseIn.ReadWNAT[f];
GGParseIn.ReadWRope[f, "]"];
IF count>0 THEN {
[childList, ptr] ← GGUtility.StartSliceList[];
FOR i: NAT IN [0..count) DO
IF version>=8802.04 THEN {
className ← GGParseIn.ReadWWord[f];
class ← IF className=NIL THEN NIL ELSE GGSlice.FetchSliceClass[Atom.MakeAtom[className]];
IF class=NIL THEN {ComplainAboutFile[f, "unknown class", "NIL child detected", feedback]; LOOP;};
}
ELSE {
className ← "Traj";
class ← trajClass;
};
nextChild ← class.filein[f, version, feedback, camera];
IF someChild=NIL AND nextChild#NIL AND class.type=$Traj AND NARROW[nextChild.data, TrajData].role=circle THEN {
I think this is a bug (changing the global fillColor) but I don't know what its about and it is probably never happening so ... KAP. January 4, 1989
fillColor ← NIL; -- needed for the transition from circle/disc class
NARROW[nextChild.data, TrajData].role ← fence; -- fixup the kludge from TrajFilein
};
IF nextChild#NIL THEN {
someChild ← nextChild;
[childList, ptr] ← GGUtility.AddSlice[nextChild, childList, ptr];
}
ELSE ComplainAboutFile[f, className, "NIL child detected", feedback];
ENDLOOP;
RETURN[childList];
}
ELSE ComplainAboutFile[f, "Parent", "parental object with no children detected", feedback];
};
Filein:
PUBLIC
PROC [f:
IO.
STREAM, version:
REAL, router: MsgRouter, camera: Camera]
RETURNS [children:
LIST
OF Slice] = {
count: NAT ← 0;
someChild, nextChild: Slice;
childList, ptr: LIST OF Slice;
IF version >= 8810.24 THEN GGParseIn.ReadWRope[f, "Children:"]
ELSE GGParseIn.ReadWRope[f, "Trajectories:"];
GGParseIn.ReadWRope[f, "["];
count ← GGParseIn.ReadWNAT[f];
GGParseIn.ReadWRope[f, "]"];
IF count>0
THEN {
[childList, ptr] ← GGUtility.StartSliceList[];
FOR i:
NAT
IN [0..count)
DO
nextChild ← GGSliceOps.FileinSlice[f, version, router, camera, IF version>=8802.04 THEN NIL ELSE trajClass]; -- older files had only trajectories as children of outlines
IF someChild=
NIL
AND nextChild#
NIL
AND GGSliceOps.GetType[nextChild]=$Traj
AND
NARROW[nextChild.data, TrajData].role=circle
THEN {
I think this is a bug (changing the global fillColor) but I don't know what its about and it is probably never happening so ... KAP. January 4, 1989
fillColor ← NIL; -- needed for the transition from circle/disc class
NARROW[nextChild.data, TrajData].role ← fence; -- fixup the kludge from TrajFilein
};
IF nextChild#
NIL
THEN {
someChild ← nextChild;
[childList, ptr] ← GGUtility.AddSlice[nextChild, childList, ptr];
}
ELSE ComplainAboutFile[f, "Parent", "NIL child detected", router];
ENDLOOP;
RETURN[childList];
}
ELSE ComplainAboutFile[f, "Parent", "parental object with no children detected", router];
};
OutlineFileout:
PUBLIC PROC [slice: Slice, f:
IO.
STREAM] = {
outlineData: OutlineData ← NARROW[slice.data];
f.PutF["fillColor: "]; GGParseOut.WriteColor[f, outlineData.fillColor];
f.PutF[" ow: "]; GGParseOut.WriteBool[f, outlineData.oddWrap];
f.PutF[" fillText: "]; GGParseOut.WriteText[f, outlineData.fillText, outlineData.screenStyle];
f.PutChar[IO.CR];
GGParent.Fileout[slice, f];
};
OutlineFilein:
PUBLIC PROC [f:
IO.
STREAM, version:
REAL, router: MsgRouter, camera: Camera]
RETURNS [slice: Slice] = {
8803.24, 8810.24
oddWrap, good: BOOL ← TRUE;
fillColor: Color;
fillText: TextNode.Ref;
screenStyle: BOOL ← TRUE;
childList: LIST OF Slice;
IF trajClass=NIL THEN trajClass ← GGSlice.FetchSliceClass[$Traj];
GGParseIn.ReadWRope[f, "fillColor:"];
fillColor ← GGParseIn.ReadColor[f, version];
IF version>=8802.04
THEN {
GGParseIn.ReadWRope[f, "ow:"];
[oddWrap, good] ← GGParseIn.ReadBool[f, version];
};
IF version>=8803.24
THEN {
GGParseIn.ReadWRope[f, "fillText:"];
[fillText, screenStyle] ← GGParseIn.ReadText[f, version];
};
IF version < 8702.26
THEN {
-- read and discard StrokeEnd from older formats
GGParseIn.ReadWRope[f, "strokeEnd:"];
[] ← GGParseIn.ReadStrokeEnd[f];
};
childList ← GGParent.Filein[f, version, router, camera];
IF childList#NIL THEN slice ← OutlineFromChildList[childList, fillColor, oddWrap, fillText, screenStyle, version];
};
ComplainAboutFile:
PROC [f:
IO.
STREAM, className, complaint: Rope.
ROPE, router: MsgRouter] = {
of: FS.OpenFile;
fileName: Rope.ROPE ← NIL;
of ←
FS.OpenFileFromStream[f !
IO.Error => { fileName ← "not a file"; CONTINUE };
];
IF fileName=NIL THEN fileName ← FS.GetName[of].fullFName;
Feedback.PutF[router, oneLiner, $Complaint, "Filein Error for %g: %g slice, %g", [rope[fileName]], [rope[className]], [rope[complaint]] ];
};
IsEmptyParts:
PUBLIC PROC [sliceD: SliceDescriptor]
RETURNS [
BOOL ← FALSE] = {
realParts: OutlineParts ← NARROW[sliceD.parts];
RETURN[IsEmpty[realParts]];
};
IsEmpty:
PUBLIC PROC [realParts: OutlineParts]
RETURNS [
BOOL ← FALSE] = {
IF realParts=NIL THEN RETURN[TRUE]; -- NIL means Empty, Bier, June 29, 1990
FOR list:
LIST
OF SliceDescriptor ← realParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first#NIL AND NOT GGSliceOps.IsEmptyParts[list.first] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
IsCompleteParts:
PUBLIC PROC [sliceD: SliceDescriptor]
RETURNS [
BOOL ← FALSE] = {
realParts: OutlineParts ← NARROW[sliceD.parts];
RETURN[IsComplete[realParts]];
};
IsComplete:
PUBLIC PROC [realParts: OutlineParts]
RETURNS [
BOOL ← FALSE] = {
IF realParts=NIL THEN RETURN[FALSE]; -- NIL means empty, Bier, June 29, 1990
FOR list:
LIST
OF SliceDescriptor ← realParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first=NIL OR NOT GGSliceOps.IsCompleteParts[list.first] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
NewParts:
PUBLIC PROC [slice: Slice, hitData:
REF
ANY, mode: SelectMode]
RETURNS [sliceD: SliceDescriptor] = {
outlineData: OutlineData ← NARROW[slice.data];
outlineHitData: OutlineHitData ← NARROW[hitData];
An outlineHitData is now [child: Slice, childHitData: REF ANY];
newParts: OutlineParts ← NEW[OutlinePartsObj];
SELECT mode
FROM
none, slice, topLevel => {
-- build a complete descriptor
ptr: LIST OF SliceDescriptor;
[newParts.descriptors, ptr] ← GGUtility.StartSliceDescriptorList[];
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children=
NIL
DO
child: Slice ← children.first;
childD: SliceDescriptor ← IF mode=none THEN NIL ELSE GGSliceOps.NewParts[child, NIL, slice];
[newParts.descriptors, ptr] ← GGUtility.AddSliceDescriptor[childD, newParts.descriptors, ptr];
ENDLOOP;
sliceD ← GGSlice.DescriptorFromParts[slice, newParts]; -- empty or complete parts
};
ENDCASE => {
-- all other cases need a legit hitData
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children=
NIL
DO
child: Slice ← children.first;
IF child=outlineHitData.child
THEN {
childD: SliceDescriptor ← GGSliceOps.NewParts[child, outlineHitData.childHitData, mode];
sliceD ← GGParent.DescriptorFromChildDescriptor[slice, childD]; -- hit parts
RETURN;
};
ENDLOOP;
SIGNAL Problem["Unmatched outline and hitData in OutlineNewParts"];
};
};
UnionParts:
PUBLIC PROC [partsA: SliceDescriptor, partsB: SliceDescriptor]
RETURNS [aPlusB: SliceDescriptor] = {
realPartsA: OutlineParts ← NARROW[partsA.parts];
realPartsB: OutlineParts ← NARROW[partsB.parts];
IF partsA.parts = NIL THEN RETURN[partsB];
IF partsB.parts = NIL THEN RETURN[partsA];
IF IsEmpty[realPartsA] THEN RETURN[partsB];
IF IsEmpty[realPartsB] THEN RETURN[partsA];
IF IsComplete[realPartsA] THEN RETURN[partsA];
IF IsComplete[realPartsB] THEN RETURN[partsB];
BEGIN
ptr: LIST OF SliceDescriptor;
union: OutlineParts ← NEW[OutlinePartsObj];
listA: LIST OF SliceDescriptor ← realPartsA.descriptors;
listB: LIST OF SliceDescriptor ← realPartsB.descriptors;
[union.descriptors, ptr] ← GGUtility.StartSliceDescriptorList[];
UNTIL listA =
NIL
DO
IF listA.first = NIL THEN [union.descriptors, ptr] ← GGUtility.AddSliceDescriptor[listB.first, union.descriptors, ptr]
ELSE IF listB.first = NIL THEN [union.descriptors, ptr] ← GGUtility.AddSliceDescriptor[listA.first, union.descriptors, ptr]
ELSE {
newSeq: SliceDescriptor ← GGSliceOps.UnionParts[listA.first, listB.first];
[union.descriptors, ptr] ← GGUtility.AddSliceDescriptor[IF GGSliceOps.IsEmptyParts[newSeq] THEN NIL ELSE newSeq, union.descriptors, ptr];
};
listA ← listA.rest;
listB ← listB.rest;
ENDLOOP;
aPlusB ← GGSlice.DescriptorFromParts[partsA.slice, union];
END;
};
DifferenceParts:
PUBLIC PROC [partsA: SliceDescriptor, partsB: SliceDescriptor]
RETURNS [aMinusB: SliceDescriptor] = {
realPartsA, realPartsB: OutlineParts;
diff: OutlineParts;
listA: LIST OF SliceDescriptor;
listB: LIST OF SliceDescriptor;
ptr: LIST OF SliceDescriptor;
IF partsA.parts = NIL OR partsB = NIL OR partsB.parts = NIL THEN RETURN[partsA];
realPartsA ← NARROW[partsA.parts];
realPartsB ← NARROW[partsB.parts];
listA ← realPartsA.descriptors;
listB ← realPartsB.descriptors;
diff ← NEW[OutlinePartsObj];
[diff.descriptors, ptr] ← GGUtility.StartSliceDescriptorList[];
UNTIL listA =
NIL
DO
IF listA.first = NIL THEN [diff.descriptors, ptr] ← GGUtility.AddSliceDescriptor[NIL, diff.descriptors, ptr]
ELSE IF listB = NIL OR listB.first = NIL THEN [diff.descriptors, ptr] ← GGUtility.AddSliceDescriptor[listA.first, diff.descriptors, ptr]
ELSE {
newSeq: SliceDescriptor ← GGSliceOps.DifferenceParts[listA.first, listB.first];
[diff.descriptors, ptr] ← GGUtility.AddSliceDescriptor[IF GGSliceOps.IsEmptyParts[newSeq] THEN NIL ELSE newSeq, diff.descriptors, ptr];
};
listA ← listA.rest;
listB ← listB.rest;
ENDLOOP;
aMinusB ← GGSlice.DescriptorFromParts[partsA.slice, diff];
};
OutlineMovingParts:
PROC [slice: Slice, selectedParts: SliceParts, editConstraints: EditConstraints, bezierDrag: GGInterfaceTypes.BezierDragRecord]
RETURNS [background, overlay, rubber, drag: SliceDescriptor] = {
outlineData: OutlineData ← NARROW[slice.data];
filled: BOOL ← outlineData.fillColor#NIL OR outlineData.fillText#NIL;
RETURN GGParent.MovingParts[slice, selectedParts, filled, editConstraints, bezierDrag];
};
MovingParts:
PUBLIC
PROC [slice: Slice, selectedParts: SliceParts, treatAsAUnit:
BOOL ←
FALSE, editConstraints: EditConstraints, bezierDrag: GGInterfaceTypes.BezierDragRecord]
RETURNS [background, overlay, rubber, drag: SliceDescriptor] = {
Filled outlines have the property that if any of their subparts are on the overlay plane, then all of their parts are on the overlay plane.
(For now, all outline children are either completely on the overlay or completely off -- Bier, March 14, 1988)
outlineData: OutlineData;
outlineParts: OutlineParts ← NARROW[selectedParts];
bkgdParts, overParts, rubberParts, dragParts: OutlineParts;
bkgdSeq, overSeq, rubberSeq, dragSeq: SliceDescriptor;
bkgdPtr, overPtr, rubberPtr, dragPtr: LIST OF SliceDescriptor;
children: LIST OF Slice;
nullD: SliceDescriptor ← slice.nullDescriptor;
forNow: BOOL ← TRUE;
IF IsEmpty[outlineParts]
THEN {
background ← overlay ← rubber ← drag ← nullD;
RETURN;
};
IF IsComplete[outlineParts]
THEN {
background ← overlay ← rubber ← nullD;
drag ← GGSlice.DescriptorFromParts[slice, selectedParts];
RETURN;
};
Some parts are selected, but not all (the hard case)
bkgdParts ← NEW[OutlinePartsObj];
overParts ← NEW[OutlinePartsObj];
rubberParts ← NEW[OutlinePartsObj];
dragParts ← NEW[OutlinePartsObj];
[bkgdParts.descriptors, bkgdPtr] ← GGUtility.StartSliceDescriptorList[];
[overParts.descriptors, overPtr] ← GGUtility.StartSliceDescriptorList[];
[rubberParts.descriptors, rubberPtr] ← GGUtility.StartSliceDescriptorList[];
[dragParts.descriptors, dragPtr] ← GGUtility.StartSliceDescriptorList[];
outlineData ← NARROW[slice.data];
children ← outlineData.children;
FOR list:
LIST
OF SliceDescriptor ← outlineParts.descriptors, list.rest
UNTIL list =
NIL
DO
IF list.first =
NIL
THEN {
This trajectory is not at all selected. If this parent is to be treated as a unit, put it on the overlay. Otherwise, leave it in the background.
[rubberParts.descriptors, rubberPtr] ← GGUtility.AddSliceDescriptor[NIL, rubberParts.descriptors, rubberPtr];
[dragParts.descriptors, dragPtr] ← GGUtility.AddSliceDescriptor[NIL, dragParts.descriptors, dragPtr];
IF treatAsAUnit
THEN {
overSeq ← GGSliceOps.NewParts[children.first, NIL, slice]; -- complete descriptor
[overParts.descriptors, overPtr] ← GGUtility.AddSliceDescriptor[overSeq, overParts.descriptors, overPtr];
[bkgdParts.descriptors, bkgdPtr] ← GGUtility.AddSliceDescriptor[NIL, bkgdParts.descriptors, bkgdPtr];
}
ELSE {
bkgdSeq ← GGSliceOps.NewParts[children.first, NIL, slice]; -- complete descriptor
[bkgdParts.descriptors, bkgdPtr] ← GGUtility.AddSliceDescriptor[bkgdSeq, bkgdParts.descriptors, bkgdPtr];
[overParts.descriptors, overPtr] ← GGUtility.AddSliceDescriptor[NIL, overParts.descriptors, overPtr];
};
}
ELSE {
Some or all of this trajectory is selected. If the outline is treatAsAUnit, put all on overlay. Otherwise, put non-moving parts on background.
[bkgdSeq, overSeq, rubberSeq, dragSeq] ← GGSliceOps.MovingParts[list.first.slice, list.first.parts, editConstraints, bezierDrag]; -- for now bezierDrag is extra
IF treatAsAUnit
OR forNow
THEN {
[bkgdParts.descriptors, bkgdPtr] ← GGUtility.AddSliceDescriptor[NIL, bkgdParts.descriptors, bkgdPtr];
[overParts.descriptors, overPtr] ← GGUtility.AddSliceDescriptor[overSeq, overParts.descriptors, overPtr];
[rubberParts.descriptors, rubberPtr] ← GGUtility.AddSliceDescriptor[rubberSeq, rubberParts.descriptors, rubberPtr];
[dragParts.descriptors, dragPtr] ← GGUtility.AddSliceDescriptor[dragSeq, dragParts.descriptors, dragPtr];
}
ELSE {
[bkgdParts.descriptors, bkgdPtr] ← GGUtility.AddSliceDescriptor[overSeq, bkgdParts.descriptors, bkgdPtr];
[overParts.descriptors, overPtr] ← GGUtility.AddSliceDescriptor[NIL, overParts.descriptors, overPtr];
[rubberParts.descriptors, rubberPtr] ← GGUtility.AddSliceDescriptor[rubberSeq, rubberParts.descriptors, rubberPtr];
[dragParts.descriptors, dragPtr] ← GGUtility.AddSliceDescriptor[dragSeq, dragParts.descriptors, dragPtr];
};
};
children ← children.rest;
ENDLOOP;
background ← GGSlice.DescriptorFromParts[slice, bkgdParts];
overlay ← GGSlice.DescriptorFromParts[slice, overParts];
rubber ← GGSlice.DescriptorFromParts[slice, rubberParts];
drag ← GGSlice.DescriptorFromParts[slice, dragParts];
};
AugmentParts:
PUBLIC PROC [sliceD: SliceDescriptor, selectClass: SelectionClass]
RETURNS [more: SliceDescriptor] = {
Like, if you (normal) select a segment, you should select its end joints as well. This PROC gives new classes a chance to invent their own rules for augmenting selections.
ptr: LIST OF SliceDescriptor;
outlineParts: OutlineParts ← NARROW[sliceD.parts];
newParts: OutlineParts;
IF outlineParts = NIL THEN RETURN[sliceD];
newParts ← NEW[OutlinePartsObj];
[newParts.descriptors, ptr] ← GGUtility.StartSliceDescriptorList[];
FOR nextParts:
LIST
OF SliceDescriptor ← outlineParts.descriptors, nextParts.rest
UNTIL nextParts=
NIL
DO
nextPart: SliceDescriptor ← nextParts.first;
[newParts.descriptors, ptr] ← GGUtility.AddSliceDescriptor[IF nextPart#NIL THEN GGSliceOps.AugmentParts[nextPart, selectClass] ELSE NIL, newParts.descriptors, ptr];
ENDLOOP;
more ← GGSlice.DescriptorFromParts[sliceD.slice, newParts];
};
AlterParts:
PUBLIC
PROC [sliceD: SliceDescriptor, action:
ATOM]
RETURNS [alteredD: SliceDescriptor] = {
outlineParts: OutlineParts ← NARROW[sliceD.parts];
outlineData: OutlineData ← NARROW[sliceD.slice.data];
newParts: OutlineParts ← NEW[OutlinePartsObj];
childD: SliceDescriptor ← GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, first];
child: Slice ← childD.slice;
BEGIN
SELECT action
FROM
$Forward, $Backward => {
IF IsComplete[outlineParts] THEN RETURN[NIL];
IF GGSliceOps.IsCompleteParts[childD]
THEN {
beforeEnt, ent, afterEnt: LIST OF Slice;
[beforeEnt, ent, afterEnt, ----] ← GGUtility.FindSliceAndNeighbors[child, outlineData.children];
IF action = $Forward
THEN
alteredD ←
IF afterEnt =
NIL
THEN
NIL
ELSE GGParent.DescriptorFromChild[sliceD.slice, afterEnt.first]
ELSE
alteredD ←
IF beforeEnt =
NIL
THEN
NIL
ELSE GGParent.DescriptorFromChild[sliceD.slice, beforeEnt.first];
}
ELSE GOTO Recurse;
};
$Grow => {
IF IsComplete[outlineParts] THEN RETURN[NIL];
IF GGSliceOps.IsCompleteParts[childD]
THEN
alteredD ← GGSliceOps.NewParts[sliceD.slice, NIL, slice]
ELSE GOTO Recurse;
};
$ShrinkForward => {
IF IsComplete[outlineParts]
THEN
alteredD ← GGParent.DescriptorFromChild[sliceD.slice, OutlineLastChild[sliceD.slice]]
ELSE GOTO Recurse;
};
$ShrinkBackward => {
IF IsComplete[outlineParts]
THEN
alteredD ← GGParent.DescriptorFromChild[sliceD.slice, OutlineFirstChild[sliceD.slice]]
ELSE GOTO Recurse;
};
ENDCASE => ERROR;
EXITS
Recurse => {
alteredChildD: SliceDescriptor ← GGSliceOps.AlterParts[childD, action];
alteredD ←
IF alteredChildD =
NIL
THEN
NIL
ELSE GGParent.DescriptorFromChildDescriptor[sliceD.slice, alteredChildD];
};
END;
};
OutlineFirstChild:
PROC [slice: Slice]
RETURNS [child: Slice] = {
outlineData: OutlineData ← NARROW[slice.data];
child ← outlineData.children.first;
};
OutlineLastChild:
PROC [slice: Slice]
RETURNS [child: Slice] = {
outlineData: OutlineData ← NARROW[slice.data];
FOR list:
LIST
OF Slice ← outlineData.children, list.rest
UNTIL list =
NIL
DO
child ← list.first;
ENDLOOP;
};
SetSelectedFields:
PUBLIC PROC [sliceD: SliceDescriptor, selected:
BOOL, selectClass: SelectionClass] = {
Set the selected fields of all of the joints and segments mentioned in sliceD.
outlineParts: OutlineParts ← NARROW[sliceD.parts];
IF outlineParts = NIL THEN RETURN;
FOR nextParts:
LIST
OF SliceDescriptor ← outlineParts.descriptors, nextParts.rest
UNTIL nextParts=
NIL
DO
nextPart: SliceDescriptor ← nextParts.first;
IF nextPart#NIL THEN GGSliceOps.SetSelectedFields[nextPart, selected, selectClass];
ENDLOOP;
};
PointGeneratorData: TYPE = REF PointGeneratorDataObj;
PointGeneratorDataObj:
TYPE =
RECORD [
seqPtr: LIST OF SliceDescriptor,
pointGen: PointGenerator -- the point generator for the first sequence of seqPtr
];
PointPairGeneratorData: TYPE = REF PointPairGeneratorDataObj;
PointPairGeneratorDataObj:
TYPE =
RECORD [
seqPtr: LIST OF SliceDescriptor,
pointGen: PointPairGenerator -- the pair generator for first sequence of seqPtr
];
SegmentGeneratorData: TYPE = REF SegmentGeneratorDataObj;
SegmentGeneratorDataObj:
TYPE =
RECORD [
seqPtr: LIST OF SliceDescriptor,
segGen: SegmentGenerator -- the point generator for the first sequence of seqPtr
];
PointsInDescriptor:
PUBLIC PROC [sliceD: SliceDescriptor]
RETURNS [pointGen: PointGenerator] = {
outlineParts: OutlineParts ← NARROW[sliceD.parts];
descriptors: LIST OF SliceDescriptor;
childPointGen: PointGenerator;
pointGenData: PointGeneratorData;
descriptors ← IF outlineParts = NIL THEN NIL ELSE outlineParts.descriptors;
childPointGen ← IF descriptors = NIL OR descriptors.first = NIL THEN NIL ELSE GGSliceOps.PointsInDescriptor[descriptors.first];
pointGenData ← NEW[PointGeneratorDataObj ← [descriptors, childPointGen]];
pointGen ← NEW[PointGeneratorObj ← [sliceD, 0, 0, pointGenData]];
};
WalkPointsInDescriptor:
PUBLIC PROC [sliceD: SliceDescriptor, walkProc: PointWalkProc] = {
outlineParts: OutlineParts ← NARROW[sliceD.parts];
descriptors: LIST OF SliceDescriptor;
IF outlineParts = NIL THEN RETURN;
descriptors ← outlineParts.descriptors;
FOR descriptors:
LIST
OF SliceDescriptor ← outlineParts.descriptors, descriptors.rest
UNTIL descriptors =
NIL
DO
IF descriptors.first # NIL THEN GGSliceOps.WalkPointsInDescriptor[descriptors.first, walkProc];
It looks like GGSliceOps.WalkPointsInDescriptor has to return a "done" BOOLEAN for this to work properly.
ENDLOOP;
};
PointPairsInDescriptor:
PUBLIC PROC [sliceD: SliceDescriptor]
RETURNS [pointPairGen: GGModelTypes.PointPairGenerator] = {
outlineParts: OutlineParts ← NARROW[sliceD.parts];
descriptors: LIST OF SliceDescriptor;
childPointPairGen: PointPairGenerator;
pointPairGenData: PointPairGeneratorData;
descriptors ← IF outlineParts = NIL THEN NIL ELSE outlineParts.descriptors;
childPointPairGen ← IF descriptors = NIL OR descriptors.first = NIL THEN NIL ELSE GGSliceOps.PointPairsInDescriptor[descriptors.first];
pointPairGenData ← NEW[PointPairGeneratorDataObj ← [descriptors, childPointPairGen]];
pointPairGen ← NEW[PointPairGeneratorObj ← [sliceD, 0, 0, pointPairGenData]];
};
SegmentsInDescriptor:
PUBLIC PROC [sliceD: SliceDescriptor]
RETURNS [segGen: SegmentGenerator] = {
outlineParts: OutlineParts ← NARROW[sliceD.parts];
descriptors: LIST OF SliceDescriptor ← IF outlineParts = NIL THEN NIL ELSE outlineParts.descriptors;
IF descriptors#
NIL
THEN {
segGenData: SegmentGeneratorData ←
NEW[SegmentGeneratorDataObj ← [
seqPtr: descriptors, -- one descriptor per child in outline, including NILs for unincluded descriptors
segGen: IF descriptors.first=NIL THEN NIL ELSE GGSliceOps.SegmentsInDescriptor[descriptors.first] -- segGen for first child
]];
segGen ←
NEW[SegmentGeneratorObj ← [
toGo: 9999, -- for debugging
sliceD: sliceD,
completeSeq: FALSE,
classSpecific: segGenData
]];
};
};
NextSegment:
PUBLIC PROC [slice: Slice, segGen: SegmentGenerator]
RETURNS [seg: Segment, transform: ImagerTransformation.Transformation] = {
segGenData: SegmentGeneratorData ← NARROW[segGen.classSpecific];
descriptors: LIST OF SliceDescriptor ← segGenData.seqPtr;
IF segGen.toGo#9999 THEN ERROR; -- for debugging
WHILE
TRUE
DO
IF descriptors.first #
NIL
THEN {
-- maybe some seg left in this child
[seg, transform] ← GGSliceOps.NextSegment[descriptors.first.slice, segGenData.segGen];
IF seg#
NIL
THEN {
segGenData.seqPtr ← descriptors;
RETURN;
};
descriptors ← descriptors.rest; -- on to the next child
};
on to next non-NIL child
UNTIL descriptors = NIL OR descriptors.first # NIL DO descriptors ← descriptors.rest ENDLOOP;
end of the line ??
IF descriptors = NIL THEN RETURN[NIL, NIL];
segGenData.segGen ← GGSliceOps.SegmentsInDescriptor[descriptors.first]; -- segGen for next child
ENDLOOP;
};
WalkSegments:
PUBLIC PROC [slice: Slice, walkProc: WalkProc]
RETURNS [sliceD: SliceDescriptor] = {
Takes a slice and calls the walkProc for each segment in the slice with the segment data and possibly an associated transformation. It returns a slice descriptor which describes all segments of the slice for which the walkProc returned TRUE.
ptr: LIST OF SliceDescriptor;
outlineData: OutlineData ← NARROW[slice.data];
newParts: OutlineParts ← NEW[OutlinePartsObj];
[newParts.descriptors, ptr] ← GGUtility.StartSliceDescriptorList[];
FOR children:
LIST
OF Slice ← outlineData.children, children.rest
UNTIL children=
NIL
DO
child: Slice ← children.first;
childD: SliceDescriptor ← GGSliceOps.WalkSegments[child, walkProc];
[newParts.descriptors, ptr] ← GGUtility.AddSliceDescriptor[childD, newParts.descriptors, ptr];
ENDLOOP;
sliceD ← GGSlice.DescriptorFromParts[slice, newParts];
};
NextPoint:
PUBLIC PROC [slice: Slice, pointGen: PointGenerator]
RETURNS [pointAndDone: GGModelTypes.PointAndDone] = {
pgd: PointGeneratorData ← NARROW[pointGen.classSpecific];
descriptors: LIST OF SliceDescriptor ← pgd.seqPtr;
IF descriptors = NIL THEN RETURN[[[0,0], TRUE]];
WHILE
TRUE
DO
IF descriptors.first #
NIL
THEN {
-- maybe some point left in this child
pointAndDone ← GGSliceOps.NextPoint[descriptors.first.slice, pgd.pointGen];
IF
NOT pointAndDone.done
THEN {
pgd.seqPtr ← descriptors;
RETURN;
};
descriptors ← descriptors.rest; -- on to the next child
};
on to next non-NIL child
UNTIL descriptors = NIL OR descriptors.first # NIL DO descriptors ← descriptors.rest ENDLOOP;
IF descriptors = NIL THEN RETURN[[[0,0], TRUE]];
pgd.pointGen ← GGSliceOps.PointsInDescriptor[descriptors.first]; -- pointGen for next child
ENDLOOP;
};
NextPointPair:
PUBLIC PROC [slice: Slice, pointPairGen: PointPairGenerator]
RETURNS [pointPairAndDone: PointPairAndDone] = {
pgd: PointPairGeneratorData ← NARROW[pointPairGen.classSpecific];
descriptors: LIST OF SliceDescriptor ← pgd.seqPtr;
IF descriptors = NIL THEN RETURN[[[0,0], [0,0], TRUE]];
WHILE
TRUE
DO
IF descriptors.first #
NIL
THEN {
-- maybe some pair left in this child
pointPairAndDone ← GGSliceOps.NextPointPair[descriptors.first.slice, pgd.pointGen];
IF
NOT pointPairAndDone.done
THEN {
pgd.seqPtr ← descriptors;
RETURN;
};
descriptors ← descriptors.rest; -- on to the next child
};
on to next non-NIL child
UNTIL descriptors = NIL OR descriptors.first # NIL DO descriptors ← descriptors.rest ENDLOOP;
IF descriptors = NIL THEN RETURN[[[0,0], [0,0], TRUE]];
pgd.pointGen ← GGSliceOps.PointPairsInDescriptor[descriptors.first];
ENDLOOP;
};
fillColor: PUBLIC Imager.Color ← Imager.MakeGray[0.5];
trajClass: SliceClass; -- filled in by OutlineFilein
END.