GGSliceImplE.mesa
Contents: Implements some of the Traj slice class (see GGSliceImplD for more).
Copyright Ó 1987, 1988 by Xerox Corporation. All rights reserved.
Created By: Ken Pier, December 3, 1987 3:25:32 pm PST
Last Edited By: Ken Pier, September 20, 1990 10:33 am PDT
Bier, August 31, 1989 10:08:47 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: BOOLFALSE, dashed: BOOLFALSE, 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.PutF["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.CR];
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.PutF[" (%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.PutF[" (%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: BOOLFALSE;
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, good: BOOL;
Traj (fence) [3] arrows: 0:
IF version < 8802.04 THEN GGParseIn.ReadWRope[f, "Traj"]; -- gone as of version 8802.04
GGParseIn.ReadWRope[f, "("];
roleName ← GGParseIn.ReadWWord[f];
role ← RoleFromRope[roleName];
GGParseIn.ReadWRope[f, ")"];
GGParseIn.ReadWRope[f, "["];
hiJoint ← GGParseIn.ReadWNAT[f];
GGParseIn.ReadWRope[f, "]"];
IF version >= 8607.22 THEN {
GGParseIn.ReadWRope[f, "arrows:"];
arrowCode ← GGParseIn.ReadWNAT[f];
}
ELSE arrowCode ← 0;
IF version >= 8702.26 THEN {
GGParseIn.ReadWRope[f, "j:"];
strokeJoint ← GGParseIn.ReadStrokeJoint[f];
GGParseIn.ReadWRope[f, "e:"];
[endsOK, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
strokeEnd ← GGParseIn.ReadStrokeEnd[f];
}
ELSE {
strokeJoint ← round;
endsOK ← FALSE;
};
IF version >= 8701.135 THEN {
GGParseIn.ReadWRope[f, "w:"];
strokeWidth ← GGParseIn.ReadWReal[f];
widthOK ← strokeWidth>= 0.0;
GGParseIn.ReadWRope[f, "c:"];
[colorOK, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
strokeColor ← GGParseIn.ReadColor[f, version];
}
ELSE {
GGParseIn.ReadWRope[f, ":"];
widthOK ← FALSE;
colorOK ← FALSE;
};
IF version >= 8701.23
THEN {
GGParseIn.ReadWRope[f, "d:"];
[dashOK, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
IF dashOK THEN {
[dashed, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
IF dashed THEN {
pattern ← GGParseIn.ReadArrayOfReal[f];
offset ← GGParseIn.ReadWReal[f];
length ← GGParseIn.ReadWReal[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.ReadWRope[f, "("];
className ← GGParseIn.ReadWWord[f];
IF NOT widthOK THEN strokeWidth ← GGParseIn.ReadWReal[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, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
IF dashed THEN {
pattern ← GGParseIn.ReadArrayOfReal[f];
offset ← GGParseIn.ReadWReal[f];
length ← GGParseIn.ReadWReal[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.ReadWRope[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.ReadWRope[f, "("];
className ← GGParseIn.ReadWWord[f];
IF NOT widthOK THEN strokeWidth ← GGParseIn.ReadWReal[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, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
IF dashed THEN {
pattern ← GGParseIn.ReadArrayOfReal[f];
offset ← GGParseIn.ReadWReal[f];
length ← GGParseIn.ReadWReal[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.ReadWRope[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.ReadWRope[f, "fwd:"];
[fwd, good] ← GGParseIn.ReadBool[f, version];
IF NOT good THEN ERROR;
}
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, bestDist: REAL, bestNormal: Vector ← [0, -1], hitData: REF ANY, success: BOOLFALSE] = {
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;
[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: BOOLFALSE;
[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: BOOLFALSE] = {
};
TrajClosestSegment: PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, bestNormal: Vector ← [0,-1], hitData: REF ANY, success: BOOLFALSE] = {
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: BOOLFALSE;
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: BOOLTRUE] = {
polyList: LIST OF GGSegmentTypes.PairSequence;
segCount: NAT ← 0;
GetPolyline: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOLFALSE] = {
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 ANYNIL, moreHitDatas: LIST OF REF ANYNIL] = {
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: BOOLTRUE;
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: BOOLTRUE] = {
trajParts: TrajParts ← NARROW[parts];
trajData: TrajData ← NARROW[slice.data];
found, aborted: BOOLFALSE;
IF parts = NIL OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0 THEN {
DoCheckWidth: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOLFALSE] = {
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: BOOLTRUE] = {
trajParts: TrajParts ← NARROW[parts];
trajData: TrajData ← NARROW[slice.data];
found, aborted: BOOLFALSE;
IF parts = NIL OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0 THEN {
DoCheckEnd: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOLFALSE] = {
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: BOOLTRUE] = {
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: BOOLTRUE] = {
trajParts: TrajParts ← NARROW[parts];
trajData: TrajData ← NARROW[slice.data];
found, aborted: BOOLFALSE;
IF parts = NIL OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0 THEN {
DoCheckColor: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE;
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: BOOLFALSE] = {
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: BOOLFALSE, pattern: SequenceOfReal, offset, length: REAL ← 0.0, isUnique: BOOLTRUE] = {
trajParts: TrajParts ← NARROW[parts];
trajData: TrajData ← NARROW[slice.data];
segCount: NAT ← GGSequence.CountSegmentsInSequence[trajData, trajParts];
found, aborted: BOOLFALSE;
IF segCount > 0 THEN {
DoCheckDashes: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOLFALSE] = {
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: BOOLTRUE] = {
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: BOOLTRUE] = {
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: BOOLFALSE;
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 𡤊tom.MakeAtom ["xerox/tiogafonts/"];
};
sliceClasses: LIST OF SliceClassDef;
printPrefix: ATOM;
screenPrefix: ATOM;
Init[];
END.