<> <> <> <> <> <> <<>> 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 => { << [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]; }; }; }; <<>> 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] = { <<| 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: 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.