--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: 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_Add[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_ABS[d.x]; dy_ABS[d.y]; IF dx+dy < 1 THEN RETURN[TRUE]; IF dy < dx THEN RETURN[ ABS[d2.y-((d.y*d2.x)/d.x)]