DIRECTORY
Atom, Feedback, GGBasicTypes, GGBoundBox, GGDescribe, GGInterfaceTypes, GGModelTypes, GGOutline, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGTraj, GGTransform, GGUtility, Imager, ImagerColorPrivate, ImagerTransformation, IO, RealFns, Rope, Rosary, Vectors2d;

GGTrajImpl: CEDAR PROGRAM
IMPORTS Atom, Feedback, GGBoundBox, GGDescribe, GGOutline, GGParseIn, GGParseOut, GGSegment, GGSelect, GGSequence, GGShapes, GGTransform, GGUtility, Imager, ImagerColorPrivate, ImagerTransformation, IO, RealFns, Rope, Rosary, Vectors2d
EXPORTS GGTraj =
BEGIN

BitVector: TYPE = GGBasicTypes.BitVector;
BoundBox: TYPE = GGBasicTypes.BoundBox;
CameraData: TYPE = GGModelTypes.CameraData;
Color: TYPE = Imager.Color;
HitType: TYPE = GGModelTypes.TrajPartType;
StrokeEnd: TYPE = Imager.StrokeEnd;
StrokeJoint: TYPE = Imager.StrokeJoint;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
FenceHoleOpen: TYPE = GGModelTypes.FenceHoleOpen;
GGData: TYPE = GGInterfaceTypes.GGData;
Joint: TYPE = REF JointObj;
JointObj: TYPE = GGSegmentTypes.JointObj;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
OutlineData: TYPE = GGOutline.OutlineData;
Point: TYPE = GGBasicTypes.Point;
PointGenerator: TYPE = GGModelTypes.PointGenerator;
PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator;
Scene: TYPE = GGModelTypes.Scene;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
SegmentClass: TYPE = GGSegmentTypes.SegmentClass;
SelectionClass: TYPE = GGInterfaceTypes.SelectionClass;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
SequenceOfReal: TYPE = GGBasicTypes.SequenceOfReal;
TriggerBag: TYPE = GGInterfaceTypes.TriggerBag;
Traj: TYPE = REF TrajObj;
TrajObj: TYPE = GGModelTypes.TrajObj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
Vector: TYPE = GGBasicTypes.Vector;

Problem: ERROR [msg: Rope.ROPE] = Feedback.Problem;


CreateTraj: PUBLIC PROC [point: Point] RETURNS [traj: Traj] =  {
firstJoint: Joint _ NEW[JointObj _ [point: point]];
bBox: BoundBox _ GGBoundBox.CreateBoundBox[point.x, point.y, point.x, point.y];
traj _ NEW[TrajObj _ [open, 0, NIL, Rosary.FromItem[firstJoint], NIL, NIL, bBox, FALSE]];
};

AddSegment: PUBLIC PROC [traj: Traj, trajEnd: TrajEnd, seg: Segment, segEnd: TrajEnd] RETURNS [success: BOOL _ TRUE] =  {
diff: Vector;
loJoint, hiJoint: Joint;
outline: Outline;
IF traj.role = fence OR traj.role = hole THEN {success _ FALSE; RETURN};
IF trajEnd = segEnd THEN GGSegment.ReverseSegment[seg];
diff _ IF trajEnd = lo THEN Vectors2d.Sub[FetchJointPos[traj, 0], seg.hi]
ELSE Vectors2d.Sub[FetchJointPos[traj, traj.segCount], seg.lo];
GGSegment.TranslateSegment[seg, diff];
IF traj.segCount = 0 THEN {
traj.segments _ Rosary.FromItem[seg];
loJoint _ NEW[JointObj _ [point: seg.lo]];
hiJoint _ NEW[JointObj _ [point: seg.hi]];
traj.joints _ Rosary.FromList[LIST[loJoint, hiJoint]];
traj.segCount _ 1;
traj.boundBox _ GGBoundBox.CopyBoundBox[seg.class.boundBox[seg]];
}
ELSE {
IF trajEnd = lo THEN {
loJoint _ NEW[JointObj _ [point: seg.lo]];
traj.joints _ Rosary.Concat[Rosary.FromItem[loJoint], traj.joints];
traj.segments _ Rosary.Concat[Rosary.FromItem[seg], traj.segments];
}
ELSE {
hiJoint _ NEW[JointObj _ [point: seg.hi]];
traj.joints _ Rosary.Concat[traj.joints, Rosary.FromItem[hiJoint]];
traj.segments _ Rosary.Concat[traj.segments, Rosary.FromItem[seg]];
};
traj.segCount _ traj.segCount + 1;
GGBoundBox.EnlargeByBox[traj.boundBox, seg.class.boundBox[seg]];
outline _ GGOutline.OutlineOfTraj[traj];
IF outline#NIL THEN GGOutline.UpdateBoundBox[outline];
};
};

CloseWithSegment: PUBLIC PROC [traj: Traj, seg: Segment, segEnd: TrajEnd] = {
diff: Vector;
outline: Outline;
IF traj.segCount = 0 THEN ERROR Problem[msg: "single closed segment not implemented"];
IF segEnd = hi THEN GGSegment.ReverseSegment[seg];
diff _ Vectors2d.Sub[LastJointPos[traj], seg.lo];
GGSegment.TranslateSegment[seg, diff];
traj.segments _ Rosary.Concat[traj.segments, Rosary.FromItem[seg]];
traj.segCount _ traj.segCount + 1;
traj.role _ fence;
GGBoundBox.EnlargeByBox[traj.boundBox, seg.class.boundBox[seg]];
outline _ GGOutline.OutlineOfTraj[traj];
IF outline#NIL THEN GGOutline.UpdateBoundBox[outline];
};

CloseByDistorting: PUBLIC PROC [traj: Traj, distortEnd: TrajEnd] = {
seg: Segment;
loJoint, hiJoint: Joint;
outline: Outline;
loJoint _ FetchJoint[traj, 0];
hiJoint _ FetchJoint[traj, HiJoint[traj]];
SELECT distortEnd FROM
lo => {
seg _ FetchSegment[traj, 0];
loJoint.point _ hiJoint.point;
seg.lo _ hiJoint.point;
seg.class.endPointMoved[seg, TRUE, hiJoint.point];
};
hi => {
seg _ FetchSegment[traj, HiSegment[traj]];
hiJoint.point _ loJoint.point;
seg.hi _ loJoint.point;
seg.class.endPointMoved[seg, FALSE, loJoint.point];
};
ENDCASE => ERROR;
traj.joints _ Rosary.Substr[traj.joints, 0, HiJoint[traj]];
traj.role _ fence;
GGBoundBox.EnlargeByBox[traj.boundBox, seg.class.boundBox[seg]];
outline _ GGOutline.OutlineOfTraj[traj];
IF outline#NIL THEN GGOutline.UpdateBoundBox[outline];
};

OnlyChild: PUBLIC PROC [traj: Traj] RETURNS [BOOL] = {
outline: Outline _ traj.parent;
outlineData: OutlineData _ NARROW[outline.data];
RETURN[outlineData.children.rest = NIL];
};


CopyTrajFromRun: PUBLIC PROC [seq: Sequence] RETURNS [copy: Traj] = {
originalSegments: Rosary.ROSARY _ seq.traj.segments;
desiredSegments: Rosary.Segment;
loSegments, hiSegments, extractedSegments: Rosary.ROSARY;
originalJoints: Rosary.ROSARY _ seq.traj.joints;
desiredJoints: Rosary.Segment;
loJoints, hiJoints, extractedJoints: Rosary.ROSARY;
segGen: SegmentGenerator;
next: GGSequence.SegAndIndex;
s1, len1, s2, len2: INT;
jointCount: NAT;
newRole: GGModelTypes.FenceHoleOpen;
CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] =  {
CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = {
copy, oldSeg: Segment;
oldSeg _ NARROW[item];
copy _ GGSegment.CopySegment[oldSeg];
q[copy, 1];
};
[] _ Rosary.Map[desiredSegments, CopySegmentAndBuild];
};
CopyEachJoint: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] =  {
CopyJointAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = {
copy, oldJoint: Joint;
oldJoint _ NARROW[item];
copy _ CopyJoint[oldJoint];
q[copy, 1];
};
[] _ Rosary.Map[desiredJoints, CopyJointAndBuild];
};
segGen _ GGSequence.OrderedSegmentsInSequence[seq];
next _ GGSequence.NextSegmentAndIndex[segGen];
[s1, len1, s2, len2] _ GGUtility.BreakIntervalMODLen[next.index, seq.segCount, seq.traj.segCount];
IF s2 # -1 THEN {
desiredSegments _ [originalSegments, s1, len1];
loSegments _ Rosary.FromProcProc[CopyEachSegment];
desiredSegments _ [originalSegments, s2, len2];
hiSegments _ Rosary.FromProcProc[CopyEachSegment];
extractedSegments _ Rosary.Concat[hiSegments, loSegments];
}
ELSE {
desiredSegments _ [originalSegments, next.index, seq.segCount];
extractedSegments _ Rosary.FromProcProc[CopyEachSegment];
};
jointCount _ seq.segCount + 1; -- ignore the joint information in the sequence.  We just want all of the segments to have a joint on each side.  This distinction is particularly important when a single joint of a closed trajectory is being deleted.
[s1, len1, s2, len2] _ GGUtility.BreakIntervalMODLen[next.index, jointCount, HiJoint[seq.traj]+1];
IF s2 # -1 THEN {
desiredJoints _ [originalJoints, s1, len1];
loJoints _ Rosary.FromProcProc[CopyEachJoint];
desiredJoints _ [originalJoints, s2, len2];
hiJoints _ Rosary.FromProcProc[CopyEachJoint];
extractedJoints _ Rosary.Concat[hiJoints, loJoints];
}
ELSE {
desiredJoints _ [originalJoints, next.index, jointCount];
extractedJoints _ Rosary.FromProcProc[CopyEachJoint];
};
newRole _ IF GGSequence.IsComplete[seq] AND seq.traj.role # open THEN fence ELSE open;
copy _ NEW[TrajObj _ [newRole, seq.segCount, extractedSegments, extractedJoints, NIL, NIL, NIL, seq.traj.visibleJoints, seq.traj.strokeJoint ]];
copy.boundBox _ GGBoundBox.NullBoundBox[];
UpdateBoundBox[copy];
};

CopyTrajFromRange: PUBLIC PROC [original: Traj, start: INT, len: INT] RETURNS [piece: Traj] = {
originalSegments: Rosary.ROSARY _ original.segments;
desiredSegments: Rosary.Segment _ [originalSegments, start, len];
extractedSegments: Rosary.ROSARY;
originalJoints: Rosary.ROSARY _ original.joints;
desiredJoints: Rosary.Segment _ [originalJoints, start, len];
extractedJoints: Rosary.ROSARY;
CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] =  {
CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = {
copy, oldSeg: Segment;
oldSeg _ NARROW[item];
copy _ GGSegment.CopySegment[oldSeg];
q[copy, 1];
};
[] _ Rosary.Map[desiredSegments, CopySegmentAndBuild];
};
CopyEachJoint: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] =  {
CopyJointAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = {
copy, oldJoint: Joint;
oldJoint _ NARROW[item];
copy _ CopyJoint[oldJoint];
q[copy, 1];
};
[] _ Rosary.Map[desiredJoints, CopyJointAndBuild];
};
extractedSegments _ Rosary.FromProcProc[CopyEachSegment];
extractedJoints _ Rosary.FromProcProc[CopyEachJoint];
piece _ NEW[TrajObj _ [fence, len, extractedSegments, extractedJoints, NIL, NIL, NIL, original.visibleJoints, original.strokeJoint ]];
piece.boundBox _ GGBoundBox.NullBoundBox[];
UpdateBoundBox[piece];
};

Concat: PUBLIC PROC [fixed: Traj, fixedEnd: TrajEnd, moving: Traj, movingEnd: TrajEnd] RETURNS [longer: Traj] = {
diff: Vector;
fixed _ CopyTraj[fixed];
moving _ CopyTraj[moving];
IF fixedEnd = movingEnd THEN ReverseTraj[moving];
IF fixedEnd = hi THEN {
diff _ Vectors2d.Sub[FetchJointPos[fixed, HiJoint[fixed]], FetchJointPos[moving, 0]];
TranslateTraj[moving, diff];
longer _ NEW[TrajObj _ [
role: open,
segCount: fixed.segCount + moving.segCount,
segments: Rosary.Concat[fixed.segments, moving.segments],
joints: Rosary.Concat[fixed.joints, Rosary.Substr[moving.joints, 1]],
extraPoints: NIL,
parent: NIL,
boundBox: NIL,
visibleJoints: fixed.visibleJoints,
strokeJoint: fixed.strokeJoint,
selectedInPart: [FALSE, FALSE, FALSE]
]];
longer.boundBox _ GGBoundBox.NullBoundBox[];
UpdateBoundBox[longer];
}
ELSE { -- fixedEnd = lo
diff _ Vectors2d.Sub[FetchJointPos[fixed, 0], FetchJointPos[moving, HiJoint[moving]]];
TranslateTraj[moving, diff];
longer _ NEW[TrajObj _ [
role: open,
segCount: fixed.segCount + moving.segCount,
segments: Rosary.Concat[moving.segments, fixed.segments],
joints: Rosary.Concat[moving.joints, Rosary.Substr[fixed.joints, 1]],
extraPoints: NIL,
parent: NIL,
boundBox: NIL,
visibleJoints: fixed.visibleJoints,
strokeJoint: fixed.strokeJoint,
selectedInPart: [FALSE, FALSE, FALSE]
]];
longer.boundBox _ GGBoundBox.NullBoundBox[];
UpdateBoundBox[longer];
};
};

SpliceIn: PUBLIC PROC [run: Sequence, traj: Traj] RETURNS [newTraj: Traj] = {
oldOutline: Outline _ GGOutline.OutlineOfTraj[run.traj];
oldOutlineData: OutlineData _ NARROW[oldOutline.data];
SELECT run.traj.role FROM
hole => {
newTraj _ SpliceInClosed[run, traj];
newTraj.role _ hole;
[] _ GGOutline.ReplaceHole[oldOutline, run.traj, newTraj];
};
open => {
newTraj _ SpliceInOpen[run, traj];
newTraj.role _ open;
[] _ GGOutline.CreateOutline[newTraj, oldOutlineData.fillColor];
};
fence => {
newTraj _ SpliceInClosed[run, traj];
newTraj.role _ fence;
[] _ GGOutline.ReplaceFence[oldOutline, newTraj];
};
ENDCASE => ERROR;
newTraj.loArrow _ run.traj.loArrow;
newTraj.hiArrow _ run.traj.hiArrow;
};

IncludesLoEnd: PROC [run: Sequence] RETURNS [BOOL] = {
RETURN[run.segments[0]];
};

