-- Callig.mesa, for drawing thick lines from JaM.
-- Maureen Stone August 27, 1982 11:23 am
-- Michael Plass 10-Mar-82 9:17:44
-- Rick Beach, May 26, 1982 10:50 am

DIRECTORY
 Complex, Cubic, Graphics, JaMFnsDefs, JaMGraphics, PolygonPen, Real, RealFns, Rope, SirPress, Vector;

Callig: PROGRAM
 IMPORTS Complex, Cubic, Graphics, JaMFnsDefs, JaMGraphics, PolygonPen, Real, RealFns, Rope, SirPress = {

Vec: TYPE = Vector.Vec;
PathType: TYPE = {moveTo, lineTo, curveTo};
PathNode: TYPE = RECORD [
 next: REF PathNode ← NIL,
 pathInfo: SELECT pathType: PathType FROM
  moveTo => [z0: Vector.Vec],
  lineTo => [z1: Vector.Vec],
  curveTo => [b1, b2, b3: Vector.Vec],
  ENDCASE];
curPath, lastPathNode: REF PathNode ← NIL;

pi: REAL = 3.14159625;
RoundPenCacheRef: TYPE = REF RoundPenCacheRec;
RoundPenCacheRec: TYPE = RECORD [
 next: RoundPenCacheRef,
 numCorners: CARDINAL,
 multiplier: Vec,
 record: PolygonPen.Pen];
pens: RoundPenCacheRef ← NIL;

curLoc: Vec;

p: SirPress.PressHandle ← NIL;
press: BOOLEAN ← FALSE;

pen: PolygonPen.Pen;

context: Graphics.Context;

Mica: PROCEDURE[pt:REAL] RETURNS [LONG INTEGER] = INLINE {
 RETURN[Real.RoundLI[pt*2540.0/72.0]]};

JBeginObject: PROC = {
 IF press THEN SirPress.StartOutline[p]
 ELSE context ← JaMGraphics.GetDC[]};

JEndObject: PROC = {
 IF press THEN SirPress.EndOutline[p]
 ELSE {context ← JaMGraphics.GetDC[]; Graphics.DrawArea[context]; JaMGraphics.Update[]};
 };

JMoveTo: PROC = {
 t: Vec ← PopVec[];
 PathMoveTo[t];
 MoveTo[t]};

PathMoveTo: PolygonPen.MoveToProc = {
 -- begin a new path. throw away any path defined previously
 IF curPath#NIL THEN curPath ← lastPathNode ← NIL;
 curPath ← lastPathNode ← NEW[moveTo PathNode];
 lastPathNode^ ← [next: NIL, pathInfo: moveTo[z0]];
 };

MoveTo: PolygonPen.MoveToProc = {
 IF press THEN {
  SirPress.EndOutline[p];
  SirPress.StartOutline[p];
  SirPress.PutMoveTo[p,Mica[z0.x],Mica[z0.y]]}
 ELSE {
  context ← JaMGraphics.GetDC[];
  Graphics.MoveTo[context,0,0];
  Graphics.DrawArea[context]; JaMGraphics.Update[];
  Graphics.MoveTo[context,z0.x,z0.y]};
 curLoc ← z0;
 };

JLineTo: PROC = {
 PathLineTo[PopVec[]]};

PathLineTo: PolygonPen.LineToProc = {
 -- add point z1 to the existing path, if one has been started
 IF curPath#NIL THEN {
  lastPathNode^.next ← NEW[lineTo PathNode];
  lastPathNode^.next^ ← [next: NIL, pathInfo: lineTo[z1]];
  lastPathNode ← lastPathNode^.next};
 };

LineTo: PolygonPen.LineToProc = {
 IF press THEN SirPress.PutDrawTo[p,Mica[z1.x],Mica[z1.y]]
 ELSE Graphics.LineTo[JaMGraphics.GetDC[],z1.x,z1.y];
 curLoc ← z1;
 };

JCurveTo: PROC = {
 b3:Vec ← PopVec[];
 b2:Vec ← PopVec[];
 PathCurveTo[PopVec[],b3,b3]};

PathCurveTo: PolygonPen.CurveToProc = {
 -- add curve definition to an existing path
 IF curPath#NIL THEN {
  lastPathNode^.next ← NEW[curveTo PathNode];
  lastPathNode^.next^ ← [next: NIL, pathInfo: curveTo[z1, z2, z3]];
  lastPathNode ← lastPathNode^.next};
 };

CurveTo: PolygonPen.CurveToProc = {
 IF press THEN {
  c: Cubic.Coeffs ← Cubic.BezierToCoeffs[[curLoc,z1,z2,z3]];
  SirPress.PutCubic[p,
   Mica[c.c1.x], Mica[c.c1.y],
   Mica[c.c2.x], Mica[c.c2.y],
   Mica[c.c3.x],Mica[c.c3.y]]}
 ELSE Graphics.CurveTo[JaMGraphics.GetDC[],z1.x,z1.y,z2.x,z2.y,z3.x,z3.y];
 curLoc ← z3;
 };

