GGRefreshImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Contents: All painting actions in Gargoyle are called thru this interface.
Last edited by Bier on January 15, 1987 1:38:40 am PST
Pier, December 11, 1986 3:43:05 pm PST
Kurlander August 28, 1986 7:11:40 pm PDT
DIRECTORY
Atom, BiScrollers, CedarProcess, GGBasicTypes, GGBoundBox, GGCaret, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGObjects, GGRefresh, GGSelect, GGSegmentTypes, GGSequence, GGShapes, GGStatistics, GGTouch, GGTraj, GGUtility, GGVector, Imager, ImagerBackdoor, ImagerOps, ImagerPixelMap, List, Process, Rope, Rosary, SlackProcess, TIPUser, Terminal, Interminal, ViewerClasses, ViewerOps;
GGRefreshImpl:
CEDAR
MONITOR
IMPORTS Atom, BiScrollers, CedarProcess, GGBoundBox, GGCaret, GGError, GGGravity, GGObjects, GGSelect, GGSequence, GGShapes, GGStatistics, GGTouch, GGTraj, GGUtility, GGVector, Imager, ImagerBackdoor, List, Process, Rope, SlackProcess, Terminal, Interminal, ViewerOps
EXPORTS GGRefresh = BEGIN
BitVector: TYPE = GGBasicTypes.BitVector;
BoundBox: TYPE = GGModelTypes.BoundBox;
BoundBoxGenerator: TYPE = GGObjects.BoundBoxGenerator;
CameraData: TYPE = GGModelTypes.CameraData;
Caret: TYPE = GGInterfaceTypes.Caret;
Color: TYPE = Imager.Color;
FeatureData: TYPE = GGGravity.FeatureData;
OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor;
Slice: TYPE = GGModelTypes.Slice;
SliceParts: TYPE = GGModelTypes.SliceParts;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
Point: TYPE = GGBasicTypes.Point;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SelectionClass: TYPE = GGInterfaceTypes.SelectionClass;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
Traj: TYPE = GGModelTypes.Traj;
Vector: TYPE = GGBasicTypes.Vector;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE;
[Artwork node; type 'ArtworkInterpress on' to command tool]
KillData: TYPE = RECORD [process: UNSAFE PROCESS, gargoyleData: GargoyleData];
NilIt: PRIVATE ENTRY PROC [data: REF KillData] = TRUSTED {data.process ← NIL};
ActionAreaPaint:
PUBLIC
PROC [screen: Imager.Context, whatHasChanged:
ATOM, gargoyleData: GargoyleData] =
TRUSTED {
IF whatHasChanged=$ViewersPaintEntireScene
THEN {
-- need a special abort watcher
data: REF KillData ← NEW[KillData ← [Process.GetCurrent[], gargoyleData]];
Fork a process that looks to see if SHIFT-SWAT is down and aborts refresh if it is.
[] ← CedarProcess.Fork[Killer, data, [excited, TRUE]];
DoActionAreaPaint[screen, whatHasChanged, gargoyleData ! UNWIND => NilIt[data]];
NilIt[data];
}
ELSE DoActionAreaPaint[screen, whatHasChanged, gargoyleData]; -- SlackProcess is watching for aborts
};
Killer:
PROC [data:
REF]
RETURNS [results:
REF ←
NIL] = {
KillKeysDown:
PROC
RETURNS [kill:
BOOL ←
FALSE] = {
tsc: TIPUser.TIPScreenCoords ~ NEW[TIPUser.TIPScreenCoordsRec];
vt: Terminal.Virtual ~ Terminal.Current[];
keyBits: Terminal.KeyBits ~ Terminal.GetKeys[vt: vt];
IF (keyBits[LeftShift]=down
OR keyBits[RightShift]=down)
AND keyBits[Spare3]=down
THEN {
mouse: Interminal.MousePosition ~ Interminal.GetMousePosition[];
viewer: ViewerClasses.Viewer;
tsc^ ← [
mouseX: mouse.mouseX,
mouseY: (IF mouse.color THEN vt.colorHeight ELSE vt.bwHeight) - mouse.mouseY,
color: mouse.color
];
viewer ← ViewerOps.MouseInViewer[tsc: tsc].viewer;
RETURN[viewer#NIL AND BiScrollers.ViewerIsABiScroller[viewer]] ;
};
};
MaybeKill:
ENTRY
PROC = {
IF killData.process #
NIL
AND KillKeysDown[]
THEN {
gargoyleData: GargoyleData ← killData.gargoyleData;
TRUSTED {Process.Abort[killData.process];};
killData.process ← NIL;
SlackProcess.FlushQueue[gargoyleData.slackHandle]; -- you have to do this HERE!
gargoyleData.refresh.suppressRefresh ← FALSE; -- in case you killed FastPlayback
gargoyleData.aborted ← ALL[TRUE]; -- copies of aborted for all purposes
};
};
killData: REF KillData ← NARROW[data];
UNTIL killData.process =
NIL DO
MaybeKill[];
Process.Pause[ticks: Process.SecondsToTicks[1]];
ENDLOOP;
};
DoActionAreaPaint:
PROC [screen: Imager.Context, whatHasChanged:
ATOM, gargoyleData: GargoyleData] = {
whatHasChanged will be an atom describing some change which has occurred to the viewable scene state, such as $CaretMoved, $OverlayMoved, $ObjectAdded, $SelectionChanged, or $Everything. A pointer to the particular objects to be repainted will be stored in gargoyleData 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 gargoyleData.
IF gargoyleData.aborted[refresh]
THEN {
-- last paint got killed => unknown bitmap cache states
gargoyleData.aborted[refresh] ← FALSE;
PaintEntireScene[screen, gargoyleData];
}
ELSE
SELECT whatHasChanged
FROM
$None => NULL;
The Sixteen Types of Scene Change:
$PaintEntireScene, $ViewersPaintEntireScene => PaintEntireScene[screen, gargoyleData];
$NewAlignmentsDeselected => PaintAllPlanes[screen, gargoyleData];
$NewAlignmentsSelected => PaintAllPlanes[screen, gargoyleData];
$SequencesMadeHot => PaintAllPlanes[screen, gargoyleData];
$SequencesMadeCold => PaintAllPlanes[screen, gargoyleData];
$Everything => PaintEntireScene[screen, gargoyleData];
$SelectionChanged => SelectionOrCaretChanged[screen, gargoyleData];
$FinishedAdding => FinishedAdding[screen, gargoyleData];
$FinishedDragging => FinishedDragging[screen, gargoyleData];
$CaretMoved => SelectionOrCaretChanged[screen, gargoyleData]; -- GGMouseEventImplA
$AnchorAdded => PaintAllPlanes[screen, gargoyleData]; -- GGEventImplB
$AnchorRemoved => PaintAllPlanes[screen, gargoyleData];
-- GGEventImplB
Dragging
$DuringMotion => PaintDragOverlay[screen, gargoyleData, TRUE]; -- GGMouseEventImplA
$DuringCaretPos => PaintDragOverlay[screen, gargoyleData, FALSE]; -- GGMouseEventImplA
$DuringSelect => DuringSelect[screen, gargoyleData];
$ObjectChangedInPlace => ObjectChangedInPlace[screen, gargoyleData, normal];
$ObjectChangedBoundBoxProvided => ObjectChangedBoundBoxProvided[screen, gargoyleData];
$ObjectAdded => ObjectAdded[screen, gargoyleData];
Debugging
$PaintSpot => PaintSpot[screen, gargoyleData];
$PaintHitLine => PaintHitLine[screen, gargoyleData];
$PaintOddHitLine => PaintOddHitLine[screen, gargoyleData];
$PaintTouchPoints => PaintTouchPoints[screen, gargoyleData];
$PaintAlign => PaintAlign[screen, gargoyleData];
$PaintBoundBoxes => PaintBoundBoxes[screen, gargoyleData];
$PaintTightBoxes => PaintTightBoxes[screen, gargoyleData];
$PaintOutlineBoxes => PaintOutlineBoxes[screen, gargoyleData];
$PaintSelectionBox => PaintSelectionBox[screen, gargoyleData];
$PaintMovingBox => PaintMovingBox[screen, gargoyleData];
ENDCASE => {
GGError.Append[gargoyleData.feedback, Rope.Cat["Gargoyle GGRefreshImpl doesn't know how to ", Atom.GetPName[whatHasChanged], "."], oneLiner];
GGError.Blink[gargoyleData.feedback];
};
};
Single-Plane Commands
DrawBackground:
PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
BackgroundBitsToDC:
PROC = {
AdjustContextForDrawBits[dc, gargoyleData];
Imager.SetColor[dc, Imager.black];
ImagerBackdoor.DrawBits[dc, backgroundBitmap.base, backgroundBitmap.wordsPerLine, 0, 0, backgroundBitmap.height, backgroundBitmap.width, 0, backgroundBitmap.height];
};
backgroundBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.backgroundBitmap;
IF gargoyleData.refresh.showColors.state = off
THEN {
IF
NOT gargoyleData.refresh.backgndBitmapOK
THEN {
Paint
Background:
PROC = {
rect: Imager.Rectangle;
rect ← ImagerBackdoor.GetBounds[gargoyleData.refresh.backgroundContext];
Imager.SetColor[gargoyleData.refresh.backgroundContext, Imager.white];
Imager.MaskRectangle[gargoyleData.refresh.backgroundContext, rect];
DrawObjectsFiltered[screen: gargoyleData.refresh.backgroundContext, gargoyleData: gargoyleData, filter: GGBoundBox.BoundBoxFromRectangle[rect], excludeOverlay: FALSE, overObject: NIL]
};
Imager.DoSaveAll[gargoyleData.refresh.backgroundContext, PaintBackground];
gargoyleData.refresh.backgndBitmapOK ← TRUE;
};
Imager.DoSaveAll[dc, BackgroundBitsToDC];
}
ELSE {
rect: Imager.Rectangle;
rect ← BiScrollers.ViewportBox[gargoyleData.biScroller];
Imager.SetColor[dc, Imager.white];
Imager.MaskRectangle[dc, rect];
DrawObjectsFiltered[screen: dc, gargoyleData: gargoyleData, filter: GGBoundBox.BoundBoxFromRectangle[rect], excludeOverlay: FALSE, overObject: NIL]
};
};
DrawFeedback: PROC [screen: Imager.Context, gargoyleData: GargoyleData, duringMotion: BOOL, quick: BOOL] = {
DrawFeedbackAux: PROC = {
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
sliceD: SliceDescriptor => {
IF selectClass = hot THEN
sliceD.slice.class.drawSelectionFeedback[sliceD.slice, NIL, sliceD.parts, NIL, screen, gargoyleData.camera, duringMotion, FALSE, quick]
ELSE
sliceD.slice.class.drawSelectionFeedback[sliceD.slice, sliceD.parts, NIL, NIL, screen, gargoyleData.camera, duringMotion, FALSE, quick];
};
outlineD: OutlineDescriptor => {
IF selectClass = hot THEN
outlineD.slice.class.drawSelectionFeedback[outlineD.slice, NIL, outlineD.parts, NIL, screen, gargoyleData.camera, duringMotion, FALSE, quick]
ELSE
outlineD.slice.class.drawSelectionFeedback[outlineD.slice, outlineD.parts, NIL, NIL, screen, gargoyleData.camera, duringMotion, FALSE, quick];
};
ENDCASE => ERROR;
ENDLOOP;
};
entityGen: EntityGenerator;
selectClass: SelectionClass;
entityCount: NAT;
IF NOT gargoyleData.camera.hideHot THEN {
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, hot];
entityCount ← GGObjects.EntityCount[entityGen];
quick ← entityCount > 10 OR quick;
selectClass ← hot;
Imager.DoSaveAll[screen, DrawFeedbackAux];
};
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
entityCount ← GGObjects.EntityCount[entityGen];
quick ← entityCount > 10 OR quick;
selectClass ← normal;
Imager.DoSaveAll[screen, DrawFeedbackAux];
};
DrawForeground:
PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
ForegroundBitsToDC:
PROC = {
AdjustContextForDrawBits[dc, gargoyleData];
Imager.SetColor[dc, Imager.black];
Imager.MaskBits[dc, foregroundBitmap.base, foregroundBitmap.wordsPerLine, 0, 0, foregroundBitmap.height, foregroundBitmap.width, 0, foregroundBitmap.height];
};
foregroundBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.foregroundBitmap;
IF gargoyleData.refresh.showColors.state = off
THEN {
Update the foreground bitmap, if necessary, and send it to the screen.
IF
NOT gargoyleData.refresh.foregndBitmapOK
THEN {
Paint
Foreground:
PROC = {
rect: Imager.Rectangle;
rect ← ImagerBackdoor.GetBounds[gargoyleData.refresh.foregroundContext];
Imager.SetColor[gargoyleData.refresh.foregroundContext, Imager.white];
Imager.MaskRectangle[gargoyleData.refresh.foregroundContext, rect];
GGGravity.DrawObjectBagRegardless[gargoyleData.refresh.foregroundContext, gargoyleData.hitTest.currentObjectBag, gargoyleData];
};
Imager.DoSaveAll[gargoyleData.refresh.foregroundContext, PaintForeground];
gargoyleData.refresh.foregndBitmapOK ← TRUE;
};
Imager.DoSaveAll[dc, ForegroundBitsToDC];
}
ELSE {
Draw the alignment objects directly to dc.
IF
NOT gargoyleData.refresh.foregndBitmapOK
THEN
GGGravity.DrawObjectBagRegardless[dc, gargoyleData.hitTest.currentObjectBag, gargoyleData];
};
};
DrawCaretPlane:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
The caret plane has no backing bitmap, so it is always made up fresh.
No Imager.DoSaveAll is used. This procedure sets the color to black.
DrawCaret[screen, gargoyleData.caret, Imager.black];
DrawAnchor[screen, gargoyleData.anchor, Imager.black];
};
NoteNewForeground:
PUBLIC
PROC [alignObjects:
LIST
OF FeatureData, gargoyleData: GargoyleData] = {
Add these new features to the foreground context.
Paint
Foreground:
PROC = {
GGGravity.DrawFeatureList[gargoyleData.refresh.foregroundContext, alignObjects, gargoyleData];
};
Imager.DoSaveAll[gargoyleData.refresh.foregroundContext, PaintForeground];
gargoyleData.refresh.foregndBitmapOK ← TRUE;
};
DrawCPFeedback:
PROC [dc: Imager.Context, gargoyleData: GargoyleData, caretIsMoving, dragInProgress:
BOOL] = {
IF gargoyleData.camera.quality#quality
THEN {
DrawAttractorFeedback[dc, gargoyleData, dragInProgress, caretIsMoving];
DrawCpsOfSelectedOutlines[dc, gargoyleData.scene, gargoyleData.camera, dragInProgress, caretIsMoving];
DrawCpsOfSelectedSlices[dc, gargoyleData.scene, gargoyleData.camera, dragInProgress, caretIsMoving];
};
};
DrawAttractorFeedback:
PROC [dc: Imager.Context, gargoyleData: GargoyleData, dragInProgress, caretIsMoving:
BOOL] = {
Draws control points on slices which are the caret attractor
attractor: REF ANY;
selectedParts: SliceParts;
attractor ← GGCaret.GetAttractor[gargoyleData.caret];
IF attractor = NIL THEN RETURN;
WITH attractor
SELECT
FROM
sliceD: SliceDescriptor => {
selectedD: SliceDescriptor ← GGSelect.FindSelectedSlice[slice: sliceD.slice, scene: gargoyleData.scene, selectClass: normal];
selectedParts ← IF selectedD = NIL THEN NIL ELSE selectedD.parts;
sliceD.slice.class.drawAttractorFeedback[sliceD, selectedParts, dragInProgress, dc, gargoyleData.camera];
};
outlineD: OutlineDescriptor => {
selectedD: OutlineDescriptor ← GGSelect.FindSelectedOutline[outlineD.slice, gargoyleData.scene, normal];
selectedParts ← IF selectedD = NIL THEN NIL ELSE selectedD.parts;
outlineD.slice.class.drawAttractorFeedback[outlineD, selectedParts, dragInProgress, dc, gargoyleData.camera];
};
ENDCASE => ERROR;
};
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];
};
AllSelectedOutlines:
PROC [scene: Scene]
RETURNS [selectedList:
LIST
OF Outline ←
NIL] = {
Puts all the selected (hot & normal) outlines in a list, and returns them
ptr: LIST OF Outline;
outDGen: GGModelTypes.OutlineDescriptorGenerator;
outDGen ← GGSelect.SelectedOutlines[scene, hot];
[selectedList, ptr] ← GGUtility.StartOutlineList[];
FOR outD: OutlineDescriptor ← GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen]
UNTIL outD =
NIL
DO
[selectedList, ptr] ← GGUtility.AddOutline[outD.slice, selectedList, ptr];
ENDLOOP;
outDGen ← GGSelect.SelectedOutlines[scene, normal];
FOR outD: OutlineDescriptor ← GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen]
UNTIL outD =
NIL
DO
IF NOT GGSelect.IsSelectedInPart[outD.slice, scene, hot] THEN [selectedList, ptr] ← GGUtility.AddOutline[outD.slice, selectedList, ptr];
ENDLOOP;
};
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;
sGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[scene, hot];
[selectedList, ptr] ← GGUtility.StartSliceList[];
FOR sd: SliceDescriptor ← GGSelect.NextSliceDescriptor[sGen], GGSelect.NextSliceDescriptor[sGen]
UNTIL sd =
NIL
DO
[selectedList, ptr] ← GGUtility.AddSlice[sd.slice, selectedList, ptr];
ENDLOOP;
sGen ← GGSelect.SelectedSlices[scene, normal];
FOR sd: SliceDescriptor ← GGSelect.NextSliceDescriptor[sGen], GGSelect.NextSliceDescriptor[sGen]
UNTIL sd =
NIL
DO
IF NOT GGSelect.IsSelectedInPart[sd.slice, scene, hot] THEN [selectedList, ptr] ← GGUtility.AddSlice[sd.slice, selectedList, ptr];
ENDLOOP;
};
DrawCpsOfSelectedOutlines:
PROC [dc: Imager.Context, scene: Scene, camera: CameraData, dragInProgress, caretIsMoving:
BOOL] = {
normalD, hotD: OutlineDescriptor;
normalParts, hotParts: SliceParts;
outline: Outline;
IF caretIsMoving OR dragInProgress THEN RETURN;
FOR oList:
LIST
OF Outline ← AllSelectedOutlines[scene], oList.rest
UNTIL oList=
NIL
DO
outline ← oList.first;
normalD ← GGSelect.FindSelectedOutline[outline, scene, normal];
hotD ← GGSelect.FindSelectedOutline[outline, scene, hot];
normalParts ← IF normalD # NIL THEN normalD.parts ELSE NIL;
hotParts ← IF hotD # NIL THEN hotD.parts ELSE NIL;
outline.class.drawSelectionFeedback[outline, normalParts, hotParts, dc, camera, dragInProgress, caretIsMoving, FALSE, caretIsMoving];
ENDLOOP;
DrawCpsOfSelectedSlices:
PROC [dc: Imager.Context, scene: Scene, camera: CameraData, dragInProgress, caretIsMoving:
BOOL] = {
normalSliceD, hotSliceD: SliceDescriptor;
normalParts, hotParts: SliceParts;
slice: Slice;
IF caretIsMoving OR dragInProgress THEN RETURN;
FOR sList:
LIST
OF Slice ← AllSelectedSlices[scene], sList.rest
UNTIL sList=
NIL
DO
slice ← sList.first;
normalSliceD ← GGSelect.FindSelectedSlice[slice, scene, normal];
hotSliceD ← GGSelect.FindSelectedSlice[slice, scene, hot];
normalParts ← IF normalSliceD # NIL THEN normalSliceD.parts ELSE NIL;
hotParts ← IF hotSliceD # NIL THEN hotSliceD.parts ELSE NIL;
slice.class.drawSelectionFeedback[slice, normalParts, hotParts, dc, camera, dragInProgress, caretIsMoving, FALSE, caretIsMoving];
ENDLOOP;
};
WhoCaresIfChairIsAttractor: PROC [gargoyleData: GargoyleData] RETURNS [attOn: GGInterfaceTypes.CaretOn, attractor: REF ANY, attJointNum: NAT, attSeg: Segment] = {
For now, I'm going to allow highlighting of moving objects. You only live once. Eric. Note: this procedure fixes a bug with ChairIsNotAttractor. Namely, highlighting does not occur with ChairIsNotAttractor when the caret is positioned on a selected segment.
[attractor: attractor, on: attOn, jointNum: attJointNum, seg: attSeg] ← GGCaret.GetAttractor[gargoyleData.caret];
WITH attractor SELECT FROM
oSliceD: OutlineDescriptor => {};
aSliceD: SliceDescriptor => {};
ENDCASE => attOn ← nothing;
};
ChairIsNotAttractor: PROC [gargoyleData: GargoyleData] RETURNS [attOn: GGInterfaceTypes.CaretOn, attractor: REF ANY, attJointNum: NAT, attSeg: Segment] = {
This code figures out if the chair and the attractor are "identical" in a bunch of weird cases. It returns on=nothing if they are "identical" and returns the attractor data if not "identical".
This is the wrong question to ask. The right thing to do is to take those objects that the caret is attracted to, and subtract away any parts that are moving. -- Bier, December 16, 1986.
chair: REF ANY;
chairOn: GGInterfaceTypes.CaretOn;
attSegNum, chairJointNum, chairSegNum: NAT;
chairSeg: Segment;
[attractor: attractor, on: attOn, jointNum: attJointNum, seg: attSeg, segNum: attSegNum] ← GGCaret.GetAttractor[gargoyleData.caret];
[chair: chair, on: chairOn, jointNum: chairJointNum, segNum: chairSegNum, seg: chairSeg] ← GGCaret.GetChair[gargoyleData.caret];
WITH attractor SELECT FROM
aTraj: Traj => {
attSeg1, attSeg2, chairSeg1, chairSeg2: NAT;
IF chair#attractor THEN RETURN; -- return attractor information
attractor and chair are the same traj
IF attOn=joint THEN {
IF GGTraj.IsEndJoint[aTraj, attJointNum] THEN attSeg2 ← attSeg1 ← IF attJointNum=0 THEN 0 ELSE attJointNum-1 -- open traj, end joint
ELSE { -- closed traj or not end joint
attSeg1 ← attJointNum;
attSeg2 ← GGTraj.PreviousSegmentNum[aTraj, attJointNum];
};
}
ELSE IF attOn=seg OR attOn=cp THEN attSeg2 ← attSeg1 ← attSegNum;
IF chairOn=joint THEN {
IF GGTraj.IsEndJoint[aTraj, chairJointNum] THEN chairSeg2 ← chairSeg1 ← IF chairJointNum=0 THEN 0 ELSE chairJointNum-1 -- open traj, end joint
ELSE { -- closed traj or not end joint
chairSeg1 ← chairJointNum;
chairSeg2 ← GGTraj.PreviousSegmentNum[aTraj, chairJointNum];
};
}
ELSE IF chairOn=seg OR chairOn=cp THEN chairSeg2 ← chairSeg1 ← chairSegNum;
IF attSeg1=chairSeg1 OR attSeg2=chairSeg1 OR attSeg1=chairSeg2 OR attSeg2=chairSeg2 THEN attOn ← nothing;
};
aSliceD: SliceDescriptor => { -- if slices are the same (don't user parts) then chair=attractor
WITH chair SELECT FROM
chairSliceD: SliceDescriptor => IF aSliceD.slice=chairSliceD.slice THEN attOn ← nothing;
ENDCASE;
};
ENDCASE => attOn ← nothing;
};
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];
};
Static
PaintEntireScene:
PUBLIC
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
Make no assumptions about the scene.
GGStatistics.StartInterval[$PaintEntireScene, GGStatistics.GlobalTable[]];
gargoyleData.refresh.foregndBitmapOK ← FALSE;
gargoyleData.refresh.backgndBitmapOK ← FALSE;
PaintAllPlanes[screen, gargoyleData];
GGStatistics.StopInterval[$PaintEntireScene, GGStatistics.GlobalTable[]];
};
PaintAllPlanes:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
IF gargoyleData.refresh.showColors.state = off
THEN {
BackgroundToChunking[gargoyleData];
FeedbackToChunking[gargoyleData, FALSE, FALSE];
CPFeedbackToChunking[gargoyleData, FALSE, FALSE];
ForegroundToChunking[gargoyleData];
CaretPlaneToChunking[gargoyleData];
ChunkingToScreen[screen, gargoyleData];
}
ELSE {
DrawBackground[screen, gargoyleData];
DrawFeedback[screen, gargoyleData, FALSE, FALSE];
DrawCPFeedback[screen, gargoyleData, FALSE, FALSE];
DrawForeground[screen, gargoyleData];
DrawCaretPlane[screen, gargoyleData];
};
ObjectChangedInPlace:
PROC [screen: Imager.Context, gargoyleData: GargoyleData, selectClass: GGInterfaceTypes.SelectionClass ← normal] = {
The selected objects have changed in some small way (e.g. line width or color). Repair the background plane and refresh the screen.
bBox: BoundBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, selectClass];
RepairBackgroundInBoundBox[gargoyleData, bBox, TRUE, NIL];
PaintAllPlanes[screen, gargoyleData];
};
ObjectChangedBoundBoxProvided:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
An object has been added to the scene, or has changed size or shape in a simple way. All of the changes are confined to the box gargoyleData.refresh.startBoundBox. Repair the background plane and refresh the screen.
RepairBackgroundInBoundBox[gargoyleData, gargoyleData.refresh.startBoundBox, TRUE, NIL];
PaintAllPlanes[screen, gargoyleData];
};
ObjectAdded:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
An object has been added to the scene. All of the changes are confined to the box gargoyleData.refresh.startBoundBox. 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[gargoyleData, gargoyleData.refresh.startBoundBox, FALSE, gargoyleData.refresh.addedObject];
PaintAllPlanes[screen, gargoyleData];
};
RepairBackgroundInBoundBox:
PROC [gargoyleData: GargoyleData, bBox: BoundBox, eraseFirst:
BOOL ←
FALSE, overObject:
REF
ANY] = {
backgroundContext: Imager.Context ← gargoyleData.refresh.backgroundContext;
backgroundBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.backgroundBitmap;
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
IF gargoyleData.refresh.showColors.state = off
THEN {
PaintObjectsInBox:
PROC = {
IF eraseFirst THEN GGBoundBox.EraseWithinBoundBox[backgroundContext, bBox];
DrawObjectsFiltered[screen: backgroundContext, gargoyleData: gargoyleData, filter: bBox, overObject: overObject];
};
Imager.DoSaveAll[backgroundContext, PaintObjectsInBox];
gargoyleData.refresh.backgndBitmapOK ← TRUE;
};
};
SelectionOrCaretChanged:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
The selection plane or the caret plane is obsolete. Since these planes have no backing bitmaps, no repairs are needed. Simply redraw the planes.
PaintAllPlanes[screen, gargoyleData];
};
Dynamic
Selection
DuringSelect:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
This routine is called DuringSelect. The chunking bitmap has the correct bkgnd. Write the selection feedback and the foreground shapes onto the chunking bitmap and then dump the chunking bitmap onto the screen for double-buffered motion.
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
chunkingBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.chunkingBitmap;
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
IF gargoyleData.refresh.showColors.state = off
THEN {
BackgroundBitsToChunking[gargoyleData];
FeedbackToChunking[gargoyleData, FALSE, TRUE];
CPFeedbackToChunking[gargoyleData, FALSE, FALSE];
ForegroundToChunking[gargoyleData];
CaretPlaneToChunking[gargoyleData];
ChunkingToScreen[screen: screen, gargoyleData: gargoyleData];
}
ELSE {}; -- no feedback in SlowPaint mode
};
Adding and Dragging
SplitBackgroundAndOverlay:
PUBLIC
PROC [gargoyleData: GargoyleData, restoreBox: BoundBox] = {
The background has split into two parts: background and overlay. Remove any overlay objects from the background plane. Also, a dragging operation is about to start, so mark the foregroundBitmap as obsolete.
PaintAllButOverlayed:
PROC = {
GGBoundBox.EraseWithinBoundBox[backgroundContext, restoreBox];
DrawObjectsFiltered[screen: backgroundContext, gargoyleData: gargoyleData, filter: restoreBox, excludeOverlay: TRUE, overObject: NIL];
};
backgroundContext: Imager.Context ← gargoyleData.refresh.backgroundContext;
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
Imager.DoSaveAll[gargoyleData.refresh.backgroundContext, PaintAllButOverlayed];
}; -- end StoreBackground
PaintDragOverlay:
PROC [screen: Imager.Context, gargoyleData: GargoyleData, dragInProgress:
BOOL] = {
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.
chunkingBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.chunkingBitmap;
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
IF gargoyleData.refresh.showColors.state = off
THEN {
BackgroundBitsToChunking[gargoyleData];
OverlayToChunking[gargoyleData];
CPFeedbackToChunking[gargoyleData: gargoyleData, duringMotion: TRUE, dragInProgress: dragInProgress];
ForegroundToChunking[gargoyleData];
CaretPlaneToChunking[gargoyleData];
ChunkingToScreen[screen, gargoyleData];
}
ELSE {
DrawCPFeedback[screen, gargoyleData, TRUE, dragInProgress];
DrawForeground[screen, gargoyleData];
DrawCaretPlane[screen, gargoyleData];
};
};
FinishedAdding:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
The boundBox describes the region of the background which must be updated from the overlay.
MergeBackgroundAndOverlay[gargoyleData, gargoyleData.refresh.startBoundBox, FALSE, gargoyleData.refresh.addedObject];
PaintAllPlanes[screen, gargoyleData];
};
FinishedDragging:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
GGBoundBox.EnlargeByBox[gargoyleData.refresh.startBoundBox, GGBoundBox.BoundBoxOfMoving[gargoyleData.scene]];
MergeBackgroundAndOverlay[gargoyleData, gargoyleData.refresh.startBoundBox, TRUE, NIL];
PaintAllPlanes[screen, gargoyleData];
MergeBackgroundAndOverlay:
PROC [gargoyleData: GargoyleData, bBox: BoundBox, eraseFirst:
BOOL ←
FALSE, overObject:
REF
ANY] = {
The background and overlay planes are about to be recombined into the background plane, after dragging.
backgroundContext: Imager.Context ← gargoyleData.refresh.backgroundContext;
backgroundBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.backgroundBitmap;
MergeBackgroundAndOverlayAux:
PROC = {
IF eraseFirst THEN GGBoundBox.EraseWithinBoundBox[backgroundContext, bBox];
DrawObjectsFiltered[screen: backgroundContext, gargoyleData: gargoyleData, filter: bBox, overObject: overObject];
};
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
IF gargoyleData.refresh.showColors.state = off
THEN {
Imager.DoSaveAll[backgroundContext, MergeBackgroundAndOverlayAux];
gargoyleData.refresh.backgndBitmapOK ← TRUE;
};
};
Dynamic Utility Routines
BackgroundBitsToChunking:
PROC [gargoyleData: GargoyleData] = {
Do no actual drawing. Send the background bits to the chunking bitmap.
background: ImagerBackdoor.Bitmap ← gargoyleData.refresh.backgroundBitmap;
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
BackgroundBitsToChunking
Aux:
PROC [] = {
AdjustContextForDrawBits[chunkingContext, gargoyleData];
IF gargoyleData.refresh.showColors.state = off THEN ImagerBackdoor.DrawBits[chunkingContext, background.base, background.wordsPerLine, 0, 0, background.height, background.width, 0, background.height]
ELSE DrawPixels[chunkingContext, gargoyleData.refresh.backgroundPixelMap, gargoyleData];
DrawPixels is a dummy routine for now until Imager implements it.
};
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
Imager.DoSaveAll[chunkingContext, BackgroundBitsToChunkingAux];
};
OverlayToChunking:
PROC [gargoyleData: GargoyleData] = {
Do no actual drawing. Send the overlay objects to the chunking bitmap.
OverlayToChunking
Aux:
PROC [] = {
DrawDragOverlay[chunkingContext, gargoyleData];
};
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
Imager.DoSaveAll[chunkingContext, OverlayToChunkingAux];
};
BackgroundToChunking:
PROC [gargoyleData: GargoyleData] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
DrawBackground[chunkingContext, gargoyleData];
};
ForegroundToChunking:
PROC [gargoyleData: GargoyleData] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
DrawForeground[chunkingContext, gargoyleData];
CPFeedbackToChunking:
PROC [gargoyleData: GargoyleData, duringMotion, dragInProgress:
BOOL] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
DrawCPFeedback[chunkingContext, gargoyleData, duringMotion, dragInProgress];
};
FeedbackToChunking: PROC [gargoyleData: GargoyleData, duringMotion: BOOL, quick: BOOL] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
DrawFeedback[chunkingContext, gargoyleData, duringMotion, quick];
CaretPlaneToChunking:
PROC [gargoyleData: GargoyleData] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
DrawCaretPlane[chunkingContext, gargoyleData];
};
ChunkingToScreen:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
ChunkingToScreenAux:
PROC = {
AdjustContextForDrawBits[screen, gargoyleData];
ImagerBackdoor.DrawBits[screen, chunkingBitmap.base, chunkingBitmap.wordsPerLine, 0, 0, chunkingBitmap.height, chunkingBitmap.width, 0, chunkingBitmap.height];
};
chunkingBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.chunkingBitmap;
Imager.DoSaveAll[screen, ChunkingToScreenAux];
};
Drawing Routines which are independent of refresh strategy.
DrawDragOverlay:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
This could use some optimizing.
DrawDragOverlayAux:
PROC = {
IF gargoyleData.refresh.orderedOverlayList=NIL THEN gargoyleData.refresh.orderedOverlayList ← OrderOverlayList[gargoyleData]; -- update ordered list
FOR oList:
LIST
OF
REF
ANY ← gargoyleData.refresh.orderedOverlayList, oList.rest
UNTIL oList =
NIL
DO
WITH oList.first SELECT FROM
sliceD: SliceDescriptor => {
sliceD.slice.class.drawTransform[sliceD.slice, sliceD.parts, screen, gargoyleData.camera, gargoyleData.drag.transform];
};
outlineD: OutlineDescriptor => {
outlineD.slice.class.drawTransform[outlineD.slice, outlineD.parts, screen, gargoyleData.camera, gargoyleData.drag.transform];
};
traj: Traj => ERROR;
caret: Caret => {
caret ← NARROW[oList.first];
DrawCaret[screen, caret, Imager.black];
};
ENDCASE => ERROR;
ENDLOOP;
IF GGCaret.Exists[gargoyleData.anchor] THEN DrawAnchor[screen, gargoyleData.anchor, Imager.black]; -- a kludge for now, June 25, 1986, Bier
};
Imager.DoSaveAll[screen, DrawDragOverlayAux];
};
DrawObjects:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
The common core of PaintEntireScene and InterpressEntireScene.
scene: Scene ← gargoyleData.scene;
entityGen: EntityGenerator;
Imager.SetColor[screen, Imager.black];
entityGen ← GGObjects.TopLevelEntitiesInScene[scene];
FOR entity:
REF
ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
outline: Outline => outline.class.drawParts[outline, NIL, screen, gargoyleData.camera, FALSE];
slice: Slice => slice.class.drawParts[slice, NIL, screen, gargoyleData.camera, FALSE];
ENDCASE => ERROR;
ENDLOOP;
};
DrawObjectsFiltered:
PROC [screen: Imager.Context, gargoyleData: GargoyleData, filter: GGBoundBox.BoundBox, excludeOverlay:
BOOL ←
FALSE, overObject:
REF
ANY] = {
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
entityGen: EntityGenerator ← GGObjects.TopLevelEntitiesInScene[scene];
thisEntity: REF ANY ← GGObjects.NextEntity[entityGen];
IF overObject #
NIL
THEN {
UNTIL thisEntity = overObject OR thisEntity = NIL DO thisEntity ← GGObjects.NextEntity[entityGen] ENDLOOP;
IF thisEntity = NIL THEN RETURN;
};
Imager.SetColor[screen, Imager.black];
GGBoundBox.Clip[dc: screen, bBox: filter];
FOR entity:
REF
ANY ← thisEntity, GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
outline: Outline => {
IF excludeOverlay AND outline.onOverlay THEN LOOP;
IF NOT OutsideOf[outline.class.getBoundBox[outline, NIL], filter] THEN outline.class.drawParts[outline, NIL, screen, gargoyleData.camera, FALSE];
};
slice: Slice => {
IF excludeOverlay AND slice.onOverlay THEN LOOP;
IF NOT OutsideOf[slice.class.getBoundBox[slice, NIL], filter] THEN slice.class.drawParts[slice, NIL, screen, gargoyleData.camera, FALSE];
};
ENDCASE => ERROR;
ENDLOOP;
};
scene: Scene ← gargoyleData.scene;
IF filter=NIL OR filter.null THEN RETURN;
Imager.DoSaveAll[screen, DrawObjectsFilteredAux];
};
DrawCaret:
PROC [screen: Imager.Context, caret: Caret, color: Imager.Color] = {
caretPos: Point ← GGCaret.GetPoint[caret];
IF NOT GGCaret.Exists[caret] THEN RETURN;
Imager.SetColor[screen, color];
GGShapes.DrawCaret[screen, caretPos];
};
DrawAnchor:
PROC [screen: Imager.Context, caret: Caret, color: Imager.Color] = {
caretPos: Point ← GGCaret.GetPoint[caret];
IF NOT GGCaret.Exists[caret] THEN RETURN;
Imager.SetColor[screen, color];
GGShapes.DrawAnchor[screen, caretPos];
};
DrawPixels: PROC [screen: Imager.Context, pm: ImagerPixelMap.PixelMap, gargoyleData: GargoyleData] = {};
Debugging
DrawNewAnchor:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
anchorPos: Point ← GGCaret.GetPoint[gargoyleData.anchor];
Imager.SetColor[screen, Imager.black];
GGShapes.DrawAnchor[screen, anchorPos];
};
PaintSpot:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
Imager.SetColor[screen, Imager.black];
GGShapes.DrawSpot[screen, gargoyleData.refresh.spotPoint];
};
PaintHitLine:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
Imager.SetColor[screen, Imager.black];
Imager.SetStrokeEnd[screen, round];
Imager.MaskVector[screen, [gargoyleData.refresh.spotPoint.x, gargoyleData.refresh.spotPoint.y], [gargoyleData.refresh.hitPoint.x, gargoyleData.refresh.hitPoint.y]];
GGShapes.DrawFilledRect[screen, gargoyleData.refresh.spotPoint, 3.0];
};
PaintOddHitLine:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
Imager.SetColor[screen, Imager.black];
Imager.SetStrokeEnd[screen, round];
Imager.MaskVector[screen, [gargoyleData.refresh.spotPoint.x, gargoyleData.refresh.spotPoint.y], [gargoyleData.refresh.hitPoint.x, gargoyleData.refresh.hitPoint.y]];
GGShapes.DrawCP[screen, gargoyleData.refresh.spotPoint];
};
PaintAlign:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
Imager.SetColor[screen, ImagerColor.ColorFromGray[0.5]];
GGGravity.DrawObjectBagRegardless[screen, NARROW[gargoyleData.hitTest.currentObjectBag], gargoyleData];
};
PaintTouchPoints:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
GGTouch.DrawAllTouchPoints[screen, gargoyleData];
};
PaintBoundBoxes:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
bBoxGen: BoundBoxGenerator;
PaintBoundBoxesAux:
PROC = {
Draw the boxes.
bBoxGen ← GGObjects.BoundBoxesInScene[gargoyleData.scene];
FOR box: BoundBox ← GGObjects.NextBox[bBoxGen], GGObjects.NextBox[bBoxGen]
UNTIL box =
NIL
DO
GGBoundBox.DrawBoundBox[screen, box];
ENDLOOP;
};
Imager.DoSaveAll[screen, PaintBoundBoxesAux];
};
PaintTightBoxes:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
box: BoundBox;
PaintTightBoxesAux:
PROC = {
Draw the boxes.
entityGen: GGModelTypes.EntityGenerator;
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity:
REF
ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
sliceD: SliceDescriptor => {
box ← sliceD.slice.class.getTightBox[sliceD.slice, sliceD.parts];
};
outlineD: OutlineDescriptor => {
box ← outlineD.slice.class.getTightBox[outlineD.slice, outlineD.parts];
};
ENDCASE => ERROR;
GGBoundBox.DrawBoundBox[screen, box];
ENDLOOP;
};
Imager.DoSaveAll[screen, PaintTightBoxesAux];
};
PaintOutlineBoxes:
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
PaintBoundBoxesAux:
PROC = {
Draw the boxes.
outSeqGen: GGSelect.OutlineSequenceGenerator;
bBox: BoundBox;
outSeqGen ← GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal];
FOR outSeq: GGSelect.OutlineSequence ← GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen]
UNTIL outSeq =
NIL
DO
IF outSeq.fenceSeq #
NIL
THEN {
bBox ← GGTraj.GetBoundBox[outSeq.fenceSeq.traj];
GGBoundBox.DrawBoundBox[screen, bBox];
};
FOR holeSeq: Sequence ← GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs]
UNTIL holeSeq =
NIL
DO
bBox ← GGTraj.GetBoundBox[holeSeq.traj];
GGBoundBox.DrawBoundBox[screen, bBox];
ENDLOOP;
ENDLOOP;
};
Imager.DoSaveAll[screen, PaintBoundBoxesAux];
};
PaintSelectionBox:
PUBLIC
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
box: BoundBox ← NIL;
PaintSelectionBoxAux:
PROC = {
Draw the box.
box ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene];
IF NOT box.null THEN GGBoundBox.DrawBoundBox[screen, box];
};
Imager.DoSaveAll[screen, PaintSelectionBoxAux];
};
PaintMovingBox:
PUBLIC
PROC [screen: Imager.Context, gargoyleData: GargoyleData] = {
box: BoundBox ← NIL;
PaintMovingBoxAux:
PROC = {
Draw the box.
box ← GGBoundBox.BoundBoxOfMoving[gargoyleData.scene];
IF NOT box.null THEN GGBoundBox.DrawBoundBox[screen, box];
};
Imager.DoSaveAll[screen, PaintMovingBoxAux];
};
EraseAll:
PROC [screen: Imager.Context] = {
rect: Imager.Rectangle;
rect ← ImagerBackdoor.GetBounds[screen];
Imager.SetColor[screen, Imager.white];
Imager.MaskRectangle[screen, rect];
};
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 [dc: Imager.Context, traj: Traj] = {
OPEN GGVector;
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;
IF NOT traj.loArrow AND NOT traj.hiArrow THEN RETURN;
[firstPoint, secondPoint, secondToLastPoint, lastPoint, firstWidth, lastWidth] ← EndFourPoints[traj];
IF traj.loArrow
THEN {
thisWidth ← firstWidth;
[height, halfWidth] ← GGShapes.ArrowSize[thisWidth];
tip ← firstPoint;
base ← secondPoint;
axis ← GGVector.Normalize[GGVector.Sub[tip, base]];
perp ← [axis.y, -axis.x];
Imager.Clip[dc, ClipPath, FALSE, TRUE];
};
IF traj.hiArrow
THEN {
thisWidth ← lastWidth;
[height, halfWidth] ← GGShapes.ArrowSize[thisWidth];
tip ← lastPoint;
base ← secondToLastPoint;
axis ← GGVector.Normalize[GGVector.Sub[tip, base]];
perp ← [axis.y, -axis.x];
Imager.Clip[dc, ClipPath, FALSE, TRUE];
};
};
DrawArrows:
PROC [dc: Imager.Context, traj: Traj, gargoyleData: GargoyleData] = {
firstPoint, secondPoint, secondToLastPoint, lastPoint: Point;
firstWidth, lastWidth: REAL;
IF NOT traj.loArrow AND NOT traj.hiArrow THEN RETURN;
[firstPoint, secondPoint, secondToLastPoint, lastPoint, firstWidth, lastWidth] ← EndFourPoints[traj];
IF traj.loArrow THEN GGShapes.DrawArrow[dc, firstPoint, secondPoint, firstWidth];
IF traj.hiArrow THEN GGShapes.DrawArrow[dc, lastPoint, secondToLastPoint, lastWidth];
};
Drawing Feedback
MaskStroke:
PROC [dc: Imager.Context, seg: Segment] = {
MaskPath: Imager.PathProc = {
moveTo[seg.lo];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
};
Imager.MaskStroke[dc, MaskPath, FALSE];
DrawJoints:
PROC [dc: Imager.Context, traj: Traj, gargoyleData: GargoyleData] = {
Let Q be a logical variable corresponding to (gargoyleData.camera.quality = quality).
Let S correspond to (joint J is selected).
Let V correspond to (joint J is marked as visible).
Let O correspond to (joint J is on the overlay plane).
Then joint J is drawn filled when: qSo.
Joint J is drawn unfilled when qsVo.
IF gargoyleData.camera.quality = quality OR traj.parent.onOverlay THEN RETURN;
IF
NOT traj.visibleJoints
THEN
RETURN;
Suppress joints for an interpress master and for dragging.
Imager.SetColor[dc, Imager.black];
FOR i:
INT
IN [0..GGTraj.HiJoint[traj]]
DO
GGShapes.DrawJoint[dc, GGTraj.FetchJointPos[traj, i]];
ENDLOOP;
};
DrawJointsInSequenceFeedback:
PROC [dc: Imager.Context, seq: Sequence, gargoyleData: GargoyleData, selectClass: SelectionClass ← normal] = {
jointGen: JointGenerator;
Suppress joints for an interpress master.
IF gargoyleData.camera.quality = quality THEN RETURN;
Imager.SetColor[dc, Imager.black];
jointGen ← GGSequence.JointsInSequence[seq];
FOR i:
INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen]
UNTIL i = -1
DO
GGShapes.DrawSelectedJoint[dc, GGTraj.FetchJointPos[seq.traj, i], selectClass];
ENDLOOP;
};
For Interpress Masters
SnapShot:
PUBLIC
PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
Called by GGMouseEvent.IPSnapShot to get a picture of Gargoyle dragging in action.
SnapshotBackground[dc, gargoyleData];
DrawDragOverlay[dc, gargoyleData];
};
SnapshotBackground:
PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
Draw all but the overlay objects into a bitmap. Auxiliary for SnapShot.
scene: Scene ← gargoyleData.scene;
entityGen: EntityGenerator;
Draw most of the scene.
Imager.SetColor[dc, Imager.black];
Worry about scene objects.
entityGen ← GGObjects.TopLevelEntitiesInScene[gargoyleData.scene];
FOR entity:
REF
ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
outline: Outline => {
IF OnOverlay[outline, gargoyleData] THEN LOOP
ELSE outline.class.drawParts[outline, NIL, dc, gargoyleData.camera, FALSE];
};
slice: Slice => {
IF OnOverlay[slice, gargoyleData] THEN LOOP
ELSE slice.class.drawParts[slice, NIL, dc, gargoyleData.camera, FALSE];
};
ENDCASE => ERROR;
ENDLOOP;
Worry about the caret.
IF NOT OnOverlay[gargoyleData.caret, gargoyleData] THEN DrawCaret[dc, gargoyleData.caret, Imager.black];
DrawAnchor[dc, gargoyleData.anchor, Imager.black];
Worry about alignment lines.
GGGravity.DrawObjectBagRegardless[dc, NARROW[gargoyleData.hitTest.currentObjectBag], gargoyleData];
};
InterpressEntireScene:
PUBLIC
PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
DrawObjects[dc, gargoyleData];
};
MoveToOverlay:
PUBLIC
PROC [entity:
REF
ANY, gargoyleData: GargoyleData] = {
WITH entity
SELECT
FROM
caret: Caret => {
IF OnOverlay[caret, gargoyleData] THEN ERROR;
GGCaret.TellOnOverlay[caret, TRUE];
gargoyleData.refresh.overlayList ← List.Nconc[LIST[caret], gargoyleData.refresh.overlayList];
};
seq: Sequence => {
parentOutline: Outline ← GGOutline.OutlineOfTraj[seq.traj];
newParts: SliceParts ← GGOutline.PartsFromSequence[parentOutline, seq];
IF OnOverlay[parentOutline, gargoyleData] THEN {
parentOutline.movingParts ← parentOutline.class.unionParts[parentOutline, parentOutline.movingParts, newParts];
}
};
outlineD: OutlineDescriptor => {
IF OnOverlay[outlineD, gargoyleData] THEN ERROR;
outlineD.slice.onOverlay ← TRUE;
gargoyleData.refresh.overlayList ← List.Nconc[LIST[outlineD], gargoyleData.refresh.overlayList];
};
sliceD: SliceDescriptor => {
IF OnOverlay[sliceD, gargoyleData] THEN ERROR;
sliceD.slice.onOverlay ← TRUE;
gargoyleData.refresh.overlayList ← List.Nconc[LIST[sliceD], gargoyleData.refresh.overlayList];
};
ENDCASE => ERROR;
gargoyleData.refresh.orderedOverlayList ← NIL;
};
MoveAllSelectedToOverlay:
PUBLIC
PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] = {
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, selectClass];
FOR entity:
REF
ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
MoveToOverlay[entity, gargoyleData];
ENDLOOP;
};
MoveToBackground:
PUBLIC
PROC [entity:
REF
ANY, gargoyleData: GargoyleData] = {
IF NOT OnOverlay[entity, gargoyleData] THEN RETURN;
gargoyleData.refresh.overlayList ← List.DRemove[entity, gargoyleData.refresh.overlayList];
WITH entity
SELECT
FROM
sliceD: SliceDescriptor => sliceD.slice.onOverlay ← FALSE;
outline: Outline => outline.onOverlay ← FALSE;
caret: Caret => GGCaret.TellOnOverlay[caret, FALSE];
ENDCASE => ERROR;
gargoyleData.refresh.orderedOverlayList ← NIL;
};
MoveOverlayToBackground:
PUBLIC
PROC [gargoyleData: GargoyleData] = {
FOR overlayList:
LIST
OF
REF
ANY ← gargoyleData.refresh.overlayList, overlayList.rest
UNTIL overlayList =
NIL
DO
WITH overlayList.first
SELECT
FROM
sliceD: SliceDescriptor => sliceD.slice.onOverlay ← FALSE;
outlineD: OutlineDescriptor => outlineD.slice.onOverlay ← FALSE;
caret: Caret => GGCaret.TellOnOverlay[caret, FALSE];
ENDCASE => ERROR;
ENDLOOP;
gargoyleData.refresh.overlayList ← NIL;
gargoyleData.refresh.orderedOverlayList ← NIL;
EmptyOverlay:
PUBLIC
PROC [gargoyleData: GargoyleData]
RETURNS [
BOOL] = {
RETURN[gargoyleData.refresh.overlayList = NIL];
};
OnOverlay:
PROC [entity:
REF
ANY, gargoyleData: GargoyleData]
RETURNS [
BOOL] = {
WITH entity
SELECT
FROM
caret: Caret => RETURN[GGCaret.IsOnOverlay[caret]];
outline: Outline => RETURN[outline.onOverlay];
slice: Slice => RETURN[slice.onOverlay];
sliceD: SliceDescriptor => RETURN[sliceD.slice.onOverlay];
outlineD: OutlineDescriptor => RETURN[outlineD.slice.onOverlay];
ENDCASE => ERROR;
};
OrderOverlayList:
PROC [gargoyleData: GargoyleData]
RETURNS [orderedList:
LIST
OF
REF
ANY ←
NIL] = {
traverse the scene.entities from back to end front.
FindOverlayedD:
PROC [slice:
REF
ANY]
RETURNS [sliceD:
REF
ANY ←
NIL] = {
FOR ov:
LIST
OF
REF
ANY ← gargoyleData.refresh.overlayList, ov.rest
UNTIL ov=
NIL
DO
WITH ov.first
SELECT
FROM
sliceD: SliceDescriptor => IF sliceD.slice=slice THEN RETURN[ov.first];
outlineD: OutlineDescriptor => IF outlineD.slice=slice THEN RETURN[ov.first];
ENDCASE => ERROR;
ENDLOOP;
RETURN[NIL];
};
sliceD: REF ANY;
finger: LIST OF REF ANY;
entityGen: EntityGenerator;
[orderedList, finger] ← GGUtility.StartList[];
entityGen ← GGObjects.TopLevelEntitiesInScene[gargoyleData.scene];
FOR entity:
REF
ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
IF OnOverlay[entity, gargoyleData]
THEN {
Scene may have: outline, slice
Overlay may have: OutlineDescriptor, SliceDescriptor, caret
WITH entity
SELECT
FROM
outline: Outline => {
-- scene entity is identical to overlay entity
sliceD ← FindOverlayedD[entity];
IF sliceD = NIL THEN sliceD ← outline.class.newParts[outline, NIL, topLevel];
[orderedList, finger] ← GGUtility.AddEntity[sliceD, orderedList, finger];
};
slice: Slice => {
sliceD ← FindOverlayedD[entity];
IF sliceD = NIL THEN sliceD ← slice.class.newParts[slice, NIL, topLevel];
[orderedList, finger] ← GGUtility.AddEntity[sliceD, orderedList, finger];
};
caret: Caret => {}; -- fix up later
ENDCASE => ERROR;
};
ENDLOOP;
IF OnOverlay[gargoyleData.caret, gargoyleData] THEN [orderedList, finger] ← GGUtility.AddEntity[gargoyleData.caret, orderedList, finger];
};
Utility
AdjustContextForDrawBits:
PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
this procedure is needed to map the chunking and background bitmaps onto the actual viewer area before DrawBits is called.
viewerToClient: Imager.Transformation ← BiScrollers.GetStyle[].GetTransforms[BiScrollers.QuaBiScroller[gargoyleData.actionArea]].viewerToClient;
Imager.ConcatT[dc, viewerToClient];
};
InitStats:
PROC [] = {
interval: GGStatistics.Interval;
interval ← GGStatistics.CreateInterval[$PaintEntireScene];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
};
InitStats[];
END.
Changed the names of some of the procedures and wrote down some conventions which may help us think about them (See comments in ActionAreaPaint). Added atom $DrawCaret for the special case $PaintSelectedRegion would be wasteful because nothing in that region has changed (see GGMouseEventImpl.EndCaretPos).
Bier, April 30, 1986 6:15:58 pm PDT: Deleted some commented out code. Thanks, Ken, for getting rid of the DrawSegArray stuff.