ResetEndSelectionBits: PROC [traj: Traj, lo, hi: BOOL _ TRUE] = {
joint: Joint;
IF lo THEN {
joint _ FetchJoint[traj, 0];
joint.TselectedInFull.active _ FALSE;
};
IF hi THEN {
joint _ FetchJoint[traj, HiJoint[traj]];
joint.TselectedInFull.active _ FALSE;
};
};

SpliceInOpen: PROC [run: Sequence, traj: Traj] RETURNS [newTraj: Traj] = {
runGen: SequenceGenerator;
wholeSeq, remainder, run1, run2, run3: Sequence;
traj1, traj2: Traj;
wholeSeq _ GGSequence.CreateComplete[run.traj];
remainder _ GGSequence.Difference[wholeSeq, run];
GGSequence.FillInJoints[remainder];
[runGen,----] _ GGSequence.RunsInSequence[remainder];
run1 _ GGSequence.NextSequence[runGen];
run2 _ GGSequence.NextSequence[runGen];
run3 _ GGSequence.NextSequence[runGen];
IF run3 # NIL THEN ERROR;
SELECT TRUE FROM
run1 = NIL => {
newTraj _ traj;
};
run2 = NIL => {
traj1 _ CopyTrajFromRun[run1];
IF IncludesLoEnd[run] THEN {
ResetEndSelectionBits[traj: traj1, lo: TRUE, hi: FALSE];
newTraj _ Concat[traj, hi, traj1, lo]; -- order is important.  traj1's lo joint is kept
}
ELSE {
ResetEndSelectionBits[traj: traj1, lo: FALSE, hi: TRUE];
newTraj _ Concat[traj, lo, traj1, hi];  -- order is important.  traj1's hi joint is kept
};
};
ENDCASE => {
traj1 _ CopyTrajFromRun[run1];
ResetEndSelectionBits[traj: traj1, lo: FALSE, hi: TRUE];
traj2 _ CopyTrajFromRun[run2];
ResetEndSelectionBits[traj: traj2, lo: TRUE, hi: FALSE];
newTraj _ Concat[traj, lo, traj1, hi];
newTraj _ Concat[newTraj, hi, traj2, lo];
};
};

SpliceInClosed: PROC [run: Sequence, traj: Traj] RETURNS [newTraj: Traj] = {
wholeSeq, remainder, run1, run2: Sequence;
runGen: SequenceGenerator;
traj1: Traj;

IF GGSequence.IsComplete[run] THEN RETURN[traj]; -- no need to splice.
wholeSeq _ GGSequence.CreateComplete[run.traj];
remainder _ GGSequence.Difference[wholeSeq, run];
GGSequence.FillInJoints[remainder];
[runGen, ----] _ GGSequence.RunsInSequence[remainder];
run1 _ GGSequence.NextSequence[runGen];
run2 _ GGSequence.NextSequence[runGen];
IF run1 = NIL OR run2 # NIL THEN ERROR;
traj1 _ CopyTrajFromRun[run1];
ResetEndSelectionBits[traj1];
newTraj _ Concat[traj, lo, traj1, hi]; -- order is important because traj1's joints are kept.
CloseByDistorting[newTraj, lo];
};


ReverseTraj: PUBLIC PROC [traj: Traj] = {
newSegments: Rosary.ROSARY _ NIL;
newJoints: Rosary.ROSARY _ NIL;
seg: Segment;
joint: Joint;
FOR i: NAT IN [0..traj.segCount) DO
seg _ FetchSegment[traj, i];
GGSegment.ReverseSegment[seg];
newSegments _ Rosary.Concat[Rosary.FromItem[seg], newSegments];
ENDLOOP;
FOR i: NAT IN [0..traj.segCount] DO
joint _ IF traj.role=open THEN FetchJoint[traj, i] ELSE FetchJoint[traj, (i MOD traj.segCount)];
newJoints _ Rosary.Concat[Rosary.FromItem[joint], newJoints];
ENDLOOP;
traj.segments _ newSegments;
traj.joints _ newJoints;
};

IsClockwiseTraj: PUBLIC PROC [traj: Traj] RETURNS [BOOL] = {
RETURN [SignedArea[traj]>0];
};

IsClockwiseTrajTransformSeq: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] RETURNS [BOOL] = {
RETURN [SignedAreaTransformSeq[seq, transform]>0];
};
GetBoundBox: PUBLIC PROC [traj: Traj] RETURNS [bBox: BoundBox] = {
UpdateBoundBox[traj];
RETURN[traj.boundBox];
};

UpdateBoundBox: PUBLIC PROC [traj: Traj] = {
segGen: SegmentGenerator;
segGen _ GGSequence.SegmentsInTraj[traj];
traj.boundBox^ _ GGBoundBox.emptyBoundBox^;
FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
GGBoundBox.EnlargeByBox[bBox: traj.boundBox, by: seg.class.boundBox[seg]];
ENDLOOP;
IF traj.parent#NIL THEN GGOutline.UpdateBoundBox[traj.parent];
};

CopyTraj: PUBLIC PROC [original: Traj] RETURNS [copy: Traj] = {
originalSegments: Rosary.ROSARY _ original.segments;
desiredSegments: Rosary.Segment _ [originalSegments, 0, original.segCount];
extractedSegments: Rosary.ROSARY;
originalJoints: Rosary.ROSARY _ original.joints;
desiredJoints: Rosary.Segment _ [originalJoints, 0, HiJoint[original] + 1];
extractedJoints: Rosary.ROSARY;
CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] =  {
CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = {
copy, oldSeg: Segment;
oldSeg _ NARROW[item];
copy _ GGSegment.CopySegment[oldSeg];
q[copy, 1];
};
[] _ Rosary.Map[desiredSegments, CopySegmentAndBuild];
};
CopyEachJoint: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] =  {
CopyJointAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = {
copy, oldJoint: Joint;
oldJoint _ NARROW[item];
copy _ CopyJoint[oldJoint];
q[copy, 1];
};
[] _ Rosary.Map[desiredJoints, CopyJointAndBuild];
};
extractedSegments _ Rosary.FromProcProc[CopyEachSegment];
extractedJoints _ Rosary.FromProcProc[CopyEachJoint];
copy _ NEW[TrajObj _ [original.role, original.segCount, extractedSegments, extractedJoints, NIL, NIL, NIL, original.visibleJoints, original.strokeJoint, original.loArrow, original.hiArrow]];
copy.boundBox _ GGBoundBox.NullBoundBox[];
UpdateBoundBox[copy];
};



DrawTraj: PUBLIC PROC [dc: Imager.Context, traj: Traj] = {
DrawTrajAux: PROC [dc: Imager.Context, traj: Traj] = {
Imager.SetStrokeJoint[dc, traj.strokeJoint];
FOR i: INT IN [0..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
IF seg.strokeWidth#0 AND seg.color#NIL THEN {
Imager.SetStrokeWidth[dc, seg.strokeWidth];
Imager.SetStrokeEnd[dc, seg.strokeEnd];
Imager.SetColor[dc, seg.color];
MaskStroke[dc, seg];
};
ENDLOOP;
};
seg: Segment;
IF AllStrokePropsAndColorsEqual[traj] THEN DrawSingleStrokeTraj[dc, traj]
ELSE DrawTrajAux[dc, traj]; -- why no DoSaveAll here ??
};

DrawSingleStrokeTraj: PROC [dc: Imager.Context, traj: Traj] = {
BuildPath: Imager.PathProc = {
seg: Segment;
firstPoint: Point _ FetchJointPos[traj, 0];
moveTo[firstPoint];
FOR i: INT IN [0..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
PatternProc: PROC [i: NAT] RETURNS [REAL] = {
RETURN[pattern[i]];
};
firstSeg: Segment _ FetchSegment[traj, 0];
strokeWidth: REAL _ firstSeg.strokeWidth;
pattern: SequenceOfReal _ firstSeg.pattern;
IF strokeWidth = 0.0 OR firstSeg.color=NIL THEN RETURN;
Imager.SetStrokeWidth[dc, strokeWidth];
Imager.SetStrokeEnd[dc, firstSeg.strokeEnd];
Imager.SetStrokeJoint[dc, traj.strokeJoint];
Imager.SetColor[dc, firstSeg.color];
IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length]
ELSE Imager.MaskStroke[dc, BuildPath, traj.role = fence OR traj.role = hole];
}; -- end DrawSingleStrokeTraj

DrawTrajSeq: PUBLIC PROC [dc: Imager.Context, seq: Sequence] = {
cpCount: NAT;
seg: Segment;
IF AllStrokePropsAndColorsEqual[seq.traj] THEN DrawSingleStrokeTrajSeq[dc, seq]
ELSE {
Imager.SetStrokeJoint[dc, seq.traj.strokeJoint];
FOR i: INT IN [0..HiSegment[seq.traj]] DO
seg _ FetchSegment[seq.traj, i];
IF seg.strokeWidth=0 OR seg.color=NIL THEN LOOP;
IF seq.segments[i] THEN {
Imager.SetStrokeWidth[dc, seg.strokeWidth];
Imager.SetStrokeEnd[dc, seg.strokeEnd];
Imager.SetColor[dc, seg.color];
SegMaskStroke[dc, seg];
cpCount _ seg.class.controlPointCount[seg];
FOR j: NAT IN [0..cpCount) DO
IF seq.controlPoints[i][j] THEN GGShapes.DrawCP[dc, seg.class.controlPointGet[seg, j]];
ENDLOOP;
};
ENDLOOP;
};
};

DrawSingleStrokeTrajSeq: PROC [dc: Imager.Context, seq: Sequence] = {
BuildPath: Imager.PathProc = {
FOR i: INT IN [0..HiSegment[seq.traj]] DO
IF seq.segments[i] THEN {
seg: Segment _ FetchSegment[seq.traj, i];
moveTo[seg.lo];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
};
ENDLOOP;
};
PatternProc: PROC [i: NAT] RETURNS [REAL] = {
RETURN[pattern[i]];
};
firstSeg: Segment _ FetchSegment[seq.traj, 0];
pattern: SequenceOfReal _ firstSeg.pattern;
IF firstSeg.strokeWidth = 0.0 OR firstSeg.color=NIL THEN RETURN;
Imager.SetStrokeWidth[dc, firstSeg.strokeWidth];
Imager.SetStrokeEnd[dc, firstSeg.strokeEnd];
Imager.SetStrokeJoint[dc, seq.traj.strokeJoint];
Imager.SetColor[dc, firstSeg.color];
IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length]
ELSE Imager.MaskStroke[dc, BuildPath, seq.traj.role = fence OR seq.traj.role = hole];
}; -- end DrawSingleStrokeTrajSeq

DrawTrajTransformSeq: PUBLIC PROC [dc: Imager.Context, selSeq: Sequence, transform: ImagerTransformation.Transformation] = {
cpCount: NAT;
seg: Segment;
IF AllStrokePropsAndColorsEqual[selSeq.traj] THEN DrawSingleStrokeTrajTransformSeq[dc, selSeq, transform]
ELSE {
point: Point;
Imager.SetStrokeJoint[dc, selSeq.traj.strokeJoint];
FOR i: INT IN [0..HiSegment[selSeq.traj]] DO
seg _ FetchSegment[selSeq.traj, i];
Imager.SetStrokeWidth[dc, (IF seg.strokeWidth#0 THEN seg.strokeWidth ELSE 1)];
Imager.SetStrokeEnd[dc, seg.strokeEnd];
Imager.SetColor[dc, IF seg.color#NIL THEN seg.color ELSE Imager.black];
MaskStrokeTransform[dc, seg, transform, selSeq.segments[i], selSeq.joints[i], selSeq.joints[IF selSeq.traj.role=open THEN (i+1) ELSE (i+1) MOD selSeq.traj.segCount], selSeq.controlPoints[i]];
cpCount _ seg.class.controlPointCount[seg];
FOR j: NAT IN [0..cpCount) DO
IF selSeq.controlPoints[i][j] THEN {
point _ ImagerTransformation.Transform[transform, seg.class.controlPointGet[seg, j]];
GGShapes.DrawCP[dc, point];
}
ELSE {
point _ seg.class.controlPointGet[seg, j];
GGShapes.DrawCP[dc, point];
};
ENDLOOP;
ENDLOOP;
};
};


DrawSingleStrokeTrajTransformSeq: PROC [dc: Imager.Context, selSeq: Sequence, transform: ImagerTransformation.Transformation] = {
BuildPath: Imager.PathProc = {
seg: Segment;
entire, lo, hi: BOOL;
controlPoints: BitVector;
firstPoint: Point;
entire _ selSeq.segments[0];
lo _ selSeq.joints[0];
firstPoint _ FetchJointPos[selSeq.traj, 0];
IF entire OR lo THEN firstPoint _ ImagerTransformation.Transform[transform, firstPoint];
moveTo[firstPoint];
FOR i: INT IN [0..HiSegment[selSeq.traj]] DO
seg _ FetchSegment[selSeq.traj, i];
entire _ selSeq.segments[i];
lo _ selSeq.joints[i];
hi _ selSeq.joints[IF selSeq.traj.role=open THEN (i+1) ELSE (i+1) MOD selSeq.traj.segCount];
controlPoints _ selSeq.controlPoints[i];
seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
PatternProc: PROC [i: NAT] RETURNS [REAL] = {
RETURN[pattern[i]];
};
firstSeg: Segment _ FetchSegment[selSeq.traj, 0];
strokeWidth: REAL _ firstSeg.strokeWidth;
pattern: SequenceOfReal _ firstSeg.pattern;
IF strokeWidth = 0.0 OR firstSeg.color=NIL THEN RETURN;
Imager.SetStrokeWidth[dc, strokeWidth];
Imager.SetStrokeEnd[dc, firstSeg.strokeEnd];
Imager.SetStrokeJoint[dc, selSeq.traj.strokeJoint];
Imager.SetColor[dc, firstSeg.color];
IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length]
ELSE Imager.MaskStroke[dc, BuildPath, selSeq.traj.role = fence OR selSeq.traj.role = hole];
}; -- end DrawSingleStrokeTrajTransformSeq

DrawSelectionFeedback: PUBLIC PROC [traj: Traj, selectedParts, hotParts: Sequence, dc: Imager.Context, camera: CameraData, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = {
DoDrawFeedback: PROC = {
segGen: GGModelTypes.SegmentGenerator;
jointGen: GGModelTypes.JointGenerator;
seg: Segment;
someNormal, someHot, thisCPisHot, thisCPisSelected: BOOL;
point: Point;
jointGen _ GGSequence.JointsInTraj[traj];
FOR i: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
thisCPisHot _ hotParts#NIL AND hotParts.joints[i];
thisCPisSelected _ selectedParts#NIL AND selectedParts.joints[i];
point _ FetchJointPos[traj, i];
IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, point, hot];
IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, point, normal];
IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawJoint[dc, point];
ENDLOOP;
someNormal _ selectedParts # NIL;
someHot _ hotParts # NIL;
segGen _ GGSequence.SegmentsInTraj[traj];
FOR next: GGSequence.SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
i: NAT _ next.index;
seg _ next.seg;
IF someNormal OR someHot THEN {
FOR j: INT IN [0..seg.class.controlPointCount[seg]) DO
thisCPisHot _ hotParts#NIL AND hotParts.controlPoints[i][j];
thisCPisSelected _ selectedParts#NIL AND selectedParts.controlPoints[i][j];
point _ seg.class.controlPointGet[seg, j];
IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, point, hot];
IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, point, normal];
IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawCP[dc, point];
ENDLOOP;
};
ENDLOOP;
};
IF caretIsMoving OR dragInProgress THEN RETURN;
IF selectedParts = NIL AND hotParts = NIL THEN RETURN;
IF camera.quality # quality THEN Imager.DoSaveAll[dc, DoDrawFeedback];
};



