-- JaMGraphicsImpl.mesa
-- Mesa 6 version
-- Last changed by Doug Wyatt, March 30, 1981 1:43 PM

DIRECTORY
JaMGraphicsDefs,
Graphics USING [DeviceContext, DisplayContext,
NewContext, FreeContext, InitContext, PushContext, PopContext,
PaintingFunction, white, Color, SetPaint, SetTexture, SetColor,
Translate, Scale, Rotate, Concatenate, ScreenToUser,
SetLineWidth, GetPosition, MoveTo, DrawTo, RelMoveTo, RelDrawTo,
DrawCubic, DrawRectangle, DrawScreenArea,
StartAreaPath, EnterPoint, EnterCubic, NewBoundary, DrawArea,
MakeFont, SetFont, DisplayChar, DisplayString,
CharData, GetCharBox, GetStringBox, GetFontBox,
BoundingBox, InitBoxer, StopBoxer, PushClipBox, PopClipBox,
Visible, SetClipArea, IntersectClipArea],
AltoDevice USING [NewAltoDevice, ScreenBitmap, ScreenOrigin],
PressDevice USING [NewPressDevice],
Device USING [Object, Free],
OpaqueDevice USING [],
Vector USING [Vec, Matrix, Sub, Cross, Min, Max],
Cubic USING [Coeffs, Bezier, CoeffsToBezier, BezierToCoeffs],
JaMFnsDefs USING [PopInteger, PushReal, GetReal, PopString,
PushBoolean, Register, SetMouseProc, JaMStream, GetJaMBreak],
KeyDefs USING [Mouse];

