--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];
 dx�S[d.x]; dy�S[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.