MaskStroke: PROC [dc: Imager.Context, seg: Segment] = {
MaskPath: Imager.PathProc = {
moveTo[seg.lo];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
};
PatternProc: PROC [i: NAT] RETURNS [REAL] = {
RETURN[pattern[i]];
};
pattern: SequenceOfReal _ seg.pattern;
IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length]
ELSE Imager.MaskStroke[dc, MaskPath, FALSE];
};

SegMaskStroke: PROC [dc: Imager.Context, seg: Segment] = {
MaskPath: Imager.PathProc = {
moveTo[seg.lo];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
};
PatternProc: PROC [i: NAT] RETURNS [REAL] = {
RETURN[pattern[i]];
};
pattern: SequenceOfReal _ seg.pattern;
IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length]
ELSE Imager.MaskStroke[dc, MaskPath, FALSE];
};

MaskStrokeTransform: PROC [dc: Imager.Context, seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector] = {
MaskPath: Imager.PathProc = {
loPoint: Point;
loPoint _ IF entire OR lo THEN ImagerTransformation.Transform[transform, seg.lo] ELSE seg.lo;
moveTo[loPoint];
seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo];
};
PatternProc: PROC [i: NAT] RETURNS [REAL] = {
RETURN[pattern[i]];
};
pattern: SequenceOfReal _ seg.pattern;
IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length]
ELSE Imager.MaskStroke[dc, MaskPath, FALSE];
};

EqualSequence: PROC [s1, s2: SequenceOfReal] RETURNS [BOOL] = {
IF s1.len # s2.len THEN RETURN[FALSE];
FOR i: NAT IN [0..s1.len) DO
IF s1[i] # s2[i] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};

AllStrokePropsAndColorsEqual: PUBLIC PROC [traj: Traj] RETURNS [BOOL] = {
firstSeg: Segment _ FetchSegment[traj, 0];
seg: Segment;
width: REAL _ firstSeg.strokeWidth;
end: StrokeEnd _ firstSeg.strokeEnd;
color: Color _ firstSeg.color;
dashed: BOOL _ firstSeg.dashed;
pattern: SequenceOfReal _ firstSeg.pattern;
offset: REAL _ firstSeg.offset;
length: REAL _ firstSeg.length;
FOR i: INT IN [1..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
IF seg.dashed # dashed THEN RETURN[FALSE];
IF seg.strokeEnd # end THEN RETURN[FALSE];
IF seg.strokeWidth # width THEN RETURN[FALSE];
IF NOT GGUtility.EquivalentColors[color, seg.color] THEN RETURN[FALSE];
IF NOT dashed THEN LOOP;
IF seg.offset # offset THEN RETURN[FALSE];
IF seg.length # length THEN RETURN[FALSE];
IF NOT EqualSequence[seg.pattern, pattern] THEN RETURN[FALSE];
REPEAT
FINISHED => RETURN[TRUE];
ENDLOOP;
};

AllStrokeWidthsEqual: PROC [traj: Traj] RETURNS [width: REAL] = {
firstSeg: Segment _ FetchSegment[traj, 0];
seg: Segment;
width _ firstSeg.strokeWidth;
FOR i: INT IN [1..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
IF seg.strokeWidth # width THEN RETURN[-1.0];
ENDLOOP;
};

AllStrokeEndsEqual: PROC [traj: Traj] RETURNS [allEqual: BOOL, end: StrokeEnd] = {
firstSeg: Segment _ FetchSegment[traj, 0];
seg: Segment;
end _ firstSeg.strokeEnd;
FOR i: INT IN [1..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
IF seg.strokeEnd # end THEN RETURN[FALSE, round];
ENDLOOP;
allEqual _ TRUE;
};

AllStrokeColorsEqual: PROC [traj: Traj] RETURNS [allEqual: BOOL, color: Color] = {
firstSeg: Segment _ FetchSegment[traj, 0];
seg: Segment;
color _ firstSeg.color;
FOR i: INT IN [1..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
IF NOT GGUtility.EquivalentColors[color, seg.color] THEN RETURN[FALSE, NIL];
ENDLOOP;
allEqual _ TRUE;
};

AllDashesEqual: PROC [traj: Traj] RETURNS [allEqual: BOOL, dashed: BOOL _ FALSE, pattern: SequenceOfReal _ NIL, offset, length: REAL _ 0.0] = {
firstSeg: Segment _ FetchSegment[traj, 0];
seg: Segment;
dashed _ firstSeg.dashed;
pattern _ firstSeg.pattern;
offset _ firstSeg.offset;
length _ firstSeg.length;
FOR i: INT IN [1..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
IF seg.dashed # dashed THEN RETURN[FALSE];
IF NOT dashed THEN LOOP;
IF NOT EqualSequence[seg.pattern, pattern] THEN RETURN[FALSE];
IF seg.offset # offset THEN RETURN[FALSE];
IF seg.length # length THEN RETURN[FALSE];
ENDLOOP;
allEqual _ TRUE;
};


SegmentSelected: PROC [segNum: NAT, selectedSeq: Sequence] RETURNS [BOOL] = {
RETURN[selectedSeq.segments[segNum]];
};

DrawSequenceFeedback: PUBLIC PROC [dc: Imager.Context, seq: Sequence, normalSeq: Sequence, camera: CameraData, quick: BOOL _ FALSE, selectClass: SelectionClass _ normal] = {
segGen: SegmentGenerator;
color: Imager.Color;
IF camera.quality = quality THEN RETURN;
segGen _ GGSequence.SegmentsInSequence[seq];
Imager.SetColor[dc, Imager.black];
IF NOT quick THEN { -- draw full selection feedback
Imager.SetStrokeJoint[dc, seq.traj.strokeJoint];
FOR next: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
IF SegmentSelected[next.index, normalSeq] THEN {
Imager.SetStrokeWidth[dc, 2.0*(IF next.seg.strokeWidth#0 THEN next.seg.strokeWidth ELSE 1.0)];
Imager.SetStrokeEnd[dc, next.seg.strokeEnd];
color _ IF (next.seg.color=NIL OR ImagerColorPrivate.GrayFromColor[NARROW[next.seg.color]] < 0.2) THEN Imager.black ELSE next.seg.color;
Imager.SetColor[dc, color]; -- want to always see feedback, even if stroke is white or NIL colored
MaskStroke[dc, next.seg];
};
ENDLOOP;
};
};

DrawThemJoints: PROC [dc: Imager.Context, traj: Traj, segNum: NAT, normalSeq: Sequence, hotSeq: Sequence] = {
drewLo, drewHi: BOOL _ FALSE;
seg: Segment _ FetchSegment[traj, segNum];
nextJoint: NAT _ FollowingJoint[traj, segNum];
IF hotSeq # NIL THEN {
IF hotSeq.joints[segNum] THEN {
GGShapes.DrawSelectedJoint[dc, seg.lo, hot];
drewLo _ TRUE;
};
IF hotSeq.joints[nextJoint] THEN {
GGShapes.DrawSelectedJoint[dc, seg.hi, hot];
drewHi _ TRUE;
};
};
IF normalSeq # NIL THEN {
IF normalSeq.joints[segNum] THEN {
GGShapes.DrawSelectedJoint[dc, seg.lo, normal];
drewLo _ TRUE;
};
IF normalSeq.joints[nextJoint] THEN {
GGShapes.DrawSelectedJoint[dc, seg.hi, normal];
drewHi _ TRUE;
};
};
IF seg.lo#seg.hi THEN { -- joints are not coincident
IF NOT drewLo THEN GGShapes.DrawJoint[dc, seg.lo];
IF NOT drewHi THEN GGShapes.DrawJoint[dc, seg.hi];
}
ELSE IF NOT (drewLo OR drewHi) THEN GGShapes.DrawJoint[dc, seg.hi];
};



TranslateTraj: PUBLIC PROC [traj: Traj, vector: Vector] =  {
transform: ImagerTransformation.Transformation;
transform _ ImagerTransformation.Translate[[vector.x, vector.y]];
TransformTraj[traj, transform];
};

TransformTraj: PUBLIC PROC [traj: Traj, transform: ImagerTransformation.Transformation] =  {
seg: Segment;
joint: Joint;
FOR i: NAT IN [0..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
GGSegment.TransformSegment[seg, transform];
ENDLOOP;
FOR i: NAT IN [0..HiJoint[traj]] DO
joint _ NARROW[Rosary.Fetch[traj.joints, i]];
joint.point _ GGTransform.Transform[transform, joint.point];
ENDLOOP;
UpdateBoundBox[traj];
GGOutline.UpdateBoundBox[traj.parent];
};

TransformSequence: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] =  {
joint: Joint;
seg: Segment;
SELECT seq.traj.role FROM
open => TransformSequenceOpen[seq, transform];
fence, hole => TransformSequenceClosed[seq, transform];
ENDCASE => ERROR;
FOR i: NAT IN [0..HiJoint[seq.traj]] DO
IF seq.joints[i] THEN {
joint _ NARROW[Rosary.Fetch[seq.traj.joints, i]];
joint.point _ GGTransform.Transform[transform, joint.point];
};
ENDLOOP;
FOR i: NAT IN [0..HiSegment[seq.traj]] DO
cpCount: NAT _ seq.controlPoints[i].len;
FOR j: NAT IN [0..cpCount) DO
IF seq.controlPoints[i][j] AND NOT seq.segments[i] THEN {
seg _ NARROW[Rosary.Fetch[seq.traj.segments, i]];
seg.class.controlPointMoved[seg, transform, j];
};
ENDLOOP;
ENDLOOP;
UpdateBoundBox[seq.traj];
GGOutline.UpdateBoundBox[seq.traj.parent];
};

TransformSequenceOpen: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] = {
seg: Segment;
FOR i: NAT IN [0..HiSegment[seq.traj]] DO
seg _ FetchSegment[seq.traj, i];
IF seq.segments[i] THEN {
GGSegment.TransformSegment[seg, transform];
}
ELSE {
IF seq.joints[i] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]];
IF seq.joints[i+1] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]];
};
ENDLOOP;
};

TransformSequenceClosed: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] = {
seg: Segment;
hiSeg: NAT _ HiSegment[seq.traj];
FOR i: NAT IN [0..hiSeg-1] DO
seg _ FetchSegment[seq.traj, i];
IF seq.segments[i] THEN {
GGSegment.TransformSegment[seg, transform];
}
ELSE {
IF seq.joints[i] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]];
IF seq.joints[i+1] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]];
};
ENDLOOP;
seg _ FetchSegment[seq.traj, hiSeg];
IF seq.segments[hiSeg] THEN {
GGSegment.TransformSegment[seg, transform];
}
ELSE {
IF seq.joints[hiSeg] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]];
IF seq.joints[0] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]];
};
};

DescribeHit: PUBLIC PROC [traj: Traj, hitType: HitType, segNum, cpNum, jointNum: INT] RETURNS [rope: Rope.ROPE] = {
SELECT hitType FROM
joint => rope _ GGDescribe.DescribeJoint[traj, jointNum];
controlPoint => rope _ GGDescribe.DescribeControlPoint[traj, segNum, cpNum];
segment => rope _ GGDescribe.DescribeSegment[traj, segNum];
ENDCASE => ERROR;
};

GetArrowCode: PROC [traj: Traj] RETURNS [code: NAT] = {
code _ 
IF NOT traj.hiArrow THEN
IF NOT traj.loArrow THEN 0
ELSE 1
ELSE
IF NOT traj.loArrow THEN 2
ELSE 3;
};