JaMGraphicsImpl: PROGRAM
IMPORTS JaMFnsDefs,Graphics,AltoDevice,PressDevice,Device,Vector,Cubic
EXPORTS JaMGraphicsDefs, OpaqueDevice = {
OPEN J:JaMFnsDefs,G:Graphics;

Vec: TYPE = Vector.Vec;
Coeffs: TYPE = Cubic.Coeffs;

DeviceObject: PUBLIC TYPE = Device.Object;

dev: G.DeviceContext←NIL;
pressdev: G.DeviceContext←NIL;
olddc: G.DisplayContext←NIL;
mousedc: G.DisplayContext←NIL;
dc: G.DisplayContext←NIL;

GetDC: PUBLIC PROCEDURE RETURNS[G.DisplayContext] = {
RETURN[dc];
};

InitDC: PROCEDURE = {
G.InitContext[dc];
};

PushDC: PROCEDURE = {
G.PushContext[dc];
};

PopDC: PROCEDURE = {
G.PopContext[dc];
};

Point: TYPE = Vec;

GetPoint: PROCEDURE[p: POINTER TO Point] = {
p.y ← J.GetReal[];
p.x ← J.GetReal[];
};

PutPoint: PROCEDURE[p: POINTER TO Point] = {
J.PushReal[p.x];
J.PushReal[p.y];
};

Rect: TYPE = RECORD[ll,ur: Point];

GetRect: PROCEDURE[r: POINTER TO Rect] = {
GetPoint[@r.ur];
GetPoint[@r.ll];
};

PutRect: PROCEDURE[r: POINTER TO Rect] = {
PutPoint[@r.ll];
PutPoint[@r.ur];
};

JSetVw: PROCEDURE = {
rf: Rect;
rt: Rect;
GetRect[@rt];
GetRect[@rf];
-- *** fix this
-- SetView[dc,@rf,@rt];
};

JEnterRect: PROCEDURE = {
r: Rect;
ll,ur: Vec;
GetRect[@r];
ll←Vector.Min[r.ll,r.ur]; ur←Vector.Max[r.ll,r.ur];
G.EnterPoint[dc,ll];
G.EnterPoint[dc,[ll.x,ur.y]];
G.EnterPoint[dc,ur];
G.EnterPoint[dc,[ur.x,ll.y]];
G.NewBoundary[dc];
};

JSetClip: PROCEDURE = {
G.StartAreaPath[dc];
JEnterRect;
G.SetClipArea[dc];
};

JClipArea: PROCEDURE = {
G.SetClipArea[dc];
};

JClippedClipArea: PROCEDURE = {
G.IntersectClipArea[dc];
};

JTranslate: PROCEDURE = {
p: Point;
GetPoint[@p];
G.Translate[dc,p];
};


JScale: PROCEDURE = {
p: Point;
GetPoint[@p];
G.Scale[dc,p];
};

JRotate: PROCEDURE = {
a: REAL;
a ← J.GetReal[];
G.Rotate[dc,a];
};

JSixPoint: PROCEDURE = {
f1,f2,f3,t1,t2,t3,o1,df1,df2,dt1,dt2:Point;
adet:REAL;
m:Vector.Matrix;
GetPoint[@t3];
GetPoint[@t2];
GetPoint[@t1];
GetPoint[@f3];
GetPoint[@f2];
GetPoint[@f1];
o1←Vector.Sub[t1,f1];
dt1←Vector.Sub[t2,t1];
df1←Vector.Sub[f2,f1];
dt2←Vector.Sub[t3,t1];
df2←Vector.Sub[f3,f1];
adet←1.0/Vector.Cross[df1,df2];
m.a11←(dt1.x*df2.y-dt2.x*df1.y)*adet;
m.a12←(df1.x*dt2.x-df2.x*dt1.x)*adet;
m.a21←(dt1.y*df2.y-dt2.y*df1.y)*adet;
m.a22←(df1.x*dt2.y-df2.x*dt1.y)*adet;
G.Translate[dc,o1];
G.Concatenate[dc,m]
};

JConcat: PROCEDURE = {
m: Vector.Matrix;
m.a22 ← J.GetReal[];
m.a21 ← J.GetReal[];
m.a12 ← J.GetReal[];
m.a11 ← J.GetReal[];
G.Concatenate[dc,m];
};


JLineWidth: PROCEDURE = {
w: REAL ← J.GetReal[];
G.SetLineWidth[dc,w];
};

JGetPos: PROCEDURE = {
p: Point←G.GetPosition[dc];
PutPoint[@p];
};

JMoveTo: PROCEDURE = {
p: Point;
GetPoint[@p];
G.MoveTo[dc,p];
};

JDrawTo: PROCEDURE = {
p: Point;
GetPoint[@p];
G.DrawTo[dc,p];
};

JDot: PROCEDURE = {
p: Point;
GetPoint[@p];
G.MoveTo[dc,p];
G.DrawTo[dc,p];
};

JRelMoveTo: PROCEDURE = {
p: Point;
GetPoint[@p];
G.RelMoveTo[dc,p];
};

JRelDrawTo: PROCEDURE = {
p: Point;
GetPoint[@p];
G.RelDrawTo[dc,p];
};

JDrawBoxArea: PROCEDURE = {
r: Rect;
GetRect[@r];
G.DrawRectangle[dc,r.ll,r.ur];
};

JDrawScreenArea: PUBLIC PROCEDURE = {
G.DrawScreenArea[dc];
};

JSetPaint: PROCEDURE = {
f: INTEGER;
p: G.PaintingFunction;
f←J.PopInteger[];
SELECT f FROM
0 => p←paint;
1 => p←replace;
2 => p←invert;
3 => p←erase;
ENDCASE => p←paint;
G.SetPaint[dc,p];
};

JSetTexture: PROCEDURE = {
t: CARDINAL;
t←LOOPHOLE[J.PopInteger[],CARDINAL];
G.SetTexture[dc,t];
};

JSetColor: PROCEDURE = {
h,s,b: CARDINAL;
b←LOOPHOLE[J.PopInteger[],CARDINAL];
s←LOOPHOLE[J.PopInteger[],CARDINAL];
h←LOOPHOLE[J.PopInteger[],CARDINAL];
G.SetColor[dc,G.Color[h,s,b]];
};

JErase: PROCEDURE = {
G.PushContext[dc];
G.SetTexture[dc,G.white];
G.SetPaint[dc,replace];
G.DrawScreenArea[dc];
G.PopContext[dc];
};

Coords: TYPE = MACHINE DEPENDENT RECORD[x,y: INTEGER];
mouse: POINTER TO Coords = LOOPHOLE[424B];
cursor: POINTER TO Coords = LOOPHOLE[426B];

JGetTouch: PROCEDURE = {
UNTIL KeyDefs.Mouse.buttons = Red DO
IF J.GetJaMBreak[] THEN RETURN
ENDLOOP;
UNTIL KeyDefs.Mouse.buttons = None DO
IF J.GetJaMBreak[] THEN RETURN
ENDLOOP;
JGetMouse;
};

JGetMouse: PROCEDURE = {
p: Point←ScreenCoordsToUser[mouse↑];
PutPoint[@p];
};

MouseToUser: PROCEDURE[x,y:INTEGER] = {
p: Point←ScreenCoordsToUser[[x,y]];
PutPoint[@p];
};

half: REAL=0.5;

ScreenCoordsToUser: PROCEDURE[c: Coords] RETURNS[Point] = {
x0,y0: INTEGER;
pt: Point;
[x0,y0]←AltoDevice.ScreenOrigin[];
pt←[(c.x-x0)+half,(c.y-y0)+half]; -- the center of the pixel
RETURN[G.ScreenToUser[mousedc,pt]];
};

JSetFont: PROCEDURE = {
fam: STRING ← [30];
size: REAL;
size←J.GetReal[];
J.PopString[fam];
G.SetFont[dc,G.MakeFont[fam],size];
};

JDrawChar: PROCEDURE = {
s: STRING ← [5];
J.PopString[s];
G.DisplayChar[dc,s[0]];
};

JDrawChars: PROCEDURE = {
s: STRING ← [256];
J.PopString[s];
FOR i:CARDINAL IN[0..s.length) DO
G.DisplayChar[dc,s[i]];
ENDLOOP;
};

JEraseChar: PROCEDURE = {
s: STRING ← [5];
J.PopString[s];
-- *** fix this
-- EraseChar[dc,s[0]];
};

JDrawString: PROCEDURE = {
s: STRING ← [256];
J.PopString[s];
G.DisplayString[dc,s]
};

JGetCharBox: PROCEDURE = {
s: STRING ← [5];
box: G.CharData;
J.PopString[s];
G.GetCharBox[dc,s[0],@box];
J.PushReal[box.size.x];
J.PushReal[box.size.y];
J.PushReal[box.origin.x];
J.PushReal[box.origin.y];
J.PushReal[box.width.x];
J.PushReal[box.width.y];
};

JGetStringBox: PROCEDURE = {
s: STRING ← [256];
box: G.CharData;
J.PopString[s];
G.GetStringBox[dc,s,@box];
J.PushReal[box.size.x];
J.PushReal[box.size.y];
J.PushReal[box.origin.x];
J.PushReal[box.origin.y];
J.PushReal[box.width.x];
J.PushReal[box.width.y];
};

JGetFontBox: PROCEDURE = {
box: G.CharData;
G.GetFontBox[dc,@box];
J.PushReal[box.size.x];
J.PushReal[box.size.y];
J.PushReal[box.origin.x];
J.PushReal[box.origin.y];
J.PushReal[box.width.x];
J.PushReal[box.width.y];
};

JEnterCubic: PROCEDURE = {
c: Coeffs;
GetPoint[@c.c3];
GetPoint[@c.c2];
GetPoint[@c.c1];
GetPoint[@c.c0];
G.EnterCubic[dc,@c];
};

JDrawCubic: PROCEDURE = {
c: Coeffs;
GetPoint[@c.c3];
GetPoint[@c.c2];
GetPoint[@c.c1];
GetPoint[@c.c0];
G.DrawCubic[dc,@c];
};

JBezierToCubic: PROCEDURE = {
b: Cubic.Bezier;
c: Coeffs;
GetPoint[@b.b3];
GetPoint[@b.b2];
GetPoint[@b.b1];
GetPoint[@b.b0];
c←Cubic.BezierToCoeffs[b];
PutPoint[@c.c0];
PutPoint[@c.c1];
PutPoint[@c.c2];
PutPoint[@c.c3];
};

JCubicToBezier: PROCEDURE = {
c: Coeffs;
b: Cubic.Bezier;
GetPoint[@c.c3];
GetPoint[@c.c2];
GetPoint[@c.c1];
GetPoint[@c.c0];
b←Cubic.CoeffsToBezier[c];
PutPoint[@b.b0];
PutPoint[@b.b1];
PutPoint[@b.b2];
PutPoint[@b.b3];
};

GetBoundingBox: PROCEDURE[b: POINTER TO G.BoundingBox] = {
FOR i: CARDINAL IN[0..4) DO GetPoint[@b[i]] ENDLOOP;
};

PutBoundingBox: PROCEDURE[b: POINTER TO G.BoundingBox] = {
FOR i: CARDINAL IN[0..4) DO PutPoint[@b[i]] ENDLOOP;
};

JStartBoxing: PROCEDURE = {
G.InitBoxer[dc];
};

JStopBoxing: PROCEDURE = {
b: G.BoundingBox;
G.StopBoxer[dc,@b];
PutBoundingBox[@b];
};

JPushBox: PROCEDURE = {
b: G.BoundingBox;
GetBoundingBox[@b];
G.PushClipBox[dc,@b];
};

JPopBox: PROCEDURE = {
G.PopClipBox[dc];
};

JVisible: PROCEDURE = {
J.PushBoolean[G.Visible[dc]];
};

JStartPath: PROCEDURE = {
G.StartAreaPath[dc];
};

JStartEOPath: PROCEDURE = {
G.StartAreaPath[dc,TRUE];
};

JEnterPoint: PROCEDURE = {
p: Point;
GetPoint[@p];
G.EnterPoint[dc,p];
};

JNewBoundary: PROCEDURE = {
G.NewBoundary[dc];
};

JDrawArea: PROCEDURE = {
G.DrawArea[dc];
};


JOpenPress: PROCEDURE = {
pressfilename: STRING←[64];
J.PopString[pressfilename];
pressdev←PressDevice.NewPressDevice[pressfilename];
olddc←dc;
dc←G.NewContext[pressdev]
};

JClosePress: PROCEDURE = {
G.FreeContext[@dc];
dc←olddc;
Device.Free[@pressdev];
};


-- Initialization starts here

dev←AltoDevice.NewAltoDevice[AltoDevice.ScreenBitmap[]];
dc←G.NewContext[dev];
mousedc←dc;

J.SetMouseProc[J.JaMStream[],MouseToUser];

J.Register[".initdc"L,InitDC];
J.Register[".pushdc"L,PushDC];
J.Register[".popdc"L,PopDC];
J.Register[".setview"L,JSetVw];
J.Register[".translate"L,JTranslate];
J.Register[".scale"L,JScale];
J.Register[".rotate"L,JRotate];
J.Register[".sixpoint"L,JSixPoint];
J.Register[".concat"L,JConcat];
J.Register[".linewidth"L,JLineWidth];
J.Register[".getpos"L,JGetPos];
J.Register[".moveto"L,JMoveTo];
J.Register[".rmoveto"L,JRelMoveTo];
J.Register[".drawto"L,JDrawTo];
J.Register[".rdrawto"L,JRelDrawTo];
J.Register[".dot"L,JDot];
J.Register[".drawboxarea"L,JDrawBoxArea];
J.Register[".drawscreenarea"L,JDrawScreenArea];
J.Register[".paint"L,JSetPaint];
J.Register[".texture"L,JSetTexture];
J.Register[".color"L,JSetColor];
J.Register[".entercubic"L,JEnterCubic];
J.Register[".drawcubic"L,JDrawCubic];
J.Register[".beziertocubic"L,JBezierToCubic];
J.Register[".cubictobezier"L,JCubicToBezier];
J.Register[".touch"L,JGetTouch];
J.Register[".mouse"L,JGetMouse];
J.Register[".erase"L,JErase];
J.Register[".setfont"L,JSetFont];
J.Register[".drawchar"L,JDrawChar];
J.Register[".drawchars"L,JDrawChars];
J.Register[".erasechar"L,JEraseChar];
J.Register[".drawstring"L,JDrawString];
J.Register[".getcharbox"L,JGetCharBox];
J.Register[".getstringbox"L,JGetStringBox];
J.Register[".getfontbox"L,JGetFontBox];
J.Register[".initboxer"L,JStartBoxing];
J.Register[".stopboxer"L,JStopBoxing];
J.Register[".pushbox"L,JPushBox];
J.Register[".popbox"L,JPopBox];
J.Register[".visible"L,JVisible];
J.Register[".setclip"L,JSetClip];
J.Register[".cliparea"L,JClipArea];
J.Register[".clippedcliparea"L,JClippedClipArea];
J.Register[".startpath"L,JStartPath];
J.Register[".starteopath"L,JStartEOPath];
J.Register[".enterpoint"L,JEnterPoint];
J.Register[".newboundary"L,JNewBoundary];
J.Register[".enterrect"L,JEnterRect];
J.Register[".drawarea"L,JDrawArea];
J.Register[".openpress",JOpenPress];
J.Register[".closepress",JClosePress];

}.