GGRefreshImpl.mesa
Contents: All painting actions in Gargoyle are called thru this interface.
Copyright Ó 1985, 1986, 1987, 1988, 1989 by Xerox Corporation. All rights reserved.
Pier, May 20, 1992 4:36 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, June 1, 1992 3:37 pm PDT
Doug Wyatt, December 18, 1989 2:57:28 pm PST
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;
Turning Refresh on and off
DisableRefresh:
PUBLIC
PROC [ggData: GGData] = {
Temporarily disable refresh.
ggData.refresh.suppressRefresh ← TRUE;
ggData.refresh.totalBox^ ← [0,0,0,0,TRUE,FALSE];
};
EnableRefresh:
PUBLIC
PROC [ggData: GGData] = {
Reenable refresh and update those screen areas that have changed since DisableRefresh was called.
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] = {
Associate the given PaintProc and clientData with this GGData. This routine will be called by GGWindow.RestoreScreenAndInvariants
ggData.embed.paintProc ← paintProc;
ggData.embed.clientData ← clientData;
};
ActionAreaPaint:
PUBLIC
PROC [screen: Imager.Context, whatHasChanged:
ATOM, ggData: GGData, handleViewerAbort:
BOOL] = {
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.
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];
GetPaintBox[ggData, whatHasChanged, ggData.refresh.paintBox];
GetPaintBox already called by PaintInParent
DoActionAreaPaint[screen, whatHasChanged, ggData, ggData.refresh.paintBox];
};
RepaintArea:
PUBLIC
PROC [screen: Imager.Context, ggData: GGData, whatHasChanged:
ATOM, bounds: BoundBox] = {
Like GGRefresh.ActionAreaPaint, but paints all of the area described by bounds, even if some of this area doesn't need to be painted.
DoActionAreaPaint[screen, whatHasChanged, ggData, bounds];
};
PaintInParent:
PUBLIC
PROC [ggData: GGData, paintAction:
ATOM] = {
Called by GGWindowImpl.RestoreScreenAndInvariants whenever scene update should be done. Computes a bounding box and then requests the parent to paint it.
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] = {
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.
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];
};
DKW: these do not test for suppressRefresh individually, since DrawSandwich tests it.
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 = {
No Imager.DoSave is used. This procedure sets the color to black.
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] = {
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.
PaintObjectsInBox: RefreshProc = {
IF eraseFirst
THEN GGBoundBox.EraseWithinBoundBox[dc, bBox];
Caution: eraseFirst means erase within bBox, not clear the layer!
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] = {
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.
RepairBackgroundInBoundBox[ggData, bounds];
PaintAllPlanes[screen, ggData, buffer, bounds];
};
ObjectChangedInPlaceBounds: BBoxProc = {
The selected objects have changed in some small way (e.g. line width or color). Repair the background plane and refresh the screen.
box ← GetABox[ggData: ggData, dragInProgress:
FALSE, selectedCPs:
TRUE, selectedParts:
TRUE,
into: into];
no need to enlarge by beforeBox in this special case; the BoundBoxOfSelected is all that is needed for refresh.
};
ObjectChangedBoundBoxProvided:
PROC [screen: Imager.Context, ggData: GGData, buffer:
BOOL, bounds: BoundBox] = {
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.
RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox];
IF AlignmentsVisible[ggData]
THEN DrawSandwich[screen, ggData, buffer]
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.
ELSE PaintAllPlanes[screen, ggData, buffer, bounds];
};
ObjectChangedBoundBoxProvidedBounds: BBoxProc = {
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 AlignmentsVisible[ggData] THEN box ← NullFromInto[into]
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.
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] = {
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).
RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, ggData.refresh.addedObject];
PaintAllPlanes[screen, ggData, buffer, bounds];
};
ObjectAddedBounds: BBoxProc = {
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).
box ← GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, attractor: TRUE, into: into];
};
AnchorAddedBounds: BBoxProc = {
The anchor has been dropped. Erase the old anchor, if any, and draw the new one at the caret.
box ← GetAFullBox[ggData: ggData, dragInProgress: FALSE, alignments: TRUE, anchor: TRUE, into: into];
};
AnchorRemovedBounds: BBoxProc = {
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).
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] = {
Add these new features to the foreground context.
AddTo
Foreground: RefreshProc = {
GGAlign.DrawFeatureList[dc, alignObjects, ggData];
};
RepairLayer[ggData, foregroundName, AddToForeground];
};
DuringSelect:
PROC [screen: Imager.Context, ggData: GGData, buffer:
BOOL, bounds: BoundBox] = {
Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear.
DrawSandwich[screen, ggData, buffer, bounds];
};
DuringSelectBounds: BBoxProc = {
Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear.
box ← GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE, into: into];
};
SelectionChangedBounds: BBoxProc = {
Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear.
ggData.refresh.dragInProgress ← FALSE;
ggData.refresh.caretIsMoving ← FALSE;
box ← GetAFullBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, into: into];
};
SelectionAndCaretChangedBounds: BBoxProc = {
Make all previous selections disappear. Make all previous carets disappear. Make the new selection and caret appear.
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.GetTightBox[sliceD.slice, sliceD.parts];
GGBoundBox.EnlargeByOffset[thisBox, GGModelTypes.halfJointSize*ggData.camera.cpScale + 1.0];
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;
GGSliceOps.DrawAttractorFeedback[sliceD.slice, sliceD.parts, selectedParts, dragInProgress, dc, camera, ggData.drag.editConstraints];
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] = {
Finds a tight box that includes the current caret position
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] = {
Finds a tight box that includes the current anchor position
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] = {
The bound box of the control points of objects that are hot or selected (in part or in full)
bBox ← SelectedCPsBox[ggData];
GGBoundBox.EnlargeByBox[bBox: bBox, by: HotCPsBox[ggData]];
};
SelectedCPsBox:
PROC [ggData: GGData]
RETURNS [bBox: BoundBox] = {
The bound box of the control points of objects that are selected (in part or in full)
bBox ← SelectionCPBoundBox[ggData, normal];
};
HotCPsBox:
PROC [ggData: GGData]
RETURNS [bBox: BoundBox] = {
The bound box of the control points of objects that are hot (in part or in full)
bBox ← SelectionCPBoundBox[ggData, hot];
};
SelectionCPBoundBox:
PROC [ggData: GGData, selectClass: GGScene.SelectionClass]
RETURNS [bBox: BoundBox] = {
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.
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
];
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.
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] = {
Find the bounding box that contains the "new" positions of the moving objects
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] = {
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.
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] = {
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];
DrawSandwich[screen, ggData, buffer, bounds];
};
StartCaretPosBounds: BBoxProc = {
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.
ggData.refresh.dragInProgress ← FALSE;
ggData.refresh.caretIsMoving ← TRUE;
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
box ← GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into];
};
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];
};
CaretMovedStaticBounds: BBoxProc = {
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.
box ← GetAFullBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, into: into];
};
CaretMoved:
PROC [screen: Imager.Context, ggData: GGData, buffer:
BOOL, bounds: BoundBox] = {
Restore the hot and selection feedback. The caret should already be in the right place.
IF GGSelect.NoSelections[ggData.scene, normal] AND GGSelect.NoSelections[ggData.scene, hot] AND bounds.null THEN RETURN;
PaintAllPlanes[screen, ggData, buffer, bounds];
};
CaretMovedBounds: BBoxProc = {
Restore the hot and selection feedback. The caret should already be in the right place.
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] = {
Erase any selection feedback, update caret and attractors, erase the shapes from their old positions and draw the shapes in their new positions.
IF AlignmentsVisible[ggData]
THEN DrawSandwich[screen, ggData, buffer]
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.
ELSE DrawSandwich[screen, ggData, buffer, bounds];
};
StartMotionBounds: BBoxProc = {
Erase any selection feedback, update caret and attractors, erase the shapes from their old positions and draw the shapes in their new positions.
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]
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.
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] = {
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.
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];
};
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];
};
FinishedAdding:
PROC [screen: Imager.Context, ggData: GGData, buffer:
BOOL, bounds: BoundBox] = {
Update the background to include the new segment. Restore hot feedback, erase alignment lines that were generated automatically, restore selection feedback.
RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox, FALSE, ggData.refresh.addedObject];
PaintAllPlanes[screen, ggData, buffer, bounds];
};
FinishedAddingBounds: BBoxProc = {
Update the background to include the new segment. Restore hot feedback, erase alignment lines that were generated automatically, restore selection feedback.
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] = {
no painting, just repair
used by StartMotion event proc to get background repaired
RepairBackgroundInBoundBox[ggData, ggData.refresh.beforeBox];
RepairBackgroundBounds: BBoxProc = {
box ← ggData.refresh.beforeBox
EndMotion:
PROC [screen: Imager.Context, ggData: GGData, buffer:
BOOL, bounds: BoundBox] = {
Recombine background and overlay planes. Restore alignments triggered by moving hot points. Restore selection and hot feedback.
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 = {
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];
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] = {
Draws control points on slices which are the caret attractor
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 => {
line: Line ← alignLine.line;
rect: Imager.Rectangle ← GGState.GetViewport[ggData];
Imager.SetColor[dc, Imager.black];
DrawStarbursts[dc, alignLine, scene, camera, ggData, dragInProgress, zip];
IF caretIsMoving THEN GGShapes.DrawLine[dc, line, rect, 0.0];
};
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] = {
Puts all the selected (hot & normal) slices in a list, and returns them
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];
};
Drawing Routines which are independent of refresh strategy.
DrawObjects:
PROC [screen: Imager.Context, ggData: GGData] = {
The common core of PaintEntireScene and InterpressEntireScene.
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] = {
Paints those objects in the scene within the filter bounding box, and in front of (and including) overObject (in PriorityOrder) into the display context.
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];
};
};
};
filterWidth=0.0 OR filterHeight=0.0 means no filter
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];
};
};
Feedback for the Edge Finder
<<FitFeedback: PROC [dc: Imager.Context, ggData: GGData] = {
IF ggData.refresh.suppressRefresh THEN RETURN;
Imager.SetColor[dc, Imager.black];
Imager.SetStrokeEnd[dc, round];
Imager.MaskVector[dc, [ggData.refresh.spotPoint.x, ggData.refresh.spotPoint.y], [ggData.refresh.hitPoint.x, ggData.refresh.hitPoint.y]];
};
>>
Debugging
Debugging procs have extra two arguments so they conform to AAPProc type, but do not use buffer and bounds arguments.
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 = {
Draw the boxes.
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 = {
Draw the box.
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] = {
DKW: Perhaps GGSliceOps.MovingParts ought to look like this ...
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];
};
};
<<
Drawing Arrows
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] = {
Called by GGMouseEvent.IPSnapShot to get a picture of Gargoyle dragging in action.
boundRect: Rectangle ← ImagerBackdoor.GetBounds[dc]; -- Not IMPLEMENTED for IP contexts
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] = {
Draw all but the overlay objects into a bitmap. Auxiliary for SnapShot.
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];
};
Draw scene objects.
[] ← 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] = {
traverse the scene.entities from back to end front.
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] = {
this procedure is needed to map the chunking and background bitmaps onto the actual viewer area before DrawBits is called.
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] = {
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.
aapEntry: AAPEntry ← NEW[AAPEntryRep ← [paint, bBox] ];
[] ← RefTab.Store[aapTable, key, aapEntry]; -- overwrites earlier entries with same key
};
AAPInit:
PROC = {
aapTable ← RefTab.Create[];
};
AAPRegistrations:
PROC = {
Scene Change
operations that cannot calculate bound boxes and have no BBoxProc
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];
operations that can calculate bound boxes:
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
Dragging
AAPRegister[$RepairBackground, RepairBackground, RepairBackgroundBounds]; -- GGMouseEventImplA
AAPRegister[$StartMotion, StartMotion, StartMotionBounds]; -- GGMouseEventImplA
AAPRegister[$StartCaretPos, StartCaretPos, StartCaretPosBounds]; -- GGMouseEventImplA
AAPRegister[$StartCopyAndDrag, StartCopyAndDrag, StartCopyAndDragBounds]; -- GGMouseEventImplA
AAPRegister[$DuringMotion, AapDuringMotion, DuringMotionBounds]; -- 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];
Non-Sandwich Drawing
AAPRegister[$FitFeedback, FitFeedback];
Debugging
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];
AAPRegister[$key, val];
};
PaintSceneNoBuffer: AAPProc = {
PaintEntireScene[screen, ggData, FALSE];
};
AapPaintEntireScene: AAPProc = {
PaintEntireScene[screen, ggData, buffer];
};
AapDuringMotion: AAPProc = {
DuringMotion[screen, ggData, TRUE, buffer, bounds];
};
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];
};
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.