Fileout: PUBLIC PROC [f: IO.STREAM, traj: Traj] = {
roleRope: Rope.ROPE _ RoleToRope[traj.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 _ HiJoint[traj];
arrowCode _ GetArrowCode[traj];
f.PutF["Traj (%g) [%g] arrows: %g ", [rope[roleRope]], [integer[hiJoint]], [integer[arrowCode]]];
f.PutRope["j: "];  -- traj Joint
GGParseOut.WriteStrokeJoint[f, traj.strokeJoint];
[endsOK, end] _ AllStrokeEndsEqual[traj];
f.PutRope[" e: "];
GGParseOut.WriteBOOL[f, endsOK];
f.PutChar[IO.SP];
GGParseOut.WriteStrokeEnd[f, end];
f.PutChar[IO.SP];
strokeWidth _ AllStrokeWidthsEqual[traj];
strokeOK _ strokeWidth >= 0.0;
f.PutF["w: %g ", [real[strokeWidth]]];
[colorOK, color] _ AllStrokeColorsEqual[traj];
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[traj];
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];
point _ FetchJointPos[traj, 0];
GGParseOut.WritePoint[f, point];
FOR index: NAT IN [1..hiJoint] DO
seg _ FetchSegment[traj, 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 _ FetchJointPos[traj, index];
GGParseOut.WritePoint[f, point];
ENDLOOP;
IF (traj.role = fence OR traj.role = hole) THEN {
seg _ FetchSegment[traj, 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[")"];
};
};

Filein: PUBLIC PROC [f: IO.STREAM, version: REAL] RETURNS [traj: Traj, hasCircle: BOOL _ FALSE] = {
hiJoint, arrowCode: NAT;
role: FenceHoleOpen;
roleName, className: Rope.ROPE;
pFirst, p0, p1: Point;
seg: Segment;
class: SegmentClass;
strokeWidth, offset, length: REAL;
pattern: SequenceOfReal;
strokeColor: Color;
strokeEnd: StrokeEnd;
strokeJoint: StrokeJoint;
success, colorOK, widthOK, endsOK, dashOK, dashed, good: BOOL;
GGParseIn.ReadBlankAndRope[f, "Traj"];
GGParseIn.ReadBlankAndRope[f, "("];
roleName _ GGParseIn.ReadBlankAndWord[f];
role _ RoleFromRope[roleName];
GGParseIn.ReadBlankAndRope[f, ")"];
GGParseIn.ReadBlankAndRope[f, "["];
hiJoint _ GGParseIn.ReadBlankAndNAT[f];
GGParseIn.ReadBlankAndRope[f, "]"];
IF version >= 8607.22 THEN {
GGParseIn.ReadBlankAndRope[f, "arrows:"];
arrowCode _ GGParseIn.ReadBlankAndNAT[f];
}
ELSE arrowCode _ 0;
IF version >= 8702.26 THEN {
GGParseIn.ReadBlankAndRope[f, "j:"];
strokeJoint _ GGParseIn.ReadStrokeJoint[f];
GGParseIn.ReadBlankAndRope[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 {
good: BOOL;
GGParseIn.ReadBlankAndRope[f, "w:"];
strokeWidth _ GGParseIn.ReadBlankAndReal[f];
widthOK _ strokeWidth>= 0.0;
GGParseIn.ReadBlankAndRope[f, "c:"];
[colorOK, good] _ GGParseIn.ReadBOOL[f, version];
IF NOT good THEN ERROR;
strokeColor _ GGParseIn.ReadColor[f, version];
}
ELSE {
GGParseIn.ReadBlankAndRope[f, ":"];
widthOK _ FALSE;
colorOK _ FALSE;
};
IF version >= 8701.23
THEN {
GGParseIn.ReadBlankAndRope[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.ReadBlankAndReal[f];
length _ GGParseIn.ReadBlankAndReal[f];
};
};
}
ELSE {
dashOK _ TRUE;
dashed _ FALSE;
};

pFirst _ p0 _ GGParseIn.ReadPoint[f];
traj _ CreateTraj[p0];
traj.strokeJoint _ strokeJoint;
FOR index: NAT IN [1..hiJoint] DO
GGParseIn.ReadBlankAndRope[f, "("];
className _ GGParseIn.ReadBlankAndWord[f];
IF NOT widthOK THEN strokeWidth _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndReal[f];
length _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndRope[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 {
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 _ AddSegment[traj, hi, GGSegment.MakeArc[newJoint, newCp, newJoint, NIL], lo];
CloseByDistorting[traj, lo];
hasCircle _ TRUE;
}
ELSE success _ AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
p0 _ p1;
ENDLOOP;
IF (role = fence OR role = hole) THEN {
GGParseIn.ReadBlankAndRope[f, "("];
className _ GGParseIn.ReadBlankAndWord[f];
IF NOT widthOK THEN strokeWidth _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndReal[f];
length _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndRope[f, ")"];
seg.strokeWidth _ strokeWidth;
seg.color _ strokeColor;
seg.strokeEnd _ strokeEnd;
seg.dashed _ dashed;
seg.pattern _ pattern;
seg.offset _ offset;
seg.length _ length;
IF traj.segCount=0 THEN { -- special case of closed single segment trajectory
success _ AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
CloseByDistorting[traj, lo];
}
ELSE CloseWithSegment[traj, seg, lo];
};
};

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;
};


FetchSegment: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [seg: Segment] = {
seg _ NARROW[Rosary.Fetch[traj.segments, index]];
};

FetchJoint: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [joint: Joint] = {
joint _ NARROW[Rosary.Fetch[traj.joints, index]];
};

FetchJointPos: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [point: Point] = {
joint: Joint;
IF index < 0 OR index > HiJoint[traj] THEN ERROR; 
joint _ NARROW[Rosary.Fetch[traj.joints, index]];
point _ joint.point
};

LastJointPos: PUBLIC PROC [traj: Traj] RETURNS [point: Point] = {
joint: Joint _ NARROW[Rosary.Fetch[traj.joints, traj.segCount]];
point _ joint.point
};

SetJointPos: PUBLIC PROC [traj: Traj, index: NAT, newPos: Point] = {
joint: Joint _ NARROW[Rosary.Fetch[traj.joints, index]];
segLeft, segRight: Segment;
joint.point _ newPos;
IF index > 0 THEN {
segLeft _ FetchSegment[traj, index-1];
segLeft.hi _ newPos;
};
IF index < traj.segCount THEN {
segRight _ FetchSegment[traj, index];
segRight.lo _ newPos;
};
};


HiSegment: PUBLIC PROC [traj: Traj] RETURNS [highestIndex: NAT] = {
highestIndex _ traj.segCount - 1;
};

HiJoint: PUBLIC PROC [traj: Traj] RETURNS [highestIndex: NAT] =  {
SELECT traj.role FROM
open => highestIndex _ traj.segCount;
fence, hole => highestIndex _ traj.segCount -1;
ENDCASE => ERROR;
};


PreviousSegment: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [prev: Segment] = {
IF traj.role = open THEN {
IF segNum = 0 THEN RETURN[NIL]
ELSE prev _ FetchSegment[traj, segNum - 1];
}
ELSE {
prev _ FetchSegment[traj, (segNum-1+traj.segCount) MOD traj.segCount];
};
};

PreviousSegmentNum: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [prevNum: INT] = {
prevNum _ IF traj.role = open THEN segNum - 1 ELSE (segNum-1+traj.segCount) MOD traj.segCount;
};

FollowingSegmentNum: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [followNum: INT] = {
IF traj.role = open THEN followNum _ IF segNum = HiSegment[traj] THEN -1 ELSE segNum + 1
ELSE followNum _ (segNum+1) MOD traj.segCount;
};

FollowingJoint: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [nextIndex: INT] =  {
SELECT traj.role FROM
open => nextIndex _ IF index = traj.segCount THEN -1 ELSE index + 1;
fence, hole => nextIndex _ (index + 1) MOD traj.segCount;
ENDCASE => ERROR;
};

IsEndJoint: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [BOOL] =  {
SELECT traj.role FROM
open => RETURN[index = 0 OR index = traj.segCount];
fence, hole => RETURN[FALSE];
ENDCASE => ERROR;
};

SetTrajPartField: PROC [traj: Traj, selected: BOOL, selectClass: SelectionClass]  = {
SELECT selectClass FROM
normal => traj.selectedInPart.normal _ selected;
hot => traj.selectedInPart.hot _ selected;
active => traj.selectedInPart.active _ selected;
ENDCASE;
};

SetSelectedFields: PUBLIC PROC [seq: Sequence, selected: BOOL, selectClass: SelectionClass] = {
joint: Joint;
jointGen: JointGenerator;
segGen: SegmentGenerator;
SetTrajPartField[seq.traj, selected, selectClass];
jointGen _ GGSequence.JointsInSequence[seq];
FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO
joint _ FetchJoint[seq.traj, jointNum];
SetJointField[joint, selected, selectClass];
ENDLOOP;
segGen _ GGSequence.SegmentsInSequence[seq];
FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
SetSegmentField[seg, selected, selectClass];
ENDLOOP;
};

SaveSelection: PUBLIC PROC [traj: Traj, selectClass: SelectionClass, scene: Scene] = {
seq: Sequence _ GGSelect.FindSelectedSequence[traj, scene, selectClass];
IF seq=NIL THEN ClearSelection[traj, selectClass]
ELSE SaveSelectionInSequence[seq, selectClass];
};

SaveSelectionInSequence: PUBLIC PROC [seq: Sequence, selectClass: SelectionClass] = {
seg: Segment;
joint: Joint;
FOR i: NAT IN [0..HiSegment[seq.traj]] DO
seg _ FetchSegment[seq.traj, i];
SetSegmentField[seg, seq.segments[i], selectClass];
FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO
SetControlPointField[seg, j, seq.controlPoints[i][j], selectClass];
ENDLOOP;
ENDLOOP;
FOR i: NAT IN [0..HiJoint[seq.traj]] DO
joint _ FetchJoint[seq.traj, i];
SetJointField[joint, seq.joints[i], selectClass];
ENDLOOP;
};

ClearSelection: PUBLIC PROC [traj: Traj, selectClass: SelectionClass] = {
seg: Segment;
joint: Joint;
FOR i: NAT IN [0..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
SetSegmentField[seg, FALSE, selectClass];
FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO
SetControlPointField[seg, j, FALSE, selectClass];
ENDLOOP;
ENDLOOP;
FOR i: NAT IN [0..HiJoint[traj]] DO
joint _ FetchJoint[traj, i];
SetJointField[joint, FALSE, selectClass];
ENDLOOP;
};

RemakeSelection: PUBLIC PROC [traj: Traj, scene: Scene, selectClass: SelectionClass] = {
seq: Sequence _ GGSequence.CreateEmpty[traj];
seg: Segment;
joint: Joint;
FOR i: NAT IN [0..HiSegment[traj]] DO
seg _ FetchSegment[traj, i];
seq.segments[i] _ GetSegmentField[seg, selectClass];
IF seq.segments[i] THEN seq.segCount _ seq.segCount + 1;
FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO
seq.controlPoints[i][j] _ GetControlPointField[seg, j, selectClass];
IF seq.controlPoints[i][j] THEN seq.controlPointCount _ seq.controlPointCount + 1;
ENDLOOP;
ENDLOOP;
FOR i: NAT IN [0..HiJoint[traj]] DO
joint _ FetchJoint[traj, i];
seq.joints[i] _ GetJointField[joint, selectClass];
IF seq.joints[i] THEN seq.jointCount _ seq.jointCount + 1;
ENDLOOP;
GGSelect.SelectSequence[seq, scene, selectClass];
};


SetControlPointField: PUBLIC PROC [seg: Segment, cpNum: NAT, selected: BOOL, selectClass: SelectionClass]  = {
seg.class.controlPointFieldSet[seg, cpNum, selected, selectClass];
};

GetControlPointField: PUBLIC PROC [seg: Segment, cpNum: NAT, selectClass: SelectionClass] RETURNS [selected: BOOL]  = {
selected _ seg.class.controlPointFieldGet[seg, cpNum, selectClass];
};

GetJointField: PUBLIC PROC [joint: Joint, selectClass: SelectionClass] RETURNS [selected: BOOL] = {
SELECT selectClass FROM
normal => selected _ joint.TselectedInFull.normal;
hot => selected _ joint.TselectedInFull.hot;
active => selected _ joint.TselectedInFull.active;
ENDCASE;
};

GetSegmentField: PUBLIC PROC [seg: Segment, selectClass: SelectionClass] RETURNS [selected: BOOL] = {
SELECT selectClass FROM
normal => selected _ seg.TselectedInFull.normal;
hot => selected _ seg.TselectedInFull.hot;
active => selected _ seg.TselectedInFull.active;
ENDCASE;
};

SetJointField: PUBLIC PROC [joint: Joint, selected: BOOL, selectClass: SelectionClass]  = {
SELECT selectClass FROM
normal => joint.TselectedInFull.normal _ selected;
hot => joint.TselectedInFull.hot _ selected;
active => joint.TselectedInFull.active _ selected;
ENDCASE;
};

SetSegmentField: PUBLIC PROC [seg: Segment, selected: BOOL, selectClass: SelectionClass]  = {
SELECT selectClass FROM
normal => seg.TselectedInFull.normal _ selected;
hot => seg.TselectedInFull.hot _ selected;
active => seg.TselectedInFull.active _ selected;
ENDCASE;
};




CopyJoint: PROC [joint: Joint] RETURNS [copy: Joint] = {
copy _ NEW[JointObj _ [point: joint.point, TselectedInFull: joint.TselectedInFull] ];
};
TrajPointGeneratorData: TYPE = REF TrajPointGeneratorDataObj;
TrajPointGeneratorDataObj: TYPE = RECORD [
jointsDone: BOOL,
jointGen: JointGenerator,
cpGen: GGModelTypes.ControlPointGenerator
];

TrajPointPairGeneratorData: TYPE = REF TrajPointPairGeneratorDataObj;
TrajPointPairGeneratorDataObj: TYPE = RECORD [
segGen: SegmentGenerator
];

PointsInDescriptor: PUBLIC PROC [seq: Sequence] RETURNS [pointGen: GGModelTypes.PointGenerator] = {
pgd: TrajPointGeneratorData;
jointGen: JointGenerator;
cpGen: GGModelTypes.ControlPointGenerator;
jointGen _ GGSequence.JointsInSequence[seq];
cpGen _ GGSequence.ControlPointsInSequence[seq];
pgd _ NEW[TrajPointGeneratorDataObj _ [FALSE, jointGen, cpGen]];
pointGen _ NEW[GGModelTypes.PointGeneratorObj _ [NIL, 0, 0, pgd]];
};

PointPairsInDescriptor: PUBLIC PROC [seq: Sequence] RETURNS [pointPairGen: GGModelTypes.PointPairGenerator] = {
pgd: TrajPointPairGeneratorData;
segGen: SegmentGenerator;
segGen _ GGSequence.SegmentsInSequence[seq];
pgd _ NEW[TrajPointPairGeneratorDataObj _ [segGen]];
pointPairGen _ NEW[GGModelTypes.PointPairGeneratorObj _ [NIL, 0, 0, pgd]];
};

NextPoint: PUBLIC PROC [pointGen: GGModelTypes.PointGenerator] RETURNS [pointAndDone: GGModelTypes.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 _ FetchJointPos[pgd.jointGen.seq.traj, nextJoint];
pointAndDone.done _ FALSE;
RETURN;
};
};
pointAndDone _ GGSequence.NextControlPoint[pgd.cpGen];
};

NextPointPair: PUBLIC PROC [pointGen: GGModelTypes.PointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = {
pgd: TrajPointPairGeneratorData _ NARROW[pointGen.classSpecific];
seg: Segment;
seg _ GGSequence.NextSegment[pgd.segGen];
IF seg = NIL THEN RETURN[[[0,0], [0,0], TRUE]]
ELSE RETURN[[seg.lo, seg.hi, FALSE]];
};

SetArrows: PUBLIC PROC [traj: Traj, loArrow, hiArrow: BOOL] = {
traj.loArrow _ loArrow;
traj.hiArrow _ hiArrow;
};

SetTrajStrokeJoint: PUBLIC PROC [traj: Traj, strokeJoint: StrokeJoint] = {
traj.strokeJoint _ strokeJoint;
};

GetTrajStrokeJoint: PUBLIC PROC [traj: Traj] RETURNS [strokeJoint: StrokeJoint] = {
RETURN[traj.strokeJoint];
};

Vector3D: TYPE = ARRAY [1..3] OF REAL;
PointToVector: PROC [point: Point] RETURNS [vector: Vector3D] = {
vector[1] _ point.x;
vector[2] _ point.y;
vector[3] _ 0;
};
CrossProduct: PROC [v1: Vector3D, v2: Vector3D] RETURNS [prodV: Vector3D] = {
prodV[1] _ v1[2]*v2[3] - v1[3]*v2[2];
prodV[2] _ v1[3]*v2[1] - v1[1]*v2[3];
prodV[3] _ v1[1]*v2[2] - v1[2]*v2[1];
};

GetPartialArea: PROC [pt1: Point, pt2: Point] RETURNS [partial: REAL] = {
D1: Vector3D _ PointToVector[pt1];
D2: Vector3D _ PointToVector[pt2];
areaVector: Vector3D _ CrossProduct[D2, D1];-- will only have a z component
RETURN [areaVector[3]];
};

SignedArea: PROC [traj: Traj] RETURNS [area: REAL _ 0.0] = {
lastPoint, thisPoint: Point _ FetchJointPos[traj, 0];
hiSeg: NAT _ HiSegment[traj];
thisSeg: Segment;
FOR index: NAT IN [0..hiSeg] DO
thisSeg _ FetchSegment[traj, index];
FOR cpIndex: NAT IN [0..thisSeg.class.controlPointCount[thisSeg]) DO
area _ area + GetPartialArea[lastPoint, (thisPoint _ thisSeg.class.controlPointGet[thisSeg, cpIndex])];
lastPoint _ thisPoint;
ENDLOOP;
area _ area + GetPartialArea[lastPoint, (thisPoint _ FetchJointPos[traj, IF index=hiSeg THEN 0 ELSE index+1])]; 
lastPoint _ thisPoint;
ENDLOOP;
}; -- end of SignedArea

SignedAreaTransformSeq: PROC [seq: Sequence, transform: ImagerTransformation.Transformation] RETURNS [area: REAL _ 0.0] = {
lastPoint, thisPoint: Point;
hiSeg: NAT _ HiSegment[seq.traj];
thisSeg: Segment;
lastPoint _ FetchJointPos[seq.traj, 0];
IF seq.joints[0] THEN lastPoint _ ImagerTransformation.Transform[transform, lastPoint];
FOR index: NAT IN [0..hiSeg] DO
thisSeg _ FetchSegment[seq.traj, index];
FOR cpIndex: NAT IN [0..thisSeg.class.controlPointCount[thisSeg]) DO
thisPoint _ thisSeg.class.controlPointGet[thisSeg, cpIndex];
IF seq.controlPoints[index][cpIndex] THEN thisPoint _ ImagerTransformation.Transform[transform, thisPoint];
area _ area + GetPartialArea[lastPoint, thisPoint];
lastPoint _ thisPoint;
ENDLOOP;
thisPoint _ FetchJointPos[seq.traj, IF index=hiSeg THEN 0 ELSE index+1];
IF seq.joints[IF index=hiSeg THEN 0 ELSE index+1] THEN thisPoint _ ImagerTransformation.Transform[transform, thisPoint];
area _ area + GetPartialArea[lastPoint, thisPoint]; 
lastPoint _ thisPoint;
ENDLOOP;
}; -- end of SignedAreaTransformSeq

useBBox: BOOL = TRUE;
maxDistance: REAL = 99999.0;
noJoint: NAT = 9999;
noCP: NAT = 8888;
noSeg: NAT = 7777;
NearestSegment: PUBLIC PROC [testPoint: Point, seq: Sequence, tolerance: REAL] RETURNS [bestDist: REAL, bestSeg: NAT, bestPoint: Point, success: BOOL] = {
thisDist2, bestDist2: REAL;
thisPoint: Point;
segGen: SegmentGenerator;
thisSuccess: BOOL;
next: GGSequence.SegAndIndex;
bigBox: GGBasicTypes.BoundBoxObj;
success _ FALSE;
IF useBBox THEN {
bigBox _ [seq.boundBox.loX-tolerance, seq.boundBox.loY-tolerance, seq.boundBox.hiX+tolerance, seq.boundBox.hiY+tolerance, FALSE, FALSE];
IF NOT GGBoundBox.PointIsInBox[testPoint, bigBox] THEN RETURN; -- success=FALSE
};
segGen _ GGSequence.SegmentsInSequence[seq];
next _ GGSequence.NextSegmentAndIndex[segGen];
IF next.seg = NIL THEN { -- The sequence is a single joint
bestDist _ maxDistance;  -- magic number for debugging
bestSeg _ noSeg;  -- magic number meaning no segment
bestPoint _ [-1.0, -1.0];
RETURN;
};
[bestPoint, thisSuccess] _ next.seg.class.closestPoint[next.seg, testPoint, tolerance];
IF NOT thisSuccess THEN {
bestDist2 _ maxDistance;
bestSeg _ noSeg;
bestPoint _ [-1.0, -1.0];
}
ELSE {
bestDist2 _ Vectors2d.DistanceSquared[bestPoint, testPoint];
bestSeg _ next.index;
success _ TRUE;
};
FOR next _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
[thisPoint, thisSuccess] _ next.seg.class.closestPoint[next.seg, testPoint, tolerance];
IF NOT thisSuccess THEN {
thisDist2 _ maxDistance;
thisPoint _ [-1.0, -1.0];
}
ELSE {
thisDist2 _ Vectors2d.DistanceSquared[thisPoint, testPoint];
success _ TRUE;
};
IF success AND thisDist2 < bestDist2 THEN {
bestDist2 _ thisDist2;
bestSeg _ next.index;
bestPoint _ thisPoint;
};
ENDLOOP;
bestDist _ RealFns.SqRt[bestDist2];
};

NearestJoint: PUBLIC PROC [testPoint: Point, seq: Sequence, tolerance: REAL] RETURNS [bestDist: REAL, bestJoint: NAT, bestPoint: Point, success: BOOL _ FALSE] = {
tolerance2: REAL _ tolerance*tolerance;
thisDist2, bestDist2: REAL;
thisPoint: Point;
jointGen: JointGenerator;
index: INT;
bigBox: GGBasicTypes.BoundBoxObj;
IF useBBox THEN {
bigBox _ [seq.boundBox.loX-tolerance, seq.boundBox.loY-tolerance, seq.boundBox.hiX+tolerance, seq.boundBox.hiY+tolerance, FALSE, FALSE];
IF NOT GGBoundBox.PointIsInBox[testPoint, bigBox] THEN RETURN; -- success=FALSE
};
jointGen _ GGSequence.JointsInSequence[seq];
index _ GGSequence.NextJoint[jointGen];
IF index = -1 THEN {
bestDist _ maxDistance;
bestJoint _ noJoint;
bestPoint _ [-1.0, -1.0];
RETURN;
};
bestPoint _ FetchJointPos[seq.traj, index];
bestDist2 _ Vectors2d.DistanceSquared[bestPoint, testPoint];
bestJoint _ index;
IF bestDist2 < tolerance2 THEN success _ TRUE;
FOR index _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL index = -1 DO
thisPoint _ FetchJointPos[seq.traj, index];
thisDist2 _ Vectors2d.DistanceSquared[thisPoint, testPoint];
IF thisDist2 < tolerance2 THEN success _ TRUE;
IF success AND thisDist2 < bestDist2 THEN {
bestDist2 _ thisDist2;
bestJoint _ index;
bestPoint _ thisPoint;
};
ENDLOOP;
bestDist _ RealFns.SqRt[bestDist2];
};

NearestControlPoint: PUBLIC PROC [testPoint: Point, seq: Sequence, tolerance: REAL] RETURNS [bestDist: REAL, bestSeg: NAT, bestControlPoint: NAT, bestPoint: Point, success: BOOL] = {
SomeCP: PROC [i: NAT] RETURNS [BOOL] = {
cpCount: NAT _ seq.controlPoints[i].len;
FOR j: NAT IN [0..cpCount) DO
IF seq.controlPoints[i][j] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
tolerance2: REAL _ tolerance*tolerance;
thisDist2, bestDist2: REAL;
thisControlPoint: NAT;
thisPoint: Point;
segGen: SegmentGenerator;
thisSuccess: BOOL;
next: GGSequence.SegAndIndex;
bigBox: GGBasicTypes.BoundBoxObj;
success _ FALSE;
IF useBBox THEN {
bigBox _ [seq.boundBox.loX-tolerance, seq.boundBox.loY-tolerance, seq.boundBox.hiX+tolerance, seq.boundBox.hiY+tolerance, FALSE, FALSE];
IF NOT GGBoundBox.PointIsInBox[testPoint, bigBox] THEN RETURN; -- success=FALSE
};
segGen _ GGSequence.SegmentsInSequence[seq];
next _ GGSequence.NextSegmentAndIndex[segGen];
WHILE next.seg#NIL AND NOT SomeCP[next.index] DO
next _ GGSequence.NextSegmentAndIndex[segGen];
ENDLOOP;
IF next.seg = NIL THEN { -- The sequence has no control points
bestDist _ maxDistance;  -- magic number meaning no segment
bestControlPoint _ noCP;  -- magic number meaning no CP
bestPoint _ [-1.0, -1.0];
RETURN;
};
[bestPoint, thisControlPoint, thisSuccess] _ next.seg.class.closestControlPoint[next.seg, testPoint, tolerance];
IF NOT thisSuccess THEN {
bestDist2 _ maxDistance;
bestSeg _ noSeg;
bestControlPoint _ noCP;
bestPoint _ [-1.0, -1.0];
}
ELSE {
bestDist2 _ Vectors2d.DistanceSquared[bestPoint, testPoint];
bestSeg _ next.index;
bestControlPoint _ thisControlPoint;
success _ TRUE;
};
FOR next _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
IF SomeCP[next.index] THEN {
[thisPoint, thisControlPoint, thisSuccess] _ next.seg.class.closestControlPoint[next.seg, testPoint, tolerance];
IF NOT thisSuccess THEN {
thisDist2 _ maxDistance;
thisPoint _ [-1.0, -1.0];
}
ELSE {
thisDist2 _ Vectors2d.DistanceSquared[thisPoint, testPoint];
success _ TRUE;
};
IF success AND thisDist2 < bestDist2 THEN {
bestDist2 _ thisDist2;
bestSeg _ next.index;
bestControlPoint _ thisControlPoint;
bestPoint _ thisPoint;
};
};
ENDLOOP;
bestDist _ RealFns.SqRt[bestDist2];
};


IndexOfJoint: PUBLIC PROC [joint: Joint, traj: Traj] RETURNS [index: INT] = {
thisJoint: Joint;
FOR i: NAT IN [0..HiJoint[traj]] DO
thisJoint _ FetchJoint[traj, i];
IF thisJoint = joint THEN RETURN[i];
ENDLOOP;
RETURN[-1];
};

IndexOfSegment: PUBLIC PROC [segment: Segment, traj: Traj] RETURNS [index: INT] = {
thisSeg: Segment;
FOR i: NAT IN [0..HiSegment[traj]] DO
thisSeg _ FetchSegment[traj, i];
IF thisSeg = segment THEN RETURN[i];
ENDLOOP;
RETURN[-1];
};

END.

����GGTrajImpl.mesa
Copyright c 1986 by Xerox Corporation.  All rights reserved.
Last edited by Bier on November 13, 1986 5:03:39 pm PST
Contents:  Procedures to implement the Traj Slice Class.  Trajectories consist of Segments.  One motivation for creating this module now is the GGOutlineImpl is too big to compile.

Trajectory-Only Routines
Moves seg so that the specified end of seg coincides with the specified end of traj.  In other words, seg is translated.
Prepare the Segment
Adding to an Empty Traj
Adding to an Existing Traj
Like AddSegment, except that here we rotate, scale, and translate seg as needed so that both of its endpoints coincide with the endpoints of traj (which must not already be closed).  The endpoint correspondence is made so that the hi end of traj touches the named (segEnd) end of seg.
For now, just translate into place and assume size is ok.

Move either the first joint or the last joint (depending on the distortEnd argument) to coincide with the other end.    This will reduce the number of joints in the trajectory by one (making all sequences which refer to this trajectory obsolete.  Usually, no distortion will actually occur.  The client is expected to call this routine when the joints already coincide.
Building Trajectories from Parts
seq should be a run (a single consecutive set of segments and joints).
First we pick out the desired subsection.  Then, Rosary will call CopyEachSegment, which in turn maps CopySegmentAndBuild to each of the desired elements.  The copies are made and are passed to procedure q of Rosary, which assembles them into a new Rosary.
I must create a new outline and copy the extrapoints as well.
Moves moving so that the specified end moving coincides with the specified end fixed.  In other words, moving is translated.
A hack to get arrows to work reasonably for now.
firstJointNum, lastJointNum: NAT;
joint: Joint;
 [Artwork node; type 'ArtworkInterpress on' to command tool] 
To make sure the algorithm terminates, make sure the end joints of the run are not active selected.  They can be normal or hot selected.
firstJointNum, lastJointNum: INT;
joint: Joint;
 [Artwork node; type 'ArtworkInterpress on' to command tool] 
To make sure the algorithm terminates, make sure the end joints of the run are not active selected.  They can be normal or hot selected.

Orientation
IF traj.role # open THEN SIGNAL Problem["not yet implemented"];

Fundamental Routines

The lifetime of a trajectory goes something like this:  It begins as a single point.  The sole purpose of this point is to be an "endpoint" to which the first segment can be attached.  Subsequent segments are also added to endpoints, so this unifies the creation process.  In Gargoyle, trajectories never appear at the top level of a scene;  they are always part of an outline.  It is suggested, then that each CreateTraj call be followed immediately by a CreateOutline call.

Computes it from segment boxes.
Rosary will call CopyEachSegment, which in turn maps CopySegmentAndBuild to each of the desired elements.  The copies are made and are passed to procedure q of Rosary, which assembles them into a new Rosary.
Drawing
Let Q be a logical variable corresponding to <ggData.camera.quality = quality>.
Let S correspond to <segment J is selected>.
Let O correspond to <segment J is on the overlay plane>.
Then segment J is drawn thick when:  qSo.
Segment J is drawn thin otherwise (i.e. when Q + s + O).
Draw the joints.
Draw the control points.
someNormal _ GGSequence.ContainsSegmentParts[selectedParts, i];
someHot _ GGSequence.ContainsSegmentParts[hotParts, i];
Drawing Utilities
[moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]
[moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]
IF seg.color # color THEN RETURN[FALSE, NIL];  NONSENSE !!

Transforming
A convenience routine which does a TransformTraj, where transform is a simple translation.
Individually translates each joint and control point of the trajectory.
 A tricky operation.  All selected segments are transformed.  Then, all segments which are not selected but which touch a selected joint are told that the joint has moved.
 A tricky operation.  All selected segments are transformed.  Then, all segments which are not selected but which touch a selected joint are told that the joint has moved.

Textual Description

Properties of the whole trajectory.
individual segments the same ??
Now fileout the segment data.
Traj (fence) [3] arrows: 0:
Now read the Segment data.
fake it with a closed arc segment

Accessing Parts
Moves the given joint and tells all interested segments that it has moved.

Use sparingly
Parts
Joint Fields.
Segment Fields.
Utilities

Hit Testing

Time to look at the control points.
Style
Will there be an arrowhead on the lo end of traj?  On the hi end?
Utilities

|   i    j    k   |
| v1x v1y v1z |=(v1y*v2z - v1z*v2y) i + (v1z*v2x - v1x*v2z) j
| v2x v2y v2z |(v1x*v2y - v1y*v2x) k
Choose an arbitrary point P in the plane.  Choose two consecutive joints (v1, v2) on the polygon described by the traj.  Call the vector P to v1, D1.  Call the distance P to v2, D2.  The area of the triangle made by these three points is |D1|*|D2|*sin(angle between D1 and D2)/2.
This is just D2 x D1 where "x" is the cross product operator. 
This will be positive if D2 is clockwise of D1.
If P is in the interior of poly for poly convex, and if we add up all of the areas we get by taking vertices pairwise in clockwise order, area will be positive.  If we take them in counter-clockwise order, area will be negative.  This turns out to be true whatever the position of P and even if poly is concave part of the time.

Finds the nearest joint of traj to testPoint (and its distance from testPoint).
Finds the nearest control point of seq (within tolerance) to testPoint (and its distance from testPoint).

Use sparingly:
Returns -1 if it doesn't exist.
Returns -1 if it doesn't exist.
ÊS°��˜�J˜�Icodešœ™Kšœ
Ïmœ1™<Kšœ7™7Kšœ´™´K™�šÏk	˜	Jšœ…žœ#˜ª—K˜�šÏn
œžœž˜Jšžœ¿žœ"˜ëKšžœ	˜—Kšž˜˜�Kšœžœ˜)Kšœ
žœ˜'Kšœžœ˜+Kšœžœ˜Kšœ	žœ˜*Kšœžœ˜#Kšœ
žœ˜'Kšœ
žœ ˜1Iprocšœžœ˜1Kšœžœ˜'Kšœžœžœ
˜Kšœ
žœ˜)Kšœžœ˜3Kšœ	žœ˜%Kšœ
žœ˜*Kšœžœ˜!Kšœžœ˜3Kšœžœ#˜;Kšœžœ˜!Kšœ
žœ˜+Kšœžœ˜1Kšœžœ#˜7Kšœ	žœ˜'Kšœžœ!˜7Kšœ
žœ˜'Kšœžœ"˜9Kšœžœ˜3Kšœžœ˜/Kšœžœžœ	˜Kšœ	žœ˜%Kšœ	žœ˜%Kšœžœ˜#K˜�KšŸœžœžœ˜3K˜�—K™K˜�šŸ
œžœžœžœ˜@Kšœžœ˜3KšœO˜OKšœžœžœžœžœžœ˜YK˜K˜�—š
Ÿ
œžœžœ?žœžœ˜yK˜
K˜K˜KšœmÏb
œ™xš
žœžœžœžœžœ˜HKš ™—Kšžœžœ˜7šœžœžœ.˜IKšžœ;˜?—šœ&˜&Kš ™—šžœžœ˜Kšœ%˜%Kšœ
žœ˜*Kšœ
žœ˜*Kšœžœ˜6Kšœ˜K˜AK˜Kš ™—šžœ˜šžœžœ˜Kšœ
žœ˜*KšœC˜CKšœC˜CK˜—šžœ˜Kšœ
žœ˜*KšœC˜CKšœC˜CK˜—Kšœ"˜"Kšœ@˜@Kšœ(˜(Kšžœ	žœžœ#˜6K˜—K˜K˜�—šŸœžœžœ0˜MKšœœ™œK˜
K˜šžœžœžœ7˜VK™9—Kšžœ
žœ˜2Kšœ1˜1Kšœ&˜&KšœC˜CKšœ"˜"Kšœ˜Kšœ@˜@Kšœ(˜(Kšžœ	žœžœ#˜6K˜K˜�K™�—šŸœžœžœ&˜DKšœñ™ñK˜
Kšœ˜K˜Kšœ˜Kšœ*˜*Kšžœž˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜2K˜—˜Kšœ*˜*Kšœ˜Kšœ˜Kšœžœ˜3K˜—Kšžœžœ˜Kšœ;˜;Kšœ˜Kšœ@˜@Kšœ(˜(Kšžœ	žœžœ#˜6K˜K˜�—š
Ÿ	œžœžœžœžœ˜6K˜Kšœžœ˜0Kšžœžœ˜(K˜K˜�—K™ K˜�šŸœžœžœžœ˜EK™FKšœžœ˜4Kšœ ˜ Kšœ2žœ˜9Kšœžœ˜0Kšœ˜Kšœ,žœ˜3K˜Kšœ˜Kšœžœ˜Kšœžœ˜K˜$šŸœžœžœžœ˜Gš
Ÿœžœžœžœžœ˜QKšœ˜Kšœ	žœ˜Kšœ%˜%Kšœ˜K˜—Kšœ6˜6K˜—šŸ
œžœžœžœ˜Eš
Ÿœžœžœžœžœ˜OKšœ˜Kšœžœ˜Kšœ˜Kšœ˜K˜—Kšœ2˜2K˜—Kšœ3˜3Kšœ.˜.Kšœb˜bšžœ	žœ˜Kšœ/˜/Kšœ2˜2Kšœ/˜/Kšœ2˜2Kšœ:˜:K˜—šžœ˜Kšœ?˜?Kšœ9˜9K˜—KšœÏcÙ˜øKšœb˜bšžœ	žœ˜Kšœ+˜+Kšœ.˜.Kšœ+˜+Kšœ.˜.Kšœ4˜4K˜—šžœ˜Kšœ9˜9Kšœ5˜5K˜—Kš	œ
žœžœžœžœ˜VKš	œžœGžœžœžœ2˜Kšœ*˜*Kšœ˜K˜K˜�—šŸœžœžœžœžœžœ˜_Kšœ€™€Kšœžœ˜4KšœA˜AKšœžœ˜!Kšœžœ˜0Kšœ=˜=Kšœžœ˜šŸœžœžœžœ˜Gš
Ÿœžœžœžœžœ˜QKšœ˜Kšœ	žœ˜Kšœ%˜%Kšœ˜K˜—Kšœ6˜6K˜—šŸ
œžœžœžœ˜Eš
Ÿœžœžœžœžœ˜OKšœ˜Kšœžœ˜Kšœ˜Kšœ˜K˜—Kšœ2˜2K˜—Kšœ9˜9Kšœ5˜5š	œžœ<žœžœžœ2˜†K™=—Kšœ+˜+Kšœ˜Kšœ˜K˜�—šŸœžœžœDžœ˜qKšœq 
œ™|K˜
Kšœ˜Kšœ˜Kšžœžœ˜1šžœžœ˜KšœU˜UKšœ˜šœ	žœ˜K˜Kšœ+˜+Kšœ9˜9KšœE˜EKšœ
žœ˜Kšœžœ˜Kšœ
žœ˜Kšœ#˜#Kšœ˜Kšœžœžœžœ˜%K˜—Kšœ,˜,Kšœ˜K˜—šžœ¡˜KšœV˜VKšœ˜šœ	žœ˜K˜Kšœ+˜+Kšœ9˜9KšœE˜EKšœ
žœ˜Kšœžœ˜Kšœ
žœ˜Kšœ#˜#Kšœ˜Kšœžœžœžœ˜%K˜—Kšœ,˜,Kšœ˜K˜—K˜K˜�—šŸœžœžœ˜MKšœ8˜8Kšœžœ˜6Kšžœž˜šœ	˜	Kšœ
 œ˜$Kšœ˜Kšœ:˜:K˜—˜	Kšœ
 œ˜"Kšœ˜Kšœ@˜@K˜—˜
Kšœ
 œ˜$Kšœ˜Kšœ1˜1K˜—šžœžœ˜Kš 0™0—Kšœ#˜#Kšœ#˜#K˜K˜�—šŸ
œžœžœžœ˜6Kšžœ˜K˜K˜�—šŸœžœžœžœ˜AK˜
šžœžœ˜Kšœ˜Kšœžœ˜%K˜—šžœžœ˜Kšœ(˜(Kšœžœ˜%K˜—K˜K˜�—šŸœžœžœ˜JK˜Kšœ0˜0K˜Kšœžœ™!K™
Kšœ/˜/Kšœ1˜1Kšœ#˜#Kšœ¡œ)˜5Kšœ'˜'Kšœ'˜'Kšœ'˜'Kšžœžœžœžœ˜Kšžœžœž˜šœžœ˜Kšœ˜K˜—šœžœ˜Kšœ˜šžœžœ˜Kšœ'žœžœ˜8Kšœ'¡0˜WK˜—šžœ˜Kšœ'žœžœ˜8Kšœ(¡0˜XK˜—K˜—Kšžœ˜
artworkFigure•
Interpressµ
Interpress/Xerox/3.0  f j k j¡¥“Ä��WB ¤ ¨  '¡£„ð ¢ ¨ r j 
» ¢ ¨¡¡¨ÄWB�� ¤ ¨ r j¡¥“Ä��WB ¤ ¨¡¡¨¢·“¢°“¢¯“¡¡¨È™þ³—˜¥¯“¡¡¨þ³™$Š—˜¥¯“¡¡¨$Š™?°—˜¥¯“¡¡¨?°™[‹—˜¥¯“¡¡¨[‹™{³—˜¢¯“¡¡¨{³™¢Œ—˜¢¯“¡¡¨¢·“¢°“þ„™|{Ž„˜ r jÄ	�Ä�g��W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Árun– k é¥¯“¡¡¨¢·“¢°“ý°™Ä{"�WÄÊ0��éÄ©�WÄ¿	��ï#‡¡’Ä�“;��_Ä�¹A��`ÄeC�@Ä�‰q��C>­¡’Ä�ÑÈ��ÄZ|��©Ä~¬�KÄ`}�2Zˆ¡’ÄÉ��œÄ�ÿ½��ˆÄ'ö�1ÄtQ�;z°¡’˜¢¯“¡¡¨¢·“¢°“ý™yzށ˜ r j1Ä�¬ô��a ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Átraj– k é¢¯“¡¡¨¢·“¢°“Èo™g¢Žo˜ r jÄH/�—Ä�•?��W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“ÁwholeSeq– k é¢¯“¡¡¨¢·“¢°“ÈU™MþŽU˜¢¯“¡¡¨¢·“¢°“{U™M¢ŽU˜ r jÙÄ���W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Árun1– k é r jƒ? ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Árun2– k é¢¯“¡¡¨¢·“¢°“µ™ë³—˜¢¯“¡¡¨¢·“¢°“µ™yëށ˜ r jÆk ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Átraj1– k é¢¯“¡¡¨¢·“¢°“а™±‰—˜¢¯“¡¡¨¢·“¢°“Ё™y±Ž˜ r jj ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Átraj2– k é k é k é k g•Artwork
Interpress•Bounds80.0 mm xmin 0.0 mm ymin 179.5639 mm xmax 47.625 mm ymax –G50.44722 mm topLeading 50.44722 mm topIndent 1.411111 mm bottomLeading šŸ=™=Kšœ˜Kšœ'žœžœ˜8Kšœ˜Kšœ'žœžœ˜8Kšœ&˜&Kšœ)˜)K˜—Kš ˆ™ˆK˜K˜�—šŸœžœžœ˜LKšœ*˜*K˜K˜Kšœžœ™!Kšœ
™
K˜�Kšžœžœžœ¡˜FKšœ/˜/Kšœ1˜1Kšœ#˜#Kšœ6˜6Kšœ'˜'Kšœ'˜'Kšžœžœžœžœžœžœ˜'Kšœ˜Kšœ˜Kšœ'¡6˜]Kšœ˜M–¿
Interpress/Xerox/3.0  f j k j¡¥“Ä��WB ¤ ¨  Ew¡£=ú ¢ ¨ r j % ¢ ¨¡¡¨ÄWB�� ¤ ¨ r j¡¥“Ä��WB ¤ ¨¡¡¨Ä¡¨‰ÄSô�A™­)—‰Äsž�A—AŽ)—AÄSô�A—‰Ž¡¡¡¡™¢·“¢°“¢¯“¡¡¨‰ÄSô�A™­)—˜¥¯“¡¡¨­)™‰Äsž�A—˜¥¯“¡¡¨‰Äsž�A™AŽ˜¥¯“¡¡¨AÄsž�A™)—˜¢¯“¡¡¨)™AÄSô�A—˜¢¯“¡¡¨AÄSô�A™‰Ž˜¢¯“¡¡¨¢·“¢°“ÄYð�Ã-™ÄQŸ�¼1—AÄw®�AÄ�ß1��Ê1¡“Ä�Ã��µ-—˜ r jÄû�Ä�¦ë��W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Árun– k é¥¯“¡¡¨¢·“¢°“y)™Ä"˜�ÄYÚ�7Ä ��Ä�¢Ó��^UÄsž�A¡’Ä�ƒù��QÄ�§{��\ÄyÉ�QÄ�§{��\
Äsž�A¡’Ä�ë«��°Ä�¢Ó��^Äÿ·�†ÄYÚ�7é)¡’˜ r j"ƒ ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Átraj– k é¢¯“¡¡¨¢·“¢°“ÄYð�Ã%™ÄQŸ�¼!—Äx!�ÑÄgY�RÄr%�O!¡“Ä�Ã��µ%—˜ r jÄ«�Æ ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Árun1– k é¢¯“¡¡¨¢·“¢°“é™
ÄQj�A—UŽy—˜ r jÄ#�Äq9�W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Átraj1– k é r jÄ(�	� ¤ê$ ¢ ¥ ¨¡¡¨Ä¯“Ÿ ™¡ Ÿ ¡“¡¸ k é r jĉ��ÈÄ�‚Ž��W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“ÁConcat– k é¢¯“¡¡¨¢·“¢°“k	™Ä�ÐÒ�Ä-&�Ämb�yÄ}²�O¡“˜£¯“¡¡¨¢·“¢°“†5™Ä�èž�
ÄI��¦—†5—Ä�s��ŸÄ²¡�
—˜ r jĉ�Ä�šÄ��a ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Álo– k é r jÄ;Ù� Ä�ˆU��W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Áhi– k é r jÄ'y� Äz�W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Áhi– k é r jÄ;y� Ä�ŠÚ��a ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“Álo– k é r jÄ>�!Ä�‚7��W ¢ ¨ÅXeroxÅ
TiogaFontsÅHelvetica10£¡ “ •  —¡¡¨  Š¡²“ÁClose– k é k é k é k g–
Interpress–:0.0 mm xmin 0.0 mm ymin 148.5194 mm xmax 75.84722 mm ymax –G78.66944 mm topLeading 78.66944 mm topIndent 1.411111 mm bottomLeading šŸ=™=Kš ˆ™ˆK˜K˜�—K™�K™K˜�šŸœžœžœ˜)Kšœžœžœ˜!Kšœžœžœ˜Kšœ
˜
K˜
Kšžœžœžœ ™?šžœžœžœž˜#Kšœ˜Kšœ˜Kšœ?˜?Kšžœ˜—šžœžœžœž˜#Kš	œžœžœžœžœ˜`Kšœ=˜=Kšžœ˜—Kšœ˜K˜K˜K˜�—š
Ÿœžœžœžœžœ˜<Kšžœ˜K˜K˜�—š
ŸœžœžœAžœžœ˜{Kšžœ,˜2K˜—K™�Kšœ™™�KšœÛ™ÛK™�—šŸœžœžœžœ˜BKšœ˜Kšžœ˜K˜K˜�—šŸœžœ˜,K™K˜Kšœ)˜)K˜+šžœOžœžœž˜dKšœJ˜JKšžœ˜—Kšžœ
žœžœ'˜>K˜K˜�—šŸœžœžœžœ˜?KšœÏ™ÏKšœžœ˜4KšœK˜KKšœžœ˜!Kšœžœ˜0KšœK˜KKšœžœ˜šŸœžœžœžœ˜Gš
Ÿœžœžœžœžœ˜QKšœ˜Kšœ	žœ˜Kšœ%˜%Kšœ˜K˜—Kšœ6˜6K˜—šŸ
œžœžœžœ˜Eš
Ÿœžœžœžœžœ˜OKšœ˜Kšœžœ˜Kšœ˜Kšœ˜K˜—Kšœ2˜2K˜—Kšœ9˜9Kšœ5˜5Kš	œžœRžœžœžœU˜¾Kšœ*˜*Kšœ˜Kšœ˜K˜�—K˜�K™K˜�šŸœžœžœ%˜:KšœO™OK™,K™8K™)K™8šŸœžœ%˜6Kšœ,˜,šžœžœžœž˜%Kšœ˜šžœžœžœžœ˜-Kšœ+˜+Kšœ'˜'Kšœ˜Kšœ˜K˜—Kšžœ˜—K˜—K˜
Kšžœ$žœ˜IKšžœ¡˜7K˜K˜�—šŸœžœ%˜?šŸ	œ˜K˜
Kšœ+˜+Kšœ˜šžœžœžœž˜%Kšœ˜Kšœ:˜:—Kšžœ˜K˜—š
Ÿœžœžœžœžœ˜-Kšžœ
˜K˜—Kšœ*˜*Kšœ
žœ˜)Kšœ+˜+Kš
žœžœžœžœžœ˜7Kšœ'˜'Kšœ,˜,Kšœ,˜,Kšœ$˜$Kšžœžœc˜zKšžœ4žœ˜MKšœ¡˜K˜�—šŸœžœ(˜@Kšœ	žœ˜
K˜
Kšžœ(žœ!˜Ošžœ˜Kšœ0˜0šžœžœžœž˜)Kšœ ˜ Kš
žœžœžœžœžœ˜0šžœžœ˜Kšœ+˜+Kšœ'˜'Kšœ˜Kšœ˜Kšœ+˜+šžœžœžœž˜Kšžœžœ8˜WKšžœ˜—K˜—Kšžœ˜—K˜—K˜K˜�—šŸœžœ(˜EšŸ	œ˜šžœžœžœž˜)šžœžœ˜Kšœ)˜)Kšœ˜Kšœ:˜:K˜—Kšžœ˜—K˜—š
Ÿœžœžœžœžœ˜-Kšžœ
˜K˜—Kšœ.˜.Kšœ+˜+Kš
žœžœžœžœžœ˜@Kšœ0˜0Kšœ,˜,Kšœ0˜0Kšœ$˜$Kšžœžœc˜zKšžœ8žœ˜UKšœ¡˜!K˜�—šŸœžœ[˜|Kšœ	žœ˜
K˜
Kšžœ+žœ8˜išžœ˜K˜
Kšœ3˜3šžœžœžœž˜,Kšœ#˜#Kšœžœžœžœ˜NKšœ'˜'Kš	œžœžœžœžœ˜GKš	œ\žœžœžœžœ1˜¿Kšœ+˜+šžœžœžœž˜šžœžœ˜$KšœU˜UKšœ˜K˜—šžœ˜Kšœ*˜*Kšœ˜K˜—Kšžœ˜—Kšžœ˜—K˜—K˜K˜�K˜�—šŸ œžœ[˜šŸ	œ˜K˜
Kšœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ+˜+KšžœžœžœD˜XKšœ˜šžœžœžœž˜,Kšœ#˜#Kšœ˜Kšœ˜Kš	œžœžœžœžœ˜\Kšœ(˜(Kšœm˜m—Kšžœ˜K˜—š
Ÿœžœžœžœžœ˜-Kšžœ
˜K˜—Kšœ1˜1Kšœ
žœ˜)Kšœ+˜+Kš
žœžœžœžœžœ˜7Kšœ'˜'Kšœ,˜,Kšœ3˜3Kšœ$˜$Kšžœžœc˜zKšžœ;žœ˜[Kšœ¡'˜*K˜�—šŸœžœžœˆžœ˜³šŸœžœ˜Jšœ&˜&Jšœ&˜&J˜
Kšœ4žœ˜9J˜
Jš ™Jšœ)˜)šžœžœBžœž˜[Jšœžœžœ˜2Jšœ!žœžœ˜AKšœ˜Kšžœ
žœ
 œ œ˜?Kšžœžœ
 œ œ˜GKšžœžœ
žœžœžœ
 	œ˜OJšžœ˜—Jš ™Kšœ?™?Kšœ7™7Kšœžœ˜!Kšœžœ˜Jšœ)˜)šžœožœžœž˜‰Jšœžœ˜J˜šžœžœ	žœ˜šžœžœžœ'ž˜6Kšœžœžœ˜<Kšœ!žœžœ#˜KKšœ*˜*Kšžœ
žœ
 œ œ˜?Kšžœžœ
 œ œ˜GKšžœžœ
žœžœžœ
 œ˜LKšžœ˜K˜——Jšžœ˜—K˜—Kšžœžœžœžœ˜/Kšžœžœžœžœžœžœ˜6Kšžœžœ&˜FK˜K˜�—K˜�K™K˜�šŸ
œžœ'˜7•StartOfExpansion¡ -- [moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]šŸœ˜Kšœ˜Kšœ:˜:K˜—š
Ÿœžœžœžœžœ˜-Kšžœ
˜K˜—Kšœ&˜&KšžœžœX˜jKšžœ!žœ˜,˜K˜�——šŸ
œžœ'˜:–¡ -- [moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]šŸœ˜Kšœ™Kšœ˜Kšœ:˜:K˜—š
Ÿœžœžœžœžœ˜-Kšžœ
˜K˜—Kšœ&˜&KšžœžœX˜jKšžœ!žœ˜,K˜K˜�—šŸœžœdžœ˜ –¡ -- [moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]šŸœ˜Kšœ™Kšœ˜Kš	œ
žœžœžœ3žœ˜]Kšœ˜Kšœm˜mK˜—š
Ÿœžœžœžœžœ˜-Kšžœ
˜K˜—Kšœ&˜&KšžœžœX˜jKšžœ!žœ˜,K˜K˜�—šŸ
œžœžœžœ˜?Kšžœžœžœžœ˜&šžœžœžœ
ž˜Kšžœžœžœžœ˜$Kšžœ˜Kšžœžœ˜
—K˜K˜�—šŸœžœžœžœ˜IKšœ*˜*Kšœ
˜
Kšœžœ˜#Kšœ$˜$Kšœ˜Kšœžœ˜Kšœ+˜+Kšœžœ˜Kšœžœ˜šžœžœžœž˜%Kšœ˜Kšžœžœžœžœ˜*Kšžœžœžœžœ˜*Kšžœžœžœžœ˜.Kš
žœžœ.žœžœžœ˜GKšžœžœžœžœ˜Kšžœžœžœžœ˜*Kšžœžœžœžœ˜*Kš
žœžœ%žœžœžœ˜>—šž˜Kšžœžœžœ˜—Kšžœ˜K˜K˜�—šŸœžœžœ	žœ˜AKšœ*˜*Kšœ
˜
Kšœ˜šžœžœžœž˜%Kšœ˜Kšžœžœžœ˜-Kšžœ˜—K˜K˜�—šŸœžœžœžœ˜RKšœ*˜*Kšœ
˜
Kšœ˜šžœžœžœž˜%Kšœ˜Kšžœžœžœžœ	˜1Kšžœ˜—Kšœžœ˜K˜K˜�—šŸœžœžœžœ˜RKšœ*˜*Kšœ
˜
Kšœ˜šžœžœžœž˜%Kšœ˜Kšžœžœžœžœžœžœ™:Kšžœžœ.žœžœžœžœ˜LKšžœ˜—Kšœžœ˜K˜K˜�—šŸœžœžœžœ
žœžœžœžœ˜Kšœ*˜*Kšœ
˜
Kšœ˜Kšœ˜Kšœ˜Kšœ˜šžœžœžœž˜%Kšœ˜Kšžœžœžœžœ˜*Kšžœžœžœžœ˜Kš
žœžœ%žœžœžœ˜>Kšžœžœžœžœ˜*Kšžœžœžœžœ˜*Kšžœ˜—Kšœžœ˜˜K˜�K˜�——š
Ÿœžœ
žœžœžœ˜MKšžœ˜%K˜K˜�—šŸœžœUžœžœ+˜­K˜Kšœ˜Kšžœžœžœ˜(Kšœ,˜,Kšœ"˜"šžœžœžœ¡˜3Kšœ0˜0šžœdžœžœž˜~šžœ(žœ˜0Kšœžœžœžœ˜^Kšœ,˜,Kš
œžœžœžœ"žœžœžœ˜ˆKšœ¡F˜bKšœ˜K˜—Kšžœ˜—K˜—K˜K˜�—šŸœžœ*žœ,˜mKšœžœžœ˜K˜*Kšœžœ ˜.šžœ
žœžœ˜šžœžœ˜Kšœ' œ˜,Kšœ	žœ˜K˜—šžœžœ˜"Kšœ' œ˜,Kšœ	žœ˜K˜—K˜—šžœ
žœžœ˜šžœžœ˜"Kšœ' œ˜/Kšœ	žœ˜K˜—šžœžœ˜%Kšœ' œ˜/Kšœ	žœ˜K˜—K˜—šžœžœ¡˜4Kšžœžœžœ ˜2Kšžœžœžœ ˜2K˜—Kš
žœžœžœ	žœ	žœ ˜C˜K˜�—K˜�—K™�K™K˜�šŸ
œžœ"˜<KšœZ™ZK˜/KšœA˜AKšœ˜K˜K˜�—šŸ
œžœžœB˜\K™GK˜
K˜
šžœžœžœž˜%Kšœ˜Kšœ+˜+Kšžœ˜—šžœžœžœž˜#Kšœžœ˜-Kšœ<˜<Kšžœ˜—Kšœ˜Kšœ&˜&K˜K˜�—šŸœžœžœE˜cK™«Kšœ
˜
Kšœ
˜
šžœž˜Kšœ.˜.Kšœ7˜7Kšžœžœ˜—šžœžœžœž˜'šžœžœ˜Kšœžœ#˜1Kšœ<˜<K˜—Kšžœ˜—šžœžœžœž˜)Kšœ	žœ˜(šžœžœžœž˜šžœžœžœžœ˜9Kšœžœ%˜1Kšœ/˜/K˜—Kšžœ˜—Kšžœ˜—Kšœ˜Kšœ*˜*K˜K˜�—šŸœžœžœD˜fK˜
šžœžœžœž˜)Kšœ ˜ šžœžœ˜Kšœ+˜+K˜—šžœ˜Kšžœžœ$žœ,˜iKšžœžœ$žœ,˜lK˜—Kšžœ˜—K˜K˜�—šŸœžœžœD˜hK™«K˜
Kšœžœ˜!šžœžœžœž˜Kšœ ˜ šžœžœ˜Kšœ+˜+K˜—šžœ˜Kšžœžœ$žœ,˜iKšžœžœ$žœ,˜lK˜—Kšžœ˜—Kšœ$˜$šžœžœ˜Kšœ+˜+K˜—šžœ˜Kšžœžœ$žœ,˜mKšžœžœ$žœ,˜jK˜—˜K˜�——K™�Kšœ™K™�šŸœžœžœ9žœžœ
žœ˜sšžœ	ž˜Kšœ 
œ˜9Kšœ" œ˜LKšœ;˜;Kšžœžœ˜—K˜K˜�—šŸœžœžœžœ˜7Lšœ˜šžœžœž˜Lšžœžœžœ˜Lšžœ˜—šž˜Lšžœžœžœ˜Lšžœ˜—˜L˜�——š
Ÿœžœžœžœžœ˜3Lšœžœ˜,Lšœžœ˜"Lšœ+žœ˜0L˜L˜
Lšœ
˜
L˜Lšœžœ˜L˜
Lšœžœ˜šœ	žœ˜Lš #™#—Lšœ˜Lšœa˜aLš Ðbc
˜ š 1˜1Lš ™—Lš )˜)Lš ˜Lš  ˜ Lš 
Ðbk £ ˜Lš "˜"Lš 
£ £ ˜Lšœ)˜)Lšœ˜Lšœ&˜&Lšœ.˜.L˜Lšœ!˜!Lšœ
žœžœ˜Lšœ ˜ Lšœ
žœžœ˜LšœA˜AL˜Lšœ ˜ šžœžœ˜Lšœ
žœžœ˜Lšœ ˜ Lšœ
žœžœ˜šžœžœ˜Lšœ(˜(Lšœ1˜1L˜—L˜—šœ
žœžœ˜Lš ™—Lšœ˜Lšœ ˜ šžœžœžœž˜!Lšœ$˜$Lšœ*˜*Lšžœ
žœ#˜3Lšžœ@˜D𣠣 £ ˜Kš ,˜,Kš 
£ £ ˜Kš ˜—šžœžœ	žœ˜Lšœ$˜$Lšœ
žœžœ˜L˜—šžœžœžœ˜Lšœ/žœžœ˜6šžœžœ˜Lšœ7žœžœ˜>Lšœ:˜:L˜—L˜—Lšœ˜Lšœ$˜$L˜Lšœ#˜#Lšœ ˜ Lšžœ˜—šžœžœžœ˜1Lšœ"˜"Lšœ*˜*Lšžœ
žœ#˜3Lšžœ@˜D𣠣 £ ˜Kš ,˜,Kš 
£ £ ˜Kš ˜—šžœžœ	žœ˜Lšœ$˜$Lšœ
žœžœ˜L˜—šžœžœžœ˜Lšœ/žœžœ˜6šžœžœ˜Lšœ7žœžœ˜>Lšœ:˜:L˜—L˜—Lšœ˜Lšœ$˜$L˜L˜—K˜—K˜�šŸœžœžœžœžœžœžœžœ˜cLšœžœ˜L˜Lšœžœ˜L˜L˜
Lšœ˜Lšœžœ˜"L˜L˜L˜L˜šœ9žœ˜>Lš ™—Lšœ&˜&Lšœ#˜#Lšœ)˜)Lšœ˜Lšœ#˜#Lšœ#˜#Lšœ'˜'Lšœ#˜#šžœžœ˜Lšœ)˜)Lšœ)˜)L˜—Lšžœ˜š£ £ ˜Lš $˜$Lš +˜+Lš $˜$Lš 0˜0L𣠣 £ £ ˜Lš '˜'Lš ˜—š£ ˜Lš ˜Lš 	£ ˜Lš ˜—šžœžœ˜Lšœžœ˜Lšœ$˜$Lšœ,˜,Lšœ˜Lšœ$˜$Lšœ1˜1Lšžœžœžœžœ˜Lšœ.˜.L˜—šžœ˜Lšœ#˜#Lšœ
žœ˜Lšœ
žœ˜L˜—šžœ˜šžœ˜Lšœ$˜$Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ'˜'Lšœ'˜'Lšœ'˜'L˜—L˜—L˜—šžœ˜Lšœ	žœ˜Lšœ	žœ˜L˜L˜�——Lš ™Lšœ%˜%Lšœ˜Lš ˜šžœžœžœž˜!Lšœ#˜#Lšœ*˜*Lšžœžœ	žœ-˜@𣠣 ˜K𣠣 £ (˜:Kš ˜—Kš£ ˜šžœžœ˜Lšžœžœ	žœ/˜BL˜—Lšžœ˜ šžœžœžœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ'˜'Lšœ'˜'Lšœ'˜'L˜—L˜—Lšœ>˜>Lšœ/˜/šžœžœ˜Lšœžœžœžœ˜Lšœ#¡A˜dš
žœžœžœžœžœžœž˜>Lšœžœ˜(Lšžœ˜—L˜—Lšœ#˜#Lšœ˜Lšœ˜Lšœžœ˜(Lšœ˜Lš ˜Lšœ˜L˜L˜L˜L˜šžœžœžœ˜0K™!K˜)K˜/K˜,KšœLžœ˜VKšœ˜Kšœžœ˜K˜—Lšžœ)˜-Lšžœžœ	žœžœ˜Lšœ˜—Lšžœ˜šžœžœžœ˜'Lšœ#˜#Lšœ*˜*Lšžœžœ	žœ-˜@𣠣 ˜K𣠣 £ (˜:Kš ˜—Kš£ ˜šžœžœ˜Lšžœžœ	žœ/˜BL˜—Lšžœ˜ šžœžœžœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ'˜'Lšœ'˜'Lšœ'˜'L˜—L˜—Lšœ>˜>Lšœ+˜+šžœžœ˜Lšœžœžœžœ˜Lšœ#¡A˜dš
žœžœžœžœžœžœž˜>Lšœžœ˜(Lšžœ˜—L˜—Lšœ#˜#Lšœ˜Lšœ˜Lš ˜L˜L˜L˜L˜šžœžœ¡3˜MKšœ(˜(Lšžœžœ	žœžœ˜Kšœ˜Kšœ˜—Kšžœ!˜%L˜—L˜L˜�—šŸœžœžœžœ˜JLšžœžœž˜Lšœ.˜.Lšœ,˜,Lšœ,˜,Lšžœžœ˜L˜L˜�—šŸ
œžœžœžœ˜HLšžœž˜Lšœ˜Lšœ˜Lšœ˜Lšžœžœ˜L˜L˜�—K™�K™K˜�š
Ÿœžœžœžœžœ˜MKšœžœ%˜1Kšœ˜K˜�—š
Ÿ
œžœžœžœžœ˜KKšœžœ#˜1K˜K˜�—š
Ÿ
œžœžœžœžœ˜NKšœ
˜
Kšžœžœžœžœ˜2Kšœžœ#˜1Kšœ˜Kšœ˜K˜�—šŸœžœžœžœ˜AKšœžœ+˜@Kšœ˜Kšœ˜K˜�—šŸœžœžœžœ˜DK™JKšœžœ#˜8Kšœ˜Kšœ˜šžœžœ˜Kšœ&˜&Kšœ˜Kšœ˜—šžœžœ˜Kšœ%˜%Kšœ˜K˜—Kšœ˜K˜�—K˜�š
Ÿ	œžœžœžœžœ˜CKšœ!˜!K˜K˜�—š
Ÿœžœžœžœžœ˜Bšžœž˜Kšœ%˜%Kšœ/˜/Kšžœžœ˜—K˜K˜�—K˜�š
Ÿœžœžœžœžœ˜Ršžœžœ˜Kšžœžœžœžœ˜Kšžœ'˜+K˜—šžœ˜Kšœ3žœ˜FK˜—K˜K˜�—šŸœžœžœžœžœžœ˜TKš	œ
žœžœžœžœ˜^K˜K˜�—šŸœžœžœžœžœ
žœ˜WKš
žœžœ
žœžœžœ˜XKšžœžœ˜.K˜K˜�—šŸœžœžœžœžœ
žœ˜Ršžœž˜Kšœžœžœžœ˜DKšœ'žœ˜9Kšžœžœ˜—K˜—K˜�šŸ
œžœžœžœžœžœ˜Dšžœž˜Kšœžœžœ˜3Kšœžœžœ˜Kšžœžœ˜—K˜K˜�—™�K™
—K™šŸœžœžœ#˜Ušžœ
ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜�—šŸœžœžœ"˜_K˜
K˜K˜Kšœ2˜2šœ,˜,Kšœ
™
—šžœžœBžœž˜iKšœ'˜'Kšœ,˜,Kšžœ˜Kšœ™—Kšœ,˜,šžœOžœžœž˜dKšœ,˜,Kšžœ˜—K˜K˜�—šŸ
œžœžœ<˜VJšœH˜HJšžœžœžœ"˜1Jšžœ+˜/J˜J˜�—šŸœžœ1˜UJ˜
J˜
šžœžœžœž˜)Jšœ ˜ Jšœ3˜3šžœžœžœ)ž˜8JšœC˜CJšžœ˜—Jšžœ˜—šžœžœžœž˜'Jšœ ˜ Jšœ1˜1Jšžœ˜—J˜J˜�—šŸœžœ.˜IJšœ
˜
Jšœ
˜
šžœžœžœž˜%Jšœ˜Jšœžœ˜)šžœžœžœ)ž˜8Jšœžœ˜1Jšžœ˜—Jšžœ˜—šžœžœžœž˜#Jšœ˜Jšœžœ˜)Jšžœ˜—šœ˜J˜�——šŸœžœžœ<˜XJšœ-˜-J˜
J˜
šžœžœžœž˜%Jšœ˜Jšœ4˜4Jšžœžœ!˜8šžœžœžœ)ž˜8JšœD˜DJšžœžœ3˜RJšžœ˜—Jšžœ˜—šžœžœžœž˜#Jšœ˜Jšœ2˜2Jšžœžœ%˜:Jšžœ˜—Jšœ1˜1K˜K˜�—K˜�šŸœžœžœžœ#˜nKšœB˜BK˜K˜�—š
Ÿœžœžœžœžœ˜wKšœC˜CK˜K˜�—šŸ
œžœ-žœžœ˜cšžœ
ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšžœ˜—K˜K˜�—šŸœžœ-žœžœ˜ešžœ
ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜�—šŸ
œžœžœ#˜[šžœ
ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšžœ˜—K˜K˜�—šŸœžœžœ#˜]šžœ
ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜�K˜�—K˜�˜�K™	—šŸ	œžœžœ˜8KšœžœK˜UK˜—K™�K™K™�Kšœžœžœ˜=šœžœžœ˜*Kšœžœ˜K˜Kšœ)˜)Kšœ˜K˜�—Kšœžœžœ˜Ešœžœžœ˜.K˜Kšœ˜K˜�—šŸœžœžœžœ,˜cKšœ˜K˜Kšœ*˜*Kšœ,˜,Kšœ0˜0Kšœžœžœ˜@Kšœžœ#žœ˜BK˜K˜�—šŸœžœžœžœ4˜oKšœ ˜ K˜Kšœ,˜,Kšœžœ+˜4Kšœžœ'žœ˜JK˜—K˜�šŸ	œžœžœ)žœ.˜tKšœžœ˜=šžœžœžœ˜Kšœžœ&˜4šžœžœ˜Kšœžœ˜K˜—šžœ˜KšœE˜EKšœžœ˜Kšžœ˜K˜—K˜K™#—Kšœ6˜6K˜K˜�—šŸ
œžœžœ-žœ6˜„Kšœ"žœ˜AK˜
Kšœ)˜)Kš
žœžœžœžœžœ˜.Kšžœžœžœ˜%K˜—K™K˜�šŸ	œžœžœ žœ˜?K™AKšœ˜Kšœ˜K˜K˜�—šŸœžœ+˜JKšœ˜K˜K˜�—šŸœžœžœžœ˜SKšžœ˜K˜—J˜�J™	J™�Jš
 œžœžœžœžœ˜&šŸ
