-- CurveImpl.mesa -- Last Edited by July 17, 1982 5:05 pm -- Last Edited by Michael Plass December 7, 1982 10:25 am DIRECTORY Cubic, Curve, Complex, ConvertUnsafe, IO, FileIO, TJaMGraphics USING [Painter], JaMFnsDefs, JaMBasic, JaMOps, Graphics, Real, Rope, Seq, Vector; CurveImpl: PROGRAM IMPORTS Complex, ConvertUnsafe, TJaMGraphics, JaMFnsDefs, JaMOps, IO, FileIO, Graphics, Real, Rope, Vector EXPORTS Curve = BEGIN OPEN JaMFnsDefs, Curve; SampleHandle: TYPE = REF Sample; SListHandle: TYPE = REF SList; LinkHandle: TYPE = REF Link; TrajHandle: TYPE = REF Traj; Sample: TYPE = RECORD [next: SampleHandle ← NIL, prev: SampleHandle ← NIL, xy: Complex.Vec ← [0,0], isNode: BOOLEAN ← TRUE, isCusp: BOOLEAN ← FALSE, tangent: Complex.Vec ← [0,0], tanOut: Complex.Vec ← [0,0] ]; SList: TYPE = RECORD [header: SampleHandle, selectedSample: SampleHandle, first: SampleHandle ← NIL, last: SampleHandle ← NIL ]; Link: TYPE = RECORD [next: LinkHandle ← NIL, prev: LinkHandle ← NIL, cubic: Cubic.Bezier ]; Traj: TYPE = RECORD [next: TrajHandle ← NIL, prev: TrajHandle ← NIL, links: LinkHandle ← NIL, lastLink: LinkHandle ← NIL ]; stream: IO.Handle ← FileIO.Open["Curve.log", append]; OpenLogFile: PROC = { ls: LONG STRING ← [80]; rope: Rope.ROPE; PopString[ls]; rope ← ConvertUnsafe.ToRope[ls]; IF stream # NIL THEN stream.Close[]; stream ← NIL; IF rope.Length[] > 0 THEN stream ← FileIO.Open[rope, append]; }; CloseLogFile: PROC = { IF stream # NIL THEN stream.Close[]; stream ← NIL; }; Handle: PUBLIC TYPE = REF Rec; Rec: PUBLIC TYPE = RECORD [ traj: TrajHandle, slist: SListHandle, visible: BOOLEAN, otherContours: LIST OF TrajAndSlist ]; TrajAndSlist: TYPE = RECORD [ traj: TrajHandle, slist: SListHandle ]; Create: PUBLIC PROC RETURNS [handle: Handle] = { handle ← NEW[Rec]; handle.traj ← NEW[Traj]; handle.slist ← NewSList[]; handle.visible ← FALSE; handle.otherContours ← NIL; }; Visible: PUBLIC PROC [handle: Handle, visible: BOOLEAN ← TRUE] RETURNS [was: BOOLEAN] = { was ← handle.visible; handle.visible ← visible; }; TTY: PUBLIC PROC RETURNS[IO.Handle] = {RETURN[stream]}; ResetSa: PROC = {ResetSamples[defaultHandle]}; ResetSamples: PUBLIC PROC [handle: Handle] = {OPEN handle↑; IF slist.header#NIL THEN slist.header.next ← slist.header.prev ← NIL; slist ← NewSList[]}; NewSList: PROC RETURNS [s: SListHandle] = { s ← NEW[SList]; s.header ← NEW[Sample]; s.selectedSample ← s.header.next ← s.header.prev ← s.header; s.first ← NIL; s.last ← NIL; }; InsertBefore: PROC[a: Complex.Vec, s: SampleHandle] = { new: SampleHandle ← NEW[Sample]; new.xy ← a; new.prev ← s.prev; new.next ← s; s.prev ← new; new.prev.next ← new; }; StartSa: PROC = {y: REAL ← GetReal[]; x: REAL ← GetReal[]; StartSamples[defaultHandle, x,y]}; StartSamples: PUBLIC PROC[handle: Handle, x,y: REAL] = {ResetSamples[handle]; InsertBefore[[x,y], handle.slist.header]}; AddSa: PROC = {y: REAL ← GetReal[]; x: REAL ← GetReal[]; AddSample[defaultHandle, x,y]}; AddSample: PUBLIC PROC[handle: Handle, x,y: REAL] = {OPEN handle↑; Paint: PROC[dc: Graphics.Context] = { IF Vector.Mag[Vector.Sub[[x,y],slist.header.prev.xy]]>sLen THEN { IF slist.header.next#slist.header THEN {MoveTo[dc,slist.header.prev.xy]; DrawTo[dc,[x,y]]}; InsertBefore[[x,y],slist.header]; }; }; IF visible THEN TJaMGraphics.Painter[Paint] ELSE { IF Vector.Mag[Vector.Sub[[x,y],slist.header.prev.xy]]>sLen THEN { InsertBefore[[x,y],slist.header]; }; }; }; CountSa: PROC = {OPEN defaultHandle↑; s: SampleHandle; n: NAT ← 0; FOR s ← slist.header.next, s.next UNTIL s=slist.header DO n ← n+1; ENDLOOP; PushInteger[n]; }; ResetLinks: PUBLIC PROC[handle: Handle] = { handle.traj.links ← NIL; handle.traj.lastLink ← NIL; }; AddLink: PUBLIC PROC[handle: Handle, b: Cubic.Bezier] = {OPEN handle↑; Paint: PROC[dc: Graphics.Context] = { DrawCubic[dc,b]; }; new: LinkHandle ← NEW[Link]; new.cubic ← b; IF traj.links=NIL THEN { traj.links ← new; traj.lastLink ← new; } ELSE { traj.lastLink.next ← new; new.prev ← traj.lastLink; traj.lastLink ← new; }; IF visible THEN TJaMGraphics.Painter[Paint]; }; ForAllLinks: PROC = { body: JaMBasic.Object ← JaMOps.Pop[JaMOps.defaultFrame.opstk]; FOR l: LinkHandle ← defaultHandle.traj.links, l.next UNTIL l=NIL DO PushReal[l.cubic.b0.x]; PushReal[l.cubic.b0.y]; PushReal[l.cubic.b1.x]; PushReal[l.cubic.b1.y]; PushReal[l.cubic.b2.x]; PushReal[l.cubic.b2.y]; PushReal[l.cubic.b3.x]; PushReal[l.cubic.b3.y]; JaMOps.Execute[JaMOps.defaultFrame, body]; ENDLOOP; }; ResetCon: PUBLIC PROC[] = {ResetContours[defaultHandle]}; ResetContours: PUBLIC PROC[handle: Handle] = { FOR p: LIST OF TrajAndSlist ← handle.otherContours, p.rest UNTIL p=NIL DO IF p.first.slist.header # NIL THEN p.first.slist.header.next ← p.first.slist.header.prev ← NIL; ENDLOOP; handle.otherContours ← NIL; ResetSamples[handle] }; CountCon: PUBLIC PROC[] = {PushInteger[CountContours[defaultHandle]]}; CountContours: PUBLIC PROC[handle: Handle] RETURNS [INT] = { i: INT ← 1; FOR p: LIST OF TrajAndSlist ← handle.otherContours, p.rest UNTIL p=NIL DO i←i+1 ENDLOOP; RETURN[i] }; AddCon: PUBLIC PROC[] = {AddContour[defaultHandle]}; AddContour: PUBLIC PROC[handle: Handle] = { IF handle.otherContours = NIL THEN handle.otherContours ← LIST[[handle.traj, handle.slist]] ELSE { p: LIST OF TrajAndSlist ← handle.otherContours; UNTIL p.rest = NIL DO p ← p.rest ENDLOOP; p.rest ← LIST[[handle.traj, handle.slist]]; }; handle.slist ← NewSList[]; handle.traj ← NEW[Traj]; }; NextCon: PUBLIC PROC[] = {NextContour[defaultHandle]}; NextContour: PUBLIC PROC[handle: Handle] = { IF handle.otherContours # NIL THEN { p: LIST OF TrajAndSlist ← handle.otherContours; ts: TrajAndSlist ← handle.otherContours.first; handle.otherContours.first ← [handle.traj, handle.slist]; [handle.traj, handle.slist] ← ts; UNTIL p.rest = NIL DO p ← p.rest ENDLOOP; p.rest ← handle.otherContours; -- make it circular for a few microseconds p ← p.rest; handle.otherContours ← p.rest; p.rest ← NIL; } }; CurrentSamples: PUBLIC PROC[handle: Handle] RETURNS [z: Seq.ComplexSequence] = {OPEN handle↑; s: SampleHandle; i: NAT; n: NAT ← 0; first: SampleHandle ← IF slist.first=NIL THEN slist.header.next ELSE slist.first; last: SampleHandle ← IF slist.last=NIL THEN slist.header ELSE slist.last.next; FOR s ← first, s.next UNTIL s=last DO n ← n+1; ENDLOOP; z ← NEW[Seq.ComplexSequenceRec[n]]; i ← 0; FOR s←first, s.next UNTIL s=last DO z[i] ← s.xy; i←i+1; ENDLOOP; }; ResetDefaultNodes: PROC = {ResetNodes[defaultHandle]}; ResetNodes: PUBLIC PROC[handle: Handle] ={OPEN handle↑; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO s.isNode ← s.isCusp ← FALSE; s.tangent ← [0,0] ENDLOOP; }; AddNode: PUBLIC PROC[handle: Handle, index: NAT, tan: Complex.Vec ← [0,0]] = {OPEN handle↑; i:NAT ← 0; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO IF i=index THEN {s.isNode ← TRUE; s.tangent ← tan; RETURN}; i←i+1; ENDLOOP; }; DeleteNode: PUBLIC PROC[handle: Handle, index: NAT] = {OPEN handle↑; i:NAT ← 0; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO IF i=index THEN {s.isNode ← s.isCusp ← FALSE; s.tangent ← [0,0]; RETURN}; i←i+1; ENDLOOP; }; CurrentNodes: PUBLIC PROC [handle: Handle] RETURNS[nodes: Seq.NatSequence, tangents: Seq.ComplexSequence] = {OPEN handle↑; i,n:NAT ← 0; first: SampleHandle ← IF slist.first=NIL THEN slist.header.next ELSE slist.first; last: SampleHandle ← IF slist.last=NIL THEN slist.header ELSE slist.last.next; FOR s: SampleHandle ← first, s.next UNTIL s=last DO IF s.isNode THEN n←n+1; ENDLOOP; nodes ← NEW[Seq.NatSequenceRec[n]]; tangents ← NEW[Seq.ComplexSequenceRec[n]]; i ← n ← 0; FOR s: SampleHandle ← first, s.next UNTIL s=last DO IF s.isNode THEN {nodes[n] ← i; tangents[n] ← s.tangent; n←n+1}; i←i+1; ENDLOOP; }; ResetDefaultCusps: PROC = {ResetCusps[defaultHandle]}; ResetCusps: PUBLIC PROC [handle: Handle] = {OPEN handle↑; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO IF s.isCusp THEN {s.isCusp ← FALSE; s.tangent ← [0,0]; s.tanOut ← [0,0]}; ENDLOOP; }; AddCusp: PUBLIC PROC[handle: Handle, index: NAT, tanIn,tanOut: Complex.Vec ← [0,0]] = {OPEN handle↑; i:NAT ← 0; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO IF i=index THEN { s.isCusp ← s.isNode ← TRUE; s.tangent ← tanIn; s.tanOut ← tanOut; RETURN}; i←i+1; ENDLOOP; }; DeleteCusp: PUBLIC PROC[handle: Handle, index: NAT] = {OPEN handle↑; i:NAT ← 0; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO IF i=index THEN { s.isCusp ← s.isNode ← FALSE; s.tangent ← s.tanOut ← [0,0]; RETURN}; i←i+1; ENDLOOP; }; CurrentCusps: PUBLIC PROC[handle: Handle] RETURNS[cusps: Seq.NatSequence, tangents: Seq.ComplexSequence] = {OPEN handle↑; i,n:NAT ← 0; first: SampleHandle ← IF slist.first=NIL THEN slist.header.next ELSE slist.first; last: SampleHandle ← IF slist.last=NIL THEN slist.header ELSE slist.last.next; FOR s: SampleHandle ← first, s.next UNTIL s=last DO IF s.isCusp THEN n←n+1; ENDLOOP; cusps ← NEW[Seq.NatSequenceRec[n]]; tangents ← NEW[Seq.ComplexSequenceRec[2*n]]; i ← n ← 0; FOR s: SampleHandle ← first, s.next UNTIL s=last DO IF s.isCusp THEN { cusps[n] ← i; tangents[2*n] ← s.tangent; tangents[2*n+1] ← s.tanOut; n←n+1}; i←i+1; ENDLOOP; }; DrawSamples: PROC= {OPEN defaultHandle↑; Paint: PROC[dc: Graphics.Context] = { s: SampleHandle ← slist.header.next; MoveTo[dc,s.xy]; s ← s.next; WHILE s#slist.header DO DrawTo[dc,s.xy]; s ← s.next; ENDLOOP; IF typescript THEN PrintLine["\n"]; }; TJaMGraphics.Painter[Paint]; }; MarkSamples: PROC= {OPEN defaultHandle↑; Paint: PROC[dc: Graphics.Context] = { oldFat: BOOLEAN ← Graphics.SetFat[dc,TRUE]; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO MarkPoint[dc,s.xy]; ENDLOOP; [] ← Graphics.SetFat[dc,oldFat]; IF typescript THEN PrintLine["\n"]; }; TJaMGraphics.Painter[Paint]; }; MarkLinks: PROC = {OPEN defaultHandle↑; Paint: PROC[dc: Graphics.Context] = { Mark[dc,traj.links.cubic.b0]; FOR l: LinkHandle ← traj.links, l.next UNTIL l=NIL DO Mark[dc,[l.cubic.b3.x,l.cubic.b3.y]]; ENDLOOP; IF typescript THEN PrintLine["\n"]; }; IF traj.links # NIL THEN TJaMGraphics.Painter[Paint]; }; MarkNodes: PROC = {OPEN defaultHandle↑; Paint: PROC[dc: Graphics.Context] = { FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO IF s.isNode THEN MarkNode[dc,s]; ENDLOOP; IF typescript THEN PrintLine["\n"]; }; TJaMGraphics.Painter[Paint]; }; MarkNode: PROC [dc: Graphics.Context, s: SampleHandle] = { d:REAL ← markSize/scale; IF s.isCusp THEN d ← 1.5*d; IF s.tangent = [0,0] THEN BEGIN OPEN s.xy; MoveTo[dc,[x+d,y+d]]; DrawTo[dc,[x-d,y-d]]; MoveTo[dc,[x-d,y+d]]; DrawTo[dc,[x+d,y-d]]; END ELSE BEGIN t: Complex.Vec ← Complex.Mul[Vector.Unit[s.tangent],[0,2*d]]; MoveTo[dc,Complex.Sub[s.xy,t]]; DrawTo[dc,Complex.Add[s.xy,t]]; IF s.isCusp THEN { t ← Complex.Mul[Vector.Unit[s.tanOut],[0,2*d]]; MoveTo[dc,Complex.Sub[s.xy,t]]; DrawTo[dc,Complex.Add[s.xy,t]]; }; END }; DrawLinks: PROC = {OPEN defaultHandle↑; Paint: PROC[dc: Graphics.Context] = { MoveTo[dc,traj.links.cubic.b0]; FOR l: LinkHandle ← traj.links, l.next UNTIL l=NIL DO CurveTo[dc,l.cubic.b1,l.cubic.b2,l.cubic.b3]; ENDLOOP; IF fill THEN DrawArea[dc] ELSE DrawPath[dc,0]; IF typescript THEN PrintLine["\n"]; }; IF traj.links # NIL THEN TJaMGraphics.Painter[Paint]; }; markSize: REAL ← 2; markPath: Graphics.Path ← Graphics.NewPath[4]; SetMarkSize: PROC = {markSize ← GetReal[]; }; Mark: PROC[dc: Graphics.Context, pt: Complex.Vec] = { pt ← XForm[pt]; Graphics.MoveTo[markPath, 0, 0]; Graphics.Rectangle[markPath, pt.x-markSize,pt.y-markSize,pt.x+markSize,pt.y+markSize]; Graphics.DrawArea[dc, markPath]; IF typescript THEN {PrintPoint[pt]; PrintString["mark "]}; }; MarkPoint: PROC[dc: Graphics.Context, pt: Complex.Vec] = { pt ← XForm[pt]; Graphics.SetCP[dc,pt.x,pt.y]; Graphics.DrawTo[dc,pt.x,pt.y]; IF typescript THEN {PrintPoint[pt]; PrintString["mark "]}; }; SelectSa: PROC = {OPEN defaultHandle↑; closest: SampleHandle; z: Complex.Vec; z.y ← GetReal[]; z.x ← GetReal[]; [closest,] ← FindSa[z]; IF closest#NIL THEN slist.selectedSample ← closest; }; FindSa: PROC [z: Complex.Vec] RETURNS[found: SampleHandle, index: NAT] = {OPEN defaultHandle↑; closest,d: REAL ← 10.0E+30; i: NAT ← 0; found ← NIL; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO p: Complex.Vec ← XForm[s.xy]; IF ABS[p.x-z.x]<closest AND ABS[p.y-z.y]<closest AND (d ← Vector.Mag[Vector.Sub[z,p]]) < closest THEN {closest ← d; found ← s; index ← i}; i ← i+1; ENDLOOP; }; Subrange: PROC = {OPEN defaultHandle↑; first,last: SampleHandle; p0,p1: Complex.Vec; i0,i1: NAT; p1.y ← GetReal[]; p1.x ← GetReal[]; [last,i1] ← FindSa[p1]; p0.y ← GetReal[]; p0.x ← GetReal[]; [first,i0] ← FindSa[p0]; IF i0<i1 THEN {slist.first ← first; slist.last ← last} ELSE {slist.first ← last; slist.last ← first}; }; NoSubrange: PROC = {OPEN defaultHandle↑; slist.first ← slist.last ← NIL}; DeleteSa: PROC = {OPEN defaultHandle↑; IF slist.selectedSample # slist.header THEN { p,q: SampleHandle; p ← slist.selectedSample.prev; q ← slist.selectedSample.next; p.next ← q; q.prev ← p; slist.selectedSample ← q }; }; InsertSa: PROC = {OPEN defaultHandle↑; z: Complex.Vec; samp: SampleHandle ← slist.selectedSample; z.y ← GetReal[]; z.x ← GetReal[]; InsertBefore[z, slist.selectedSample]; }; InsertBetween: PROC = {OPEN defaultHandle↑; z: Complex.Vec; insertBefore: BOOLEAN; samp: SampleHandle ← slist.selectedSample; z.y ← GetReal[]; z.x ← GetReal[]; insertBefore ← (Vector.Mag[Vector.Sub[z, samp.prev.xy]] < Vector.Mag[Vector.Sub[z, samp.next.xy]]); IF insertBefore THEN InsertBefore[z, samp] ELSE InsertBefore[z, samp.next]; }; TheSa: PROC = {OPEN defaultHandle↑; PushReal[slist.selectedSample.xy.x]; PushReal[slist.selectedSample.xy.y]}; TheTan: PROC = {OPEN defaultHandle.slist.selectedSample; PushReal[tangent.x]; PushReal[tangent.y]; PushBoolean[isNode]}; HomeSa: PROC = {OPEN defaultHandle↑; slist.selectedSample ← slist.header}; MakeFirstSa: PROC = {OPEN defaultHandle↑; IF slist.selectedSample # NIL AND slist.selectedSample # slist.header AND slist.selectedSample # slist.header.next THEN { slist.header.next.prev ← slist.header.prev; slist.header.prev.next ← slist.header.next; slist.header.next ← slist.selectedSample; slist.header.prev ← slist.selectedSample.prev; slist.header.next.prev ← slist.header; slist.header.prev.next ← slist.header} }; NextSa: PROC = {OPEN defaultHandle↑; slist.selectedSample ← slist.selectedSample.next; IF slist.selectedSample = slist.header THEN slist.selectedSample ← slist.selectedSample.next }; PrevSa: PROC = {OPEN defaultHandle↑; slist.selectedSample ← slist.selectedSample.prev; IF slist.selectedSample = slist.header THEN slist.selectedSample ← slist.selectedSample.prev }; NodeSa: PROC = {OPEN defaultHandle↑; slist.selectedSample.isNode ← PopBoolean[]; IF ~slist.selectedSample.isNode THEN slist.selectedSample.isCusp ← FALSE; }; CuspSa: PROC = {OPEN defaultHandle↑; slist.selectedSample.isCusp ← slist.selectedSample.isNode ← PopBoolean[]}; CuspNode: PROC = {OPEN defaultHandle↑; IF slist.selectedSample.isNode THEN slist.selectedSample.isCusp ← PopBoolean[]}; GetVec: PROC RETURNS [z:Complex.Vec] = {z.y ← GetReal[]; z.x ← GetReal[]}; TanSa: PROC = {defaultHandle.slist.selectedSample.tangent ← GetVec[]}; TanOutSa: PROC = {defaultHandle.slist.selectedSample.tanOut ← GetVec[]}; ScaleSa: PROC = {OPEN defaultHandle↑; z: Complex.Vec ← GetVec[]; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO p:Complex.Vec ← s.xy; s.xy ← Complex.Mul[p,z]; p ← s.tangent; s.tangent ← Complex.Mul[p,z]; ENDLOOP; }; TranSa: PROC = {OPEN defaultHandle↑; z: Complex.Vec ← GetVec[]; FOR s: SampleHandle ← slist.header.next, s.next UNTIL s=slist.header DO s.xy ← Complex.Add[s.xy,z]; ENDLOOP; }; InterpolateSa: PROC = {OPEN defaultHandle↑; mindelta: REAL ← MAX[GetReal[], 0.00001]; s: SampleHandle ← slist.header.next; UNTIL s=slist.header DO nexts: SampleHandle ← s.next; nextxy: Complex.Vec ← (IF s.next = slist.header THEN slist.header.next ELSE s.next).xy; delta: Complex.Vec ← Complex.Sub[nextxy, s.xy]; k: REAL ← Real.RoundLI[Complex.Abs[delta]/mindelta]; FOR i: REAL ← 1, i+1 UNTIL i>k DO InsertBefore[Complex.Add[s.xy, Vector.Mul[delta, i/(k+1)]], nexts]; ENDLOOP; s ← nexts; ENDLOOP; }; PrintReal: PROC[r: REAL] = {OPEN IO; Put[stream,real[r],char[SP]]}; PrintString: PROC[s: LONG STRING] = {OPEN IO; Put[stream,string[s]]}; PrintLine: PROC[s: LONG STRING] = {OPEN IO; Put[stream,string[s],char[CR]]}; PrintPoint: PROC[pt: Complex.Vec] = {PrintReal[pt.x]; PrintReal[pt.y]}; cp: Complex.Vec ← [0,0]; thePath: Graphics.Path ← Graphics.NewPath[300]; MoveTo: PROC[dc: Graphics.Context, p: Complex.Vec] = { Graphics.SetColor[dc,Graphics.black]; [] ← Graphics.SetFat[dc,TRUE]; p ← XForm[p]; Graphics.MoveTo[thePath,p.x,p.y]; Graphics.SetCP[dc, p.x,p.y]; IF typescript AND fill THEN PrintLine["beginoutline"]; IF typescript THEN PrintPoint[p]; IF typescript AND fill THEN PrintLine["moveto"]; cp ← p; }; DrawTo: PROC[dc: Graphics.Context, p: Complex.Vec] = { p ← XForm[p]; Graphics.DrawTo[dc, p.x,p.y]; IF typescript THEN {PrintPoint[p]; PrintString["drawto "]}; cp ← p; }; LineTo: PROC[dc: Graphics.Context, p: Complex.Vec] = { p ← XForm[p]; Graphics.LineTo[thePath, p.x,p.y]; Graphics.SetCP[dc, p.x,p.y]; IF typescript THEN { IF fill THEN {PrintPoint[p]; PrintString["lineto "]} ELSE {PrintPoint[p]; PrintString["drawto "]}; }; cp ← p; }; DrawLine: PUBLIC PROC[from, to: Complex.Vec] = { Paint: PROC[dc: Graphics.Context] = { MoveTo[dc,from]; DrawTo[dc,to]; }; TJaMGraphics.Painter[Paint]; }; DrawCubic: PROC[dc: Graphics.Context, b: Cubic.Bezier] = { oldfill: BOOLEAN ← fill; fill ← FALSE; MoveTo[dc,b.b0]; CurveTo[dc,b.b1,b.b2,b.b3]; DrawPath[dc,0]; fill ← oldfill }; CurveTo: PROC[dc: Graphics.Context, p1,p2,p3: Complex.Vec] = { p1 ← XForm[p1]; p2 ← XForm[p2]; p3 ← XForm[p3]; Graphics.CurveTo[thePath,p1.x,p1.y,p2.x,p2.y,p3.x,p3.y]; Graphics.SetCP[dc, p3.x,p3.y]; IF typescript THEN { IF fill THEN { PrintPoint[p1]; PrintPoint[p2]; PrintPoint[p3]; PrintLine["curveto"]; } ELSE { PrintPoint[cp]; PrintPoint[p1]; PrintPoint[p2]; PrintPoint[p3]; PrintLine["bezier"]; }; }; cp ← p3; }; DrawPath: PROC[dc: Graphics.Context, width: REAL] = { Graphics.DrawStroke[dc,thePath,width]; }; DrawArea: PROC[dc: Graphics.Context, parityFill: BOOLEAN ← FALSE] = { Graphics.DrawArea[dc,thePath,parityFill]; IF typescript AND fill THEN PrintLine["endoutline"]; }; sLen: REAL ← 0; SetSLen: PROC = {sLen ← GetReal[]}; fill: BOOLEAN ← FALSE; SetFill: PROC = {fill ← PopBoolean[]}; scale: REAL ← 1; SetScale: PROC = {scale ← GetReal[]}; offset: Complex.Vec ← [0,0]; SetOffset: PROC = {offset.y ← GetReal[]; offset.x ← GetReal[]}; XForm: PROC[v: Complex.Vec] RETURNS[vt: Complex.Vec] = {vt.x ← v.x*scale+offset.x; vt.y ← v.y*scale+offset.y}; IXForm: PROC[v: Complex.Vec] RETURNS[vt: Complex.Vec] = {vt.x ← (v.x-offset.x)/scale; vt.y ← (v.y+offset.y)/scale}; typescript: BOOLEAN ← FALSE; SetTypescript: PROC = {typescript ← PopBoolean[]}; Note: PROC = { c: LONG STRING ← [80]; PopString[c]; IF typescript THEN PrintString[c]; }; Notes: PROC = { c: LONG STRING ← [80]; PopString[c]; IF typescript THEN {PrintString[c]; PrintString[" "]}; }; Noter: PROC = { c: LONG STRING ← [80]; PopString[c]; IF typescript THEN {PrintLine[c]; stream.Flush[]}; }; defaultHandle: PUBLIC Handle ← Create[]; [] ← Visible[defaultHandle]; JaMFnsDefs.Register[".startsa", StartSa]; JaMFnsDefs.Register[".resetsa", ResetSa]; JaMFnsDefs.Register[".addsa", AddSa]; JaMFnsDefs.Register[".countsa", CountSa]; JaMFnsDefs.Register[".foralllinks", ForAllLinks]; JaMFnsDefs.Register[".nextcon", NextCon]; JaMFnsDefs.Register[".resetcon", ResetCon]; JaMFnsDefs.Register[".addcon", AddCon]; JaMFnsDefs.Register[".countcon", CountCon]; JaMFnsDefs.Register[".interpolatesa", InterpolateSa]; -- d => . Interpolates samples to make deltas no larger than about d JaMFnsDefs.Register[".drawsa", DrawSamples]; JaMFnsDefs.Register[".marksa", MarkSamples]; JaMFnsDefs.Register[".drawli", DrawLinks]; JaMFnsDefs.Register[".markli", MarkLinks]; JaMFnsDefs.Register[".marknodes", MarkNodes]; JaMFnsDefs.Register[".setslen", SetSLen]; JaMFnsDefs.Register[".setfill", SetFill]; JaMFnsDefs.Register[".setlog", SetTypescript]; JaMFnsDefs.Register[".setscale", SetScale]; JaMFnsDefs.Register[".setoffset", SetOffset]; JaMFnsDefs.Register[".setmarksize", SetMarkSize]; JaMFnsDefs.Register[".note", Note]; JaMFnsDefs.Register[".notes", Notes]; JaMFnsDefs.Register[".noter", Noter]; -- Sample Editing Commands JaMFnsDefs.Register[".selectsa", SelectSa]; -- x y => . Selects a sample point for editing JaMFnsDefs.Register[".thesa", TheSa]; -- => x y . Returns the current sample JaMFnsDefs.Register[".thetan", TheTan]; -- => x y boolean . Returns the tangent of the current sample, and whether it is a node JaMFnsDefs.Register[".deletesa", DeleteSa]; -- => . Deletes the current sample JaMFnsDefs.Register[".insertsa", InsertSa]; -- x y => . Inserts before the current sample JaMFnsDefs.Register[".insertbetween", InsertBetween]; -- x y => . Inserts between the current sample and the neighbor nearest the new point JaMFnsDefs.Register[".nodesa", NodeSa]; -- boolean => . Makes or unmakes a node JaMFnsDefs.Register[".tansa", TanSa]; -- deltax deltay => . Sets the tangent at a sample JaMFnsDefs.Register[".cuspsa", CuspSa]; -- boolean => . Makes or unmakes a cusp JaMFnsDefs.Register[".cuspnode", CuspNode]; -- boolean => . Makes or unmakes a cusp ONLY on a node JaMFnsDefs.Register[".tanoutsa", TanOutSa]; -- deltax deltay => . Sets the outgoing tangent JaMFnsDefs.Register[".homesa", HomeSa]; -- => . Selects the header JaMFnsDefs.Register[".makefirstsa", MakeFirstSa]; -- => . Selects the header JaMFnsDefs.Register[".nextsa", NextSa]; -- => x y . Moves selection to the next sample JaMFnsDefs.Register[".prevsa", PrevSa]; -- => x y . Moves selection to the previous sample JaMFnsDefs.Register[".scalesa", ScaleSa]; -- x y => . Scales/rotates all samples by multiplying by x+iy JaMFnsDefs.Register[".transa", TranSa]; -- x y => . Translates all samples by adding x+iy JaMFnsDefs.Register[".subrange", Subrange]; -- change CurrentSamples so it returns a subrange JaMFnsDefs.Register[".allsa", NoSubrange]; -- remove the subrange JaMFnsDefs.Register[".resetnodes", ResetDefaultNodes]; -- remove the nodes JaMFnsDefs.Register[".resetcusps", ResetDefaultCusps]; -- remove the cusps JaMFnsDefs.Register[".openlogfile", OpenLogFile]; -- filename => . Switches the log file. JaMFnsDefs.Register[".closelogfile", CloseLogFile]; -- => . Closes the log file. END. Michael Plass August 13, 1982 9:48 am: Added multiple sample sets, InsertBetween. Michael Plass August 13, 1982 10:29 am: Added CornerSa. Michael Plass August 23, 1982 9:59 am: Added ForAllLinks. Michael Plass August 23, 1982 2:31 pm: Sent Curve.Log to file. Michael Plass August 23, 1982 2:34 pm: CornerSa moved out because of Storage overflow in Pass 3. CornerSa: PROC = {OPEN Vector; samp: SampleHandle ← IF defaultHandle.slist.selectedSample = defaultHandle.slist.header THEN defaultHandle.slist.selectedSample.next ELSE defaultHandle.slist.selectedSample; p: SampleHandle ← IF samp.prev = defaultHandle.slist.header THEN samp.prev.prev ELSE samp.prev; pp: SampleHandle ← IF p.prev = defaultHandle.slist.header THEN p.prev.prev ELSE p.prev; n: SampleHandle ← IF samp.next = defaultHandle.slist.header THEN samp.next.next ELSE samp.next; nn: SampleHandle ← IF n.next = defaultHandle.slist.header THEN n.next.next ELSE n.next; a: Vec ← pp.xy; b: Vec ← p.xy; c: Vec ← nn.xy; d: Vec ← n.xy; coeffMat: Matrix ← [a.y-b.y, b.x-a.x, c.y-d.y, d.x-c.x]; r: Vec ← [Det[[b.x, b.y, a.x, a.y]], Det[[d.x, d.y, c.x, c.y]]]; denom: REAL ← Det[coeffMat]; xTimesDenom: REAL ← Det[[r.x, coeffMat.a12, r.y, coeffMat.a22]]; yTimesDenom: REAL ← Det[[coeffMat.a11, r.x, coeffMat.a21, r.y]]; IF denom=0 THEN RETURN; samp.xy.x ← xTimesDenom/denom; samp.xy.y ← yTimesDenom/denom; }; JaMFnsDefs.Register[".cornersa", CornerSa]; -- => . moves the selected sample so it is colinear with its two left neightbors and with its two right neighbors Michael Plass August 24, 1982 9:48 am: Removed Curve.Log typescript. Michael Plass August 27, 1982 7:21 pm: Bulletproofed against empty links. Michael Plass August 30, 1982 1:33 pm: Added OpenLogFile. Michael Plass September 28, 1982 12:26 pm: Added CloseLogFile. Michael Plass December 7, 1982 10:25 am: Took out Transaction import for 3.5.