-- TAGraphicsHacksImpl.mesa
-- Rick Beach, April 15, 1983 5:08 pm
-- Maureen Stone April 19, 1983 11:48 am

DIRECTORY
 CGArea USING [Empty, Ref, Remove],
 CGContext USING [Rep],
 CGDevice USING [Ref, Rep],
 CGMatrix USING [Make, Ref],
 CGSource USING [Ref],
 CGStorage USING [qZone],
 Graphics USING [Box, Context, NewContext],
 GraphicsBasic USING [Trap],
 JaMFnsDefs USING [PushReal, Register],
 JaMOps USING [defaultFrame],
 Real,
 TAGraphicsHacks,
 TJaMGraphicsContexts USING [AddContext, EnableContext, DisableContext];

TAGraphicsHacksImpl: PROGRAM
IMPORTS CGArea, CGMatrix, CGStorage, Graphics, JaMFnsDefs, JaMOps, TJaMGraphicsContexts
EXPORTS GraphicsBasic, TAGraphicsHacks = {

-- convince the world that I know what is in these opaque types
DeviceObject: PUBLIC TYPE = CGDevice.Rep;
ContextObject: PUBLIC TYPE = CGContext.Rep;

-- a global bounds box
boundsBox: Graphics.Box;
nullBoundsBox: Graphics.Box = [Real.LargestNumber, Real.LargestNumber, -Real.LargestNumber, -Real.LargestNumber];

Data: TYPE = REF DataRep;
DataRep: TYPE = RECORD [
matrix: CGMatrix.Ref ← NIL -- base transformation matrix
];
dataZone: ZONE = CGStorage.qZone;
repZone: ZONE = CGStorage.qZone;

NewBoxerDevice: PROCEDURE RETURNS[CGDevice.Ref] = {
 data: Data ← dataZone.NEW[DataRep];
 data.matrix ← CGMatrix.Make[[1,0,0,1,0,0]];
 boundsBox ← nullBoundsBox;
RETURN[repZone.NEW[CGDevice.Rep ← [
 GetMatrix: GetMatrix, GetBounds: GetBounds, Show: BoxerShow, GetRaster: BoxerGetRaster, data: data]]];
};

InitBoxerDevice: PROCEDURE RETURNS [Graphics.Context] = {
RETURN [Graphics.NewContext[NewBoxerDevice[]]];
 };

GetMatrix: SAFE PROC[self: CGDevice.Ref] RETURNS[CGMatrix.Ref] = TRUSTED {
data: Data ← NARROW[self.data];
RETURN[data.matrix];
};

GetBounds: SAFE PROC[self: CGDevice.Ref] RETURNS[Graphics.Box] = TRUSTED {
RETURN[[0.1, 0.1, 4000-.1, 4000-.1]]};

BoxerShow: SAFE PROC[self: CGDevice.Ref, area: CGArea.Ref, src: CGSource.Ref, map: CGMatrix.Ref] = TRUSTED {
-- extract the bounding box from the area and update the context bbox
UNTIL CGArea.Empty[area] DO
  trap: GraphicsBasic.Trap ← CGArea.Remove[area];
 { OPEN boundsBox, trap;
   xmin ← MIN[xmin, xbotL];
   xmin ← MIN[xmin, xtopL];
   ymin ← MIN[ymin, ybot];
   xmax ← MAX[xmax, xbotR];
   xmax ← MAX[xmax, xtopR];
   ymax ← MAX[ymax, ytop]};
  ENDLOOP;
 };

BoxerGetRaster: SAFE PROC[self: CGDevice.Ref] RETURNS[LONG POINTER,CARDINAL] = TRUSTED {
-- no raster for this device (sic!)
RETURN[NIL,0];
 };

JBeginBox: PROCEDURE[] = {
-- JaM callable procedure to begin bounding boxing
 BeginBox[];
 };

JEndBox: PROCEDURE[] = {
 box: Graphics.Box;
 box ← EndBox[];
 JaMFnsDefs.PushReal[box.xmin];
 JaMFnsDefs.PushReal[box.ymin];
 JaMFnsDefs.PushReal[box.xmax];
 JaMFnsDefs.PushReal[box.ymax];
 };

BeginBox: PUBLIC PROCEDURE = {
 init: PROC[dc: Graphics.Context] = {Graphics.Erase[dc]};
-- reset the boundsBox and and turn on the Boxer
 TJaMGraphicsContexts.EnableContext[JaMOps.defaultFrame, $Boxer];
 TJaMGraphics.Painter[init];
 };

EndBox: PUBLIC PROCEDURE[] RETURNS[Graphics.Box] = {
-- Disable the context and return the bounding box on the JaM stack
 TJaMGraphicsContexts.DisableContext[JaMOps.defaultFrame, $Boxer];
IF boundsBox=nullBoundsBox THEN RETURN[[0,0,0,0]]
ELSE RETURN[boundsBox] };

JaMFnsDefs.Register[".beginbox", JBeginBox];
JaMFnsDefs.Register[".endbox", JEndBox];
TJaMGraphicsContexts.AddContext[JaMOps.defaultFrame, InitBoxerDevice, $Boxer, FALSE];

}.