GGSliceImplE.mesa
Contents: Implements some of the Traj slice class (see GGSliceImplD for more).
Copyright Ó 1987, 1988, 1990, 1992 by Xerox Corporation. All rights reserved.
Created By: Ken Pier, December 3, 1987 3:25:32 pm PST
Bier, December 3, 1992 5:38 pm PST
Last Edited By: Ken Pier, September 20, 1990 10:33 am PDT
Doug Wyatt, April 14, 1992 2:39 pm PDT
DIRECTORY
Atom, Feedback, FeedbackTypes, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, ImagerTransformation, IO, Rope, Vectors2d;
GGSliceImplE:
CEDAR
PROGRAM
IMPORTS Atom, Feedback, GGBoundBox, GGCoreOps, GGParseIn, GGParseOut, GGSegment, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, IO, Rope, Vectors2d
EXPORTS GGSlice = BEGIN
BitVector: TYPE = GGBasicTypes.BitVector;
BoundBox: TYPE = GGCoreTypes.BoundBox;
BoundBoxObj: TYPE = GGCoreTypes.BoundBoxObj;
Camera: TYPE = GGModelTypes.Camera;
Circle: TYPE = GGBasicTypes.Circle;
Color: TYPE = Imager.Color;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
Corner: TYPE = GGSlice.Corner;
DefaultData: TYPE = GGModelTypes.DefaultData;
Edge: TYPE = GGBasicTypes.Edge;
EditConstraints: TYPE = GGModelTypes.EditConstraints;
ExtendMode: TYPE = GGModelTypes.ExtendMode;
FactoredTransformation: TYPE = ImagerTransformation.FactoredTransformation;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
MsgRouter: TYPE = FeedbackTypes.MsgRouter;
FenceHoleOpen: TYPE = GGModelTypes.FenceHoleOpen;
GGData: TYPE = GGInterfaceTypes.GGData;
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
HitType: TYPE = GGModelTypes.TrajPartType;
Joint: TYPE = GGSegmentTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
JointObj: TYPE = GGSegmentTypes.JointObj;
Line: TYPE = GGCoreTypes.Line;
Object: TYPE = Imager.Object;
Orientation: TYPE = GGModelTypes.Orientation;
Point: TYPE = GGBasicTypes.Point;
PointAndDone: TYPE = GGModelTypes.PointAndDone;
PointGenerator: TYPE = GGModelTypes.PointGenerator;
PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj;
PointPairAndDone: TYPE = GGModelTypes.PointPairAndDone;
PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator;
PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj;
PointWalkProc: TYPE = GGModelTypes.PointWalkProc;
Scene: TYPE = GGModelTypes.Scene;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
SegCPSequence: TYPE = GGTraj.SegCPSequence;
SegCPSequenceObj: TYPE = GGTraj.SegCPSequenceObj;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentClass: TYPE = GGSegmentTypes.SegmentClass;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj;
SelectedObjectData: TYPE = GGModelTypes.SelectedObjectData;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
SelectMode: TYPE = GGModelTypes.SelectMode;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal;
Slice: TYPE = GGModelTypes.Slice;
SliceBoundBoxProc: TYPE = GGModelTypes.SliceBoundBoxProc;
SliceClass: TYPE = GGModelTypes.SliceClass;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceObj: TYPE = GGModelTypes.SliceObj;
SliceParts: TYPE = GGModelTypes.SliceParts;
StrokeEnd: TYPE = GGModelTypes.StrokeEnd;
StrokeJoint: TYPE = GGModelTypes.StrokeJoint;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajHitData: TYPE = GGTraj.TrajHitData;
TrajHitDataObj: TYPE = GGTraj.TrajHitDataObj;
TrajParts: TYPE = GGModelTypes.TrajParts;
TrajPartsObj: TYPE = GGModelTypes.TrajPartsObj;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
Transformation: TYPE = GGModelTypes.Transformation;
TriggerBag: TYPE = GGInterfaceTypes.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
WalkProc: TYPE = GGModelTypes.WalkProc;
SliceClassDef: TYPE = REF SliceClassDefObj;
SliceClassDefObj: TYPE = RECORD[type: ATOM, class: SliceClass];
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
BuildMoreTrajSliceClass:
PUBLIC
PROC [class: SliceClass] = {
Textual Description
class.describe ¬ TrajDescribe;
class.describeHit ¬ TrajDescribeHit;
class.fileout ¬ TrajFileout;
class.filein ¬ TrajFilein;
Part Generators
class.pointsInDescriptor ¬ TrajPointsInDescriptor;
class.walkPointsInDescriptor ¬ TrajWalkPointsInDescriptor;
class.pointPairsInDescriptor ¬ TrajPointPairsInDescriptor;
class.segmentsInDescriptor ¬ TrajSegmentsInDescriptor;
class.walkSegments ¬ TrajWalkSegments;
class.nextPoint ¬ TrajNextPoint;
class.nextPointPair ¬ TrajNextPointPair;
class.nextSegment ¬ TrajNextSegment;
Hit Testing
class.closestPoint ¬ TrajClosestPoint;
class.closestJointToHitData ¬ TrajClosestJointToHitData;
class.closestPointAndTangent ¬ TrajClosestPointAndTangent;
class.closestSegment ¬ TrajClosestSegment;
class.filledPathsUnderPoint ¬ TrajFilledPathsUnderPoint;
class.lineIntersection ¬ TrajLineIntersection;
class.circleIntersection ¬ TrajCircleIntersection;
class.hitDataAsSimpleCurve ¬ TrajHitDataAsSimpleCurve;
Style
class.setDefaults ¬ TrajSetDefaults;
class.setStrokeWidth ¬ TrajSetStrokeWidth;
class.getStrokeWidth ¬ TrajGetStrokeWidth;
class.setStrokeEnd ¬ TrajSetStrokeEnd;
class.getStrokeEnd ¬ TrajGetStrokeEnd;
class.setStrokeJoint ¬ TrajSetStrokeJoint;
class.getStrokeJoint ¬ TrajGetStrokeJoint;
class.setStrokeColor ¬ TrajSetStrokeColor;
class.getStrokeColor ¬ TrajGetStrokeColor;
class.setFillColor ¬ GGSlice.NoOpSetFillColor;
class.getFillColor ¬ GGSlice.NoOpGetFillColor;
class.setArrows ¬ TrajSetArrows;
class.getArrows ¬ TrajGetArrows;
class.setDashed ¬ TrajSetDashed;
class.getDashed ¬ TrajGetDashed;
class.setOrientation ¬ TrajSetOrientation;
class.getOrientation ¬ TrajGetOrientation;
};
AllStrokeEndsEqual:
PROC [slice: Slice]
RETURNS [allEqual:
BOOL ¬
FALSE, end: StrokeEnd] = {
seg: Segment;
firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0];
hiSegment: NAT ¬ GGTraj.HiSegment[slice];
end ¬ firstSeg.strokeEnd;
FOR i:
INT
IN [1..hiSegment]
DO
seg ¬ GGTraj.FetchSegment[slice, i];
IF seg.strokeEnd # end THEN RETURN[FALSE, round];
ENDLOOP;
allEqual ¬ TRUE;
};
AllStrokeWidthsEqual:
PROC [slice: Slice]
RETURNS [width:
REAL] = {
seg: Segment;
firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0];
hiSegment: NAT ¬ GGTraj.HiSegment[slice];
width ¬ firstSeg.strokeWidth;
FOR i:
INT
IN [1..hiSegment]
DO
seg ¬ GGTraj.FetchSegment[slice, i];
IF seg.strokeWidth # width THEN RETURN[-1.0];
ENDLOOP;
};
AllStrokeColorsEqual:
PROC [slice: Slice]
RETURNS [allEqual:
BOOL ¬
FALSE, color: Color] = {
seg: Segment;
firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0];
hiSegment: NAT ¬ GGTraj.HiSegment[slice];
color ¬ firstSeg.color;
FOR i:
INT
IN [1..hiSegment]
DO
seg ¬ GGTraj.FetchSegment[slice, i];
IF NOT GGCoreOps.EquivalentColors[color, seg.color] THEN RETURN[FALSE, NIL];
ENDLOOP;
allEqual ¬ TRUE;
};
AllDashesEqual:
PROC [slice: Slice]
RETURNS [allEqual:
BOOL ¬
FALSE, dashed:
BOOL ¬
FALSE, pattern: SequenceOfReal ¬
NIL, offset, length:
REAL ¬ 0.0] = {
seg: Segment;
firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0];
hiSegment: NAT ¬ GGTraj.HiSegment[slice];
dashed ¬ firstSeg.dashed;
pattern ¬ firstSeg.pattern;
offset ¬ firstSeg.offset;
length ¬ firstSeg.length;
FOR i:
INT
IN [1..hiSegment]
DO
seg ¬ GGTraj.FetchSegment[slice, i];
IF seg.dashed # dashed THEN RETURN[FALSE];
IF NOT dashed THEN LOOP;
IF NOT GGUtility.EquivalentPatterns[seg.pattern, pattern] THEN RETURN[FALSE];
IF seg.offset # offset THEN RETURN[FALSE];
IF seg.length # length THEN RETURN[FALSE];
ENDLOOP;
allEqual ¬ TRUE;
};
TrajDescribe:
PROC [sliceD: SliceDescriptor]
RETURNS [rope: Rope.
ROPE] = {
GGModelTypes.SliceDescribeProc
trajData: TrajData ¬ NARROW[sliceD.slice.data];
trajParts: TrajParts ¬ NARROW[sliceD.parts];
IF trajParts=
NIL
THEN
RETURN[
NIL]
ELSE {
segNum, cpNum: NAT;
segCount: NAT ¬ trajParts.segCount;
jointCount: NAT ¬ trajParts.jointCount;
cpCount: NAT ¬ trajParts.controlPointCount;
SELECT
TRUE
FROM
segCount=1 => {
-- single segment selected. Describe it
segGen: SegmentGenerator ¬ GGSequence.SegmentsInSequence[trajData, trajParts];
rope ¬ GGUtility.
DescribeSegment[sliceD.slice,
GGSequence.NextSegmentAndIndex[segGen].index];
};
segCount=0
AND cpCount=1 => {
-- single CP selected. Describe it
cpGen: ControlPointGenerator ¬ GGSequence.ControlPointsInSequence[trajData, trajParts];
[segNum, cpNum] ¬ GGSequence.NextSegNumAndCPNum[cpGen];
rope ¬ GGUtility.DescribeControlPoint[sliceD.slice, segNum, cpNum];
};
segCount=0
AND cpCount=0
AND jointCount=1 => {
-- single joint selected. Describe it
jointGen: JointGenerator ¬ GGSequence.JointsInSequence[trajParts];
rope ¬ GGUtility.DescribeJoint[sliceD.slice, GGSequence.NextJoint[jointGen]];
};
ENDCASE => rope ¬ GGUtility.DescribeSequence[sliceD];
};
};
TrajDescribeHit:
PROC [slice: Slice, hitData:
REF
ANY]
RETURNS [rope: Rope.
ROPE] = {
trajHitData: TrajHitData ¬ NARROW[hitData];
SELECT trajHitData.hitType
FROM
joint => rope ¬ GGUtility.DescribeJoint[slice, trajHitData.jointNum];
controlPoint => rope ¬ GGUtility.DescribeControlPoint[slice, trajHitData.segNum, trajHitData.cpNum];
segment => rope ¬ GGUtility.DescribeSegment[slice, trajHitData.segNum];
interior => rope ¬ GGUtility.DescribeInterior[slice];
ENDCASE => ERROR;
};
TrajFileout:
PROC [slice: Slice, f:
IO.
STREAM] = {
GGModelTypes.SliceFileoutProc
trajData: TrajData ¬ NARROW[slice.data];
roleRope: Rope.ROPE ¬ RoleToRope[trajData.role];
strokeWidth, offset, length: REAL;
strokeOK, endsOK, colorOK, dashOK, dashed: BOOL;
pattern: SequenceOfReal;
color: Color;
point: Point;
end: StrokeEnd;
className: Rope.ROPE;
seg: Segment;
arrowCode: NAT;
hiJoint:
NAT ¬ GGTraj.HiJoint[slice];
Properties of the whole trajectory.
arrowCode ¬
SELECT
TRUE
FROM
trajData.hiArrow =>
SELECT
TRUE
FROM
trajData.loArrow => 3,
ENDCASE => 2,
ENDCASE =>
SELECT
TRUE
FROM
trajData.loArrow => 1,
ENDCASE => 0;
f.PutF["Traj (%g) [%g] arrows: %g ", [rope[roleRope]], [integer[hiJoint]], [integer[arrowCode]]];
Version 8802.04 eliminated "Traj" from output
f.PutF[" (%g) [%g] arrows: %g ", [rope[roleRope]], [integer[hiJoint]], [integer[arrowCode]]];
f.PutRope["j: "]; -- traj Joint
GGParseOut.WriteStrokeJoint[f, trajData.strokeJoint];
individual segments the same ??
[endsOK, end] ¬ AllStrokeEndsEqual[slice];
f.PutRope[" e: "];
GGParseOut.WriteBool[f, endsOK];
f.PutChar[IO.SP];
GGParseOut.WriteStrokeEnd[f, end];
f.PutChar[IO.SP];
strokeWidth ¬ AllStrokeWidthsEqual[slice];
strokeOK ¬ strokeWidth >= 0.0;
f.PutF1["w: %g ", [real[strokeWidth]]];
[colorOK, color] ¬ AllStrokeColorsEqual[slice];
f.PutRope["c: "];
GGParseOut.WriteBool[f, colorOK];
f.PutChar[IO.SP];
GGParseOut.WriteColor[f, color];
f.PutChar[IO.SP];
[dashOK, dashed, pattern, offset, length] ¬ AllDashesEqual[slice];
f.PutRope["d: "];
GGParseOut.WriteBool[f, dashOK];
IF dashOK
THEN {
f.PutChar[IO.SP];
GGParseOut.WriteBool[f, dashed];
f.PutChar[IO.SP];
IF dashed
THEN {
GGParseOut.WriteArrayOfReal[f, pattern];
f.PutF[" %g %g", [real[offset]], [real[length]]];
};
};
f.PutChar[
IO.
LF];
Now fileout the segment data.
point ¬ GGTraj.FetchJointPos[slice, 0];
GGParseOut.WritePoint[f, point];
FOR index:
NAT
IN [1..hiJoint]
DO
seg ¬ GGTraj.FetchSegment[slice, index - 1];
className ¬ Atom.GetPName[seg.class.type];
IF strokeOK THEN f.PutF1[" (%g ", [rope[className]]]
ELSE f.PutF[" (%g %g ", [rope[className]], [real[seg.strokeWidth]]];
IF
NOT endsOK
THEN {
GGParseOut.WriteStrokeEnd[f, seg.strokeEnd];
f.PutChar[IO.SP];
};
IF
NOT colorOK
THEN {
GGParseOut.WriteColor[f, seg.color];
f.PutChar[IO.SP];
};
IF
NOT dashOK
THEN {
GGParseOut.WriteBool[f, seg.dashed]; f.PutChar[IO.SP];
IF seg.dashed
THEN {
GGParseOut.WriteArrayOfReal[f, seg.pattern]; f.PutChar[IO.SP];
f.PutF[" %g %g ", [real[seg.offset]], [real[seg.length]]];
};
};
seg.class.fileOut[seg, f];
GGParseOut.WriteProps[f, seg.props];
f.PutRope[") "];
point ¬ GGTraj.FetchJointPos[slice, index];
GGParseOut.WritePoint[f, point];
ENDLOOP;
IF (trajData.role = fence
OR trajData.role = hole)
THEN {
seg ¬ GGTraj.FetchSegment[slice, hiJoint];
className ¬ Atom.GetPName[seg.class.type];
IF strokeOK THEN f.PutF1[" (%g ", [rope[className]]]
ELSE f.PutF[" (%g %g ", [rope[className]], [real[seg.strokeWidth]]];
IF
NOT endsOK
THEN {
GGParseOut.WriteStrokeEnd[f, seg.strokeEnd];
f.PutChar[IO.SP];
};
IF
NOT colorOK
THEN {
GGParseOut.WriteColor[f, seg.color];
f.PutChar[IO.SP];
};
IF
NOT dashOK
THEN {
GGParseOut.WriteBool[f, seg.dashed]; f.PutChar[IO.SP];
IF seg.dashed
THEN {
GGParseOut.WriteArrayOfReal[f, seg.pattern]; f.PutChar[IO.SP];
f.PutF[" %g %g ", [real[seg.offset]], [real[seg.length]]];
};
};
seg.class.fileOut[seg, f];
GGParseOut.WriteProps[f, seg.props];
f.PutRope[")"];
};
f.PutRope[" fwd: "];
GGParseOut.WriteBool[f, trajData.forward];
};
TrajFilein:
PROC [f:
IO.
STREAM, version:
REAL, router: MsgRouter, camera: Camera]
RETURNS [slice: Slice] = {
GGModelTypes.SliceFileinProc
hasCircle: BOOL ¬ FALSE;
hiJoint, arrowCode: NAT;
role: FenceHoleOpen;
roleName, className: Rope.ROPE;
pFirst, p0, p1: Point;
trajData: TrajData;
seg: Segment;
class: SegmentClass;
strokeWidth, offset, length: REAL;
pattern: SequenceOfReal;
strokeColor: Color;
strokeEnd: StrokeEnd;
strokeJoint: StrokeJoint;
success, colorOK, widthOK, endsOK, dashOK, dashed, fwd:
BOOL;
Traj (fence) [3] arrows: 0:
IF version < 8802.04 THEN GGParseIn.ReadRope[f, "Traj"]; -- gone as of version 8802.04
GGParseIn.ReadChar[f, '(];
roleName ¬ GGParseIn.ReadWord[f];
role ¬ RoleFromRope[roleName];
GGParseIn.ReadChar[f, ')];
GGParseIn.ReadChar[f, '[];
hiJoint ¬ GGParseIn.ReadNAT[f];
GGParseIn.ReadChar[f, ']];
IF version >= 8607.22
THEN {
GGParseIn.ReadRope[f, "arrows:"];
arrowCode ¬ GGParseIn.ReadNAT[f];
}
ELSE arrowCode ¬ 0;
IF version >= 8702.26
THEN {
GGParseIn.ReadRope[f, "j:"];
strokeJoint ¬ GGParseIn.ReadStrokeJoint[f];
GGParseIn.ReadRope[f, "e:"];
endsOK ¬ GGParseIn.ReadBool[f];
strokeEnd ¬ GGParseIn.ReadStrokeEnd[f];
}
ELSE {
strokeJoint ¬ round;
endsOK ¬ FALSE;
};
IF version >= 8701.135
THEN {
GGParseIn.ReadRope[f, "w:"];
strokeWidth ¬ GGParseIn.ReadReal[f];
widthOK ¬ strokeWidth>= 0.0;
GGParseIn.ReadRope[f, "c:"];
colorOK ¬ GGParseIn.ReadBool[f];
strokeColor ¬ GGParseIn.ReadColor[f, version];
}
ELSE {
GGParseIn.ReadChar[f, ':];
widthOK ¬ FALSE;
colorOK ¬ FALSE;
};
IF version >= 8701.23
THEN {
GGParseIn.ReadRope[f, "d:"];
dashOK ¬ GGParseIn.ReadBool[f];
IF dashOK
THEN {
dashed ¬ GGParseIn.ReadBool[f];
IF dashed
THEN {
pattern ¬ GGParseIn.ReadArrayOfReal[f];
offset ¬ GGParseIn.ReadReal[f];
length ¬ GGParseIn.ReadReal[f];
};
};
}
ELSE {
dashOK ¬ TRUE;
dashed ¬ FALSE;
};
Now read the Segment data.
pFirst ¬ p0 ¬ GGParseIn.ReadPoint[f];
slice ¬ GGTraj.CreateTraj[p0];
trajData ¬ NARROW[slice.data];
trajData.strokeJoint ¬ strokeJoint;
FOR index:
NAT
IN [1..hiJoint]
DO
GGParseIn.ReadChar[f, '(];
className ¬ GGParseIn.ReadWord[f];
IF NOT widthOK THEN strokeWidth ¬ GGParseIn.ReadReal[f];
IF version >= 8702.26
THEN {
IF NOT endsOK THEN strokeEnd ¬ GGParseIn.ReadStrokeEnd[f];
}
ELSE strokeEnd ¬ round;
IF version >= 8607.30
THEN {
IF NOT colorOK THEN strokeColor ¬ GGParseIn.ReadColor[f, version];
}
ELSE strokeColor ¬ Imager.black;
IF
NOT dashOK
THEN {
dashed ¬ GGParseIn.ReadBool[f];
IF dashed
THEN {
pattern ¬ GGParseIn.ReadArrayOfReal[f];
offset ¬ GGParseIn.ReadReal[f];
length ¬ GGParseIn.ReadReal[f];
};
};
class ¬ GGSegment.FetchSegmentClass[Atom.MakeAtom[className]];
seg ¬ class.fileIn[f, p0, [0.0, 0.0], version];
IF version >= 8610.29
THEN {
lor: LIST OF Rope.ROPE;
lor ¬ GGParseIn.ReadListOfRope[f]; -- ReadListOfRope cheats and terminates at ') as well as at IO.CR
FOR next:
LIST
OF Rope.
ROPE ¬ lor, next.rest
UNTIL next=
NIL
DO
seg.props ¬ CONS[next.first, seg.props];
ENDLOOP;
};
GGParseIn.ReadChar[f, ')];
p1 ¬ GGParseIn.ReadPoint[f];
seg.hi ¬ p1;
seg.class.endPointMoved[seg, FALSE, p1];
seg.strokeWidth ¬ strokeWidth;
seg.strokeEnd ¬ strokeEnd;
seg.color ¬ strokeColor;
seg.dashed ¬ dashed;
seg.pattern ¬ pattern;
seg.offset ¬ offset;
seg.length ¬ length;
IF class.type=$Circle
OR class.type=$Disc
THEN {
fake it with a closed arc segment
p: Point ¬ Vectors2d.Sub[seg.hi, seg.lo];
newJoint: Point ¬ [seg.lo.x-p.x, seg.lo.y-p.y];
newCp: Point ¬ [seg.lo.x+p.x, seg.lo.y+p.y];
success ¬ GGTraj.AddSegment[slice, hi, GGSegment.MakeArc[newJoint, newCp, newJoint, NIL], lo];
GGTraj.CloseByDistorting[slice, lo];
hasCircle ¬ TRUE;
}
ELSE success ¬ GGTraj.AddSegment[slice, hi, seg, lo];
IF NOT success THEN ERROR;
p0 ¬ p1;
ENDLOOP;
IF (role = fence
OR role = hole)
THEN {
GGParseIn.ReadChar[f, '(];
className ¬ GGParseIn.ReadWord[f];
IF NOT widthOK THEN strokeWidth ¬ GGParseIn.ReadReal[f];
IF version >= 8702.26
THEN {
IF NOT endsOK THEN strokeEnd ¬ GGParseIn.ReadStrokeEnd[f];
}
ELSE strokeEnd ¬ round;
IF version >= 8607.30
THEN {
IF NOT colorOK THEN strokeColor ¬ GGParseIn.ReadColor[f, version];
}
ELSE strokeColor ¬ Imager.black;
IF
NOT dashOK
THEN {
dashed ¬ GGParseIn.ReadBool[f];
IF dashed
THEN {
pattern ¬ GGParseIn.ReadArrayOfReal[f];
offset ¬ GGParseIn.ReadReal[f];
length ¬ GGParseIn.ReadReal[f];
};
};
class ¬ GGSegment.FetchSegmentClass[Atom.MakeAtom[className]];
seg ¬ class.fileIn[f, p0, pFirst, version];
IF version >= 8610.29
THEN {
lor: LIST OF Rope.ROPE;
lor ¬ GGParseIn.ReadListOfRope[f]; -- ReadListOfRope cheats and terminates at ') as well as at IO.CR
FOR next:
LIST
OF Rope.
ROPE ¬ lor, next.rest
UNTIL next=
NIL
DO
seg.props ¬ CONS[next.first, seg.props];
ENDLOOP;
};
GGParseIn.ReadChar[f, ')];
seg.strokeWidth ¬ strokeWidth;
seg.color ¬ strokeColor;
seg.strokeEnd ¬ strokeEnd;
seg.dashed ¬ dashed;
seg.pattern ¬ pattern;
seg.offset ¬ offset;
seg.length ¬ length;
IF trajData.segCount=0
THEN {
-- special case of closed single segment trajectory
success ¬ GGTraj.AddSegment[slice, hi, seg, lo];
IF NOT success THEN ERROR;
GGTraj.CloseByDistorting[slice, lo];
}
ELSE GGTraj.CloseWithSegment[slice, seg, lo];
};
IF version>=8802.04
THEN {
-- for trajectories, fwd=true means "think of me as clockwise"
GGParseIn.ReadRope[f, "fwd:"];
fwd ¬ GGParseIn.ReadBool[f];
}
ELSE fwd ¬ TRUE;
trajData.forward ¬ fwd;
trajData.role ¬ IF hasCircle THEN circle ELSE role; -- caller better fix this up !!
};
RoleFromRope:
PROC [roleName: Rope.
ROPE]
RETURNS [role: FenceHoleOpen] = {
SELECT TRUE FROM
Rope.Equal[roleName, "fence"] => role ¬ fence;
Rope.Equal[roleName, "hole"] => role ¬ hole;
Rope.Equal[roleName, "open"] => role ¬ open;
ENDCASE => ERROR;
};
RoleToRope:
PROC [role: FenceHoleOpen]
RETURNS [roleName: Rope.
ROPE] = {
SELECT role FROM
fence => roleName ¬ "fence";
hole => roleName ¬ "hole";
open => roleName ¬ "open";
ENDCASE => ERROR;
};
TrajPointGeneratorData: TYPE = REF TrajPointGeneratorDataObj;
TrajPointGeneratorDataObj:
TYPE =
RECORD [
jointsDone: BOOL,
jointGen: JointGenerator,
cpGen: GGModelTypes.ControlPointGenerator
];
TrajPointPairGeneratorData: TYPE = REF TrajPointPairGeneratorDataObj;
TrajPointPairGeneratorDataObj:
TYPE =
RECORD [
segGen: SegmentGenerator
];
TrajPointsInDescriptor:
PROC [sliceD: SliceDescriptor]
RETURNS [pointGen: PointGenerator] = {
trajData: TrajData ¬ NARROW[sliceD.slice.data];
trajParts: TrajParts ¬ NARROW[sliceD.parts];
pgd: TrajPointGeneratorData;
jointGen: JointGenerator;
cpGen: ControlPointGenerator;
jointGen ¬ GGSequence.JointsInSequence[trajParts];
cpGen ¬ GGSequence.ControlPointsInSequence[trajData, trajParts];
pgd ¬ NEW[TrajPointGeneratorDataObj ¬ [FALSE, jointGen, cpGen]];
pointGen ¬ NEW[PointGeneratorObj ¬ [sliceD, 0, 0, pgd]];
};
TrajWalkPointsInDescriptor:
PROC [sliceD: SliceDescriptor, walkProc: PointWalkProc] = {
trajParts: TrajParts ¬ NARROW[sliceD.parts];
trajData: TrajData ¬ NARROW[sliceD.slice.data];
GGSequence.WalkJointsInSequence[trajData, trajParts, walkProc];
GGSequence.WalkControlPointsInSequence[trajData, trajParts, walkProc];
};
TrajPointPairsInDescriptor:
PROC [sliceD: SliceDescriptor]
RETURNS [pointPairGen: PointPairGenerator] = {
trajData: TrajData ¬ NARROW[sliceD.slice.data];
trajParts: TrajParts ¬ NARROW[sliceD.parts];
pgd: TrajPointPairGeneratorData;
segGen: SegmentGenerator;
segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts];
pgd ¬ NEW[TrajPointPairGeneratorDataObj ¬ [segGen]];
pointPairGen ¬ NEW[PointPairGeneratorObj ¬ [sliceD, 0, 0, pgd]];
};
TrajSegmentsInDescriptor:
PROC [sliceD: SliceDescriptor]
RETURNS [segGen: SegmentGenerator] = {
trajData: TrajData ¬ NARROW[sliceD.slice.data];
trajParts: TrajParts ¬ NARROW[sliceD.parts];
segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts];
};
TrajWalkSegments:
PROC [slice: Slice, walkProc: WalkProc]
RETURNS [sliceD: SliceDescriptor] = {
GGModelTypes.SliceWalkSegmentsProc
trajData: TrajData ¬ NARROW[slice.data];
trajParts: TrajParts ¬ GGSequence.CreateEmpty[trajData];
segGen: SegmentGenerator ¬ GGSequence.SegmentsInTraj[trajData];
FOR next: SegAndIndex ¬ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen]
UNTIL next.seg=
NIL
DO
keep: BOOL ¬ walkProc[next.seg, NIL];
IF keep
THEN {
trajParts.segments[next.index] ¬ TRUE;
trajParts.segCount ¬ trajParts.segCount+1;
};
ENDLOOP;
GGSequence.FillInJoints[trajParts];
GGSequence.FillInControlPoints[trajParts];
sliceD ¬ GGSlice.DescriptorFromParts[slice, trajParts];
};
TrajNextPoint:
PROC [slice: Slice, pointGen: PointGenerator]
RETURNS [pointAndDone: PointAndDone] = {
pgd: TrajPointGeneratorData ¬ NARROW[pointGen.classSpecific];
IF
NOT pgd.jointsDone
THEN {
nextJoint: INT ¬ GGSequence.NextJoint[pgd.jointGen];
IF nextJoint = -1
THEN {
pgd.jointsDone ¬ TRUE;
}
ELSE {
pointAndDone.point ← FetchJointPosTraj[pgd.jointGen.seq.traj, nextJoint];
IF pointGen.sliceD.slice#slice THEN ERROR;
pointAndDone.point ¬ GGTraj.FetchJointPos[slice, nextJoint];
pointAndDone.done ¬ FALSE;
RETURN;
};
};
Time to look at the control points.
pointAndDone ¬ GGSequence.NextControlPoint[pgd.cpGen];
};
TrajNextPointPair:
PROC [slice: Slice, pointPairGen: PointPairGenerator]
RETURNS [pointPairAndDone: PointPairAndDone] = {
pgd: TrajPointPairGeneratorData ¬ NARROW[pointPairGen.classSpecific];
seg: Segment ¬ GGSliceOps.NextSegment[slice, pgd.segGen].seg;
IF seg = NIL THEN RETURN[[[0,0], [0,0], TRUE]]
ELSE RETURN[[seg.lo, seg.hi, FALSE]];
};
TrajNextSegment:
PROC [slice: Slice, segGen: SegmentGenerator]
RETURNS [seg: Segment, transform: Transformation] = {
RETURN[GGSequence.NextSegment[segGen], NIL];
};
TrajClosestPoint:
PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance:
REAL]
RETURNS [bestPoint: Point ¬ [0,0], bestDist:
REAL, bestNormal: Vector ¬ [0, -1], hitData:
REF
ANY, success:
BOOL ¬
FALSE] = {
GGModelTypes.SliceClosestPointProc
trajParts: TrajParts ¬ NARROW[sliceD.parts];
thisDist: REAL;
thisSegNum, bestSegNum, thisCP, bestCP, thisJointNum, bestJointNum: NAT;
thisPoint: Point;
thisNormal: Vector;
thisSuccess: BOOL;
bestType: TrajPartType ¬ none;
trajHitData: TrajHitData;
bestDist ¬ GGUtility.plusInfinity;
IF
NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[sliceD.slice], tolerance]
THEN RETURN; -- Bier, November 9, 1992
[thisDist, thisSegNum, thisCP, thisPoint, thisNormal, thisSuccess] ¬ GGTraj.NearestControlPoint[testPoint, sliceD, tolerance];
IF thisSuccess
AND thisDist < bestDist
THEN {
bestType ¬ controlPoint;
bestPoint ¬ thisPoint;
bestNormal ¬ thisNormal;
bestDist ¬ thisDist;
bestSegNum ¬ thisSegNum;
bestCP ¬ thisCP;
success ¬ TRUE;
};
[thisDist, thisJointNum, thisPoint, thisNormal, thisSuccess] ¬ GGTraj.NearestJoint[testPoint, sliceD, tolerance];
IF thisSuccess
AND thisDist < bestDist
THEN {
bestType ¬ joint;
bestPoint ¬ thisPoint;
bestNormal ¬ thisNormal;
bestDist ¬ thisDist;
bestJointNum ¬ thisJointNum;
success ¬ TRUE;
};
IF success
THEN {
SELECT bestType FROM
controlPoint => {
hitData ¬ trajHitData ¬
NEW[TrajHitDataObj ¬ [
controlPoint, bestSegNum, bestCP, -1, bestPoint]];
};
joint => {
hitData ¬ trajHitData ¬
NEW[TrajHitDataObj ¬ [
joint, -1, -1, bestJointNum, bestPoint]];
};
ENDCASE => ERROR;
};
};
TrajClosestJointToHitData:
PROC [sliceD: SliceDescriptor, mapPoint, testPoint: Point, hitData:
REF
ANY]
RETURNS [jointD: SliceDescriptor, point: Point, normal: Vector ¬ [0,-1]] = {
GGModelTypes.SliceClosestJointToHitDataProc
FindJointNormal:
PROC []
RETURNS [normal: Vector] = {
seg1, seg2: Segment;
normal1, normal2, tangent1, tangent2, direction: Vector;
IF GGTraj.HiSegment[traj] < jointNum THEN seg2 ¬ IF trajData.role = open THEN NIL ELSE GGTraj.FetchSegment[traj, 0]
ELSE seg2 ¬ GGTraj.FetchSegment[traj, jointNum];
seg1 ¬ GGTraj.PreviousSegment[traj, jointNum];
IF seg1 #
NIL
AND seg2 #
NIL
THEN {
[normal2, tangent2] ¬ seg2.class.jointNormal[seg2, point, testPoint, FALSE];
[normal1, tangent1] ¬ seg1.class.jointNormal[seg1, point, testPoint, TRUE];
direction ¬ Vectors2d.VectorFromPoints[point, testPoint];
normal ¬ IF ABS[Vectors2d.SmallestAngleBetweenVectors[tangent1, direction]] < ABS[Vectors2d.SmallestAngleBetweenVectors[tangent2, direction]] THEN normal1 ELSE normal2;
}
ELSE {
IF seg1 # NIL THEN [normal, ----] ¬ seg1.class.jointNormal[seg1, point, testPoint, TRUE];
IF seg2 # NIL THEN [normal, ----] ¬ seg2.class.jointNormal[seg2, point, testPoint, FALSE];
};
};
hitType: HitType;
segNum, cpNum, jointNum: INT;
hitPoint: Point;
jointParts: TrajParts;
traj: Slice ¬ sliceD.slice;
trajData: TrajData ¬ NARROW[traj.data];
[hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGTraj.UnpackHitData[hitData];
SELECT hitType
FROM
interior => {
jointDist, cpDist: REAL;
bestControlPoint, bestSeg: NAT;
bestPoint, bestCPPoint: Point;
bestNormal: Vector;
jointSuccess, cpSuccess: BOOL ¬ FALSE;
[jointDist, jointNum, bestPoint, bestNormal, jointSuccess] ¬ GGTraj.NearestJoint[mapPoint, sliceD, 9999.0];
[cpDist, bestSeg, bestControlPoint, bestCPPoint, bestNormal, cpSuccess] ¬ GGTraj.NearestControlPoint[mapPoint, sliceD, 9999.0];
IF jointSuccess
THEN {
IF cpSuccess
THEN {
IF cpDist < jointDist
THEN {
seg: Segment;
jointParts ¬ GGSequence.CreateFromControlPoint[trajData, bestSeg, bestControlPoint];
point ¬ bestCPPoint;
seg ¬ GGTraj.FetchSegment[traj, bestSeg];
normal ¬ seg.class.cPNormal[seg, bestControlPoint, bestCPPoint, testPoint];
}
ELSE {
jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum];
point ¬ bestPoint;
normal ¬ FindJointNormal[];
};
}
ELSE {
jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum];
point ¬ bestPoint;
normal ¬ FindJointNormal[];
};
}
ELSE ERROR; -- no joints and no control points
};
joint => {
jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum];
point ¬ mapPoint;
normal ¬ FindJointNormal[];
};
controlPoint => {
seg: Segment;
jointParts ¬ GGSequence.CreateFromControlPoint[trajData, segNum, cpNum];
point ¬ mapPoint;
seg ¬ GGTraj.FetchSegment[traj, segNum];
normal ¬ seg.class.cPNormal[seg, cpNum, point, testPoint];
};
segment => {
NearestJointToHitData:
PROC []
RETURNS [jointNum:
NAT] = {
nextNum: NAT;
p1, p2: Point;
d1, d2: REAL;
trajHitData: TrajHitData ¬ NARROW[hitData];
SELECT trajHitData.hitType
FROM
joint => {
jointNum ¬ trajHitData.jointNum;
};
segment, controlPoint => {
nextNum ¬ GGTraj.FollowingJoint[traj, trajHitData.segNum];
p1 ¬ GGTraj.FetchJointPos[traj, trajHitData.segNum];
p2 ¬ GGTraj.FetchJointPos[traj, nextNum];
d1 ¬ Vectors2d.DistanceSquared[p1, trajHitData.hitPoint];
d2 ¬ Vectors2d.DistanceSquared[p2, trajHitData.hitPoint];
IF d1 <= d2 THEN jointNum ¬ trajHitData.segNum
ELSE jointNum ¬ nextNum;
};
ENDCASE => ERROR;
};
success: BOOL ¬ FALSE;
jointPoint, cpPoint: Point;
cpNormal: Vector;
seg: Segment;
seg ¬ GGTraj.FetchSegment[traj, segNum];
jointNum ¬ NearestJointToHitData[];
jointPoint ¬ GGTraj.FetchJointPos[traj, jointNum];
[cpPoint, cpNormal, cpNum, success] ¬ seg.class.closestControlPoint[seg, mapPoint, GGUtility.plusInfinity];
IF
NOT success
THEN {
-- no control point found. Use a joint.
jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum];
point ¬ jointPoint;
normal ¬ FindJointNormal[];
}
ELSE {
-- could be a cp instead of a joint
cpDist: REAL ¬ Vectors2d.DistanceSquared[cpPoint, mapPoint];
jointDist: REAL ¬ Vectors2d.DistanceSquared[jointPoint, mapPoint];
tisAJoint: BOOL ¬ jointDist <= cpDist;
IF tisAJoint
THEN {
jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum];
point ¬ jointPoint;
normal ¬ FindJointNormal[];
}
ELSE {
jointParts ¬ GGSequence.CreateFromControlPoint[trajData, segNum, cpNum];
point ¬ cpPoint;
normal ¬ cpNormal;
};
};
};
ENDCASE => ERROR;
jointD ¬ GGSlice.DescriptorFromParts[traj, jointParts];
};
TrajClosestPointAndTangent:
PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance:
REAL]
RETURNS [bestPoint: Point ¬ [0.0, 0.0], bestDist:
REAL ¬ 0.0, tangent: Vector ¬ [0,-1], hitData:
REF
ANY, success:
BOOL ¬
FALSE] = {
};
TrajClosestSegment:
PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance:
REAL]
RETURNS [bestPoint: Point, bestDist:
REAL, bestNormal: Vector ¬ [0,-1], hitData:
REF
ANY, success:
BOOL ¬
FALSE] = {
GGModelTypes.SliceClosestSegmentProc
bestSegNum: NAT;
trajHitData: TrajHitData;
[bestDist, bestSegNum, bestPoint, bestNormal, success] ¬ GGTraj.NearestSegment[testPoint, sliceD, tolerance];
IF success
THEN {
hitData ¬ trajHitData ¬
NEW[TrajHitDataObj ¬ [
segment, bestSegNum, -1, -1, bestPoint]];
};
};
InOutOn: TYPE = {in, out, on};
InsideContour:
PROC [p: Point, pairs: GGSegmentTypes.PairSequence]
RETURNS [
BOOL] ~ {
RETURN[HanrahanInsideContour[p, pairs] # out]
RETURN[BoysePointInPoly[p, pairs] # out];
};
HanrahanInsideContour:
PROC [p: Point, pairs: GGSegmentTypes.PairSequence]
RETURNS [InOutOn] ~ {
Based on code from Pat Hanrahan:
zcross: REAL;
odd: BOOL ¬ FALSE;
d2: Point ¬ [pairs[pairs.length-1].x-p.x, pairs[pairs.length-1].y-p.y];
FOR n:
NAT
IN [0..pairs.length)
DO
d1: Point ¬ d2;
d2 ¬ [pairs[n].x-p.x, pairs[n].y-p.y];
IF (d1.y > 0
AND d2.y > 0)
OR (d1.y < 0
AND d2.y < 0)
OR (d1.x < 0
AND d2.x < 0)
THEN LOOP; -- no chance to cross
IF (zcross ¬ d2.y*d1.x-d1.y*d2.x) = 0.0 THEN RETURN[on]; -- on the edge
IF (d1.y > 0 OR d2.y > 0) AND (zcross < 0) # (d1.y-d2.y < 0) THEN odd ¬ NOT odd;
ENDLOOP;
RETURN[IF odd THEN in ELSE out];
};
PolylineOfTraj:
PROC [trajData: TrajData, tolerance:
REAL]
RETURNS [wholePolyline: GGSegmentTypes.PairSequence, success:
BOOL ¬
TRUE] = {
polyList: LIST OF GGSegmentTypes.PairSequence;
segCount: NAT ¬ 0;
GetPolyline:
PROC [traj: TrajData, seg: Segment, index:
NAT]
RETURNS [done:
BOOL ¬
FALSE] = {
thisPolyline: GGSegmentTypes.PairSequence ¬ seg.class.asPolyline[seg, tolerance];
segCount ¬ segCount+(thisPolyline.length-1);
polyList ¬ CONS[thisPolyline, polyList];
};
[] ¬ GGSequence.WalkSegmentsInTraj[trajData, GetPolyline];
IF polyList = NIL THEN RETURN[NIL, FALSE]
ELSE {
index: NAT ¬ 0;
success ¬ TRUE;
wholePolyline ¬ NEW[GGSegmentTypes.PairSequenceRep[segCount]];
wholePolyline.length ¬ segCount;
FOR list:
LIST
OF GGSegmentTypes.PairSequence ¬ polyList, list.rest
UNTIL list =
NIL
DO
FOR j:
NAT
DECREASING IN [1..list.first.length)
DO
wholePolyline[index] ¬ list.first[j];
index ¬ index + 1;
ENDLOOP;
ENDLOOP;
};
};
TrajFilledPathsUnderPoint:
PROC [slice: Slice, point: Point, tolerance:
REAL]
RETURNS [hitData:
REF
ANY ¬
NIL, moreHitDatas:
LIST
OF
REF
ANY ¬
NIL] = {
Since trajectories don't have a fill color (outlines do), we will answer the question as though trajectories were always filled.
trajData: TrajData ¬ NARROW[slice.data];
wholePolyline: GGSegmentTypes.PairSequence;
success: BOOL ¬ TRUE;
IF
NOT GGBoundBox.PointIsInGrownBox[point, GGSliceOps.GetTightBox[slice], tolerance]
THEN RETURN;
IF trajData.role = open
THEN
RETURN;
IF trajData.polyline #
NIL
AND trajData.polylineTolerance <= tolerance
THEN wholePolyline ¬ trajData.polyline
ELSE {
[wholePolyline, success] ¬ PolylineOfTraj[trajData, tolerance];
IF NOT success THEN RETURN[NIL, NIL];
trajData.polyline ¬ wholePolyline; -- cache the polyline for later use
trajData.polylineTolerance ¬ tolerance;
};
IF InsideContour[point, wholePolyline]
THEN {
hitData ¬ NEW[TrajHitDataObj ¬ [interior, -1, -1, -1, point]];
};
};
TrajLineIntersection:
PROC [sliceD: SliceDescriptor, line: Line]
RETURNS [points:
LIST
OF Point, pointCount:
NAT ¬ 0] = {
segGen: SegmentGenerator;
thesePoints: LIST OF Point;
thisCount: NAT ¬ 0;
trajParts: TrajParts ¬ NARROW[sliceD.parts];
trajData: TrajData ¬ NARROW[sliceD.slice.data];
segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts];
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;
};
TrajCircleIntersection:
PROC [sliceD: SliceDescriptor, circle: Circle]
RETURNS [points:
LIST
OF Point, pointCount:
NAT ¬ 0] = {
segGen: SegmentGenerator;
thesePoints: LIST OF Point;
thisCount: NAT ¬ 0;
trajParts: TrajParts ¬ NARROW[sliceD.parts];
trajData: TrajData ¬ NARROW[sliceD.slice.data];
segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts];
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;
pointCount ¬ pointCount + thisCount;
ENDLOOP;
};
TrajHitDataAsSimpleCurve:
PROC [slice: Slice, hitData:
REF
ANY]
RETURNS [simpleCurve:
REF
ANY] = {
simpleCurve will be of type Edge, Arc, etc. There will be Conic and Bezier types as well.
trajHitData: TrajHitData ¬ NARROW[hitData];
SELECT trajHitData.hitType
FROM
joint, controlPoint, interior => RETURN[NIL];
segment => {
segNum: INT ¬ trajHitData.segNum;
seg: Segment ¬ GGTraj.FetchSegment[slice, segNum];
simpleCurve ¬ seg.class.asSimpleCurve[seg, trajHitData.hitPoint];
};
ENDCASE => ERROR;
};
TrajSetDefaults:
PROC [slice: Slice, parts: SliceParts, defaults: DefaultData, history: HistoryEvent] = {
maybe should implement parts=NIL to mean all parts
this is inefficient but easy to implement
[] ¬ GGSliceOps.SetStrokeWidth[slice, parts, defaults.strokeWidth, NIL];
GGSliceOps.SetStrokeJoint[slice, parts, defaults.strokeJoint, NIL];
GGSliceOps.SetStrokeEnd[slice, parts, defaults.strokeEnd, NIL];
GGSliceOps.SetDashed[slice, parts, defaults.dashed, GGUtility.CopyPattern[defaults.pattern], defaults.offset, defaults.length, NIL];
GGSliceOps.SetStrokeColor[slice, parts, defaults.strokeColor, $Set, NIL];
};
TrajSetStrokeWidth:
PROC [slice: Slice, parts: SliceParts, strokeWidth:
REAL, history: HistoryEvent]
RETURNS [box: BoundBox] = {
bBoxes: LIST OF BoundBox;
thisBox: BoundBox;
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData];
FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
seg.class.setStrokeWidth[seg, strokeWidth];
thisBox ¬ seg.class.boundBox[seg];
IF thisBox#NIL THEN bBoxes ¬ CONS[thisBox, bBoxes];
ENDLOOP;
box ¬ GGBoundBox.BoundBoxOfBoxes[bBoxes];
GGSlice.KillBoundBoxOnly[slice];
No need to kill tight box. It is still good.
};
TrajGetStrokeWidth:
PROC [slice: Slice, parts: SliceParts]
RETURNS [strokeWidth:
REAL ¬ -1.0, isUnique:
BOOL ¬
TRUE] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
found, aborted: BOOL ¬ FALSE;
IF parts =
NIL
OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0
THEN {
DoCheckWidth:
PROC [traj: TrajData, seg: Segment, index:
NAT]
RETURNS [done:
BOOL ¬
FALSE] = {
IF found
THEN {
IF seg.strokeWidth # strokeWidth THEN done ¬ TRUE;
}
ELSE {
found ¬ TRUE;
strokeWidth ¬ seg.strokeWidth;
};
};
IF parts#
NIL
THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckWidth]
ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckWidth];
isUnique ¬ NOT aborted;
}
ELSE {
strokeWidth ← -1.0;
isUnique ¬ FALSE;
};
};
Traj
SetStrokeEnd:
PROC [slice: Slice, parts: SliceParts, strokeEnd: StrokeEnd, history: HistoryEvent] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData];
FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
seg.strokeEnd ¬ strokeEnd;
ENDLOOP;
};
Traj
GetStrokeEnd:
PROC [slice: Slice, parts: SliceParts]
RETURNS [strokeEnd: StrokeEnd ¬ round, isUnique:
BOOL ¬
TRUE] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
found, aborted: BOOL ¬ FALSE;
IF parts =
NIL
OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0
THEN {
DoCheckEnd:
PROC [traj: TrajData, seg: Segment, index:
NAT]
RETURNS [done:
BOOL ¬
FALSE] = {
IF found
THEN {
IF seg.strokeEnd # strokeEnd THEN done ¬ TRUE;
}
ELSE {
found ¬ TRUE;
strokeEnd ¬ seg.strokeEnd;
};
};
IF parts#
NIL
THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckEnd]
ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckEnd];
isUnique ¬ NOT aborted;
}
ELSE {
strokeEnd ← round;
isUnique ¬ FALSE;
};
};
Traj
SetStrokeJoint:
PROC [slice: Slice, parts: SliceParts, strokeJoint: StrokeJoint, history: HistoryEvent] = {
trajData: TrajData ¬ NARROW[slice.data];
trajData.strokeJoint ¬ strokeJoint;
};
Traj
GetStrokeJoint:
PROC [slice: Slice, parts: SliceParts]
RETURNS [strokeJoint: StrokeJoint, isUnique:
BOOL ¬
TRUE] = {
trajData: TrajData ¬ NARROW[slice.data];
RETURN[trajData.strokeJoint];
};
Traj
SetStrokeColor:
PROC [slice: Slice, parts: SliceParts, color: Color, setHow:
ATOM, history: HistoryEvent] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData];
FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
SELECT setHow
FROM
$Set => seg.color ¬ color;
$ChangeHue => {
newColor: Color ¬ GGUtility.ChangeHue[seg.color, color];
seg.color ¬ newColor;
};
ENDCASE => ERROR;
ENDLOOP;
};
Traj
GetStrokeColor:
PROC [slice: Slice, parts: SliceParts]
RETURNS [color: Color, isUnique:
BOOL ¬
TRUE] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
found, aborted: BOOL ¬ FALSE;
IF parts =
NIL
OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0
THEN {
DoCheckColor:
PROC [traj: TrajData, seg: Segment, index:
NAT]
RETURNS [done:
BOOL ¬
FALSE] = {
IF found
THEN {
IF NOT GGCoreOps.EquivalentColors[seg.color, color] THEN done ¬ TRUE;
}
ELSE {
found ¬ TRUE;
color ¬ seg.color;
};
};
IF parts#
NIL
THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckColor]
ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckColor];
isUnique ¬ NOT aborted;
}
ELSE
{
success: BOOL ¬ FALSE;
partType: TrajPartType;
jointNum: NAT;
seg: Segment;
IF trajParts.jointCount > 0
THEN {
[success, partType, ----, ----, jointNum, ----, ----, seg, ----] ¬ GGSequence.UnpackSimpleSequence[slice, parts];
IF success
THEN {
SELECT partType
FROM
joint => {
IF trajData.segCount > 0
THEN {
IF jointNum <= GGTraj.HiSegmentTraj[trajData] THEN color ¬ GGTraj.FetchSegmentTraj[trajData, jointNum].color
ELSE color ¬ GGTraj.FetchSegmentTraj[trajData, jointNum-1].color;
};
};
controlPoint, segment => color ¬ seg.color;
ENDCASE;
};
};
};
};
TrajSetArrows:
PROC [slice: Slice, parts: SliceParts, leftDown, rightUp:
BOOL, history: HistoryEvent] = {
Will there be an arrowhead on the lo end of traj? On the hi end?
trajData: TrajData ¬ NARROW[slice.data];
trajData.loArrow ¬ leftDown;
trajData.hiArrow ¬ rightUp;
};
TrajGetArrows:
PROC [slice: Slice]
RETURNS [leftDown, rightUp:
BOOL ¬
FALSE] = {
trajData: TrajData ¬ NARROW[slice.data];
RETURN[trajData.loArrow, trajData.hiArrow];
};
Traj
SetDashed:
PROC [slice: Slice, parts: SliceParts, dashed:
BOOL, pattern: SequenceOfReal ¬
NIL, offset:
REAL ¬ 0.0, length:
REAL ¬ -1.0, history: HistoryEvent] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData];
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;
};
TrajGetDashed:
PROC [slice: Slice, parts: SliceParts]
RETURNS [dashed:
BOOL ¬
FALSE, pattern: SequenceOfReal, offset, length:
REAL ¬ 0.0, isUnique:
BOOL ¬
TRUE] = {
trajParts: TrajParts ¬ NARROW[parts];
trajData: TrajData ¬ NARROW[slice.data];
segCount: NAT ¬ GGSequence.CountSegmentsInSequence[trajData, trajParts];
found, aborted: BOOL ¬ FALSE;
IF segCount > 0
THEN {
DoCheckDashes:
PROC [traj: TrajData, seg: Segment, index:
NAT]
RETURNS [done:
BOOL ¬
FALSE] = {
IF found
THEN
done ¬
seg.dashed # dashed OR (seg.dashed AND
(seg.offset # offset OR
seg.length # length OR
NOT GGUtility.EquivalentPatterns[seg.pattern, pattern]))
ELSE {
found ¬ TRUE;
dashed ¬ seg.dashed;
offset ¬ seg.offset;
length ¬ seg.length;
pattern ¬ seg.pattern;
};
};
IF parts#
NIL
THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckDashes]
ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckDashes];
isUnique ¬ NOT aborted;
}
ELSE RETURN[FALSE, NIL, 0.0, 0.0, FALSE];
};
TrajSetOrientation:
PROC [slice: Slice, parts: SliceParts, orientation: Orientation, history: HistoryEvent]
RETURNS [success:
BOOL ¬
TRUE] = {
trajData: TrajData ¬ NARROW[slice.data];
SELECT orientation
FROM
cw => {
IF
NOT GGTraj.IsClockwiseTraj[slice]
THEN GGTraj.ReverseTraj[slice];
trajData.forward ¬ TRUE; -- trajData.forward MEANS IsClockwiseTraj
};
ccw => {
IF GGTraj.IsClockwiseTraj[slice]
THEN GGTraj.ReverseTraj[slice];
trajData.forward ¬ FALSE; -- trajData.forward MEANS IsClockwiseTraj
};
reverse => {
GGTraj.ReverseTraj[slice];
trajData.forward ¬ NOT trajData.forward;
};
ENDCASE => ERROR;
};
TrajGetOrientation:
PROC [slice: Slice, parts: SliceParts]
RETURNS [orientation: Orientation, isUnique:
BOOL ¬
TRUE] = {
trajData: TrajData ¬ NARROW[slice.data];
RETURN[IF trajData.forward THEN cw ELSE ccw];
};
FetchSliceClass:
PUBLIC
PROC [type:
ATOM]
RETURNS [class: SliceClass] = {
FOR l:
LIST
OF SliceClassDef ¬ sliceClasses, l.rest
UNTIL l=
NIL
DO
IF l.first.type=type THEN RETURN[l.first.class];
ENDLOOP;
SIGNAL Problem[msg: "Slice class not found."];
RETURN[NIL];
};
RegisterSliceClass:
PUBLIC
PROC [class: SliceClass] = {
classDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: class.type, class: class]];
sliceClasses ¬ CONS[classDef, sliceClasses];
};
UpdateDescriptorBoundBoxes: PUBLIC PROC [sliceD: SliceDescriptor] = {
Slice classes other than outlines don't use descriptor bound boxes !!
IF GGSliceOps.GetType[sliceD.slice]=$Outline THEN {
parts: OutlineParts ← NARROW[sliceD.parts];
FOR list: LIST OF Sequence ← parts.descriptors, list.rest UNTIL list = NIL DO
IF list.first # NIL THEN GGSequence.UpdateBoundBox[list.first];
ENDLOOP;
};
};
DescriptorFromParts:
PUBLIC
PROC [slice: Slice, parts: SliceParts]
RETURNS [sliceD: SliceDescriptor] = {
sliceD ¬ NEW[SliceDescriptorObj ¬ [slice, parts]];
IF testing THEN masterSDlist ¬ CONS[sliceD, masterSDlist]; -- kludge for testing
};
testing: BOOL ¬ FALSE;
masterSDlist: LIST OF SliceDescriptor; -- kludge for testing
UnlinkAllSliceDescriptors:
PROC [list:
LIST
OF SliceDescriptor] = {
For use ONLY to destroy REFs in slices so that Cedar garbage collector can collect storage. Only for wizards.
FOR sdList:
LIST
OF SliceDescriptor ¬
masterSDlist, sdList.rest
UNTIL sdList=
NIL
DO
IF sdList.first.slice=NIL THEN LOOP;
IF sdList.first.slice.data#NIL THEN GGSlice.UnlinkSlice[sdList.first.slice];
sdList.first.slice ¬ NIL;
ENDLOOP;
masterSDlist ¬ NIL; -- drop the list pointers. All SD should be collectible
};
UnlinkSlice:
PUBLIC
PROC [slice: Slice] = {
For use ONLY to destroy REFs in slices so that Cedar garbage collector can collect storage. Only for the initiated.
IF slice#
NIL
AND slice.data#
NIL
THEN {
-- may have already been unlinked
break links from selected slice descriptors to slices
IF slice.normalSelectedParts#NIL THEN slice.normalSelectedParts.slice ¬ NIL;
IF slice.hotSelectedParts#NIL THEN slice.hotSelectedParts.slice ¬ NIL;
IF slice.activeSelectedParts#NIL THEN slice.activeSelectedParts.slice ¬ NIL;
IF slice.matchSelectedParts#NIL THEN slice.matchSelectedParts.slice ¬ NIL;
IF slice.nullDescriptor#NIL THEN slice.nullDescriptor.slice ¬ NIL;
IF slice.fullDescriptor#
NIL
THEN slice.fullDescriptor.slice ¬
NIL;
break link from slices to selected slice descriptors
slice.normalSelectedParts ¬ slice.hotSelectedParts ¬ slice.activeSelectedParts ¬ slice.matchSelectedParts ¬ slice.nullDescriptor ¬ slice.fullDescriptor ¬
NIL;
NIL out other REFs for garbage collection efficiency
slice.class ¬ NIL; slice.data ¬ NIL; slice.parent ¬ NIL; slice.boundBox ¬ NIL; slice.tightBox ¬ NIL;
};
};
EntitiesInSlice:
PUBLIC
PROC [slice: Slice]
RETURNS [entityGenerator: SliceGenerator] = {
SIGNAL Problem[msg: "GGSlice.EntitiesInSlice NYI"];
};
Class-Specific Routines for Traj
Init:
PROC [] = {
textDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Text, class: GGSlice.BuildTextSliceClass[]]];
ipDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $IP, class: GGSlice.BuildIPSliceClass[]]];
boxDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Box, class: GGSlice.BuildBoxSliceClass[]]];
circleDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Circle, class: GGSlice.BuildCircleSliceClass[]]];
outlineDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Outline, class: GGSlice.BuildOutlineSliceClass[]]];
clusterDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Cluster, class: GGSlice.BuildClusterSliceClass[]]];
trajDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Traj, class: GGSlice.BuildTrajSliceClass[]]];
sliceClasses ¬ LIST[trajDef, outlineDef, clusterDef, circleDef, boxDef, ipDef, textDef];
printPrefix ¬ Atom.MakeAtom["xerox/pressfonts/"];
screenPrefix ¬Atom.MakeAtom ["xerox/tiogafonts/"];
};
sliceClasses: LIST OF SliceClassDef;
printPrefix: ATOM;
screenPrefix: ATOM;
Init[];
END.