DIRECTORY BufferedRefresh, BufferedRefreshTypes, CodeTimer, Draw2d, Feedback, GGAlign, GGAlignTypes, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGCoreTypes, GGDragTypes, GGEmbedTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGUtility, GGWindow, Imager, ImagerColor, ImagerColorPrivate, ImagerTransformation, Lines2dTypes, Real, RefTab, Rope, SlackProcess, ViewerAbort; GGRefreshImpl: CEDAR MONITOR IMPORTS BufferedRefresh, CodeTimer, Draw2d, Feedback, GGAlign, GGBoundBox, GGCaret, GGOutline, GGParent, GGScene, GGSelect, GGSequence, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGUtility, GGWindow, Imager, ImagerColorPrivate, ImagerTransformation, RefTab, SlackProcess, ViewerAbort EXPORTS GGInterfaceTypes, GGRefresh, GGRefreshTypes = BEGIN Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes Sandwich: TYPE = BufferedRefreshTypes.Sandwich; SandwichObj: PUBLIC TYPE = BufferedRefreshTypes.SandwichObj; -- exported to GGRefreshTypes RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; 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; FilterLists: TYPE = GGAlignTypes.FilterLists; FilterListsObj: TYPE = GGAlignTypes.FilterListsObj; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = Lines2dTypes.Line; Point: TYPE = GGBasicTypes.Point; Rectangle: TYPE = Imager.Rectangle; RefreshProc: TYPE = BufferedRefresh.RefreshProc; 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, buffer, useBackingMap ! 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 }; ]; }; buffer: BOOL ¬ GGState.GetDoubleBuffer[ggData]; useBackingMap: BOOL ¬ GGState.GetUseBackingMap[ggData]; IF handleViewerAbort THEN ViewerAbort.CallWithAbortEnabled[ggData.controls.actionArea, PaintWithAbort] ELSE DoActionAreaPaint[screen, whatHasChanged, ggData, ggData.refresh.paintBox, buffer, useBackingMap]; }; RepaintArea: PUBLIC PROC [screen: Imager.Context, ggData: GGData, whatHasChanged: ATOM, bounds: BoundBox, buffer, useBackingMap: BOOL] = { DoActionAreaPaint[screen, whatHasChanged, ggData, bounds, buffer, useBackingMap]; }; PaintInParent: PUBLIC PROC [ggData: GGData, paintAction: ATOM] = { CodeTimer.StartInt[$PaintViewer, $Gargoyle]; IF paintAction # $None THEN { ggData.refresh.paintAction ¬ paintAction; GetPaintBox[ggData, paintAction, ggData.refresh.paintBox]; ggData.embed.paintProc[ggData, paintAction, ggData.refresh.paintBox, ggData.embed.clientData]; } ELSE ggData.refresh.paintAction ¬ $PaintEntireScene; 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, buffer, useBackingMap: BOOL] = { 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, buffer, useBackingMap, bounds]; } ELSE Feedback.PutF[ggData.router, oneLiner, $Complaint, "GGRefreshImpl doesn't know how to paint %g", [atom[whatHasChanged]] ]; }; CodeTimer.StopInt[$Gargoyle, $DoActionAreaPaint]; }; backgroundName: ATOM ~ $Background; foregroundName: ATOM ~ $Foreground; CreateSandwich: PUBLIC PROC [] RETURNS [sandwich: Sandwich] = { sandwich ¬ BufferedRefresh.CreateSandwich[LIST[ [backgroundName, TRUE, RefreshBackground, TRUE], -- 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, cx, cy: INTEGER ¬ 0] ~ { sandwich: Sandwich ~ ggData.refresh.sandwich; BufferedRefresh.FitSandwichToScreen[sandwich, cw, ch, screen, cx, cy]; }; 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, useBackingMap: 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]; IF (bounds=NIL OR bounds.infinite) THEN BufferedRefresh.DrawSandwich[ sandwich: ggData.refresh.sandwich, screen: IF ggData.refresh.suppressScreen THEN NIL ELSE screen, clientToViewer: clientToViewer, viewerToClient: viewerToClient, bkgndColor: GGScene.GetBackgroundColor[ggData.scene], clientData: ggData, ignoreBackingMap: NOT useBackingMap, noBuffer: NOT buffer] ELSE BufferedRefresh.DrawSandwich[ggData.refresh.sandwich, IF ggData.refresh.suppressScreen THEN NIL ELSE screen, clientToViewer, viewerToClient, ggData, GGScene.GetBackgroundColor[ggData.scene], NOT useBackingMap, 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, GGScene.GetBackgroundColor[ggData.scene], 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, useBackingMap: BOOL ¬ TRUE] = { CodeTimer.StartInt[$PaintEntireScene, $Gargoyle]; InvalidateBackground[ggData]; InvalidateForeground[ggData]; PaintAllPlanes[screen, ggData, buffer, useBackingMap, NIL]; CodeTimer.StopInt[$PaintEntireScene, $Gargoyle]; }; PaintEntireSceneDrag: PROC [screen: Imager.Context, ggData: GGData, buffer, useBackingMap: BOOL ¬ TRUE, bounds: BoundBox ¬ NIL] = { CodeTimer.StartInt[$PaintEntireSceneDrag, $Gargoyle]; InvalidateBackground[ggData]; InvalidateForeground[ggData]; IF ggData.state=$Main THEN { dip: BOOL _ ggData.refresh.dragInProgress; ggData.refresh.dragInProgress ¬ TRUE; DrawSandwich[screen, ggData, buffer, useBackingMap, bounds]; ggData.refresh.dragInProgress ¬ dip; } ELSE PaintAllPlanes[screen, ggData, buffer, useBackingMap, NIL]; CodeTimer.StopInt[$PaintEntireSceneDrag, $Gargoyle]; }; PaintAllPlanesDrag: PROC [screen: Imager.Context, ggData: GGData, buffer, useBackingMap: BOOL ¬ TRUE, bounds: BoundBox ¬ NIL] = { IsSelection: PROC [m: ATOM] RETURNS [BOOL] = INLINE { RETURN[m=$SelectWithBox OR m=$SelectJoint OR m=$ExtSelectJoint OR m=$SelectSegment OR m=$ExtSelectSegment OR m=$SelectTrajectory OR m=$ExtSelectTrajectory OR m=$SelectTopLevel OR m=$ExtSelectTopLevel OR m=$ExtendSelection OR m=$DeselectJoint OR m=$DeselectSegment OR m=$DeselectTrajectory OR m=$DeselectTopLevel]; }; CodeTimer.StartInt[$PaintAllPlanesDrag, $Gargoyle]; IF ggData.state=$Main AND NOT IsSelection[ggData.mouseMode] THEN { dip: BOOL _ ggData.refresh.dragInProgress; ggData.refresh.dragInProgress ¬ TRUE; DrawSandwich[screen, ggData, buffer, useBackingMap, bounds]; ggData.refresh.dragInProgress ¬ dip; } ELSE PaintAllPlanes[screen, ggData, buffer, useBackingMap, NIL]; CodeTimer.StopInt[$PaintAllPlanesDrag, $Gargoyle]; }; PaintAllPlanes: AAPProc = { ggData.refresh.caretIsMoving ¬ FALSE; ggData.refresh.dragInProgress ¬ FALSE; DrawSandwich[screen, ggData, buffer, useBackingMap, bounds]; }; RefreshBackground: RefreshProc = { ggData: GGData ~ NARROW[clientData]; color: Color _ GGScene.GetBackgroundColor[ggData.scene]; box: BoundBox ¬ GGBoundBox.BoundBoxFromRectangle[boundRect]; CodeTimer.StartInt[$RefreshBackground, $Gargoyle]; IF color#NIL THEN { WITH color SELECT FROM cc: ImagerColor.ConstantColor => IF ImagerColorPrivate.ConstantColorsEqual[ccWhite, cc] THEN GOTO NoBackground; ENDCASE; Imager.SetColor[dc, color]; Imager.MaskRectangle[dc, [box.loX, box.loY, box.hiX - box.loX, box.hiY - box.loY]]; EXITS NoBackground => NULL; }; DrawObjectsFiltered[dc: dc, ggData: ggData, filter: box, 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]; }; bigReal: REAL = 1000000.0; reallyBigReal: REAL = 10000000.0; infiniteRect: Imager.Rectangle = [-bigReal, -bigReal, reallyBigReal, reallyBigReal]; RepairBackgroundInBoundBox: PUBLIC PROC [ggData: GGData, bBox: BoundBox, eraseFirst: BOOL ¬ TRUE, overObject: Slice ¬ NIL] = { PaintObjectsInBox: RefreshProc = { BEGIN color: Color _ GGScene.GetBackgroundColor[ggData.scene]; IF eraseFirst AND color=NIL THEN GOTO WhiteBackground; IF eraseFirst AND color#NIL THEN { WITH color SELECT FROM cc: ImagerColor.ConstantColor => IF ImagerColorPrivate.ConstantColorsEqual[ccWhite, cc] THEN GOTO WhiteBackground; ENDCASE; Imager.SetColor[dc, color]; Imager.MaskRectangle[dc, IF bBox.infinite THEN infiniteRect ELSE [bBox.loX, bBox.loY, bBox.hiX - bBox.loX, bBox.hiY - bBox.loY]]; }; EXITS WhiteBackground => GGBoundBox.EraseWithinBoundBox[dc, bBox]; END; 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: AAPProc = { RepairBackgroundInBoundBox[ggData, bounds]; PaintAllPlanes[screen, ggData, buffer, useBackingMap, bounds]; }; ObjectChangedInPlaceBounds: BBoxProc = { box ¬ GetABox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, selectedParts: TRUE, into: into]; }; ObjectChangedBoundBoxProvided: AAPProc = { RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox]; IF AlignmentsVisible[ggData] THEN DrawSandwich[screen, ggData, buffer, useBackingMap] ELSE PaintAllPlanes[screen, ggData, buffer, useBackingMap, 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: AAPProc = { RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, ggData.refresh.addedObject]; PaintAllPlanes[screen, ggData, buffer, useBackingMap, 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: AAPProc = { DrawSandwich[screen, ggData, buffer, useBackingMap, 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 ¬ GGBoundBox.CopyBoundBox[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 SpecialTransformedBoundBox[sliceD.slice, sliceD.parts, movingD.parts, transform]; GGBoundBox.EnlargeByBox[bBox: movingBox, by: thisBox]; } }; movingBox ¬ GGBoundBox.NullBoundBox[]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, EnlargeMovingBox, normal]; }; SpecialTransformedBoundBox: GGModelTypes.SliceTransformedBoundBoxProc = { ancestor: Slice; IF GGParent.IsParent[(ancestor _ GGParent.GetTopLevelAncestor[slice])] THEN { DoOutline: PROC [outline: Slice] RETURNS [done: BOOL] = { RETURN [GGOutline.HasHoles[outline] ]; }; IF GGOutline.HasHoles[ancestor] OR GGParent.WalkChildren[ancestor, all, DoOutline, $Outline] THEN { box ¬ GGBoundBox.CopyBoundBox[GGSliceOps.GetBoundBox[ancestor, NIL]]; }; }; IF GGSliceOps.GetType[slice]=$Text OR GGSliceOps.IsCompleteParts[GGSlice.DescriptorFromParts[slice, selectedParts]] THEN { cBox: BoundBox ¬ GGSliceOps.GetBoundBox[slice, NIL]; cBox ¬ GGBoundBox.BoundBoxOfBoundBox[cBox, transform]; IF box=NIL THEN box ¬ cBox ELSE GGBoundBox.EnlargeByBox[box, cBox]; IF GGSliceOps.GetType[slice]=$Traj THEN { FindMaxStrokeWidth: GGSequence.SegmentWalkProc = { maxStrokeWidth ¬ MAX[maxStrokeWidth, seg.strokeWidth]; }; trajData: GGModelTypes.TrajData ¬ NARROW[slice.data]; maxStrokeWidth: REAL ¬ 0.0; [] ¬ GGSequence.WalkSegmentsInTraj[trajData, FindMaxStrokeWidth]; GGBoundBox.EnlargeByOffset[box, maxStrokeWidth/2.0 + 1.0]; -- grow boundBox to include strokeWidth for refresh purposes }; } ELSE { tBox: BoundBox; copies: LIST OF Slice ¬ GGSliceOps.Copy[slice, NIL]; firstCopy: Slice ¬ copies.first; IF copies.rest#NIL THEN SIGNAL Problem [msg: "Failed to procure complete copy"]; GGSliceOps.Transform[firstCopy, selectedParts, transform, none, NIL]; -- transform it tBox ¬ GGSliceOps.GetBoundBox[firstCopy, movingParts]; -- now get the bound box of the transformed moving parts IF box=NIL THEN box ¬ tBox ELSE GGBoundBox.EnlargeByBox[box, tBox]; }; }; StartCaretPos: AAPProc = { DrawSandwich[screen, ggData, buffer, useBackingMap, bounds]; }; StartCaretPosBounds: BBoxProc = { ggData.refresh.dragInProgress ¬ FALSE; ggData.refresh.caretIsMoving ¬ TRUE; box ¬ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into]; }; CaretMovedStaticBounds: BBoxProc = { box ¬ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into]; }; CaretMoved: AAPProc = { IF GGSelect.NoSelections[ggData.scene, normal] AND GGSelect.NoSelections[ggData.scene, hot] AND bounds.null THEN RETURN; PaintAllPlanes[screen, ggData, buffer, useBackingMap, bounds]; }; CaretMovedBounds: BBoxProc = { box ¬ GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, into: into]; }; 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: AAPProc = { IF AlignmentsVisible[ggData] THEN DrawSandwich[screen, ggData, buffer, useBackingMap] ELSE DrawSandwich[screen, ggData, buffer, useBackingMap, 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: AAPProc = { DrawSandwich[screen, ggData, buffer, useBackingMap, 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: AAPProc = { DrawSandwich[screen, ggData, buffer, useBackingMap, bounds]; }; DuringCaretPosBounds: BBoxProc = { ggData.refresh.dragInProgress ¬ FALSE; ggData.refresh.caretIsMoving ¬ TRUE; box ¬ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into]; }; DuringDrag: AAPProc = { DrawSandwich[screen, ggData, buffer, useBackingMap, 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: AAPProc = { RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, ggData.refresh.addedObject]; PaintAllPlanes[screen, ggData, buffer, useBackingMap, bounds]; }; FinishedAddingBounds: BBoxProc = { box ¬ GetAFullBox[ggData: ggData, dragInProgress: FALSE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE, alignments: TRUE, into: into]; }; RepairBackground: AAPProc = { RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox]; }; RepairBackgroundBounds: BBoxProc = { box ¬ ggData.refresh.beforeBox }; EndMotion: AAPProc = { 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, useBackingMap, 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]; }; DrawSelectionFeedback: PUBLIC PROC [dc: Imager.Context, scene: Scene, camera: Camera] = { DrawCpsOfSelectedSlices[dc, scene, camera, FALSE, FALSE]; }; 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]; }; OutsideOf: PROC [test, bound: BoundBox] RETURNS [BOOL] = { IF bound.infinite OR test.infinite THEN RETURN[FALSE] ELSE IF bound.null OR test.null THEN RETURN[TRUE] ELSE 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 >= }; DrawObjectsFiltered: PROC [dc: Imager.Context, ggData: GGData, filter: BoundBox, excludeOverlay: BOOL ¬ FALSE, overObject: Slice ¬ NIL] = { 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: AAPProc = { IF ggData.refresh.suppressRefresh THEN RETURN; Imager.SetColor[screen, Imager.black]; GGShapes.DrawSpot[screen, ggData.refresh.spotPoint]; }; PaintHitLine: AAPProc = { 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: AAPProc = { 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: AAPProc = { IF ggData.refresh.suppressRefresh THEN NULL ELSE { GGAlign.DrawAlignBagRegardless[screen, ggData.hitTest.alignBag, ggData]; }; }; PaintPolylines: AAPProc = { 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: AAPProc = { 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: AAPProc = { 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: AAPProc = { 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: AAPProc = { 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]; }; ccWhite: ImagerColor.ConstantColor = NARROW[Imager.white]; pageBox: BoundBox = GGBoundBox.CreateBoundBox[0, 0, 8.5*72.0, 11.0*72.0]; InterpressEntireScene: PUBLIC PROC [dc: Imager.Context, ggData: GGData, bBox: BoundBox] = { PaintSceneWithCamera[dc, ggData.scene, ggData.camera, bBox]; }; PaintSceneWithCamera: PUBLIC PROC [dc: Imager.Context, scene: Scene, camera: Camera, bBox: BoundBox ¬ NIL] = { DrawClipped: PROC = { DoDrawObject: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { IF bBox = NIL OR NOT OutsideOf[GGSliceOps.GetBoundBox[slice, NIL], bBox] THEN GGSliceOps.DrawParts[slice, NIL, dc, camera, FALSE]; }; color: Color _ GGScene.GetBackgroundColor[scene]; IF bBox # NIL THEN GGBoundBox.Clip[dc, bBox]; IF color#NIL THEN { WITH color SELECT FROM cc: ImagerColor.ConstantColor => IF ImagerColorPrivate.ConstantColorsEqual[ccWhite, cc] THEN GOTO NoBackground; ENDCASE; BEGIN box: BoundBox _ GGScene.BoundBoxOfScene[scene]; GGBoundBox.EnlargeByOffset[box, 72.0]; GGBoundBox.EnlargeByBox[bBox: box, by: pageBox]; IF bBox # NIL AND NOT bBox.infinite THEN GGBoundBox.EnlargeByBox[bBox: box, by: bBox]; Imager.SetColor[dc, color]; Imager.MaskRectangle[dc, [box.loX, box.loY, box.hiX - box.loX, box.hiY - box.loY]]; END; EXITS NoBackground => NULL; }; Imager.SetColor[dc, Imager.black]; [] ¬ GGScene.WalkSlices[scene, first, DoDrawObject]; }; Imager.DoSave[dc, DrawClipped]; }; 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[$PaintEntireSceneDrag, AapPaintEntireSceneDrag]; AAPRegister[$PaintAllPlanesDrag, AapPaintAllPlanesDrag]; 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, FALSE]; }; AapPaintEntireScene: AAPProc = { PaintEntireScene[screen, ggData, buffer, useBackingMap]; }; AapPaintEntireSceneDrag: AAPProc = { PaintEntireSceneDrag[screen, ggData, buffer, useBackingMap, bounds]; }; AapPaintAllPlanesDrag: AAPProc = { PaintAllPlanesDrag[screen, ggData, buffer, useBackingMap, bounds]; }; PaintInTotalBox: AAPProc = { RepairBackgroundInBoundBox[ggData, ggData.refresh.totalBox]; PaintAllPlanes[screen, ggData, buffer, useBackingMap, 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]; }; AAPEntry: TYPE = REF AAPEntryRep; AAPEntryRep: TYPE = RECORD [aapProc: AAPProc, bBoxProc: BBoxProc]; AAPProc: TYPE = PROC [screen: Imager.Context, ggData: GGData, buffer, useBackingMap: 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. D8 GGRefreshImpl.mesa Contents: All painting actions in Gargoyle are called thru this interface. Copyright Ó 1985, 1986, 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved. Pier, June 23, 1993 4:55 pm PDT Kurlander August 28, 1986 7:11:40 pm PDT Eisenman, October 2, 1987 6:37:52 pm PDT Maureen Stone, September 21, 1987 1:56:46 pm PDT Bier, July 15, 1993 8:10 pm PDT Doug Wyatt, December 18, 1989 2:57:28 pm PST Turning Refresh on and off Temporarily disable refresh. Reenable refresh and update those screen areas that have changed since DisableRefresh was called. Associate the given PaintProc and clientData with this GGData. This routine will be called by GGWindow.RestoreScreenAndInvariants If Viewers called us directly (whatChanged=NIL), then we need to handle aborts here. Otherwise, we presumably were called from SlackProcess, which is watching for aborts itself. Like GGRefresh.ActionAreaPaint, but paints all of the area described by bounds, even if some of this area doesn't need to be painted. Called by GGWindowImpl.RestoreScreenAndInvariants whenever scene update should be done. Computes a bounding box and then requests the parent to paint it. this is necessary in case the next paint request is generated by a parent instead of by Gargoyle itself; parent can't set the paintAction without a lot of work whatHasChanged will be an atom describing some change which has occurred to the viewable scene state, such as $CaretMoved, $SequencesMadeHot, $ObjectAdded, $SelectionChanged, or $Everything. A pointer to the particular objects to be repainted will be stored in ggData for now. We envision a scheme where Dispatch may actually queue up painting jobs and attempt optimizations on the queue. In this case, the objects to be repainted will have to be stored separately from the ggData. [Artwork node; type 'Artwork on' to command tool] This is a special purpose procedure so that MMM can solve a particular problem in which PaintEntireScene is getting called during a drag in MMM when a magic lens is over the Gargoyle scene. MMM can call PaintEntireSceneDrag instead and avoid unwanted control point feedback. This proc checks the mouse state machine (gag!) and if it is in the $Main state, decides that a drag is in progress and acts accordingly. This is a special purpose procedure so that MMM can solve a particular problem in which PaintEntireScene is getting called during a drag in MMM when a magic lens is over the Gargoyle scene. MMM can call PaintAllPlanesDrag instead and avoid unwanted control point feedback. This proc checks the mouse state machine (gag!) and if it is in the $Main state, decides that a drag is in progress and acts accordingly. Single-Plane Commands DKW: these do not test for suppressRefresh individually, since DrawSandwich tests it. No Imager.DoSave is used. This procedure sets the color to black. Repairing the Background Repair the image of the Background plane inside bBox. For example: The background has split into two parts: background and overlay. Call this with the bounding box of the overlay objects to remove them from the background plane. Caution: eraseFirst means erase within bBox, not clear the layer! The selected objects have changed in some small way (e.g. line width or color). The bounds passed in should be the BoundBoxOfSelected. Repair the background plane and refresh the screen. The selected objects have changed in some small way (e.g. line width or color). Repair the background plane and refresh the screen. no need to enlarge by beforeBox in this special case; the BoundBoxOfSelected is all that is needed for refresh. An object has been added to the scene, or has changed size, shape, or style properties. All of the changes to objects are confined to the box ggData.refresh.beforeBox. Repair the background plane in this box. On the screen we need only update the boundbox of the changed object, plus a few pixels in case the object has selected or hot parts. Also, in case this operation deselected anything, we must include the BoundBox of old selections. Also, if this object was triggering alignment objects, we may need to reposition or delete them. If alignment lines might be appearing or disappearing, then redraw the whole screen. Since the background and foreground rasters have already been recomputed at this point, this just affects how many pixels in the chunking and screen maps are touched. There are cases when no alignment lines will change but this code will still draw the whole screen, e.g., if the object that changed had no hot points, but other objects do. An object has been added to the scene, or has changed size, shape, or style properties. All of the changes to objects are confined to the box ggData.refresh.beforeBox. Repair the background plane in this box. On the screen we need only update the boundbox of the changed object, plus a few pixels in case the object has selected or hot parts. Also, in case this operation deselected anything, we must include the BoundBox of old selections. Also, if this object was triggering alignment objects, we may need to reposition or delete them. If alignment lines might be appearing or disappearing, then redraw the whole screen. Since the background and foreground rasters have already been recomputed at this point, this just affects how many pixels in the chunking and screen maps are touched. There are cases when no alignment lines will change but this code will still draw the whole screen, e.g., if the object that changed had no hot points, but other objects do. An object has been added to the scene. All of the changes are confined to the box ggData.refresh.beforeBox. Repair the background plane and refresh the screen. Since this is an addition, we only need to draw the objects which are over (in overlap order) the new shape (usually there aren't any). An object has been added to the scene. All of the changes are confined to the box ggData.refresh.beforeBox. Repair the background plane and refresh the screen. Since this is an addition, we only need to draw the objects which are over (in overlap order) the new shape (usually there aren't any). The anchor has been dropped. Erase the old anchor, if any, and draw the new one at the caret. The anchor has been lifted. Update the region where the anchor was and the region where any alignments were. (In fact only alignments triggered by the anchor need to be erased, but that is harder to code). Repairing the Foreground Add these new features to the foreground context. Dynamic Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear. Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear. Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear. Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear. thisBox _ GGSliceOps.GetTightBox[sliceD.slice, sliceD.parts]; GGBoundBox.EnlargeByOffset[thisBox, GGModelTypes.halfJointSize*ggData.camera.cpScale + 1.0]; GGSliceOps.DrawAttractorFeedback[sliceD.slice, sliceD.parts, selectedParts, dragInProgress, dc, camera, ggData.drag.editConstraints]; Finds a tight box that includes the current caret position Finds a tight box that includes the current anchor position The bound box of the control points of objects that are hot or selected (in part or in full) The bound box of the control points of objects that are selected (in part or in full) The bound box of the control points of objects that are hot (in part or in full) Because we highlight both the selected parts (with black squares) and the unselected parts (with white squares) of selected objects, this routine finds the bounding box of all top-level objects that are selected in part or in full. We use the tight box because it includes control points. includes control points but not control point sizes. The factor of 3.0 is used below in case some of the selected objects are clusters. Find the bounding box that contains the "new" positions of the moving objects The bounding box of those objects that would move if an interactive translation, rotation, or scaling were performed on the current selected objects. Note that this box may be bigger than the box of selected objects because partial selections can cause rubber-banding. It is imperative that leaf slices be walked here because of the way SpecialTransformedBoundBox is currently implemented. KAP. June 19, 1992. PROC [slice: Slice, selectedParts: SliceParts, movingParts: SliceParts, transform: Transformation ¬ NIL] RETURNS [box: BoundBox]; This proc is an expensive way to calculate the bound box of a transformed object: make a complete copy that can then be transformed, transform it, then get its bound box. It differs from the generic form of TransformedBoundBox in that it deals with the special case of objects with holes and with stroke widths of Trajectories. KAP. July 14, 1992. This slice is a child slice. Deal with special case of a child Outline containing holes. For the purpose of refresh boundBox calculation, need to find the original boundBox of the entire slice, not just the selected or moving parts, so that hole-containing Outlines will be completely removed from the background and re-rendered as appropriate. The selectedParts are ignored and the boundBox of the entire slice is found. One of the children has a hole. Find the original boundBox of the top level ancestor. must make a copy because it may be Enlarged later First, an optimization. If the selected parts is complete OR if it is a $Text slice then there is no need to make a copy of the object. Simply get the complete bound box and transform the box. cBox is a new box, allocated by BoundBoxOfBoundBox so it is kosher to just copy it into box or to Enlarge box by cBox. Trajectories must also allow for strokeWidth because when interactively scaled they do not scale the stroke widths when painting. SegmentWalkProc: TYPE = PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL _ FALSE]; Note that the following code works ONLY for slices whose Parts do not contain references to other slices. The reason for this is that we call procedures in this code using a copy of the SLICE but the original PARTS; if those original Parts contain references to slices they will refer to the ORIGINAL slices not the copied slices and the originals will get transformed instead of the copy. We do this because it is deemed too onerous to construct Parts for the copy which mirror the Parts of the original. live dangerously and return the actual slice.boundBox (tBox) instead of a copy because the callers of this proc don't mutate the box. We must erase any selection feedback (normal and hot), erase the old caret, erase any old attractor feedback, draw the new caret, and the new attractor feedback. IF NOT GGAlign.HasVisibleObjects[ggData.hitTest.alignBag] THEN DrawSandwich[screen, ggData, buffer, bounds] ELSE DrawSandwich[screen, ggData, buffer]; We must erase any selection feedback (normal and hot), erase the old caret, erase any old attractor feedback, draw the new caret, and the new attractor feedback. The selection feedback (normal and hot), old caret, old attractor bound boxes have been placed in beforeBox, so we only deal with the new caret/attractor position here. IF NOT GGAlign.HasVisibleObjects[ggData.hitTest.alignBag] THEN box _ GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into] ELSE box _ NullFromInto[into]; -- null box CaretMovedStatic: PROC [screen: Imager.Context, ggData: GGData, buffer: BOOL, bounds: BoundBox] = { The caret was "jumped" from one position to another. Everything else remained the same. Simple repaint the new caret and new attractor feedback. PaintAllPlanes[screen, ggData, buffer, bounds]; }; PROC [ggData: GGData, whatHasChanged: ATOM, into: BoundBox] RETURNS [box: BoundBox]; The caret was "jumped" from one position to another. Everything else remained the same. Simple repaint the new caret and new attractor feedback. Restore the hot and selection feedback. The caret should already be in the right place. Restore the hot and selection feedback. The caret should already be in the right place. Erase any selection feedback, update caret and attractors, erase the shapes from their old positions and draw the shapes in their new positions. If alignment lines might be appearing or disappearing, then redraw the whole screen. Since the background and foreground rasters have already been recomputed at this point, this just affects how many pixels in the chunking and screen maps are touched. This test is conservative. There are cases when no alignment lines will change but this code will still draw the whole screen. For instance, if we are dragging an object that isn't hot in a scene where some objects are hot; the test can be improved, by getting more information from GGAlign. Erase any selection feedback, update caret and attractors, erase the shapes from their old positions and draw the shapes in their new positions. If alignment lines might be appearing or disappearing, then redraw the whole screen. Since the background and foreground rasters have already been recomputed at this point, this just affects how many pixels in the chunking and screen maps are touched. This test is conservative. There are cases when no alignment lines will change but this code will still draw the whole screen. For instance, if we are dragging an object that isn't hot in a scene where some objects are hot; the test can be improved, by getting more information from GGAlign. This routine is called DuringDrag. Write the overlay shapes, selection feedback, and the foreground shapes onto the chunking bitmap and then dump the chunking bitmap onto the screen. The result is double-buffered motion. DuringMotion: PROC [screen: Imager.Context, ggData: GGData, dragInProgress: BOOL, buffer: BOOL, bounds: BoundBox] = { DrawSandwich[screen, ggData, buffer, bounds]; }; DuringMotionBounds: BBoxProc = { ggData.refresh.dragInProgress _ TRUE; ggData.refresh.caretIsMoving _ TRUE; box _ GetAFullBox[ggData: ggData, dragInProgress: TRUE, caret: TRUE, attractor: TRUE, into: into]; }; Update the background to include the new segment. Restore hot feedback, erase alignment lines that were generated automatically, restore selection feedback. Update the background to include the new segment. Restore hot feedback, erase alignment lines that were generated automatically, restore selection feedback. no painting, just repair used by StartMotion event proc to get background repaired Recombine background and overlay planes. Restore alignments triggered by moving hot points. Restore selection and hot feedback. Recombine background and overlay planes. Restore alignments triggered by moving hot points. Restore selection and hot feedback. moveBox: BoundBox ~ GGScene.BoundBoxOfMoving[ggData.scene, ggData.drag.editConstraints, ggData.drag.bezierDrag]; Drawing Utilities Draws control points on slices which are the caret attractor line: Line _ alignLine.line; rect: Imager.Rectangle _ GGState.GetViewport[ggData]; IF caretIsMoving THEN GGShapes.DrawLine[dc, line, rect, 0.0]; Puts all the selected (hot & normal) slices in a list, and returns them Drawing Routines which are independent of refresh strategy. Paints those objects in the scene within the filter bounding box, and in front of (and including) overObject (in PriorityOrder) into the display context. filterWidth=0.0 OR filterHeight=0.0 means no filter Feedback for the Edge Finder Debugging Debugging procs have extra two arguments so they conform to AAPProc type, but do not use buffer and bounds arguments. Draw the boxes. Draw the box. DKW: Perhaps GGSliceOps.MovingParts ought to look like this ... Drawing Arrows Drawing Feedback For Interpress Masters Called by GGMouseEvent.IPSnapShot to get a picture of Gargoyle dragging in action. boundRect: Rectangle _ ImagerBackdoor.GetBounds[dc]; -- Not IMPLEMENTED for IP contexts Draw all but the overlay objects into a bitmap. Auxiliary for SnapShot. Draw scene objects. Paints just scene objects (not alignment objects, selection feedback, carets, or anchors), using the parameters specified in camera. No GGData is required for this routine. If bBox # NIL then only paint objects that intersect bBox. If background color is neither white nor clear than draw a big background rectangle first. The Overlay Plane traverse the scene.entities from back to end front. Utility this procedure is needed to map the chunking and background bitmaps onto the actual viewer area before DrawBits is called. A global table of [key, AAPEntry] entries is maintained. Clients who wish to have their paint actions known are encouraged to Register their action area paint procs below. Scene Change operations that cannot calculate bound boxes and have no BBoxProc operations that can calculate bound boxes: Dragging AAPRegister[$DuringMotion, AapDuringMotion, DuringMotionBounds]; -- GGMouseEventImplA Non-Sandwich Drawing AAPRegister[$FitFeedback, FitFeedback]; Debugging AAPRegister[$key, val]; AapDuringMotion: AAPProc = { DuringMotion[screen, ggData, TRUE, buffer, bounds]; }; AAPProc: AAPProc = {  }; ÊU~•NewlineDelimiter –(cedarcode) style™code™KšÏnœC™KKšœ ÏeœO™ZK™Kšœ%Ïk™(Kšœ(™(K™0K™K™,K™—šŸ ˜ J˜úK˜—š œŸœŸ˜JšŸœ™˜ KšŸœ/Ÿ˜;—˜KšœŸœŸœ Ÿœ˜;K˜Kšœ ŸœŸœ#˜;KšœŸœŸœÏc˜VKšœ ŸœŸœ ˜SKšœ Ÿœ!˜/Kšœ ŸœŸœ% ˜ZKšœŸœŸœ!˜ŸœŸœ˜jKšœ‚™‚K˜#K˜%K˜K™—š œŸœŸœ*Ÿœ%Ÿœ˜xKšœ+Ÿœ(™VKšœ\™\šœŸœ˜šÏbœQ˜bšŸœ˜ KšœG˜GKšœ- ˜IKšœ!Ÿœ "˜JKšœ Ÿœ "˜IKšœŸœŸœ %˜AKšœ˜—Kšœ˜—K˜—KšœŸœ#˜/KšœŸœ$˜7šŸœ˜KšŸœM˜QKšŸœ¡œQ˜g—K˜K˜—š œŸœŸœ:Ÿœ+Ÿœ˜ŠKšœ…™…K˜QK˜K˜—š œŸœŸœŸœ˜BKšœš™šKšœ,˜,šŸœŸœ˜K˜)Kšœ:˜:K˜^K˜—šŸœ0˜4K™Ÿ—Kšœ+˜+K˜K˜—š œŸœŸœŸœ˜PKšœ Ÿœ˜KšœŸœŸœ˜Kšœ,˜,K˜K˜:šŸœŸœ˜KšœŸœ ˜(Kš Ÿœ ŸœŸœŸœŸœ2˜`KšŸœŸœŸœ˜4K˜—šŸœ˜Kšœ Ÿœ˜KšœŸœ˜Kšœ˜K˜—K˜Kšœ+˜+K˜—K˜šœŸœ*Ÿœ;Ÿœ˜‰Kšœ•™•KšœÍ™ÍK™Kšœ2˜2K˜K˜@šŸœŸœ 7˜YKšœŸœ˜ Kšœ!˜!K˜—šŸœ˜Kšœ Ÿœ˜KšœŸœŸœ˜K˜=šŸœŸœ˜KšœŸœ ˜(Kš Ÿœ ŸœŸœŸœŸœA˜nK˜—KšŸœ{˜K˜—Kšœ1˜1K˜—˜I artworkFigure• InterpressInterpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨ x jÄ•I¦Äÿ{þy ¢ ¨ x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤ÄIiÄJ4# ¢ ¥ ¨  Š¡²“Á Caret Plane– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Äÿ^ÄQk) ¢ ¥ ¨  Š¡²“ÁForeground Plane– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤ÄVõbÄV3 ¢ ¥ ¨  Š¡²“ÁSelection Feedback Plane– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤ÄüC^ÄÿKÑ ¢ ¥ ¨  Š¡²“ÁBackground Plane– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Ä{¸Ä! ¢ ¥ ¨  Š¡²“Á Overlay Plane– k x j r j  Š ªé]Ä?C¸ÄU«¡£¡¡¨¢¯“¢°“¢·“Ä[¬CÄ8>«™ë_—˜ k é k x j r j  Š ªÄ[&C[Äœõ¸Ä««¡£¡¡¨¢¯“¢°“¢·“Ä[¬CÄ8>«™4]—˜ k é k x j r j  Š ªçÄ<›&ÄÿrÄvê[¡£¡¡¨¢¯“¢°“¢·“éI™Ä”ôqÄ<ç&—˜ k é k x j r j  Š ªÄ”qÄ<›&Ä$pqÄV[¡£¡¡¨¢¯“¢°“¢·“@M™Ä”ôqÄ<ç&—˜ k é k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Ä«(Ä1ø# ¢ ¥ ¨  Š¡²“Á*Caret Plane: Anchor, Caret, moving Caret.– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Ä)?Äî̯ ¢ ¥ ¨  Š¡²“Á"Foreground Plane: Alignment lines– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Ä\ŸÄƒe ¢ ¥ ¨  Š¡²“Á=ControlPointFeedback Plane: control point and joint feedback– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Äk]¦Äèó½ ¢ ¥ ¨  Š¡²“Á>Selection Feedback Plane: Selected segment and slice feedback– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Ä/ÀIÄ+ ¢ ¥ ¨  Š¡²“Á*Overlay Plane: Moving outlines and slices– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤ÄGõnÄ…æy ¢ ¥ ¨  Š¡²“Á1Background Plane: Stationary outlines and slices– k x jÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨ª ¤Ä£•ÄŸ]V ¢ ¥ ¨  Š¡²“ÁControlPoint Feedback Plane– k x j r j  Š ªûÄÏagÄÃjľC¡£¡¡¨¢¯“¢°“¢·“ý¹™Ä[\CÄÐ/g—˜ k é k x j r j  Š ªÄYYBÄ6ÇÄeøÄ53Z¡£¡¡¨¢¯“¢°“¢·“ÄYÝBÄ|?™ÄH>5Ä7—˜ k é k k k g•Artwork Interpress•Bounds:0.0 mm xmin 0.0 mm ymin 137.5834 mm xmax 96.82118 mm ymax –ç99.6434 mm bigger topLeading 99.6434 mm bigger topIndent 1.411111 mm bigger bottomLeading 0.5 0.3 0.95 backgroundColor the topLeading 6 pt .sub backgroundAscent 3 pt backgroundDescent 4 pt outlineBoxThickness 1 pt outlineBoxBearoff•GGFileÕGargoyle file for scene: stuffed from Gargoyle at November 21, 1991 9:19:20 pm PST Produced by version 9106.27 Scripts: Slope: [F 150.0] [F 135.0] [F 120.0] [T 90.0] [F 60.0] [F 45.0] [F 30.0] [F 0.0] Angle: [F 90.0] [F 60.0] [F 45.0] [F 30.0] [F 0.0] [F -30.0] [F -45.0] [F -60.0] [F -90.0] Radius: [F 5.555556e-2 1/18] [F 0.1111111 1/9] [F 0.125 1/8] [F 0.25 1/4] [F 0.3333334 1/3] [F 0.5 1/2] [F 0.6666668 2/3] [F 0.75 3/4] [F 1.0 1] [F 2.0 2] [F 4.0 4] LineDistance: [F 0.0 0] [F 5.555556e-2 1/18] [F 0.1111111 1/9] [F 0.5 1/2] [F 1.0 1] Midpoints: F Heuristics: F ShowAlignments: T ScaleUnit: 72.0 DisplayStyle: print Gravity: F GravityExtent: 0.3472222 GravityType: pointsPreferred DefaultFont: xerox/pressfonts/Helvetica-mrr [r1: 0.0 s: [12.0 12.0] r2: 0.0] 1.0 1.0 Defaults: [1 0.5] [1 1.0] 2.0 round round Dashed: F Shadows: [1 1.0]F Anchor: F Palette: F Entities: [18]: Text T "Caret Plane" xerox/tiogafonts/Helvetica10 [10.0 0.0 315.2095 0.0 10.0 542.743][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Foreground Plane" xerox/tiogafonts/Helvetica10 [10.0 0.0 305.0012 0.0 10.0 508.3659][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Selection Feedback Plane" xerox/tiogafonts/Helvetica10 [10.0 0.0 227.1531 0.0 10.0 432.2745][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Background Plane" xerox/tiogafonts/Helvetica10 [10.0 0.0 303.0012 0.0 10.0 395.9993][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Overlay Plane" xerox/tiogafonts/Helvetica10 [10.0 0.0 381.8859 0.0 10.0 434.0624][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Outline fillColor: [] ow: T fillText: T 0 Children: [1] Traj (open) [1] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [350.2687,467.4503] (Line ) [331.0,447.0] fwd: T pList: ( ) Outline fillColor: [] ow: T fillText: T 0 Children: [1] Traj (open) [1] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [350.2687,467.4503] (Line ) [404.0,445.0] fwd: T pList: ( ) Outline fillColor: [] ow: T fillText: T 0 Children: [1] Traj (open) [1] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [329.0,425.0] (Line ) [337.4514,410.2895] fwd: T pList: ( ) Outline fillColor: [] ow: T fillText: T 0 Children: [1] Traj (open) [1] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [416.0,429.0] (Line ) [337.4514,410.2895] fwd: T pList: ( ) Text T "Caret Plane: Anchor, Caret, moving Caret." xerox/tiogafonts/Helvetica10 [10.0 0.0 166.6008 0.0 10.0 365.4858][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Foreground Plane: Alignment lines" xerox/tiogafonts/Helvetica10 [10.0 0.0 166.7302 0.0 10.0 349.3257][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "ControlPointFeedback Plane: control point and joint feedback" xerox/tiogafonts/Helvetica10 [10.0 0.0 167.3928 0.0 10.0 332.0693][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Selection Feedback Plane: Selected segment and slice feedback" xerox/tiogafonts/Helvetica10 [10.0 0.0 165.5723 0.0 10.0 315.5291][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Overlay Plane: Moving outlines and slices" xerox/tiogafonts/Helvetica10 [10.0 0.0 167.452 0.0 10.0 298.7142][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "Background Plane: Stationary outlines and slices" xerox/tiogafonts/Helvetica10 [10.0 0.0 167.4636 0.0 10.0 283.2893][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Text T "ControlPoint Feedback Plane" xerox/tiogafonts/Helvetica10 [10.0 0.0 280.094 0.0 10.0 474.3838][1 1.0] F 1.0 props: ( F ) ls: 1.2 pList: ( ) Outline fillColor: [] ow: T fillText: T 0 Children: [1] Traj (open) [1] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [349.0,537.0] (Line ) [349.0746,517.4273] fwd: T pList: ( ) Outline fillColor: [] ow: T fillText: T 0 Children: [1] Traj (open) [1] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [348.5606,504.1746] (Line ) [348.9436,485.5516] fwd: T pList: ( ) š3™3—K˜KšœŸœ˜#KšœŸœ˜#K˜šœŸœŸœŸœ˜?procšœ*Ÿœ˜/MšœŸœŸœ  ˜K˜K˜K˜5K˜KšœŸœ˜$Kšœ Ÿœ˜——KšŸœ7ŸœŸœŸœŸœ[ŸœŸœ4˜ŽK˜—K˜K˜—š œŸœŸœŸœŸœ˜_KšŸœŸœ Ÿœ€˜ªK˜K˜—šœŸœŸœ˜6Kšœ-˜-Kšœ5Ÿœ˜K˜K˜—šœ˜(K™„š œ.ŸœŸœŸœ¡ œ˜iK™o—K˜K˜—šœ ˜*Kšœž™žKš¡œ#˜=šŸœŸœ4˜UK™«—KšŸœ?˜CK˜K˜—š#œ˜1Kšœž™žKšŸœŸœ˜:K™«š Ÿœ3ŸœŸœ Ÿœ Ÿœ˜|K™—K˜K˜—š œ ˜Kšœª™ªKš¡œ#Ÿœ˜`K˜>K˜K˜—šœ˜Kšœª™ªKš œ2ŸœŸœŸœ Ÿœ Ÿœ˜ŒK˜K˜—šœ˜K™^Kšœ2ŸœŸœ Ÿœ˜eK˜K˜—šœ˜!K™ÏK˜KšœA˜AK˜K˜—K™™K˜—šœŸœŸœŸœ˜Dšœ˜ KšŸœ Ÿœ ˜2KšœD˜DK˜—KšœA˜AK˜K˜—š œŸœŸœ ŸœŸœ˜VJš¡œ.™1š¢ œ˜ Kšœ2˜2K˜—Kšœ5˜5K˜K˜—K™šœ™K™—š œ ˜K™vK˜K˜K˜—šœ˜K™XKšœ2ŸœŸœ Ÿœ˜fK˜K˜—š œŸœŸœŸœŸœ˜KKšŸœŸœŸœŸœŸœŸœŸœŸœ˜~K˜K˜—š œŸœŸœ ŸœŸœ˜KKš ŸœŸœ#ŸœŸœŸœ˜K˜K˜—šœ˜"Kšœ™Kšœ2ŸœŸœŸœ Ÿœ Ÿœ ŸœŸœ˜©K˜K˜—šœ ˜K™K™9Kšœ=˜=˜K˜——šœ˜$K˜˜K˜——š œ ˜K™šœŸœŸœŸœ˜Nš œŸœŸœŸœŸœ˜BK˜DšŸœŸœŸœ˜K˜KšœŸœ˜ K˜—K˜—Kšœ$£œ £˜4K˜—Kšœ8˜8Kšœ=Ÿœ˜PK˜>˜K˜——šœ˜K™Kšœp™pKš œ2ŸœŸœŸœ ŸœŸœ˜‹˜K˜——K˜™K˜—šœŸœEŸœ˜iK™Kšœ,£œ £¡£˜AK˜K˜—šœŸœŸœ7˜YKšœ+ŸœŸœ˜9K˜K˜—šœŸœSŸœ˜yš œŸœŸœŸœŸœ˜Pš œ ˜+K˜K˜Kšœ˜—Kšœ7˜7K˜K˜—Kšœ)˜)K˜"K˜ KšœŸœŸœ˜Kšœ#Ÿœ˜+K˜KšŸœŸœŸœŸœ˜/Kšœ,£œ£œ£˜GK˜K˜Kšœ,£œ£œ£˜DK˜KšœŸœ˜*š ŸœŸœŸœ.ŸœŸœŸ˜RK˜K˜9K˜3Kš œŸœŸœŸœŸœŸœ˜EKš œ Ÿœ ŸœŸœŸœŸœ˜Mšœ˜—šŸœ˜M˜Mšœ˜—Mšœ˜K˜—š œŸœ)˜KšœR™RKšœ5 "™WKšœD  ˜MKšœ˜Kšœ, 4˜`K˜K˜—šœŸœ)˜AKšœH™HK˜K˜KšœŸœ+˜6š œŸœŸœŸœŸœ˜AKš ŸœŸœŸœŸœŸœŸœ˜XK˜—Kš¡™Kšœ$£œ £˜3Kšœ_˜_Kšœ  Q˜qKšœD˜DKšœ1˜1Kšœ3˜3K˜K˜—Kšœ%Ÿœ˜:K˜IšœŸœŸœ9˜[K˜