--SDContour.mesa
--sd path module
--Written by M.Stone
--Last changed by Michael Plass August 24, 1982 9:32 am
DIRECTORY
Cubic USING[Bezier],
Rope USING[ROPE],
ConvertUnsafe USING [ToRope],
Vector USING[Vec,Sub,Div,Mag,Mul,Max,Min,In,Add],
PressFontReader,
CurveDefs,
Curve,
JaMFnsDefs;
SDContour: CEDAR PROGRAM IMPORTS JaMFnsDefs, PressFontReader, Vector, ConvertUnsafe, CurveDefs, Curve =
BEGIN OPEN Vector, CurveDefs, Cubic;
sdContour: ContourHandle ← NEW[Contour];
currentPt: Vec ← [0,0];
SetEps: PROC = {sdEps ← JaMFnsDefs.GetReal[]};
SetMaxL: PROC = {sdMaxL ← JaMFnsDefs.GetReal[]};
SetSDScale: PROC = {sdScale ← JaMFnsDefs.GetReal[]};
GetNPieces: PROC = {JaMFnsDefs.PushInteger[nPieces]};
sdname: Rope.ROPE ← NIL;
sdFile: PressFontReader.Handle;
font: PressFontReader.Font;
OpenSD: PROC = {
ls: LONG STRING ← [80];
IF sdname#NIL THEN CloseSD[];
JaMFnsDefs.PopString[ls];
sdname ← ConvertUnsafe.ToRope[ls];
sdFile ← PressFontReader.FromFile[sdname];
font ← sdFile.FirstFont[];
};
CloseSD: PROC = {PressFontReader.Close[sdFile]};
SetUpSDContour: PROC = {
ls: LONG STRING ← [2];
JaMFnsDefs.PopString[ls];
[] ← GetSDContours[ls[0]];
};
AnalyzeSDContour: PROCEDURE = {
count: INTEGER ← 0;
slist: SListHandle;
cn: INTEGER ← JaMFnsDefs.PopInteger[];
FOR slist ← sdContour.sLists, slist.next UNTIL slist = NIL DO
IF cn=count THEN EXIT;
count ← count+1;
ENDLOOP;
IF slist#NIL THEN {
contour: ContourHandle ← NEW[Contour];
contour.sLists ← contour.lastSList ← slist;
CurveDefs.AnalyzeContour[contour];
};
};
SDSetContours: PROCEDURE = {
Curve.ResetContours[Curve.defaultHandle];
FOR slist: SListHandle ← sdContour.sLists, slist.next UNTIL slist = NIL DO
FOR sa: SampleHandle ← slist.samples, sa.next UNTIL sa=NIL DO
Curve.AddSample[Curve.defaultHandle, sa.xy.x,sa.xy.y];
ENDLOOP;
IF slist.next#NIL THEN Curve.AddContour[Curve.defaultHandle];
ENDLOOP;
};
sdEps: REAL ← .1;
sdScale: REAL ← 72;
sdMaxL: REAL ← 1000.0;
nPieces: INTEGER ← 0;
s,maxY,minY,minMag,maxMag: REAL ← 0;
GetSDContours: PUBLIC PROC[char: CHARACTER] RETURNS[ContourHandle] = {
Move: PROCEDURE[x, y: REAL] = {NewSList[Mul[[x,y],sdScale]]; cp ← [x,y]};
Draw: PROCEDURE[x, y: REAL] = {
b: Cubic.Bezier;
cp: Vec ← Div[currentPt,sdScale];
dir: Vec ← Sub[[x,y],cp];
m: REAL ← Mag[dir];
b.b0 ← cp;
b.b1 ← Add[cp,Mul[dir,m/3]];
b.b2 ← Sub [[x,y],Mul[dir,m/3]];
b.b3 ← [x,y];
doCubic[b];
};
addCubic: PROC[x1, y1, x2, y2, x3, y3: REAL] = {
doCubic[[cp, [x1, y1], [x2, y2], [x3, y3]]];
};
doCubic: PROC[b: Cubic.Bezier] = {
cp ← b.b3;
b.b0 ← Vector.Mul[b.b0,sdScale];
b.b1 ← Vector.Mul[b.b1,sdScale];
b.b2 ← Vector.Mul[b.b2,sdScale];
b.b3 ← Vector.Mul[b.b3,sdScale];
BezierPolygon[b,sdEps, AddSample,sdMaxL];
nPieces ← nPieces+1;
};
Area: PROC = {};
cp: Vec ← [0,0];
sdContour ← NEW[Contour];
nPieces ← 0;
maxY ← 0;
minY ← 1000;
minMag ← 1000;
maxMag ← 0;
currentPt ← [0,0];
font.GetCharOutline[char,Move,Draw,addCubic,Area];
--make the samples follow the conventions in Analyze
FOR sl: SListHandle ← sdContour.sLists, sl.next UNTIL sl=NIL DO
sl.closed ← TRUE;
sl.lastSample ← sl.lastSample.prev;
sl.lastSample.next ← NIL;
ENDLOOP;
RETURN[sdContour];
};
NewSList: PROC[vec: Vec] = {
slist: SListHandle ← NEW[SList];
slist.samples ← slist.lastSample ← NEW[Sample ← [NIL,NIL,vec,0,0]];
currentPt ← vec;
IF sdContour.sLists=NIL THEN sdContour.sLists ← sdContour.lastSList ← slist
ELSE {
sdContour.lastSList.next ← slist;
slist.prev ← sdContour.lastSList;
sdContour.lastSList ← sdContour.lastSList.next;
};
};
AddSample: PROC[vec: Vec] = {
sa: SampleHandle ← NIL;
m: REAL;
m ← Mag[Sub[vec,currentPt]];
IF m=0 THEN RETURN;
IF sdContour.lastSList=NIL THEN RETURN;
IF m > sdMaxL THEN {SplitSample[vec]; RETURN};
sa ← NEW[Sample ← [NIL,NIL,vec,m+sdContour.lastSList.lastSample.s,0]];
sdContour.lastSList.lastSample.next ← sa;
sa.prev ← sdContour.lastSList.lastSample;
sdContour.lastSList.lastSample ← sdContour.lastSList.lastSample.next;
sdContour.lastSList.nsamples ← sdContour.lastSList.nsamples+1;
sdContour.lastSList.arcLen ← sdContour.lastSList.arcLen+m;
currentPt ← vec;
--keep some statistics
IF m < minMag THEN minMag ← m;
IF m > maxMag THEN maxMag ← m;
IF ABS[vec.y] > maxY THEN maxY ← ABS[vec.y];
IF ABS[vec.y] < minY THEN minY ← ABS[vec.y];
};
SplitSample: PROC[vec: Vec] = {
s0: Vec ← Sub[vec,currentPt];
AddSample[Sub[vec,Div[s0,2.0]]];
AddSample[vec];
};
BezierPolygon: PROCEDURE[b: Bezier, epsilon: REAL,Proc: PROCEDURE[Vector.Vec], maxlen: REAL] = {
lvlmax: CARDINAL=10;
SubDivide: PROCEDURE[b: Bezier, lvl: CARDINAL] = {
IF lvl>=lvlmax OR IsFlat[b,epsilon] AND Mag[Sub[b.b3,b.b0]] <= maxlen THEN Proc[b.b3]
ELSE {
OPEN Vector;
q1,q2,q3,qp1,qp2,q: Vec;
bb: Bezier;
q1←Mul[Add[b.b0,b.b1],0.5];
q2←Mul[Add[b.b1,b.b2],0.5];
q3←Mul[Add[b.b2,b.b3],0.5];
qp1←Mul[Add[q1,q2],0.5];
qp2←Mul[Add[q2,q3],0.5];
q←Mul[Add[qp1,qp2],0.5];
bb←[b0: b.b0, b1: q1, b2: qp1, b3: q];
SubDivide[bb,lvl+1];
bb←[b0: q, b1: qp2, b2: q3, b3: b.b3];
SubDivide[bb,lvl+1];
};
};
SubDivide[b,1];
};
IsFlat: PROCEDURE[b: Bezier, eps: REAL]
RETURNS[BOOLEAN] = INLINE {
OPEN Vector;
dx,dy: REAL;
d1,d2,d,bl,bh: Vec;
oh: Vec=[0.5,0.5];
bh[Max[b.b0,b.b3],oh];
bl←Sub[Min[b.b0,b.b3],oh];
IF ~In[b.b1,bl,bh] OR ~In[b.b2,bl,bh] THEN RETURN[FALSE];
d1←Sub[b.b1,b.b0];
d2←Sub[b.b2,b.b0];
d←Sub[b.b3,b.b0];
dxS[d.x]; dyS[d.y];
IF dx+dy < 1 THEN RETURN[TRUE];
IF dy < dx THEN RETURN[
ABS[d2.y-((d.y*d2.x)/d.x)]<eps AND ABS[d1.y-((d.y*d1.x)/d.x)]<eps]
ELSE RETURN[
ABS[d2.x-((d.x*d2.y)/d.y)]<eps AND ABS[d1.x-((d.x*d1.y)/d.y)]<eps];
};
JaMFnsDefs.Register[".opensd"L,OpenSD];
JaMFnsDefs.Register[".closesd"L,CloseSD];
JaMFnsDefs.Register[".setupsdcontour"L,SetUpSDContour];
JaMFnsDefs.Register[".analyzesdcontour"L,AnalyzeSDContour];
JaMFnsDefs.Register[".setmaxl"L,SetMaxL];
JaMFnsDefs.Register[".setsdeps"L,SetEps];
JaMFnsDefs.Register[".sdsetcontours"L,SDSetContours];
JaMFnsDefs.Register[".getsdnpieces"L,GetNPieces];
END.
Michael Plass August 24, 1982 9:32 am: Converted to use PressFontReader.