<> <> <> <> <> <> <<>> DIRECTORY CodeTimer, Feedback, FeedbackTypes, FunctionCache, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGHistoryTypes, GGModelTypes, GGMUserProfile, GGOutline, GGProps, GGSegment, GGSegmentTypes, GGSequence, GGShapes, GGSlice, GGTraj, GGTransform, GGUtility, Imager, ImagerPath, ImagerTransformation, RealFns, Rope, Rosary, Vectors2d; GGSliceImplD: CEDAR PROGRAM IMPORTS CodeTimer, Feedback, FunctionCache, GGBoundBox, GGCoreOps, GGMUserProfile, GGProps, GGSegment, GGSequence, GGShapes, GGSlice, GGTraj, GGTransform, GGUtility, Imager, ImagerTransformation, RealFns, Rosary, Vectors2d EXPORTS GGSlice = BEGIN BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGCoreTypes.BoundBox; BoundBoxObj: TYPE = GGCoreTypes.BoundBoxObj; Camera: TYPE = GGModelTypes.Camera; Circle: TYPE = GGBasicTypes.Circle; Color: TYPE = Imager.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; DefaultData: TYPE = GGModelTypes.DefaultData; EditConstraints: TYPE = GGModelTypes.EditConstraints; MsgRouter: TYPE = FeedbackTypes.MsgRouter; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; HitType: TYPE = GGModelTypes.TrajPartType; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; JointObj: TYPE = GGSegmentTypes.JointObj; Line: TYPE = GGCoreTypes.Line; Object: TYPE = Imager.Object; OutlineData: TYPE = GGOutline.OutlineData; Point: TYPE = GGBasicTypes.Point; PointAndDone: TYPE = GGModelTypes.PointAndDone; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj; PointPairAndDone: TYPE = GGModelTypes.PointPairAndDone; Scene: TYPE = GGModelTypes.Scene; SegAndIndex: TYPE = GGSequence.SegAndIndex; SegCPSequence: TYPE = GGTraj.SegCPSequence; SegCPSequenceObj: TYPE = GGTraj.SegCPSequenceObj; Segment: TYPE = GGSegmentTypes.Segment; SegmentClass: TYPE = GGSegmentTypes.SegmentClass; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; SelectMode: TYPE = GGModelTypes.SelectMode; Sequence: TYPE = GGModelTypes.Sequence; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceBoundBoxProc: TYPE = GGModelTypes.SliceBoundBoxProc; SliceClass: TYPE = GGModelTypes.SliceClass; SliceClassObj: TYPE = GGModelTypes.SliceClassObj; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceObj: TYPE = GGModelTypes.SliceObj; SliceParts: TYPE = GGModelTypes.SliceParts; StrokeEnd: TYPE = GGModelTypes.StrokeEnd; StrokeJoint: TYPE = GGModelTypes.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajHitData: TYPE = GGTraj.TrajHitData; TrajHitDataObj: TYPE = GGTraj.TrajHitDataObj; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartsObj: TYPE = GGModelTypes.TrajPartsObj; TrajPartType: TYPE = GGModelTypes.TrajPartType; Transformation: TYPE = GGModelTypes.Transformation; Vector: TYPE = GGBasicTypes.Vector; WalkProc: TYPE = GGModelTypes.WalkProc; MoveToProc: TYPE = ImagerPath.MoveToProc; LineToProc: TYPE = ImagerPath.LineToProc; CurveToProc: TYPE = ImagerPath.CurveToProc; ConicToProc: TYPE = ImagerPath.ConicToProc; ArcToProc: TYPE = ImagerPath.ArcToProc; CPSequence: TYPE = REF CPSequenceObj; CPSequenceObj: TYPE = RECORD [ cpsInSeg: SEQUENCE len: NAT OF SegCPSequence]; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; KillBoundBox: PUBLIC PROC [slice: Slice] = { FOR nextSlice: Slice _ slice, nextSlice.parent UNTIL nextSlice=NIL DO nextSlice.boxValid _ FALSE; nextSlice.tightBoxValid _ FALSE; ENDLOOP; }; KillBoundBoxOnly: PUBLIC PROC [slice: Slice] = { FOR nextSlice: Slice _ slice, nextSlice.parent UNTIL nextSlice=NIL DO nextSlice.boxValid _ FALSE; ENDLOOP; }; KillTightBoxOnly: PUBLIC PROC [slice: Slice] = { FOR nextSlice: Slice _ slice, nextSlice.parent UNTIL nextSlice=NIL DO nextSlice.tightBoxValid _ FALSE; ENDLOOP; }; TrajShapeChangeNotify: PUBLIC PROC [slice: Slice] = { trajData: TrajData _ NARROW[slice.data]; KillBoundBox[slice]; trajData.polyline _ NIL; }; <> BuildTrajSliceClass: PUBLIC PROC [] RETURNS [class: SliceClass] = { class _ NEW[SliceClassObj _ [ type: $Traj , unlink: TrajUnlink, <> getBoundBox: TrajGetBoundBox, getTransformedBoundBox: GGSlice.GenericTransformedBoundBox, getTightBox: TrajGetTightBox, copy: TrajCopy, restore: TrajRestore, <> buildPath: TrajBuildPath, drawBorder: TrajDrawBorder, drawParts: TrajDrawParts, drawTransform: TrajDrawTransform, drawSelectionFeedback: TrajDrawSelectionFeedback, drawAttractorFeedback: TrajDrawAttractorFeedback, attractorFeedbackBoundBox: TrajAttractorFeedbackBoundBox, saveSelections: TrajSaveSelections, remakeSelections: TrajRemakeSelections, <> transform: TrajTransform, <> isEmptyParts: TrajIsEmptyParts, isCompleteParts: TrajIsCompleteParts, newParts: TrajNewParts, unionParts: TrajUnionParts, differenceParts: TrajDiffParts, movingParts: TrajMovingParts, augmentParts: TrajAugmentParts, alterParts: TrajAlterParts, setSelectedFields: TrajSetSelectedFields ]]; GGSlice.BuildMoreTrajSliceClass[class]; -- kludge to split class procs across modules }; <> <<>> <> <<>> TrajUnlink: PROC [slice: Slice] = { trajData: TrajData _ NARROW[slice.data]; GGSlice.UnlinkSlice[slice]; trajData.segments _ trajData.joints _ NIL; }; TrajGetBoundBox: PROC [slice: Slice, parts: SliceParts] RETURNS [box: BoundBox] = { <> IF slice.boxValid AND parts=NIL THEN RETURN[slice.boundBox] -- fast case ELSE { EnlargeBySegmentBox: GGSequence.SegmentWalkProc = { <> GGBoundBox.EnlargeByBox[bBox: box, by: seg.class.boundBox[seg]]; }; trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ NARROW[parts]; <<>> box _ GGBoundBox.NullBoundBox[]; IF parts=NIL THEN [] _ GGSequence.WalkSegmentsInTraj[trajData, EnlargeBySegmentBox] ELSE [] _ GGSequence.WalkSegmentsInSequence[trajData, trajParts, EnlargeBySegmentBox]; IF parts=NIL THEN { -- set up cache for fast case next time around GGSlice.KillBoundBoxOnly[slice.parent]; -- invalidate ancestor caches slice.boundBox _ box; slice.boxValid _ TRUE; }; }; }; TrajGetTightBox: PROC [slice: Slice, parts: SliceParts] RETURNS [box: BoundBox] = { <> trajData: TrajData _ NARROW[slice.data]; IF slice.tightBoxValid AND parts=NIL THEN RETURN[slice.tightBox] -- fast case ELSE { EnlargeBySegmentTightBox: GGSequence.SegmentWalkProc = { <> GGBoundBox.EnlargeByBox[bBox: box, by: seg.class.tightBox[seg]]; }; trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ NARROW[parts]; box _ GGBoundBox.NullBoundBox[]; IF parts=NIL THEN [] _ GGSequence.WalkSegmentsInTraj[trajData, EnlargeBySegmentTightBox] ELSE [] _ GGSequence.WalkSegmentsInSequence[trajData, trajParts, EnlargeBySegmentTightBox]; IF parts=NIL THEN { -- set up cache for fast case next time around GGSlice.KillTightBoxOnly[slice.parent]; -- invalidate ancestor caches slice.tightBox _ box; slice.tightBoxValid _ TRUE; }; }; }; TrajCopy: PROC [slice: Slice, parts: SliceParts _ NIL] RETURNS [copy: LIST OF Slice] = { <> trajParts: TrajParts; runs: GGSequence.SequenceGenerator; IF parts=NIL THEN RETURN[LIST[CopyTraj[slice]]]; -- quickly copy the whole thing trajParts _ NARROW[parts]; CodeTimer.StartInt[$CopyTraj, $Gargoyle]; runs _ GGSequence.RunsInSequence[trajParts].seqGen; FOR nextSeq: TrajParts _ GGSequence.NextSequence[runs], GGSequence.NextSequence[runs] UNTIL nextSeq=NIL DO newSlice: Slice _ GGTraj.CopyTrajFromRun[slice, nextSeq]; NARROW[newSlice.data, TrajData].forward _ NARROW[slice.data, TrajData].forward; GGProps.CopyAll[fromSlice: slice, toSlice: newSlice]; copy _ CONS[newSlice, copy]; ENDLOOP; CodeTimer.StopInt[$CopyTraj, $Gargoyle]; }; CopyTraj: PROC [original: Traj] RETURNS [copy: Traj] = { <> trajData: TrajData _ NARROW[original.data]; originalSegments: Rosary.ROSARY _ trajData.segments; desiredSegments: Rosary.Segment _ [originalSegments, 0, trajData.segCount]; extractedSegments: Rosary.ROSARY; originalJoints: Rosary.ROSARY _ trajData.joints; desiredJoints: Rosary.Segment _ [originalJoints, 0, GGTraj.HiJoint[original] + 1]; extractedJoints: Rosary.ROSARY; CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOL _ 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: BOOL _ FALSE] = { copy, oldJoint: Joint; oldJoint _ NARROW[item]; <> copy _ NEW[JointObj _ [point: oldJoint.point, TselectedInFull: oldJoint.TselectedInFull] ]; q[copy, 1]; }; [] _ Rosary.Map[desiredJoints, CopyJointAndBuild]; }; CodeTimer.StartInt[$CopyTraj, $Gargoyle]; extractedSegments _ Rosary.FromProcProc[CopyEachSegment]; extractedJoints _ Rosary.FromProcProc[CopyEachJoint]; copy _ GGTraj.CreateTrajFromData[trajData.role, trajData.segCount, extractedSegments, extractedJoints, NIL, trajData.visibleJoints, trajData.strokeJoint, trajData.loArrow, trajData.hiArrow]; NARROW[copy.data, TrajData].forward _ NARROW[original.data, TrajData].forward; GGProps.CopyAll[fromSlice: original, toSlice: copy]; CodeTimer.StopInt[$CopyTraj, $Gargoyle]; }; TrajRestore: PROC [from: Slice, to: Slice] = { <> <> IF to=NIL OR from=NIL THEN ERROR; IF to.class#from.class THEN ERROR; IF to.class.type#$Traj THEN ERROR; BEGIN fromData: TrajData _ NARROW[from.data]; toData: TrajData _ NARROW[to.data]; <> toData^ _ fromData^; to.selectedInFull _ from.selectedInFull; -- RECORD of BOOL to.normalSelectedParts _ NIL; -- caller must reselect to.hotSelectedParts _ NIL; -- caller must reselect to.activeSelectedParts _ NIL; -- caller must reselect to.matchSelectedParts _ NIL; -- caller must reselect <> <> to.tightBox^ _ from.tightBox^; to.tightBoxValid _ from.tightBoxValid; to.boundBox^ _ from.boundBox^; to.boxValid _ from.boxValid; to.onOverlay _ from.onOverlay; to.extraPoints _ from.extraPoints; to.priority _ from.priority; to.historyTop _ from.historyTop; END; }; <<>> <> <<>> BuildConstrainedPathSeg: PROC [slice: Slice, transformParts: SliceParts, segNum: NAT, seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector, editConstraints: EditConstraints, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc] = { IF entire THEN seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo] ELSE { dirVec: Vector; distance: REAL; partialTransform, ctrlLoTrans, ctrlHiTrans: Transformation; newJoint: Point; ctrlPoint: Point; ctrlPoints: BitVector _ NEW[GGBasicTypes.BitVectorObj[2]]; -- only need two for a Bezier segment trajTransformParts: TrajParts _ NARROW[transformParts]; ctrlPoints[0] _ IF controlPoints=NIL THEN FALSE ELSE controlPoints[0]; ctrlPoints[1] _ IF controlPoints=NIL THEN FALSE ELSE controlPoints[1]; ctrlLoTrans _ IF ctrlPoints[0] THEN transform ELSE idTransform; ctrlHiTrans _ IF ctrlPoints[1] THEN transform ELSE idTransform; IF lo AND NOT ctrlPoints[0] THEN { -- Low side of segment ctrlPoint _ seg.class.controlPointGet[seg, 0]; ctrlPoints[0] _ TRUE; newJoint _ ImagerTransformation.Transform[transform, seg.lo]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, newJoint]]; dirVec _ Vectors2d.VectorFromPoints[seg.lo, ctrlPoint]; IF editConstraints = tangent THEN { distance _ Vectors2d.Distance[newJoint, ctrlPoint]; IF dirVec#[0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[dirVec,distance]]; } ELSE ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, dirVec]; }; IF hi AND NOT ctrlPoints[1] THEN { -- High side of Segment ctrlPoint _ seg.class.controlPointGet[seg, 1]; ctrlPoints[1] _ TRUE; newJoint _ ImagerTransformation.Transform[transform, seg.hi]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, newJoint]]; dirVec _ Vectors2d.VectorFromPoints[seg.hi, ctrlPoint]; IF editConstraints = tangent THEN { distance _ Vectors2d.Distance[newJoint, ctrlPoint]; IF dirVec#[0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[dirVec,distance]]; } ELSE ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, dirVec]; }; <> IF NOT ctrlPoints[0] AND NOT lo THEN { prevSegNum: INT _ GGTraj.PreviousSegmentNum[slice, segNum]; IF prevSegNum#-1 AND GGTraj.FetchSegment[slice, prevSegNum].class.type = $Bezier THEN { <> IF trajTransformParts.controlPoints[prevSegNum][1] THEN { angle: REAL; transformedPoint: Point; otherCtrlPoint: Point _ seg.class.controlPointGet[GGTraj.FetchSegment[slice, prevSegNum], 1]; otherDirVector: Vector _ Vectors2d.VectorFromPoints[seg.lo, otherCtrlPoint]; ctrlPoint _ seg.class.controlPointGet[seg, 0]; dirVec _ Vectors2d.VectorFromPoints[seg.lo, ctrlPoint]; distance _ Vectors2d.Magnitude[dirVec]; transformedPoint _ ImagerTransformation.Transform[transform, otherCtrlPoint]; angle _ Vectors2d.SmallestAngleBetweenVectors[otherDirVector, Vectors2d.VectorFromPoints[seg.lo, transformedPoint]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.lo]]; ctrlPoints[0] _ TRUE; IF editConstraints = tangent THEN { IF transformedPoint # seg.lo AND otherDirVector # [0,0] THEN { --Don't flip based on other cps 0 length vec. ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; -- Else just use identity. } ELSE { -- length IF otherDirVector # [0,0] THEN { distance _ RealFns.SqRt[Vectors2d.DistanceSquared[seg.lo, transformedPoint] / Vectors2d.MagnitudeSquared[otherDirVector]] * distance; ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; -- Else just use identity. }; }; }; }; IF NOT ctrlPoints[1] AND NOT hi THEN { nextSegNum: INT _ GGTraj.FollowingSegmentNum[slice, segNum]; IF nextSegNum # -1 AND GGTraj.FetchSegment[slice, nextSegNum].class.type = $Bezier THEN { IF trajTransformParts.controlPoints[nextSegNum][0] THEN { angle: REAL; transformedPoint: Point; otherCtrlPoint: Point _ seg.class.controlPointGet[GGTraj.FetchSegment[slice, nextSegNum], 0]; otherDirVector: Vector _ Vectors2d.VectorFromPoints[seg.hi, otherCtrlPoint]; ctrlPoint _ seg.class.controlPointGet[seg, 1]; dirVec _ Vectors2d.VectorFromPoints[seg.hi, ctrlPoint]; distance _ Vectors2d.Magnitude[dirVec]; transformedPoint _ ImagerTransformation.Transform[transform, otherCtrlPoint]; angle _ Vectors2d.SmallestAngleBetweenVectors[otherDirVector, Vectors2d.VectorFromPoints[seg.hi, transformedPoint]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.hi]]; ctrlPoints[1] _ TRUE; IF editConstraints = tangent THEN { IF transformedPoint # seg.hi AND otherDirVector # [0,0] THEN { --Don't flip based on other cps 0 length vec. ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; -- Else just use identity. } ELSE { -- length IF otherDirVector # [0,0] THEN { distance _ RealFns.SqRt[Vectors2d.DistanceSquared[seg.hi, transformedPoint] / Vectors2d.MagnitudeSquared[otherDirVector]] * distance; ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; --Else just use identity. }; }; }; }; GGSegment.BZSpecialTransformPath[seg, transform, lo, hi, ctrlPoints, ctrlLoTrans, ctrlHiTrans, curveTo]; }; }; <> TrajBuildPath: PROC [slice: Slice, transformParts: SliceParts, transform: ImagerTransformation.Transformation, moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc, editConstraints: EditConstraints _ none] = { TransformParts: PROC = { hiSegment: NAT _ GGTraj.HiSegment[slice]; trajParts: TrajParts _ NARROW[transformParts]; moveTo[IF trajParts.segments[0] OR trajParts.joints[0] THEN ImagerTransformation.Transform[transform, GGTraj.FetchJointPos[slice, 0]] ELSE GGTraj.FetchJointPos[slice, 0] ]; FOR index: INT IN [0..hiSegment] DO seg: Segment _ GGTraj.FetchSegment[slice, index]; IF seg.class.type=$Bezier AND editConstraints#none THEN BuildConstrainedPathSeg[slice, transformParts, index, seg, transform, trajParts.segments[index], trajParts.joints[index], trajParts.joints[(index+1) MOD trajParts.segments.len], trajParts.controlPoints[index], editConstraints, lineTo, curveTo, conicTo, arcTo] ELSE seg.class.buildPathTransform[seg, transform, trajParts.segments[index], trajParts.joints[index], trajParts.joints[(index+1) MOD trajParts.segments.len], trajParts.controlPoints[index], lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; TransformEntire: PROC = { hiSegment: NAT _ GGTraj.HiSegment[slice]; moveTo[ImagerTransformation.Transform[transform, GGTraj.FetchJointPos[slice, 0]] ]; FOR index: INT IN [0..hiSegment] DO seg: Segment _ GGTraj.FetchSegment[slice, index]; IF seg.class.type=$Bezier AND editConstraints#none THEN BuildConstrainedPathSeg[slice, transformParts, index, seg, transform, TRUE, TRUE, TRUE, NIL, editConstraints, lineTo, curveTo, conicTo, arcTo] ELSE seg.class.buildPathTransform[seg, transform, TRUE, TRUE, TRUE, NIL, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; TransformNone: PROC = { hiSegment: NAT _ GGTraj.HiSegment[slice]; moveTo[GGTraj.FetchJointPos[slice, 0]]; FOR index: INT IN [0..hiSegment] DO seg: Segment _ GGTraj.FetchSegment[slice, index]; <> <> seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; IF transform#NIL THEN IF transformParts=NIL THEN TransformEntire[] ELSE TransformParts[] ELSE TransformNone[]; }; <<>> TrajDrawBorder: PROC [slice: Slice, drawParts: SliceParts, transformParts: SliceParts, transform: Transformation, dc: Imager.Context, camera: Camera, quick: BOOL, editConstraints: EditConstraints _ none] = { <> DoTrajDrawBorder: PROC = { trajDrawParts: TrajParts _ NARROW[drawParts]; trajTransformParts: TrajParts _ NARROW[transformParts]; drawEntireTraj: BOOL; transformNone: BOOL; transformEntireTraj: BOOL; CodeTimer.StartInt[$TrajDrawBorder, $Gargoyle]; drawEntireTraj _ trajDrawParts=NIL OR GGSequence.IsComplete[trajDrawParts]; transformNone _ transform=NIL; transformEntireTraj _ NOT transformNone AND (trajTransformParts=NIL OR GGSequence.IsComplete[trajTransformParts]); IF drawEntireTraj AND (transformNone OR (transformEntireTraj AND ImagerTransformation.CloseToTranslation[transform, identity])) THEN CachedDrawBorder[dc, slice, transform, trajTransformParts] ELSE DrawBorderAux[slice, trajDrawParts, trajTransformParts, transform, dc, camera, editConstraints, drawEntireTraj, transformNone, transformEntireTraj]; CodeTimer.StopInt[$TrajDrawBorder, $Gargoyle]; }; Imager.DoSave[dc, DoTrajDrawBorder]; }; identity: Transformation _ ImagerTransformation.Scale[1.0]; TrajDrawParts: PROC [slice: Slice, parts: SliceParts _ NIL, dc: Imager.Context, camera: Camera, quick: BOOL] = { <> cpCount: NAT; seg: Segment; trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ NARROW[parts]; IF trajParts=NIL THEN DrawTraj[dc, slice] ELSE IF AllStrokePropsAndColorsEqual[slice] THEN DrawSingleStrokeTrajSeq[dc, slice, parts] ELSE { hiSegment: NAT _ GGTraj.HiSegment[slice]; Imager.SetStrokeJoint[dc, trajData.strokeJoint]; FOR i: INT IN [0..hiSegment] DO IF trajParts.segments[i] THEN { seg _ GGTraj.FetchSegment[slice, i]; IF seg.strokeWidth=0.0 OR seg.color=NIL THEN LOOP; Imager.SetStrokeWidth[dc, seg.strokeWidth]; Imager.SetStrokeEnd[dc, seg.strokeEnd]; Imager.SetColor[dc, seg.color]; SegMaskStroke[dc, seg]; cpCount _ seg.class.controlPointCount[seg]; FOR j: NAT IN [0..cpCount) DO IF trajParts.controlPoints[i][j] THEN GGShapes.DrawCP[dc, seg.class.controlPointGet[seg, j], camera.cpScale]; ENDLOOP; }; ENDLOOP; }; }; TrajDrawTransform: PROC [slice: Slice, parts: SliceParts _ NIL, dc: Imager.Context, camera: Camera, transform: ImagerTransformation.Transformation, editConstraints: EditConstraints] = { <> <> <> TrajDrawBorder[slice, NIL, parts, transform, dc, camera, FALSE, editConstraints]; }; TrajDrawSelectionFeedback: PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = { <> DoDrawFeedback: PROC = { segGen: GGModelTypes.SegmentGenerator; jointGen: GGModelTypes.JointGenerator; seg: Segment; someNormal, someHot, thisCPisHot, thisCPisSelected: BOOL; point: Point; <> sliceTrajData: TrajData _ NARROW[slice.data]; selectedTrajParts: TrajParts _ NARROW[selectedParts]; hotTrajParts: TrajParts _ NARROW[hotParts]; jointGen _ GGSequence.JointsInTraj[sliceTrajData]; FOR i: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO thisCPisHot _ hotTrajParts#NIL AND hotTrajParts.joints[i]; thisCPisSelected _ selectedTrajParts#NIL AND selectedTrajParts.joints[i]; point _ GGTraj.FetchJointPos[slice, i]; IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, point, hot, camera.cpScale]; IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, point, normal, camera.cpScale]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawJoint[dc, point, camera.cpScale]; ENDLOOP; <> someNormal _ selectedTrajParts#NIL; someHot _ hotTrajParts#NIL; segGen _ GGSequence.SegmentsInTraj[sliceTrajData]; FOR next: GGSequence.SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg=NIL DO i: NAT _ next.index; seg _ next.seg; IF someNormal OR someHot THEN { FOR j: INT IN [0..seg.class.controlPointCount[seg]) DO thisCPisHot _ hotTrajParts#NIL AND hotTrajParts.controlPoints[i][j]; thisCPisSelected _ selectedTrajParts#NIL AND selectedTrajParts.controlPoints[i][j]; point _ seg.class.controlPointGet[seg, j]; IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, point, hot, camera.cpScale]; IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, point, normal, camera.cpScale]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawCP[dc, point, camera.cpScale]; ENDLOOP; }; ENDLOOP; }; trajData: TrajData _ NARROW[slice.data]; IF caretIsMoving OR dragInProgress THEN RETURN; IF selectedParts=NIL AND hotParts=NIL THEN RETURN; IF camera.quality # quality THEN Imager.DoSave[dc, DoDrawFeedback]; }; TrajDrawAttractorFeedback: PROC [slice: Slice, attractorParts: SliceParts, selectedParts: SliceParts, dragInProgress: BOOL, dc: Imager.Context, camera: Camera, editConstraints: EditConstraints] = { success: BOOL _ FALSE; partType: TrajPartType; traj: Traj _ slice; seg: Segment; jointNum, segNum: NAT; [success, partType, ----, ----, jointNum , ----, ----, seg, segNum] _ GGTraj.UnpackSimpleDescriptor[slice, attractorParts]; IF NOT success THEN ERROR; -- for debugging SELECT partType FROM joint => { -- highlight the cps and joints of the two adjacent segments prev: INT _ GGTraj.PreviousSegmentNum[slice, jointNum]; previous: Segment _ GGTraj.PreviousSegment[slice, jointNum]; this: Segment _ IF jointNum <= GGTraj.HiSegment[slice] THEN GGTraj.FetchSegment[slice, jointNum] ELSE NIL; IF selectedParts=NIL THEN { IF previous#NIL THEN DrawCpsAndJoints[dc, previous, camera]; IF this#NIL THEN DrawCpsAndJoints[dc, this, camera]; } ELSE { <> IF this#NIL AND NOT (dragInProgress AND GGSequence.ContainsSegmentParts[NARROW[selectedParts], jointNum]) THEN DrawCpsAndJoints[dc, this, camera, GGSlice.DescriptorFromParts[traj, selectedParts], jointNum, editConstraints]; IF previous#NIL AND NOT (dragInProgress AND GGSequence.ContainsSegmentParts[NARROW[selectedParts], GGTraj.PreviousSegmentNum[traj, jointNum]]) THEN DrawCpsAndJoints[dc, previous, camera, GGSlice.DescriptorFromParts[traj, selectedParts], prev, editConstraints]; }; }; segment, controlPoint => DrawCpsAndJoints[dc, seg, camera]; ENDCASE; -- none }; TrajAttractorFeedbackBoundBox: PROC [slice: Slice, attractorParts: SliceParts, selectedParts: SliceParts, dragInProgress: BOOL, camera: Camera, editConstraints: EditConstraints] RETURNS [box: BoundBox] = { success: BOOL _ FALSE; partType: TrajPartType; traj: Traj _ slice; seg: Segment; jointNum, segNum: NAT; box _ GGBoundBox.NullBoundBox[]; [success, partType, ----, ----, jointNum , ----, ----, seg, segNum] _ GGTraj.UnpackSimpleDescriptor[slice, attractorParts]; IF NOT success THEN ERROR; -- for debugging SELECT partType FROM joint => { -- highlight the cps and joints of the two adjacent segments prev: INT _ GGTraj.PreviousSegmentNum[slice, jointNum]; previous: Segment _ GGTraj.PreviousSegment[slice, jointNum]; this: Segment _ IF jointNum <= GGTraj.HiSegment[slice] THEN GGTraj.FetchSegment[slice, jointNum] ELSE NIL; IF selectedParts=NIL THEN { IF previous#NIL THEN EnlargeByCpsAndJoints[box, previous, camera]; IF this#NIL THEN EnlargeByCpsAndJoints[box, this, camera]; } ELSE { <> IF this#NIL AND NOT (dragInProgress AND GGSequence.ContainsSegmentParts[NARROW[selectedParts], jointNum]) THEN EnlargeByCpsAndJoints[box, this, camera, GGSlice.DescriptorFromParts[traj, selectedParts], jointNum, editConstraints]; IF previous#NIL AND NOT (dragInProgress AND GGSequence.ContainsSegmentParts[NARROW[selectedParts], GGTraj.PreviousSegmentNum[traj, jointNum]]) THEN EnlargeByCpsAndJoints[box, previous, camera, GGSlice.DescriptorFromParts[traj, selectedParts], prev, editConstraints]; }; }; segment, controlPoint => EnlargeByCpsAndJoints[box, seg, camera]; ENDCASE; -- none GGBoundBox.EnlargeByOffset[box, GGModelTypes.halfJointSize*camera.cpScale +1]; }; <> Props: TYPE = REF PropsRec; PropsRec: TYPE = RECORD [ slice: Slice, hiSeg: NAT, cpCount: SEQUENCE len: NAT OF NAT ]; FindImagerObject: PROC [slice: Slice, trajData: TrajData] RETURNS [object: Imager.Object, displacement: Vector _ [0,0]] = { EqualPattern: PROC [p1, p2: SequenceOfReal] RETURNS [BOOL _ FALSE] = { IF p1.len#p2.len THEN RETURN[FALSE]; FOR i: NAT IN [0.. p1.len) DO IF p1[i]#p2[i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; CompareProps: FunctionCache.CompareProc = { cachedProps: Props _ NARROW[argument]; cachedSlice: Slice _ cachedProps.slice; cachedHiSeg: NAT _ cachedProps.hiSeg; cachedTrajData: TrajData _ NARROW[cachedSlice.data]; cachedSeg, seg: Segment; epsilon: REAL = 0.001; IF cachedHiSeg # hiSeg THEN RETURN[FALSE]; IF cachedTrajData.strokeJoint # trajData.strokeJoint THEN RETURN[FALSE]; FOR i: NAT IN [0..cachedHiSeg] DO cachedSeg _ GGTraj.FetchSegment[cachedSlice, i]; seg _ GGTraj.FetchSegment[slice, i]; <> IF seg.class # cachedSeg.class THEN RETURN[FALSE]; <> IF NOT GGSegment.SameShape[seg, cachedSeg] THEN RETURN[FALSE]; displacement _ Vectors2d.Sub[seg.lo, cachedSeg.lo]; IF NOT ClosePoints[Vectors2d.Add[cachedSeg.hi, displacement], seg.hi] THEN RETURN[FALSE]; IF cachedProps.cpCount[i] # seg.class.controlPointCount[seg] THEN RETURN[FALSE]; FOR j: NAT IN [0..cachedProps.cpCount[i]) DO IF NOT ClosePoints[Vectors2d.Add[cachedSeg.class.controlPointGet[cachedSeg, j], displacement], seg.class.controlPointGet[seg, j]] THEN RETURN[FALSE]; ENDLOOP; <> IF ABS[cachedSeg.strokeWidth-seg.strokeWidth] > epsilon THEN RETURN[FALSE]; IF cachedSeg.strokeEnd#seg.strokeEnd THEN RETURN[FALSE]; IF NOT GGCoreOps.EquivalentColors[cachedSeg.color, seg.color] THEN RETURN[FALSE]; IF cachedSeg.dashed#seg.dashed THEN RETURN[FALSE]; IF cachedSeg.dashed AND ( cachedSeg.offset#seg.offset OR cachedSeg.length#seg.length OR NOT EqualPattern[cachedSeg.pattern, seg.pattern]) THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; value: FunctionCache.Range; ok: BOOL; hiSeg: NAT _ GGTraj.HiSegment[slice]; [value, ok] _ FunctionCache.Lookup[x: cache, compare: CompareProps, clientID: $GGTrajObject]; RETURN [IF ok THEN NARROW[value] ELSE NIL, displacement]; }; ClosePoints: PROC [p1, p2: Point] RETURNS [BOOL] = { epsilon: REAL = 0.01; -- control points within 0.01 pixels should be plenty close enough RETURN[ABS[p1.x-p2.x] < epsilon AND ABS[p1.y-p2.y] < epsilon]; }; CreateProps: PROC [slice: Slice, trajData: TrajData] RETURNS [props: Props] = { hiSeg: NAT _ GGTraj.HiSegment[slice]; seg: Segment; props _ NEW[PropsRec[hiSeg+1]]; props.slice _ TrajCopy[slice].first; props.hiSeg _ hiSeg; FOR i: NAT IN [0..hiSeg] DO seg _ GGTraj.FetchSegment[slice, i]; props.cpCount[i] _ seg.class.controlPointCount[seg]; ENDLOOP; }; <> CachedDrawBorder: PROC [dc: Imager.Context, slice: Slice, viewView: Transformation, trajTransformParts: TrajParts] = { OPEN ImagerTransformation; -- see Transform and InverseTransform object: Imager.Object; position: Imager.VEC; trajProps: Props; trajData: TrajData _ NARROW[slice.data]; IF GGMUserProfile.GetTurboOn[] THEN { [object, position] _ FindImagerObject[slice, trajData]; IF object = NIL THEN { -- put traj in cache clipR: Imager.Rectangle _ GGBoundBox.RectangleFromBoundBox[TrajGetBoundBox[slice, NIL]]; props: Props _ CreateProps[slice, trajData]; -- props of this Imager.Object object _ NEW[Imager.ObjectRep _ [draw: TrajDrawObject, clip: clipR, data: props]]; position _ [0,0]; FunctionCache.Insert[cache, props, object, 60, $GGTrajObject]; }; trajProps _ NARROW[object.data]; IF viewView # NIL THEN Imager.ConcatT[dc, viewView]; Imager.DrawObject[context: dc, object: object, interactive: TRUE, position: position]; } ELSE DrawBorderAux[slice, NIL, trajTransformParts, viewView, dc, NIL, none, TRUE, viewView = NIL, viewView # NIL]; }; TrajDrawObject: PROC [self: Imager.Object, context: Imager.Context] = { trajProps: Props _ NARROW[self.data]; DrawBorderAux[trajProps.slice, NIL, NIL, NIL, context, NIL, none, TRUE, TRUE, FALSE]; }; DrawBorderAux: PROC [slice: Slice, trajDrawParts: TrajParts, trajTransformParts: TrajParts, transform: Transformation, dc: Imager.Context, camera: Camera, editConstraints: EditConstraints _ none, drawEntireTraj, transformNone, transformEntireTraj: BOOL] = { hiSegment: NAT _ GGTraj.HiSegment[slice]; IF drawEntireTraj AND AllStrokePropsAndColorsEqual[slice] THEN { TrajDrawBorderSingleStroke[slice, trajTransformParts, transform, dc, editConstraints]; IF transform#NIL THEN DrawRubberControlPoints[dc, trajTransformParts, slice, transform, camera]; RETURN; }; FOR i: INT IN [0..hiSegment] DO IF drawEntireTraj OR trajDrawParts.segments[i] THEN { -- draw this segment seg: Segment _ GGTraj.FetchSegment[slice, i]; IF seg.strokeWidth=0.0 OR seg.color=NIL THEN LOOP; Imager.SetStrokeJoint[dc, bevel]; -- some contexts require that this be set to something Imager.SetStrokeEnd[dc, seg.strokeEnd]; Imager.SetStrokeWidth[dc, seg.strokeWidth]; Imager.SetColor[dc, seg.color]; SELECT TRUE FROM transformNone => MaskStrokeTransform[ dc: dc, seg: seg, transform: transform, entire: FALSE, lo: FALSE, hi: FALSE, controlPoints: NIL, -- nothing transformed slice: slice, transformParts: trajTransformParts, segNum: i, camera: camera, editConstraints: editConstraints]; transformEntireTraj => MaskStrokeTransform[ dc: dc, seg: seg, transform: transform, entire: TRUE, lo: TRUE, hi: TRUE, controlPoints: NIL, -- everything transformed slice: slice, transformParts: trajTransformParts, segNum: i, camera: camera, editConstraints: editConstraints]; ENDCASE => MaskStrokeTransform[ dc: dc, seg: seg, transform: transform, entire: trajTransformParts.segments[i], lo: trajTransformParts.joints[i], hi: trajTransformParts.joints[GGTraj.FollowingJoint[slice, i]], controlPoints: trajTransformParts.controlPoints[i], slice: slice, transformParts: trajTransformParts, segNum: i, camera: camera, editConstraints: editConstraints]; }; ENDLOOP; }; TrajDrawBorderSingleStroke: PROC [slice: Slice, trajParts: TrajParts, transform: Transformation, dc: Imager.Context, editConstraints: EditConstraints] = { BuildPathNoTransform: Imager.PathProc = { seg: Segment; hiSegment: NAT _ GGTraj.HiSegment[slice]; firstPoint: Point _ GGTraj.FetchJointPos[slice, 0]; moveTo[firstPoint]; FOR i: INT IN [0..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; BuildPathTransform: Imager.PathProc = { seg: Segment; entire, lo, hi: BOOL _ FALSE; controlPoints: BitVector; firstPoint: Point; hiSegment: NAT _ GGTraj.HiSegment[slice]; <> entire _ trajParts=NIL OR trajParts.segments[0]; lo _ trajParts=NIL OR trajParts.joints[0]; firstPoint _ GGTraj.FetchJointPos[slice, 0]; IF entire OR lo THEN firstPoint _ ImagerTransformation.Transform[transform, firstPoint]; moveTo[firstPoint]; <> FOR i: INT IN [0..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; entire _ trajParts=NIL OR trajParts.segments[i]; lo _ trajParts=NIL OR trajParts.joints[i]; hi _ trajParts=NIL OR trajParts.joints[GGTraj.FollowingJoint[slice, i]]; controlPoints _ trajParts.controlPoints[i]; IF seg.class.type = $Bezier AND editConstraints # none THEN { BuildConstrainedPathSeg[slice, trajParts, i, seg, transform, entire, lo, hi, controlPoints, editConstraints, lineTo, curveTo, conicTo, arcTo]; <> } ELSE { seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo]; }; ENDLOOP; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = {RETURN[pattern[i]];}; trajData: TrajData _ NARROW[slice.data]; firstSeg: Segment _ GGTraj.FetchSegment[slice, 0]; strokeWidth: REAL _ firstSeg.strokeWidth; pattern: SequenceOfReal _ firstSeg.pattern; IF strokeWidth # 0.0 AND firstSeg.color # NIL THEN { Imager.SetColor[dc, firstSeg.color]; Imager.SetStrokeWidth[dc, strokeWidth]; Imager.SetStrokeEnd[dc, firstSeg.strokeEnd]; Imager.SetStrokeJoint[dc, trajData.strokeJoint]; IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, IF transform=NIL THEN BuildPathNoTransform ELSE BuildPathTransform, pattern.len, PatternProc, firstSeg.offset, firstSeg.length] ELSE Imager.MaskStroke[dc, IF transform=NIL THEN BuildPathNoTransform ELSE BuildPathTransform, trajData.role=fence OR trajData.role=hole]; }; }; -- end TrajDrawBorderSingleStroke DrawRubberControlPoints: PROC [dc: Imager.Context, trajParts: TrajParts, slice: Slice, transform: ImagerTransformation.Transformation, camera: Camera] = { <> controlPoints: BitVector; seg: Segment; cpCount: NAT; cPointsInSeq: CPSequence; cPointsInSeg: SegCPSequence; hiSegment: NAT _ GGTraj.HiSegment[slice]; drawForTraj, drawForSeg: BOOL _ FALSE; IF trajParts = NIL THEN RETURN; <
> FOR i: INT IN [0..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; controlPoints _ trajParts.controlPoints[i]; cpCount _ seg.class.controlPointCount[seg]; IF NOT trajParts.segments[i] THEN { FOR j: NAT IN [0..cpCount) DO IF controlPoints[j] THEN { IF NOT drawForTraj THEN cPointsInSeq _ NEW[CPSequenceObj[GGTraj.HiSegment[slice] + 1]]; IF NOT drawForSeg THEN cPointsInSeg _ NEW[SegCPSequenceObj[cpCount]]; cPointsInSeg[j].cPoint _ ImagerTransformation.Transform[transform, seg.class.controlPointGet[seg, j]]; cPointsInSeg[j].show _ TRUE; drawForTraj _ drawForSeg _ TRUE; } ELSE IF drawForSeg THEN cPointsInSeg[j].show _ FALSE; ENDLOOP; IF drawForSeg THEN { cPointsInSeq[i] _ cPointsInSeg; drawForSeg _ FALSE; }; }; ENDLOOP; <> IF cPointsInSeq#NIL THEN { FOR i: NAT IN [0..cPointsInSeq.len) DO IF NOT trajParts.segments[i] AND NOT cPointsInSeq[i]=NIL THEN { FOR j: NAT IN [0..cPointsInSeq[i].len) DO cPoint: Point _ cPointsInSeq[i][j].cPoint; IF cPointsInSeq[i][j].show THEN GGShapes.DrawCP[dc, cPoint, camera.cpScale]; <> ENDLOOP; }; ENDLOOP; }; }; -- end of DrawRubberControlPoints <> DrawSingleStrokeTraj: PROC [dc: Imager.Context, slice: Slice] = { BuildPath: Imager.PathProc = { seg: Segment; hiSegment: NAT _ GGTraj.HiSegment[slice]; firstPoint: Point _ GGTraj.FetchJointPos[slice, 0]; moveTo[firstPoint]; FOR i: INT IN [0..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; trajData: TrajData _ NARROW[slice.data]; firstSeg: Segment _ GGTraj.FetchSegment[slice, 0]; strokeWidth: REAL _ firstSeg.strokeWidth; pattern: SequenceOfReal _ firstSeg.pattern; IF strokeWidth=0.0 OR firstSeg.color=NIL THEN RETURN; Imager.SetStrokeWidth[dc, strokeWidth]; Imager.SetStrokeEnd[dc, firstSeg.strokeEnd]; Imager.SetStrokeJoint[dc, trajData.strokeJoint]; Imager.SetColor[dc, firstSeg.color]; IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length] ELSE Imager.MaskStroke[dc, BuildPath, trajData.role = fence OR trajData.role = hole]; }; -- end DrawSingleStrokeTraj DrawTraj: PROC [dc: Imager.Context, slice: Slice] = { <.>> <.>> <.>> <> <> DoDrawTraj: PROC [dc: Imager.Context] = { seg: Segment; hiSegment: NAT _ GGTraj.HiSegment[slice]; Imager.SetStrokeJoint[dc, trajData.strokeJoint]; FOR i: INT IN [0..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; IF seg.strokeWidth#0.0 AND seg.color#NIL THEN { Imager.SetStrokeWidth[dc, seg.strokeWidth]; Imager.SetStrokeEnd[dc, seg.strokeEnd]; Imager.SetColor[dc, seg.color]; MaskStroke[dc, seg]; }; ENDLOOP; }; trajData: TrajData _ NARROW[slice.data]; IF AllStrokePropsAndColorsEqual[slice] THEN DrawSingleStrokeTraj[dc, slice] ELSE DoDrawTraj[dc]; -- why no DoSave here ?? }; DrawSingleStrokeTrajSeq: PROC [dc: Imager.Context, slice: Slice, parts: SliceParts _ NIL] = { BuildPath: Imager.PathProc = { hiSegment: NAT _ GGTraj.HiSegment[slice]; FOR i: INT IN [0..hiSegment] DO IF trajParts=NIL OR trajParts.segments[i] THEN { seg: Segment _ GGTraj.FetchSegment[slice, i]; moveTo[seg.lo]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; }; ENDLOOP; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ NARROW[parts]; firstSeg: Segment _ GGTraj.FetchSegment[slice, 0]; pattern: SequenceOfReal _ firstSeg.pattern; IF firstSeg.strokeWidth=0.0 OR firstSeg.color=NIL THEN RETURN; Imager.SetStrokeWidth[dc, firstSeg.strokeWidth]; Imager.SetStrokeEnd[dc, firstSeg.strokeEnd]; Imager.SetStrokeJoint[dc, trajData.strokeJoint]; Imager.SetColor[dc, firstSeg.color]; IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length] ELSE Imager.MaskStroke[dc, BuildPath, trajData.role = fence OR trajData.role = hole]; }; DrawCpsAndJoints: PROC [dc: Imager.Context, seg: Segment, camera: Camera, selSeq: Sequence _ NIL, segNum: NAT _ 999, editConstraints: EditConstraints _ none] = { point: Point; GGShapes.DrawJoint[dc, seg.lo, camera.cpScale]; GGShapes.DrawJoint[dc, seg.hi, camera.cpScale]; FOR j:INT IN [0..seg.class.controlPointCount[seg]) DO IF editConstraints = none OR NOT GGSequence.IsConstrained[selSeq, segNum, j, editConstraints] THEN { point _ seg.class.controlPointGet[seg, j]; GGShapes.DrawCP[dc, point, camera.cpScale]; }; ENDLOOP; }; EnlargeByCpsAndJoints: PROC [box: BoundBox, seg: Segment, camera: Camera, selSeq: Sequence _ NIL, segNum: NAT _ 999, editConstraints: EditConstraints _ none] = { point: Point; GGBoundBox.EnlargeByPoint[box, seg.lo]; GGBoundBox.EnlargeByPoint[box, seg.hi]; FOR j: INT IN [0..seg.class.controlPointCount[seg]) DO IF editConstraints = none OR NOT GGSequence.IsConstrained[selSeq, segNum, j, editConstraints] THEN { point _ seg.class.controlPointGet[seg, j]; GGBoundBox.EnlargeByPoint[box, point]; }; ENDLOOP; }; TrajSaveSelections: PROC [slice: Slice, parts: SliceParts, selectClass: SelectionClass] = { <> trajParts: TrajParts _ NARROW[parts]; IF trajParts=NIL THEN GGTraj.ClearSelection[slice, selectClass] ELSE GGTraj.SaveSelectionInParts[slice, trajParts, selectClass]; }; TrajRemakeSelections: PROC [slice: Slice, selectClass: SelectionClass] RETURNS [parts: SliceParts] = { <> RETURN[GGTraj.RemakeSelection[slice, selectClass]]; }; DrawConstrained: PROC [dc: Imager.Context, slice: Slice, transformParts: SliceParts, segNum: NAT, seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector, editConstraints: EditConstraints, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc] RETURNS [cPointsInSeg: SegCPSequence] = { IF entire THEN seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo] ELSE { selSeq: TrajParts _ NARROW[transformParts]; dirVec: Vector; newJoint: Point; distance: REAL; partialTransform, ctrlLoTrans, ctrlHiTrans: Transformation; ctrlPoint: Point; ctrlPoints: BitVector _ NEW[GGBasicTypes.BitVectorObj[2]]; ctrlPoints[0] _ controlPoints[0]; ctrlPoints[1] _ controlPoints[1]; ctrlLoTrans _ IF ctrlPoints[0] THEN transform ELSE idTransform; ctrlHiTrans _ IF ctrlPoints[1] THEN transform ELSE idTransform; IF lo AND NOT ctrlPoints[0] THEN { -- Low side of segment ctrlPoint _ seg.class.controlPointGet[seg, 0]; ctrlPoints[0] _ TRUE; newJoint _ ImagerTransformation.Transform[transform, seg.lo]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, newJoint]]; dirVec _ Vectors2d.VectorFromPoints[seg.lo, ctrlPoint]; IF editConstraints = tangent THEN { distance _ Vectors2d.Distance[newJoint, ctrlPoint]; IF dirVec # [0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[dirVec,distance]]; } ELSE ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, dirVec]; }; IF hi AND NOT ctrlPoints[1] THEN { -- High side of Segment ctrlPoint _ seg.class.controlPointGet[seg, 1]; ctrlPoints[1] _ TRUE; newJoint _ ImagerTransformation.Transform[transform, seg.hi]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, newJoint]]; dirVec _ Vectors2d.VectorFromPoints[seg.hi, ctrlPoint]; IF editConstraints = tangent THEN { distance _ Vectors2d.Distance[newJoint, ctrlPoint]; IF dirVec # [0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[dirVec,distance]]; } ELSE ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, dirVec]; }; <> IF NOT ctrlPoints[0] AND NOT lo THEN { prevSegNum: INT _ GGTraj.PreviousSegmentNum[slice, segNum]; IF prevSegNum # -1 AND GGTraj.FetchSegment[slice, prevSegNum].class.type = $Bezier THEN { <> IF selSeq.controlPoints[prevSegNum][1] THEN { angle: REAL; transformedPoint: Point; otherCtrlPoint: Point _ seg.class.controlPointGet[GGTraj.FetchSegment[slice, prevSegNum], 1]; otherDirVector: Vector _ Vectors2d.VectorFromPoints[seg.lo, otherCtrlPoint]; ctrlPoint _ seg.class.controlPointGet[seg, 0]; dirVec _ Vectors2d.VectorFromPoints[seg.lo, ctrlPoint]; distance _ Vectors2d.Magnitude[dirVec]; transformedPoint _ ImagerTransformation.Transform[transform, otherCtrlPoint]; angle _ Vectors2d.SmallestAngleBetweenVectors[otherDirVector, Vectors2d.VectorFromPoints[seg.lo, transformedPoint]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.lo]]; ctrlPoints[0] _ TRUE; IF editConstraints = tangent THEN { IF transformedPoint # seg.lo AND otherDirVector # [0,0] THEN { --Don't flip based on other cps 0 length vec. ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; -- Else just use identity. } ELSE { -- length IF otherDirVector # [0,0] THEN { distance _ RealFns.SqRt[Vectors2d.DistanceSquared[seg.lo, transformedPoint] / Vectors2d.MagnitudeSquared[otherDirVector]] * distance; ctrlLoTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; -- Else just use identity. }; }; }; }; IF NOT ctrlPoints[1] AND NOT hi THEN { nextSegNum: INT _ GGTraj.FollowingSegmentNum[slice, segNum]; IF nextSegNum # -1 AND GGTraj.FetchSegment[slice, nextSegNum].class.type = $Bezier THEN { IF selSeq.controlPoints[nextSegNum][0] THEN { angle: REAL; transformedPoint: Point; otherCtrlPoint: Point _ seg.class.controlPointGet[GGTraj.FetchSegment[slice, nextSegNum], 0]; otherDirVector: Vector _ Vectors2d.VectorFromPoints[seg.hi, otherCtrlPoint]; ctrlPoint _ seg.class.controlPointGet[seg, 1]; dirVec _ Vectors2d.VectorFromPoints[seg.hi, ctrlPoint]; distance _ Vectors2d.Magnitude[dirVec]; transformedPoint _ ImagerTransformation.Transform[transform, otherCtrlPoint]; angle _ Vectors2d.SmallestAngleBetweenVectors[otherDirVector, Vectors2d.VectorFromPoints[seg.hi, transformedPoint]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.hi]]; ctrlPoints[1] _ TRUE; IF editConstraints = tangent THEN { IF transformedPoint # seg.hi AND otherDirVector # [0,0] THEN { --Don't flip based on other cps 0 length vec. ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; -- Else just use identity. } ELSE { -- length IF otherDirVector # [0,0] THEN { distance _ RealFns.SqRt[Vectors2d.DistanceSquared[seg.hi, transformedPoint] / Vectors2d.MagnitudeSquared[otherDirVector]] * distance; ctrlHiTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; }; --Else just use identity. }; }; }; }; GGSegment.BZSpecialTransformPath[seg, transform, lo, hi, ctrlPoints, ctrlLoTrans, ctrlHiTrans, curveTo]; IF NOT (ctrlPoints[0] OR ctrlPoints[1]) THEN RETURN[NIL]; cPointsInSeg _ NEW[SegCPSequenceObj[2]]; -- For Beziers IF ctrlPoints[0] THEN { cPointsInSeg[0].cPoint _ ImagerTransformation.Transform[ctrlLoTrans, seg.class.controlPointGet[seg, 0]]; cPointsInSeg[0].show _ TRUE; } ELSE cPointsInSeg[0].show _ FALSE; IF ctrlPoints[1] THEN { cPointsInSeg[1].cPoint _ ImagerTransformation.Transform[ctrlHiTrans, seg.class.controlPointGet[seg, 1]]; cPointsInSeg[1].show _ TRUE; } ELSE cPointsInSeg[1].show _ FALSE; RETURN[cPointsInSeg]; }; }; SegMaskStroke: PROC [dc: Imager.Context, seg: Segment] = { MaskPath: Imager.PathProc = { <<[moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]>> moveTo[seg.lo]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; pattern: SequenceOfReal _ seg.pattern; IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length] ELSE Imager.MaskStroke[dc, MaskPath, FALSE]; }; MaskStroke: PROC [dc: Imager.Context, seg: Segment] = { MaskPath: Imager.PathProc = { moveTo[seg.lo]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; pattern: SequenceOfReal _ seg.pattern; IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length] ELSE Imager.MaskStroke[dc, MaskPath, FALSE]; }; MaskStrokeTransform: PROC [dc: Imager.Context, seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector, slice: Slice, transformParts: TrajParts, segNum: NAT, camera: Camera, editConstraints: EditConstraints] = { <> MaskPath: Imager.PathProc = { loPoint: Point; cpCount: NAT _ seg.class.controlPointCount[seg]; loPoint _ IF entire OR lo THEN ImagerTransformation.Transform[transform, seg.lo] ELSE seg.lo; moveTo[loPoint]; IF seg.class.type=$Bezier AND editConstraints#none THEN [] _ DrawConstrained[dc, slice, transformParts, segNum, seg, transform, entire, lo, hi, controlPoints, editConstraints, lineTo, curveTo, conicTo, arcTo] ELSE seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo]; IF transform#NIL AND NOT entire THEN { -- might be rubber-banding FOR j: NAT IN [0..cpCount) DO IF controlPoints=NIL OR controlPoints[j] THEN { -- it is rubber-banding cPoint: Point _ ImagerTransformation.Transform[transform, seg.class.controlPointGet[seg, j]]; GGShapes.DrawCP[dc, cPoint, camera.cpScale]; } ENDLOOP; }; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; pattern: SequenceOfReal _ seg.pattern; IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length] ELSE Imager.MaskStroke[dc, MaskPath, FALSE]; }; <<>> AllStrokePropsAndColorsEqual: PROC [slice: Slice] RETURNS [BOOL _ FALSE] = { seg: Segment; firstSeg: Segment _ GGTraj.FetchSegment[slice, 0]; width: REAL _ firstSeg.strokeWidth; end: StrokeEnd _ firstSeg.strokeEnd; color: Color _ firstSeg.color; dashed: BOOL _ firstSeg.dashed; pattern: SequenceOfReal _ firstSeg.pattern; offset: REAL _ firstSeg.offset; length: REAL _ firstSeg.length; hiSegment: NAT _ GGTraj.HiSegment[slice]; FOR i: INT IN [1..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; IF seg.dashed # dashed THEN RETURN[FALSE]; IF seg.strokeEnd # end THEN RETURN[FALSE]; IF seg.strokeWidth # width THEN RETURN[FALSE]; IF NOT GGCoreOps.EquivalentColors[color, seg.color] THEN RETURN[FALSE]; IF NOT dashed THEN LOOP; IF seg.offset # offset THEN RETURN[FALSE]; IF seg.length # length THEN RETURN[FALSE]; IF NOT GGUtility.EquivalentPatterns[seg.pattern, pattern] THEN RETURN[FALSE]; REPEAT FINISHED => RETURN[TRUE]; ENDLOOP; }; SegmentSelected: PROC [segNum: NAT, selectedSeq: TrajParts] RETURNS [BOOL _ FALSE] = { RETURN[selectedSeq.segments[segNum]]; }; <<>> <> TrajTransform: PROC [slice: Slice, parts: SliceParts _ NIL, transform: ImagerTransformation.Transformation, editConstraints: EditConstraints, history: HistoryEvent] = { <> joint: Joint; seg: Segment; trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ NARROW[parts]; hiSegment: NAT _ GGTraj.HiSegment[slice]; hiJoint: NAT _ GGTraj.HiJoint[slice]; SELECT trajData.role FROM open => TransformSequenceOpen[slice, parts, transform, editConstraints]; fence, hole => TransformSequenceClosed[slice, parts, transform, editConstraints]; ENDCASE => ERROR; FOR i: NAT IN [0..hiJoint] DO IF trajParts=NIL OR trajParts.joints[i] THEN { joint _ NARROW[Rosary.Fetch[trajData.joints, i]]; joint.point _ GGTransform.Transform[transform, joint.point]; }; ENDLOOP; TrajShapeChangeNotify[slice]; IF trajParts = NIL THEN RETURN; <> FOR i: NAT IN [0..hiSegment] DO cpCount: NAT _ trajParts.controlPoints[i].len; seg _ NARROW[Rosary.Fetch[trajData.segments, i]]; FOR j: NAT IN [0..cpCount) DO IF trajParts.controlPoints[i][j] AND NOT trajParts.segments[i] THEN { seg.class.controlPointMoved[seg, transform, j]; }; ENDLOOP; ENDLOOP; }; TransformSequenceOpen: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation, editConstraints: EditConstraints] = { trajParts: TrajParts _ NARROW[parts]; seg: Segment; hiSegment: NAT _ GGTraj.HiSegment[slice]; FOR i: NAT IN [0..hiSegment] DO seg _ GGTraj.FetchSegment[slice, i]; IF trajParts=NIL OR trajParts.segments[i] THEN { GGSegment.TransformSegment[seg, transform]; } ELSE { IF seg.class.type = $Bezier AND editConstraints # none THEN { TransformConstrained[slice, parts, i, seg, transform, editConstraints]; } ELSE { IF trajParts.joints[i] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; IF trajParts.joints[i+1] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; }; }; ENDLOOP; }; TransformSequenceClosed: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation, editConstraints: EditConstraints] = { << A tricky operation. All selected segments are transformed. Then, all segments which are not selected but which touch a selected joint are told that the joint has moved.>> trajParts: TrajParts _ NARROW[parts]; seg: Segment; hiSegment: NAT _ GGTraj.HiSegment[slice]; FOR i: NAT IN [0..hiSegment) DO seg _ GGTraj.FetchSegment[slice, i]; IF trajParts=NIL OR trajParts.segments[i] THEN { GGSegment.TransformSegment[seg, transform]; } ELSE { IF seg.class.type = $Bezier AND editConstraints # none THEN { TransformConstrained[slice, parts, i, seg, transform, editConstraints]; -- Same as open since not End. } ELSE { IF trajParts.joints[i] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; IF trajParts.joints[i+1] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; }; }; ENDLOOP; seg _ GGTraj.FetchSegment[slice, hiSegment]; IF trajParts=NIL OR trajParts.segments[hiSegment] THEN { GGSegment.TransformSegment[seg, transform]; } ELSE { IF seg.class.type = $Bezier AND editConstraints # none THEN { TransformConstrained[slice, parts, hiSegment, seg, transform, editConstraints]; -- Same as open } ELSE { IF trajParts.joints[hiSegment] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; IF trajParts.joints[0] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; }; }; }; TransformConstrained: PROC [slice: Slice, parts: SliceParts, segNum: NAT, seg: Segment, transform:ImagerTransformation.Transformation, editConstraints: GGModelTypes.EditConstraints] = { nextJointNum: NAT; dirVec: Vector; distance: REAL; ctrlPoint: Point; ctrlTrans, partialTransform: ImagerTransformation.Transformation; selSeq: TrajParts _ NARROW[parts]; IF selSeq.joints[segNum] AND NOT selSeq.controlPoints[segNum][0] THEN { -- Low side of segment ctrlPoint _ seg.class.controlPointGet[seg, 0]; dirVec _ Vectors2d.VectorFromPoints[seg.lo, ctrlPoint]; GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.lo]]; IF editConstraints = tangent THEN { -- ??Should this really behave differently? distance _ Vectors2d.Distance[seg.lo, ctrlPoint]; IF dirVec # [0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[dirVec,distance]]; } ELSE ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, dirVec]; seg.class.controlPointMoved[seg, ctrlTrans, 0]; } ELSE { IF selSeq.controlPoints[segNum][0] THEN { <> IF selSeq.joints[segNum] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; } ELSE { --CP not selected (and not joint selected) so see if constrained movement. prevSegNum: INT _ GGTraj.PreviousSegmentNum[slice, segNum]; IF prevSegNum # -1 AND GGTraj.FetchSegment[slice, prevSegNum].class.type = $Bezier THEN { <> IF selSeq.controlPoints[prevSegNum][1] THEN { angle: REAL; transformedPoint: Point; otherCtrlPoint: Point _ seg.class.controlPointGet[GGTraj.FetchSegment[slice, prevSegNum], 1]; otherDirVector: Vector _ Vectors2d.VectorFromPoints[seg.lo, otherCtrlPoint]; ctrlPoint _ seg.class.controlPointGet[seg, 0]; dirVec _ Vectors2d.VectorFromPoints[seg.lo, ctrlPoint]; distance _ Vectors2d.Magnitude[dirVec]; transformedPoint _ ImagerTransformation.Transform[transform, otherCtrlPoint]; angle _ Vectors2d.SmallestAngleBetweenVectors[otherDirVector, Vectors2d.VectorFromPoints[seg.lo, transformedPoint]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.lo]]; IF editConstraints = tangent THEN { IF transformedPoint # seg.lo AND otherDirVector # [0,0] THEN { -- Don't flip based on 0 length vec. ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; } ELSE ctrlTrans _ ImagerTransformation.Scale[1]; -- Identity. } ELSE { -- length IF otherDirVector # [0,0] THEN { distance _ RealFns.SqRt[Vectors2d.DistanceSquared[seg.lo, transformedPoint] / Vectors2d.MagnitudeSquared[otherDirVector]] * distance; ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; } ELSE ctrlTrans _ ImagerTransformation.Scale[1]; -- Identity. }; seg.class.controlPointMoved[seg, ctrlTrans, 0]; }; }; }; }; <> nextJointNum _ GGTraj.FollowingJoint[slice, segNum]; IF selSeq.joints[nextJointNum] AND NOT selSeq.controlPoints[segNum][1] THEN { -- High side of Segment ctrlPoint _ seg.class.controlPointGet[seg, 1]; dirVec _ Vectors2d.VectorFromPoints[seg.hi, ctrlPoint]; GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.hi]]; IF editConstraints = tangent THEN { distance _ Vectors2d.Distance[seg.hi, ctrlPoint]; IF dirVec # [0,0] THEN dirVec _ Vectors2d.Normalize[dirVec]; -- use exception ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[dirVec,distance]]; } ELSE ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, dirVec]; seg.class.controlPointMoved[seg, ctrlTrans, 1]; -- For Beziers } ELSE { IF selSeq.controlPoints[segNum][1] THEN { <> IF selSeq.joints[nextJointNum] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; } ELSE { --CP not selected (and not joint selected) so see if constrained movement. nextSegNum: INT _ GGTraj.FollowingSegmentNum[slice, segNum]; IF nextSegNum # -1 AND GGTraj.FetchSegment[slice, nextSegNum].class.type = $Bezier THEN { IF selSeq.controlPoints[nextSegNum][0] THEN { angle: REAL; transformedPoint: Point; otherCtrlPoint: Point _ seg.class.controlPointGet[GGTraj.FetchSegment[slice, nextSegNum], 0]; otherDirVector: Vector _ Vectors2d.VectorFromPoints[seg.hi, otherCtrlPoint]; ctrlPoint _ seg.class.controlPointGet[seg, 1]; dirVec _ Vectors2d.VectorFromPoints[seg.hi, ctrlPoint]; distance _ Vectors2d.Magnitude[dirVec]; transformedPoint _ ImagerTransformation.Transform[transform, otherCtrlPoint]; angle _ Vectors2d.SmallestAngleBetweenVectors[otherDirVector, Vectors2d.VectorFromPoints[seg.hi, transformedPoint]]; partialTransform _ ImagerTransformation.Translate[Vectors2d.VectorFromPoints[ctrlPoint, seg.hi]]; IF editConstraints = tangent THEN { IF transformedPoint # seg.hi AND otherDirVector # [0,0] THEN { -- Don't flip based on 0 length vec. ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; } ELSE ctrlTrans _ ImagerTransformation.Scale[1]; -- Identity } ELSE { -- length IF otherDirVector # [0,0] THEN { distance _ RealFns.SqRt[Vectors2d.DistanceSquared[seg.hi, transformedPoint] / Vectors2d.MagnitudeSquared[otherDirVector]] * distance; ctrlTrans _ ImagerTransformation.PostTranslate[partialTransform, Vectors2d.Scale[Vectors2d.VectorPlusAngle[dirVec, angle], distance]]; } ELSE ctrlTrans _ ImagerTransformation.Scale[1]; -- Identity }; seg.class.controlPointMoved[seg, ctrlTrans, 1]; }; }; }; }; }; <<>> <> <<>> TrajIsEmptyParts: PROC [sliceD: SliceDescriptor] RETURNS [BOOL _ TRUE] = { trajParts: TrajParts _ NARROW[sliceD.parts]; RETURN[trajParts=NIL OR GGSequence.IsEmpty[trajParts]]; }; TrajIsCompleteParts: PROC [sliceD: SliceDescriptor] RETURNS [BOOL _ FALSE] = { trajParts: TrajParts _ NARROW[sliceD.parts]; RETURN[trajParts#NIL AND GGSequence.IsComplete[trajParts]]; }; TrajNewParts: PROC [slice: Slice, hitData: REF ANY, mode: SelectMode] RETURNS [sliceD: SliceDescriptor] = { <> trajParts: TrajParts; trajData: TrajData _ NARROW[slice.data]; trajHitData: TrajHitData _ NARROW[hitData]; SELECT mode FROM literal => { hitType: HitType; segNum, cpNum, jointNum: INT; [hitType, segNum, cpNum, jointNum, ----] _ GGTraj.UnpackHitData[hitData]; trajParts _ SELECT hitType FROM joint => GGSequence.CreateFromJoint[trajData, jointNum], controlPoint => GGSequence.CreateFromControlPoint[trajData, segNum, cpNum], segment => GGSequence.CreateSimpleFromSegment[trajData, segNum], ENDCASE => ERROR; }; none => { trajParts _ GGSequence.CreateEmpty[trajData]; }; slice => { trajParts _ GGSequence.CreateComplete[trajData]; }; joint => { SELECT trajHitData.hitType FROM joint => { -- hit a joint jointNum: NAT _ trajHitData.jointNum; trajParts _ GGSequence.CreateJointToJoint[trajData, jointNum, jointNum]; }; segment => { -- we are in the middle of a segment. Select nearest joint or cp success: BOOL _ FALSE; segNum, cpNum, jointNum: NAT; jointPoint, cpPoint, caretPt: Point; seg: Segment; segNum _ trajHitData.segNum; seg _ GGTraj.FetchSegment[slice, segNum]; caretPt _ trajHitData.hitPoint; jointNum _ NearestJointToHitSpot[caretPt, slice, -1, segNum, segment]; jointPoint _ GGTraj.FetchJointPos[slice, jointNum]; [cpPoint, ----, cpNum, success] _ seg.class.closestControlPoint[seg, caretPt, GGUtility.plusInfinity]; IF NOT success THEN trajParts _ GGSequence.CreateJointToJoint[trajData, jointNum, jointNum] ELSE { cpDist: REAL _ Vectors2d.DistanceSquared[cpPoint, caretPt]; jointDist: REAL _ Vectors2d.DistanceSquared[jointPoint, caretPt]; trajParts _ IF cpDist < jointDist THEN GGSequence.CreateFromControlPoint[trajData, segNum, cpNum] ELSE GGSequence.CreateJointToJoint[trajData, jointNum, jointNum]; }; }; controlPoint => { segNum, cpNum: NAT; segNum _ trajHitData.segNum; cpNum _ trajHitData.cpNum; trajParts _ GGSequence.CreateFromControlPoint[trajData, segNum, cpNum]; }; ENDCASE => ERROR; }; segment => { SELECT trajHitData.hitType FROM joint => { jointNum: NAT _ trajHitData.jointNum; this: INT _ IF jointNum>GGTraj.HiSegment[slice] THEN -1 ELSE jointNum; previous: INT _ GGTraj.PreviousSegmentNum[slice, jointNum]; trajParts _ GGSequence.CreateFromSegment[trajData, IF this#-1 THEN this ELSE previous]; }; segment, controlPoint => { trajParts _ GGSequence.CreateFromSegment[trajData, trajHitData.segNum]; }; ENDCASE => ERROR; }; traj, topLevel => { trajParts _ GGSequence.CreateComplete[trajData]; }; ENDCASE => ERROR; sliceD _ GGSlice.DescriptorFromParts[slice, trajParts]; }; NearestJointToHitSpot: PROC [caretPt: Point, traj: Traj, hitJointNum: INT, hitSegNum: INT, hitType: HitType] RETURNS [jointNum: NAT] = { nextNum: NAT; p1, p2: Point; d1, d2: REAL; SELECT hitType FROM joint => { jointNum _ hitJointNum; }; segment, controlPoint => { nextNum _ GGTraj.FollowingJoint[traj, hitSegNum]; p1 _ GGTraj.FetchJointPos[traj, hitSegNum]; p2 _ GGTraj.FetchJointPos[traj, nextNum]; d1 _ Vectors2d.DistanceSquared[p1, caretPt]; d2 _ Vectors2d.DistanceSquared[p2, caretPt]; IF d1 <= d2 THEN jointNum _ hitSegNum ELSE jointNum _ nextNum; }; ENDCASE => ERROR Problem[msg: "NearestJointToHitSpot NYI"]; }; -- end of NearestJointToHitSpot TrajUnionParts: PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aPlusB: SliceDescriptor] = { <> aPlusB _ GGSequence.Union[partsA, partsB]; }; TrajDiffParts: PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aMinusB: SliceDescriptor] = { <> aMinusB _ GGSequence.Difference[partsA, partsB]; }; TrajMovingParts: PROC [slice: Slice, selectedParts: SliceParts, editConstraints: EditConstraints, bezierDrag: GGModelTypes.BezierDragRecord] RETURNS [background, overlay, rubber, drag: SliceDescriptor] = { <> bkgdParts, overParts, rubberParts, dragParts: TrajParts; [bkgdParts, overParts, rubberParts, dragParts] _ GGSequence.TrajMovingParts[slice, selectedParts, editConstraints, bezierDrag]; background _ GGSlice.DescriptorFromParts[slice, bkgdParts]; overlay _ GGSlice.DescriptorFromParts[slice, overParts]; rubber _ GGSlice.DescriptorFromParts[slice, rubberParts]; drag _ GGSlice.DescriptorFromParts[slice, dragParts]; }; TrajAugmentParts: PROC [sliceD: SliceDescriptor, selectClass: SelectionClass] RETURNS [more: SliceDescriptor] = { <> <> <> trajParts: TrajParts _ NARROW[sliceD.parts]; IF selectClass = normal THEN GGSequence.FillInJoints[trajParts]; GGSequence.FillInControlPoints[trajParts]; more _ GGSlice.DescriptorFromParts[sliceD.slice, trajParts]; }; TrajAlterParts: PROC [sliceD: SliceDescriptor, action: ATOM] RETURNS [alteredD: SliceDescriptor] = { <> slice: Slice _ sliceD.slice; trajData: TrajData _ NARROW[slice.data]; trajParts: TrajParts _ NARROW[sliceD.parts]; isACP: BOOL _ FALSE; <> jointNum, cpNum, segNum: INT _ 999; SELECT action FROM $Forward, $Backward => { IF TrajIsCompleteParts[sliceD] THEN RETURN[NIL]; <<[success, partType, trajData, ----, jointNum, ----, cpNum, ----, segNum] _ GGTraj.UnpackSimpleDescriptor[sliceD.slice, sliceD.parts];>> <> <> IF GGSequence.CountSegmentsInSequence[trajData, trajParts]=0 THEN [isACP, segNum, cpNum, jointNum] _ GGSequence.UnpackOnePointSequence[trajParts] ELSE segNum _ GGSequence.UnpackOneSegmentSequence[trajParts]; SELECT TRUE FROM jointNum#999 => { IF action = $Forward THEN jointNum _ GGTraj.FollowingJoint[slice, jointNum] ELSE jointNum _ PreviousJoint[slice, jointNum]; alteredD _ IF jointNum = -1 THEN NIL ELSE GGSlice.DescriptorFromParts[slice, GGSequence.CreateFromJoint[trajData, jointNum]]; }; isACP => { alteredD _ sliceD; -- Don't walk for now }; segNum#999 => { IF action = $Forward THEN segNum _ GGTraj.FollowingSegmentNum[slice, segNum] ELSE segNum _ GGTraj.PreviousSegmentNum[slice, segNum]; alteredD _ IF segNum = -1 THEN NIL ELSE GGSlice.DescriptorFromParts[slice, GGSequence.CreateFromSegment[trajData, segNum]]; }; ENDCASE => ERROR; }; $Grow => RETURN[TrajNewParts[sliceD.slice, NIL, slice]]; $ShrinkForward, $ShrinkBackward => RETURN[NIL]; ENDCASE => ERROR; }; PreviousJoint: PROC [slice: Slice, index: INT] RETURNS [nextIndex: INT] = { trajData: TrajData _ NARROW[slice.data]; SELECT trajData.role FROM open => nextIndex _ index - 1; fence, hole => nextIndex _ (index + trajData.segCount -1) MOD trajData.segCount; ENDCASE => ERROR; }; TrajSetSelectedFields: PROC [sliceD: SliceDescriptor, selected: BOOL, selectClass: SelectionClass] = { joint: Joint; jointGen: JointGenerator; segGen: SegmentGenerator; trajParts: TrajParts _ NARROW[sliceD.parts]; trajData: TrajData _ NARROW[sliceD.slice.data]; SELECT selectClass FROM normal => trajData.selectedInPart.normal _ selected; hot => trajData.selectedInPart.hot _ selected; active => trajData.selectedInPart.active _ selected; match => trajData.selectedInPart.match _ selected; ENDCASE; jointGen _ GGSequence.JointsInSequence[trajParts]; <> FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO joint _ GGTraj.FetchJoint[sliceD.slice, jointNum]; SELECT selectClass FROM normal => joint.TselectedInFull.normal _ selected; hot => joint.TselectedInFull.hot _ selected; active => joint.TselectedInFull.active _ selected; match => joint.TselectedInFull.match _ selected; ENDCASE; ENDLOOP; <> segGen _ GGSequence.SegmentsInSequence[trajData, trajParts]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SELECT selectClass FROM normal => seg.TselectedInFull.normal _ selected; hot => seg.TselectedInFull.hot _ selected; active => seg.TselectedInFull.active _ selected; match => seg.TselectedInFull.match _ selected; ENDCASE; ENDLOOP; }; idTransform: Transformation; cache: FunctionCache.Cache; <> <> <> <> <> <> <<};>> <<>> Init: PROC = { idTransform _ ImagerTransformation.Create[1,0,0,0,1,0]; -- identity; cache _ FunctionCache.Create[maxEntries: 100]; }; <> Init[]; END.