GGRefreshImpl.mesa
Last edited by Bier on November 7, 1985 11:42:13 pm PST
Contents: All painting actions in Gargoyle are called thru this interface.
DIRECTORY
Atom,
GGBoundBox,
GGCaret,
GGError,
GGGravity,
GGInterfaceTypes,
GGModelTypes,
GGObjects,
GGRefresh,
GGSelect,
GGShapes,
GGTouch,
GGTransform,
GGUtility,
Imager,
ImagerBackdoor,
ImagerColor,
ImagerOps,
ImagerPath,
ImagerTransformation,
Rosary,
Rope;
GGRefreshImpl: CEDAR PROGRAM
IMPORTS Atom, GGBoundBox, GGCaret, GGError, GGGravity, GGObjects, GGSelect, GGShapes, GGTouch, GGTransform, GGUtility, Imager, ImagerBackdoor, ImagerColor, Rope, Rosary
EXPORTS GGRefresh =
BEGIN
BoundBox: TYPE = GGModelTypes.BoundBox;
BoundBoxGenerator: TYPE = GGObjects.BoundBoxGenerator;
Camera: TYPE = GGModelTypes.Camera;
Caret: TYPE = GGInterfaceTypes.Caret;
Cluster: TYPE = GGModelTypes.Cluster;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
JointPair: TYPE = GGModelTypes.JointPair;
JointGenerator: TYPE = GGObjects.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
Point: TYPE = GGModelTypes.Point;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGModelTypes.Segment;
SegmentGenerator: TYPE = GGObjects.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SelectionClass: TYPE = GGInterfaceTypes.SelectionClass;
Traj: TYPE = GGModelTypes.Traj;
ActionAreaPaint: PUBLIC PROC [dc: Imager.Context, whatToDo: ATOM, gargoyleData: GargoyleData] = {
whatToDo will be an atom describing a painting action such as $EraseControlPoint, $PaintControlPoint, $EraseAllControlPoints, $EraseAll, $PaintEntireScene, or $PaintTrajectory. A pointer to the particular object 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.
SELECT whatToDo FROM
$PaintEntireScene => PaintEntireScene[dc, gargoyleData];
$PaintSelectedRegion => PaintEntireScene[dc, gargoyleData];
Dragging
$PickUpEntity => {};
$EraseOverlay => EraseOverlay[dc, gargoyleData];
$PaintDragOverlay => PaintDragOverlay[dc, gargoyleData];
$PaintNewAnchor => PaintNewAnchor[dc, gargoyleData];
Debugging
$PaintSpot => PaintSpot[dc, gargoyleData];
$PaintHitLine => PaintHitLine[dc, gargoyleData];
$PaintTouchPoints => PaintTouchPoints[dc, gargoyleData];
$PaintAlign => PaintAlign[dc, gargoyleData];
$PaintBoundBoxes => PaintBoundBoxes[dc, gargoyleData];
$PaintSelectionBox => PaintSelectionBox[dc, gargoyleData];
ENDCASE => {
GGError.Append[
Rope.Cat["Gargoyle GGRefreshImpl doesn't know how to ",
Atom.GetPName[whatToDo], "."],
oneLiner];
};
};
WorldCoordsContext: PROC [dc: Imager.Context, camera: Camera] = {
Imager.TranslateT[dc, [camera.cameraScreen[1], camera.cameraScreen[2]]];
};
Drawing Trajectories
DrawCluster: PUBLIC PROC [dc: Imager.Context, cluster: Cluster, gargoyleData: GargoyleData] = {
};
DrawOutline: PUBLIC PROC [dc: Imager.Context, outline: Outline, gargoyleData: GargoyleData] = {
fence: Traj;
fence ← outline.children.first;
IF fence.role = fence THEN {
FillTraj[dc, fence, outline.fillColor];
};
DrawTraj[dc, fence, gargoyleData];
};
DrawTraj: PUBLIC PROC [dc: Imager.Context, traj: Traj, gargoyleData: GargoyleData] = {
seg: Segment;
FOR i: INT IN [0..GGObjects.HiSegment[traj]] DO
seg ← GGObjects.FetchSegment[traj, i];
Imager.SetStrokeWidth[dc, seg.strokeWidth];
Imager.SetColor[dc, seg.color];
seg.class.maskStroke[dc, seg];
ENDLOOP;
Imager.SetStrokeWidth[dc, 1.0];
DrawJoints[dc, traj, gargoyleData];
};
DrawSequence: PUBLIC PROC [dc: Imager.Context, seq: Sequence, gargoyleData: GargoyleData] = {
segGen: SegmentGenerator;
segGen ← GGObjects.SegmentsInSequence[seq];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.NextSegment[segGen] UNTIL seg = NIL DO
Imager.SetStrokeWidth[dc, seg.strokeWidth];
Imager.SetColor[dc, seg.color];
seg.class.maskStroke[dc, seg];
ENDLOOP;
Imager.SetStrokeWidth[dc, 1.0];
DrawJointsInSequence[dc, seq, gargoyleData];
};
DrawJoints: PROC [dc: Imager.Context, traj: Traj, gargoyleData: GargoyleData] = {
visible: BOOL;
joint: Joint;
IF GGSelect.IsSelected[traj, gargoyleData, normal] THEN {
FOR i: INT IN [0..GGObjects.HiJoint[traj]] DO
GGShapes.DrawSelectedJoint[dc, GGObjects.FetchJointPos[traj, i]];
ENDLOOP;
RETURN;
};
visible ← traj.visibleJoints OR traj.outline.onOverlay;
FOR i: INT IN [0..GGObjects.HiJoint[traj]] DO
joint ← NARROW[Rosary.Fetch[traj.joints, i]];
IF GGSelect.IsSelected[joint, gargoyleData, normal] THEN GGShapes.DrawSelectedJoint[dc, GGObjects.FetchJointPos[traj, i]]
ELSE IF visible THEN GGShapes.DrawJoint[dc, GGObjects.FetchJointPos[traj, i]];
ENDLOOP;
};
DrawJointsInSequence: PROC [dc: Imager.Context, seq: Sequence, gargoyleData: GargoyleData] = {
visible: BOOL;
joint: Joint;
jointGen: JointGenerator;
jointGen ← GGObjects.JointsInSequence[seq];
IF GGSelect.IsSelected[seq.traj, gargoyleData, normal] THEN {
FOR i: INT ← GGObjects.NextJoint[jointGen], GGObjects.NextJoint[jointGen] UNTIL i = -1 DO
GGShapes.DrawSelectedJoint[dc, GGObjects.FetchJointPos[seq.traj, i]];
ENDLOOP;
RETURN;
};
visible ← seq.traj.visibleJoints OR seq.traj.outline.onOverlay;
FOR i: INT ← GGObjects.NextJoint[jointGen], GGObjects.NextJoint[jointGen] UNTIL i = -1 DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, i]];
IF GGSelect.IsSelected[joint, gargoyleData, normal] THEN GGShapes.DrawSelectedJoint[dc, GGObjects.FetchJointPos[seq.traj, i]]
ELSE IF visible THEN GGShapes.DrawJoint[dc, GGObjects.FetchJointPos[seq.traj, i]];
ENDLOOP;
};
FillTraj: PUBLIC PROC [dc: Imager.Context, traj: Traj, fillColor: Imager.Color] = {
BuildPath: Imager.PathProc = {
seg: Segment;
firstPoint: Point ← GGObjects.FetchJointPos[traj, 0];
moveTo[ [firstPoint[1], firstPoint[2]] ];
FOR i: INT IN [0..GGObjects.HiSegment[traj]] DO
seg ← GGObjects.FetchSegment[traj, i];
seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
Imager.SetColor[dc, fillColor];
Imager.MaskFill[dc, BuildPath];
};
NotYetImplemented: PUBLIC SIGNAL = CODE;
DrawOutlineTransformSeq: PROC [dc: Imager.Context, outline: Outline, selSeq: Sequence, transform: ImagerTransformation.Transformation, gargoyleData: GargoyleData] = {
This won't work right when outlines have holes.
fence: Traj;
fence ← outline.children.first;
IF fence.role = fence THEN FillTrajTransformSeq[dc, outline.fillColor, selSeq, transform];
Otherwise, fence.role = open in which case filling is inappropriate.
FOR trajs: LIST OF Traj ← outline.children, trajs.rest UNTIL trajs = NIL DO
IF trajs.first = selSeq.traj THEN DrawTrajTransformSeq[dc, selSeq, transform]
ELSE DrawTraj[dc, trajs.first, gargoyleData];
ENDLOOP;
};
MaxSegments: NAT = GGModelTypes.MaxSegments;
DrawSegArray: TYPE = REF DrawSegArrayObj;
DrawSegArrayObj: TYPE = RECORD [
entire: ARRAY [0..MaxSegments-1] OF BOOL,
lo: ARRAY [0..MaxSegments-1] OF BOOL,
hi: ARRAY [0..MaxSegments-1] OF BOOL
];
arrayPool: ARRAY [0..PoolSize-1] OF DrawSegArray;
arrayPoolIndex: NAT;
PoolSize: NAT = 3;
Init: PROC [] = {
FOR i: NAT IN [0..PoolSize) DO
arrayPool[i] ← NEW[DrawSegArrayObj];
ENDLOOP;
arrayPoolIndex ← 0;
};
AllocateDrawSegArray: PROC [] RETURNS [array: DrawSegArray] = {
IF arrayPoolIndex = PoolSize THEN ERROR;
array ← arrayPool[arrayPoolIndex];
arrayPoolIndex ← arrayPoolIndex + 1;
};
ReturnDrawSegArray: PROC [array: DrawSegArray] = {
IF arrayPoolIndex = 0 THEN ERROR;
arrayPoolIndex ← arrayPoolIndex - 1;
arrayPool[arrayPoolIndex] ← array;
};
SegAndIndex: TYPE = GGObjects.SegAndIndex;
StoreTruthInArrayAboutSequence: PROC [segData: DrawSegArray, selSeq: Sequence] = {
Initialize.
start, end, prevSegNum, followSegNum: NAT;
segGen: SegmentGenerator;
IF selSeq.all THEN ERROR;
FOR i: NAT IN [0..GGObjects.HiSegment[selSeq.traj]] DO
segData.entire[i] ← FALSE;
segData.lo[i] ← FALSE;
segData.hi[i] ← FALSE;
ENDLOOP;
FOR partList: LIST OF JointPair ← selSeq.parts, partList.rest UNTIL partList = NIL DO
start ← partList.first.start;
end ← partList.first.end;
IF NOT(selSeq.traj.role = open AND start = 0) THEN {
prevSegNum ← GGObjects.PreviousSegmentNum[selSeq.traj, start];
segData.hi[prevSegNum] ← TRUE;
};
segGen ← GGObjects.SegmentsInJointPair[selSeq.traj, start, end];
FOR midSeg: SegAndIndex ← GGObjects.NextSegmentAndIndex[segGen], GGObjects.NextSegmentAndIndex[segGen] UNTIL midSeg.seg = NIL DO
segData.entire[midSeg.index] ← TRUE;
ENDLOOP;
IF NOT(selSeq.traj.role = open AND end = GGObjects.HiJoint[selSeq.traj]) THEN {
followSegNum ← end;
segData.lo[followSegNum] ← TRUE;
};
ENDLOOP;
};
DrawTrajTransformSeqClosed: PROC [dc: Imager.Context, traj: Traj, start, end: NAT, transform: ImagerTransformation.Transformation] = {
lo, hi: BOOL;
seg: Segment;
FOR i: INT IN [0..GGObjects.HiSegment[traj]] DO
lo ← GGObjects.InMODRegion[i, start, end, traj.segCount];
hi ← GGObjects.InMODRegion[ (i+1) MOD traj.segCount, start, end, traj.segCount];
seg ← GGObjects.FetchSegment[traj, i];
Imager.SetStrokeWidth[dc, seg.strokeWidth];
seg.class.maskStrokeTransform[dc, seg, transform, lo, hi];
ENDLOOP;
};
DrawTrajTransformSeq: PROC [dc: Imager.Context, selSeq: Sequence, transform: ImagerTransformation.Transformation] = {
For each segment, we must determine (with respect to selSeq) whether it is:
1) Unselected.     (entire = FALSE, lo = FALSE, hi = FALSE)
2) One endpoint selected.  (entire = FALSE, lo = TRUE, hi = FALSE) or vice-versa.
3) Both endpoints selected. (entire = FALSE, lo = TRUE, hi = TRUE)
4) Selected in entirety.  (entire = TRUE, lo = X, hi = X).
segData: DrawSegArray ← AllocateDrawSegArray[];
seg: Segment;
entire, lo, hi: BOOL;
StoreTruthInArrayAboutSequence[segData, selSeq];
FOR i: INT IN [0..GGObjects.HiSegment[selSeq.traj]] DO
entire ← segData.entire[i];
lo ← segData.lo[i];
hi ← segData.hi[i];
seg ← GGObjects.FetchSegment[selSeq.traj, i];
Imager.SetStrokeWidth[dc, seg.strokeWidth];
seg.class.maskStrokeTransform[dc, seg, transform, entire, lo, hi];
ENDLOOP;
ReturnDrawSegArray[segData];
};
FillTrajTransformSeq: PROC [dc: Imager.Context, fillColor: Imager.Color, selSeq: Sequence, transform: ImagerTransformation.Transformation] = {
oldColor: Imager.Color ← ImagerBackdoor.GetColor[dc];
segData: DrawSegArray ← AllocateDrawSegArray[];
seg: Segment;
BuildPath: Imager.PathProc = {
firstPoint: Point;
entire, lo, hi: BOOL;
lo ← segData.lo[0];
entire ← segData.entire[0];
firstPoint ← GGObjects.FetchJointPos[selSeq.traj, 0];
IF lo OR entire THEN firstPoint ← GGTransform.Transform[transform, firstPoint];
moveTo[ [firstPoint[1], firstPoint[2]] ];
FOR i: INT IN [0..GGObjects.HiSegment[selSeq.traj]] DO
entire ← segData.entire[i];
lo ← segData.lo[i];
hi ← segData.hi[i];
seg ← GGObjects.FetchSegment[selSeq.traj, i];
seg.class.buildPathTransform[seg, lineTo, curveTo, conicTo, arcTo, transform, entire, lo, hi];
ENDLOOP;
};
StoreTruthInArrayAboutSequence[segData, selSeq];
Imager.SetColor[dc, fillColor];
Imager.MaskFill[dc, BuildPath];
Imager.SetColor[dc, oldColor];
ReturnDrawSegArray[segData];
};
The Overlay Plane
MoveJointsToOverlay: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData] = {
gargoyleData.refresh.overlayList ← CONS[traj, gargoyleData.refresh.overlayList];
};
RemoveJointsFromOverlay: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData] = {
gargoyleData.refresh.overlayList ← GGUtility.DeleteEntityFromList[traj, gargoyleData.refresh.overlayList];
};
MoveToOverlay: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData] = {
parentOutline: Outline;
WITH entity SELECT FROM
outline: Outline => {
IF OnOverlay[outline] THEN ERROR;
outline.onOverlay ← TRUE;
outline.whyOnOverlay ← outline;
gargoyleData.refresh.overlayList ← CONS[outline, gargoyleData.refresh.overlayList];
};
traj: Traj => {
parentOutline ← GGObjects.OutlineOfTraj[traj];
IF OnOverlay[parentOutline] THEN ERROR;
parentOutline.onOverlay ← TRUE;
parentOutline.whyOnOverlay ← traj;
gargoyleData.refresh.overlayList ← CONS[parentOutline, gargoyleData.refresh.overlayList];
};
seq: Sequence => {
parentOutline ← GGObjects.OutlineOfTraj[seq.traj];
IF OnOverlay[parentOutline] THEN ERROR;
parentOutline.onOverlay ← TRUE;
parentOutline.whyOnOverlay ← seq;
gargoyleData.refresh.overlayList ← CONS[parentOutline, gargoyleData.refresh.overlayList];
};
caret: Caret => {
IF OnOverlay[caret] THEN ERROR;
GGCaret.TellOnOverlay[caret, TRUE];
gargoyleData.refresh.overlayList ← CONS[caret, gargoyleData.refresh.overlayList];
};
ENDCASE => ERROR NotYetImplemented;
};
MoveToBackground: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData] = {
IF NOT OnOverlay[entity] THEN RETURN;
gargoyleData.refresh.overlayList ← GGUtility.DeleteEntityFromList[entity, gargoyleData.refresh.overlayList];
WITH entity SELECT FROM
outline: Outline => outline.onOverlay ← FALSE;
caret: Caret => GGCaret.TellOnOverlay[caret, FALSE];
ENDCASE => ERROR NotYetImplemented;
};
MoveAllSelectedToOverlay: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] = {
entityGen: EntityGenerator;
entityGen ← GGSelect.SelectedEntities[gargoyleData, selectClass];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
MoveToOverlay[entity, gargoyleData];
ENDLOOP;
};
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
outline: Outline => outline.onOverlay ← FALSE;
caret: Caret => GGCaret.TellOnOverlay[caret, FALSE];
traj: Traj => {}; -- can only happen if Gargoyle is confused. Normally, trajectories are only added and removed by MoveJointsToOverlay[] and RemoveJointsFromOverlay[], above.
ENDCASE => ERROR NotYetImplemented;
ENDLOOP;
gargoyleData.refresh.overlayList ← NIL;
};
EmptyOverlay: PUBLIC PROC [gargoyleData: GargoyleData] RETURNS [BOOL] = {
RETURN[gargoyleData.refresh.overlayList = NIL];
};
OnOverlay: PROC [entity: REF ANY] RETURNS [BOOL] = {
WITH entity SELECT FROM
outline: Outline => RETURN[outline.onOverlay];
caret: Caret => RETURN[GGCaret.IsOnOverlay[caret]];
ENDCASE => ERROR;
};
Painting Commands
EraseOverlay: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
Do no actual drawing. Send the background bits to the chunking bitmap and wait for a call to PaintDragEntities.
camera: Camera ← gargoyleData.camera;
background: ImagerBackdoor.Bitmap ← gargoyleData.refresh.backgroundBitmap;
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
ImagerBackdoor.DrawBits[chunkingContext, background.base, background.wordsPerLine, 0, 0, background.height, background.width, 0, background.height];
};
PaintEntireScene: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
chunkingBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.chunkingBitmap;
camera: Camera ← gargoyleData.camera;
rect: Imager.Rectangle;
PaintEntireSceneAux: PROC = {
Erase viewer first.
rect ← ImagerBackdoor.GetBounds[chunkingContext];
Imager.SetColor[chunkingContext, Imager.white];
Imager.MaskRectangle[chunkingContext, rect];
Draw the objects.
WorldCoordsContext[chunkingContext, camera];
DrawObjects[chunkingContext, gargoyleData];
DrawCaret[chunkingContext, gargoyleData.caret, Imager.black];
DrawAnchor[chunkingContext, gargoyleData.anchor, Imager.black];
};
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
IF gargoyleData.refresh.doubleBuffer.state = on THEN {
Imager.DoSaveAll[chunkingContext, PaintEntireSceneAux];
Now, dump on the screen.
ImagerBackdoor.DrawBits[dc, chunkingBitmap.base, chunkingBitmap.wordsPerLine, 0, 0, chunkingBitmap.height, chunkingBitmap.width, 0, chunkingBitmap.height];
}
ELSE {
Erase viewer first.
rect ← ImagerBackdoor.GetBounds[dc];
Imager.SetColor[dc, Imager.white];
Imager.MaskRectangle[dc, rect];
Draw the objects.
WorldCoordsContext[dc, camera];
DrawObjects[dc, gargoyleData];
DrawCaret[dc, gargoyleData.caret, Imager.black];
DrawAnchor[dc, gargoyleData.anchor, Imager.black];
};
};
PaintSelectedRegion: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
chunkingBitmap: ImagerBackdoor.Bitmap ← gargoyleData.refresh.chunkingBitmap;
background: ImagerBackdoor.Bitmap ← gargoyleData.refresh.backgroundBitmap;
camera: Camera ← gargoyleData.camera;
boundBox: BoundBox;
rect: Imager.Rectangle;
PaintSelectedRegionAux: PROC = {
Draw the background.
ImagerBackdoor.DrawBits[chunkingContext, background.base, background.wordsPerLine, 0, 0, background.height, background.width, 0, background.height];
Set up world coordinates and clip.
WorldCoordsContext[chunkingContext, camera];
GGBoundBox.Clip[chunkingContext, boundBox];
Erase the region first.
rect ← ImagerBackdoor.GetBounds[chunkingContext];
Imager.SetColor[chunkingContext, Imager.white];
Imager.MaskRectangle[chunkingContext, rect];
Draw the objects.
DrawObjects[chunkingContext, gargoyleData];
DrawCaret[chunkingContext, gargoyleData.caret, Imager.black];
DrawAnchor[chunkingContext, gargoyleData.anchor, Imager.black];
};
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
boundBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData];
IF gargoyleData.refresh.doubleBuffer.state = on THEN {
Imager.DoSaveAll[chunkingContext, PaintSelectedRegionAux];
Now, dump on the screen.
ImagerBackdoor.DrawBits[dc, chunkingBitmap.base, chunkingBitmap.wordsPerLine, 0, 0, chunkingBitmap.height, chunkingBitmap.width, 0, chunkingBitmap.height];
}
ELSE {
ImagerBackdoor.DrawBits[dc, background.base, background.wordsPerLine, 0, 0, background.height, background.width, 0, background.height];
Set up world coordinates and clip.
WorldCoordsContext[dc, camera];
GGBoundBox.Clip[dc, boundBox];
Erase the region first.
rect ← ImagerBackdoor.GetBounds[dc];
Imager.SetColor[dc, Imager.white];
Imager.MaskRectangle[dc, rect];
Draw the objects.
DrawObjects[dc, gargoyleData];
DrawCaret[dc, gargoyleData.caret, Imager.black];
DrawAnchor[dc, gargoyleData.anchor, Imager.black];
};
};
InterpressEntireScene: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
camera: Camera ← gargoyleData.camera;
WorldCoordsContext[dc, camera];
DrawObjects[dc, gargoyleData];
};
StoreBackground: PUBLIC PROC [gargoyleData: GargoyleData] = {
Draw all but the overlay objects into a bitmap.
PaintAllButSelected: PROC = {
camera: Camera ← gargoyleData.camera;
scene: Scene ← gargoyleData.scene;
rect: Imager.Rectangle;
Erase the bitmap.
rect ← ImagerBackdoor.GetBounds[gargoyleData.refresh.backgroundContext];
Imager.SetColor[gargoyleData.refresh.backgroundContext, Imager.white];
Imager.MaskRectangle[gargoyleData.refresh.backgroundContext, rect];
Draw most of the scene.
Imager.SetColor[gargoyleData.refresh.backgroundContext, Imager.black];
Imager.TranslateT[gargoyleData.refresh.backgroundContext, [camera.cameraScreen[1], camera.cameraScreen[2]]];
Worry about scene objects.
FOR entities: LIST OF REF ANY ← gargoyleData.scene.entities, entities.rest UNTIL entities = NIL DO
WITH entities.first SELECT FROM
outline: Outline => {
IF OnOverlay[outline] THEN LOOP
ELSE DrawOutline[gargoyleData.refresh.backgroundContext, outline, gargoyleData];
};
cluster: Cluster => StoreCluster[gargoyleData.refresh.backgroundContext, cluster, gargoyleData];
ENDCASE => ERROR;
ENDLOOP;
Worry about the caret.
IF NOT OnOverlay[gargoyleData.caret] THEN DrawCaret[gargoyleData.refresh.backgroundContext, gargoyleData.caret, Imager.black];
DrawAnchor[gargoyleData.refresh.backgroundContext, gargoyleData.anchor, Imager.black];
Worry about alignment lines.
IF gargoyleData.hitTest.linesAlwaysOn.state = on THEN
GGGravity.DrawObjectBagRegardless[gargoyleData.refresh.backgroundContext, NARROW[gargoyleData.hitTest.environ], camera, gargoyleData];
};
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
Imager.DoSaveAll[gargoyleData.refresh.backgroundContext, PaintAllButSelected];
};
StoreCluster: PRIVATE PROC [backgroundContext: Imager.Context, cluster: Cluster, gargoyleData: GargoyleData] = {
FOR children: LIST OF REF ANY ← cluster.children, children.rest UNTIL children = NIL DO
WITH children.first SELECT FROM
outline: Outline => {
IF OnOverlay[outline] THEN LOOP
ELSE DrawOutline[backgroundContext, outline, gargoyleData];
};
cluster: Cluster => StoreCluster[backgroundContext, cluster, gargoyleData];
ENDCASE => ERROR;
ENDLOOP;
};
PaintOverlay: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
Write 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;
camera: Camera ← gargoyleData.camera;
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
PaintOverlayAux: PROC = {
WorldCoordsContext[chunkingContext, camera];
DrawOverlay[chunkingContext, gargoyleData];
};
Imager.DoSaveAll[chunkingContext, PaintOverlayAux];
Now, dump on the screen.
ImagerBackdoor.DrawBits[dc, chunkingBitmap.base, chunkingBitmap.wordsPerLine, 0, 0, chunkingBitmap.height, chunkingBitmap.width, 0, chunkingBitmap.height];
};
PaintDragOverlay: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
Write 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;
camera: Camera ← gargoyleData.camera;
chunkingContext: Imager.Context ← gargoyleData.refresh.chunkingContext;
PaintDragEntitiesAux: PROC = {
WorldCoordsContext[chunkingContext, camera];
DrawDragOverlay[chunkingContext, gargoyleData];
};
IF gargoyleData.refresh.suppressRefresh THEN RETURN;
Imager.DoSaveAll[chunkingContext, PaintDragEntitiesAux];
Now, dump on the screen.
ImagerBackdoor.DrawBits[dc, chunkingBitmap.base, chunkingBitmap.wordsPerLine, 0, 0, chunkingBitmap.height, chunkingBitmap.width, 0, chunkingBitmap.height];
};
DrawOverlay: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
Simply draw all overlay objects right where they are.
FOR overlayList: LIST OF REF ANY ← gargoyleData.refresh.overlayList, overlayList.rest UNTIL overlayList = NIL DO
WITH overlayList.first SELECT FROM
seq: Sequence => {};
traj: Traj => DrawJoints[dc, traj];
outline: Outline => DrawOutline[dc, outline];
cluster: Cluster => DrawCluster[dc, cluster];
caret: Caret => DrawCaret[dc, caret, Imager.black];
ENDCASE => ERROR;
ENDLOOP;
And also draw object bag if it is "on overlay".
IF gargoyleData.hitTest.linesAlwaysOn.state = off THEN
GGGravity.DrawObjectBag[dc, NARROW[gargoyleData.hitTest.environ], gargoyleData.camera];
};
DrawDragOverlay: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
This could use some optimizing.
outline: Outline;
Draw object bag if it is "on overlay".
IF gargoyleData.hitTest.linesAlwaysOn.state = off THEN
GGGravity.DrawObjectBag[dc, NARROW[gargoyleData.hitTest.environ], gargoyleData.camera, gargoyleData];
Handle rubberbanding first.
FOR overlayList: LIST OF REF ANY ← gargoyleData.refresh.overlayList, overlayList.rest UNTIL overlayList = NIL DO
WITH overlayList.first SELECT FROM
outline: Outline => {
WITH outline.whyOnOverlay SELECT FROM
seq: Sequence => {
Draw the outline to which the selected sequence belongs. Within the trajectory of interest, individually transform each of the selected points.
IF seq.all THEN LOOP;
DrawOutlineTransformSeq[dc, outline, seq, gargoyleData.drag.transform, gargoyleData];
};
ENDCASE => {};
};
traj: Traj => DrawJoints[dc, traj, gargoyleData];
caret: Caret => {
caret ← NARROW[overlayList.first];
DrawCaret[dc, caret, Imager.black];
};
ENDCASE => ERROR;
ENDLOOP;
Now handle the dragging cases.
Imager.ConcatT[dc, gargoyleData.drag.transform];
FOR overlayList: LIST OF REF ANY ← gargoyleData.refresh.overlayList, overlayList.rest UNTIL overlayList = NIL DO
IF ISTYPE[overlayList.first, Outline] THEN {
outline ← NARROW[overlayList.first];
WITH outline.whyOnOverlay SELECT FROM
seq: Sequence => {
IF seq.all THEN DrawOutline[dc, outline, gargoyleData];
};
traj: Traj => ERROR;
outline: Outline => DrawOutline[dc, outline, gargoyleData];
cluster: Cluster => DrawCluster[dc, cluster, gargoyleData];
ENDCASE => ERROR;
};
ENDLOOP;
};
DrawObjects: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
The common core of PaintEntireScene and InterpressEntireScene.
scene: Scene ← gargoyleData.scene;
Imager.SetColor[dc, Imager.black];
FOR entities: LIST OF REF ANY ← scene.entities, entities.rest UNTIL entities = NIL DO
WITH entities.first SELECT FROM
outline: Outline => DrawOutline[dc, outline, gargoyleData];
cluster: Cluster => ERROR;
ENDCASE => ERROR;
ENDLOOP;
};
DrawCaret: PROC [dc: Imager.Context, caret: Caret, color: Imager.Color] = {
caretPos: Point ← GGCaret.GetPoint[caret];
IF NOT GGCaret.Exists[caret] THEN RETURN;
Imager.SetColor[dc, color];
GGShapes.DrawCaret[dc, caretPos];
};
DrawAnchor: PROC [dc: Imager.Context, caret: Caret, color: Imager.Color] = {
caretPos: Point ← GGCaret.GetPoint[caret];
IF NOT GGCaret.Exists[caret] THEN RETURN;
Imager.SetColor[dc, color];
GGShapes.DrawAnchor[dc, caretPos];
};
Debugging
PaintNewAnchor: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
camera: Camera ← gargoyleData.camera;
anchorPos: Point ← GGCaret.GetPoint[gargoyleData.anchor];
Imager.SetColor[dc, Imager.black];
WorldCoordsContext[dc, camera];
GGShapes.DrawAnchor[dc, anchorPos];
};
PaintSpot: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
camera: Camera ← gargoyleData.camera;
Imager.SetColor[dc, Imager.black];
WorldCoordsContext[dc, camera];
GGShapes.DrawSpot[dc, gargoyleData.refresh.spotPoint];
};
PaintHitLine: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
camera: Camera ← gargoyleData.camera;
Imager.SetColor[dc, Imager.black];
WorldCoordsContext[dc, camera];
Imager.MaskVector[dc, [gargoyleData.refresh.spotPoint[1], gargoyleData.refresh.spotPoint[2]], [gargoyleData.refresh.hitPoint[1], gargoyleData.refresh.hitPoint[2]]];
GGShapes.DrawFilledRect[dc, gargoyleData.refresh.spotPoint, 3.0];
};
PaintAlign: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
camera: Camera ← gargoyleData.camera;
Imager.SetColor[dc, ImagerColor.ColorFromGray[0.5]];
WorldCoordsContext[dc, camera];
GGGravity.DrawObjectBagRegardless[dc, NARROW[gargoyleData.hitTest.environ], camera, gargoyleData];
};
PaintTouchPoints: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
WorldCoordsContext[dc, gargoyleData.camera];
GGTouch.DrawAllTouchPoints[dc, gargoyleData];
};
PaintBoundBoxes: PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
bBoxGen: BoundBoxGenerator;
camera: Camera ← gargoyleData.camera;
PaintBoundBoxesAux: PROC = {
Draw the boxes.
WorldCoordsContext[dc, camera];
bBoxGen ← GGObjects.BoundBoxesInScene[gargoyleData.scene];
FOR box: BoundBox ← GGObjects.NextBox[bBoxGen], GGObjects.NextBox[bBoxGen] UNTIL box = NIL DO
GGBoundBox.DrawBoundBox[dc, box];
ENDLOOP;
};
Imager.DoSaveAll[dc, PaintBoundBoxesAux];
};
PaintSelectionBox: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
camera: Camera ← gargoyleData.camera;
box: BoundBox;
PaintSelectionBoxAux: PROC = {
Draw the box.
WorldCoordsContext[dc, camera];
box ← GGBoundBox.BoundBoxOfSelected[gargoyleData];
GGBoundBox.DrawBoundBox[dc, box];
};
Imager.DoSaveAll[dc, PaintSelectionBoxAux];
};
EraseAll: PROC [dc: Imager.Context] = {
rect: Imager.Rectangle;
rect ← ImagerBackdoor.GetBounds[dc];
Imager.SetColor[dc, Imager.white];
Imager.MaskRectangle[dc, rect];
};
Init[];
END.