JDrawPath: PROC = {
 n: REF PathNode ← curPath;
 pen ← RoundPen[JaMFnsDefs.PopReal[]];
 IF press THEN SirPress.StartOutline[p];
 WHILE n#NIL DO
  WITH n SELECT FROM
   moveTo => curLoc ← z0;
   lineTo => {
    PolygonPen.Line[pen, curLoc, z1, MoveTo, LineTo, CurveTo];
    curLoc ← z1};
   curveTo => {
    PolygonPen.Stroke[pen, [curLoc, b1, b2, b3], MoveTo, LineTo, CurveTo];
    curLoc ← b3};
   ENDCASE;
  n ← n.next;
  ENDLOOP;
 IF press THEN SirPress.EndOutline[p];
 };

JDrawArea: PROC = {
 n: REF PathNode ← curPath;
 IF press THEN SirPress.StartOutline[p]
 ELSE context ← JaMGraphics.GetDC[];
 WHILE n#NIL DO
  WITH n SELECT FROM
   moveTo => {
    IF press THEN SirPress.PutMoveTo[p, Mica[z0.x], Mica[z0.y]]
    ELSE Graphics.MoveTo[context,z0.x,z0.y];
    curLoc ← z0};
   lineTo => {
    IF press THEN SirPress.PutDrawTo[p, Mica[z1.x], Mica[z1.y]]
    ELSE Graphics.LineTo[context,z1.x,z1.y];
    curLoc ← z1};
   curveTo => {
    IF press THEN {
     c: Cubic.Coeffs ← Cubic.BezierToCoeffs[[curLoc, b1, b2, b3]];
     SirPress.PutCubic [p,
      Mica[c.c1.x], Mica[c.c1.y],
      Mica[c.c2.x], Mica[c.c2.y],
      Mica[c.c3.x], Mica[c.c3.y]]}
    ELSE Graphics.CurveTo[context,b1.x,b1.y,b2.x,b2.y,b3.x,b3.y];
    curLoc ← b3;
    };
   ENDCASE;
  n ← n.next;
  ENDLOOP;
 IF press THEN SirPress.EndOutline[p];
 };


PopVec: PROCEDURE RETURNS [v:Vec] = {
 v.y ← JaMFnsDefs.GetReal[];
 v.x ← JaMFnsDefs.GetReal[]};

PopText: PROCEDURE RETURNS [t: REF TEXT] = {
 t←NEW[TEXT[128]];
 JaMFnsDefs.PopString[LOOPHOLE[t]];
 };

