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]]];
};
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.camera
Screen[1], camera.camera
Screen[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];
};
END.