<> <> <> <> <> <> <<>> DIRECTORY Feedback, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTraj, GGTrajTypes, GGTransform, GGUtility, Imager, ImagerTransformation, Lines2d, Lines2dTypes, RealFns, Rope, Rosary, SimpleFeedback, Vectors2d; GGTrajImpl: CEDAR PROGRAM IMPORTS Feedback, GGBoundBox, GGCoreOps, GGOutline, GGParent, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTransform, GGUtility, Imager, ImagerTransformation, Lines2d, RealFns, Rosary, SimpleFeedback, 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; MakeCurveProc: TYPE = GGTrajTypes.MakeCurveProc; OutlineData: TYPE = GGOutline.OutlineData; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; RunProc: TYPE = GGTrajTypes.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; }; }; SegToSegMatchCp: PUBLIC PROC [seg: Segment, traj: Slice, makeCurve: MakeCurveProc, type: ATOM] = { <> tSeg: Segment; success: BOOL ¬ FALSE; IF seg.class.type = type THEN { -- no need to change type or control points IF type = $Conic THEN { tSeg ¬ makeCurve[seg.lo, seg.hi, seg.props]; GGSegment.ConicSetControlPoint[tSeg, seg.class.controlPointGet[seg, 0]]; GGSegment.CopyLooks[seg, tSeg]; } ELSE { tSeg ¬ GGSegment.CopySegment[seg]; }; success ¬ AddSegment[traj, hi, tSeg, lo]; } ELSE IF type = $Arc AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types both have single control points, keep the control points the same tSeg ¬ GGSegment.MakeArc[seg.lo, seg.class.controlPointGet[seg, 0], seg.hi, seg.props]; GGSegment.CopyLooks[seg, tSeg]; success ¬ AddSegment[traj, hi, tSeg , lo] } ELSE IF type = $Conic AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types have single control points, keep the control points the same tSeg ¬ makeCurve[seg.lo, seg.hi, seg.props]; GGSegment.ConicSetControlPoint[tSeg, seg.class.controlPointGet[seg, 0]]; GGSegment.CopyLooks[seg, tSeg]; success ¬ AddSegment[traj, hi, tSeg, lo] } ELSE { -- make segment of new type with old endpoint info tSeg ¬ makeCurve[seg.lo, seg.hi, seg.props]; GGSegment.CopyLooks[seg, tSeg]; success ¬ AddSegment[traj, hi, tSeg, lo]; }; IF NOT success THEN ERROR; }; SegAndCpsToSegs: PUBLIC PROC [seg: Segment, traj: Slice, makeCurve: MakeCurveProc, type: ATOM] = { <> success: BOOL ¬ FALSE; IF seg.class.type = type THEN -- if seg is already the right type, then we don't change it success ¬ AddSegment[traj, hi, GGSegment.CopySegment[seg], lo] ELSE { -- seg isn't the right type, so we convert all of its point/controlpoint spans tSeg: Segment; last, next: Point ¬ seg.lo; FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO next ¬ seg.class.controlPointGet[seg, i]; tSeg ¬ makeCurve[last, next, seg.props]; GGSegment.CopyLooks[seg, tSeg]; -- KAP. March 6, 1987 7:29:37 pm PST success ¬ AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; last ¬ next; ENDLOOP; tSeg ¬ makeCurve[last, seg.hi, seg.props]; GGSegment.CopyLooks[seg, tSeg]; -- KAP. March 6, 1987 7:29:37 pm PST success ¬ AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; }; }; 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: Slice ¬ 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 { SimpleFeedback.Append[$Gargoyle, oneLiner, $Apology, "Only delete control points from Cubic Splines"]; }; }; ENDLOOP; bBox ¬ GGBoundBox.CopyBoundBox[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: Slice ¬ seq.slice; seqData: TrajData ¬ NARROW[seqTraj.data]; seqParts: TrajParts ¬ NARROW[seq.parts]; seqGen: SequenceGenerator; newTraj: Slice; 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.FromRuns[CopyEachSegment]; desiredSegments ¬ [originalSegments, s2, len2]; hiSegments ¬ Rosary.FromRuns[CopyEachSegment]; extractedSegments ¬ Rosary.Concat[hiSegments, loSegments]; } ELSE { desiredSegments ¬ [originalSegments, next.index, trajParts.segCount]; extractedSegments ¬ Rosary.FromRuns[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.FromRuns[CopyEachJoint]; desiredJoints ¬ [originalJoints, s2, len2]; hiJoints ¬ Rosary.FromRuns[CopyEachJoint]; extractedJoints ¬ Rosary.Concat[hiJoints, loJoints]; } ELSE { desiredJoints ¬ [originalJoints, next.index, jointCount]; extractedJoints ¬ Rosary.FromRuns[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.FromRuns[CopyEachSegment]; extractedJoints ¬ Rosary.FromRuns[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 => { << [Artwork node; type 'ArtworkInterpress on' to command tool] >> 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]; << [Artwork node; type 'ArtworkInterpress on' to command tool] >> <> }; <<>> <> 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]; }; }; }; <<>> closeness: INT ¬ -18; -- 18 bits of precision, determined by trial and error for GetIPEditable. KAP. July 6, 1992 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 point1, point2: Point; IF (point1 ¬ seg1.class.controlPointGet[seg1, j]) = (point2 ¬ seg2.class.controlPointGet[seg2, j]) THEN LOOP; <> IF RealFns.AlmostEqual[point1.x, point2.x, closeness] AND RealFns.AlmostEqual[point1.y, point2.y, closeness] THEN LOOP ELSE 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] = { <<| i j k |>> <<| v1x v1y v1z |=(v1y*v2z - v1z*v2y) i + (v1z*v2x - v1x*v2z) j>> <<| v2x v2y v2z |(v1x*v2y - v1y*v2x) k>> 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]; }; <> [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; bestSegment: Segment; diffX, diffY: REAL; bestDist ¬ RealFns.SqRt[bestDist2]; bestNormal ¬ Vectors2d.Sub[testPoint, bestPoint]; IF bestSeg = 0 THEN { jointPoint ¬ FetchJointPos[traj, 0]; diffX ¬ ABS[jointPoint.x - bestPoint.x]; diffY ¬ ABS[jointPoint.y - bestPoint.y]; bestSegment ¬ FetchSegment[traj, 0]; IF trajData.role = open AND diffX < 0.01 AND diffY < 0.01 THEN { [bestNormal, ----] ¬ bestSegment.class.jointNormal[bestSegment, jointPoint, testPoint, FALSE]; }; }; IF bestSeg = HiSegment[traj] THEN { jointPoint ¬ FetchJointPos[traj, HiJoint[traj]]; diffX ¬ ABS[jointPoint.x - bestPoint.x]; diffY ¬ ABS[jointPoint.y - bestPoint.y]; bestSegment ¬ FetchSegment[traj, bestSeg]; IF trajData.role = open AND diffX < 0.01 AND diffY < 0.01 THEN { [bestNormal, ----] ¬ bestSegment.class.jointNormal[bestSegment, jointPoint, testPoint, TRUE]; }; }; }; }; >> 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: Slice ¬ 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: Slice ¬ 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: Slice ¬ 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.