GGOutlineImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on June 2, 1986 5:10:01 pm PDT
Contents: Procedures to implement the Outline Slice Class in Gargoyle. Outlines consist of Trajectories which in turn consist of Segments. Outline is the most important slice class.
DIRECTORY
GGBasicTypes, GGBoundBox, GGDescribe, GGError, GGInterfaceTypes, GGModelTypes, GGOutline, GGObjects, GGSelect, GGSegmentTypes, GGSequence, GGShapes, GGTraj, GGUtility, GGVector, GList, Imager, ImagerColor, ImagerPath, ImagerTransformation, Rope, Rosary;
GGOutlineImpl:
CEDAR
PROGRAM
IMPORTS GGBoundBox, GGDescribe, GGError, GGObjects, GGSelect, GGSequence, GGShapes, GGTraj, GGUtility, GGVector, GList, Imager, ImagerColor, ImagerPath, ImagerTransformation, Rosary
EXPORTS GGOutline = BEGIN
BitVector: TYPE = GGBasicTypes.BitVector;
BoundBox: TYPE = GGBasicTypes.BoundBox;
CameraData: TYPE = GGModelTypes.CameraData;
Circle: TYPE = GGBasicTypes.Circle;
Color: TYPE = Imager.Color;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGSegmentTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Line: TYPE = GGBasicTypes.Line;
ObjectBag: TYPE = GGInterfaceTypes.ObjectBag;
Outline: TYPE = REF OutlineObj;
OutlineObj: TYPE = GGModelTypes.OutlineObj;
OutlineClass: TYPE = REF OutlineClassObj;
OutlineClassObj: TYPE = GGModelTypes.OutlineClassObj;
OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor;
OutlineSequence: TYPE = GGSelect.OutlineSequence;
OutlineSequenceGenerator: TYPE = GGSelect.OutlineSequenceGenerator;
Point: TYPE = GGBasicTypes.Point;
PointGenerator: TYPE = GGModelTypes.PointGenerator;
PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectMode: TYPE = GGModelTypes.SelectMode;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
Sequence: TYPE = GGModelTypes.Sequence;
SliceClass: TYPE = REF SliceClassObj;
SliceClassObj: TYPE = GGModelTypes.SliceClassObj;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = REF TrajObj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = REF TrajGeneratorObj;
TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj;
TrajObj: TYPE = GGModelTypes.TrajObj;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TriggerBag: TYPE = GGInterfaceTypes.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
OutlineDescribeProc: TYPE = GGModelTypes.OutlineDescribeProc;
OutlineFileoutProc: TYPE = GGModelTypes.OutlineFileoutProc;
OutlineFileinProc: TYPE = GGModelTypes.OutlineFileinProc;
OutlineUnionPartsProc: TYPE = GGModelTypes.OutlineUnionPartsProc;
OutlineDifferencePartsProc: TYPE = GGModelTypes.OutlineDifferencePartsProc;
OutlineFixedPartsProc: TYPE = GGModelTypes.OutlineFixedPartsProc;
OutlineAugmentPartsProc: TYPE = GGModelTypes.OutlineAugmentPartsProc;
OutlinePointsInDescriptorProc: TYPE = GGModelTypes.OutlinePointsInDescriptorProc;
OutlinePointPairsInDescriptorProc: TYPE = GGModelTypes.OutlinePointPairsInDescriptorProc;
OutlineNextPointProc: TYPE = GGModelTypes.OutlineNextPointProc;
OutlineNextPointPairProc: TYPE = GGModelTypes.OutlineNextPointPairProc;
OutlineClosestPointProc: TYPE = GGModelTypes.OutlineClosestPointProc;
OutlineClosestPointAndTangentProc: TYPE = GGModelTypes.OutlineClosestPointAndTangentProc;
OutlineClosestSegmentProc: TYPE = GGModelTypes.OutlineClosestSegmentProc;
OutlineSetArrowsProc: TYPE = GGModelTypes.OutlineSetArrowsProc;
OutlineGetArrowsProc: TYPE = GGModelTypes.OutlineGetArrowsProc;
NotYetImplemented: PUBLIC ERROR = CODE;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = GGError.Problem;
NoOp Class Routines
Textual Description
NoOpFileout:
PUBLIC OutlineFileoutProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
NoOpFilein:
PUBLIC OutlineFileinProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
NoOpFixedParts:
PUBLIC OutlineFixedPartsProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
NoOpPointsInDescriptor:
PUBLIC
PROC [sliceD: OutlineDescriptor]
RETURNS [pointGen: PointGenerator] = {
pointGen ← NIL;
};
NoOpPointPairsInDescriptor:
PUBLIC
PROC [sliceD: OutlineDescriptor]
RETURNS [pointPairGen: PointPairGenerator] = {
pointPairGen ← NIL;
};
NoOpNextPoint:
PUBLIC
PROC [pointGen: PointGenerator]
RETURNS [pointAndDone: GGModelTypes.PointAndDone] = {
pointAndDone.done ← TRUE;
};
NoOpNextPointPair:
PUBLIC
PROC [pointPairGen: PointPairGenerator]
RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = {
pointPairAndDone.done ← TRUE;
};
NoOpClosestPoint:
PUBLIC OutlineClosestPointProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
NoOpClosestPointAndTangent:
PUBLIC OutlineClosestPointAndTangentProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
NoOpClosestSegment:
PUBLIC OutlineClosestSegmentProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
Style
NoOpSetArrows:
PUBLIC OutlineSetArrowsProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
NoOpGetArrows:
PUBLIC OutlineGetArrowsProc = {
SIGNAL Problem [msg: "All slice classes must have this proc."];
};
The Outline Class
MakeOutlineClass:
PROC []
RETURNS [class: OutlineClass] = {
class ←
NEW[OutlineClassObj ← [
type: $Outline,
getBoundBox: OutlineBoundBox,
getTightBox: OutlineTightBox,
copy:
OutlineCopy,
Drawing
drawParts: OutlineDrawParts,
drawTransform: OutlineDrawTransform,
drawSelectionFeedback: OutlineDrawSelectionFeedback,
drawAttractorFeedback:
OutlineDrawAttractorFeedback,
Transforming
transform:
OutlineTransform,
Textual Description
describe: OutlineDescribe,
fileout: NoOpFileout,
filein: NoOpFilein,
Parts
emptyParts: OutlineEmptyParts,
newParts: OutlineNewParts,
unionParts: OutlineUnionParts,
differenceParts: OutlineDifferenceParts,
movingParts: OutlineMovingParts,
fixedParts: OutlineFixedParts,
augmentParts: OutlineAugmentParts,
setSelectedFields:
OutlineSetSelectedFields,
Hit Testing
pointsInDescriptor: OutlinePointsInDescriptor,
pointPairsInDescriptor: OutlinePointPairsInDescriptor,
nextPoint: OutlineNextPoint,
nextPointPair: OutlineNextPointPair,
closestPoint: OutlineClosestPoint,
closestPointAndTangent: NoOpClosestPointAndTangent,
closestSegment: OutlineClosestSegment,
lineIntersection: OutlineLineIntersection,
circleIntersection: OutlineCircleIntersection,
hitDataAsSimpleCurve:
OutlineHitDataAsSimpleCurve,
Style
setStrokeWidth: OutlineSetStrokeWidth,
getStrokeWidth: OutlineGetStrokeWidth,
setStrokeColor: OutlineSetStrokeColor,
getStrokeColor: OutlineGetStrokeColor,
setFillColor: OutlineSetFillColor,
getFillColor: OutlineGetFillColor,
setArrows: NoOpSetArrows,
getArrows: NoOpGetArrows
]];
};
OutlineParts: TYPE = REF OutlinePartsObj;
OutlinePartsObj:
TYPE =
RECORD [
simple: BOOL ← FALSE, -- TRUE if seqs contains only a single control point or a single joint
seqs: LIST OF Sequence
];
CreateOutline:
PUBLIC
PROC [traj: Traj, lineEnds: Imager.StrokeEnd ← square, fillColor: Imager.Color ← Imager.black]
RETURNS [outline: Outline] = {
boundBox: BoundBox ← GGBoundBox.CopyBoundBox[traj.boundBox];
outline ←
NEW[OutlineObj ← [
class: FetchOutlineClass[],
fillColor: fillColor,
lineEnds: lineEnds,
children: LIST[traj],
parent: NIL,
boundBox: boundBox]];
traj.parent ← outline;
IF traj.role = hole THEN traj.role ← fence;
};
Fundamentals
OutlineBoundBox:
PROC [slice: Outline, parts: SliceParts]
RETURNS [box: BoundBox] = {
IF parts = NIL THEN box ← slice.boundBox
ELSE {
outlineParts: OutlineParts ← NARROW[parts];
boxList: LIST OF BoundBox ← NIL;
thisBox: BoundBox;
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
thisBox ← GGSequence.ComputeBoundBox[list.first];
boxList ← CONS[thisBox, boxList];
};
ENDLOOP;
box ← GGBoundBox.BoundBoxOfBoxes[boxList];
};
};
OutlineTightBox:
PROC [slice: Outline, parts: SliceParts]
RETURNS [box: BoundBox] = {
outlineParts: OutlineParts;
boxList: LIST OF BoundBox ← NIL;
thisBox: BoundBox;
IF parts = NIL THEN parts ← slice.class.newParts[slice, NIL, slice];
outlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
thisBox ← GGSequence.ComputeTightBox[list.first];
boxList ← CONS[thisBox, boxList];
};
ENDLOOP;
box ← GGBoundBox.BoundBoxOfBoxes[boxList];
};
UpdateOutlineBoundBox:
PUBLIC PROC [slice: Outline] = {
Computes it from traj boxes.
outlineBoxes: LIST OF BoundBox ← NIL;
trajGen: TrajGenerator;
IF slice=NIL THEN RETURN;
trajGen ← TrajsInOutline[slice];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
outlineBoxes ← CONS[traj.boundBox, outlineBoxes];
ENDLOOP;
UpdateBoundBoxFromList[slice.boundBox, outlineBoxes];
};
UpdateBoundBoxFromList:
PROC [bBox: BoundBox, list:
LIST
OF BoundBox] = {
IF list = NIL THEN ERROR;
bBox.loX ← list.first.loX;
bBox.hiX ← list.first.hiX;
bBox.loY ← list.first.loY;
bBox.hiY ← list.first.hiY;
FOR bBoxList:
LIST
OF BoundBox ← list.rest, bBoxList.rest
UNTIL bBoxList =
NIL
DO
bBox.loX ← MIN[bBox.loX, bBoxList.first.loX];
bBox.hiX ← MAX[bBox.hiX, bBoxList.first.hiX];
bBox.loY ← MIN[bBox.loY, bBoxList.first.loY];
bBox.hiY ← MAX[bBox.hiY, bBoxList.first.hiY];
ENDLOOP;
};
OutlineCopy:
PROC [slice: Outline]
RETURNS [copy: Outline] = {
holeGen: TrajGenerator;
newTraj, fence: Traj;
fence ← FenceOfOutline[slice];
newTraj ← GGTraj.CopyTraj[fence];
copy ← CreateOutline[newTraj, slice.lineEnds, slice.fillColor];
holeGen ← HolesOfOutline[slice];
FOR hole: Traj ← GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen]
UNTIL hole =
NIL
DO
newTraj ← GGTraj.CopyTraj[hole];
copy.children ← AppendTrajList[copy.children, LIST[newTraj]];
newTraj.parent ← copy;
ENDLOOP;
};
Drawing
OutlineDrawParts:
PROC [slice: Outline, parts: SliceParts, dc: Imager.Context, camera: CameraData, quick:
BOOL] = {
GGModelTypes.OutlineDrawPartsProc
IF parts # NIL THEN ERROR Problem[msg: "outline parts are not yet implemented"];
DrawOutline[dc, slice];
};
Drawing Utilities
DrawOutline:
PROC [dc: Imager.Context, outline: Outline] = {
BuildOutline: Imager.PathProc = {
BuildPath: Imager.PathProc = {
Someday this will be a call to
path.class.buildPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo];
BuildTrajPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo];
};
imagerHole: ImagerPath.Trajectory;
cc: BOOL;
traj ← fence;
cc ← GGTraj.IsClockwiseTraj[traj];
path.class.buildPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo];
BuildTrajPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo];
FOR holeList:
LIST
OF Traj ← outline.children.rest, holeList.rest
UNTIL holeList =
NIL
DO
traj ← holeList.first;
imagerHole ← ImagerPath.TrajectoryListFromPath[BuildPath].first;
IF cc=GGTraj.IsClockwiseTraj[traj] THEN ImagerPath.MapTrajectoryBackward[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo]
ELSE ImagerPath.MapTrajectory[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
fence, traj: Traj;
fence ← outline.children.first;
IF fence.role = fence
AND outline.fillColor#
NIL
THEN {
Imager.SetColor[dc, outline.fillColor];
Imager.MaskFill[dc, BuildOutline];
};
GGTraj.DrawTraj[dc, fence];
Will be path.class.drawBorder[fence, dc, gargoyleData];
FOR holeList:
LIST
OF Traj ← outline.children.rest, holeList.rest
UNTIL holeList =
NIL
DO
GGTraj.DrawTraj[dc, holeList.first];
Will be path.class.drawBorder[holeList.first, dc, gargoyleData];
ENDLOOP;
};
OutlineDrawTransform:
PROC [slice: Outline, parts: SliceParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = {
sliceParts: OutlineParts ← NARROW[parts];
BuildOutline: Imager.PathProc = {
PathProc: TYPE ~ PROC[moveTo: MoveToProc, lineTo: LineToProc,
curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc];
BuildPath: Imager.PathProc = {
seg: Segment;
firstPoint: Point ← GGTraj.FetchJointPos[traj, 0];
moveTo[ [firstPoint.x, firstPoint.y] ];
FOR i:
INT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
BuildPathTransformSeq: Imager.PathProc = {
seg: Segment;
firstPoint: Point ← GGTraj.FetchJointPos[traj, 0];
IF thisSeq.segments[0] OR thisSeq.joints[0] THEN firstPoint ← ImagerTransformation.Transform[transform, firstPoint];
moveTo[ [firstPoint.x, firstPoint.y] ];
FOR i:
INT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
seg.class.buildPathTransform[seg, transform, thisSeq.segments[i], thisSeq.joints[i], thisSeq.joints[(i+1) MOD thisSeq.traj.segCount], thisSeq.controlPoints[i], lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
imagerHole: ImagerPath.Trajectory;
cc, holeCC: BOOL;
traj ← fence;
thisSeq ← FindSequenceInList[traj, sliceParts.seqs];
IF thisSeq #
NIL
THEN {
BuildPathTransformSeq[moveTo, lineTo, curveTo, conicTo, arcTo];
cc ← GGTraj.IsClockwiseTrajTransformSeq[thisSeq, transform];
}
ELSE {
BuildPath[moveTo, lineTo, curveTo, conicTo, arcTo];
cc ← GGTraj.IsClockwiseTraj[traj];
};
FOR holeList:
LIST
OF Traj ← slice.children.rest, holeList.rest
UNTIL holeList =
NIL
DO
traj ← holeList.first;
thisSeq ← FindSequenceInList[traj, sliceParts.seqs];
IF thisSeq #
NIL
THEN {
imagerHole ← ImagerPath.TrajectoryListFromPath[BuildPathTransformSeq].first;
holeCC ← GGTraj.IsClockwiseTrajTransformSeq[thisSeq, transform];
}
ELSE {
imagerHole ← ImagerPath.TrajectoryListFromPath[BuildPath].first;
holeCC ← GGTraj.IsClockwiseTraj[traj];
};
IF cc=holeCC THEN ImagerPath.MapTrajectoryBackward[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo]
ELSE ImagerPath.MapTrajectory[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
traj: Traj;
thisSeq: Sequence;
fence: Traj ← slice.children.first;
IF fence.role = fence
AND slice.fillColor#
NIL
THEN {
transformedColor: Color;
WITH slice.fillColor
SELECT
FROM
cc: ImagerColor.ConstantColor => {
Imager.SetColor[dc, cc];
};
sc: ImagerColor.SampledColor => {
IF OutlineCompleteParts[slice, parts]
THEN {
transformedColor ← ImagerColor.MakeSampledColor[pa: sc.pa, um: ImagerTransformation.Concat[sc.um, transform], colorOperator: sc.colorOperator];
Imager.SetColor[dc, transformedColor];
};
};
ENDCASE => ERROR;
Imager.MaskFill[dc, BuildOutline];
};
thisSeq ← FindSequenceInList[fence, sliceParts.seqs];
IF thisSeq # NIL THEN GGTraj.DrawTrajTransformSeq[dc, thisSeq, transform] ELSE GGTraj.DrawTraj[dc, fence];
FOR holeList:
LIST
OF Traj ← slice.children.rest, holeList.rest
UNTIL holeList =
NIL
DO
thisSeq ← FindSequenceInList[holeList.first, sliceParts.seqs];
IF thisSeq # NIL THEN GGTraj.DrawTrajTransformSeq[dc, thisSeq, transform] ELSE GGTraj.DrawTraj[dc, holeList.first];
ENDLOOP;
};
FindSequenceInList:
PROC [traj: Traj, seqList:
LIST
OF Sequence]
RETURNS [seq: Sequence] = {
FOR list:
LIST
OF Sequence ← seqList, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL AND list.first.traj = traj THEN RETURN[list.first];
ENDLOOP;
RETURN[NIL];
};
BuildTrajPath:
PROC [traj: Traj, moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc,
curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = {
DoBuildTrajPath: Imager.PathProc = {
seg: Segment;
firstPoint: Point ← GGTraj.FetchJointPos[traj, 0];
moveTo[ [firstPoint.x, firstPoint.y] ];
FOR i:
INT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
DoBuildTrajPath[moveTo, lineTo, curveTo, conicTo, arcTo];
};
OutlineDrawSelectionFeedback:
PROC [slice: Outline, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: CameraData, dragInProgress, caretIsMoving, hideHot, quick:
BOOL] = {
normalOutlineParts, hotOutlineParts: OutlineParts;
normalList, hotList: LIST OF Sequence;
normalOutlineParts ← NARROW[selectedParts];
hotOutlineParts ← NARROW[hotParts];
IF caretIsMoving OR dragInProgress THEN RETURN;
IF selectedParts = NIL AND hotParts = NIL THEN RETURN;
IF selectedParts #
NIL
AND hotParts #
NIL
THEN {
hotList ← hotOutlineParts.seqs;
FOR normalList ← normalOutlineParts.seqs, normalList.rest
UNTIL normalList =
NIL
DO
IF normalList.first # NIL THEN GGTraj.DrawSelectionFeedback[normalList.first.traj, normalList.first, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]
ELSE IF hotList.first # NIL THEN GGTraj.DrawSelectionFeedback[hotList.first.traj, normalList.first, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
hotList ← hotList.rest;
ENDLOOP;
}
ELSE
IF selectedParts #
NIL
THEN {
FOR normalList ← normalOutlineParts.seqs, normalList.rest
UNTIL normalList =
NIL
DO
IF normalList.first # NIL THEN GGTraj.DrawSelectionFeedback[normalList.first.traj, normalList.first, NIL, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
ENDLOOP;
}
ELSE {
FOR hotList ← hotOutlineParts.seqs, hotList.rest
UNTIL hotList =
NIL
DO
IF hotList.first # NIL THEN GGTraj.DrawSelectionFeedback[hotList.first.traj, NIL, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
ENDLOOP;
};
};
DrawCpsAndJoints:
PROC [dc: Imager.Context, seg: Segment] = {
point: Point;
GGShapes.DrawJoint[dc, seg.lo];
GGShapes.DrawJoint[dc, seg.hi];
FOR j:
INT
IN [0..seg.class.controlPointCount[seg])
DO
point ← seg.class.controlPointGet[seg, j];
GGShapes.DrawCP[dc, point];
ENDLOOP;
};
OutlineDrawAttractorFeedback:
PUBLIC
PROC [sliceD: OutlineDescriptor, selectedParts: SliceParts, dragInProgress:
BOOL, dc: Imager.Context, camera: CameraData] = {
success: BOOL;
partType: TrajPartType;
traj: Traj;
seg: Segment;
jointNum: NAT;
[success, partType, traj, ----, jointNum, ----, ----, seg] ← UnpackSimpleDescriptorOld[sliceD];
SELECT partType
FROM
joint => {
IF jointNum > 0 THEN DrawCpsAndJoints[dc, GGTraj.FetchSegment[traj, jointNum-1]];
IF jointNum < traj.segCount THEN DrawCpsAndJoints[dc, GGTraj.FetchSegment[traj, jointNum]];
};
segment, controlPoint => DrawCpsAndJoints[dc, seg];
ENDCASE; -- none
};
Transforming
OutlineTransform:
PUBLIC
PROC [slice: Outline, parts: SliceParts, transform: ImagerTransformation.Transformation] = {
outlineParts: OutlineParts ← NARROW[parts];
transformedColor: Color;
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN GGTraj.TransformSequence[list.first, transform];
ENDLOOP;
UpdateOutlineBoundBox[slice];
IF slice.fillColor
# NIL THEN {
WITH slice.fillColor
SELECT
FROM
cc: ImagerColor.ConstantColor => {};
sc: ImagerColor.SampledColor => {
IF OutlineCompleteParts[slice, parts]
THEN {
transformedColor ← ImagerColor.MakeSampledColor[pa: sc.pa, um: ImagerTransformation.Concat[sc.um, transform], colorOperator: sc.colorOperator];
slice.fillColor ← transformedColor;
};
};
ENDCASE => ERROR;
};
};
Textual Description
OneSequenceOnly:
PROC [realParts: OutlineParts]
RETURNS [theSeq: Sequence] = {
theSeq ← NIL;
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF theSeq # NIL THEN RETURN[NIL]
ELSE theSeq ← list.first;
};
ENDLOOP;
};
OutlineDescribe:
PROC [slice: Outline, parts: SliceParts]
RETURNS [rope: Rope.
ROPE] = {
realParts: OutlineParts ← NARROW[parts];
theSeq: Sequence;
IF (theSeq ← OneSequenceOnly[realParts]) #
NIL THEN {
rope ← DescribeSequence[theSeq];
}
ELSE {
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN RETURN["several parts of a multi-trajectory outline"];
ENDLOOP;
rope ← "No outline parts to speak of.";
};
};
DescribeSequence:
PROC [seq: Sequence]
RETURNS [rope: Rope.
ROPE] = {
segNum, cpNum: NAT;
segCount: NAT ← seq.segCount;
jointCount: NAT ← seq.jointCount;
cpCount: NAT ← seq.controlPointCount;
SELECT
TRUE
FROM
segCount=1 => {
-- single segment selected. Describe it
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
rope ← GGDescribe.
DescribeSegment[seq.traj,
GGSequence.NextSegmentAndIndex[segGen].index];
};
segCount=0
AND cpCount=1 => {
-- single CP selected. Describe it
cpGen: ControlPointGenerator ← GGSequence.ControlPointsInSequence[seq];
[segNum, cpNum] ← GGSequence.NextSegNumAndCPNum[cpGen];
rope ← GGDescribe.DescribeControlPoint[seq.traj, segNum, cpNum];
};
segCount=0
AND cpCount=0
AND jointCount=1 => {
-- single joint selected. Describe it
jointGen: JointGenerator ← GGSequence.JointsInSequence[seq];
rope ← GGDescribe.DescribeJoint[seq.traj, GGSequence.NextJoint[jointGen]];
};
ENDCASE => rope ← GGDescribe.DescribeSequence[seq];
};
Parts
OutlineMovingParts:
PROC [slice: Outline, parts: SliceParts]
RETURNS [moving: SliceParts] = {
outlineParts: OutlineParts ← NARROW[parts];
moveParts: OutlineParts;
ptr: LIST OF Sequence;
movingSeq: Sequence;
moving ← moveParts ← NEW[OutlinePartsObj];
[moveParts.seqs, ptr] ← GGUtility.StartSequenceList[];
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first =
NIL
THEN {
[moveParts.seqs, ptr] ← GGUtility.AddSequence[NIL, moveParts.seqs, ptr]
}
ELSE {
movingSeq ← MovingSequence[list.first];
[moveParts.seqs, ptr] ← GGUtility.AddSequence[movingSeq, moveParts.seqs, ptr];
};
ENDLOOP;
};
OutlineFixedParts:
PROC [slice: Outline, parts: SliceParts, selectedList:
LIST
OF
REF
ANY]
RETURNS [fixed: SliceParts] = {
realParts: OutlineParts;
seq: Sequence;
fixed ← realParts ←
NARROW[parts];
Trim away the moving parts.
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
seq ← list.first;
IF seq #
NIL
THEN {
exclude segments with moving cps
GGSequence.TrimSelectedControlPointSegments[seq, selectedList];
exclude segments with moving joints
GGSequence.TrimSelectedJointSegments[seq, selectedList];
exclude all directly moving parts
GGSequence.TrimSelectedParts[seq, selectedList];
};
ENDLOOP;
};
MovingSequence:
PROC [seq: Sequence]
RETURNS [movingSeq: Sequence] = {
completeSeq: Sequence ← GGSequence.CreateComplete[seq.traj];
stationarySeq: Sequence ← GGSequence.Copy[completeSeq];
Segments with selected control points are not stationary
GGSequence.DDifference[stationarySeq, seq]; -- remove everything that is directly moving
GGSequence.TrimControlPointSegments[stationarySeq, FALSE]; -- remove all segments with a directly moving control point.
GGSequence.TrimJointSegments[stationarySeq, FALSE]; -- remove all segments with a directly moving joint
movingSeq ← GGSequence.Difference[completeSeq, stationarySeq]; -- Difference calculates the moving parts
};
OutlineEmptyParts:
PROC [slice: Outline, parts: SliceParts]
RETURNS [
BOOL] = {
realParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL AND NOT GGSequence.IsEmpty[list.first] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
OutlineCompleteParts:
PROC [slice: Outline, parts: SliceParts]
RETURNS [
BOOL] = {
realParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first = NIL THEN RETURN[FALSE];
IF NOT GGSequence.IsComplete[list.first] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
NearestJointToHitSpot:
PUBLIC
PROC [caretPt: Point, traj: Traj, hitJointNum:
INT, hitSegNum:
INT, hitType: HitType]
RETURNS [jointNum:
NAT] = {
nextNum: NAT;
p1, p2: Point;
d1, d2: REAL;
SELECT hitType
FROM
joint => {
jointNum ← hitJointNum;
};
segment, controlPoint => {
nextNum ← GGTraj.FollowingJoint[traj, hitSegNum];
p1 ← GGTraj.FetchJointPos[traj, hitSegNum];
p2 ← GGTraj.FetchJointPos[traj, nextNum];
d1 ← GGVector.DistanceSquared[p1, caretPt];
d2 ← GGVector.DistanceSquared[p2, caretPt];
IF d1 <= d2 THEN jointNum ← hitSegNum
ELSE jointNum ← nextNum;
};
ENDCASE => ERROR Problem[msg: "not yet implemented"];
}; -- end of NearestJointToHitSpot
OutlineNewParts:
PROC [slice: Outline, hitData:
REF
ANY, mode: SelectMode]
RETURNS [parts: SliceParts] = {
realParts: OutlineParts;
ptr: LIST OF Sequence;
trajGen: GGModelTypes.TrajGenerator;
parts ← realParts ← NEW[OutlinePartsObj];
[realParts.seqs, ptr] ← GGUtility.StartSequenceList[];
SELECT mode
FROM
none => {
outlineHitData: OutlineHitData ← NARROW[hitData];
SELECT outlineHitData.hitType
FROM
joint, controlPoint => parts ← OutlineNewParts[slice, hitData, joint];
segment => parts ← OutlineNewParts[slice, hitData, segment];
ENDCASE => ERROR;
};
slice => {
completeSeq: Sequence;
trajGen ← TrajsInOutline[slice];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
completeSeq ← GGSequence.CreateComplete[traj];
[realParts.seqs, ptr] ← GGUtility.AddSequence[completeSeq, realParts.seqs, ptr];
ENDLOOP;
};
joint => {
outlineHitData: OutlineHitData ← NARROW[hitData];
SELECT outlineHitData.hitType
FROM
joint => {
-- hit a joint
traj: Traj ← outlineHitData.traj;
jointNum: NAT ← outlineHitData.jointNum;
seq: Sequence ← GGSequence.CreateJointToJoint[traj, jointNum, jointNum];
parts ← PartsFromSequence[slice, seq];
};
segment => {
-- we are in the middle of a segment. Select nearest joint or cp
success: BOOL;
segNum: NAT;
cpNum, jointNum: NAT;
jointPoint, cpPoint, caretPt: Point;
traj: Traj;
seg: Segment;
seq: Sequence;
traj ← outlineHitData.traj;
segNum ← outlineHitData.segNum;
seg ← GGTraj.FetchSegment[traj, segNum];
caretPt ← outlineHitData.hitPoint;
jointNum ← NearestJointToHitSpot[caretPt, traj, -1, segNum, segment];
jointPoint ← GGTraj.FetchJointPos[traj, jointNum];
[cpPoint, cpNum, success] ← seg.class.closestControlPoint[seg, caretPt, GGUtility.plusInfinity];
IF NOT success THEN seq ← GGSequence.CreateJointToJoint[traj, jointNum, jointNum]
ELSE {
cpDist: REAL ← GGVector.DistanceSquared[cpPoint, caretPt];
jointDist: REAL ← GGVector.DistanceSquared[jointPoint, caretPt];
seq ← IF cpDist < jointDist THEN GGSequence.CreateFromControlPoint[traj, segNum, cpNum]
ELSE GGSequence.CreateJointToJoint[traj, jointNum, jointNum];
};
parts ← PartsFromSequence[slice, seq];
};
controlPoint => {
traj: Traj;
segNum, cpNum: NAT;
controlPointSeq: Sequence;
traj ← outlineHitData.traj;
segNum ← outlineHitData.segNum;
cpNum ← outlineHitData.cpNum;
controlPointSeq ← GGSequence.CreateFromControlPoint[traj, segNum, cpNum];
parts ← PartsFromSequence[slice, controlPointSeq];
};
ENDCASE => ERROR;
};
segment => {
outlineHitData: OutlineHitData ← NARROW[hitData];
SELECT outlineHitData.hitType
FROM
joint => {
parts ← CreateEmptyParts[slice];
};
segment, controlPoint => {
seq: Sequence;
seq ← GGSequence.CreateFromSegment[outlineHitData.traj, outlineHitData.segNum];
parts ← PartsFromSequence[slice, seq];
};
ENDCASE => ERROR;
};
traj => {
outlineHitData: OutlineHitData ← NARROW[hitData];
traj: Traj ← outlineHitData.traj;
seq: Sequence ← GGSequence.CreateComplete[traj];
parts ← PartsFromSequence[slice, seq];
};
topLevel => {
parts ← slice.class.newParts[slice, NIL, slice];
};
ENDCASE => ERROR;
};
CreateEmptyParts:
PROC [slice: Outline]
RETURNS [empty: SliceParts] = {
realParts: OutlineParts;
empty ← realParts ← NEW[OutlinePartsObj];
realParts.seqs ← NIL;
FOR list:
LIST
OF Traj ← slice.children, list.rest
UNTIL list =
NIL
DO
realParts.seqs ← CONS[NIL, realParts.seqs];
ENDLOOP;
};
OutlineUnionParts:
PROC [slice: Outline, partsA: SliceParts, partsB: SliceParts]
RETURNS [aPlusB: SliceParts] = {
realPartsA: OutlineParts ← NARROW[partsA];
realPartsB: OutlineParts ← NARROW[partsB];
union: OutlineParts;
listA: LIST OF Sequence ← realPartsA.seqs;
listB: LIST OF Sequence ← realPartsB.seqs;
ptr: LIST OF Sequence;
aPlusB ← union ← NEW[OutlinePartsObj];
[union.seqs, ptr] ← GGUtility.StartSequenceList[];
UNTIL listA =
NIL
DO
IF listA.first = NIL THEN [union.seqs, ptr] ← GGUtility.AddSequence[listB.first, union.seqs, ptr]
ELSE IF listB.first = NIL THEN [union.seqs, ptr] ← GGUtility.AddSequence[listA.first, union.seqs, ptr]
ELSE {
newSeq: Sequence ← GGSequence.Union[listA.first, listB.first];
[union.seqs, ptr] ← GGUtility.AddSequence[newSeq, union.seqs, ptr];
};
listA ← listA.rest;
listB ← listB.rest;
ENDLOOP;
};
OutlineDifferenceParts:
PROC [slice: Outline, partsA: SliceParts, partsB: SliceParts]
RETURNS [aMinusB: SliceParts] = {
realPartsA: OutlineParts ← NARROW[partsA];
realPartsB: OutlineParts ← NARROW[partsB];
diff: OutlineParts;
listA: LIST OF Sequence ← realPartsA.seqs;
listB: LIST OF Sequence ← realPartsB.seqs;
ptr: LIST OF Sequence;
aMinusB ← diff ← NEW[OutlinePartsObj];
[diff.seqs, ptr] ← GGUtility.StartSequenceList[];
UNTIL listA =
NIL
DO
IF listA.first = NIL THEN [diff.seqs, ptr] ← GGUtility.AddSequence[NIL, diff.seqs, ptr]
ELSE IF listB.first = NIL THEN [diff.seqs, ptr] ← GGUtility.AddSequence[listA.first, diff.seqs, ptr]
ELSE {
newSeq: Sequence ← GGSequence.Difference[listA.first, listB.first];
[diff.seqs, ptr] ← GGUtility.AddSequence[newSeq, diff.seqs, ptr];
};
listA ← listA.rest;
listB ← listB.rest;
ENDLOOP;
};
OutlineAugmentParts:
PROC [slice: Outline, parts: SliceParts, selectClass: SelectionClass]
RETURNS [more: SliceParts] = {
If selectClass = normal, then for each segment mentioned in parts, place it, its adjacent joints, and its control points in more. If selectClass # normal then just place each segment and its control points in more.
sliceParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← sliceParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN SequenceAugmentParts[list.first, selectClass];
ENDLOOP;
more ← parts;
};
SequenceAugmentParts:
PROC [seq: Sequence, selectClass: SelectionClass] = {
Selected sequences must satisfy the requirement that if a segment is selected its joints and control points are as well.
IF selectClass = normal
THEN {
GGSequence.FillInJoints[seq];
GGSequence.FillInControlPoints[seq];
}
ELSE {
GGSequence.FillInControlPoints[seq];
};
};
OutlineSetSelectedFields:
PROC [sliceD: OutlineDescriptor, selected:
BOOL, selectClass: SelectionClass] = {
Set the selected fields of all of the joints and segments mentioned in sliceD.
sliceParts: OutlineParts ← NARROW[sliceD.parts];
FOR list:
LIST
OF Sequence ← sliceParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN SequenceSetSelectedFields[list.first, selected, selectClass];
ENDLOOP;
};
SequenceSetSelectedFields:
PROC [seq: Sequence, selected:
BOOL, selectClass: SelectionClass] = {
joint: Joint;
jointGen: JointGenerator;
segGen: SegmentGenerator;
SetTrajPartField[seq.traj, selected, selectClass];
jointGen ← GGSequence.JointsInSequence[seq];
Joint Fields.
FOR jointNum:
INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen]
UNTIL jointNum = -1
DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, jointNum]];
SetJointField[joint, selected, selectClass];
ENDLOOP;
Segment Fields.
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
SetSegmentField[seg, selected, selectClass];
ENDLOOP;
};
SetTrajPartField:
PROC [traj: Traj, selected:
BOOL, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => traj.selectedInPart.normal ← selected;
hot => traj.selectedInPart.hot ← selected;
active => traj.selectedInPart.active ← selected;
ENDCASE;
};
SetJointField:
PROC [joint: Joint, selected:
BOOL, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => joint.TselectedInFull.normal ← selected;
hot => joint.TselectedInFull.hot ← selected;
active => joint.TselectedInFull.active ← selected;
ENDCASE;
};
SetSegmentField:
PROC [seg: Segment, selected:
BOOL, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => seg.TselectedInFull.normal ← selected;
hot => seg.TselectedInFull.hot ← selected;
active => seg.TselectedInFull.active ← selected;
ENDCASE;
};
SetControlPointField:
PROC [seg: Segment, cpNum:
NAT, selected:
BOOL, selectClass: SelectionClass] = {
seg.class.controlPointFieldSet[seg, cpNum, selected, selectClass];
};
GetControlPointField:
PROC [seg: Segment, cpNum:
NAT, selectClass: SelectionClass]
RETURNS [selected:
BOOL] = {
selected ← seg.class.controlPointFieldGet[seg, cpNum, selectClass];
};
GetJointField:
PROC [joint: Joint, selectClass: SelectionClass]
RETURNS [selected:
BOOL] = {
SELECT selectClass
FROM
normal => selected ← joint.TselectedInFull.normal;
hot => selected ← joint.TselectedInFull.hot;
active => selected ← joint.TselectedInFull.active;
ENDCASE;
};
GetSegmentField:
PROC [seg: Segment, selectClass: SelectionClass]
RETURNS [selected:
BOOL] = {
SELECT selectClass
FROM
normal => selected ← seg.TselectedInFull.normal;
hot => selected ← seg.TselectedInFull.hot;
active => selected ← seg.TselectedInFull.active;
ENDCASE;
};
Hit Testing
HitType: TYPE = GGModelTypes.TrajPartType;
OutlineHitData: TYPE = REF OutlineHitDataObj;
OutlineHitDataObj:
TYPE =
RECORD [
traj: Traj,
hitType: HitType,
segNum: INT,
cpNum: INT,
jointNum: INT,
hitPoint: Point
];
PointGeneratorData: TYPE = REF PointGeneratorDataObj;
PointGeneratorDataObj:
TYPE =
RECORD [
seqPtr: LIST OF Sequence,
pointGen: GGModelTypes.PointGenerator -- the point generator for the first sequence of seqPtr
];
PointPairGeneratorData: TYPE = REF PointPairGeneratorDataObj;
PointPairGeneratorDataObj:
TYPE =
RECORD [
seqPtr: LIST OF Sequence,
pointGen: GGModelTypes.PointPairGenerator -- the pair generator for first sequence of seqPtr
];
OutlinePointsInDescriptor:
PUBLIC
PROC [sliceD: OutlineDescriptor]
RETURNS [pointGen: GGModelTypes.OutlinePointGenerator] = {
realParts: OutlineParts ← NARROW[sliceD.parts];
seqPtr: LIST OF Sequence ← realParts.seqs;
trajPointGen: GGModelTypes.PointGenerator;
pointGenData: PointGeneratorData;
trajPointGen ← IF seqPtr.first = NIL THEN NIL ELSE GGTraj.PointsInDescriptor[seqPtr.first];
pointGenData ← NEW[PointGeneratorDataObj ← [seqPtr: seqPtr, pointGen: trajPointGen]];
pointGen ← NEW[GGModelTypes.OutlinePointGeneratorObj ← [sliceD, 0, 0, pointGenData]];
};
OutlinePointPairsInDescriptor:
PUBLIC
PROC [sliceD: OutlineDescriptor]
RETURNS [pointPairGen: GGModelTypes.OutlinePointPairGenerator] = {
realParts: OutlineParts ← NARROW[sliceD.parts];
seqPtr: LIST OF Sequence ← realParts.seqs;
trajPointGen: GGModelTypes.PointPairGenerator;
pointGenData: PointPairGeneratorData;
trajPointGen ← IF seqPtr.first = NIL THEN NIL ELSE GGTraj.PointPairsInDescriptor[seqPtr.first];
pointGenData ← NEW[PointPairGeneratorDataObj ← [seqPtr: seqPtr, pointGen: trajPointGen]];
pointPairGen ← NEW[GGModelTypes.OutlinePointPairGeneratorObj ← [sliceD, 0, 0, pointGenData]];
};
OutlineNextPoint:
PUBLIC
PROC [pointGen: GGModelTypes.OutlinePointGenerator]
RETURNS [pointAndDone: GGModelTypes.PointAndDone] = {
pgd: PointGeneratorData ← NARROW[pointGen.classSpecific];
seqPtr: LIST OF Sequence ← pgd.seqPtr;
IF seqPtr = NIL THEN RETURN[[[0,0], TRUE]];
WHILE
TRUE
DO
IF seqPtr.first #
NIL
THEN {
pointAndDone ← GGTraj.NextPoint[pgd.pointGen];
IF
NOT pointAndDone.done
THEN
{
pgd.seqPtr ← seqPtr;
RETURN;
};
seqPtr ← seqPtr.rest; -- on to the next sequence
};
UNTIL seqPtr = NIL OR seqPtr.first # NIL DO seqPtr ← seqPtr.rest ENDLOOP;
IF seqPtr = NIL THEN RETURN[[[0,0], TRUE]];
pgd.pointGen ← GGTraj.PointsInDescriptor[seqPtr.first];
ENDLOOP;
};
OutlineNextPointPair:
PUBLIC
PROC [pointPairGen: GGModelTypes.OutlinePointPairGenerator]
RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = {
pgd: PointPairGeneratorData ← NARROW[pointPairGen.classSpecific];
seqPtr: LIST OF Sequence ← pgd.seqPtr;
IF seqPtr = NIL THEN RETURN[[[0,0], [0,0], TRUE]];
WHILE
TRUE
DO
IF seqPtr.first #
NIL
THEN {
pointPairAndDone ← GGTraj.NextPointPair[pgd.pointGen];
IF
NOT pointPairAndDone.done
THEN
{
pgd.seqPtr ← seqPtr;
RETURN;
};
seqPtr ← seqPtr.rest; -- on to the next sequence
};
UNTIL seqPtr = NIL OR seqPtr.first # NIL DO seqPtr ← seqPtr.rest ENDLOOP;
IF seqPtr = NIL THEN RETURN[[[0,0], [0,0], TRUE]];
pgd.pointGen ← GGTraj.PointPairsInDescriptor[seqPtr.first];
ENDLOOP;
};
GoodPointType: TYPE = {joint, intersectionPoint, midpoint, controlPoint, slice, none};
OutlineClosestPoint:
PROC [sliceD: OutlineDescriptor, testPoint: Point, tolerance:
REAL]
RETURNS [bestPoint: Point, bestDist:
REAL, hitData:
REF
ANY, success:
BOOL] = {
parts: OutlineParts ← NARROW[sliceD.parts];
thisDist: REAL;
thisSegNum, bestSegNum, thisCP, bestCP, thisJointNum, bestJointNum: NAT;
thisPoint: Point;
thisSuccess: BOOL;
bestTraj: Traj;
bestType: GoodPointType ← none;
outlineHitData: OutlineHitData;
success ← FALSE;
bestDist ← GGUtility.plusInfinity;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
[thisDist, thisSegNum, thisCP, thisPoint, thisSuccess] ← GGTraj.NearestControlPoint[testPoint, list.first, tolerance];
IF thisSuccess
AND thisDist < bestDist
THEN {
bestType ← controlPoint;
bestPoint ← thisPoint;
bestDist ← thisDist;
bestTraj ← list.first.traj;
bestSegNum ← thisSegNum;
bestCP ← thisCP;
success ← TRUE;
};
ENDLOOP;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
[thisDist, thisJointNum, thisPoint, thisSuccess] ← GGTraj.NearestJoint[testPoint, list.first, tolerance];
IF thisSuccess
AND thisDist < bestDist
THEN {
bestType ← joint;
bestPoint ← thisPoint;
bestDist ← thisDist;
bestTraj ← list.first.traj;
bestJointNum ← thisJointNum;
success ← TRUE;
};
ENDLOOP;
IF success
THEN {
SELECT bestType FROM
controlPoint => {
hitData ← outlineHitData ←
NEW[OutlineHitDataObj ← [
bestTraj, controlPoint, bestSegNum, bestCP, -1, bestPoint]];
};
joint => {
hitData ← outlineHitData ←
NEW[OutlineHitDataObj ← [
bestTraj, joint, -1, -1, bestJointNum, bestPoint]];
};
ENDCASE => ERROR;
};
};
OutlineClosestSegment:
PUBLIC
PROC [sliceD: OutlineDescriptor, testPoint: Point, tolerance:
REAL]
RETURNS [bestPoint: Point, bestDist:
REAL, hitData:
REF
ANY, success:
BOOL] = {
parts: OutlineParts ← NARROW[sliceD.parts];
thisDist: REAL;
thisSegNum, bestSegNum: NAT;
thisPoint: Point;
thisSuccess: BOOL;
bestTraj: Traj;
outlineHitData: OutlineHitData;
success ← FALSE;
bestDist ← GGUtility.plusInfinity;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
[thisDist, thisSegNum, thisPoint, thisSuccess] ← GGTraj.NearestSegment[testPoint, list.first, tolerance];
IF thisSuccess
AND thisDist < bestDist
THEN {
bestPoint ← thisPoint;
bestDist ← thisDist;
bestTraj ← list.first.traj;
bestSegNum ← thisSegNum;
success ← TRUE;
};
ENDLOOP;
IF success
THEN {
hitData ← outlineHitData ←
NEW[OutlineHitDataObj ← [
bestTraj, segment, bestSegNum, -1, -1, bestPoint]];
};
};
OutlineLineIntersection:
PUBLIC
PROC [sliceD: OutlineDescriptor, line: Line]
RETURNS [points:
LIST
OF Point, pointCount:
NAT] = {
Finds the intersection of the line with those slice parts mentioned in sliceD.
segGen: GGModelTypes.SegmentGenerator;
thesePoints: LIST OF Point;
thisCount: NAT;
seq: Sequence;
parts: OutlineParts ← NARROW[sliceD.parts];
points ← NIL;
pointCount ← 0;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
seq ← list.first;
IF seq = NIL THEN LOOP;
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
[thesePoints, thisCount] ← seg.class.lineIntersection[seg, line];
FOR list:
LIST
OF Point ← thesePoints, list.rest
UNTIL list =
NIL
DO
points ← CONS[list.first, points];
ENDLOOP;
pointCount ← pointCount + thisCount;
ENDLOOP;
ENDLOOP;
};
OutlineCircleIntersection:
PROC [sliceD: OutlineDescriptor, circle: Circle]
RETURNS [points:
LIST
OF Point, pointCount:
NAT] = {
segGen: GGModelTypes.SegmentGenerator;
thesePoints: LIST OF Point;
thisCount: NAT;
seq: Sequence;
parts: OutlineParts ← NARROW[sliceD.parts];
points ← NIL;
pointCount ← 0;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
seq ← list.first;
IF seq = NIL THEN LOOP;
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
[thesePoints, thisCount] ← seg.class.circleIntersection[seg, circle];
FOR list:
LIST
OF Point ← thesePoints, list.rest
UNTIL list =
NIL
DO
points ← CONS[list.first, points];
ENDLOOP;
ENDLOOP;
pointCount ← pointCount + thisCount;
ENDLOOP;
};
OutlineHitDataAsSimpleCurve:
PROC [slice: Outline, hitData:
REF
ANY]
RETURNS [simpleCurve:
REF
ANY] = {
simpleCurve will be of type Edge, Arc, etc. There will be Conic and Bezier types as well.
outlineHitData: OutlineHitData ← NARROW[hitData];
traj: Traj ← outlineHitData.traj;
SELECT outlineHitData.hitType
FROM
joint, controlPoint => RETURN[NIL];
segment => {
segNum: INT ← outlineHitData.segNum;
seg: Segment ← GGTraj.FetchSegment[traj, segNum];
simpleCurve ← seg.class.asSimpleCurve[seg, outlineHitData.hitPoint];
};
ENDCASE => ERROR;
};
Style
OutlineSetStrokeWidth:
PROC [slice: Outline, parts: SliceParts, strokeWidth:
REAL] = {
Sets the stroke width of the named parts of slice to be strokeWidth.
realParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN GGSequence.SetStrokeWidth[list.first, strokeWidth];
ENDLOOP;
};
OutlineGetStrokeWidth:
PROC [slice: Outline, parts: SliceParts]
RETURNS [strokeWidth:
REAL] = {
Get the stroke width of the first segment you come to.
realParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN RETURN[GGSequence.GetStrokeWidth[list.first]];
ENDLOOP;
ERROR Problem[msg: "No segments to get a stroke width from"];
};
OutlineSetStrokeColor:
PROC [slice: Outline, parts: SliceParts, color: Imager.Color] = {
Sets the stroke color of the named parts of slice to be color.
realParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN SequenceSetStrokeColor[list.first, color];
ENDLOOP;
};
SequenceSetStrokeColor:
PROC [seq: Sequence, color: Imager.Color] = {
segGen: SegmentGenerator;
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
seg.color ← color;
ENDLOOP;
};
OutlineGetStrokeColor:
PROC [slice: Outline, parts: SliceParts]
RETURNS [color: Imager.Color] = {
Get the stroke color of the named parts of slice.
realParts: OutlineParts ← NARROW[parts];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN RETURN[SequenceGetStrokeColor[list.first]];
ENDLOOP;
ERROR Problem[msg: "No segments to get a stroke width from"];
};
SequenceGetStrokeColor:
PROC [seq: Sequence]
RETURNS [color: Imager.Color] = {
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
seg: Segment ← GGSequence.NextSegment[segGen];
IF seg#NIL THEN color ← seg.color;
};
Outline
SetFillColor:
PROC [slice: Outline, color: Imager.Color] = {
Sets the fill color of the slice to be color.
slice.fillColor ← color
};
OutlineGetFillColor:
PROC [slice: Outline]
RETURNS [color: Imager.Color] = {
Get the fill color of the slice.
color ← slice.fillColor;
};
Outline-Only operations.
UnpackOnePointDescriptorOld:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [traj: Traj, isACP:
BOOL, segNum, cpNum, jointNum:
NAT] = {
outlineD describes either a single control point or a single joint. Return the [segNum, cpNum] pair or the jointNum as appropriate.
parts: OutlineParts;
theSeq: Sequence;
IF outlineD = NIL THEN RETURN[NIL, FALSE, 999, 999, 999];
parts ← NARROW[outlineD.parts];
traj ← NIL;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF traj # NIL THEN RETURN[NIL, FALSE, 999, 999, 999]
ELSE {
theSeq ← list.first;
traj ← theSeq.traj;
[isACP, segNum, cpNum, jointNum] ← GGSequence.UnpackOnePointSequence[theSeq];
};
};
ENDLOOP;
};
UnpackSimpleDescriptorOld:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [success:
BOOL, partType: TrajPartType, traj: Traj, joint: Joint ←
NIL, jointNum:
NAT ← 999, cp: Point, cpNum:
NAT ← 999, seg: Segment ←
NIL, segNum:
NAT ← 999] = {
parts: OutlineParts;
theSeq: Sequence;
IF outlineD = NIL THEN RETURN[FALSE, joint, NIL, NIL, 999, [0,0]];
parts ← NARROW[outlineD.parts];
traj ← NIL;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF traj # NIL THEN RETURN[FALSE, joint, NIL, NIL, 999, [0,0]]
ELSE {
theSeq ← list.first;
traj ← theSeq.traj;
[success, partType, traj, joint, jointNum, cp, cpNum, seg, segNum] ← GGSequence.UnpackSimpleSequence[theSeq];
};
};
ENDLOOP;
};
UnpackSimpleDescriptor:
PUBLIC
PROC [sliceD: SliceDescriptor]
RETURNS [success:
BOOL, partType: TrajPartType, traj: Traj, joint: Joint ←
NIL, jointNum:
NAT ← 999, cp: Point, cpNum:
NAT ← 999, seg: Segment ←
NIL, segNum:
NAT ← 999] = {
ERROR Problem[msg: "Outlines are not slices yet"];
};
UnpackOnePointDescriptor:
PUBLIC
PROC [sliceD: SliceDescriptor]
RETURNS [traj: Traj, isACP:
BOOL, segNum, cpNum, jointNum:
NAT] = {
ERROR Problem[msg: "Outlines are not slices yet"];
};
UnpackOneSegmentDescriptorOld:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [traj: Traj, segNum:
NAT] = {
outlineD describes either a single control point or a single joint. Return the [segNum, cpNum] pair or the jointNum as appropriate.
parts: OutlineParts;
theSeq: Sequence;
IF outlineD = NIL THEN RETURN[NIL, 999];
parts ← NARROW[outlineD.parts];
traj ← NIL;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF traj # NIL THEN RETURN[NIL, 999]
ELSE {
theSeq ← list.first;
traj ← theSeq.traj;
[segNum] ← GGSequence.UnpackOneSegmentSequence[theSeq];
};
};
ENDLOOP;
};
UnpackOneSegmentDescriptor:
PUBLIC
PROC [sliceD: SliceDescriptor]
RETURNS [traj: Traj, segNum:
NAT] = {
ERROR Problem[msg: "Outlines are not slices yet"];
};
FindTrajInDescriptor:
PUBLIC
PROC [outlineD: OutlineDescriptor, traj: Traj]
RETURNS [seq: Sequence] = {
outlineParts: OutlineParts;
outlineParts ← NARROW[outlineD.parts];
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL AND list.first.traj = traj THEN RETURN[list.first];
ENDLOOP;
RETURN[NIL];
};
PartsFromSequence:
PUBLIC PROC [outline: Outline, seq: Sequence]
RETURNS [sliceParts: SliceParts] = {
realParts: OutlineParts;
ptr: LIST OF Sequence;
trajGen: GGModelTypes.TrajGenerator;
sliceParts ← realParts ← NEW[OutlinePartsObj];
[realParts.seqs, ptr] ← GGUtility.StartSequenceList[];
trajGen ← TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
IF traj = seq.traj THEN [realParts.seqs, ptr] ← GGUtility.AddSequence[seq, realParts.seqs, ptr]
ELSE [realParts.seqs, ptr] ← GGUtility.AddSequence[NIL, realParts.seqs, ptr];
ENDLOOP;
};
CopyParts:
PUBLIC
PROC [outline: Outline, parts: SliceParts]
RETURNS [copy: SliceParts] = {
realParts, realCopyParts: OutlineParts;
copySeq: Sequence;
ptr: LIST OF Sequence;
realParts ← NARROW[parts];
copy ← realCopyParts ← NEW[OutlinePartsObj];
[realCopyParts.seqs, ptr] ← GGUtility.StartSequenceList[];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first = NIL THEN [realCopyParts.seqs, ptr] ← GGUtility.AddSequence[NIL, realCopyParts.seqs, ptr]
ELSE {
copySeq ← GGSequence.Copy[list.first];
[realCopyParts.seqs, ptr] ← GGUtility.AddSequence[copySeq, realCopyParts.seqs, ptr]
};
ENDLOOP;
};
AddHole:
PUBLIC
PROC [outline: Outline, hole: Traj]
RETURNS [holier: Outline] = {
Holes can be removed using DeleteSequence.
Should check for duplicates. -- Bier, February 20
holier ← outline.class.copy[outline];
holier.children ← AppendTrajList[holier.children, LIST[hole]];
hole.parent ← holier;
hole.role ← hole;
UpdateOutlineBoundBox[holier];
};
ReplaceFence:
PUBLIC PROC [outline: Outline, newFence: Traj]
RETURNS [newOutline: Outline] = {
Like CopyOutline except in one place.
holeGen: TrajGenerator;
newTraj: Traj;
newOutline ← CreateOutline[newFence, outline.lineEnds, outline.fillColor];
holeGen ← HolesOfOutline[outline];
FOR hole: Traj ← GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen]
UNTIL hole =
NIL
DO
newTraj ← GGTraj.CopyTraj[hole];
newOutline.children ← AppendTrajList[newOutline.children, LIST[newTraj]];
newTraj.parent ← newOutline;
ENDLOOP;
UpdateOutlineBoundBox[newOutline];
};
ReplaceHole:
PUBLIC PROC [outline: Outline, oldHole, newHole: Traj]
RETURNS [newOutline: Outline] = {
Like CopyOutline except in one place.
holeGen: TrajGenerator;
newTraj, fence: Traj;
fence ← FenceOfOutline[outline];
newTraj ← GGTraj.CopyTraj[fence];
newOutline ← CreateOutline[newTraj, outline.lineEnds, outline.fillColor];
holeGen ← HolesOfOutline[outline];
FOR hole: Traj ← GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen]
UNTIL hole =
NIL
DO
IF hole = oldHole
THEN {
newOutline.children ← AppendTrajList[newOutline.children, LIST[newHole]];
newHole.parent ← newOutline;
}
ELSE {
newTraj ← GGTraj.CopyTraj[hole];
newOutline.children ← AppendTrajList[newOutline.children, LIST[newTraj]];
newTraj.parent ← newOutline;
};
ENDLOOP;
UpdateOutlineBoundBox[newOutline];
};
OutlineOfTraj:
PUBLIC
PROC [traj: Traj]
RETURNS [outline: Outline] = {
Finds the unique outline to which traj belongs.
outline ← traj.parent;
};
SaveSelectionsInOutline:
PUBLIC
PROC [outline: Outline, scene: Scene] = {
outlineD: OutlineDescriptor;
outlineParts: OutlineParts;
hotness.
outlineD ← GGSelect.FindSelectedOutline[outline, scene, hot];
ClearSelectionInOutline[outline, hot];
IF outlineD #
NIL
THEN {
outlineParts ← NARROW[outlineD.parts];
FOR seqList: LIST OF Sequence ← outlineParts.seqs, seqList.rest
UNTIL seqList =
NIL
DO
IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, hot];
ENDLOOP;
};
normal selection.
outlineD ← GGSelect.FindSelectedOutline[outline, scene, normal];
ClearSelectionInOutline[outline, normal];
IF outlineD #
NIL
THEN {
outlineParts ← NARROW[outlineD.parts];
FOR seqList: LIST OF Sequence ← outlineParts.seqs, seqList.rest
UNTIL seqList =
NIL
DO
IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, normal];
ENDLOOP;
};
active selection.
outlineD ← GGSelect.FindSelectedOutline[outline, scene, active];
ClearSelectionInOutline[outline, active];
IF outlineD #
NIL
THEN {
outlineParts ← NARROW[outlineD.parts];
FOR seqList:
LIST
OF Sequence ← outlineParts.seqs, seqList.rest
UNTIL seqList =
NIL
DO
IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, active];
ENDLOOP;
};
};
SaveSelectionInTraj:
PUBLIC
PROC [traj: Traj, selectClass: SelectionClass, scene: Scene] = {
seq: Sequence ← GGSelect.FindSelectedSequence[traj, scene, selectClass];
IF seq=NIL THEN ClearSelectionInTraj[traj, selectClass]
ELSE SaveSelectionInSequence[seq, selectClass];
};
ClearSelectionInTraj:
PROC [traj: Traj, selectClass: SelectionClass] = {
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
SetSegmentField[seg, FALSE, selectClass];
FOR j:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
SetControlPointField[seg, j, FALSE, selectClass];
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..GGTraj.HiJoint[traj]]
DO
joint ← GGTraj.FetchJoint[traj, i];
SetJointField[joint, FALSE, selectClass];
ENDLOOP;
RemakeSelectionFromTraj:
PUBLIC
PROC [traj: Traj, scene: Scene, selectClass: SelectionClass] = {
seq: Sequence ← GGSequence.CreateEmpty[traj];
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
seq.segments[i] ← GetSegmentField[seg, selectClass];
IF seq.segments[i] THEN seq.segCount ← seq.segCount + 1;
FOR j:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
seq.controlPoints[i][j] ← GetControlPointField[seg, j, selectClass];
IF seq.controlPoints[i][j] THEN seq.controlPointCount ← seq.controlPointCount + 1;
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..GGTraj.HiJoint[traj]]
DO
joint ← GGTraj.FetchJoint[traj, i];
seq.joints[i] ← GetJointField[joint, selectClass];
IF seq.joints[i] THEN seq.jointCount ← seq.jointCount + 1;
ENDLOOP;
GGSelect.SelectSequence[seq, scene, selectClass];
};
SaveSelectionInSequence:
PROC [seq: Sequence, selectClass: SelectionClass] = {
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGTraj.HiSegment[seq.traj]]
DO
seg ← GGTraj.FetchSegment[seq.traj, i];
SetSegmentField[seg, seq.segments[i], selectClass];
FOR j:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
SetControlPointField[seg, j, seq.controlPoints[i][j], selectClass];
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..GGTraj.HiJoint[seq.traj]]
DO
joint ← GGTraj.FetchJoint[seq.traj, i];
SetJointField[joint, seq.joints[i], selectClass];
ENDLOOP;
};
ClearSelectionInOutline:
PROC [outline: Outline, selectClass: SelectionClass] = {
trajGen: GGModelTypes.TrajGenerator;
trajGen ← TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
ClearSelectionInTraj[traj, selectClass];
ENDLOOP;
};
RemakeSelectionsFromOutline:
PUBLIC
PROC [outline: Outline, scene: Scene] = {
This outline has just been created by copying parts from an existing outline. The selection information was copied as well. Make sequences from the selection bits, and select these sequences.
trajGen: GGModelTypes.TrajGenerator;
trajGen ← TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
hotness.
RemakeSelectionFromTraj[traj, scene, hot];
normal selection.
RemakeSelectionFromTraj[traj, scene, normal];
active selection.
RemakeSelectionFromTraj[traj, scene, active];
ENDLOOP;
};
SequencesOfOutline:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [seqList:
LIST
OF Sequence] = {
outlineParts: OutlineParts ← NARROW[outlineD.parts];
ptr: LIST OF Sequence;
[seqList, ptr] ← GGUtility.StartSequenceList[];
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN [seqList, ptr] ← GGUtility.AddSequence[list.first, seqList, ptr];
ENDLOOP;
};
RemoveTraj:
PUBLIC
PROC [outlineD: OutlineDescriptor, traj: Traj] = {
Used by GGSelect to do a DeselectTraj.
outlineParts: OutlineParts ← NARROW[outlineD.parts];
seq: Sequence;
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first#
NIL
AND list.first.traj = traj
THEN {
seq ← list.first;
GOTO Done;
};
REPEAT
Done => {
outlineParts.seqs ←
NARROW[GList.DSubst[
NIL, seq, outlineParts.seqs]];
Replace the old sequence with a NIL. No trajectory has actually been deleted. It is just no longer mentioned in outlineD.
};
FINISHED => NULL;
ENDLOOP;
};
NearestJointToHitData:
PUBLIC PROC [hitData:
REF
ANY]
RETURNS [jointNum:
NAT, traj: Traj] = {
outlineHitData: OutlineHitData ← NARROW[hitData];
nextNum: NAT;
p1, p2: Point;
d1, d2: REAL;
traj ← outlineHitData.traj;
SELECT outlineHitData.hitType
FROM
joint => {
jointNum ← outlineHitData.jointNum;
};
segment, controlPoint => {
nextNum ← GGTraj.FollowingJoint[traj, outlineHitData.segNum];
p1 ← GGTraj.FetchJointPos[traj, outlineHitData.segNum];
p2 ← GGTraj.FetchJointPos[traj, nextNum];
d1 ← GGVector.DistanceSquared[p1, outlineHitData.hitPoint];
d2 ← GGVector.DistanceSquared[p2, outlineHitData.hitPoint];
IF d1 <= d2 THEN jointNum ← outlineHitData.segNum
ELSE jointNum ← nextNum;
};
ENDCASE => ERROR;
};
UnpackHitData:
PUBLIC
PROC [hitData:
REF
ANY]
RETURNS [traj: Traj, hitType: TrajPartType, segNum, cpNum, jointNum:
INT, hitPoint: Point] = {
This will go away when the Caret machinery is revised.
outlineHitData: OutlineHitData ← NARROW[hitData];
traj ← outlineHitData.traj;
hitType ← outlineHitData.hitType;
segNum ← outlineHitData.segNum;
cpNum ← outlineHitData.cpNum;
jointNum ← outlineHitData.jointNum;
hitPoint ← outlineHitData.hitPoint;
};
UpdateDescriptorBoundBoxes:
PUBLIC
PROC [outlineD: OutlineDescriptor] = {
Update the bound boxes of all contained sequences.
parts: OutlineParts ← NARROW[outlineD.parts];
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN GGSequence.UpdateBoundBox[list.first];
ENDLOOP;
};
Queries about Outlines.
HasHoles:
PUBLIC
PROC [outline: Outline]
RETURNS [
BOOL] = {
RETURN[outline.children.rest # NIL];
};
FenceOfOutline:
PUBLIC
PROC [outline: Outline]
RETURNS [fence: Traj] = {
Returns the unique fence trajectory of outline.
fence ← outline.children.first;
};
HolesOfOutline:
PUBLIC
PROC [outline: Outline]
RETURNS [trajGen: TrajGenerator] = {
Generates all of the holes of outline.
list: LIST OF Traj ← outline.children.rest;
trajGen ←
NEW[TrajGeneratorObj ← [
list: list
]];
};
TrajsInOutline:
PUBLIC
PROC [outline: Outline]
RETURNS [trajGen: TrajGenerator] = {
Generates the fence and all of the holes of outline.
list: LIST OF Traj ← outline.children;
trajGen ←
NEW[TrajGeneratorObj ← [
list: list
]];
};
FetchOutlineClass:
PROC []
RETURNS [class: OutlineClass] = {
class ← globalOutlineClass;
};
globalOutlineClass: OutlineClass;
Init:
PROC [] = {
globalOutlineClass ← MakeOutlineClass[];
};
Utility Routines
AppendTrajList:
PROC [list1, list2:
LIST
OF Traj]
RETURNS [result:
LIST
OF Traj] = {
pos: LIST OF Traj;
newCell: LIST OF Traj;
Non-destructive (copies the first list).
IF list1 = NIL THEN RETURN[list2];
result ← CONS[list1.first, NIL];
pos ← result;
FOR l:
LIST
OF Traj ← list1.rest, l.rest
UNTIL l =
NIL
DO
newCell ← CONS[l.first, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
pos.rest ← list2;
};
Init[];
END.