--compiler localspline --m.stone September 26, 1980 5:09 PM DIRECTORY SplineMemoryDefs: FROM "SplineMemoryDefs", SplineDefs: FROM "SplineDefs"; LocalSpline: PROGRAM IMPORTS SplineMemoryDefs EXPORTS SplineDefs = BEGIN OPEN SplineDefs; --here are some REAL constants used for assigning real constants. ONE: REAL ← 1; TWO: REAL ← 2; E1: REAL ← 10; E2: REAL ← 100; E3: REAL ← 1000; E4: REAL ← 10000; TooFewKnots: PUBLIC SIGNAL[numknots: INTEGER] = CODE; UnknownSpline: PUBLIC SIGNAL[splineType: SplineType] = CODE; RABS: PROCEDURE[r: REAL] RETURNS[REAL] = INLINE BEGIN RETURN[IF r<0 THEN -r ELSE r] END; MakeSpline: PUBLIC PROCEDURE [knots: DESCRIPTOR FOR ARRAY OF FPCoords, splineType: SplineType] RETURNS [DESCRIPTOR FOR ARRAY OF Coeffs] = BEGIN allCoeffs: DESCRIPTOR FOR ARRAY OF Coeffs; numknots: INTEGER ← LENGTH[knots]; IF numknots <= 0 THEN SIGNAL TooFewKnots[numknots]; --special case for 1 and 2 points IF numknots < 3 THEN BEGIN allCoeffs ← DESCRIPTOR[SplineMemoryDefs.Allocate[SIZE[Coeffs]], 1]; allCoeffs[0].t0 ← knots[0]; allCoeffs[0].t1 ← [0,0]; allCoeffs[0].t2 ← [0,0]; allCoeffs[0].t3 ← [0,0]; IF numknots=2 THEN BEGIN allCoeffs[0].t1[1] ← knots[1][1]-knots[0][1]; allCoeffs[0].t1[2] ← knots[1][2]-knots[0][2]; END; RETURN[allCoeffs]; END; SELECT splineType FROM bsplineInterp => BEGIN interpKnots: DESCRIPTOR FOR ARRAY OF FPCoords ← KnotsToCPoints[knots]; allCoeffs ← MakeLocalSpline[interpKnots,bspline]; SplineMemoryDefs.Free[BASE[interpKnots]]; --free the array space END; bezier => BEGIN kindex,cindex,i: INTEGER; fourKnots: ARRAY [0..3] OF FPCoords; --allocate for allCoeffs IF numknots>=4 THEN i ← (numknots-1)/3 ELSE i ←0; IF (numknots-1) MOD 3#0 THEN i ← i+1; --corresponds to knots hanging over allCoeffs ← DESCRIPTOR[SplineMemoryDefs.Allocate[i*SIZE[Coeffs]],i]; cindex ← 0; --set up first 4 knots, duplicating if necessary FOR kindex IN [0..3] DO IF kindex >numknots-1 THEN EXIT; fourKnots[kindex] ← knots[kindex]; ENDLOOP; --only case where duplication is necessary since check above for numknots<3 IF numknots=3 THEN fourKnots[3] ← fourKnots[2]; kindex ← 4; --need to do this at least once DO allCoeffs[cindex] ← Bezier[DESCRIPTOR[fourKnots]]; cindex ← cindex + 1; --Take 4th knot + next 3 knots. Special case if less than 3 left fourKnots[0] ← fourKnots[3]; SELECT numknots-kindex FROM <=0 => EXIT; =1 => BEGIN fourKnots[1] ← fourKnots[0]; fourKnots[2] ← knots[kindex]; kindex ← kindex+1; fourKnots[3] ← fourKnots[2]; END; =2 => BEGIN FOR i IN [1..2] DO fourKnots[i] ← knots[kindex]; kindex ← kindex+1; ENDLOOP; fourKnots[3] ← fourKnots[2]; END; >=3 => FOR i IN [1..3] DO fourKnots[i] ← knots[kindex]; kindex ← kindex+1; ENDLOOP; ENDCASE; ENDLOOP; END; IN [bspline..crspline] => allCoeffs ← MakeLocalSpline[knots,splineType]; ENDCASE => SIGNAL UnknownSpline[splineType]; RETURN[allCoeffs]; END; --take knots and Spline procedure name and draw the curve MakeLocalSpline: PROCEDURE [knots: DESCRIPTOR FOR ARRAY OF FPCoords, splineType: SplineType] RETURNS [DESCRIPTOR FOR ARRAY OF Coeffs] = BEGIN fourKnots: ARRAY [0..3] OF FPCoords; kindex,cindex,i: INTEGER; numknots: INTEGER ← LENGTH[knots]; lastknot: INTEGER ← numknots-1; allCoeffs: DESCRIPTOR FOR ARRAY OF Coeffs; IF numknots >= 4 THEN i ← numknots-3 ELSE i ← 1; allCoeffs ← DESCRIPTOR[SplineMemoryDefs.Allocate[i*SIZE[Coeffs]],i]; cindex ← 0; --set up first 4 knots FOR kindex IN [0..3] DO IF kindex >lastknot THEN EXIT; fourKnots[kindex] ← knots[kindex]; ENDLOOP; IF numknots=3 THEN fourKnots[3] ← fourKnots[2]; kindex ← 4; --need to do this at least once DO allCoeffs[cindex] ← SELECT splineType FROM bspline => BSpline[DESCRIPTOR[fourKnots]], crspline => CRSpline[DESCRIPTOR[fourKnots]], ENDCASE => ZeroSpline[DESCRIPTOR[fourKnots]]; cindex ← cindex+1; IF kindex > lastknot THEN EXIT; --shift the segment over fourKnots[0] ← fourKnots[1]; fourKnots[1] ← fourKnots[2]; fourKnots[2] ← fourKnots[3]; fourKnots[3] ← knots[kindex]; kindex ← kindex+1; ENDLOOP; RETURN[allCoeffs]; END; --Preform Inversion routine to get control points from drawn points --From Yamaguci's paper KnotsToCPoints: PROCEDURE[fpKnots: DESCRIPTOR FOR ARRAY OF FPCoords] RETURNS[fpCPoints: DESCRIPTOR FOR ARRAY OF FPCoords] = BEGIN delta: DESCRIPTOR FOR ARRAY OF REAL; deltaMax,tolerence: REAL; xyzw,i,loopCount: INTEGER; numknots: INTEGER ← LENGTH[fpKnots]; fpCPoints ← DESCRIPTOR[SplineMemoryDefs.Allocate[(numknots+2)*SIZE[FPCoords]],numknots+2]; delta ← DESCRIPTOR[SplineMemoryDefs.Allocate[numknots*SIZE[REAL]],numknots]; --inits FOR i IN [1..numknots] DO fpCPoints[i] ← fpKnots[i-1]; ENDLOOP; --set the first and last control points, and the tolerence fpCPoints[0] ← fpCPoints[1]; fpCPoints[numknots+1] ← fpCPoints[numknots]; tolerence ← ONE/3; --Main Loop loopCount ← 0; DO deltaMax ← 0; --iterate and collect maximum delta FOR xyzw IN [1..NDIM] DO FOR i IN [1..numknots] DO delta[i-1] ← fpKnots[i-1][xyzw] -fpCPoints[i][xyzw] +fpKnots[i-1][xyzw]/2-(fpCPoints[i-1][xyzw]+fpCPoints[i+1][xyzw])/4; IF RABS[delta[i-1]] >= deltaMax THEN deltaMax ← RABS[delta[i-1]]; ENDLOOP; FOR i IN [1..numknots] DO fpCPoints[i][xyzw] ← delta[i-1]+fpCPoints[i][xyzw]; ENDLOOP; ENDLOOP; --set the first and last control points fpCPoints[0] ← fpCPoints[1]; fpCPoints[numknots+1] ← fpCPoints[numknots]; --End Check IF RABS[deltaMax] <= tolerence THEN EXIT; ENDLOOP; SplineMemoryDefs.Free[BASE[delta]]; --free the deltas array END; --take 4 knots and make the BSpline Coeficient matrix BSpline: PROCEDURE[fpKnots4: DESCRIPTOR FOR ARRAY OF FPCoords] RETURNS[coeffs: Coeffs] = BEGIN xyzw: INTEGER; FOR xyzw IN [1..NDIM] DO coeffs.t3[xyzw] ← (-fpKnots4[0][xyzw] +3*fpKnots4[1][xyzw] -3*fpKnots4[2][xyzw] +fpKnots4[3][xyzw])/6; coeffs.t2[xyzw] ← (3*fpKnots4[0][xyzw]-6*fpKnots4[1][xyzw]+3*fpKnots4[2][xyzw])/6; coeffs.t1[xyzw] ← (-3*fpKnots4[0][xyzw]+3*fpKnots4[2][xyzw])/6; coeffs.t0[xyzw] ← (fpKnots4[0][xyzw]+4*fpKnots4[1][xyzw]+fpKnots4[2][xyzw])/6; ENDLOOP; END; --take 4 knots and make the Catmul-Rom Coeficient matrix --tan = a(P2 - P1) where a is chosen to be .5 CRSpline: PROCEDURE[fpKnots4: DESCRIPTOR FOR ARRAY OF FPCoords] RETURNS[coeffs: Coeffs] = BEGIN xyzw: INTEGER; a: REAL ←5/E1; FOR xyzw IN [1..NDIM] DO coeffs.t3[xyzw] ← (-a*fpKnots4[0][xyzw] +(2-a)*fpKnots4[1][xyzw] +(a-2)*fpKnots4[2][xyzw] +a*fpKnots4[3][xyzw]); coeffs.t2[xyzw] ← (2*a*fpKnots4[0][xyzw]+(a-3)*fpKnots4[1][xyzw]+(3-2*a)*fpKnots4[2][xyzw]-a*fpKnots4[3][xyzw]); coeffs.t1[xyzw] ← (-a*fpKnots4[0][xyzw]+a*fpKnots4[2][xyzw]); coeffs.t0[xyzw] ← fpKnots4[1][xyzw]; ENDLOOP; END; --take 4 knots and make the Bezier Coeficient matrix Bezier: PROCEDURE[fpKnots4: DESCRIPTOR FOR ARRAY OF FPCoords] RETURNS[coeffs: Coeffs] = BEGIN xyzw: INTEGER; FOR xyzw IN [1..NDIM] DO coeffs.t3[xyzw] ← -fpKnots4[0][xyzw] +3*fpKnots4[1][xyzw] -3*fpKnots4[2][xyzw] +fpKnots4[3][xyzw]; coeffs.t2[xyzw] ← 3*fpKnots4[0][xyzw]-6*fpKnots4[1][xyzw]+3*fpKnots4[2][xyzw]; coeffs.t1[xyzw] ← -3*fpKnots4[0][xyzw]+3*fpKnots4[1][xyzw]; coeffs.t0[xyzw] ← fpKnots4[0][xyzw]; ENDLOOP; END; --ZeroSpline fills the coeff matrix with zeros. Mostly for a pesky SELECT expression, but --might be generally useful ZeroSpline: PROCEDURE[fpKnots4: DESCRIPTOR FOR ARRAY OF FPCoords] RETURNS[coeffs: Coeffs] = BEGIN xyzw: INTEGER; FOR xyzw IN [1..NDIM] DO coeffs.t3[xyzw] ← 0; coeffs.t2[xyzw] ← 0; coeffs.t1[xyzw] ← 0; coeffs.t0[xyzw] ← 0; ENDLOOP; END; END.