GGOutlineImplA.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, GGParseIn, GGParseOut, GGSelect, GGSegmentTypes, GGSequence, GGShapes, GGTraj, GGUtility, GGVector, Imager, ImagerColor, ImagerPath, ImagerTransformation, IO, Rope, Rosary, ViewerClasses;
GGOutlineImplA: CEDAR PROGRAM
IMPORTS GGBoundBox, GGDescribe, GGError, GGObjects, GGOutline, GGParseIn, GGParseOut, GGSequence, GGShapes, GGTraj, GGUtility, GGVector, Imager, ImagerColor, ImagerPath, ImagerTransformation, IO, Rosary
EXPORTS GGOutline = BEGIN
OPEN GGOutline;
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;
OutlineHitData: TYPE = REF OutlineHitDataObj;
OutlineHitDataObj: TYPE = GGOutline.OutlineHitDataObj;
OutlineParts: TYPE = REF OutlinePartsObj;
OutlinePartsObj: TYPE = GGOutline.OutlinePartsObj;
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;
SequenceOfReal: TYPE = GGBasicTypes.SequenceOfReal;
SliceClass: TYPE = REF SliceClassObj;
SliceClassObj: TYPE = GGModelTypes.SliceClassObj;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceParts: TYPE = GGModelTypes.SliceParts;
StrokeEnd: TYPE = Imager.StrokeEnd;
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;
Viewer: TYPE = ViewerClasses.Viewer;
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
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
FetchSliceClass: PUBLIC PROC [name: ATOM] RETURNS [class: OutlineClass] = {
class ← globalOutlineClass;
};
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: OutlineFileout,
filein: OutlineFilein,
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,
setDashed: OutlineSetDashed,
getDashed: OutlineGetDashed
]];
};
FetchOutlineClass: PROC [] RETURNS [class: OutlineClass] = {
class ← globalOutlineClass;
};
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];
};
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;
Fill in the outline if necessary.
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];
};
Draw the strokes.
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;
};
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;
};
};
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
};
Drawing Utilities
DrawOutline: PROC [dc: Imager.Context, outline: Outline] = {
Fill the outline if necessary.
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];
};
Draw the strokes.
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;
};
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];
};
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;
};
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];
};
OutlineFileout: PROC [slice: Outline, f: IO.STREAM] = {
count: NAT;
f.PutF["Outline:\n"];
f.PutF["fillColor: "]; GGParseOut.WriteColor[f, slice.fillColor];
f.PutF[" strokeEnd: "]; GGParseOut.WriteStrokeEnd[f, slice.lineEnds];
f.PutChar[IO.CR];
count ← 0;
FOR trajList: LIST OF Traj ← slice.children, trajList.rest UNTIL trajList = NIL DO
count ← count + 1;
ENDLOOP;
f.PutF["Trajectories: [%g]\n", [integer[count]]];
FOR trajList: LIST OF Traj ← slice.children, trajList.rest UNTIL trajList = NIL DO
GGTraj.Fileout[f, trajList.first];
ENDLOOP;
f.PutChar[IO.CR];
};
OutlineFilein: PROC [f: IO.STREAM, version: REAL, feedback: Viewer] RETURNS [slice: Outline] = {
fillColor: Color;
strokeEnd: StrokeEnd;
count: NAT;
fence, hole: Traj;
hasCircle: BOOLFALSE;
GGParseIn.ReadBlankAndRope[f, "fillColor:"];
fillColor ← GGParseIn.ReadColor[f, version];
GGParseIn.ReadBlankAndRope[f, "strokeEnd:"];
strokeEnd ← GGParseIn.ReadStrokeEnd[f];
GGParseIn.ReadBlankAndRope[f, "Trajectories:"];
GGParseIn.ReadBlankAndRope[f, "["];
count ← GGParseIn.ReadBlankAndNAT[f];
GGParseIn.ReadBlankAndRope[f, "]"];
[fence, hasCircle] ← GGTraj.Filein[f, version];
IF hasCircle THEN fillColor ← NIL; -- needed for the transition from circle/disc class
slice ← GGOutline.CreateOutline[fence, strokeEnd, fillColor];
FOR i: NAT IN [1..count-1] DO
[hole, ----] ← GGTraj.Filein[f, version];
slice ← GGOutline.AddHole[slice, hole];
ENDLOOP;
};
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: PUBLIC 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: PUBLIC 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: PUBLIC PROC [seg: Segment, cpNum: NAT, selected: BOOL, selectClass: SelectionClass] = {
seg.class.controlPointFieldSet[seg, cpNum, selected, selectClass];
};
GetControlPointField: PUBLIC PROC [seg: Segment, cpNum: NAT, selectClass: SelectionClass] RETURNS [selected: BOOL] = {
selected ← seg.class.controlPointFieldGet[seg, cpNum, selectClass];
};
GetJointField: PUBLIC 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: PUBLIC 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
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;
};
OutlineSetDashed: PUBLIC PROC [slice: Outline, parts: SliceParts, dashed: BOOL, pattern: SequenceOfReal ← NIL, offset: REAL ← 0.0, length: REAL ← -1.0] = {
realParts: OutlineParts ← NARROW[parts];
FOR list: LIST OF Sequence ← realParts.seqs, list.rest UNTIL list = NIL DO
IF list.first # NIL THEN SequenceSetDashed[list.first, dashed, pattern, offset, length];
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;
};
SequenceSetDashed: PROC [seq: Sequence, dashed: BOOL, pattern: SequenceOfReal ← NIL, offset: REAL ← 0.0, length: REAL ← -1.0] = {
segGen: SegmentGenerator;
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
seg.dashed ← dashed;
seg.pattern ← pattern;
seg.offset ← offset;
seg.length ← length;
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;
};
OutlineGetDashed: PROC [slice: Outline, parts: SliceParts] RETURNS [dashed: BOOL, pattern: SequenceOfReal, offset, length: REAL] = {
Get the dash pattern 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 {
[dashed, pattern, offset, length] ← SequenceGetDashed[list.first];
RETURN;
};
ENDLOOP;
ERROR Problem[msg: "No segments to get a dash pattern from"];
};
SequenceGetDashed: PROC [seq: Sequence] RETURNS [dashed: BOOL, pattern: SequenceOfReal, offset, length: REAL] = {
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
seg: Segment ← GGSequence.NextSegment[segGen];
IF seg#NIL THEN {
dashed ← seg.dashed;
pattern ← seg.pattern;
offset ← seg.offset;
length ← seg.length;
}
ELSE ERROR Problem[msg: "No segments to get a dash pattern from"];
};
OutlineSetFillColor: 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;
};
Utility Routines
AppendTrajList: PUBLIC 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;
};
globalOutlineClass: OutlineClass;
Init: PROC [] = {
globalOutlineClass ← MakeOutlineClass[];
};
Init[];
END.