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;
};
Textual Description
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;
};
Part Generators
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];
};
Hit Testing
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;
};
Style
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;
};
};
TrajSetStrokeEnd: 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;
};
TrajGetStrokeEnd: 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;
};
};
TrajSetStrokeJoint: PROC [slice: Slice, parts: SliceParts, strokeJoint: StrokeJoint, history: HistoryEvent] = {
trajData: TrajData ¬ NARROW[slice.data];
trajData.strokeJoint ¬ strokeJoint;
};
TrajGetStrokeJoint: PROC [slice: Slice, parts: SliceParts] RETURNS [strokeJoint: StrokeJoint, isUnique: BOOL ¬ TRUE] = {
trajData: TrajData ¬ NARROW[slice.data];
RETURN[trajData.strokeJoint];
};
TrajSetStrokeColor: 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;
};
TrajGetStrokeColor: 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];
};
TrajSetDashed: 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];
};
Class registration
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.