TACalligImpl.mesa, for drawing thick lines for TiogaArtwork.
Copyright © 1985 by Xerox Corporation. All rights reserved.
liberally borrowed from Callig.mesa
Rick Beach, February 11, 1985 12:21:41 pm PST
Maureen Stone April 24, 1983 5:37 pm
DIRECTORY
Ascii USING [Digit],
Complex, Cubic, Graphics, GraphicsBasic, GraphicsColor, JaM, TAPrivate, TJaMGraphics, PolygonPen, Real, RealFns, Rope, SirPress, Vector, CGPath, TACallig;
TACalligImpl: CEDAR PROGRAM
IMPORTS Ascii, Complex, Cubic, Graphics, GraphicsColor, JaM, TAPrivate, TJaMGraphics, PolygonPen, Real, RealFns, Rope, SirPress, CGPath, Vector
EXPORTS TACallig= {
ROPE: TYPE = Rope.ROPE;
Vec: TYPE = GraphicsBasic.Vec;
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;
pen: PolygonPen.Pen;
rectangularPen: BOOLEANFALSE;
shadowLoc: Vec;
shadowPen: PolygonPen.Pen;
p: SirPress.PressHandle ← NIL;
press: BOOLEANFALSE;
path: Graphics.Path ← Graphics.NewPath[];
penPath: Graphics.Path ← Graphics.NewPath[];
PaintPath: PROC [context: Graphics.Context] = {Graphics.DrawArea[context,path]};
PaintPenPath: PROC [context: Graphics.Context] = {Graphics.DrawArea[context,penPath]};
Mica: PROCEDURE[pt:REAL] RETURNS [LONG INTEGER] = INLINE {
RETURN[Real.RoundLI[pt*2540.0/72.0]]};
Transform: PROCEDURE[u: Vec] RETURNS[v: Vec] = {
paint: PROC[context: Graphics.Context] = {
[v.x, v.y] ← Graphics.UserToWorld[context, u.x, u.y]};
TJaMGraphics.Painter[paint, TAPrivate.state];
};
JBeginObject: PROC [state: JaM.State] = {
IF press THEN SirPress.StartOutline[p]
ELSE Graphics.FlushPath[path];
};
JEndObject: PROC [state: JaM.State] = {
IF press THEN SirPress.EndOutline[p]
ELSE {TJaMGraphics.Painter[PaintPath, state]; Graphics.FlushPath[path]};
};
JMoveTo: PROC [state: JaM.State] = {t: Vec ← PopVec[state]; Graphics.MoveTo[path, t.x, t.y]};
JLineTo: PROC [state: JaM.State] = {t: Vec ← PopVec[state]; Graphics.LineTo[path, t.x, t.y]};
JCurveTo: PROC [state: JaM.State] = {
b3:Vec ← PopVec[state];
b2:Vec ← PopVec[state];
b1: Vec ← PopVec[state];
Graphics.CurveTo[path,b1.x,b1.y, b2.x,b2.y, b3.x,b3.y]};
JDrawPath: PROC [state: JaM.State] = {
width: REAL ← JaM.PopReal[state];
IF width <= 0 AND ~press THEN
SimplePath[state]
ELSE {
pen ← RoundPen[width];
JOutlinePath[state]};
};
SimplePath: PROC [state: JaM.State] = {TJaMGraphics.Painter[PaintPath, state]};
JOutlinePath: PROC [state: JaM.State] = {
moveTo: PROC[z0: Vec] = {curLoc ← z0};
lineTo: PROC[z1: Vec] = {
IF rectangularPen THEN {OPEN Vector;
dv: Vec ← Unit[Sub[z1,curLoc]];
dh: Vec ← [-pen[0].y*dv.y,pen[0].y*dv.x]; --normal * height
dw: Vec ← [pen[0].x*dv.x,pen[0].x*dv.y]; --direction * width
v: Vec ← curLoc;
IF dv=[0,0] THEN PolygonPen.Dot[pen, z1, PenMoveTo, PenLineTo, PenCurveTo]
ELSE {
PenMoveTo[ Add[Sub[v,dw],dh] ];
PenLineTo[ Sub[Sub[v,dw],dh] ];
PenLineTo[ Sub[Add[z1,dw],dh] ];
PenLineTo[ Add[Add[z1,dw],dh] ];
PenLineTo[ Add[Sub[v,dw],dh] ];
};
}
ELSE PolygonPen.Line[pen, curLoc, z1, PenMoveTo, PenLineTo, PenCurveTo];
curLoc ← z1};
curveTo: PROC[b1,b2,b3: Vec] = {
PolygonPen.Stroke[pen, [curLoc, b1, b2, b3], PenMoveTo, PenLineTo, PenCurveTo];
curLoc ← b3};
IF press THEN SirPress.StartOutline[p];
CGPath.Generate[path,moveTo,lineTo,curveTo];
IF press THEN SirPress.EndOutline[p]
ELSE TJaMGraphics.Painter[PaintPenPath, state];
Graphics.FlushPath[path];
Graphics.FlushPath[penPath];
};
PenMoveTo: PolygonPen.MoveToProc = {
IF press THEN {
tz0: Vec = Transform[z0];
SirPress.EndOutline[p];
SirPress.StartOutline[p];
SirPress.PutMoveTo[p,Mica[tz0.x],Mica[tz0.y]]}
ELSE {
TJaMGraphics.Painter[PaintPenPath, TAPrivate.state];
Graphics.MoveTo[penPath,z0.x,z0.y]};
curLoc ← z0;
};
PenLineTo: PolygonPen.LineToProc = {
IF press THEN {tz1: Vec = Transform[z1]; SirPress.PutDrawTo[p,Mica[tz1.x],Mica[tz1.y]]}
ELSE Graphics.LineTo[penPath,z1.x,z1.y];
curLoc ← z1;
};
PenCurveTo: PolygonPen.CurveToProc = {
IF press THEN {
tcurLoc: Vec = Transform[curLoc];
tz1: Vec = Transform[z1];
tz2: Vec = Transform[z2];
tz3: Vec = Transform[z3];
c: Cubic.Coeffs ← Cubic.BezierToCoeffs[[tcurLoc,tz1,tz2,tz3]];
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[penPath,z1.x,z1.y,z2.x,z2.y,z3.x,z3.y];
curLoc ← z3;
};
JShadowPen: PROC [state: JaM.State] = {
shadowPen ← pen};
JShadowPath: PROC [state: JaM.State] = {
moveTo: PROC[z0: Vec] = {curLoc ← z0};
lineTo: PROC[z1: Vec] = {
PolygonPen.Line[pen, curLoc, z1, ShadowMoveTo, ShadowLineTo, ShadowCurveTo];
curLoc ← z1};
curveTo: PROC[b1,b2,b3: Vec] = {
PolygonPen.Stroke[pen, [curLoc, b1, b2, b3], ShadowMoveTo, ShadowLineTo, ShadowCurveTo];
curLoc ← b3};
IF press THEN SirPress.StartOutline[p];
CGPath.Generate[path,moveTo,lineTo,curveTo];
IF press THEN SirPress.EndOutline[p]
ELSE TJaMGraphics.Painter[PaintPenPath, state];
Graphics.FlushPath[path];
Graphics.FlushPath[penPath];
};
ShadowMoveTo: PolygonPen.MoveToProc = {
shadowLoc ← z0};
ShadowLineTo: PolygonPen.LineToProc = {
PolygonPen.Line[shadowPen, shadowLoc, z1, PenMoveTo, PenLineTo, PenCurveTo];
shadowLoc ← z1};
ShadowCurveTo: PolygonPen.CurveToProc = {
PolygonPen.Stroke[shadowPen, [shadowLoc, z1, z2, z3], PenMoveTo, PenLineTo, PenCurveTo];
shadowLoc ← z3};
JDrawArea: PROC [state: JaM.State] = {
IF press THEN {
moveTo: PROC[z0: Vec] = {
tz0: Vec = Transform[z0]; SirPress.PutMoveTo[p, Mica[tz0.x], Mica[tz0.y]];
curLoc ← z0};
lineTo: PROC[z1: Vec] = {
tz1: Vec = Transform[z1]; SirPress.PutDrawTo[p, Mica[tz1.x], Mica[tz1.y]];
curLoc ← z1};
curveTo: PROC[b1,b2,b3: Vec] = {
tcurLoc: Vec = Transform[curLoc];
tb1: Vec = Transform[b1];
tb2: Vec = Transform[b2];
tb3: Vec = Transform[b3];
c: Cubic.Coeffs ← Cubic.BezierToCoeffs[[tcurLoc, tb1, tb2, tb3]];
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]];
curLoc ← b3};
SirPress.StartOutline[p];
CGPath.Generate[path,moveTo,lineTo,curveTo];
SirPress.EndOutline[p];
}
ELSE {
TJaMGraphics.Painter[PaintPath, state];
Graphics.FlushPath[path];
};
};
PopVec: PROCEDURE [state: JaM.State] RETURNS [v:Vec] = {
v.y ← JaM.PopReal[state];
v.x ← JaM.PopReal[state]};
PopRope: PROCEDURE [state: JaM.State] RETURNS [t: ROPE] = {
t ← JaM.PopRope[state];
};
UseThisPressHandle: PUBLIC PROCEDURE[pressHandle: SirPress.PressHandle] = {
p ← pressHandle;
press ← p#NIL};
ForgetThePressHandle: PUBLIC PROCEDURE = {
p ← NIL;
press ← FALSE};
JOpenPress: PROC [state: JaM.State] = {
name: ROPE ← PopRope[state];
p ← SirPress.NewPressHandle[FixFileName[name, ".Press"]];
press ← p#NIL};
FixFileName: PROC [oldname, extension: ROPE] RETURNS [newname: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 [state: JaM.State] = {
IF press THEN SirPress.ClosePress[p];
p ← NIL;
press ← FALSE};
JNewPage: PROC [state: JaM.State] = {
IF press THEN SirPress.WritePage[p]};
JPen: PROC [state: JaM.State] = {
n: NAT ← JaM.PopInt[state];
pen ← NEW[PolygonPen.PenRec[n]];
FOR i:NAT DECREASING IN [0..n) DO pen[i] ← PopVec[state]; ENDLOOP;
rectangularPen ← FALSE;
};
JRoundPen: PROC [state: JaM.State] = {
pen ← RoundPen[JaM.PopReal[state]]};
create a regular polygon of n sides, with n > pi*sqrt(w/2)
RoundPen: PROC[width: REAL] RETURNS[PolygonPen.Pen] = {
halfWidth: REAL = MAX[.5, width/2];
nCorners: CARDINAL ← Real.RoundC[0.5+(pi*RealFns.SqRt[halfWidth])];
r: RoundPenCacheRef ← pens;
i: CARDINAL;
rectangularPen ← FALSE;
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] ← [halfWidth, 0];
FOR i IN [1 .. nCorners) DO
pens.record[i] ← Complex.Mul[pens.record[i-1], pens.multiplier];
ENDLOOP;
RETURN[pens.record];
};
JRectangularPen: PROC [state: JaM.State] = {
slant: REAL ← JaM.PopReal[state];
height: REAL ← JaM.PopReal[state];
width: REAL ← JaM.PopReal[state];
pen ← RectangularPen[width, height, slant]};
RectangularPen: PROC[width, height, slant: REAL] RETURNS[pen: PolygonPen.Pen] = {
halfWidth: REALMAX[0.5, width/2];
halfHeight: REALMAX[0.5, height/2];
rotate: Vec = Complex.Exp[[0,-(slant*pi/180)]];
pen ← NEW[PolygonPen.PenRec[4]];
pen[0] ← Complex.Mul[[halfWidth, halfHeight], rotate];
pen[1] ← Complex.Mul[[halfWidth, -halfHeight], rotate];
pen[2] ← Complex.Mul[[-halfWidth, -halfHeight], rotate];
pen[3] ← Complex.Mul[[-halfWidth, halfHeight], rotate];
rectangularPen ← slant=0;
RETURN[pen];
};
JItalicPen: PROC [state: JaM.State] = {
slant: REAL ← JaM.PopReal[state];
length: REAL ← JaM.PopReal[state];
pen ← ItalicPen[length, slant];
};
ItalicPen: PROC[length, slant: REAL] RETURNS[pen: PolygonPen.Pen] = {
pen ← NEW[PolygonPen.PenRec[2]];
pen[0] ← [0,0];
pen[1] ← Complex.Mul[[0, -MAX[1.0,length]], Complex.Exp[[0,-(slant*pi/180)]]];
rectangularPen ← FALSE;
RETURN[pen];
};
JEllipticalPen: PROC [state: JaM.State] = {
slant: REAL ← JaM.PopReal[state];
height: REAL ← JaM.PopReal[state];
width: REAL ← JaM.PopReal[state];
pen ← EllipticalPen[width, height, slant]};
EllipticalPen: PROC[width, height, slant: REAL] RETURNS[pen: PolygonPen.Pen] = {
a: REAL = MAX[0.5, width/2];
b: REAL = MAX[0.5, height/2];
nCorners: CARDINAL = Real.RoundC[0.5+(pi*RealFns.SqRt[2*MIN[a,b]])];
theta: REAL = 2*pi/nCorners;
cosTheta: REAL = RealFns.Cos[theta];
sinTheta: REAL = RealFns.Sin[theta];
cosSlant: REAL = RealFns.CosDeg[slant];
sinSlant: REAL = RealFns.SinDeg[slant];
cosAccum: REAL ← 1;
sinAccum: REAL ← 0;
x,y,t: REAL;
pen ← NEW[PolygonPen.PenRec[nCorners]];
FOR i: CARDINAL IN [0 .. nCorners) DO
x ← a*cosAccum;
y ← b*sinAccum;
pen[i] ← [x*cosSlant-y*sinSlant, x*sinSlant+y*cosSlant];
t ← cosAccum*cosTheta-sinAccum*sinTheta;
sinAccum ← sinAccum*cosTheta+cosAccum*sinTheta;
cosAccum ← t;
ENDLOOP;
rectangularPen ← FALSE;
RETURN[pen];
};
JDrawDot: PROC [state: JaM.State] = {
point: Vec ← PopVec[state];
IF press THEN SirPress.StartOutline[p];
PolygonPen.Dot[pen,point,PenMoveTo,PenLineTo,PenCurveTo];
IF press THEN SirPress.EndOutline[p]
ELSE {TJaMGraphics.Painter[PaintPath, state]; Graphics.FlushPath[path]};
};
JDrawLine: PROC [state: JaM.State] = {
point: Vec ← PopVec[state];
t: Vec ← PopVec[state];
s: Vec ← PopVec[state];
IF press THEN SirPress.StartOutline[p];
PolygonPen.Line[pen,s,t,PenMoveTo,PenLineTo,PenCurveTo];
IF press THEN SirPress.EndOutline[p]
ELSE {TJaMGraphics.Painter[PaintPath, state]; Graphics.FlushPath[path]};
};
JDrawBezier: PROC [state: JaM.State] = {
b3: Vec ← PopVec[state];
b2: Vec ← PopVec[state];
b1: Vec ← PopVec[state];
b0: Vec ← PopVec[state];
IF press THEN SirPress.StartOutline[p];
PolygonPen.Stroke[pen,[b0,b1,b2,b3],PenMoveTo,PenLineTo,PenCurveTo];
IF press THEN SirPress.EndOutline[p]
ELSE {TJaMGraphics.Painter[PaintPath, state]; Graphics.FlushPath[path]};
};
JDrawText: PROC [state: JaM.State] = {
paint: PROC [context: Graphics.Context] = {
Graphics.SetCP[context,loc.x,loc.y];
Graphics.DrawRope[self: context, rope: string, font: font];
};
string: ROPE ← PopRope[state];
loc: Vec ← PopVec[state];
IF press THEN {
tloc: Vec = Transform[loc]; SirPress.PutText[p,string,Mica[tloc.x],Mica[tloc.y]]}
ELSE TJaMGraphics.Painter[paint, state];
};
font: Graphics.FontRef ← NIL;
JSetFont: PROC [state: JaM.State] = {
fontName: ROPE ← PopRope[state];
IF press THEN {
len: INTEGER ← Rope.Length[fontName]-1;
family: 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 Ascii.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,family,size,face]}
ELSE font ← Graphics.MakeFont[fontName];
};
JHSVColor: PROC [state: JaM.State] = {
v: REAL ← JaM.PopReal[state];
s: REAL ← JaM.PopReal[state];
h: REAL ← JaM.PopReal[state];
paint: PROC [context: Graphics.Context] = {
Graphics.SetColor[context, GraphicsColor.HSVToColor[h, s, v]];
};
IF press
THEN SirPress.SetColor[p,Real.RoundI[h*240],Real.RoundI[s*255],Real.RoundI[v*255]]
ELSE TJaMGraphics.Painter[paint, state];
};
JPressHue: PROC [state: JaM.State] = {
IF press THEN SirPress.SetHue[p,JaM.PopInt[state]]};
JPressSaturation: PROC [state: JaM.State] = {
IF press THEN SirPress.SetSaturation[p,JaM.PopInt[state]]};
JPressBrightness: PROC [state: JaM.State] = {
IF press THEN SirPress.SetBrightness[p,JaM.PopInt[state]]};
JPressHSB: PROC [state: JaM.State] = {
IF press THEN {
v: REAL ← JaM.PopReal[state];
s: REAL ← JaM.PopReal[state];
h: REAL ← JaM.PopReal[state];
SirPress.SetColor[p,Real.RoundI[h*240],Real.RoundI[s*255],Real.RoundI[v*255]];
};
};
RegisterCallig: PUBLIC PROC [initState: JaM.State] ~ {
JaM.Register[initState, ".openpress",JOpenPress]; -- filename .openpress
JaM.Register[initState, ".closepress",JClosePress]; -- .closepress
JaM.Register[initState, ".pressnewpage",JNewPage]; -- .pressnewpage
JaM.Register[initState, ".beginpressobject",JBeginObject]; -- .beginpressobject
JaM.Register[initState, ".endpressobject",JEndObject]; -- .endpressobject
JaM.Register[initState, ".calligmoveto",JMoveTo]; -- x y .calligmoveto
JaM.Register[initState, ".calliglineto",JLineTo]; -- x y .calliglineto
JaM.Register[initState, ".calligcurveto",JCurveTo]; -- b1.x b1.y b2.x b2.y b3.x b3.y .calligcurveto
JaM.Register[initState, ".calligdrawdot",JDrawDot]; -- x y .calligdrawdot
JaM.Register[initState, ".calligdrawline",JDrawLine]; -- sx sy tx ty .calligdrawline
JaM.Register[initState, ".calligdrawbezier",JDrawBezier]; -- b0.x b0.y b1.x b1.y b2.x b2.y b3.x b3.y .calligdrawbezier
JaM.Register[initState, ".calligdrawpath",JDrawPath]; -- width .calligdrawpath
JaM.Register[initState, ".calligoutlinepath",JOutlinePath]; -- .calligdrawpath, assumes pen
JaM.Register[initState, ".calligdrawarea",JDrawArea]; -- .calligdrawarea
JaM.Register[initState, ".callighsvcolor",JHSVColor]; -- hue[0-1) saturation[0-1) brightness[0-1) .callighsvcolor
JaM.Register[initState, ".calligpen",JPen]; -- x1 y1 . . . xn yn n .calligpen
JaM.Register[initState, ".calligroundpen",JRoundPen]; -- diameter .calligroundpen
JaM.Register[initState, ".calligrectangularpen",JRectangularPen]; -- width height slant .calligrectangularpen
JaM.Register[initState, ".calligitalicpen",JItalicPen]; -- length slant .calligitalicpen
JaM.Register[initState, ".calligellipticalpen",JEllipticalPen]; -- width height slant .calligellipticalpen
JaM.Register[initState, ".presshue",JPressHue]; -- hue[0-255] .presshue
JaM.Register[initState, ".presssaturation",JPressSaturation]; -- saturation[0-255] .presssaturation
JaM.Register[initState, ".pressbrightness",JPressBrightness]; -- brightness[0-255] .pressbrightness 
JaM.Register[initState, ".presshsb",JPressHSB]; -- hue[0-1) saturation[0-1) brightness[0-1) .presshsb 
JaM.Register[initState, ".calligtext",JDrawText]; -- string .calligtext
JaM.Register[initState, ".presssetfont",JSetFont]; -- fontname .presssetfont
JaM.Register[initState, ".calligshadowpen",JShadowPen]; -- some-pen-definition .calligshadowpen
JaM.Register[initState, ".calligshadowpath",JShadowPath]; -- .calligshadowpath, assumes shadow pen
pen ← NEW[PolygonPen.PenRec[2]];
pen[0] ← [-2,-2];
pen[1] ← [2,2];
};
}.