œžœžœ˜ALšœ˜Lšœ˜Lšœ˜Lšœ˜—šŸœžœžœ˜MLšœ™Lšœ=™=Lšœ$™$Lšœ%˜%Lšœ%˜%Lšœ%˜%Lšœ˜L˜�—šŸœžœžœžœ˜ILšŸœ ˜"LšŸœ ˜"Lšœ,¡˜KLšžœ˜L˜L˜�—šŸ
œžœžœžœ˜<Lšœ—™—Lšœ>™>Lšœ/™/LšœÈ™ÈL˜5Lšœžœ˜L˜šžœžœžœž˜L˜$šžœ
žœžœ/ž˜DL˜gL˜Lšžœ˜—Lšœp˜pL˜Lšžœ˜—Lšœ¡
œ
˜L˜�—šŸœžœAžœžœ˜{Lšœ˜Lšœžœ˜!L˜Lšœ'˜'LšžœžœB˜Wšžœžœžœž˜L˜(šžœ
žœžœ/ž˜DL˜<Lšžœ#žœB˜kL˜3L˜Lšžœ˜—Lšœ$žœ
žœžœ
˜HLš
žœžœ
žœžœ
žœB˜xLšœ4˜4L˜Lšžœ˜—Lšœ¡
œ˜#—L˜�K™�Kšœ	žœžœ˜Kšœ
žœ˜Kšœ	žœ˜Kšœžœ˜Kšœžœ˜šŸœžœ.žœžœžœžœžœ˜šKšœÏuœ
¤œžœ˜Kšœ˜K˜Kšœ
žœ˜Kšœ˜Kšœ!˜!K–[bBox: GGBasicTypes.BoundBox]šœ
žœ˜šžœ	žœ˜Kšœzžœžœ˜ˆKš	žœžœ,žœžœ¡˜OKšœ˜—Kšœ,˜,Kšœ.˜.šžœžœžœ¡!˜:Kšœ¡˜6Kšœ¡"˜4Kšœ˜Kšžœ˜K˜—KšœW˜Wšžœžœ
žœ˜Kšœ¤œ˜Kšœ˜Kšœ˜K˜—šžœ˜Kšœ¤œ3˜<Kšœ˜Kšœ
žœ˜K˜—šžœWžœžœž˜qKšœW˜Wšžœžœ
žœ˜Kšœ¤œ˜Kšœ˜K˜—šžœ˜Kšœ¤œ3˜<Kšœ
žœ˜K˜—š
žœ	žœ	¤œ¤œžœ˜+Kšœ¤œ¤œ˜Kšœ˜Kšœ˜K˜—Kšžœ˜—Kšœ ¤œ˜#K˜K˜�—šŸœžœ.žœžœžœ
žœžœ˜¢KšœO™OKšœ	¤œžœ˜'Kšœ¤œ
¤œžœ˜Kšœ˜K˜Kšœžœ˜Kšœ!˜!šžœ	žœ˜Kšœzžœžœ˜ˆKš	žœžœ,žœžœ¡˜OKšœ˜—Kšœ,˜,Kšœ'˜'šžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšžœ˜K˜—Kšœ+˜+Kšœ¤œ3˜<Kšœ˜Kš
žœ	¤œ¤œžœžœ˜.šžœHžœž˜^Kšœ+˜+Kšœ¤œ3˜<Kš
žœ	¤œ¤œžœžœ˜.š
žœ	žœ	¤œ¤œžœ˜+Kšœ¤œ¤œ˜Kšœ˜Kšœ˜K˜—Kšžœ˜—Kšœ ¤œ˜#Kšœ˜K˜�—šŸœžœ.žœžœžœžœžœžœ˜¶Kšœi™iš
Ÿœžœžœžœžœ˜(Kšœ	žœ˜(šžœžœžœž˜Kšžœžœžœžœ˜-Kšžœ˜—Kšžœžœ˜K˜—Kšœ	¤œžœ˜'Kšœ¤œ
¤œžœ˜Kšœžœ˜Kšœ˜K˜Kšœ
žœ˜Kšœ˜Kšœ!˜!Kšœ
žœ˜šžœ	žœ˜Kšœzžœžœ˜ˆKš	žœžœ,žœžœ¡˜OKšœ˜—Kšœ,˜,Kšœ.˜.š	žœ
žœžœžœž˜0Kšœ.˜.Kšžœ˜—šžœžœžœ¡%˜>Kšœ¡"˜;Kšœ¡˜7Kšœ˜Kšžœ˜K˜—Kšœp˜pšžœžœ
žœ˜Kšœ¤œ˜Kšœ˜Kšœ˜Kšœ˜K˜—šžœ˜Kšœ¤œ3˜<Kšœ˜Kšœ$˜$Kšœ
žœ˜K˜—šžœWžœžœž˜qšžœžœ˜Kšœp˜pšžœžœ
žœ˜Kšœ¤œ˜Kšœ˜K˜—šžœ˜Kšœ¤œ3˜<Kšœ
žœ˜K˜—š
žœ	žœ	¤œ¤œžœ˜+Kšœ¤œ¤œ˜Kšœ˜Kšœ$˜$Kšœ˜K˜—K˜—Kšžœ˜—Kšœ ¤œ˜#Kšœ˜K˜�—K™�Kšœ™K˜�š
Ÿœžœžœžœ	žœ˜MK™Kšœ˜šžœžœžœž˜#Kšœ ˜ Kšžœžœžœ˜$Kšžœ˜—Kšžœ˜˜K˜�——š
Ÿœžœžœ žœ	žœ˜SK™K˜šžœžœžœž˜%Kšœ ˜ Kšžœžœžœ˜$Kšžœ˜—Kšžœ˜K˜—K˜�Kšžœ˜K˜�—�…—����é¶��UT