GGOutlineImplA.mesa
Contents: Implementations of the Parent Meta-Class and the Outline class.
Copyright Ó 1986, 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Pier, June 19, 1992 10:19 pm PDT
Eisenman, October 2, 1987 9:22:03 pm PDT
Bier, February 23, 1993 7:14 pm PST
Doug Wyatt, April 14, 1992 2:39 pm PDT
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;
The Outline Class
BuildOutlineSliceClass: PUBLIC PROC [] RETURNS [class: SliceClass] = {
class ¬ NEW[SliceClassObj ¬ [
type: $Outline,
unlink: GGParent.Unlink,
Fundamentals
getBoundBox: GGParent.GetBoundBox,
getTransformedBoundBox: GGParent.GetTransformedBoundBox,
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: GGParent.GetTransformedBoundBox,
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.PutRope["frozen: "]; GGParseOut.WriteBool[f, clusterData.frozen];
f.PutChar[IO.LF];
GGParent.Fileout[slice, f];
};
ClusterFilein: PUBLIC PROC [f: IO.STREAM, version: REAL, router: MsgRouter, camera: Camera] RETURNS [slice: Slice] = {
frozen: BOOL ¬ TRUE;
childList: LIST OF Slice;
GGParseIn.ReadRope[f, "frozen:"];
frozen ¬ GGParseIn.ReadBool[f];
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
Fundamentals
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];
IF thisBoundBox # NIL THEN 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;
};
};
};
GetTransformedBoundBox: PUBLIC PROC [slice: Slice, selectedParts: SliceParts, movingParts: SliceParts, transform: Transformation ¬ NIL] RETURNS [box: BoundBox] = {
GGModelTypes.SliceTransformedBoundBoxProc
IF slice.boxValid AND selectedParts=NIL THEN RETURN[GGBoundBox.BoundBoxOfBoundBox[slice.boundBox, transform]] -- fast case
ELSE {
outlineSelectedParts: OutlineParts ¬ NARROW[selectedParts];
outlineMovingParts: OutlineParts ¬ NARROW[movingParts];
outlineData: OutlineData ¬ NARROW[slice.data];
boundBoxList: LIST OF BoundBox;
thisBoundBox: BoundBox;
IF selectedParts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO
transformedBound box of all the children
thisBoundBox ¬ GGSliceOps.GetTransformedBoundBox[childList.first, NIL, NIL, transform];
boundBoxList ¬ CONS[thisBoundBox, boundBoxList];
ENDLOOP
ELSE {
nextMovingList: LIST OF SliceDescriptor ¬ outlineMovingParts.descriptors;
FOR list: LIST OF SliceDescriptor ¬ outlineSelectedParts.descriptors, list.rest UNTIL list = NIL DO
nextMovingD: SliceDescriptor ¬ nextMovingList.first; -- parallel LIST walk
IF list.first#NIL THEN {
thisBoundBox ¬ GGSliceOps.GetTransformedBoundBox[list.first.slice, list.first.parts, nextMovingD.parts, transform];
boundBoxList ¬ CONS[thisBoundBox, boundBoxList];
};
nextMovingList ¬ nextMovingList.rest;
ENDLOOP;
};
box ¬ GGBoundBox.BoundBoxOfBoxes[boundBoxList];
};
};
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 {
router: MsgRouter ~ Feedback.EnsureRouter[$Gargoyle];
Feedback.Append[router, 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;
};
Drawing
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;
};
Transforming
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];
};
};
Textual Description
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.PutFR1["child %g", [integer[count]]]
ELSE clusterRope ¬ IO.PutFR1["/%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.PutF1["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.ReadRope[f, "Children:"]
ELSE GGParseIn.ReadRope[f, "Trajectories:"];
GGParseIn.ReadChar[f, '[];
count ← GGParseIn.ReadNAT[f];
GGParseIn.ReadChar[f, ']];
IF count>0 THEN {
[childList, ptr] ← GGUtility.StartSliceList[];
FOR i: NAT IN [0..count) DO
IF version>=8802.04 THEN {
className ← GGParseIn.ReadWord[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.ReadRope[f, "Children:"]
ELSE GGParseIn.ReadRope[f, "Trajectories:"];
GGParseIn.ReadChar[f, '[];
count ¬ GGParseIn.ReadNAT[f];
GGParseIn.ReadChar[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.PutRope["fillColor: "]; GGParseOut.WriteColor[f, outlineData.fillColor];
f.PutRope[" ow: "]; GGParseOut.WriteBool[f, outlineData.oddWrap];
f.PutRope[" fillText: "]; GGParseOut.WriteText[f, outlineData.fillText, outlineData.screenStyle];
f.PutChar[IO.LF];
GGParent.Fileout[slice, f];
};
OutlineFilein: PUBLIC PROC [f: IO.STREAM, version: REAL, router: MsgRouter, camera: Camera] RETURNS [slice: Slice] = {
8803.24, 8810.24
oddWrap: BOOL ¬ TRUE;
fillColor: Color;
fillText: TextNode.Ref;
screenStyle: BOOL ¬ TRUE;
childList: LIST OF Slice;
IF trajClass=NIL THEN trajClass ¬ GGSlice.FetchSliceClass[$Traj];
GGParseIn.ReadRope[f, "fillColor:"];
fillColor ¬ GGParseIn.ReadColor[f, version];
IF version>=8802.04 THEN {
GGParseIn.ReadRope[f, "ow:"];
oddWrap ¬ GGParseIn.ReadBool[f];
};
IF version>=8803.24 THEN {
GGParseIn.ReadRope[f, "fillText:"];
[fillText, screenStyle] ¬ GGParseIn.ReadText[f, version];
};
IF version < 8702.26 THEN { -- read and discard StrokeEnd from older formats
GGParseIn.ReadRope[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.PutFL[router, oneLiner, $Complaint, "Filein Error for %g: %g slice, %g", LIST[[rope[fileName]], [rope[className]], [rope[complaint]]] ];
};
Parts
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;
};
Hit Testing
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.