JOpenPress: PROC = {
 name: Rope.ROPE ← Rope.FromRefText[PopText[]];
 p ← SirPress.NewPressHandle[FixFileName[name, ".Press"]];
 press ← p#NIL};

FixFileName: PROC [oldname, extension: Rope.ROPE] RETURNS [newname:Rope.ROPE] = {
 dotPosition: INTEGER ← Rope.Find[oldname, "."];
 IF dotPosition < 0 THEN
  newname ← Rope.Cat[oldname, extension]
 ELSE
  newname ← Rope.Cat[Rope.Substr[oldname, 0, dotPosition], extension];
 };

JClosePress: PROC = {
 IF press THEN SirPress.ClosePress[p];
 p ← NIL;
 press ← FALSE};

JNewPage: PROC = {
 IF press THEN SirPress.WritePage[p]};

JPen: PROC = {
 n: NAT ← JaMFnsDefs.PopInteger[];
 pen ← NEW[PolygonPen.PenRec[n]];
 FOR i:NAT DECREASING IN [0..n) DO pen[i] ← PopVec[]; ENDLOOP;
 };

JRoundPen: PROC = {
 pen ← RoundPen[JaMFnsDefs.PopReal[]]};

-- create a regular polygon of n sides, with n > pi*sqrt(w/2)
RoundPen: PROC[width: REAL] RETURNS[PolygonPen.Pen] = {
 nCorners: CARDINAL ← Real.RoundC[0.5+(pi*RealFns.SqRt[width/2])];
 r: RoundPenCacheRef ← pens;
 i: CARDINAL;
 WHILE r#NIL DO
  IF r.numCorners=nCorners THEN RETURN[r.record];
  r ← r.next;
  ENDLOOP;
 pens ← NEW[RoundPenCacheRec ← [pens, nCorners, Complex.Exp[[0,2*pi/nCorners]], NEW[PolygonPen.PenRec[nCorners]]] ];
 pens.record[0] ← [width/2, 0];
 FOR i IN [1 .. nCorners) DO
  pens.record[i] ← Complex.Mul[pens.record[i-1], pens.multiplier];
  ENDLOOP;
 RETURN[pens.record];
 };

JDrawDot: PROC = {
 point: Vec ← PopVec[];
 IF press THEN SirPress.StartOutline[p];
 PolygonPen.Dot[pen,point,MoveTo,LineTo,CurveTo];
 IF press THEN SirPress.EndOutline[p]
 ELSE {Graphics.DrawArea[JaMGraphics.GetDC[]]; JaMGraphics.Update[]};
 };

JDrawLine: PROC = {
 t: Vec ← PopVec[];
 s: Vec ← PopVec[];
 IF press THEN SirPress.StartOutline[p];
 PolygonPen.Line[pen,s,t,MoveTo,LineTo,CurveTo];
 IF press THEN SirPress.EndOutline[p]
 ELSE {Graphics.DrawArea[JaMGraphics.GetDC[]]; JaMGraphics.Update[]};
 };

JDrawBezier: PROC = {
 b3: Vec ← PopVec[];
 b2: Vec ← PopVec[];
 b1: Vec ← PopVec[];
 b0: Vec ← PopVec[];
 IF press THEN SirPress.StartOutline[p];
 PolygonPen.Stroke[pen,[b0,b1,b2,b3],MoveTo,LineTo,CurveTo];
 IF press THEN SirPress.EndOutline[p]
 ELSE {Graphics.DrawArea[JaMGraphics.GetDC[]]; JaMGraphics.Update[]};
 };

JDrawText: PROC = {
 s: REF TEXT ← PopText[];
 loc: Vec ← PopVec[];
 IF press THEN SirPress.PutText[p,LOOPHOLE[s],Mica[loc.x],Mica[loc.y]]
 ELSE {
  context ← JaMGraphics.GetDC[];
  Graphics.MoveTo[context,loc.x,loc.y];
  Graphics.DrawText[context,LOOPHOLE[s]];
  JaMGraphics.Update[]};
 };

JSetFont: PROC = {
 fontName: Rope.ROPE ← Rope.FromRefText[PopText[]];
 IF press THEN {
  len: INTEGER ← Rope.Length[fontName]-1;
  family: Rope.ROPE;
  face, size: INTEGER ← 0;
  units: INTEGER ← 1;
  DO SELECT Rope.Fetch[fontName, len] FROM
   'B => {face ← face + 1; len ← len - 1};
   'I => {face ← face + 2; len ← len - 1};
   ENDCASE => EXIT;
   ENDLOOP;
  WHILE Rope.Digit[Rope.Fetch[fontName, len]] DO
   size ← size+(Rope.Fetch[fontName, len]-'0)*units;
   len ← len-1;
   units ← units*10;
   ENDLOOP;
  family ← Rope.Substr[fontName, 0, len+1];
  SirPress.SetFont[p,LOOPHOLE[family],size,face]}
 ELSE {
  Graphics.SetDefaultFont[JaMGraphics.GetDC[],Graphics.MakeFont[fontName]]};
 };

JPressHue: PROC = {
 IF press THEN SirPress.SetHue[p,JaMFnsDefs.PopInteger[]]};

JPressSaturation: PROC = {
 IF press THEN SirPress.SetSaturation[p,JaMFnsDefs.PopInteger[]]};

JPressBrightness: PROC = {
 IF press THEN SirPress.SetBrightness[p,JaMFnsDefs.PopInteger[]]};

JaMFnsDefs.Register[".openpress"L,JOpenPress]; -- .openpress
JaMFnsDefs.Register[".closepress"L,JClosePress]; -- .closepress
JaMFnsDefs.Register[".pressnewpage"L,JNewPage]; -- .pressnewpage
JaMFnsDefs.Register[".beginpressobject"L,JBeginObject]; -- .beginpressobject
JaMFnsDefs.Register[".endpressobject"L,JEndObject]; -- .endpressobject
JaMFnsDefs.Register[".calligmoveto"L,JMoveTo]; -- x y .calligmoveto
JaMFnsDefs.Register[".calliglineto"L,JLineTo]; -- x y .calliglineto
JaMFnsDefs.Register[".calligcurveto"L,JCurveTo]; -- b1.x b1.y b2.x b2.y b3.x b3.y .calligcurveto
JaMFnsDefs.Register[".calligdrawdot"L,JDrawDot]; -- x y .calligdrawdot
JaMFnsDefs.Register[".calligdrawline"L,JDrawLine]; -- sx sy tx ty .calligdrawline
JaMFnsDefs.Register[".calligdrawbezier"L,JDrawBezier]; -- b0.x b0.y b1.x b1.y b2.x b2.y b3.x b3.y .calligdrawbezier
JaMFnsDefs.Register[".calligdrawpath"L,JDrawPath]; -- width .calligdrawpath
JaMFnsDefs.Register[".calligdrawarea"L,JDrawArea]; -- .calligdrawarea
JaMFnsDefs.Register[".calligpen"L,JPen]; -- x1 y1 . . . xn yn n .calligpen
JaMFnsDefs.Register[".calligroundpen"L,JRoundPen]; -- width .calligroundpen
JaMFnsDefs.Register[".presshue"L,JPressHue]; -- hue[0-255] .presshue
JaMFnsDefs.Register[".presssaturation"L,JPressSaturation]; -- saturation[0-255] .presssaturation
JaMFnsDefs.Register[".pressbrightness"L,JPressBrightness]; -- brightness[0-255] .pressbrightness 
JaMFnsDefs.Register[".calligtext"L,JDrawText]; -- string .calligtext
JaMFnsDefs.Register[".presssetfont"L,JSetFont]; -- fontname .presssetfont

pen ← NEW[PolygonPen.PenRec[2]];
pen[0] ← [-2,-2];
pen[1] ← [2,2];

}.