<> <> <> <> <> <> DIRECTORY CardTab, CodeTimer, Draw2d, Feedback, GGAlign, GGBasicTypes, GGCaret, GGCircleCache, GGCircles, GGControlPanelTypes, GGCoreTypes, GGDragTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGUtility, Imager, ImagerBackdoor, Lines2d, Process, Real, RealFns, RefTab, Rope, Vectors2d; GGAlignImpl: CEDAR PROGRAM IMPORTS CardTab, CodeTimer, Draw2d, Feedback, GGCaret, GGCircleCache, GGCircles, GGParent, GGRefresh, GGSliceOps, GGScene, GGSelect, GGSequence, GGShapes, GGSlice, GGState, GGTraj, GGUtility, Imager, ImagerBackdoor, Lines2d, Process, Real, RealFns, RefTab, Vectors2d EXPORTS GGAlign, GGInterfaceTypes = BEGIN DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes AlignBag: TYPE = GGInterfaceTypes.AlignBag; AlignBagObj: TYPE = GGInterfaceTypes.AlignBagObj; AlignmentCircle: TYPE = GGInterfaceTypes.AlignmentCircle; AlignmentCircleObj: TYPE = GGInterfaceTypes.AlignmentCircleObj; AlignmentLine: TYPE = GGInterfaceTypes.AlignmentLine; AlignmentLineObj: TYPE = GGInterfaceTypes.AlignmentLineObj; AlignmentObject: TYPE = GGModelTypes.AlignmentObject; AlignmentPoint: TYPE = REF AlignmentPointObj; AlignmentPointObj: TYPE = GGInterfaceTypes.AlignmentPointObj; Angle: TYPE = GGBasicTypes.Angle; BezierDragRecord: TYPE = GGInterfaceTypes.BezierDragRecord; Caret: TYPE = GGInterfaceTypes.Caret; Circle: TYPE = GGBasicTypes.Circle; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; Edge: TYPE = GGBasicTypes.Edge; EditConstraints: TYPE = GGModelTypes.EditConstraints; FeatureData: TYPE = REF FeatureDataObj; FeatureDataObj: TYPE = GGModelTypes.FeatureDataObj; FeatureWalkProc: TYPE = GGAlign.FeatureWalkProc; Filters: TYPE = GGInterfaceTypes.Filters; FilterSliceProc: TYPE = GGAlign.FilterSliceProc; FilterType: TYPE = GGAlign.FilterType; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = GGCoreTypes.Line; OutlineData: TYPE = GGOutline.OutlineData; Point: TYPE = GGBasicTypes.Point; PointAndDone: TYPE = GGModelTypes.PointAndDone; PointPairAndDone: TYPE = GGModelTypes.PointPairAndDone; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; PointWalkProc: TYPE = GGModelTypes.PointWalkProc; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SequenceGenerator: TYPE = GGSequence.SequenceGenerator; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajData: TYPE = GGModelTypes.TrajData; TriggerBag: TYPE = REF TriggerBagObj; TriggerBagObj: TYPE = GGInterfaceTypes.TriggerBagObj; Vector: TYPE = GGBasicTypes.Vector; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; EntityNotFound: PUBLIC SIGNAL = CODE; UnexpectedType: PUBLIC ERROR = CODE; BrokenInvariant: PUBLIC ERROR = CODE; <> <> <> <> <> <> <> <> <> << [Artwork node; type 'ArtworkInterpress on' to command tool] >> <<>> <> emptyTriggerBag: PUBLIC TriggerBag; CreateTriggerBag: PUBLIC PROC [] RETURNS [triggerBag: TriggerBag] = { triggerBag _ NEW[TriggerBagObj _ [ slices: RefTab.Create[mod: 211], anchor: NIL]]; }; FlushTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] = { RefTab.Erase[triggerBag.slices]; triggerBag.anchor _ NIL; }; EmptyTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] RETURNS [BOOL] = { RETURN[ EmptyTriggerSlices[triggerBag] AND triggerBag.anchor = NIL ]; }; EmptyTriggerSlices: PROC [triggerBag: TriggerBag] RETURNS [empty: BOOL _ FALSE] = { SliceFeatureFound: RefTab.EachPairAction = { <> IF val = NIL THEN ERROR; quit _ TRUE; }; empty _ NOT RefTab.Pairs[triggerBag.slices, SliceFeatureFound]; }; CopyTriggerBag: PUBLIC PROC [to, from: TriggerBag] = { FlushTriggerBag[to]; CopyTriggerSlices[to.slices, from.slices]; to.anchor _ from.anchor; }; CopyTriggerSlices: PROC [toTable, fromTable: RefTab.Ref] = { CopySliceFeatures: RefTab.EachPairAction = { <> <> inserted: BOOL _ RefTab.Insert[toTable, key, val]; IF NOT inserted THEN ERROR; }; RefTab.Erase[toTable]; [] _ RefTab.Pairs[fromTable, CopySliceFeatures]; }; WalkSliceTriggers: PUBLIC PROC [triggerBag: TriggerBag, walkProc: FeatureWalkProc] = { DoForTrigger: RefTab.EachPairAction = { <> FOR list: LIST OF FeatureData _ NARROW[val], list.rest UNTIL list = NIL DO quit _ walkProc[list.first]; IF quit THEN EXIT; ENDLOOP; }; [] _ RefTab.Pairs[triggerBag.slices, DoForTrigger]; }; ListSliceTriggers: PROC [triggerBag: TriggerBag] RETURNS [features: LIST OF FeatureData] = { DoMakeList: PROC [feature: FeatureData] RETURNS [done: BOOL _ FALSE] = { [features, ptr] _ GGUtility.AddFeatureData[feature, features, ptr]; }; ptr: LIST OF FeatureData; WalkSliceTriggers[triggerBag, DoMakeList]; }; FindFeature: PROC [triggerBag: TriggerBag, slice: Slice] RETURNS [feature: FeatureData _ NIL] = { val: REF; found: BOOL _ FALSE; [found, val] _ RefTab.Fetch[triggerBag.slices, slice]; IF NOT found THEN RETURN[NIL]; FOR list: LIST OF FeatureData _ NARROW[val], list.rest UNTIL list = NIL DO IF NARROW[list.first.shape, SliceDescriptor].slice = slice THEN RETURN[list.first]; ENDLOOP; }; ReplaceFeature: PROC [triggerBag: TriggerBag, oldFeature, newFeature: FeatureData] = { <> val: REF; key: REF _ NARROW[oldFeature.shape, SliceDescriptor].slice; found: BOOL _ FALSE; [found, val] _ RefTab.Fetch[triggerBag.slices, key]; IF found AND val # NIL THEN { list: LIST OF FeatureData _ NARROW[val]; list _ RemoveFeatureFromList[oldFeature, list]; list _ CONS[newFeature, list]; [] _ RefTab.Store[triggerBag.slices, key, list]; } ELSE ERROR; }; DeleteFeature: PROC [triggerBag: TriggerBag, oldFeature: FeatureData] = { <> val: REF; key: REF _ NARROW[oldFeature.shape, SliceDescriptor].slice; found: BOOL _ FALSE; [found, val] _ RefTab.Fetch[triggerBag.slices, key]; IF found AND val # NIL THEN { list: LIST OF FeatureData _ NARROW[val]; list _ RemoveFeatureFromList[oldFeature, list]; IF list = NIL THEN [] _ RefTab.Delete[triggerBag.slices, key] ELSE [] _ RefTab.Store[triggerBag.slices, key, list]; } ELSE ERROR; }; AppendFeature: PUBLIC PROC [feature: FeatureData, sliceTriggers: RefTab.Ref] = { <> val: REF; slice: Slice _ NARROW[feature.shape, SliceDescriptor].slice; found: BOOL _ FALSE; Process.CheckForAbort[]; [found, val] _ RefTab.Fetch[sliceTriggers, slice]; IF found AND val # NIL THEN { list: LIST OF FeatureData _ NARROW[val]; <> list _ CONS[feature, list]; [] _ RefTab.Store[sliceTriggers, slice, list]; } ELSE { list: LIST OF FeatureData _ LIST[feature]; [] _ RefTab.Insert[sliceTriggers, slice, list]; }; }; FeatureFromSlice: PUBLIC PROC [slice: Slice, parts: SliceParts _ NIL] RETURNS [feature: FeatureData] = { sliceD: SliceDescriptor _ IF parts=NIL THEN GGSliceOps.NewParts[slice, NIL, slice] ELSE GGSlice.DescriptorFromParts[slice, parts]; feature _ NEW[FeatureDataObj]; feature.type _ slice; feature.shape _ sliceD; }; FeatureFromAnchor: PUBLIC PROC [anchor: Caret] RETURNS [feature: FeatureData] = { feature _ NEW[FeatureDataObj]; feature.type _ anchor; -- that is, anchor => the member of the enumerated type feature.shape _ anchor; -- that is, anchor => the parameter }; <<>> FillStaticTriggerBag: PUBLIC PROC [anchor: Caret, scene: Scene, heuristics: BOOL, triggerBag: TriggerBag] = { AddAnchorTrigger[anchor, triggerBag]; AddAllHotSlices[scene, triggerBag]; }; FillDynamicTriggerBag: PUBLIC PROC [anchor: Caret, scene: Scene, heuristics: BOOL, triggerBag: TriggerBag, editConstraints: EditConstraints, bezierDrag: BezierDragRecord] = { AddAnchorTrigger[anchor, triggerBag]; AddAllHotSlices[scene, triggerBag]; [] _ AddHeuristics[scene, heuristics, $Drag, triggerBag, editConstraints, bezierDrag]; [] _ RemoveMoving[scene, triggerBag, editConstraints, bezierDrag]; }; <<>> <> FillStaticSceneBag: PUBLIC PROC [scene: Scene, sceneBag: TriggerBag] = { AddAllSlices[scene, sceneBag]; }; FillDynamicSceneBag: PUBLIC PROC [scene: Scene, sceneBag: TriggerBag, editConstraints: GGModelTypes.EditConstraints, bezierDrag: GGInterfaceTypes.BezierDragRecord] = { AddAllSlices[scene, sceneBag]; [] _ RemoveMoving[scene, sceneBag, editConstraints, bezierDrag]; }; <<>> <> emptyAlignBag: PUBLIC AlignBag; CreateAlignBag: PUBLIC PROC [] RETURNS [alignBag: AlignBag] = { alignBag _ NEW[AlignBagObj _ [ slopeLineTable: CardTab.Create[mod: 211], angleLines: NIL, radiiCircles: NIL, distanceLines: NIL, anchor: NIL ]]; }; CopyAlignBag: PROC [to, from: AlignBag] = { CopySlopeLineTable[toTable: to.slopeLineTable, fromTable: from.slopeLineTable]; to.angleLines _ GGUtility.CopyFeatureDataList[from.angleLines]; to.radiiCircles _ GGUtility.CopyFeatureDataList[from.radiiCircles]; to.distanceLines _ GGUtility.CopyFeatureDataList[from.distanceLines]; to.anchor _ from.anchor; }; EmptyAlignBag: PUBLIC PROC [alignBag: AlignBag] RETURNS [BOOL] = { RETURN[ EmptySlopeLines[alignBag] AND alignBag.angleLines = NIL AND alignBag.radiiCircles = NIL AND alignBag.distanceLines = NIL AND alignBag.anchor = NIL]; }; CopySlopeLineTable: PROC [toTable, fromTable: CardTab.Ref] = { CopyFeatures: CardTab.EachPairAction = { <> inserted: BOOL _ CardTab.Insert[toTable, key, val]; IF NOT inserted THEN ERROR; }; CardTab.Erase[toTable]; [] _ CardTab.Pairs[fromTable, CopyFeatures]; }; EmptySlopeLines: PROC [alignBag: AlignBag] RETURNS [empty: BOOL _ FALSE] = { FeatureFound: CardTab.EachPairAction = { <> IF val = NIL THEN ERROR; quit _ TRUE; }; empty _ NOT CardTab.Pairs[alignBag.slopeLineTable, FeatureFound]; }; FlushAlignBag: PUBLIC PROC [alignBag: AlignBag] = { FlushSlopeLines[alignBag]; alignBag.angleLines _ NIL; alignBag.radiiCircles _ NIL; alignBag.distanceLines _ NIL; alignBag.anchor _ NIL; }; TriggersPerSlopeLine: PROC [ggData: GGData] RETURNS [lines, min, max: CARD, avg: REAL] = { <> Count: CardTab.EachPairAction = { <> list: LIST OF FeatureData _ NARROW[val]; aLine: AlignmentLine; triggersPerSlope: CARD _ 0; FOR l: LIST OF FeatureData _ list, l.rest UNTIL l = NIL DO lines _ lines + 1; aLine _ NARROW[l.first.shape]; triggersPerSlope _ 0; FOR pointList: LIST OF Point _ aLine.triggerPoints, pointList.rest UNTIL pointList = NIL DO triggersPerSlope _ triggersPerSlope + 1; ENDLOOP; min _ MIN[min, triggersPerSlope]; max _ MAX[max, triggersPerSlope]; totalTriggers _ totalTriggers + triggersPerSlope; ENDLOOP; -- next slope }; totalTriggers: REAL _ 0.0; lines _ 0; min _ LAST[CARD]; max _ 0; [] _ CardTab.Pairs[ggData.hitTest.alignBag.slopeLineTable, Count]; avg _ totalTriggers/lines; }; FlushSlopeLines: PROC [alignBag: AlignBag] = { CardTab.Erase[alignBag.slopeLineTable]; }; FillStaticAlignBag: PUBLIC PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, ggData: GGData, hideAlignments: BOOL, alignBag: AlignBag] = { BuiltInFilters[triggerBag, ggData, hideAlignments, alignBag]; }; FillDynamicAlignBag: PUBLIC PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, ggData: GGData, hideAlignments: BOOL, action: ATOM, alignBag: AlignBag] = { BuiltInFilters[triggerBag, ggData, hideAlignments, alignBag]; }; <> AddAlignment: PROC [featureData: FeatureData, alignBag: AlignBag] = { Process.CheckForAbort[]; SELECT featureData.type FROM distanceLine => alignBag.distanceLines _ CONS[featureData, alignBag.distanceLines]; slopeLine => AddSlopeLine[featureData, alignBag]; angleLine => alignBag.angleLines _ CONS[featureData, alignBag.angleLines]; radiiCircle => alignBag.radiiCircles _ CONS[featureData, alignBag.radiiCircles]; anchor => alignBag.anchor _ featureData; ENDCASE => SIGNAL Problem[msg: "Unimplemented feature type"]; }; AddSlopeLine: PROC [featureData: FeatureData, alignBag: AlignBag] = { line: Line _ NARROW[featureData.shape, AlignmentLine].line; distance: REAL _ line.d; key: CARD _ KeyFromDistance[distance]; val: REF; found: BOOL _ FALSE; [found, val] _ CardTab.Fetch[alignBag.slopeLineTable, key]; IF NOT found THEN { list: LIST OF FeatureData _ LIST[featureData]; [] _ CardTab.Insert[alignBag.slopeLineTable, key, list]; } ELSE { list: LIST OF FeatureData _ NARROW[val]; list _ CONS[featureData, list]; [] _ CardTab.Store[alignBag.slopeLineTable, key, list]; }; }; JointAddSlopeLine: PUBLIC PROC [degrees: REAL, point: Point, alignBag: AlignBag] RETURNS [feature: FeatureData] = { <> line: Line; coincident: FeatureData; cosine, sine, distance: REAL; [coincident, cosine, sine, distance] _ LineThru[point, degrees, alignBag.slopeLineTable]; IF coincident # NIL THEN { alignmentLine: AlignmentLine _ NARROW[coincident.shape]; alignmentLine.triggerPoints _ CONS[point, alignmentLine.triggerPoints]; feature _ NIL; } ELSE { alignmentLine: AlignmentLine; line _ Lines2d.LineFromCoefficients[sine, cosine, distance]; alignmentLine _ NEW[AlignmentLineObj _ [line: line, degrees: degrees, triggerPoints: CONS[point, NIL]]]; feature _ NEW[FeatureDataObj]; feature.type _ slopeLine; feature.shape _ alignmentLine; AddAlignment[feature, alignBag]; }; }; WalkSlopeLines: PUBLIC PROC [alignBag: AlignBag, walkProc: FeatureWalkProc] = { WalkAction: CardTab.EachPairAction = { <> FOR list: LIST OF FeatureData _ NARROW[val], list.rest UNTIL list = NIL DO quit _ walkProc[list.first]; IF quit THEN EXIT; ENDLOOP; }; [] _ CardTab.Pairs[alignBag.slopeLineTable, WalkAction]; }; ListOfSlopeLines: PROC [alignBag: AlignBag] RETURNS [slopeLines: LIST OF FeatureData _ NIL] = { ConsSlopeLine: CardTab.EachPairAction = { <> FOR list: LIST OF FeatureData _ NARROW[val], list.rest UNTIL list = NIL DO slopeLines _ CONS[list.first, slopeLines]; ENDLOOP; }; [] _ CardTab.Pairs[alignBag.slopeLineTable, ConsSlopeLine]; }; epsilon: REAL = 1.0e-3; epsilonInverse: REAL = 1.0e+3; KeysFromDistance: PROC [distance: REAL] RETURNS [keys: ARRAY[0..2] OF CARD] = { int: INT; distance _ distance * epsilonInverse; int _ Real.Floor[distance]; keys[0] _ LOOPHOLE[int]; keys[1] _ LOOPHOLE[int+1]; keys[2] _ LOOPHOLE[int-1]; }; KeyFromDistance: PROC [distance: REAL] RETURNS [key: CARD] = { <> < 1.001.>> <> int: INT; distance _ distance * epsilonInverse; -- giving 1001.2 in our example int _ Real.Floor[distance]; key _ LOOPHOLE[int]; }; LineThru: PROC [point: Point, degrees: REAL, table: CardTab.Ref] RETURNS [coincident: FeatureData, cosine, sine, distance: REAL] = { line: Line; keys: ARRAY[0..2] OF CARD; val: REF; found: BOOL _ FALSE; [distance, cosine, sine] _ DistanceFromOrigin[point, degrees]; keys _ KeysFromDistance[distance]; FOR i: NAT IN [0..2] DO [found, val] _ CardTab.Fetch[table, keys[i]]; IF NOT found THEN LOOP; FOR l: LIST OF FeatureData _ NARROW[val], l.rest UNTIL l = NIL DO line _ NARROW[l.first.shape, AlignmentLine].line; IF NARROW[l.first.shape, AlignmentLine].degrees = degrees AND Lines2d.LineDistance[point, line] < epsilon THEN {coincident _ l.first; RETURN} ENDLOOP; ENDLOOP; coincident _ NIL; }; JointAddCircle: PUBLIC PROC [radius: REAL, point: Point, alignBag: AlignBag] RETURNS [feature: FeatureData] = { <> <> circle: Circle; coincident: FeatureData; coincident _ SameCircle[point, radius, alignBag.radiiCircles]; IF coincident # NIL THEN { alignmentCircle: AlignmentCircle _ NARROW[coincident.shape]; alignmentCircle.triggerPoints _ CONS[point, alignmentCircle.triggerPoints]; feature _ NIL; } ELSE { alignmentCircle: AlignmentCircle; circle _ GGCircles.CircleFromPointAndRadius[point, radius]; alignmentCircle _ NEW[AlignmentCircleObj _ [circle: circle, triggerPoints: CONS[point, NIL]]]; feature _ NEW[FeatureDataObj]; feature.type _ radiiCircle; feature.shape _ alignmentCircle; AddAlignment[feature, alignBag]; }; }; SameCircle: PROC [point: Point, radius: REAL, list: LIST OF FeatureData] RETURNS [coincident: FeatureData] = { circle: Circle; FOR l: LIST OF FeatureData _ list, l.rest UNTIL l = NIL DO circle _ NARROW[l.first.shape, AlignmentCircle].circle; IF RealFns.AlmostEqual[circle.origin.x, point.x, -10] AND RealFns.AlmostEqual[circle.origin.y, point.y, -10] AND RealFns.AlmostEqual[circle.radius, radius, -10] THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; DistanceFromOrigin: PROC [pt1: Point, degrees: REAL] RETURNS [distance, c, s: REAL] = { <> c _ RealFns.CosDeg[degrees]; s _ RealFns.SinDeg[degrees]; distance _ pt1.y*c - pt1.x*s; }; SegmentAddTwoAngleLines: PUBLIC PROC [degrees: REAL, segNum: NAT, lo, hi: Point, alignBag: AlignBag] RETURNS [line1, line2: FeatureData] = { loLine: Line _ Lines2d.LineFromPointAndAngle[pt: lo, degrees: Vectors2d.AngleFromVector[[x: hi.x-lo.x, y: hi.y-lo.y]]+degrees]; hiLine: Line _ Lines2d.LineFromPointAndAngle[pt: hi, degrees: Vectors2d.AngleFromVector[[x: lo.x-hi.x, y: lo.y-hi.y]]+degrees]; line1 _ NEW[FeatureDataObj]; line1.type _ angleLine; line1.shape _ NEW[AlignmentLineObj _ [line: loLine, degrees: degrees, triggerPoints: NIL]]; AddAlignment[line1, alignBag]; line2 _ NEW[FeatureDataObj]; line2.type _ angleLine; line2.shape _ NEW[AlignmentLineObj _ [line: hiLine, degrees: degrees, triggerPoints: NIL]]; AddAlignment[line2, alignBag]; }; SegmentAddDistanceLines: PUBLIC PROC [distance: REAL, segNum: NAT, lo, hi: Point, alignBag: AlignBag] RETURNS [line1, line2: FeatureData] = { <> line, leftLine, rightLine: Line; line _ Lines2d.LineFromPoints[lo, hi]; leftLine _ Lines2d.LineLeftOfLine[line, distance]; line1 _ NEW[FeatureDataObj]; line1.type _ distanceLine; line1.shape _ leftLine; AddAlignment[line1, alignBag]; IF ABS[distance] > 0.0 THEN { rightLine _ Lines2d.LineRightOfLine[line, distance]; line2 _ NEW[FeatureDataObj]; line2.type _ distanceLine; line2.shape _ rightLine; AddAlignment[line2, alignBag]; } ELSE line2 _ NIL; }; <<>> <> SetStaticBags: PUBLIC PROC [ggData: GGData] = { <> <> <> <> triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; scene: Scene _ ggData.scene; anchor: Caret _ ggData.anchor; heuristics: BOOL _ GGState.GetHeuristics[ggData]; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.GetShowAlignments[ggData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; FlushTriggerBag[sceneBag]; FillStaticSceneBag[scene, sceneBag]; } ELSE { CodeTimer.StartInt[$SetStaticBags, $Gargoyle]; <> FlushTriggerBag[triggerBag]; FillStaticTriggerBag[anchor, scene, heuristics, triggerBag]; <> FlushTriggerBag[sceneBag]; FillStaticSceneBag[scene, sceneBag]; <> FlushAlignBag[alignBag]; FillStaticAlignBag[triggerBag, sceneBag, ggData, hideAlignments, alignBag]; CodeTimer.StopInt[$SetStaticBags, $Gargoyle]; }; }; SetStaticTriggerAndAlignBags: PUBLIC PROC [ggData: GGData] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; scene: Scene _ ggData.scene; anchor: Caret _ ggData.anchor; heuristics: BOOL _ GGState.GetHeuristics[ggData]; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.GetShowAlignments[ggData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; } ELSE { sceneBag: TriggerBag _ ggData.hitTest.sceneBag; <> FlushTriggerBag[triggerBag]; FillStaticTriggerBag[anchor, scene, heuristics, triggerBag]; <> FlushAlignBag[alignBag]; FillStaticAlignBag[triggerBag, sceneBag, ggData, hideAlignments, alignBag]; }; }; SetDynamicBags: PUBLIC PROC [ggData: GGData, action: ATOM] = { <> <> <> <> triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; scene: Scene _ ggData.scene; anchor: Caret _ ggData.anchor; heuristics: BOOL _ GGState.GetHeuristics[ggData]; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.GetShowAlignments[ggData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; FlushTriggerBag[sceneBag]; FlushTriggerBag[triggerBag]; -- added March 30, 1987. KAP FillDynamicSceneBag[scene, sceneBag, ggData.drag.editConstraints, ggData.drag.bezierDrag]; } ELSE { CodeTimer.StartInt[$SetDynamicBags, $Gargoyle]; <> FlushTriggerBag[triggerBag]; FillDynamicTriggerBag[anchor, scene, heuristics, triggerBag, ggData.drag.editConstraints, ggData.drag.bezierDrag]; <> FlushTriggerBag[sceneBag]; FillDynamicSceneBag[scene, sceneBag, ggData.drag.editConstraints, ggData.drag.bezierDrag]; <> FlushAlignBag[alignBag]; FillDynamicAlignBag[triggerBag, sceneBag, ggData, hideAlignments, action, alignBag]; CodeTimer.StopInt[$SetDynamicBags, $Gargoyle]; }; }; StaticToDynamicBags: PUBLIC PROC [ggData: GGData, saveForeground: BOOL _ TRUE] = { <> <> <> <> <> <> scene: Scene _ ggData.scene; bd: BezierDragRecord _ ggData.drag.bezierDrag; ec: EditConstraints _ ggData.drag.editConstraints; triggerBag: TriggerBag _ ggData.hitTest.triggerBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; alignBag: AlignBag _ ggData.hitTest.alignBag; heuristics: BOOL _ GGState.GetHeuristics[ggData]; hideAlignments: BOOL _ NOT GGState.GetShowAlignments[ggData]; hotMoving: BOOL _ SomeSelectedIsHot[scene]; IF ggData.camera.hideAlignments THEN { -- No alignments. Just build sceneBag. FlushAlignBag[alignBag]; CopyTriggerBag[to: ggData.hitTest.oldTriggerBag, from: triggerBag]; CopyTriggerBag[to: ggData.hitTest.oldSceneBag, from: sceneBag]; [] _ RemoveMoving[scene, sceneBag, ec, bd]; } ELSE { autoAdded: BOOL _ FALSE; autoTriggers: LIST OF SliceDescriptor _ NIL; filterLists: FilterLists _ GGState.GetFilterLists[ggData]; someAlignmentsActive: BOOL _ NOT EmptyFilterLists[filterLists]; CodeTimer.StartInt[$StaticToDynamicBags, $Gargoyle]; <> IF someAlignmentsActive THEN { CopyTriggerBag[to: ggData.hitTest.oldTriggerBag, from: triggerBag]; [] _ RemoveMoving[scene, triggerBag, ec, bd]; [autoAdded, autoTriggers] _ AddHeuristics[scene, heuristics, $Drag, triggerBag, ec, bd]; ggData.hitTest.oldTriggerBagOK _ TRUE; } ELSE ggData.hitTest.oldTriggerBagOK _ FALSE; <> CopyTriggerBag[to: ggData.hitTest.oldSceneBag, from: sceneBag]; [] _ RemoveMoving[scene, sceneBag, ec, bd]; <> IF autoAdded OR hotMoving THEN { ggData.hitTest.alignBagIsOldAlignBag _ FALSE; IF hotMoving THEN { -- all is lost ggData.hitTest.oldAlignBagOK _ FALSE; FlushAlignBag[alignBag]; BuiltInFilters[triggerBag, ggData, hideAlignments, alignBag]; GGRefresh.UpdateForeground[ggData, TRUE]; } ELSE { -- no hot parts are moving alignObjects: LIST OF FeatureData; ggData.hitTest.oldAlignBagOK _ TRUE; CopyAlignBag[to: ggData.hitTest.oldAlignBag, from: alignBag]; FOR list: LIST OF SliceDescriptor _ autoTriggers, list.rest UNTIL list = NIL DO alignObjects _ GGUtility.FeatureDataNconc[IncrementalFilterSlice[list.first, filterLists, hideAlignments, alignBag], alignObjects]; ENDLOOP; IF saveForeground THEN { GGRefresh.SaveForeground[ggData]; SaveLineTable[ggData]; }; GGRefresh.NoteNewForeground[ggData, alignObjects]; }; } ELSE { -- leave the Align Bag alone. Don't bother saving foreground plane. ggData.hitTest.alignBagIsOldAlignBag _ TRUE; ggData.hitTest.oldAlignBagOK _ FALSE; }; CodeTimer.StopInt[$StaticToDynamicBags, $Gargoyle]; }; }; DynamicToStaticBags: PUBLIC PROC [ggData: GGData, restoreForeground: BOOL _ TRUE] = { alignBag: AlignBag _ ggData.hitTest.alignBag; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.GetShowAlignments[ggData]; CodeTimer.StartInt[$DynamicToStaticBags, $Gargoyle]; <> IF ggData.hitTest.oldTriggerBagOK THEN SwapOldAndNewTriggerBags[ggData]; SwapOldAndNewSceneBags[ggData]; <> IF ggData.hitTest.alignBagIsOldAlignBag THEN {} -- already restored ELSE { IF ggData.hitTest.oldAlignBagOK THEN { -- no hot were moving SwapOldAndNewAlignBags[ggData]; IF restoreForeground THEN { GGRefresh.RestoreForeground[ggData]; RestoreLineTable[ggData]; }; } ELSE { -- hot were moving. Just remake the alignBag FlushAlignBag[alignBag]; BuiltInFilters[ggData.hitTest.triggerBag, ggData, hideAlignments, alignBag]; GGRefresh.UpdateForeground[ggData, TRUE]; }; }; CodeTimer.StopInt[$DynamicToStaticBags, $Gargoyle]; }; SwapOldAndNewAlignBags: PROC [ggData: GGData] = { tempBag: AlignBag; tempBag _ ggData.hitTest.alignBag; ggData.hitTest.alignBag _ ggData.hitTest.oldAlignBag; ggData.hitTest.oldAlignBag _ tempBag; }; SwapOldAndNewTriggerBags: PROC [ggData: GGData] = { tempBag: TriggerBag; tempBag _ ggData.hitTest.triggerBag; ggData.hitTest.triggerBag _ ggData.hitTest.oldTriggerBag; ggData.hitTest.oldTriggerBag _ tempBag; }; SwapOldAndNewSceneBags: PROC [ggData: GGData] = { tempBag: TriggerBag; tempBag _ ggData.hitTest.sceneBag; ggData.hitTest.sceneBag _ ggData.hitTest.oldSceneBag; ggData.hitTest.oldSceneBag _ tempBag; }; UpdateBagsForAdd: PUBLIC PROC [oldAncestor: Slice, newAncestorD: SliceDescriptor, trajEnd: TrajEnd, ggData: GGData] RETURNS [repaintForeground: BOOL _ FALSE] = { <> <> <> <> <> <> triggerBag: TriggerBag _ ggData.hitTest.triggerBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; alignBag: AlignBag _ ggData.hitTest.alignBag; scene: Scene _ ggData.scene; newHot, oldHot, newWhole, oldWhole, hotMinusOldHot: SliceDescriptor; hideAlignments: BOOL _ NOT GGState.GetShowAlignments[ggData]; alignObjects: LIST OF FeatureData; <> oldHot _ RemoveEntireHotSlice[oldAncestor, triggerBag]; newHot _ GGSelect.FindSelectedSlice[newAncestorD.slice, hot]; <> <> IF newHot # NIL THEN [] _ AddSliceFeature[newHot, triggerBag]; <> oldWhole _ RemoveEntireHotSlice[oldAncestor, sceneBag]; newWhole _ GGSliceOps.NewParts[newAncestorD.slice, NIL, slice]; [] _ AddSliceFeature[newWhole, sceneBag]; <> <> IF newHot = NIL THEN hotMinusOldHot _ NIL ELSE { maskD: SliceDescriptor; -- for a traj outD: SliceDescriptor; -- for the corresponding outline traj: Traj _ GGParent.FirstIncludedChild[newAncestorD.slice, newAncestorD.parts, leaf, $Traj].slice; trajData: TrajData _ NARROW[traj.data]; filterLists: FilterLists _ GGState.GetFilterLists[ggData]; IF trajEnd = lo THEN maskD _ GGSequence.Union[GGSlice.DescriptorFromParts[traj, GGSequence.CreateFromSegment[trajData, 0]], GGSlice.DescriptorFromParts[traj, GGSequence.CreateFromJoint[trajData, 0]]] ELSE maskD _ GGSequence.Union[GGSlice.DescriptorFromParts[traj, GGSequence.CreateFromSegment[trajData, GGTraj.HiSegment[traj]]], GGSlice.DescriptorFromParts[traj, GGSequence.CreateFromJoint[trajData, GGTraj.HiJoint[traj]]]]; outD _ GGParent.TopLevelDescriptorFromChildDescriptor[maskD]; hotMinusOldHot _ GGSliceOps.DifferenceParts[newHot, GGSliceOps.DifferenceParts[newWhole, outD]]; alignObjects _ IncrementalFilterSlice[hotMinusOldHot, filterLists, hideAlignments, alignBag]; }; }; UpdateBagsForNewSlices: PUBLIC PROC [newSlices: LIST OF Slice, ggData: GGData] = { <> <> <> triggerBag: TriggerBag _ ggData.hitTest.triggerBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; alignBag: AlignBag _ ggData.hitTest.alignBag; scene: Scene _ ggData.scene; <> FOR list: LIST OF Slice _ newSlices, list.rest UNTIL list = NIL DO firstSlice: Slice _ list.first; wholeD: SliceDescriptor _ GGSliceOps.NewParts[firstSlice, NIL, slice]; [] _ AddSliceFeature[wholeD, sceneBag]; ENDLOOP; }; <> <<>> AddAnchorTrigger: PROC [anchor: Caret, triggerBag: TriggerBag] = { [] _ CreateAnchorTrigger[anchor, triggerBag]; }; AddAllHotSlices: PROC [scene: Scene, triggerBag: TriggerBag] = { feature: FeatureData; AddHotToTriggerBag: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { feature _ FeatureFromSlice[sliceD.slice, sliceD.parts]; AddFeature[feature, triggerBag]; }; [] _ GGScene.WalkSelectedSlices[scene, first, AddHotToTriggerBag, hot]; }; AddHeuristics: PROC [scene: Scene, heuristics: BOOL, atom: ATOM, triggerBag: TriggerBag, ec: EditConstraints, bd: BezierDragRecord] RETURNS [autoAdded: BOOL _ FALSE, autoTriggers: LIST OF SliceDescriptor _ NIL] = { feature: FeatureData; IF NOT heuristics THEN RETURN; SELECT atom FROM $Drag => { AddToTriggerBag: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { stationaryD: SliceDescriptor; oldFeature: FeatureData; IF GGSliceOps.IsCompleteParts[sliceD] THEN RETURN; -- the whole thing is moving stationaryD _ StationaryParts[sliceD, scene, ec, bd]; IF GGSliceOps.IsEmptyParts[stationaryD] THEN RETURN; -- the whole thing is moving oldFeature _ FindFeature[triggerBag, stationaryD.slice]; IF oldFeature # NIL THEN { hotD: SliceDescriptor _ NARROW[oldFeature.shape]; newlyHotD: SliceDescriptor _ GGSliceOps.DifferenceParts[stationaryD, hotD]; totalD: SliceDescriptor; IF NOT GGSliceOps.IsEmptyParts[newlyHotD] THEN { totalD _ GGSliceOps.UnionParts[stationaryD, hotD]; feature _ FeatureFromSlice[totalD.slice, totalD.parts]; ReplaceFeature[triggerBag, oldFeature, feature]; autoTriggers _ CONS[newlyHotD, autoTriggers]; autoAdded _ TRUE; }; } ELSE { feature _ FeatureFromSlice[stationaryD.slice, stationaryD.parts]; AddFeature[feature, triggerBag]; autoTriggers _ CONS[stationaryD, autoTriggers]; autoAdded _ TRUE; }; }; [] _ GGScene.WalkSelectedSlices[scene, first, AddToTriggerBag, normal]; }; $CaretPos => NULL; ENDCASE => ERROR UnexpectedType; }; StationaryParts: PROC [selectedD: SliceDescriptor, scene: Scene, ec: EditConstraints, bd: BezierDragRecord] RETURNS [stationaryD: SliceDescriptor] = { background, overlay, rubber, drag, move, totalD: SliceDescriptor; totalD _ GGSliceOps.NewParts[selectedD.slice, NIL, slice]; [background, overlay, rubber, drag] _ GGSliceOps.MovingParts[selectedD.slice, selectedD.parts, ec, bd]; move _ GGSliceOps.UnionParts[rubber, drag]; IF move = NIL THEN RETURN[totalD]; stationaryD _ GGSliceOps.DifferenceParts[totalD, move]; }; StationaryTriggerParts: PUBLIC PROC [hotD, selSliceD: SliceDescriptor, editConstraints: EditConstraints, bezierDrag: BezierDragRecord] RETURNS [stationary: SliceDescriptor, someRemoved: BOOL _ FALSE] = { background, overlay, rubber, drag, move: SliceDescriptor; IF selSliceD = NIL THEN RETURN[hotD]; -- clearly nothing is moving [background, overlay, rubber, drag] _ GGSliceOps.MovingParts[selSliceD.slice, selSliceD.parts, editConstraints, bezierDrag]; move _ GGSliceOps.UnionParts[rubber, drag]; IF move = NIL THEN RETURN[hotD, FALSE]; stationary _ GGSliceOps.DifferenceParts[hotD, move]; IF stationary # hotD THEN someRemoved _ TRUE; }; RemoveMoving: PROC [scene: Scene, bag: TriggerBag, ec: EditConstraints, bd: BezierDragRecord] RETURNS [someRemoved: BOOL _ FALSE] = { <> <> thisRemoved: BOOL _ FALSE; DoRemoveMoving: PROC [selectedD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { hotSliceD, stationaryD: SliceDescriptor; oldFeature: FeatureData; oldFeature _ FindFeature[bag, selectedD.slice]; IF oldFeature = NIL THEN RETURN; hotSliceD _ NARROW[oldFeature.shape]; [stationaryD, thisRemoved] _ StationaryTriggerParts[hotSliceD, selectedD, ec, bd]; IF thisRemoved THEN { -- nothing was removed newFeature: FeatureData; someRemoved _ TRUE; IF NOT GGSliceOps.IsEmptyParts[stationaryD] THEN { newFeature _ FeatureFromSlice[stationaryD.slice, stationaryD.parts]; ReplaceFeature[bag, oldFeature, newFeature]; } ELSE DeleteFeature[bag, oldFeature]; }; }; [] _ GGScene.WalkSelectedSlices[scene, first, DoRemoveMoving, normal]; }; FilterLists: TYPE = REF FilterListsObj; FilterListsObj: TYPE = GGState.FilterListsObj; EmptyFilterLists: PROC [filterLists: FilterLists] RETURNS [BOOL] = { RETURN[filterLists.onSlopes=NIL AND filterLists.onRadii = NIL AND filterLists.onAngles=NIL AND filterLists.onDistances = NIL]; }; FilterListsFromFilter: PROC [value: REAL, filterType: FilterType] RETURNS [filterLists: FilterLists] = { filterLists _ NEW[FilterListsObj _ [NIL, NIL, NIL, NIL]]; SELECT filterType FROM slope => filterLists.onSlopes _ LIST[value]; radius => filterLists.onRadii _ LIST[value]; angle => filterLists.onAngles _ LIST[value]; distance => filterLists.onDistances _ LIST[value]; ENDCASE => ERROR; }; BuiltInFilters: PUBLIC PROC [triggerBag: TriggerBag, ggData: GGData, hideAlignments: BOOL, alignBag: AlignBag] = { <> filterLists: FilterLists; anchor: Caret; anchorFeature: FeatureData; CodeTimer.StartInt[$BuiltInFilters, $Gargoyle]; anchorFeature _ triggerBag.anchor; IF hideAlignments THEN { FlushAlignBag[alignBag]; IF anchorFeature#NIL AND GGCaret.Exists[(anchor _ NARROW[anchorFeature.shape, Caret])] THEN AddAnchorObject[anchorFeature, alignBag]; RETURN; }; filterLists _ GGState.GetFilterLists[ggData]; -- filterLists are faster to loop over than filters IF EmptyFilterLists[filterLists] THEN { <> IF anchorFeature#NIL AND GGCaret.Exists[(anchor _ NARROW[anchorFeature.shape, Caret])] THEN AddAnchorObject[anchorFeature, alignBag]; } ELSE { DoPointFireRule: PointWalkProc = { <> [] _ PointFireRule[point, filterLists, alignBag]; }; DoForSlice: FeatureWalkProc = { <> sliceD _ NARROW[feature.shape]; GGSliceOps.WalkPointsInDescriptor[sliceD, DoPointFireRule]; pointPairGen _ GGSliceOps.PointPairsInDescriptor[sliceD]; FOR next: PointPairAndDone _ GGSliceOps.NextPointPair[sliceD.slice, pointPairGen], GGSliceOps.NextPointPair[sliceD.slice, pointPairGen] UNTIL next.done DO [] _ SegmentFireRule[9999, next.lo, next.hi, filterLists, alignBag]; ENDLOOP; }; sliceD: SliceDescriptor; pointPairGen: PointPairGenerator; <> IF anchorFeature#NIL THEN { anchor _ NARROW[anchorFeature.shape, Caret]; IF GGCaret.Exists[anchor] THEN { AddAnchorObject[anchorFeature, alignBag]; [] _ AnchorFireRule[anchor, filterLists, alignBag]; }; }; <> WalkSliceTriggers[triggerBag, DoForSlice]; }; CodeTimer.StopInt[$BuiltInFilters, $Gargoyle]; }; <<>> <> CreateAnchorTrigger: PUBLIC PROC [anchor: Caret, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { feature _ FeatureFromAnchor[anchor]; AddFeature[feature, triggerBag]; }; AddSliceFeature: PUBLIC PROC [sliceD: SliceDescriptor, triggerBag: TriggerBag] RETURNS [newFeature: FeatureData] = { oldD, unionD: SliceDescriptor; oldFeature: FeatureData; [oldD, oldFeature] _ FindOldSlice[sliceD.slice, triggerBag.slices]; IF oldD = NIL THEN { newFeature _ FeatureFromSlice[sliceD.slice, sliceD.parts]; AppendFeature[newFeature, triggerBag.slices]; } ELSE { unionD _ GGSliceOps.UnionParts[oldD, sliceD]; newFeature _ FeatureFromSlice[sliceD.slice, unionD.parts]; ReplaceFeature[triggerBag, oldFeature, newFeature]; }; }; IncrementalFilterSlice: PROC [sliceD: SliceDescriptor, filterLists: FilterLists, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { SplineInPointFeatures: PointWalkProc = { PointWalkProc: TYPE = PROC [point: Point] RETURNS [done: BOOL _ FALSE]; alignObjects _ GGUtility.FeatureDataNconc[PointFireRule[point, filterLists, alignBag], alignObjects]; }; IF filterLists.onSlopes # NIL OR filterLists.onRadii # NIL THEN GGSliceOps.WalkPointsInDescriptor[sliceD, SplineInPointFeatures]; IF filterLists.onAngles # NIL OR filterLists.onDistances # NIL THEN { pointPairGen: PointPairGenerator _ GGSliceOps.PointPairsInDescriptor[sliceD]; FOR next: PointPairAndDone _ GGSliceOps.NextPointPair[sliceD.slice, pointPairGen], GGSliceOps.NextPointPair[sliceD.slice, pointPairGen] UNTIL next.done DO alignObjects _ GGUtility.FeatureDataNconc[SegmentFireRule[9999, next.lo, next.hi, filterLists, alignBag], alignObjects]; ENDLOOP; }; }; IncrementalNewTrigger: PUBLIC PROC [trigger: FeatureData, filterLists: FilterLists, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { <> alignObjects _ NIL; IF hideAlignments THEN { FlushAlignBag[alignBag]; } ELSE { WITH trigger.shape SELECT FROM sliceD: SliceDescriptor => { alignObjects _ IncrementalFilterSlice[sliceD, filterLists, hideAlignments, alignBag]; }; anchor: Caret => { <> IF GGCaret.Exists[anchor] THEN { point: Point _ GGCaret.GetPoint[anchor]; [] _ PointFireRule[point, filterLists, alignBag]; }; }; ENDCASE => ERROR; }; }; <<>> IncrementalNewFilter: PUBLIC PROC [value: REAL, filterType: FilterType, triggerBag: TriggerBag, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData _ NIL] = { <> filterLists: FilterLists _ FilterListsFromFilter[value, filterType]; DoPointFireRule: PointWalkProc = { <> alignObjects _ GGUtility.FeatureDataNconc[PointFireRule[point, filterLists, alignBag], alignObjects]; }; DoForSlice: FeatureWalkProc = { <> sliceD _ NARROW[feature.shape]; SELECT filterType FROM slope, radius => { GGSliceOps.WalkPointsInDescriptor[sliceD, DoPointFireRule]; }; angle, distance => { pointPairGen _ GGSliceOps.PointPairsInDescriptor[sliceD]; FOR next: PointPairAndDone _ GGSliceOps.NextPointPair[sliceD.slice, pointPairGen], GGSliceOps.NextPointPair[sliceD.slice, pointPairGen] UNTIL next.done DO alignObjects _ GGUtility.FeatureDataNconc[SegmentFireRule[9999, next.lo, next.hi, filterLists, alignBag], alignObjects]; ENDLOOP; }; ENDCASE => ERROR; }; sliceD: SliceDescriptor; pointPairGen: PointPairGenerator; anchor: Caret; anchorFeature: FeatureData; IF hideAlignments THEN RETURN; <> anchorFeature _ triggerBag.anchor; IF anchorFeature # NIL THEN { anchor _ NARROW[anchorFeature.shape, Caret]; IF GGCaret.Exists[anchor] THEN { AddAnchorObject[anchorFeature, alignBag]; alignObjects _ GGUtility.FeatureDataNconc[AnchorFireRule[anchor, filterLists, alignBag], alignObjects]; }; }; <> WalkSliceTriggers[triggerBag, DoForSlice]; }; <> RemoveAnchorTrigger: PUBLIC PROC [triggerBag: TriggerBag] = { triggerBag.anchor _ NIL; }; RemoveEntireHotSlice: PUBLIC PROC [slice: Slice, triggerBag: TriggerBag] RETURNS [oldSliceD: SliceDescriptor]= { oldFeature: FeatureData; [oldSliceD, oldFeature] _ FindOldSlice[slice, triggerBag.slices]; IF oldSliceD#NIL THEN { DeleteFeature[triggerBag, oldFeature]; }; }; <<>> <> <<>> <> AddFeature: PROC [featureData: FeatureData, triggerBag: TriggerBag] = { Process.CheckForAbort[]; SELECT featureData.type FROM slice => { sliceD: SliceDescriptor _ NARROW[featureData.shape]; key: RefTab.Key _ sliceD.slice; val: REF; found: BOOL _ FALSE; [found, val] _ RefTab.Fetch[triggerBag.slices, key]; IF NOT found THEN { list: LIST OF FeatureData _ LIST[featureData]; [] _ RefTab.Insert[triggerBag.slices, key, list]; } ELSE { list: LIST OF FeatureData _ NARROW[val]; list _ CONS[featureData, list]; [] _ RefTab.Store[triggerBag.slices, key, list]; }; }; anchor => { triggerBag.anchor _ featureData; }; ENDCASE => ERROR; }; <> <<>> AddAllSlices: PROC [scene: Scene, triggerBag: TriggerBag] = { feature: FeatureData; DoAddSlice: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { feature _ FeatureFromSlice[slice]; AddFeature[feature, triggerBag]; }; [] _ GGScene.WalkSlices[scene, first, DoAddSlice]; }; <<>> <> AddAnchorObject: PROC [anchorFeature: FeatureData, alignBag: AlignBag] = { alignBag.anchor _ anchorFeature; }; PointFireRule: PROC [point: Point, filterLists: FilterLists, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { feature: FeatureData; alignObjects _ NIL; FOR list: LIST OF REAL _ filterLists.onSlopes, list.rest UNTIL list = NIL DO feature _ JointAddSlopeLine[ degrees: list.first, point: point, alignBag: alignBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; ENDLOOP; FOR list: LIST OF REAL _ filterLists.onRadii, list.rest UNTIL list = NIL DO feature _ JointAddCircle[ radius: list.first*filterLists.scaleUnit, point: point, alignBag: alignBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; ENDLOOP; }; SegmentFireRule: PROC [segNum: NAT, lo, hi: Point, filterLists: FilterLists, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { line1, line2: FeatureData; alignObjects _ NIL; FOR list: LIST OF REAL _ filterLists.onDistances, list.rest UNTIL list = NIL DO [line1, line2] _ SegmentAddDistanceLines[distance: list.first*filterLists.scaleUnit, segNum: segNum, lo: lo, hi: hi, alignBag: alignBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; IF line2 # NIL THEN alignObjects _ CONS[line2, alignObjects]; ENDLOOP; FOR list: LIST OF REAL _ filterLists.onAngles, list.rest UNTIL list = NIL DO [line1, line2] _ SegmentAddTwoAngleLines[degrees: list.first, segNum: segNum, lo: lo, hi: hi, alignBag: alignBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; IF line2 # NIL THEN alignObjects _ CONS[line2, alignObjects]; ENDLOOP; }; AnchorFireRule: PROC [anchor: Caret, filterLists: FilterLists, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { line1, line2: FeatureData; point: Point _ GGCaret.GetPoint[anchor]; normal: Vector _ GGCaret.GetNormal[anchor]; lo: Point _ [point.x+normal.y, point.y-normal.x]; hi: Point _ [point.x-normal.y, point.y+normal.x]; alignObjects _ PointFireRule[point, filterLists, alignBag]; FOR list: LIST OF REAL _ filterLists.onDistances, list.rest UNTIL list = NIL DO [line1, line2] _ SegmentAddDistanceLines[distance: list.first*filterLists.scaleUnit, segNum: 9999, lo: lo, hi: hi, alignBag: alignBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; IF line2 # NIL THEN alignObjects _ CONS[line2, alignObjects]; ENDLOOP; FOR list: LIST OF REAL _ filterLists.onAngles, list.rest UNTIL list = NIL DO degrees: REAL _ list.first; loLine: Line _ Lines2d.LineFromPointAndAngle[pt: point, degrees: Vectors2d.AngleFromVector[normal]+90+degrees]; -- angle normal to anchor normal line1 _ NEW[FeatureDataObj]; line1.type _ angleLine; line1.shape _ NEW[AlignmentLineObj _ [line: loLine, degrees: degrees, triggerPoints: NIL]]; AddAlignment[line1, alignBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; ENDLOOP; }; <<>> <> SomeSelectedIsHot: PROC [scene: Scene] RETURNS [hotFound: BOOL _ FALSE] = { FindHot: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF GGSelect.IsSelectedInPart[sliceD.slice, scene, hot] THEN { hotFound _ TRUE; done _ TRUE; }; }; [] _ GGScene.WalkSelectedSlices[scene, first, FindHot, normal]; }; <> <<>> FindOldSlice: PROC [slice: Slice, sliceTriggers: RefTab.Ref] RETURNS [oldSliceD: SliceDescriptor, oldFeature: FeatureData] = { key: REF _ slice; val: REF; found: BOOL _ FALSE; [found, val] _ RefTab.Fetch[sliceTriggers, key]; IF NOT found OR val = NIL THEN RETURN[NIL, NIL]; FOR list: LIST OF FeatureData _ NARROW[val], list.rest UNTIL list = NIL DO oldFeature _ list.first; oldSliceD _ NARROW[oldFeature.shape]; IF oldSliceD.slice = slice THEN RETURN; ENDLOOP; RETURN [NIL, NIL]; }; RemoveFeatureFromList: PUBLIC PROC [ref: FeatureData, list: LIST OF FeatureData] RETURNS [val: LIST OF FeatureData _ NIL] = { z: LIST OF FeatureData _ NIL; UNTIL list = NIL DO IF list.first#ref THEN {IF val = NIL THEN {val _ CONS[list.first, NIL]; z _ val} ELSE {z.rest _ CONS[list.first, z.rest]; z _ z.rest}}; list _ list.rest; ENDLOOP; }; -- of RemoveFeatureFromList <<>> <> alignmentColor: Imager.Color _ ImagerBackdoor.MakeStipple[145065B]; checkerColor: Imager.Color _ ImagerBackdoor.MakeStipple[122645B]; <> <> useCache: BOOL _ TRUE; CreateLineTable: PUBLIC PROC [ggData: GGData] = { ggData.refresh.lineCache _ CardTab.Create[mod: 211]; ggData.refresh.savedLineTable _ CardTab.Create[mod: 211]; }; LineIsPresent: PROC [ggData: GGData, line: Line] RETURNS [present: BOOL _ FALSE] = { <> <> <> <<};>> <<[----, present] _ FunctionCache.Lookup[ggData.refresh.lineCache, LineCompare];>> key: CARD _ CardFromLine[line]; found: BOOL _ FALSE; val: REF; list: LIST OF Line; [found, val] _ CardTab.Fetch[ggData.refresh.lineCache, key]; IF NOT found THEN RETURN[FALSE]; list _ NARROW[val]; FOR l: LIST OF Line _ list, l.rest UNTIL l = NIL DO IF l.first.d = line.d AND l.first.theta = line.theta THEN RETURN[TRUE]; ENDLOOP; present _ FALSE; }; <> <> <> <<>> TableStatistics: PROC [table: CardTab.Ref] RETURNS [count, min, max: CARD, avg: REAL] = { Count: CardTab.EachPairAction = { <> list: LIST OF Line _ NARROW[val]; itemsInBucket: CARD _ 0; count _ count + 1; FOR l: LIST OF Line _ list, l.rest UNTIL l = NIL DO itemsInBucket _ itemsInBucket + 1; ENDLOOP; min _ MIN[min, itemsInBucket]; max _ MAX[max, itemsInBucket]; total _ total + itemsInBucket; }; total: REAL _ 0.0; count _ 0; min _ LAST[CARD]; max _ 0; [] _ CardTab.Pairs[table, Count]; avg _ total/count; }; CopyLineList: PUBLIC PROC [l: LIST OF Line] RETURNS [copyList: LIST OF Line] = { z: LIST OF Line _ NIL; IF l = NIL THEN RETURN[NIL]; copyList _ CONS[l.first, NIL]; z _ copyList; UNTIL (l _ l.rest) = NIL DO z.rest _ CONS[l.first, NIL]; z _ z.rest; ENDLOOP; }; SaveLineTable: PROC [ggData: GGData] = { CopyLines: CardTab.EachPairAction = { <> inserted: BOOL _ CardTab.Insert[ggData.refresh.savedLineTable, key, val]; IF NOT inserted THEN ERROR; }; CardTab.Erase[ggData.refresh.savedLineTable]; [] _ CardTab.Pairs[ggData.refresh.lineCache, CopyLines]; }; RestoreLineTable: PROC [ggData: GGData] = { temp: CardTab.Ref _ ggData.refresh.savedLineTable; ggData.refresh.savedLineTable _ ggData.refresh.lineCache; ggData.refresh.lineCache _ temp; }; FlushLineTable: PUBLIC PROC [ggData: GGData] = { <> CardTab.Erase[ggData.refresh.lineCache]; }; CardFromLine: PROC [line: Line] RETURNS [key: CARD] = { key _ LOOPHOLE[line.d]; }; AddLineToTable: PROC [ggData: GGData, line: Line] = { <> key: CARD _ CardFromLine[line]; found, newValue: BOOL _ FALSE; val: REF; list: LIST OF Line; [found, val] _ CardTab.Fetch[ggData.refresh.lineCache, key]; IF NOT found THEN { -- empty bucket list _ LIST[line]; newValue _ CardTab.Store[ggData.refresh.lineCache, key, list]; IF NOT newValue THEN ERROR; } ELSE { -- collision, add to bucket list _ NARROW[val]; list _ CONS[line, list]; newValue _ CardTab.Store[ggData.refresh.lineCache, key, list]; IF newValue THEN ERROR; }; }; DrawFeatureList: PUBLIC PROC [dc: Imager.Context, alignObjects: LIST OF FeatureData, ggData: GGData] = { DrawLineAux: PROC [line: Line] = { -- uses FunctionCache to avoid duplication IF LineIsPresent[ggData, line] THEN RETURN ELSE AddLineToTable[ggData, line]; IF ABS[line.theta] < 1.0 OR ABS[line.theta - 90.0] < 1.0 THEN { -- horizontal or vertical Imager.SetColor[dc, checkerColor]; GGShapes.DrawLine[dc, line, rect, 0.0, zip]; -- 0 width lines go fast Imager.SetColor[dc, alignmentColor]; } ELSE GGShapes.DrawLine[dc, line, rect, 0.0, zip]; -- 0 width lines go fast }; DrawCircleAux: PROC [circle: Circle] = { -- uses GGCircleCache cachedCircle: GGCircleCache.CachedCircle; IF (cachedCircle _ GGCircleCache.Lookup[ggData.controls.radiusCircleCache, circle.radius])#NIL THEN GGCircleCache.DrawCachedCircle[dc, circle.origin, cachedCircle] -- cache hit ELSE { -- cache miss. Attempt a cache entry and retry GGCircleCache.Insert[ggData.controls.radiusCircleCache, circle.radius]; IF (cachedCircle _ GGCircleCache.Lookup[ggData.controls.radiusCircleCache, circle.radius])#NIL THEN GGCircleCache.DrawCachedCircle[dc, circle.origin, cachedCircle] ELSE GGShapes.DrawCircle[dc, circle]; }; }; rect: Imager.Rectangle _ GGState.GetViewport[ggData]; zip: Draw2d.Zip; Imager.SetStrokeWidth[dc, 0.0]; -- to convince Draw2d.GetZip that everything is ok zip _ Draw2d.GetZip[dc]; Imager.SetStrokeWidth[dc, 1.0]; Imager.SetColor[dc, alignmentColor]; FOR list: LIST OF FeatureData _ alignObjects, list.rest UNTIL list = NIL DO WITH list.first.shape SELECT FROM slopeLine: AlignmentLine => DrawLineAux[slopeLine.line]; angleLine: AlignmentLine => DrawLineAux[angleLine.line]; circle: AlignmentCircle => IF useCache THEN DrawCircleAux[circle.circle] ELSE GGShapes.DrawCircle[dc, circle.circle]; distanceLine: Line => GGShapes.DrawLine[dc, distanceLine, rect]; ENDCASE => ERROR; ENDLOOP; Draw2d.ReleaseZip[zip]; }; HasVisibleObjects: PUBLIC PROC [alignBag: AlignBag] RETURNS [BOOL] = { RETURN[NOT ( EmptySlopeLines[alignBag] AND alignBag.angleLines = NIL AND alignBag.radiiCircles = NIL AND alignBag.distanceLines = NIL)]; }; DrawAlignBagRegardless: PUBLIC PROC [dc: Imager.Context, alignBag: AlignBag, ggData: GGData] = { slopeLines: LIST OF FeatureData; <> IF alignBag = NIL THEN RETURN; slopeLines _ ListOfSlopeLines[alignBag]; DrawFeatureList[dc, slopeLines, ggData]; DrawFeatureList[dc, alignBag.angleLines, ggData]; DrawFeatureList[dc, alignBag.radiiCircles, ggData]; DrawFeatureList[dc, alignBag.distanceLines, ggData]; }; MakeFiltersGarbage: PUBLIC PROC [filters: Filters] = { <> <> UnlinkBag: PROC [bag: TriggerBag] = { DoUnLink: FeatureWalkProc = { WITH feature.shape SELECT FROM sd: SliceDescriptor => IF sd.slice#NIL THEN { GGSlice.UnlinkSlice[sd.slice]; sd.slice _ NIL; }; ENDCASE; }; IF bag=NIL THEN RETURN; WalkSliceTriggers[bag, DoUnLink]; }; UnlinkBag[filters.triggerBag]; UnlinkBag[filters.oldTriggerBag]; UnlinkBag[filters.sceneBag]; UnlinkBag[filters.oldSceneBag]; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<>> Init: PROC [] = { emptyTriggerBag _ CreateTriggerBag[]; emptyAlignBag _ CreateAlignBag[]; }; <> Init[]; <<>> END.