<> <> <> <> <> <> <> <> <> <<>> DIRECTORY BufferedRefresh, CodeTimer, Draw2d, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGCoreTypes, GGDragTypes, GGEmbedTypes, GGInterfaceTypes, GGModelTypes, GGRefresh, GGRefreshTypes, GGScene, GGSegmentTypes, GGSelect, GGShapes, GGSliceOps, GGState, GGTraj, GGUtility, GGWindow, Imager, ImagerTransformation, Lines2dTypes, Real, RefTab, SlackProcess<<, Vectors2d>>, ViewerAbort; GGRefreshImpl: CEDAR MONITOR IMPORTS BufferedRefresh, CodeTimer, Draw2d, Feedback, GGAlign, GGBoundBox, GGCaret, GGScene, GGSelect, GGShapes, GGSliceOps, GGState, GGTraj, GGUtility, GGWindow, Imager, ImagerTransformation, RefTab, SlackProcess<<, Vectors2d>>, ViewerAbort EXPORTS GGInterfaceTypes, GGRefresh = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes AlignmentCircle: TYPE = GGInterfaceTypes.AlignmentCircle; AlignmentLine: TYPE = GGInterfaceTypes.AlignmentLine; AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; AlignmentObject: TYPE = GGModelTypes.AlignmentObject; BezierDragRecord: TYPE = GGModelTypes.BezierDragRecord; BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGCoreTypes.BoundBox; BoundBoxGenerator: TYPE = GGScene.BoundBoxGenerator; Camera: TYPE = GGModelTypes.Camera; Caret: TYPE = GGInterfaceTypes.Caret; Circle: TYPE = GGBasicTypes.Circle; Color: TYPE = Imager.Color; EditConstraints: TYPE = GGModelTypes.EditConstraints; FeatureData: TYPE = GGModelTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = Lines2dTypes.Line; Point: TYPE = GGBasicTypes.Point; Rectangle: TYPE = Imager.Rectangle; Sandwich: TYPE = BufferedRefresh.Sandwich; SandwichObj: PUBLIC TYPE ~ BufferedRefresh.SandwichObj; -- export to GGRefresh RefreshProc: TYPE = BufferedRefresh.RefreshProc; 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; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; Vector: TYPE = GGBasicTypes.Vector; <> DisableRefresh: PUBLIC PROC [ggData: GGData] = { <> ggData.refresh.suppressRefresh _ TRUE; ggData.refresh.totalBox^ _ [0,0,0,0,TRUE,FALSE]; }; EnableRefresh: PUBLIC PROC [ggData: GGData] = { <> ggData.refresh.suppressRefresh _ FALSE; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintInTotalBox, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; <<>> RegisterPaintProc: PUBLIC PROC [ggData: GGData, paintProc: GGRefresh.PaintProc, clientData: REF _ NIL] = { <> ggData.embed.paintProc _ paintProc; ggData.embed.clientData _ clientData; }; <<>> ActionAreaPaint: PUBLIC PROC [screen: Imager.Context, whatHasChanged: ATOM, ggData: GGData, handleViewerAbort: BOOL] = { <> <> PaintWithAbort: PROC = { DoActionAreaPaint[screen, whatHasChanged, ggData, ggData.refresh.paintBox ! UNWIND => { Feedback.Append[ggData.router, oneLiner, $Feedback, "Refresh Aborted"]; SlackProcess.FlushQueue[ggData.slackHandle]; -- you have to do this HERE! ggData.refresh.suppressRefresh _ FALSE; -- in case you killed FastPlayback ggData.refresh.suppressScreen _ FALSE; -- in case you killed FastPlayback ggData.aborted _ ALL[TRUE]; -- copies of aborted for all purposes }; ]; }; IF handleViewerAbort THEN ViewerAbort.CallWithAbortEnabled[ggData.controls.actionArea, PaintWithAbort] ELSE DoActionAreaPaint[screen, whatHasChanged, ggData, ggData.refresh.paintBox]; <> <> <> }; RepaintArea: PUBLIC PROC [screen: Imager.Context, ggData: GGData, whatHasChanged: ATOM, bounds: BoundBox] = { <> DoActionAreaPaint[screen, whatHasChanged, ggData, bounds]; }; PaintInParent: PUBLIC PROC [ggData: GGData, paintAction: ATOM] = { <> CodeTimer.StartInt[$PaintViewer, $Gargoyle]; ggData.refresh.paintAction _ paintAction; GetPaintBox[ggData, paintAction, ggData.refresh.paintBox]; IF paintAction # $None THEN ggData.embed.paintProc[ggData, paintAction, ggData.refresh.paintBox, ggData.embed.clientData]; CodeTimer.StopInt[$PaintViewer, $Gargoyle]; }; GetPaintBox: PUBLIC PROC [ggData: GGData, paintAction: ATOM, into: BoundBox] ~ { tableEntry: REF; found: BOOL _ FALSE; CodeTimer.StartInt[$Gargoyle, $GetPaintBox]; [found, tableEntry] _ RefTab.Fetch[aapTable, paintAction]; IF found THEN { aapEntry: AAPEntry _ NARROW[tableEntry]; IF aapEntry#NIL AND aapEntry.bBoxProc#NIL THEN [] _ aapEntry.bBoxProc[ggData, paintAction, into] ELSE { into.null _ FALSE; into.infinite _ TRUE; }; } ELSE { into.null _ FALSE; into.infinite _ TRUE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "GGRefreshImpl doesn't know how to GetPaintBox for %g", [atom[paintAction]] ]; }; CodeTimer.StopInt[$Gargoyle, $GetPaintBox]; }; DoActionAreaPaint: PROC [screen: Imager.Context, whatHasChanged: ATOM, ggData: GGData, bounds: BoundBox] = { <> <> <<>> CodeTimer.StartInt[$Gargoyle, $DoActionAreaPaint]; ggData.camera.cpScale _ 1.0/GGState.GetBiScrollersScale[ggData]; IF ggData.aborted[refresh] THEN { -- last paint got killed => unknown bitmap cache states ggData.aborted[refresh] _ FALSE; PaintEntireScene[screen, ggData]; } ELSE { tableEntry: REF; found: BOOL _ FALSE; [found, tableEntry] _ RefTab.Fetch[aapTable, whatHasChanged]; IF found THEN { aapEntry: AAPEntry _ NARROW[tableEntry]; IF aapEntry#NIL AND aapEntry.aapProc#NIL THEN aapEntry.aapProc[screen, ggData, GGState.GetDoubleBuffer[ggData], bounds]; } ELSE Feedback.PutF[ggData.router, oneLiner, $Complaint, "GGRefreshImpl doesn't know how to paint %g", [atom[whatHasChanged]] ]; }; CodeTimer.StopInt[$Gargoyle, $DoActionAreaPaint]; }; << [Artwork node; type 'Artwork on' to command tool] >> backgroundName: ATOM ~ $Background; foregroundName: ATOM ~ $Foreground; CreateSandwich: PUBLIC PROC [] RETURNS [sandwich: Sandwich] = { sandwich _ BufferedRefresh.CreateSandwich[LIST[ [backgroundName, TRUE, RefreshBackground], -- back ... [$Overlay, FALSE, RefreshOverlay], [$CPFeedback, FALSE, RefreshCPFeedback], [foregroundName, TRUE, RefreshForeground], [$CaretPlane, FALSE, RefreshCaretPlane]]]; -- ... to front }; SetScreen: PUBLIC PROC [ggData: GGData, cw, ch: INTEGER, screen: Imager.Context] ~ { sandwich: Sandwich ~ ggData.refresh.sandwich; BufferedRefresh.FitSandwichToScreen[sandwich, cw, ch, screen]; }; BoundBoxFromRectangle: PROC [rect: Imager.Rectangle] RETURNS [bBox: BoundBox] = { bBox _ NEW[GGCoreTypes.BoundBoxObj]; bBox^ _ IF rect.x = -Real.LargestNumber THEN [0,0,0,0,FALSE,TRUE] ELSE [rect.x, rect.y, rect.x + rect.w, rect.y + rect.h, FALSE, FALSE]; }; infiniteBox: BoundBox = GGBoundBox.CreateBoundBox[0,0,0,0,FALSE,TRUE]; DrawSandwich: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ TRUE, bounds: BoundBox _ NIL] ~ { IF ggData.refresh.suppressRefresh THEN { IF bounds = NIL THEN GGBoundBox.UpdateCopyBoundBox[bBox: ggData.refresh.totalBox, from: infiniteBox] ELSE GGBoundBox.EnlargeByBox[ggData.refresh.totalBox, bounds] } ELSE { clientToViewer, viewerToClient: Imager.Transformation; [clientToViewer, viewerToClient] _ GGState.GetBiScrollersTransforms[ggData]; BufferedRefresh.DrawSandwich[ggData.refresh.sandwich, IF ggData.refresh.suppressScreen THEN NIL ELSE screen, clientToViewer, viewerToClient, ggData, NOT buffer, NOT buffer, GGBoundBox.RectangleFromBoundBox[bounds] ]; }; }; RepairLayer: PROC [ggData: GGData, layerName: ATOM, proc: RefreshProc, clear: BOOL _ FALSE] ~ { IF NOT ggData.refresh.suppressRefresh THEN BufferedRefresh.RepairLayer[ggData.refresh.sandwich, layerName, proc, ggData, clear]; }; InvalidateBackground: PUBLIC PROC [ggData: GGData] ~ { sandwich: Sandwich ~ ggData.refresh.sandwich; BufferedRefresh.SetLayerOK[sandwich, backgroundName, FALSE]; }; InvalidateForeground: PUBLIC PROC [ggData: GGData] ~ { sandwich: Sandwich ~ ggData.refresh.sandwich; BufferedRefresh.SetLayerOK[sandwich, foregroundName, FALSE]; GGAlign.FlushLineTable[ggData]; }; SaveForeground: PUBLIC PROC [ggData: GGData] ~ { sandwich: Sandwich ~ ggData.refresh.sandwich; BufferedRefresh.SaveLayer[sandwich, foregroundName]; }; RestoreForeground: PUBLIC PROC [ggData: GGData] ~ { sandwich: Sandwich ~ ggData.refresh.sandwich; BufferedRefresh.RestoreLayer[sandwich, foregroundName]; }; PaintEntireScene: PUBLIC PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ TRUE] = { sandwich: Sandwich ~ ggData.refresh.sandwich; CodeTimer.StartInt[$PaintEntireScene, $Gargoyle]; InvalidateBackground[ggData]; InvalidateForeground[ggData]; PaintAllPlanes[screen, ggData, buffer]; CodeTimer.StopInt[$PaintEntireScene, $Gargoyle]; }; PaintAllPlanes: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ TRUE, bounds: BoundBox _ NIL] = { ggData.refresh.caretIsMoving _ FALSE; ggData.refresh.dragInProgress _ FALSE; DrawSandwich[screen, ggData, buffer, bounds]; }; <<>> <> <<>> <> <<>> RefreshBackground: RefreshProc = { ggData: GGData ~ NARROW[clientData]; boundBox: BoundBox; CodeTimer.StartInt[$RefreshBackground, $Gargoyle]; boundBox _ GGBoundBox.BoundBoxFromRectangle[boundRect]; DrawObjectsFiltered[dc: dc, ggData: ggData, filter: boundBox, excludeOverlay: TRUE]; CodeTimer.StopInt[$RefreshBackground, $Gargoyle]; }; RefreshOverlay: RefreshProc = { ggData: GGData ~ NARROW[clientData]; DoRefreshOverlay: PROC = { IF ggData.refresh.orderedOverlayList=NIL THEN ggData.refresh.orderedOverlayList _ OrderOverlayList[ggData]; -- update ordered list FOR oList: LIST OF SliceDescriptor _ ggData.refresh.orderedOverlayList, oList.rest UNTIL oList = NIL DO GGSliceOps.DrawTransform[oList.first.slice, oList.first.parts, dc, ggData.camera, ggData.drag.transform, ggData.drag.editConstraints]; ENDLOOP; IF ggData.drag.transform = NIL THEN ggData.refresh.oldTransform _ NIL ELSE IF ggData.refresh.oldTransform = NIL THEN ggData.refresh.oldTransform _ ImagerTransformation.Copy[ggData.drag.transform] ELSE ggData.refresh.oldTransform^ _ ggData.drag.transform^; -- only allocates when it must }; CodeTimer.StartInt[$RefreshOverlay, $Gargoyle]; Imager.DoSave[dc, DoRefreshOverlay]; CodeTimer.StopInt[$RefreshOverlay, $Gargoyle]; }; RefreshCPFeedback: RefreshProc = { ggData: GGData ~ NARROW[clientData]; CodeTimer.StartInt[$RefreshCPFeedback, $Gargoyle]; IF ggData.camera.quality#quality THEN { caretIsMoving: BOOL ~ ggData.refresh.caretIsMoving; dragInProgress: BOOL ~ ggData.refresh.dragInProgress; DrawAttractorFeedback[dc, ggData, dragInProgress, caretIsMoving]; DrawCpsOfSelectedSlices[dc, ggData.scene, ggData.camera, dragInProgress, caretIsMoving]; }; CodeTimer.StopInt[$RefreshCPFeedback, $Gargoyle]; }; RefreshForeground: RefreshProc = { ggData: GGData ~ NARROW[clientData]; CodeTimer.StartInt[$RefreshForeground, $Gargoyle]; GGAlign.FlushLineTable[ggData]; IF ggData.camera.quality#quality AND GGAlign.HasVisibleObjects[ggData.hitTest.alignBag] THEN GGAlign.DrawAlignBagRegardless[dc, ggData.hitTest.alignBag, ggData] ELSE drawnOn _ FALSE; CodeTimer.StopInt[$RefreshForeground, $Gargoyle]; }; RefreshCaretPlane: RefreshProc = { <> ggData: GGData ~ NARROW[clientData]; CodeTimer.StartInt[$RefreshCaretPlane, $Gargoyle]; IF ggData.camera.quality#quality THEN { scale: REAL ~ 1.0/GGState.GetBiScrollersScale[ggData]; DrawCaret[dc, ggData.caret, Imager.black, scale]; DrawAnchor[dc, ggData.anchor, Imager.black, scale]; }; CodeTimer.StopInt[$RefreshCaretPlane, $Gargoyle]; }; <> <<>> RepairBackgroundInBoundBox: PUBLIC PROC [ggData: GGData, bBox: BoundBox, eraseFirst: BOOL _ TRUE, overObject: Slice _ NIL] = { <> <> PaintObjectsInBox: RefreshProc = { IF eraseFirst THEN GGBoundBox.EraseWithinBoundBox[dc, bBox]; <> DrawObjectsFiltered[dc: dc, ggData: ggData, filter: bBox, excludeOverlay: TRUE, overObject: overObject]; }; CodeTimer.StartInt[$RepairBackgroundInBoundBox, $Gargoyle]; RepairLayer[ggData, backgroundName, PaintObjectsInBox]; CodeTimer.StopInt[$RepairBackgroundInBoundBox, $Gargoyle]; }; NullFromInto: PROC [into: BoundBox] RETURNS [BoundBox] ~ { IF into=NIL THEN RETURN[GGBoundBox.NullBoundBox[] ]; into.null _ TRUE; into.infinite _ FALSE; RETURN[into]; }; ObjectChangedInPlace: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> RepairBackgroundInBoundBox[ggData, bounds]; PaintAllPlanes[screen, ggData, buffer, bounds]; }; ObjectChangedInPlaceBounds: BBoxProc = { <> box _ GetABox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, selectedParts: TRUE, into: into]; <> }; ObjectChangedBoundBoxProvided: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox]; IF AlignmentsVisible[ggData] THEN DrawSandwich[screen, ggData, buffer] <> ELSE PaintAllPlanes[screen, ggData, buffer, bounds]; }; ObjectChangedBoundBoxProvidedBounds: BBoxProc = { <> IF AlignmentsVisible[ggData] THEN box _ NullFromInto[into] <> ELSE box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, attractor: TRUE, into: into]; <<>> }; ObjectAdded: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, ggData.refresh.addedObject]; PaintAllPlanes[screen, ggData, buffer, bounds]; }; ObjectAddedBounds: BBoxProc = { <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, attractor: TRUE, into: into]; }; AnchorAddedBounds: BBoxProc = { <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, alignments: TRUE, anchor: TRUE, into: into]; }; AnchorRemovedBounds: BBoxProc = { <> box _ NullFromInto[into]; GGBoundBox.EnlargeByBox[bBox: box, by: ggData.refresh.beforeBox]; }; <<>> <> UpdateForeground: PUBLIC PROC [ggData: GGData, eraseFirst: BOOL] = { PaintForeground: RefreshProc = { IF eraseFirst THEN GGAlign.FlushLineTable[ggData]; GGAlign.DrawAlignBagRegardless[dc, ggData.hitTest.alignBag, ggData]; }; RepairLayer[ggData, foregroundName, PaintForeground, eraseFirst]; }; NoteNewForeground: PUBLIC PROC [ggData: GGData, alignObjects: LIST OF FeatureData] = { <> AddToForeground: RefreshProc = { GGAlign.DrawFeatureList[dc, alignObjects, ggData]; }; RepairLayer[ggData, foregroundName, AddToForeground]; }; <<>> <> <<>> DuringSelect: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> DrawSandwich[screen, ggData, buffer, bounds]; }; DuringSelectBounds: BBoxProc = { <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE, into: into]; }; SelectionChangedBounds: BBoxProc = { <> ggData.refresh.dragInProgress _ FALSE; ggData.refresh.caretIsMoving _ FALSE; box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, into: into]; }; SelectionAndCaretChangedBounds: BBoxProc = { <> ggData.refresh.dragInProgress _ FALSE; ggData.refresh.caretIsMoving _ FALSE; box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, caret: TRUE, into: into]; }; AlignmentsBox: PROC [ggData: GGData] RETURNS [bBox: BoundBox] = { bBox _ IF GGAlign.HasVisibleObjects[ggData.hitTest.alignBag] THEN GGBoundBox.CreateBoundBox[0,0,0,0,FALSE,TRUE] -- for now ELSE GGBoundBox.NullBoundBox[]; }; AttractorBox: PROC [ggData: GGData, dragInProgress: BOOL] RETURNS [bBox: BoundBox] = { attractor: REF ANY _ GGCaret.GetAttractor[ggData.caret]; bBox _ GGBoundBox.NullBoundBox[]; IF attractor#NIL THEN bBox _ AttractorBoundBox[attractor, ggData, dragInProgress]; }; AttractorBoundBox: PROC [attractor: REF, ggData: GGData, dragInProgress: BOOL] RETURNS [thisBox: BoundBox] = { WITH attractor SELECT FROM sliceD: SliceDescriptor => { selectedD: SliceDescriptor; selectedParts: SliceParts _ NIL; IF dragInProgress THEN { selectedD _ GGSelect.FindSelectedSlice[sliceD.slice, normal]; selectedParts _ IF selectedD = NIL THEN NIL ELSE selectedD.parts; -- used to determine whether the slice is moving or not. }; <> <> thisBox _ GGSliceOps.AttractorFeedbackBoundBox[sliceD.slice, sliceD.parts, selectedParts, dragInProgress, ggData.camera, ggData.drag.editConstraints]; }; alignLine: AlignmentLine => { thisBox _ BoundBoxOfStarbursts[alignLine, ggData, dragInProgress]; }; alignCircle: AlignmentCircle => { thisBox _ BoundBoxOfStarbursts[alignCircle, ggData, dragInProgress]; }; alignPoint: AlignmentPoint => { IF alignPoint.curve1 # NIL THEN thisBox _ BoundBoxOfStarbursts[alignPoint.curve1.shape, ggData, dragInProgress] ELSE thisBox _ GGBoundBox.NullBoundBox[]; IF alignPoint.curve2 # NIL THEN GGBoundBox.EnlargeByBox[thisBox, BoundBoxOfStarbursts[alignPoint.curve2.shape, ggData, dragInProgress]]; }; ENDCASE => thisBox _ GGBoundBox.NullBoundBox[]; }; BoundBoxOfStarbursts: PROC [alignObj: AlignmentObject, ggData: GGData, dragInProgress: BOOL] RETURNS [box: BoundBox] = { InfiniteBox: PROC RETURNS [box: BoundBox] = { box _ GGBoundBox.CreateBoundBox[0,0,0,0,FALSE,TRUE]; }; WITH alignObj SELECT FROM alignLine: AlignmentLine => { box _ GGBoundBox.NullBoundBox[]; FOR list: LIST OF Point _ alignLine.triggerPoints, list.rest UNTIL list = NIL DO GGBoundBox.EnlargeByBox[box, BoundBoxOfStarburst[list.first, ggData.camera.cpScale]]; ENDLOOP; }; alignCircle: AlignmentCircle => { circle: Circle _ alignCircle.circle; box _ BoundBoxOfStarburst[circle.origin, ggData.camera.cpScale]; }; sliceD: SliceDescriptor => { selectedD: SliceDescriptor _ GGSelect.FindSelectedSlice[sliceD.slice, normal]; selectedParts: SliceParts _ IF selectedD = NIL THEN NIL ELSE selectedD.parts; <> box _ GGSliceOps.GetTightBox[sliceD.slice, selectedParts]; GGBoundBox.EnlargeByOffset[box, GGModelTypes.halfJointSize*ggData.camera.cpScale + 1.0]; }; ENDCASE => box _ InfiniteBox[]; }; BoundBoxOfStarburst: PROC [point: Point, cpScale: REAL] RETURNS [box: BoundBox] = { halfBox: REAL _ 7.0*cpScale + 1.0; box _ GGBoundBox.CreateBoundBox[loX: point.x - halfBox, loY: point.y - halfBox, hiX: point.x + halfBox, hiY: point.y + halfBox]; }; caretSize: REAL ~ MAX[GGCaret.caretWidth, GGCaret.caretHeight] + 1.0; CaretBox: PROC [ggData: GGData] RETURNS [bBox: BoundBox] = { <> scale: REAL ~ 1.0/GGState.GetBiScrollersScale[ggData]; scaledSize: REAL ~ scale*caretSize; point: Point ~ GGCaret.GetPoint[ggData.caret]; bBox _ GGBoundBox.CreateBoundBox[point.x-scaledSize, point.y-scaledSize, point.x+scaledSize, point.y+scaledSize]; }; anchorSize: REAL ~ MAX[GGCaret.anchorWidth, GGCaret.anchorHeight] + 1.0; AnchorBox: PROC [ggData: GGData] RETURNS [bBox: BoundBox] = { <> scale: REAL ~ 1.0/GGState.GetBiScrollersScale[ggData]; scaledSize: REAL ~ scale*anchorSize; point: Point ~ GGCaret.GetPoint[ggData.anchor]; bBox _ GGBoundBox.CreateBoundBox[point.x-scaledSize, point.y-scaledSize, point.x+scaledSize, point.y+scaledSize]; }; CPBox: PROC [ggData: GGData] RETURNS [bBox: BoundBox] = { <> bBox _ SelectedCPsBox[ggData]; GGBoundBox.EnlargeByBox[bBox: bBox, by: HotCPsBox[ggData]]; }; SelectedCPsBox: PROC [ggData: GGData] RETURNS [bBox: BoundBox] = { <> bBox _ SelectionCPBoundBox[ggData, normal]; }; HotCPsBox: PROC [ggData: GGData] RETURNS [bBox: BoundBox] = { <> bBox _ SelectionCPBoundBox[ggData, hot]; }; SelectionCPBoundBox: PROC [ggData: GGData, selectClass: GGScene.SelectionClass] RETURNS [bBox: BoundBox] = { <> DoEnlargeBox: PROC [childD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGBoundBox.EnlargeByBox[bBox: bBox, by: GGSliceOps.GetTightBox[childD.slice, NIL]]; }; bBox _ GGBoundBox.NullBoundBox[]; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, DoEnlargeBox, selectClass]; <> IF selectClass = hot THEN GGBoundBox.EnlargeByOffset[bBox, 3.0*GGModelTypes.halfHotJointSize*ggData.camera.cpScale + 1.0] ELSE GGBoundBox.EnlargeByOffset[bBox, 3.0*GGModelTypes.halfJointSize*ggData.camera.cpScale + 1.0]; }; MovingPartsBox: PROC [ggData: GGData] RETURNS [movingBox: BoundBox] = { <> dragBox: BoundBox _ MovingBoundBox[ggData, drag, ggData.drag.transform]; movingBox _ MovingBoundBox[ggData, rubber, ggData.drag.transform]; GGBoundBox.EnlargeByBox[movingBox, dragBox]; }; MovingBoundBox: PROC [ggData: GGData, type: MovingPartType, transform: ImagerTransformation.Transformation _ NIL] RETURNS [movingBox: BoundBox] = { <> EnlargeMovingBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { movingD: SliceDescriptor ~ MovingPart[sliceD.slice, sliceD.parts, ggData.drag.editConstraints, ggData.drag.bezierDrag, type]; IF movingD.parts#NIL AND NOT GGSliceOps.IsEmptyParts[movingD] THEN { thisBox: BoundBox ~ IF transform=NIL THEN GGSliceOps.GetBoundBox[movingD.slice, movingD.parts] ELSE GGSliceOps.GetTransformedBoundBox[sliceD.slice, sliceD.parts, movingD.parts, transform]; GGBoundBox.EnlargeByBox[bBox: movingBox, by: thisBox]; } }; movingBox _ GGBoundBox.NullBoundBox[]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, EnlargeMovingBox, normal]; }; StartCaretPos: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> <> <> DrawSandwich[screen, ggData, buffer, bounds]; }; StartCaretPosBounds: BBoxProc = { <> <> ggData.refresh.dragInProgress _ FALSE; ggData.refresh.caretIsMoving _ TRUE; <> <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into]; }; CaretMovedStatic: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> PaintAllPlanes[screen, ggData, buffer, bounds]; }; CaretMovedStaticBounds: BBoxProc = { <> <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into]; }; CaretMoved: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> IF GGSelect.NoSelections[ggData.scene, normal] AND GGSelect.NoSelections[ggData.scene, hot] AND bounds.null THEN RETURN; PaintAllPlanes[screen, ggData, buffer, bounds]; }; CaretMovedBounds: BBoxProc = { <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, into: into]; }; FilterLists: TYPE = REF FilterListsObj; FilterListsObj: TYPE = GGState.FilterListsObj; EmptyFilterLists: PROC [filterLists: FilterLists] RETURNS [BOOL] = INLINE { RETURN[filterLists.onSlopes=NIL AND filterLists.onRadii = NIL AND filterLists.onAngles=NIL AND filterLists.onDistances = NIL]; }; AlignmentsVisible: PROC [ggData: GGData] RETURNS [visible: BOOL _ TRUE] = { IF NOT GGState.GetShowAlignments[ggData] THEN RETURN[FALSE]; IF EmptyFilterLists[GGState.GetFilterLists[ggData]] THEN RETURN[FALSE]; IF GGSelect.NoSelections[ggData.scene, hot] AND NOT GGCaret.Exists[ggData.anchor] THEN RETURN[FALSE]; }; StartMotion: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> IF AlignmentsVisible[ggData] THEN DrawSandwich[screen, ggData, buffer] <> ELSE DrawSandwich[screen, ggData, buffer, bounds]; }; StartMotionBounds: BBoxProc = { <> IF ggData.refresh.oldTransform = NIL THEN ggData.refresh.oldTransform _ ImagerTransformation.Copy[identity] ELSE ggData.refresh.oldTransform^ _ identity^; ggData.refresh.dragInProgress _ TRUE; ggData.refresh.caretIsMoving _ TRUE; IF AlignmentsVisible[ggData] THEN box _ NullFromInto[into] <> ELSE box _ GetAFullBox[ggData: ggData, dragInProgress: TRUE, caret: TRUE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, attractor: TRUE, into: into]; }; identity: ImagerTransformation.Transformation = ImagerTransformation.Scale[1.0]; StartCopyAndDrag: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { DrawSandwich[screen, ggData, buffer, bounds]; }; StartCopyAndDragBounds: BBoxProc = { ggData.refresh.dragInProgress _ TRUE; ggData.refresh.caretIsMoving _ TRUE; box _ GetAFullBox[ggData: ggData, dragInProgress: TRUE, caret: TRUE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, attractor: TRUE, into: into]; }; DuringCaretPos: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { DrawSandwich[screen, ggData, buffer, bounds]; }; DuringCaretPosBounds: BBoxProc = { ggData.refresh.dragInProgress _ FALSE; ggData.refresh.caretIsMoving _ TRUE; box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into]; }; DuringDrag: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> DrawSandwich[screen, ggData, buffer, bounds]; }; DuringDragBounds: BBoxProc = { ggData.refresh.dragInProgress _ TRUE; ggData.refresh.caretIsMoving _ TRUE; box _ GetAFullBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE, into: into]; }; <> <> <<};>> <<>> <> <> <> <> <<};>> <<>> FinishedAdding: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, ggData.refresh.addedObject]; PaintAllPlanes[screen, ggData, buffer, bounds]; }; FinishedAddingBounds: BBoxProc = { <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE, alignments: TRUE, into: into]; }; RepairBackground: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> <> RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox]; }; RepairBackgroundBounds: BBoxProc = { box _ ggData.refresh.beforeBox }; EndMotion: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { <> BackmostSelectedSlice: PROC [scene: Scene] RETURNS [backmost: Slice _ NIL] = { FindBackmost: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { sliceD: SliceDescriptor _ GGSelect.FindSelectedSlice[slice, normal]; IF sliceD#NIL THEN { backmost _ sliceD.slice; done _ TRUE; }; }; [] _ GGScene.WalkSlices[scene, first, FindBackmost]; }; overObject: Slice ~ BackmostSelectedSlice[ggData.scene]; RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, overObject]; PaintAllPlanes[screen, ggData, buffer, bounds]; }; EndMotionBounds: BBoxProc = { <> <> box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, alignments: TRUE, into: into]; }; <> DrawAttractorFeedback: PROC [dc: Imager.Context, ggData: GGData, dragInProgress, caretIsMoving: BOOL] = { <> scene: Scene _ ggData.scene; camera: Camera _ ggData.camera; attractor: REF ANY _ GGCaret.GetAttractor[ggData.caret]; DrawAttractor[dc, scene, camera, ggData, dragInProgress, caretIsMoving, attractor]; }; DrawAttractor: PROC [dc: Imager.Context, scene: Scene, camera: Camera, ggData: GGData, dragInProgress, caretIsMoving: BOOL, attractor: REF ANY] = { IF attractor#NIL THEN { WITH attractor SELECT FROM sliceD: SliceDescriptor => { selectedD: SliceDescriptor _ GGSelect.FindSelectedSlice[sliceD.slice, normal]; selectedParts: SliceParts _ IF selectedD = NIL THEN NIL ELSE selectedD.parts; GGSliceOps.DrawAttractorFeedback[sliceD.slice, sliceD.parts, selectedParts, dragInProgress, dc, camera, ggData.drag.editConstraints]; }; ENDCASE => IF caretIsMoving THEN { zip: Draw2d.Zip _ Draw2d.GetZip[dc]; WITH attractor SELECT FROM alignLine: AlignmentLine => { <> <> Imager.SetColor[dc, Imager.black]; DrawStarbursts[dc, alignLine, scene, camera, ggData, dragInProgress, zip]; <> }; alignCircle: AlignmentCircle => { DrawStarbursts[dc, alignCircle, scene, camera, ggData, dragInProgress, zip]; }; alignPoint: AlignmentPoint => { IF alignPoint.curve1 # NIL THEN DrawStarbursts[dc, alignPoint.curve1.shape, scene, camera, ggData, dragInProgress, zip]; IF alignPoint.curve2 # NIL THEN DrawStarbursts[dc, alignPoint.curve2.shape, scene, camera, ggData, dragInProgress, zip]; }; ENDCASE; Draw2d.ReleaseZip[zip]; }; }; }; DrawStarbursts: PROC [dc: Imager.Context, alignObj: AlignmentObject, scene: Scene, camera: Camera, ggData: GGData, dragInProgress: BOOL, zip: Draw2d.Zip] = { WITH alignObj SELECT FROM alignLine: AlignmentLine => { FOR list: LIST OF Point _ alignLine.triggerPoints, list.rest UNTIL list = NIL DO GGShapes.DrawStarburst[dc, list.first, camera.cpScale, zip]; ENDLOOP; }; alignCircle: AlignmentCircle => { circle: Circle _ alignCircle.circle; GGShapes.DrawStarburst[dc, circle.origin, camera.cpScale, zip]; }; sliceD: SliceDescriptor => { selectedD: SliceDescriptor _ GGSelect.FindSelectedSlice[sliceD.slice, normal]; selectedParts: SliceParts _ IF selectedD = NIL THEN NIL ELSE selectedD.parts; GGSliceOps.DrawAttractorFeedback[sliceD.slice, sliceD.parts, selectedParts, dragInProgress, dc, camera, ggData.drag.editConstraints]; }; ENDCASE; }; MemberTraj: PROC [ref: Traj, list: LIST OF Traj] RETURNS [BOOL] = { FOR tl: LIST OF Traj _ list, tl.rest UNTIL tl = NIL DO IF tl.first = ref THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; AllSelectedSlices: PROC [scene: Scene] RETURNS [selectedList: LIST OF Slice _ NIL] = { <> ptr: LIST OF Slice; AddToList: PROC [sd: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { [selectedList, ptr] _ GGUtility.AddSlice[sd.slice, selectedList, ptr]; }; AddUnique: PROC [sd: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF NOT GGSelect.IsSelectedInPart[sd.slice, scene, hot] THEN [selectedList, ptr] _ GGUtility.AddSlice[sd.slice, selectedList, ptr]; }; [selectedList, ptr] _ GGUtility.StartSliceList[]; [] _ GGScene.WalkSelectedSlices[scene, first, AddToList, hot]; [] _ GGScene.WalkSelectedSlices[scene, first, AddUnique, normal]; }; DrawCpsOfSelectedSlices: PROC [dc: Imager.Context, scene: Scene, camera: Camera, dragInProgress, caretIsMoving: BOOL] = { CountSelections: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { CountPoints: GGModelTypes.PointWalkProc = { sliceCount _ sliceCount + 1; done _ sliceCount > 20; }; GGSliceOps.WalkPointsInDescriptor[sliceD, CountPoints]; done _ sliceCount > 20; }; normalSliceD, hotSliceD: SliceDescriptor; normalParts, hotParts: SliceParts; slice: Slice; quick: BOOL _ FALSE; hotCount, normalCount, sliceCount: NAT _ 0; IF caretIsMoving OR dragInProgress THEN RETURN; [] _ GGScene.WalkSelectedSlices[scene, first, CountSelections, normal]; normalCount _ sliceCount; sliceCount _ 0; [] _ GGScene.WalkSelectedSlices[scene, first, CountSelections, hot]; hotCount _ sliceCount; quick _ normalCount > 20 OR hotCount > 20; FOR sList: LIST OF Slice _ AllSelectedSlices[scene], sList.rest UNTIL sList=NIL DO slice _ sList.first; normalSliceD _ GGSelect.FindSelectedSlice[slice, normal]; hotSliceD _ GGSelect.FindSelectedSlice[slice, hot]; normalParts _ IF normalSliceD # NIL THEN normalSliceD.parts ELSE NIL; hotParts _ IF hotSliceD # NIL THEN hotSliceD.parts ELSE NIL; GGSliceOps.DrawSelectionFeedback[slice, normalParts, hotParts, dc, camera, dragInProgress, caretIsMoving, FALSE, quick]; ENDLOOP; IF quick THEN { box: BoundBox _ GGScene.SelectionBoxOfSelected[scene]; Imager.SetColor[dc, Imager.black]; GGShapes.DrawBoundBox[dc, box]; }; }; MemberSlice: PROC [ref: SliceDescriptor, list: LIST OF SliceDescriptor] RETURNS [BOOL] = { FOR tl: LIST OF SliceDescriptor _ list, tl.rest UNTIL tl = NIL DO IF tl.first = ref THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; <<>> <> <<>> DrawObjects: PROC [screen: Imager.Context, ggData: GGData] = { <> scene: Scene _ ggData.scene; camera: Camera _ ggData.camera; DoDrawObject: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { GGSliceOps.DrawParts[slice, NIL, screen, camera, FALSE]; }; Imager.SetColor[screen, Imager.black]; [] _ GGScene.WalkSlices[scene, first, DoDrawObject]; }; DrawObjectsFiltered: PROC [dc: Imager.Context, ggData: GGData, filter: GGBoundBox.BoundBox, excludeOverlay: BOOL _ FALSE, overObject: Slice _ NIL] = { <> OutsideOf: PROC [test, bound: GGBoundBox.BoundBox] RETURNS [BOOL] = { RETURN[ test.hiX < bound.loX OR test.loX > bound.hiX OR test.hiY < bound.loY OR test.loY > bound.hiY ]; -- these tests may have to be <= or >= }; DrawObjectsFilteredAux: PROC = { -- need to clip to filter, then image noFilter: BOOL _ FALSE; DoSkipAndDraw: PROC [thisSlice: Slice] RETURNS [done: BOOL _ FALSE] = { IF timeToDraw THEN { IF excludeOverlay AND thisSlice.onOverlay THEN RETURN; IF noFilter OR NOT OutsideOf[GGSliceOps.GetBoundBox[thisSlice, NIL], filter] THEN GGSliceOps.DrawParts[thisSlice, NIL, dc, camera, FALSE]; } ELSE { IF thisSlice = overObject THEN { timeToDraw _ TRUE; IF excludeOverlay AND thisSlice.onOverlay THEN RETURN; IF noFilter OR NOT OutsideOf[GGSliceOps.GetBoundBox[thisSlice, NIL], filter] THEN GGSliceOps.DrawParts[thisSlice, NIL, dc, camera, FALSE]; }; }; }; <> noFilter _ filter.infinite OR filter.hiX-filter.loX=0.0 OR filter.hiY-filter.loY=0.0; IF NOT noFilter THEN GGBoundBox.Clip[dc: dc, bBox: filter]; Imager.SetColor[dc, Imager.black]; [] _ GGScene.WalkSlices[scene, first, DoSkipAndDraw]; }; timeToDraw: BOOL _ overObject = NIL; scene: Scene _ ggData.scene; camera: Camera _ ggData.camera; IF filter=NIL OR filter.null THEN RETURN; Imager.DoSave[dc, DrawObjectsFilteredAux]; }; DrawCaret: PROC [dc: Imager.Context, caret: Caret, color: Imager.Color, scale: REAL] = { IF GGCaret.Exists[caret] THEN { caretPos: Point _ GGCaret.GetPoint[caret]; normalVec: Vector _ GGCaret.GetNormal[caret]; Imager.SetColor[dc, color]; GGShapes.DrawCaret[dc, caretPos, normalVec, scale]; }; }; DrawAnchor: PROC [dc: Imager.Context, anchor: Caret, color: Imager.Color, scale: REAL] = { IF GGCaret.Exists[anchor] THEN { anchorPos: Point _ GGCaret.GetPoint[anchor]; normalVec: Vector _ GGCaret.GetNormal[anchor]; Imager.SetColor[dc, color]; GGShapes.DrawAnchor[dc, anchorPos, normalVec, scale]; }; }; <<>> <> <> <<>> <> <> <<>> PaintSpot: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN RETURN; Imager.SetColor[screen, Imager.black]; GGShapes.DrawSpot[screen, ggData.refresh.spotPoint]; }; PaintHitLine: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN RETURN; Imager.SetColor[screen, Imager.black]; Imager.SetStrokeEnd[screen, round]; Imager.MaskVector[screen, [ggData.refresh.spotPoint.x, ggData.refresh.spotPoint.y], [ggData.refresh.hitPoint.x, ggData.refresh.hitPoint.y]]; GGShapes.DrawFilledLoLeftSquare[screen, ggData.refresh.spotPoint, 3.0]; }; PaintOddHitLine: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN RETURN; Imager.SetColor[screen, Imager.black]; Imager.SetStrokeEnd[screen, round]; Imager.MaskVector[screen, [ggData.refresh.spotPoint.x, ggData.refresh.spotPoint.y], [ggData.refresh.hitPoint.x, ggData.refresh.hitPoint.y]]; GGShapes.DrawCP[screen, ggData.refresh.spotPoint, 1.0]; }; PaintAlign: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { GGAlign.DrawAlignBagRegardless[screen, ggData.hitTest.alignBag, ggData]; }; }; PaintPolylines: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { PaintPolylinesAux: PROC = { DoDrawPolyline: PROC [traj: Slice] RETURNS [done: BOOL _ FALSE] = { GGTraj.DrawPolyline[screen, traj]; }; [] _ GGScene.WalkSlices[ggData.scene, leaf, DoDrawPolyline, $Traj]; }; Imager.DoSave[screen, PaintPolylinesAux]; }; }; PaintBoundBoxes: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { PaintBoundBoxesAux: PROC = { <> bBoxGen: BoundBoxGenerator ~ GGScene.BoundBoxesInScene[ggData.scene]; FOR box: BoundBox _ GGScene.NextBox[bBoxGen], GGScene.NextBox[bBoxGen] UNTIL box = NIL DO GGShapes.DrawBoundBox[screen, box]; ENDLOOP; }; Imager.DoSave[screen, PaintBoundBoxesAux]; }; }; PaintTightBoxes: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { PaintTightBoxesAux: PROC = { DoDrawBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { box: BoundBox ~ GGSliceOps.GetTightBox[sliceD.slice, sliceD.parts]; GGShapes.DrawBoundBox[screen, box]; }; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, DoDrawBox, normal]; }; Imager.DoSave[screen, PaintTightBoxesAux]; }; }; PaintOutlineBoxes: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { PaintBoundBoxesAux: PROC = { DoDrawBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { bBox: BoundBox _ GGSliceOps.GetBoundBox[sliceD.slice, sliceD.parts]; GGShapes.DrawBoundBox[screen, bBox]; }; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, DoDrawBox, normal, $Outline]; }; Imager.DoSave[screen, PaintBoundBoxesAux]; }; }; PaintSelectionBox: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL _ FALSE, bounds: BoundBox] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { PaintSelectionBoxAux: PROC = { <> box: BoundBox ~ GGScene.BoundBoxOfSelected[ggData.scene]; IF NOT box.null THEN GGShapes.DrawBoundBox[screen, box]; }; Imager.DoSave[screen, PaintSelectionBoxAux]; }; }; MovingPartType: TYPE ~ {background, overlay, rubber, drag}; MovingPart: PROC [slice: Slice, selectedParts: SliceParts, editConstraints: EditConstraints, bezierDrag: BezierDragRecord, type: MovingPartType] RETURNS [SliceDescriptor] = { <> RETURN[SELECT type FROM background => GGSliceOps.MovingParts[slice, selectedParts, editConstraints, bezierDrag].background, overlay => GGSliceOps.MovingParts[slice, selectedParts, editConstraints, bezierDrag].overlay, rubber => GGSliceOps.MovingParts[slice, selectedParts, editConstraints, bezierDrag].rubber, drag => GGSliceOps.MovingParts[slice, selectedParts, editConstraints, bezierDrag].drag, ENDCASE => ERROR]; }; DrawMovingBox: PROC [screen: Imager.Context, ggData: GGData, type: MovingPartType] = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { box: BoundBox _ MovingBoundBox[ggData, type]; IF NOT box.null THEN GGShapes.DrawBoundBox[screen, box]; }; }; <<>> << <> EndFourPoints: PROC [traj: Traj] RETURNS [firstPoint, secondPoint, secondToLastPoint, lastPoint: Point, firstWidth, lastWidth: REAL] = { seg: Segment; cpCount: NAT; seg _ GGTraj.FetchSegment[traj, 0]; firstWidth _ seg.strokeWidth; firstPoint _ seg.lo; cpCount _ seg.class.controlPointCount[seg]; IF cpCount > 0 THEN { secondPoint _ seg.class.controlPointGet[seg, 0]; } ELSE { secondPoint _ seg.hi; }; seg _ GGTraj.FetchSegment[traj, GGTraj.HiSegment[traj]]; lastWidth _ seg.strokeWidth; lastPoint _ seg.hi; cpCount _ seg.class.controlPointCount[seg]; IF cpCount > 0 THEN { secondToLastPoint _ seg.class.controlPointGet[seg, cpCount-1]; } ELSE { secondToLastPoint _ seg.lo; }; }; ExcludeArrows: PROC [screen: Imager.Context, traj: Traj] = { OPEN Vectors2d; ClipPath: Imager.PathProc = { moveTo[Add[Add[tip, Scale[perp, -halfWidth]], Scale[axis, thisWidth/2.0]]]; lineTo[Add[Add[tip, Scale[perp, halfWidth]], Scale[axis, thisWidth/2.0]]]; lineTo[Sub[tip, Add[Scale[axis, height], Scale[perp, halfWidth]]]]; lineTo[Sub[tip, Add[Scale[axis, height], Scale[perp, -halfWidth]]]]; lineTo[Add[tip, Scale[perp, -halfWidth]]]; }; firstPoint, secondPoint, secondToLastPoint, lastPoint, tip, base: Point; firstWidth, lastWidth, thisWidth, height, halfWidth: REAL; axis, perp: Vector; trajData: TrajData _ NARROW[traj.data]; IF NOT trajData.loArrow AND NOT trajData.hiArrow THEN RETURN; [firstPoint, secondPoint, secondToLastPoint, lastPoint, firstWidth, lastWidth] _ EndFourPoints[traj]; IF trajData.loArrow THEN { thisWidth _ firstWidth; [height, halfWidth] _ GGShapes.ArrowSize[thisWidth]; tip _ firstPoint; base _ secondPoint; axis _ Vectors2d.Normalize[Vectors2d.Sub[tip, base]]; perp _ [axis.y, -axis.x]; Imager.Clip[screen, ClipPath, FALSE, TRUE]; }; IF trajData.hiArrow THEN { thisWidth _ lastWidth; [height, halfWidth] _ GGShapes.ArrowSize[thisWidth]; tip _ lastPoint; base _ secondToLastPoint; axis _ Vectors2d.Normalize[Vectors2d.Sub[tip, base]]; perp _ [axis.y, -axis.x]; Imager.Clip[screen, ClipPath, FALSE, TRUE]; }; }; DrawArrows: PROC [screen: Imager.Context, traj: Traj, ggData: GGData] = { firstPoint, secondPoint, secondToLastPoint, lastPoint: Point; firstWidth, lastWidth: REAL; trajData: TrajData _ NARROW[traj.data]; IF NOT trajData.loArrow AND NOT trajData.hiArrow THEN RETURN; [firstPoint, secondPoint, secondToLastPoint, lastPoint, firstWidth, lastWidth] _ EndFourPoints[traj]; IF trajData.loArrow THEN GGShapes.DrawArrow[screen, firstPoint, secondPoint, firstWidth]; IF trajData.hiArrow THEN GGShapes.DrawArrow[screen, lastPoint, secondToLastPoint, lastWidth]; }; >> <> <<>> <> <<>> SnapShot: PUBLIC PROC [dc: Imager.Context, ggData: GGData] = { <> <> boundRect: Rectangle _ [x: 0.0, y: 0.0, w: 8.5*72.0, h: 11.0*72.0]; -- kludge SnapshotBackground[dc, ggData]; [] _ RefreshOverlay[dc, boundRect, ggData]; -- fortunately, RefreshOverlay doesn't use boundRect }; SnapshotBackground: PROC [dc: Imager.Context, ggData: GGData] = { <> scene: Scene _ ggData.scene; camera: Camera _ ggData.camera; scale: REAL _ 1.0/GGState.GetBiScrollersScale[ggData]; DoDrawParts: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { IF slice.onOverlay THEN RETURN ELSE GGSliceOps.DrawParts[slice, NIL, dc, camera, FALSE]; }; <> [] _ GGScene.WalkSlices[scene, first, DoDrawParts]; DrawAttractorFeedback[dc, ggData, ggData.refresh.dragInProgress, ggData.refresh.caretIsMoving]; GGAlign.FlushLineTable[ggData]; -- need to fully refresh foreground when GGAlign.DrawAlignBagRegardless is called GGAlign.DrawAlignBagRegardless[dc, ggData.hitTest.alignBag, ggData]; DrawCaret[dc, ggData.caret, Imager.black, scale]; DrawAnchor[dc, ggData.anchor, Imager.black, scale]; }; InterpressEntireScene: PUBLIC PROC [dc: Imager.Context, ggData: GGData] = { DrawObjects[dc, ggData]; }; <<>> <> <<>> MoveToOverlay: PUBLIC PROC [sliceD: SliceDescriptor, ggData: GGData] = { IF OnOverlay[sliceD, ggData] THEN ERROR; sliceD.slice.onOverlay _ TRUE; ggData.refresh.overlayList _ GGUtility.AppendSliceDescriptorList[LIST[sliceD], ggData.refresh.overlayList]; ggData.refresh.orderedOverlayList _ NIL; }; MoveAllSelectedToOverlay: PUBLIC PROC [ggData: GGData, selectClass: SelectionClass] = { DoMoveToOverlay: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { MoveToOverlay[sliceD, ggData]; }; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, DoMoveToOverlay, selectClass]; }; MoveToBackground: PUBLIC PROC [sliceD: SliceDescriptor, ggData: GGData] = { IF NOT OnOverlay[sliceD, ggData] THEN RETURN; ggData.refresh.overlayList _ GGUtility.DeleteSliceDescriptorFromList[sliceD, ggData.refresh.overlayList]; sliceD.slice.onOverlay _ FALSE; ggData.refresh.orderedOverlayList _ NIL; }; MoveOverlayToBackground: PUBLIC PROC [ggData: GGData] = { FOR overlayList: LIST OF SliceDescriptor _ ggData.refresh.overlayList, overlayList.rest UNTIL overlayList = NIL DO overlayList.first.slice.onOverlay _ FALSE; ENDLOOP; ggData.refresh.overlayList _ NIL; ggData.refresh.orderedOverlayList _ NIL; }; EmptyOverlay: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { RETURN[ggData.refresh.overlayList = NIL]; }; OnOverlay: PROC [sliceD: SliceDescriptor, ggData: GGData] RETURNS [BOOL] = { RETURN[sliceD.slice.onOverlay]; }; OrderOverlayList: PROC [ggData: GGData] RETURNS [orderedList: LIST OF SliceDescriptor] = { <> FindOverlayedD: PROC [slice: Slice] RETURNS [sliceD: SliceDescriptor] = { FOR ov: LIST OF SliceDescriptor _ ggData.refresh.overlayList, ov.rest UNTIL ov=NIL DO IF ov.first.slice=slice THEN RETURN[ov.first]; ENDLOOP; RETURN[NIL]; }; DoBuildList: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { IF slice.onOverlay THEN { sliceD: SliceDescriptor _ FindOverlayedD[slice]; IF sliceD = NIL THEN sliceD _ GGSliceOps.NewParts[slice, NIL, topLevel]; [orderedList, finger] _ GGUtility.AddSliceDescriptor[sliceD, orderedList, finger]; }; }; finger: LIST OF SliceDescriptor; [orderedList, finger] _ GGUtility.StartSliceDescriptorList[]; [] _ GGScene.WalkSlices[ggData.scene, first, DoBuildList]; }; <<>> <> <<>> AdjustContextForDrawBits: PROC [dc: Imager.Context, ggData: GGData] = { <> viewerToClient: Imager.Transformation _ GGState.GetBiScrollersTransforms[ggData].viewerToClient; Imager.ConcatT[dc, viewerToClient]; }; SetStartBox: PUBLIC PROC [ggData: GGData, dragInProgress: BOOL, caret, anchor, selectedParts, movingParts, selectedCPs, hotCPs, attractor, alignments: BOOL _ FALSE] = { ggData.refresh.beforeBox _ GetABox[ggData, dragInProgress, caret, anchor, selectedParts, movingParts, selectedCPs, hotCPs, attractor, alignments, ggData.refresh.beforeBox]; }; EnlargeStartBox: PUBLIC PROC [ggData: GGData, by, andBy: BoundBox] = { IF by#NIL THEN GGBoundBox.EnlargeByBox[ggData.refresh.beforeBox, by]; IF andBy#NIL THEN GGBoundBox.EnlargeByBox[ggData.refresh.beforeBox, andBy]; }; NullStartBox: PUBLIC PROC [ggData: GGData] = { IF ggData.refresh.beforeBox=NIL THEN ggData.refresh.beforeBox _ GGBoundBox.NullBoundBox[] ELSE { ggData.refresh.beforeBox.null _ TRUE; ggData.refresh.beforeBox.infinite _ FALSE; }; }; GetAFullBox: PROC [ggData: GGData, dragInProgress: BOOL, caret, anchor, selectedParts, movingParts, selectedCPs, hotCPs, attractor, alignments: BOOL _ FALSE, into: BoundBox] RETURNS [box: BoundBox] = { box _ GetABox[ggData, dragInProgress, caret, anchor, selectedParts, movingParts, selectedCPs, hotCPs, attractor, alignments, into]; GGBoundBox.EnlargeByBox[bBox: box, by: ggData.refresh.beforeBox]; }; GetABox: PUBLIC PROC [ggData: GGData, dragInProgress: BOOL, caret, anchor, selectedParts, movingParts, selectedCPs, hotCPs, attractor, alignments: BOOL _ FALSE, into: BoundBox] RETURNS [box: BoundBox] = { IF into#NIL THEN { box _ into; box.null _ TRUE; box.infinite _ FALSE; } ELSE box _ GGBoundBox.NullBoundBox[]; IF alignments THEN { GGBoundBox.EnlargeByBox[box, AlignmentsBox[ggData]]; IF box.infinite THEN RETURN; }; IF caret THEN GGBoundBox.EnlargeByBox[box, CaretBox[ggData]]; IF anchor THEN GGBoundBox.EnlargeByBox[box, AnchorBox[ggData]]; IF selectedParts THEN GGBoundBox.EnlargeByBox[box, GGScene.BoundBoxOfSelected[ggData.scene, normal]]; IF movingParts THEN GGBoundBox.EnlargeByBox[box, MovingPartsBox[ggData]]; IF selectedCPs THEN GGBoundBox.EnlargeByBox[box, SelectedCPsBox[ggData]]; IF hotCPs THEN GGBoundBox.EnlargeByBox[box, HotCPsBox[ggData]]; IF attractor THEN GGBoundBox.EnlargeByBox[box, AttractorBox[ggData, dragInProgress]]; }; AAPRegister: PROC [key: ATOM, paint: AAPProc, bBox: BBoxProc _ NIL] = { <> aapEntry: AAPEntry _ NEW[AAPEntryRep _ [paint, bBox] ]; [] _ RefTab.Store[aapTable, key, aapEntry]; -- overwrites earlier entries with same key }; AAPInit: PROC = { aapTable _ RefTab.Create[]; }; AAPRegistrations: PROC = { <> <> AAPRegister[$None, NIL]; AAPRegister[$PaintSceneNoBuffer, PaintSceneNoBuffer]; AAPRegister[$PaintEntireScene, AapPaintEntireScene]; AAPRegister[$ViewersPaintEntireScene, AapPaintEntireScene]; AAPRegister[$Everything, AapPaintEntireScene]; AAPRegister[$NewAlignmentsSelected, PaintAllPlanes]; AAPRegister[$NewAlignmentsDeselected, PaintAllPlanes]; AAPRegister[$PaintAllPlanes, PaintAllPlanes]; AAPRegister[$ViewersPaintAllPlanes, PaintAllPlanes]; AAPRegister[$SequencesMadeHot, PaintAllPlanes]; AAPRegister[$SequencesMadeCold, PaintAllPlanes]; <> AAPRegister[$SelectionChanged, DuringSelect, SelectionChangedBounds]; AAPRegister[$SelectionAndCaretChanged, DuringSelect, SelectionAndCaretChangedBounds]; AAPRegister[$FinishedAdding, FinishedAdding, FinishedAddingBounds]; AAPRegister[$CaretMoved, CaretMoved, CaretMovedBounds]; -- GGMouseEventImplA AAPRegister[$CaretMovedStatic, PaintAllPlanes, CaretMovedStaticBounds]; -- AAPRegister[$AnchorAdded, PaintAllPlanes, AnchorAddedBounds]; -- GGEventImplB AAPRegister[$AnchorRemoved, PaintAllPlanes, AnchorRemovedBounds]; -- GGEventImplB <> AAPRegister[$RepairBackground, RepairBackground, RepairBackgroundBounds]; -- GGMouseEventImplA AAPRegister[$StartMotion, StartMotion, StartMotionBounds]; -- GGMouseEventImplA AAPRegister[$StartCaretPos, StartCaretPos, StartCaretPosBounds]; -- GGMouseEventImplA AAPRegister[$StartCopyAndDrag, StartCopyAndDrag, StartCopyAndDragBounds]; -- GGMouseEventImplA <> AAPRegister[$DuringDrag, DuringDrag, DuringDragBounds]; -- GGMouseEventImplA AAPRegister[$DuringCaretPos, DuringCaretPos, DuringCaretPosBounds]; -- GGMouseEventImplA AAPRegister[$EndMotion, EndMotion, EndMotionBounds]; AAPRegister[$DuringSelect, DuringSelect, DuringSelectBounds]; AAPRegister[$EndSelect, DuringSelect, DuringSelectBounds]; -- used for CycleSelection AAPRegister[$ObjectChangedInPlace, ObjectChangedInPlace, ObjectChangedInPlaceBounds]; AAPRegister[$ObjectChangedBoundBoxProvided, ObjectChangedBoundBoxProvided, ObjectChangedBoundBoxProvidedBounds]; AAPRegister[$PaintInTotalBox, PaintInTotalBox, PaintInTotalBoxBounds]; AAPRegister[$ObjectAdded, ObjectAdded, ObjectAddedBounds]; <> <> <> AAPRegister[$PaintSpot, PaintSpot]; AAPRegister[$PaintHitLine, PaintHitLine]; AAPRegister[$PaintOddHitLine, PaintOddHitLine]; AAPRegister[$PaintAlign, PaintAlign]; AAPRegister[$PaintBoundBoxes, PaintBoundBoxes]; AAPRegister[$PaintPolylines, PaintPolylines]; AAPRegister[$PaintTightBoxes, PaintTightBoxes]; AAPRegister[$PaintOutlineBoxes, PaintOutlineBoxes]; AAPRegister[$PaintSelectionBox, PaintSelectionBox]; AAPRegister[$DrawBackgroundBox, DrawBackgroundBox]; AAPRegister[$DrawOverlayBox, DrawOverlayBox]; AAPRegister[$DrawRubberBox, DrawRubberBox]; AAPRegister[$DrawDragBox, DrawDragBox]; <> }; PaintSceneNoBuffer: AAPProc = { PaintEntireScene[screen, ggData, FALSE]; }; AapPaintEntireScene: AAPProc = { PaintEntireScene[screen, ggData, buffer]; }; <> <> <<};>> <<>> PaintInTotalBox: AAPProc = { RepairBackgroundInBoundBox[ggData, ggData.refresh.totalBox]; PaintAllPlanes[screen, ggData, buffer, ggData.refresh.totalBox]; }; PaintInTotalBoxBounds: BBoxProc = { box _ IF into#NIL THEN into ELSE GGBoundBox.NullBoundBox[]; box^ _ ggData.refresh.totalBox^; }; DrawBackgroundBox: AAPProc = { DrawMovingBox[screen, ggData, background]; }; DrawOverlayBox: AAPProc = { DrawMovingBox[screen, ggData, overlay]; }; DrawRubberBox: AAPProc = { DrawMovingBox[screen, ggData, rubber]; }; DrawDragBox: AAPProc = { DrawMovingBox[screen, ggData, drag]; }; <<AAPProc: AAPProc = {>> <<>> <<};>> AAPEntry: TYPE = REF AAPEntryRep; AAPEntryRep: TYPE = RECORD [aapProc: AAPProc, bBoxProc: BBoxProc]; AAPProc: TYPE = PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox]; BBoxProc: TYPE = PROC [ggData: GGData, whatHasChanged: ATOM, into: BoundBox] RETURNS [box: BoundBox]; aapTable: RefTab.Ref; -- keys are ATOM, vals are AAPEntry AAPInit[]; AAPRegistrations[]; END.