DIRECTORY Feedback, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTraj, GGTransform, GGUtility, Imager, ImagerTransformation, Lines2dTypes, Lines2d, RealFns, Rope, Rosary, Vectors2d; GGTrajImpl: CEDAR PROGRAM IMPORTS Feedback, GGBoundBox, GGCoreOps, GGOutline, GGParent, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTransform, GGUtility, Imager, ImagerTransformation, Lines2d, RealFns, Rosary, Vectors2d EXPORTS GGTraj = BEGIN BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGCoreTypes.BoundBox; BoundBoxObj: TYPE = GGCoreTypes.BoundBoxObj; Camera: TYPE = GGModelTypes.Camera; Edge: TYPE = Lines2dTypes.Edge; EditConstraints: TYPE = GGModelTypes.EditConstraints; FeatureData: TYPE = GGInterfaceTypes.FeatureData; FenceHoleOpen: TYPE = GGModelTypes.FenceHoleOpen; GGData: TYPE = GGInterfaceTypes.GGData; HitType: TYPE = GGModelTypes.TrajPartType; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; JointObj: TYPE = GGSegmentTypes.JointObj; OutlineData: TYPE = GGOutline.OutlineData; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; RunProc: TYPE = GGTraj.RunProc; 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; SelectedObjectData: TYPE = GGSegmentTypes.SelectedObjectData; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SequenceGenerator: TYPE = GGSequence.SequenceGenerator; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceClass: TYPE = GGModelTypes.SliceClass; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceObj: TYPE = GGModelTypes.SliceObj; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajDataObj: TYPE = GGModelTypes.TrajDataObj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajHitData: TYPE = GGTraj.TrajHitData; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; Problem: ERROR [msg: Rope.ROPE] = Feedback.Problem; CreateTrajFromData: PUBLIC PROC [role: FenceHoleOpen, segCount: NAT, segments: Rosary.ROSARY, joints: Rosary.ROSARY, extraPoints: LIST OF Joint, visibleJoints: BOOL _ TRUE, strokeJoint: Imager.StrokeJoint _ round, loArrow, hiArrow: BOOL _ FALSE, selectedInPart: SelectedObjectData _ [FALSE, FALSE, FALSE, FALSE]] RETURNS [slice: Slice] = { trajData: TrajData _ NEW[TrajDataObj _ [role, segCount, segments, joints, extraPoints, visibleJoints, strokeJoint, loArrow, hiArrow, TRUE, selectedInPart, NIL, 9999.0]]; slice _ NEW[SliceObj _ [class: GGSlice.FetchSliceClass[$Traj], data: trajData]]; slice.boundBox _ GGBoundBox.NullBoundBox[]; slice.tightBox _ GGBoundBox.NullBoundBox[]; slice.nullDescriptor _ GGSlice.DescriptorFromParts[slice, NIL]; }; CreateTraj: PUBLIC PROC [point: Point] RETURNS [slice: Slice] = { firstJoint: Joint _ NEW[JointObj _ [point: point]]; trajData: TrajData _ NEW[TrajDataObj _ [role: open, segCount: 0, segments: NIL, joints: Rosary.FromItem[firstJoint], extraPoints: NIL, visibleJoints: FALSE, polylineTolerance: 9999.0]]; slice _ NEW[SliceObj _ [class: GGSlice.FetchSliceClass[$Traj], data: trajData, parent: NIL, normalSelectedParts: NIL, hotSelectedParts: NIL, activeSelectedParts: NIL, matchSelectedParts: NIL, nullDescriptor: NIL, fullDescriptor: NIL, tightBox: GGBoundBox.NullBoundBox[], boundBox: GGBoundBox.NullBoundBox[], boxValid: FALSE]]; slice.nullDescriptor _ GGSlice.DescriptorFromParts[slice, NIL]; }; AddSegment: PUBLIC PROC [slice: Slice, trajEnd: TrajEnd, seg: Segment, segEnd: TrajEnd] RETURNS [success: BOOL _ TRUE] = { diff: Vector; loJoint, hiJoint: Joint; trajData: TrajData _ NARROW[slice.data]; IF trajData.role = fence OR trajData.role = hole THEN {success _ FALSE; RETURN}; IF trajEnd = segEnd THEN GGSegment.ReverseSegment[seg]; diff _ IF trajEnd = lo THEN Vectors2d.Sub[FetchJointPos[slice, 0], seg.hi] ELSE Vectors2d.Sub[FetchJointPos[slice, trajData.segCount], seg.lo]; GGSegment.TranslateSegment[seg, diff]; IF trajData.segCount = 0 THEN { trajData.segments _ Rosary.FromItem[seg]; loJoint _ NEW[JointObj _ [point: seg.lo]]; hiJoint _ NEW[JointObj _ [point: seg.hi]]; trajData.joints _ Rosary.FromList[LIST[loJoint, hiJoint]]; trajData.segCount _ 1; slice.boundBox^ _ seg.class.boundBox[seg]^; } ELSE { IF trajEnd = lo THEN { loJoint _ NEW[JointObj _ [point: seg.lo]]; trajData.joints _ Rosary.Concat[Rosary.FromItem[loJoint], trajData.joints]; trajData.segments _ Rosary.Concat[Rosary.FromItem[seg], trajData.segments]; } ELSE { hiJoint _ NEW[JointObj _ [point: seg.hi]]; trajData.joints _ Rosary.Concat[trajData.joints, Rosary.FromItem[hiJoint]]; trajData.segments _ Rosary.Concat[trajData.segments, Rosary.FromItem[seg]]; }; trajData.segCount _ trajData.segCount + 1; GGSlice.KillBoundBox[slice]; }; }; CloseWithSegment: PUBLIC PROC [slice: Slice, seg: Segment, segEnd: TrajEnd] = { diff: Vector; trajData: TrajData _ NARROW[slice.data]; -- NarrowRefFault if SliceType#$Traj IF trajData.segCount = 0 THEN ERROR Problem[msg: "single closed segment not implemented"]; IF segEnd = hi THEN GGSegment.ReverseSegment[seg]; diff _ Vectors2d.Sub[LastJointPos[slice], seg.lo]; GGSegment.TranslateSegment[seg, diff]; trajData.segments _ Rosary.Concat[trajData.segments, Rosary.FromItem[seg]]; trajData.segCount _ trajData.segCount + 1; trajData.role _ fence; GGSlice.KillBoundBox[slice]; }; CloseByDistorting: PUBLIC PROC [slice: Slice, distortEnd: TrajEnd] = { seg: Segment; loJoint, hiJoint: Joint; trajData: TrajData _ NARROW[slice.data]; -- NarrowRefFault if SliceType#$Traj loJoint _ FetchJoint[slice, 0]; hiJoint _ FetchJoint[slice, HiJoint[slice]]; SELECT distortEnd FROM lo => { seg _ FetchSegment[slice, 0]; loJoint.point _ hiJoint.point; seg.lo _ hiJoint.point; seg.class.endPointMoved[seg, TRUE, hiJoint.point]; }; hi => { seg _ FetchSegment[slice, HiSegment[slice]]; hiJoint.point _ loJoint.point; seg.hi _ loJoint.point; seg.class.endPointMoved[seg, FALSE, loJoint.point]; }; ENDCASE => ERROR; trajData.joints _ Rosary.Substr[trajData.joints, 0, HiJoint[slice]]; trajData.role _ fence; GGSlice.KillBoundBox[slice]; }; SetSelectionBits: PROC [traj: Slice, selected: BOOL, selectClass: SelectionClass] = { segGen: GGModelTypes.SegmentGenerator; jointGen: GGModelTypes.JointGenerator; joint: Joint; trajData: TrajData _ NARROW[traj.data]; segGen _ GGSequence.SegmentsInTraj[trajData]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SetSegmentField[seg, selected, selectClass]; FOR i: NAT IN [0..seg.class.controlPointCount[seg]) DO seg.class.controlPointFieldSet[seg, i, selected, selectClass]; ENDLOOP; ENDLOOP; jointGen _ GGSequence.JointsInTraj[trajData]; FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO joint _ FetchJoint[traj, jointNum]; SetJointField[joint, selected, selectClass]; ENDLOOP; }; ReplaceFirstRun: PUBLIC PROC [trajD: SliceDescriptor, runProc: RunProc, segmentsOnly: BOOL, selectNewRuns: BOOL] RETURNS [newOutline: Slice] = { trajParts: TrajParts _ NARROW[trajD.parts]; traj: Slice _ trajD.slice; run: SliceDescriptor; runParts: TrajParts; oldOutline: Slice _ GGParent.GetParent[traj]; IF GGSequence.IsComplete[trajParts] THEN { run _ trajD; runParts _ trajParts; } ELSE { runParts _ GGSequence.FirstRun[trajParts]; IF runParts = NIL THEN GOTO NothingToReplace; -- The sequence has only control points. run _ GGSlice.DescriptorFromParts[traj, runParts]; }; IF runParts.segCount>0 OR NOT segmentsOnly THEN { newRun: Slice _ runProc[run]; IF newRun#NIL THEN { SetSelectionBits[newRun, FALSE, active]; -- don't process run twice IF selectNewRuns THEN SetSelectionBits[newRun, TRUE, normal]; }; newOutline _ GGParent.GetParent[SpliceIn[run, newRun]]; } ELSE GOTO NothingToReplace; EXITS NothingToReplace => { SetSelectionBits[trajD.slice, FALSE, active]; -- nothing left to work on newOutline _ NIL; }; }; DeleteControlPoints: PUBLIC PROC [trajD: SliceDescriptor, scene: Scene] RETURNS [bBox: BoundBox] = { trajData: TrajData _ NARROW[trajD.slice.data]; trajParts: TrajParts _ NARROW[trajD.parts]; outlineOfTraj: Slice _ GGParent.GetParent[trajD.slice]; GGSlice.KillBoundBox[trajD.slice]; SaveSelection[trajD.slice, hot, scene]; -- to be restored by SubstituteForSegment SaveSelection[trajD.slice, normal, scene]; FOR i: INT IN [0..trajData.segCount) DO IF NOT GGUtility.AllFalse[trajParts.controlPoints[i]] AND NOT trajParts.segments[i] THEN { oldSeg: Segment _ FetchSegment[trajD.slice, i]; IF oldSeg.class.type = $CubicSpline THEN { newSeg: Segment _ GGSegment.CSControlPointDelete[oldSeg, trajParts.controlPoints[i]]; newRun: Traj _ CreateTraj[newSeg.lo]; GGSliceOps.SetStrokeJoint[newRun, NIL, trajData.strokeJoint, NIL]; IF NOT AddSegment[newRun, hi, newSeg, lo] THEN ERROR; [] _ GGSelect.SubstituteForSegment[trajD.slice, i, newRun, scene] } ELSE { Feedback.AppendByName[$Gargoyle, oneLiner, $Apology, "Only delete control points from Cubic Splines"]; }; }; ENDLOOP; bBox _ GGSliceOps.GetBoundBox[outlineOfTraj]; }; DeleteSequence: PUBLIC PROC [seq: SliceDescriptor] RETURNS [smallerOutline: Slice, openTrajOutlines: LIST OF Slice] = { traj: Slice _ seq.slice; trajData: TrajData _ NARROW[traj.data]; trajParts: TrajParts _ NARROW[seq.parts]; oldOutline: Slice _ GGParent.GetParent[traj]; SELECT trajData.role FROM fence, hole => { openTrajOutlines _ OutlinesOfTrajMinusSequence[seq]; -- pieces of traj become outlines smallerOutline _ GGOutline.ReplaceChild[oldOutline, traj, NIL]; -- old outline gets smaller }; open => { openTrajOutlines _ OutlinesOfTrajMinusSequence[seq]; smallerOutline _ NIL; }; ENDCASE => ERROR; }; OutlinesOfTrajMinusSequence: PROC [seq: SliceDescriptor] RETURNS [newOutlines: LIST OF Slice] = { keepParts: SliceDescriptor; wholeTrajParts: TrajParts; traj: Slice _ seq.slice; trajData: TrajData _ NARROW[traj.data]; trajParts: TrajParts _ NARROW[seq.parts]; IF GGSequence.IsComplete[trajParts] THEN RETURN[NIL]; wholeTrajParts _ GGSequence.CreateComplete[trajData]; IF NOT GGSequence.IsEmpty[trajParts] AND trajData.segCount=1 THEN GGSegment.OpenUpSegment[FetchSegment[traj, 0]]; keepParts _ GGSequence.Difference[GGSlice.DescriptorFromParts[traj, wholeTrajParts], seq]; newOutlines _ GroupPieces[keepParts]; }; GroupPieces: PROC [seq: SliceDescriptor] RETURNS [newOutlines: LIST OF Slice] = { seqTraj: Traj _ seq.slice; seqData: TrajData _ NARROW[seqTraj.data]; seqParts: TrajParts _ NARROW[seq.parts]; seqGen: SequenceGenerator; newTraj: Traj; newTrajData: TrajData; newOutline, oldOutline: Slice; joint: Joint; oldOutline _ GGParent.GetParent[seqTraj]; [seqGen, ----] _ GGSequence.RunsInSequence[seqParts]; FOR run: TrajParts _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL run = NIL DO IF run.segCount=0 THEN LOOP; -- special case needed by BS command. newTraj _ CopyTrajFromRun[seqTraj, run]; joint _ FetchJoint[newTraj, 0]; joint.TselectedInFull.active _ FALSE; joint _ FetchJoint[newTraj, HiJoint[newTraj]]; joint.TselectedInFull.active _ FALSE; newTrajData _ NARROW[newTraj.data]; newTrajData.role _ open; newOutline _ GGOutline.CreateOutline[newTraj, GGSliceOps.GetFillColor[oldOutline, NIL].color]; newOutlines _ CONS[newOutline, newOutlines]; ENDLOOP; }; CopyTrajFromRun: PUBLIC PROC [slice: Slice, run: TrajParts] RETURNS [copy: Slice] = { trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ run; originalSegments: Rosary.ROSARY _ trajData.segments; desiredSegments: Rosary.Segment; loSegments, hiSegments, extractedSegments: Rosary.ROSARY; originalJoints: Rosary.ROSARY _ trajData.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[trajData, trajParts]; next _ GGSequence.NextSegmentAndIndex[segGen]; [s1, len1, s2, len2] _ GGCoreOps.BreakIntervalMODLen[next.index, trajParts.segCount, trajData.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, trajParts.segCount]; extractedSegments _ Rosary.FromProcProc[CopyEachSegment]; }; jointCount _ trajParts.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] _ GGCoreOps.BreakIntervalMODLen[next.index, jointCount, HiJoint[slice]+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[trajParts] AND trajData.role#open THEN fence ELSE open; copy _ CreateTrajFromData[newRole, trajParts.segCount, extractedSegments, extractedJoints, NIL, trajData.visibleJoints, trajData.strokeJoint]; copy.props _ slice.props; }; CopyTrajFromRange: PUBLIC PROC [slice: Slice, start: INT, len: INT] RETURNS [piece: Slice] = { original: TrajData _ NARROW[slice.data]; -- NarrowRefFault if SliceType#$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 _ CreateTrajFromData[fence, len, extractedSegments, extractedJoints, NIL, original.visibleJoints, original.strokeJoint]; }; Concat: PUBLIC PROC [fixed: Slice, fixedEnd: TrajEnd, moving: Slice, movingEnd: TrajEnd] RETURNS [longer: Slice] = { diff: Vector; fixedTraj, movingTraj: TrajData; fixed _ GGSliceOps.Copy[fixed].first; moving _ GGSliceOps.Copy[moving].first; IF fixedEnd = movingEnd THEN ReverseTraj[moving]; IF fixedEnd = hi THEN { diff _ Vectors2d.Sub[FetchJointPos[fixed, HiJoint[fixed]], FetchJointPos[moving, 0]]; TranslateTraj[moving, diff]; fixedTraj _ NARROW[fixed.data]; movingTraj _ NARROW[moving.data]; longer _ CreateTrajFromData[ role: open, segCount: fixedTraj.segCount + movingTraj.segCount, segments: Rosary.Concat[fixedTraj.segments, movingTraj.segments], joints: Rosary.Concat[fixedTraj.joints, Rosary.Substr[movingTraj.joints, 1]], extraPoints: NIL, visibleJoints: fixedTraj.visibleJoints, strokeJoint: fixedTraj.strokeJoint, selectedInPart: [FALSE, FALSE, FALSE, FALSE] ]; } ELSE { -- fixedEnd = lo diff _ Vectors2d.Sub[FetchJointPos[fixed, 0], FetchJointPos[moving, HiJoint[moving]]]; TranslateTraj[moving, diff]; fixedTraj _ NARROW[fixed.data]; movingTraj _ NARROW[moving.data]; longer _ CreateTrajFromData[ role: open, segCount: fixedTraj.segCount + movingTraj.segCount, segments: Rosary.Concat[movingTraj.segments, fixedTraj.segments], joints: Rosary.Concat[movingTraj.joints, Rosary.Substr[fixedTraj.joints, 1]], extraPoints: NIL, visibleJoints: fixedTraj.visibleJoints, strokeJoint: fixedTraj.strokeJoint, selectedInPart: [FALSE, FALSE, FALSE, FALSE] ]; }; }; SpliceIn: PUBLIC PROC [runDescriptor: SliceDescriptor, slice: Slice] RETURNS [newSlice: Slice] = { oldTraj: TrajData _ NARROW[runDescriptor.slice.data]; -- traj to be replaced newOutline: Slice; oldOutline: Slice _ GGParent.GetParent[runDescriptor.slice]; SELECT oldTraj.role FROM hole => { newSlice _ SpliceInClosed[runDescriptor, slice]; NARROW[newSlice.data, TrajData].role _ hole; newOutline _ GGOutline.ReplaceChild[oldOutline, runDescriptor.slice, newSlice]; newSlice.parent _ newOutline; -- gotta patch this in for return value }; open => { newSlice _ SpliceInOpen[runDescriptor, slice]; NARROW[newSlice.data, TrajData].role _ open; newOutline _ GGOutline.CreateOutline[newSlice, GGSliceOps.GetFillColor[oldOutline, NIL].color]; newSlice.parent _ newOutline; -- gotta patch this in for return value }; fence => { newSlice _ SpliceInClosed[runDescriptor, slice]; NARROW[newSlice.data, TrajData].role _ fence; newOutline _ GGOutline.ReplaceFirstChild[oldOutline, newSlice]; newSlice.parent _ newOutline; -- gotta patch this in for return value }; ENDCASE => ERROR; NARROW[newSlice.data, TrajData].loArrow _ oldTraj.loArrow; NARROW[newSlice.data, TrajData].hiArrow _ oldTraj.hiArrow; GGSlice.KillBoundBox[oldOutline]; GGSlice.KillBoundBox[newSlice]; }; ResetEndSelectionBits: PROC [slice: Slice, lo, hi: BOOL _ TRUE] = { joint: Joint; IF lo THEN { joint _ FetchJoint[slice, 0]; joint.TselectedInFull.active _ FALSE; }; IF hi THEN { joint _ FetchJoint[slice, HiJoint[slice]]; joint.TselectedInFull.active _ FALSE; }; }; SpliceInOpen: PROC [runDescriptor: SliceDescriptor, slice: Slice] RETURNS [newSlice: Slice] = { runSlice: Slice _ runDescriptor.slice; -- contains traj to be replaced runData: TrajData _ NARROW[runSlice.data]; -- contains traj parts to be replaced runParts: TrajParts _ NARROW[runDescriptor.parts]; -- contains traj parts to be replaced runGen: GGSequence.SequenceGenerator; slice1, slice2: Slice; remainder: Sequence; remainderParts, run1, run2, run3: TrajParts; wholeParts: TrajParts _ GGSequence.CreateComplete[runData]; wholeDescriptor: SliceDescriptor _ GGSlice.DescriptorFromParts[runSlice, wholeParts]; remainder _ GGSequence.Difference[wholeDescriptor, runDescriptor]; remainderParts _ NARROW[remainder.parts]; GGSequence.FillInJoints[remainderParts]; [runGen,----] _ GGSequence.RunsInSequence[remainderParts]; run1 _ GGSequence.NextSequence[runGen]; run2 _ GGSequence.NextSequence[runGen]; run3 _ GGSequence.NextSequence[runGen]; IF run3 # NIL THEN ERROR; SELECT TRUE FROM run1 = NIL => { newSlice _ slice; }; run2 = NIL => { slice1 _ CopyTrajFromRun[runSlice, run1]; IF runParts.segments[0] THEN { -- includes lowest segment ResetEndSelectionBits[slice: slice1, lo: TRUE, hi: FALSE]; newSlice _ Concat[slice, hi, slice1, lo]; -- order is important. traj1's lo joint is kept } ELSE { ResetEndSelectionBits[slice: slice1, lo: FALSE, hi: TRUE]; newSlice _ Concat[slice, lo, slice1, hi]; -- order is important. traj1's hi joint is kept }; }; ENDCASE => { slice1 _ CopyTrajFromRun[runSlice, run1]; ResetEndSelectionBits[slice: slice1, lo: FALSE, hi: TRUE]; slice2 _ CopyTrajFromRun[runSlice, run2]; ResetEndSelectionBits[slice: slice2, lo: TRUE, hi: FALSE]; newSlice _ Concat[slice, lo, slice1, hi]; newSlice _ Concat[newSlice, hi, slice2, lo]; }; }; SpliceInClosed: PROC [runDescriptor: SliceDescriptor, slice: Slice] RETURNS [newSlice: Slice] = { runSlice: Slice _ runDescriptor.slice; -- contains traj to be replaced runData: TrajData _ NARROW[runSlice.data]; -- contains traj parts to be replaced runParts: TrajParts _ NARROW[runDescriptor.parts]; -- contains traj parts to be replaced runGen: GGSequence.SequenceGenerator; slice1: Slice; remainder: Sequence; wholeParts, remainderParts, run1, run2: TrajParts; wholeDescriptor: SliceDescriptor; IF GGSequence.IsComplete[runParts] THEN RETURN[slice]; -- no need to splice. wholeParts _ GGSequence.CreateComplete[runData]; wholeDescriptor _ GGSlice.DescriptorFromParts[runSlice, wholeParts]; remainder _ GGSequence.Difference[wholeDescriptor, runDescriptor]; remainderParts _ NARROW[remainder.parts]; GGSequence.FillInJoints[remainderParts]; [runGen, ----] _ GGSequence.RunsInSequence[remainderParts]; run1 _ GGSequence.NextSequence[runGen]; run2 _ GGSequence.NextSequence[runGen]; IF run1 = NIL OR run2 # NIL THEN ERROR; slice1 _ CopyTrajFromRun[runSlice, run1]; ResetEndSelectionBits[slice1]; newSlice _ Concat[slice, lo, slice1, hi]; -- order is important because traj1's joints are kept. CloseByDistorting[newSlice, lo]; }; ReverseTraj: PUBLIC PROC [slice: Slice] = { newSegments: Rosary.ROSARY; newJoints: Rosary.ROSARY; seg: Segment; joint: Joint; trajData: TrajData _ NARROW[slice.data]; FOR i: NAT IN [0..trajData.segCount) DO seg _ FetchSegment[slice, i]; GGSegment.ReverseSegment[seg]; newSegments _ Rosary.Concat[Rosary.FromItem[seg], newSegments]; ENDLOOP; FOR i: NAT IN [0..trajData.segCount] DO joint _ IF trajData.role=open THEN FetchJoint[slice, i] ELSE FetchJoint[slice, (i MOD trajData.segCount)]; newJoints _ Rosary.Concat[Rosary.FromItem[joint], newJoints]; ENDLOOP; trajData.segments _ newSegments; trajData.joints _ newJoints; }; IsClockwiseTraj: PUBLIC PROC [slice: Slice] RETURNS [BOOL] = { RETURN [SignedArea[slice]>0]; }; IsClockwiseTrajTransformSeq: PUBLIC PROC [descriptor: SliceDescriptor, transform: ImagerTransformation.Transformation] RETURNS [BOOL] = { RETURN [SignedAreaTransformSeq[descriptor, transform]>0]; }; DrawPolyline: PUBLIC PROC [dc: Imager.Context, traj: Slice] = { trajData: TrajData _ NARROW[traj.data]; PolyPathProc: Imager.PathProc = { moveTo[wholePolyline[0]]; FOR i: NAT IN [1..wholePolyline.length) DO lineTo[wholePolyline[i]]; ENDLOOP; }; wholePolyline: GGSegmentTypes.PairSequence; success: BOOL _ FALSE; wholePolyline _ trajData.polyline; IF wholePolyline = NIL THEN RETURN; IF trajData.role = open THEN RETURN; Imager.SetColor[dc, Imager.black]; Imager.SetStrokeJoint[dc, bevel]; Imager.SetStrokeWidth[dc, 1.0]; Imager.MaskStroke[dc, PolyPathProc, TRUE]; }; <> TranslateTraj: PROC [slice: Slice, vector: Vector] = { transform: ImagerTransformation.Transformation _ ImagerTransformation.Translate[[vector.x, vector.y]]; seg: Segment; joint: Joint; trajData: TrajData _ NARROW[slice.data]; hiSegment: NAT _ HiSegment[slice]; hiJoint: NAT _ HiJoint[slice]; FOR i: NAT IN [0..hiSegment] DO seg _ FetchSegment[slice, i]; GGSegment.TransformSegment[seg, transform]; ENDLOOP; FOR i: NAT IN [0..hiJoint] DO joint _ NARROW[Rosary.Fetch[trajData.joints, i]]; joint.point _ GGTransform.Transform[transform, joint.point]; ENDLOOP; GGSlice.KillBoundBox[slice]; -- force bound box to update for now }; ConstrainJoint: PUBLIC PROC [slice: Slice, editConstraints: EditConstraints, jointNum: NAT] RETURNS [constrained: BOOL _ FALSE] ~ { newPos: Point; loSeg, hiSeg: Segment; cPointLo, cPointHi: Point; joint: Joint; trajData: TrajData _ NARROW[slice.data]; IF NOT IsEndJoint[slice, jointNum] THEN { joint _ FetchJoint[slice, jointNum]; hiSeg _ FetchSegment[slice, jointNum]; loSeg _ PreviousSegment[slice, jointNum]; IF hiSeg.class.type = $Bezier AND loSeg.class.type = $Bezier THEN { constrained _ TRUE; cPointLo _ loSeg.class.controlPointGet[loSeg, 1]; cPointHi _ hiSeg.class.controlPointGet[hiSeg, 0]; IF editConstraints = length THEN { newPos _ Vectors2d.Add[Vectors2d.Scale[Vectors2d.VectorFromPoints[cPointLo, cPointHi], 0.5], cPointLo]; } ELSE { -- tangent cpLine: Edge _ Lines2d.CreateEdge[cPointLo, cPointHi]; newPos _ Lines2d.NearestPointOnEdge[joint.point, cpLine]; }; joint.point _ newPos; GGSegment.MoveEndPointSegment[hiSeg, TRUE, newPos]; GGSegment.MoveEndPointSegment[loSeg, FALSE, newPos]; GGSlice.KillBoundBox[slice]; }; }; }; ConstrainCP: PUBLIC PROC [slice: Slice, editConstraints: EditConstraints, segNum: NAT, cpNum: NAT] RETURNS [constrained: BOOL _ FALSE] ~ { newPos: Point; thisSeg, otherSeg: Segment; thisCP, otherCP: Point; joint: Joint; otherSegNum, otherCPNum: INT; trajData: TrajData _ NARROW[slice.data]; IF cpNum = 0 THEN { otherSegNum _ PreviousSegmentNum[slice, segNum]; joint _ FetchJoint[slice, segNum]; otherCPNum _ 1; } ELSE { otherSegNum _ FollowingSegmentNum[slice, segNum]; joint _ FetchJoint[slice, FollowingJoint[slice, segNum]]; otherCPNum _ 0; }; IF otherSegNum # -1 THEN { thisSeg _ FetchSegment[slice, segNum]; otherSeg _ FetchSegment[slice, otherSegNum]; IF thisSeg.class.type = $Bezier AND otherSeg.class.type = $Bezier THEN { constrained _ TRUE; thisCP _ thisSeg.class.controlPointGet[thisSeg, cpNum]; otherCP _ otherSeg.class.controlPointGet[otherSeg, otherCPNum]; IF editConstraints = length THEN { newPos _ Vectors2d.Add[Vectors2d.Scale[Vectors2d.VectorFromPoints[otherCP, joint.point], 2.0], otherCP]; } ELSE { -- tangent, can do 1 of 2 ways [drop perpendicular or same length]. dirVec: Vector _ Vectors2d.VectorFromPoints[otherCP, joint.point]; distance: REAL _ Vectors2d.Distance[joint.point, thisCP]; IF dirVec # [0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception dirVec _ Vectors2d.Scale[dirVec, distance]; newPos _ Vectors2d.Add[joint.point, dirVec]; }; GGSegment.BZControlPointMovedTo[thisSeg, newPos, cpNum]; GGSlice.KillBoundBox[slice]; }; }; }; MatchShape: PUBLIC PROC [traj1, traj2: Slice] RETURNS [BOOL] = { segCount1: NAT _ HiSegment[traj1]; segCount2: NAT _ HiSegment[traj2]; trajData1: TrajData _ NARROW[traj1.data]; trajData2: TrajData _ NARROW[traj1.data]; seg1, seg2: Segment; cpCount1: NAT; closed1: BOOL _ trajData1.role = hole OR trajData1.role = fence; closed2: BOOL _ trajData2.role = hole OR trajData2.role = fence; IF segCount1 # segCount2 THEN RETURN[FALSE]; IF closed1 # closed2 THEN RETURN[FALSE]; FOR i: NAT IN [0..segCount1] DO seg1 _ FetchSegment[traj1, i]; seg2 _ FetchSegment[traj2, i]; IF seg1.class # seg2.class THEN RETURN[FALSE]; IF NOT GGSegment.SameShape[seg1, seg2] THEN RETURN[FALSE]; IF seg1.hi # seg2.hi THEN RETURN[FALSE]; cpCount1 _ seg1.class.controlPointCount[seg1]; IF cpCount1 # seg2.class.controlPointCount[seg2] THEN RETURN[FALSE]; FOR j: NAT IN [0..cpCount1) DO IF seg1.class.controlPointGet[seg1, j] # seg2.class.controlPointGet[seg2, j] THEN RETURN[FALSE]; ENDLOOP; ENDLOOP; RETURN[TRUE]; }; FetchSegment: PUBLIC PROC [slice: Slice, index: NAT] RETURNS [seg: Segment] = { trajData: TrajData _ NARROW[slice.data]; seg _ NARROW[Rosary.Fetch[trajData.segments, index]]; }; FetchSegmentTraj: PUBLIC PROC [trajData: TrajData, index: NAT] RETURNS [seg: Segment] = { seg _ NARROW[Rosary.Fetch[trajData.segments, index]]; }; FetchJoint: PUBLIC PROC [slice: Slice, index: NAT] RETURNS [joint: Joint] = { trajData: TrajData _ NARROW[slice.data]; joint _ NARROW[Rosary.Fetch[trajData.joints, index]]; }; FetchJointPos: PUBLIC PROC [slice: Slice, index: NAT] RETURNS [point: Point] = { trajData: TrajData _ NARROW[slice.data]; joint: Joint; hiJoint: NAT _ SELECT trajData.role FROM open => trajData.segCount, fence, hole => trajData.segCount -1, ENDCASE => ERROR; IF index < 0 OR index > hiJoint THEN ERROR; joint _ NARROW[Rosary.Fetch[trajData.joints, index]]; point _ joint.point; }; FetchJointPosTraj: PUBLIC PROC [trajData: TrajData, index: NAT] RETURNS [point: Point] = { joint: Joint; hiJoint: NAT _ SELECT trajData.role FROM open => trajData.segCount, fence, hole => trajData.segCount -1, ENDCASE => ERROR; IF index < 0 OR index > hiJoint THEN ERROR; joint _ NARROW[Rosary.Fetch[trajData.joints, index]]; point _ joint.point; }; FetchJointNormal: PUBLIC PROC [slice: Slice, index: NAT] RETURNS [normal: Vector] = { normal _ [0,-1]; -- for now }; LastJointPos: PUBLIC PROC [slice: Slice] RETURNS [point: Point] = { trajData: TrajData _ NARROW[slice.data]; joint: Joint _ NARROW[Rosary.Fetch[trajData.joints, trajData.segCount]]; point _ joint.point }; SetJointPos: PUBLIC PROC [slice: Slice, index: NAT, newPos: Point] = { trajData: TrajData _ NARROW[slice.data]; joint: Joint _ NARROW[Rosary.Fetch[trajData.joints, index]]; segLeft, segRight: Segment; joint.point _ newPos; IF index > 0 THEN { segLeft _ FetchSegment[slice, index-1]; segLeft.hi _ newPos; }; IF index < trajData.segCount THEN { segRight _ FetchSegment[slice, index]; segRight.lo _ newPos; }; }; HiSegment: PUBLIC PROC [slice: Slice] RETURNS [highestIndex: NAT] = { trajData: TrajData _ NARROW[slice.data]; highestIndex _ trajData.segCount - 1; }; HiSegmentTraj: PUBLIC PROC [trajData: TrajData] RETURNS [highestIndex: NAT] = { highestIndex _ trajData.segCount - 1; }; HiJoint: PUBLIC PROC [slice: Slice] RETURNS [highestIndex: NAT] = { trajData: TrajData _ NARROW[slice.data]; SELECT trajData.role FROM open => highestIndex _ trajData.segCount; fence, hole => highestIndex _ trajData.segCount -1; ENDCASE => ERROR; }; HiJointTraj: PUBLIC PROC [trajData: TrajData] RETURNS [highestIndex: NAT] = { SELECT trajData.role FROM open => highestIndex _ trajData.segCount; fence, hole => highestIndex _ trajData.segCount -1; ENDCASE => ERROR; }; PreviousSegment: PUBLIC PROC [slice: Slice, segNum: NAT] RETURNS [prev: Segment] = { trajData: TrajData _ NARROW[slice.data]; IF trajData.role = open THEN { IF segNum = 0 THEN RETURN[NIL] ELSE prev _ FetchSegment[slice, segNum - 1]; } ELSE { prev _ FetchSegment[slice, (segNum-1+trajData.segCount) MOD trajData.segCount]; }; }; PreviousSegmentNum: PUBLIC PROC [slice: Slice, segNum: NAT] RETURNS [prevNum: INT] = { trajData: TrajData _ NARROW[slice.data]; prevNum _ IF trajData.role = open THEN segNum - 1 ELSE (segNum-1+trajData.segCount) MOD trajData.segCount; }; FollowingSegmentNum: PUBLIC PROC [slice: Slice, segNum: NAT] RETURNS [followNum: INT] = { trajData: TrajData _ NARROW[slice.data]; IF trajData.role = open THEN followNum _ IF segNum = HiSegment[slice] THEN -1 ELSE segNum + 1 ELSE followNum _ (segNum+1) MOD trajData.segCount; }; FollowingJoint: PUBLIC PROC [slice: Slice, index: NAT] RETURNS [nextIndex: INT] = { trajData: TrajData _ NARROW[slice.data]; SELECT trajData.role FROM open => nextIndex _ IF index = trajData.segCount THEN -1 ELSE index + 1; fence, hole => nextIndex _ (index + 1) MOD trajData.segCount; ENDCASE => ERROR; }; IsEndJoint: PUBLIC PROC [slice: Slice, index: NAT] RETURNS [BOOL] = { trajData: TrajData _ NARROW[slice.data]; SELECT trajData.role FROM open => RETURN[index = 0 OR index = trajData.segCount]; fence, hole => RETURN[FALSE]; ENDCASE => ERROR; }; SaveSelection: PUBLIC PROC [slice: Slice, selectClass: SelectionClass, scene: Scene] = { seq: SliceDescriptor _ GGSelect.FindSelectedSlice[slice, selectClass]; IF seq=NIL THEN ClearSelection[slice, selectClass] ELSE SaveSelectionInSequence[seq, selectClass]; }; SaveSelectionInSequence: PUBLIC PROC [descriptor: SliceDescriptor, selectClass: SelectionClass] = { SaveSelectionInParts[descriptor.slice, descriptor.parts, selectClass]; }; SaveSelectionInParts: PUBLIC PROC [slice: Slice, parts: SliceParts, selectClass: SelectionClass] = { seg: Segment; joint: Joint; trajParts: TrajParts _ NARROW[parts]; hiSegment: NAT _ HiSegment[slice]; hiJoint: NAT _ HiJoint[slice]; FOR i: NAT IN [0..hiSegment] DO seg _ FetchSegment[slice, i]; SetSegmentField[seg, trajParts.segments[i], selectClass]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]) DO SetControlPointField[seg, j, trajParts.controlPoints[i][j], selectClass]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..hiJoint] DO joint _ FetchJoint[slice, i]; SetJointField[joint, trajParts.joints[i], selectClass]; ENDLOOP; }; ClearSelection: PUBLIC PROC [slice: Slice, selectClass: SelectionClass] = { seg: Segment; joint: Joint; hiSegment: NAT _ HiSegment[slice]; hiJoint: NAT _ HiJoint[slice]; FOR i: NAT IN [0..hiSegment] DO seg _ FetchSegment[slice, i]; SetSegmentField[seg, FALSE, selectClass]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]) DO SetControlPointField[seg, j, FALSE, selectClass]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..hiJoint] DO joint _ FetchJoint[slice, i]; SetJointField[joint, FALSE, selectClass]; ENDLOOP; }; ClearSelections: PUBLIC PROC [slice: Slice] = { seg: Segment; joint: Joint; hiSegment: NAT _ HiSegment[slice]; hiJoint: NAT _ HiJoint[slice]; FOR i: NAT IN [0..hiSegment] DO seg _ FetchSegment[slice, i]; SetSegmentField[seg, FALSE, normal]; SetSegmentField[seg, FALSE, hot]; SetSegmentField[seg, FALSE, active]; SetSegmentField[seg, FALSE, match]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]) DO SetControlPointField[seg, j, FALSE, normal]; SetControlPointField[seg, j, FALSE, hot]; SetControlPointField[seg, j, FALSE, active]; SetControlPointField[seg, j, FALSE, match]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..hiJoint] DO joint _ FetchJoint[slice, i]; SetJointField[joint, FALSE, normal]; SetJointField[joint, FALSE, hot]; SetJointField[joint, FALSE, active]; SetJointField[joint, FALSE, match]; ENDLOOP; }; RemakeSelection: PUBLIC PROC [slice: Slice, selectClass: SelectionClass] RETURNS [parts: SliceParts] = { trajData: TrajData _ NARROW[slice.data]; -- SHOULD THIS BE A COPY ?? trajParts: TrajParts _ NARROW[GGSequence.CreateEmpty[trajData]]; seg: Segment; joint: Joint; hiSegment: NAT _ HiSegment[slice]; hiJoint: NAT _ HiJoint[slice]; FOR i: NAT IN [0..hiSegment] DO seg _ FetchSegment[slice, i]; trajParts.segments[i] _ GetSegmentField[seg, selectClass]; IF trajParts.segments[i] THEN trajParts.segCount _ trajParts.segCount + 1; FOR j: NAT IN [0..seg.class.controlPointCount[seg]) DO trajParts.controlPoints[i][j] _ GetControlPointField[seg, j, selectClass]; IF trajParts.controlPoints[i][j] THEN trajParts.controlPointCount _ trajParts.controlPointCount + 1; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..hiJoint] DO joint _ FetchJoint[slice, i]; trajParts.joints[i] _ GetJointField[joint, selectClass]; IF trajParts.joints[i] THEN trajParts.jointCount _ trajParts.jointCount + 1; ENDLOOP; parts _ IF GGSequence.IsEmpty[trajParts] THEN NIL ELSE trajParts; }; SetControlPointField: PROC [seg: Segment, cpNum: NAT, selected: BOOL, selectClass: SelectionClass] = { seg.class.controlPointFieldSet[seg, cpNum, selected, selectClass]; }; GetControlPointField: PROC [seg: Segment, cpNum: NAT, selectClass: SelectionClass] RETURNS [selected: BOOL] = { selected _ seg.class.controlPointFieldGet[seg, cpNum, selectClass]; }; GetJointField: 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; match => selected _ joint.TselectedInFull.match; ENDCASE; }; GetSegmentField: 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; match => selected _ seg.TselectedInFull.match; ENDCASE; }; SetJointField: 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; match => joint.TselectedInFull.match _ selected; ENDCASE; }; SetSegmentField: 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; match => seg.TselectedInFull.match _ selected; ENDCASE; }; CopyJoint: PROC [joint: Joint] RETURNS [copy: Joint] = { copy _ NEW[JointObj _ [point: joint.point, TselectedInFull: joint.TselectedInFull] ]; }; SetTrajRole: PUBLIC PROC [traj: Slice, role: FenceHoleOpen] = { IF GGSliceOps.GetType[traj]=$Traj THEN NARROW[traj.data, TrajData].role _ role; }; GetTrajRole: PUBLIC PROC [traj: Slice] RETURNS [role: FenceHoleOpen] = { role _ NARROW[traj.data, TrajData].role; }; UnpackSimpleDescriptor: PUBLIC PROC [traj: Slice, parts: SliceParts] RETURNS [success: BOOL _ FALSE, partType: TrajPartType _ none, trajData: TrajData, joint: Joint _ NIL, jointNum: NAT _ 999, cp: Point _ [0,0], cpNum: NAT _ 999, seg: Segment _ NIL, segNum: NAT _ 999] = { IF traj = NIL OR GGSliceOps.GetType[traj]#$Traj THEN RETURN; [success, partType, trajData, joint, jointNum, cp, cpNum, seg, segNum] _ GGSequence.UnpackSimpleSequence[traj, parts]; }; UnpackHitData: PUBLIC PROC [hitData: REF ANY] RETURNS [hitType: HitType, segNum, cpNum, jointNum: INT, hitPoint: Point] = { trajHitData: TrajHitData _ NARROW[hitData]; hitType _ trajHitData.hitType; segNum _ trajHitData.segNum; cpNum _ trajHitData.cpNum; jointNum _ trajHitData.jointNum; hitPoint _ trajHitData.hitPoint; }; 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 [slice: Slice] RETURNS [area: REAL _ 0.0] = { lastPoint, thisPoint: Point _ FetchJointPos[slice, 0]; hiSeg: NAT _ HiSegment[slice]; thisSeg: Segment; FOR index: NAT IN [0..hiSeg] DO thisSeg _ FetchSegment[slice, 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[slice, IF index=hiSeg THEN 0 ELSE index+1])]; lastPoint _ thisPoint; ENDLOOP; }; -- end of SignedArea SignedAreaTransformSeq: PROC [descriptor: SliceDescriptor, transform: ImagerTransformation.Transformation] RETURNS [area: REAL _ 0.0] = { lastPoint, thisPoint: Point; trajParts: TrajParts _ NARROW[descriptor.parts]; hiSeg: NAT _ HiSegment[descriptor.slice]; thisSeg: Segment; lastPoint _ FetchJointPos[descriptor.slice, 0]; IF trajParts.joints[0] THEN lastPoint _ ImagerTransformation.Transform[transform, lastPoint]; FOR index: NAT IN [0..hiSeg] DO thisSeg _ FetchSegment[descriptor.slice, index]; FOR cpIndex: NAT IN [0..thisSeg.class.controlPointCount[thisSeg]) DO thisPoint _ thisSeg.class.controlPointGet[thisSeg, cpIndex]; IF trajParts.controlPoints[index][cpIndex] THEN thisPoint _ ImagerTransformation.Transform[transform, thisPoint]; area _ area + GetPartialArea[lastPoint, thisPoint]; lastPoint _ thisPoint; ENDLOOP; thisPoint _ FetchJointPos[descriptor.slice, IF index=hiSeg THEN 0 ELSE index+1]; IF trajParts.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; OnlyChild: PUBLIC PROC [slice: Slice] RETURNS [BOOL] = { trajData: TrajData _ NARROW[slice.data]; -- NarrowRefFault if SliceType#$Traj outline: Slice _ slice.parent; outlineData: OutlineData _ NARROW[outline.data]; RETURN[outlineData.children.rest = NIL]; }; <> NearestSegment: PUBLIC PROC [testPoint: Point, descriptor: SliceDescriptor, tolerance: REAL] RETURNS [bestDist: REAL _ 0.0, bestSeg: NAT _ 0, bestPoint: Point _ [0.0, 0.0], bestNormal: Vector _ [0,-1], success: BOOL _ FALSE] = { traj: Traj _ descriptor.slice; trajData: TrajData _ NARROW[traj.data]; trajParts: TrajParts _ NARROW[descriptor.parts]; thisDist2, bestDist2: REAL; thisPoint: Point; thisSuccess: BOOL _ FALSE; tolerance2: REAL _ tolerance*tolerance; ProcessSegment: GGSequence.SegmentWalkProc = { [thisPoint, thisSuccess] _ seg.class.closestPoint[seg, testPoint, tolerance]; IF thisSuccess THEN { thisDist2 _ Vectors2d.DistanceSquared[thisPoint, testPoint]; IF thisDist2 < bestDist2 THEN { bestDist2 _ thisDist2; bestSeg _ index; bestPoint _ thisPoint; success _ TRUE; }; }; }; IF useBBox THEN { IF NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[traj], tolerance] THEN RETURN; -- success=FALSE }; bestDist _ tolerance; bestDist2 _ tolerance2; bestSeg _ noSeg; -- magic number meaning no segment bestPoint _ [-1.0, -1.0]; [] _ GGSequence.WalkSegmentsInSequence[trajData, trajParts, ProcessSegment]; IF success THEN { -- compute the surface normal jointPoint: Point; diffX, diffY: REAL; bestDist _ RealFns.SqRt[bestDist2]; bestNormal _ Vectors2d.Sub[testPoint, bestPoint]; jointPoint _ FetchJointPos[traj, bestSeg]; diffX _ ABS[jointPoint.x - bestPoint.x]; diffY _ ABS[jointPoint.y - bestPoint.y]; IF trajData.role = open AND diffX < 0.01 AND diffY < 0.01 THEN { bestSegment: Segment _ FetchSegment[traj, bestSeg]; bestNormal _ bestSegment.class.jointNormal[bestSegment, jointPoint, testPoint, FALSE].normal; }; }; }; NearestJoint: PUBLIC PROC [testPoint: Point, descriptor: SliceDescriptor, tolerance: REAL] RETURNS [bestDist: REAL _ 0.0, bestJoint: NAT _ 0, bestPoint: Point _ [0.0, 0.0], bestNormal: Vector _ [0,-1], success: BOOL _ FALSE] = { traj: Traj _ descriptor.slice; trajData: TrajData _ NARROW[traj.data]; trajParts: TrajParts _ NARROW[descriptor.parts]; tolerance2: REAL _ tolerance*tolerance; thisDist2, bestDist2: REAL; seg1, seg2: Segment; normal1, normal2, tangent1, tangent2, direction: Vector; ProcessJoint: PROC [traj: TrajData, jointPos: Point, index: NAT] RETURNS [done: BOOL _ FALSE] = { thisDist2 _ Vectors2d.DistanceSquared[jointPos, testPoint]; IF thisDist2 < bestDist2 THEN { bestDist2 _ thisDist2; bestJoint _ index; bestPoint _ jointPos; success _ TRUE; }; }; IF useBBox THEN { IF NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[traj], tolerance] THEN RETURN; -- success=FALSE }; bestDist _ tolerance; bestDist2 _ tolerance2; bestJoint _ noJoint; bestPoint _ [-1.0, -1.0]; GGSequence.WalkJointPositionsInSequence[trajData, trajParts, ProcessJoint]; IF success THEN { -- compute the normal bestDist _ RealFns.SqRt[bestDist2]; IF HiSegment[traj] < bestJoint THEN seg2 _ IF trajData.role = open THEN NIL ELSE FetchSegment[traj, 0] ELSE seg2 _ FetchSegment[traj, bestJoint]; seg1 _ PreviousSegment[traj, bestJoint]; IF seg1 # NIL AND seg2 # NIL THEN { [normal2, tangent2] _ seg2.class.jointNormal[seg2, bestPoint, testPoint , FALSE]; [normal1, tangent1] _ seg1.class.jointNormal[seg1, bestPoint, testPoint, TRUE]; direction _ Vectors2d.VectorFromPoints[bestPoint, testPoint]; bestNormal _ IF ABS[Vectors2d.SmallestAngleBetweenVectors[tangent1, direction]] < ABS[Vectors2d.SmallestAngleBetweenVectors[tangent2, direction]] THEN normal1 ELSE normal2; } ELSE { IF seg1 # NIL THEN [bestNormal, ----] _ seg1.class.jointNormal[seg1, bestPoint, testPoint, TRUE]; IF seg2 # NIL THEN [bestNormal, ----] _ seg2.class.jointNormal[seg2, bestPoint, testPoint, FALSE]; }; }; }; NearestControlPoint: PUBLIC PROC [testPoint: Point, descriptor: SliceDescriptor, tolerance: REAL] RETURNS [bestDist: REAL _ 0.0, bestSeg: NAT _ 0, bestControlPoint: NAT _ 0, bestPoint: Point _ [0.0, 0.0], bestNormal: Vector _ [0,-1], success: BOOL _ FALSE] = { SomeCP: PROC [i: NAT] RETURNS [BOOL] = { cpCount: NAT _ trajParts.controlPoints[i].len; FOR j: NAT IN [0..cpCount) DO IF trajParts.controlPoints[i][j] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; traj: Traj _ descriptor.slice; trajData: TrajData _ NARROW[traj.data]; trajParts: TrajParts _ NARROW[descriptor.parts]; tolerance2: REAL _ tolerance*tolerance; thisDist2, bestDist2: REAL; thisControlPoint: NAT; thisPoint: Point; thisNormal: Vector; thisSuccess: BOOL _ FALSE; ProcessSegment: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL _ FALSE] = { IF NOT SomeCP[index] THEN RETURN; [thisPoint, thisNormal, thisControlPoint, thisSuccess] _ seg.class.closestControlPoint[seg, testPoint, tolerance]; IF thisSuccess THEN { thisDist2 _ Vectors2d.DistanceSquared[thisPoint, testPoint]; IF thisDist2 < bestDist2 THEN { bestDist2 _ thisDist2; bestSeg _ index; bestControlPoint _ thisControlPoint; bestPoint _ thisPoint; bestNormal _ thisNormal; success _ TRUE; }; }; }; IF useBBox THEN { IF NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[traj], tolerance] THEN RETURN; -- success=FALSE }; bestDist _ tolerance; bestDist2 _ tolerance2; bestSeg _ noSeg; bestControlPoint _ noCP; -- magic number meaning no CP bestPoint _ [-1.0, -1.0]; [] _ GGSequence.WalkSegmentsInSequence[trajData, trajParts, ProcessSegment]; IF success THEN bestDist _ RealFns.SqRt[bestDist2]; }; IndexOfJoint: PUBLIC PROC [joint: Joint, slice: Slice] RETURNS [index: INT] = { thisJoint: Joint; FOR i: NAT IN [0..HiJoint[slice]] DO thisJoint _ FetchJoint[slice, i]; IF thisJoint = joint THEN RETURN[i]; ENDLOOP; RETURN[-1]; }; IndexOfSegment: PUBLIC PROC [segment: Segment, slice: Slice] RETURNS [index: INT] = { thisSeg: Segment; FOR i: NAT IN [0..HiSegment[slice]] DO thisSeg _ FetchSegment[slice, i]; IF thisSeg = segment THEN RETURN[i]; ENDLOOP; RETURN[-1]; }; END. ˜GGTrajImpl.mesa Contents: Procedures to implement the Traj Slice Class. Trajectories consist of Segments. Copyright Ó 1986, 1987, 1989 by Xerox Corporation. All rights reserved. Pier, February 18, 1992 5:22 pm PST Bier, January 31, 1991 6:04 pm PST Doug Wyatt, December 18, 1989 4:23:20 pm PST Trajectory-Only Routines Creating Create a new trajectory from the given data. Used by various copy and concat routines. Create a new trajectory containing a single point. Slice must be of class $Traj. Translates seg so that the specified end of seg coincides with the specified end of traj. Mutates slice to add the new segment. Slice Descriptors must be updated by the caller. Orient and translate the segment Add segment to an empty traj Add segment 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. Get the first run of trajD and replace it with the results of runProc. IMPORTANT INVARIANT: When exiting from this loop, make sure that some part of trajD has been deselected from the active list. Otherwise, infinite loops are possible. Find the first run Replace the first run, as specified by the runProc. Deletes selected control points and returns bounding box of area requiring refresh. Cyclic segments must become acyclic Compute a descriptor for the parts that should NOT be deleted. Make sure the end joints of the run are not active selected. They can be normal or hot or match selected. This is important for the delete operation. Create the new outline. Building from Parts runDescriptor 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. Moves moving so that the specified end moving coincides with the specified end fixed. In other words, moving is translated. Run describes a part of its traj that is to be replaced by slice. The result is similar to Rope.Cat[Rope.Substr[...], newRope, Rope.Substr[...]] except that a Traj can be a circular structure. A hack to get arrows to work reasonably for now. [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. [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 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. Drawing Transforming Check segment types Check geometric properties Accessing Parts Moves the given joint and tells all interested segments that it has moved. Use sparingly Parts Utilities Hit Testing 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. PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL _ FALSE]; These values may be returned if the sequence is a single joint PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL _ FALSE]; These values may be returned if the sequence is a single joint Finds the nearest joint of traj to testPoint (and its distance from testPoint). These values may be returned if no joints are described in the descriptor. Same code as FindJointNormal in GGOutlineImplA: 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. ÊAx•NewlineDelimiter – "cedar" style˜Icodešœ™šÏnœS™[KšœH™HKšœ#™#K™"K™,—K™šÏk ˜ Jšœ­˜­—K˜š œžœž˜JšžœÆ˜ÍKšžœ ž˜K˜Kšœ žœ˜)Kšœ žœ˜&Kšœ žœ˜,Kšœžœ˜#Kšœžœ˜Kšœžœ ˜5Kšœ žœ ˜1Iprocšœžœ˜1Kšœžœ˜'Kšœ žœ˜*Kšœžœ˜#Kšœžœ˜3Kšœ žœ˜)Kšœ žœ˜*Kšœžœ˜!Kšœžœ˜3Kšœžœ#˜;Kšœ žœ˜Kšœžœ˜!Kšœ žœ˜+Kšœžœ˜+Kšœžœ˜1Kšœ žœ˜'Kšœžœ˜1Kšœžœ!˜7Kšœžœ%˜=Kšœžœ!˜5Kšœ žœ˜'Kšœžœ ˜7Kšœžœ˜2Kšœžœ˜!Kšœ žœ˜+Kšœžœ ˜5Kšœ žœ˜'Kšœ žœ˜+Kšœžœ˜Kšœ žœ˜'Kšœ žœ˜-Kšœ žœ˜%Kšœ žœ˜'Kšœ žœ˜)Kšœžœ˜/Kšœ žœ˜/Kšœžœ˜#K˜Kšœžœ žœ˜3—headšœ™K™—Kšœ™š œž œ!žœžœžœžœžœžœžœ=žœžœ(žœžœžœžœžœ˜ÓKšœW™WKšœžœmžœžœ ˜©KšœžœE˜PKšœ+˜+Kšœ+˜+Kšœ:žœ˜?K˜K˜—š œžœžœžœ˜BK™2Kšœžœ˜3Kš œžœ3žœ4žœžœ˜¹KšœžœLžœžœžœžœžœžœžœWžœ˜ÇKšœ:žœ˜?K˜K˜—š œžœžœAžœ žœžœ˜{K˜ K˜KšœÑ™ÑKšœžœ ˜(Kš žœžœžœ žœžœ˜PKšÏb ™ Kšžœžœ˜7šœžœžœ/˜JKšžœ@˜D—Kšœ&˜&KšŸ™šžœžœ˜Kšœ)˜)Kšœ žœ˜*Kšœ žœ˜*Kšœ"žœ˜:Kšœ˜K˜+K˜—KšŸ™šžœ˜šžœžœ˜Kšœ žœ˜*KšœK˜KKšœK˜KK˜—šžœ˜Kšœ žœ˜*KšœK˜KKšœK˜KK˜—Kšœ*˜*Kšœ˜K˜—K˜K˜—šœžœžœ2˜OKšœœ™œK˜ KšœžœÏc$˜Mšžœžœžœ7˜ZK™9—Kšžœ žœ˜2Kšœ2˜2Kšœ&˜&KšœK˜KKšœ*˜*Kšœ˜Kšœ˜K˜K™—šœžœžœ(˜FKšœñ™ñK˜ Kšœ˜Kšœžœ $˜MKšœ˜Kšœ,˜,Kšžœ ž˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜2K˜—˜Kšœ,˜,Kšœ˜Kšœ˜Kšœžœ˜3K˜—Kšžœžœ˜KšœD˜DKšœ˜Kšœ˜K˜K˜—šœžœžœ"˜UJšœ&˜&Jšœ&˜&Jšœ ˜ Jšœžœ ˜'Jšœ-˜-šžœOžœžœž˜dJšœ,˜,šžœžœžœ'ž˜6Jšœ>˜>Jšžœ˜—Jšžœ˜—Jšœ-˜-šžœ žœBžœž˜iJšœ#˜#Jšœ,˜,Jšžœ˜—J˜K˜—š œž œ:žœžœžœ˜KšœF™FKšž œž œ”™§Kšœžœ˜+K˜Kšœ˜Kšœ˜Kšœ-˜-K˜KšŸ™šžœ"žœ˜*Kšœ ˜ Kšœ˜K˜—šžœ˜Kšœ*˜*Kš žœ žœžœžœ (˜VKšœ2˜2K˜K˜—KšŸ3™3šžœžœžœžœ˜1KšœŸœ˜šžœžœžœ˜KšœžœŸœ ˜CKšžœžœžœŸœ˜=K˜—Kšœ7˜7K˜—Kšžœžœ˜šž˜˜KšœžœŸœ ˜HKšœ žœ˜K˜——K˜K˜—šœž œ(žœ˜dK™SJšœžœ˜.Jšœžœ˜+Jšœ7˜7Jšœ"˜"Jšœ( )˜QJšœ*˜*šžœžœžœž˜'š žœžœ0žœžœžœ˜ZJšœ/˜/šžœ"žœ˜*JšœU˜UJ˜%Jšœ"žœžœ˜BJšžœžœ$žœžœ˜5JšœŸœ˜AJ˜—šžœ˜Jšœf˜fK˜—J˜—Jšžœ˜—K˜-K˜—K˜š œžœžœžœ+žœžœ ˜wKšœ˜Kšœžœ ˜'Kšœžœ ˜)Kšœ-˜-K˜šžœž˜šœ˜Kšœ5 !˜VKšœ:žœ ˜[K˜—šœ ˜ Kšœ4˜4Kšœžœ˜K˜—Kšžœžœ˜—K˜K˜—š œžœžœžœžœ ˜aKšœ˜Kšœ˜Kšœ˜Kšœžœ ˜'Kšœžœ ˜)K˜Kšžœ"žœžœžœ˜5Kšœ5˜5KšŸÐbc™#šžœžœžœ˜™>KšœZ˜ZKšœ%˜%K˜K˜—š œžœžœžœžœ ˜QK˜Kšœžœ˜)Kšœžœ ˜(Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜ K˜Kšœ)˜)Kšœ  œ(˜5šžœSžœžœž˜hKšžœžœžœ %˜BKšœ Ÿœ˜(KšŸ—™—Kšœ˜Kšœžœ˜%Kšœ.˜.Kšœžœ˜%KšŸ™Kšœžœ˜#Kšœ˜KšœRžœ ˜^Kšœžœ˜,Kšžœ˜—K˜K™—šœ™K˜—šœžœžœ žœ˜UKšœP™PKšœžœ ˜(Kšœ˜Kšœžœ˜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šœC˜CKšœ.˜.Kšœh˜hšžœ žœ˜Kšœ/˜/Kšœ2˜2Kšœ/˜/Kšœ2˜2Kšœ:˜:K˜—šžœ˜KšœE˜EKšœ9˜9K˜—Kšœ% Ù˜þKšœ_˜_šžœ žœ˜Kšœ+˜+Kšœ.˜.Kšœ+˜+Kšœ.˜.Kšœ4˜4K˜—šžœ˜Kšœ9˜9Kšœ5˜5K˜—Kš œ žœ"žœžœžœ˜ZKšœ[žœ0˜ŽKšœ˜K˜K˜—š œžœžœžœžœžœ˜^Kšœ€™€Kšœžœ $˜MKšœžœ˜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˜5KšœKžœ0˜~Kšœ˜K˜—šœžœžœFžœ˜tKšœ|™|K˜ K˜ Kšœ%˜%Kšœ'˜'Kšžœžœ˜1šžœžœ˜KšœU˜UKšœ˜Kšœ žœ ˜Kšœ žœ˜!šœ˜K˜ Kšœ3˜3KšœA˜AKšœM˜MKšœ žœ˜Kšœ'˜'Kšœ#˜#Kš œžœžœžœžœ˜,Kšœ˜—K˜—šžœ ˜KšœV˜VKšœ˜Kšœ žœ ˜Kšœ žœ˜!šœ˜K˜ Kšœ3˜3KšœA˜AKšœM˜MKšœ žœ˜Kšœ'˜'Kšœ#˜#Kš œžœžœžœžœ˜,Kšœ˜—K˜—K˜K˜—šœžœžœ0žœ˜bKšœÁ™ÁKšœžœ ˜LK˜Kšœ<˜­¡’ÄÑÈÄZ|©Ä~¬KÄ`}2Zˆ¡’ÄÉœÄÿ½ˆÄ'ö1ÄtQ;z°¡’˜¢¯“¡¡¨¢·“¢°“ý™yzŽ˜ 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 jj ¢ ¨Å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šœ)žœžœ˜:Kšœ)˜)Kšœ)žœžœ˜:Kšœ)˜)Kšœ,˜,K˜—Kšœˆ™ˆK˜K˜—šœžœ0žœ˜aKšœ' ˜FKšœžœ %˜PKšœžœ %˜XKšœ%˜%Kšœ˜Kšœ˜Kšœ2˜2K˜!Kšžœ!žœžœ  ˜LKšœ0˜0K˜DKšœB˜BKšœžœ˜)Kšœ(˜(Kšœ  œ.˜;Kšœ'˜'Kšœ'˜'Kš žœžœžœžœžœžœ˜'Kšœ)˜)Kšœ˜Kšœ* 6˜`Kšœ ˜ N–¿ 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!ÑÄgYRÄr%O!¡“Äõ%—˜ r jÄ«Æ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Árun1– k é¢¯“¡¡¨¢·“¢°“é™ ÄQjA—UŽy—˜ r jÄ #Äq9W ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Átraj1– k é r jÄ(  ¤ê$ ¢ ¥ ¨¡¡¨Ä¯“Ÿ ™¡ Ÿ ¡“¡¸ k é r jĉÈÄ‚ŽW ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁConcat– k é¢¯“¡¡¨¢·“¢°“k ™ÄÐÒÄ-&ÄmbyÄ}²O¡“˜£¯“¡¡¨¢·“¢°“†5™Äèž ÄI¦—†5—ÄsŸÄ²¡ —˜ r jĉĚÄa ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Álo– k é r jÄ;٠ĈUW ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Áhi– k é r jÄ'y ÄzW ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Áhi– k é r jÄ;y ÄŠÚa ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Álo– k é r jÄ>!Ä‚7W ¢ ¨Å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š œžœžœžœžœ˜jKšœ=˜=Kšžœ˜—Kšœ ˜ Kšœ˜K˜K˜—š œžœžœžœžœ˜>Kšžœ˜K˜K˜—š œžœžœOžœžœ˜‰Kšžœ3˜9K˜—K™šœ™K™KšœÛ™Û—K˜K™š œžœžœ&˜?Kšœžœ ˜'K˜š œ˜!Kšœ˜šžœžœžœž˜*Jšœ˜Jšžœ˜—K˜—Kšœ+˜+Kšœ žœžœ˜K˜Kšœ"˜"Kšžœžœžœžœ˜#Kšžœžœžœ˜$K˜"Kšœ!˜!Kšœ˜Kšœ$žœ˜*K˜K˜—šœžœžœ&˜AKšœžœ ˜'Kšœ žœ˜!Kšœ žœ˜Kšœ&˜&K˜ K˜š œ˜!Kšœ˜K˜+K˜šžœžœžœž˜%J˜Jšžœ˜—šžœžœžœž˜Kšœ˜K˜+šžœžœžœž˜%J˜Jšžœ˜—Kšžœ˜—K˜—Kšžœžœžœ˜$K˜"Kšœ$žœ˜*Kšœ˜—K˜šœ ™ K˜—š œžœ$˜7Kšœf˜fK˜ K˜ Kšœžœ ˜(Kšœ žœ˜"Kšœ žœ˜šžœžœžœž˜Kšœ˜Kšœ+˜+Kšžœ˜—šžœžœžœž˜Kšœžœ#˜1Kšœ<˜˜XJšœF˜FJšžœžœžœ#˜2Jšžœœ˜/J˜J˜—šœžœžœ?˜cKšœF˜FK˜K˜—šœžœžœC˜dJ˜ J˜ Jšœžœ˜%Jšœ žœ˜"Jšœ žœ˜šžœžœžœž˜Jšœ˜Jšœ9˜9šžœžœžœ'ž˜6JšœI˜IJšžœ˜—Jšžœ˜—šžœžœžœž˜Jšœ˜Jšœ7˜7Jšžœ˜—J˜J˜—šœž œ0˜KJšœ ˜ Jšœ ˜ Jšœ žœ˜"Jšœ žœ˜šžœžœžœž˜Jšœ˜Jšœžœ˜)šžœžœžœ'ž˜6Jšœžœ˜1Jšžœ˜—Jšžœ˜—šžœžœžœž˜Jšœ˜Jšœžœ˜)Jšžœ˜—šœ˜J˜——šœžœžœ˜/Jšœ ˜ Jšœ ˜ Jšœ žœ˜"Jšœ žœ˜šžœžœžœž˜Jšœ˜Jšœžœ ˜$Jšœžœ˜!Jšœžœ ˜$Jšœžœ ˜#šžœžœžœ'ž˜6Jšœžœ ˜,Jšœžœ˜)Jšœžœ ˜,Jšœžœ ˜+Jšžœ˜—Jšžœ˜—šžœžœžœž˜Jšœ˜Jšœžœ ˜$Jšœžœ˜!Jšœžœ ˜$Jšœžœ ˜#Jšžœ˜—šœ˜J˜——šœžœžœ,žœ˜hJšœžœ ˜DJšœžœ#˜@J˜ J˜ Jšœ žœ˜"Jšœ žœ˜šžœžœžœž˜Jšœ˜Jšœ:˜:Jšžœžœ-˜Jšžœžœžœ'ž˜6JšœJ˜JJšžœžœ?˜dJšžœ˜—Jšžœ˜—šžœžœžœž˜Jšœ˜Jšœ8˜8Jšžœžœ1˜LJšžœ˜—Jš œžœžœžœžœ ˜AK˜K˜—šœžœžœ žœ#˜gKšœB˜BK˜K˜—š œžœžœžœ žœ˜pKšœC˜CK˜K˜—š œžœ-žœ žœ˜\šžœ ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšœ0˜0Kšžœ˜—K˜K˜—šœžœ-žœ žœ˜^šžœ ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšœ.˜.Kšžœ˜—K˜K˜—š œžœžœ#˜Tšžœ ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšœ0˜0Kšžœ˜—K˜K˜—šœžœžœ#˜Všžœ ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšœ.˜.Kšžœ˜—K˜—K˜K™ š œžœžœ˜8KšœžœK˜UK˜K˜—š œžœžœ'˜?Kšžœ žœžœ"˜OK˜K˜—š œžœžœžœ˜HKšœžœ˜(K˜—K™™ K˜—šœž œ"žœ ž œDžœ žœ"žœžœ žœ ˜Kš žœžœžœ žœžœ˜Lš œ’žœžœ;žœžœžœžœ™—Lšœ žœžœ*™>Lšœžœžœ™/LšœÈ™ÈL˜6Lšœžœ˜L˜šžœžœžœ ž˜L˜%šžœ žœžœ/ž˜DL˜gL˜Lšžœ˜—LšœJžœ žœžœ ˜qL˜Lšžœ˜—Lšœ ˜L˜—šœžœOžœžœ ˜‰Lšœ˜Lšœžœ˜0Lšœžœ˜)L˜Lšœ/˜/LšžœžœB˜]šžœžœžœ ž˜Lšœ0˜0šžœ žœžœ/ž˜DL˜Kšœ˜Kšœ˜Kšœ "˜4Kšœ˜KšœL˜LK˜šžœ žœ ˜/Kšœ˜Kšœ˜Kšœžœ˜Kšœ#˜#Kšœ1˜1šžœžœ˜Kšœ$˜$Kšœžœ˜(Kšœžœ˜(Kšœ$˜$šžœžœžœžœ˜AKšœ  œFžœ˜^Kšœ˜—Kšœ˜—šžœžœ˜#Kšœ0˜0Kšœžœ˜(Kšœžœ˜(Kšœ*˜*šžœžœžœžœ˜AKšœ  œFžœ˜]Kšœ˜—Kšœ˜—Kšœ˜—Kšœ˜Kš˜—šœžœžœ<žœžœ žœžœKžœžœ˜äKšœ˜Kšœžœ ˜'Kšœžœ˜0Kšœžœ˜Kšœ˜Kšœ žœžœ˜Kšœ žœ˜'šœ ˜.Kš žœ'žœžœžœžœ™MKšœM˜Mšžœ žœ˜Kšœ<˜<šžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ žœ˜K˜—K˜—K˜K˜—šžœ žœ˜KšžœžœQ˜WKšžœžœ ˜Kšœ˜—Kšœ$ ™>Kšœ˜Kšœ˜Kšœ "˜4Kšœ˜KšœL˜LK˜šžœ žœ ˜/Kšœ˜Kšœžœ˜Kšœ#˜#Kšœ1˜1Kšœ*˜*Kšœžœ˜(Kšœžœ˜(šžœžœžœžœ˜@Kšœ3˜3KšœOžœ ˜]Kšœ˜—Kšœ˜—Kšœ˜K˜—š œžœžœ<žœžœ žœžœKžœžœ˜äKšœO™OKšœ˜Kšœžœ ˜'Kšœžœ˜0Kšœ žœ˜'Kšœžœ˜Kšœ˜Kšœ8˜8š œžœ*žœžœžœžœ˜aKšœ;˜;šžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ žœ˜K˜—K˜—šžœ žœ˜KšžœžœQ˜WKšžœžœ ˜Kšœ˜—KšœJ™JKšœ˜Kšœ˜Kšœ˜Kšœ˜KšŸK˜KK˜šžœ žœ˜'Kšœ/™/Kšœ#˜#šžœžœ˜+Kšžœžœžœžœ˜;—Kšžœ&˜*Kšœ(˜(š žœžœžœžœžœ˜#KšœJžœ˜QKšœIžœ˜OKšœ=˜=Kš œ žœžœ?žœ=žœ žœ ˜®Kšœ˜—šžœ˜Kš žœžœžœ œ7žœ˜aKš žœžœžœ œ7žœ˜bKšœ˜—K˜—Kšœ˜K˜—šœžœžœ<žœžœ žœžœžœKžœžœ˜„Kšœi™iš œžœžœžœžœ˜(Kšœ žœ"˜.šžœžœžœž˜Kšžœžœžœžœ˜3Kšžœ˜—Kšžœžœ˜K˜—Kšœ˜Kšœžœ ˜'Kšœžœ˜0Kšœ žœ˜'Kšœžœ˜Kšœžœ˜Kšœ˜Kšœ˜Kšœ žœžœ˜š œžœ'žœžœžœžœ˜`Kšžœžœžœžœ˜!Kšœr˜ršžœ žœ˜Kšœ<˜<šžœžœ˜Kšœ˜Kšœ˜Kšœ$˜$Kšœ˜Kšœ˜Kšœ žœ˜K˜—K˜—K˜—šžœ žœ˜KšžœžœQ˜WKšžœžœ ˜Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ ˜7Kšœ˜KšœL˜LKšžœ žœ$˜3Kšœ˜—K™šœ™K˜—š œžœžœžœ žœ˜OK™Kšœ˜šžœžœžœž˜$Kšœ!˜!Kšžœžœžœ˜$Kšžœ˜—Kšžœ˜ ˜K˜——š œžœžœ"žœ žœ˜UK™K˜šžœžœžœž˜&Kšœ!˜!Kšžœžœžœ˜$Kšžœ˜—Kšžœ˜ K˜—K˜Kšžœ˜K˜—…—Àp€