-- Analyze.mesa
-- M. Stone November 3, 1983 10:16 am
-- M. Plass March 12, 1982 11:21 am
DIRECTORY
Graphics,
Real,
Seq,
Runtime,
RealFns,
LSFit,
ConvertUnsafe USING [ToRope],
DynFit,
Vector USING[Sub,Cross,Dot,Mag,Vec,Mul,Det,Matrix],
CurveDefs,
TJaMGraphics USING[Painter],
AnalyzeDefs,
Cubic USING[Coeffs,Bezier,CoeffsToBezier],
JaMFnsDefs USING [PushReal, PushInteger, PopInteger,Register, GetReal,PopBoolean,PopString];
Analyze: PROGRAM
IMPORTS RealFns, TJaMGraphics, Graphics, Vector, JaMFnsDefs, CurveDefs, LSFit, DynFit, Runtime, ConvertUnsafe, Real, Cubic
EXPORTS CurveDefs,AnalyzeDefs = {
OPEN JaMFnsDefs, Vector,CurveDefs;
--JaM registered procedures
BaseLine: PROC = {
baseLine ← ~baseLine;
};
SAOff: PROC = {saOff.y ← GetReal[];saOff.x ← GetReal[]};
VSOff: PROC = {vOff ← GetReal[];sOff ← GetReal[]};
RDrawLocs: PROC = {
DrawLocs[];
};
SaScale: PROC = {saScale ← GetReal[];};
VScale: PROC = { vScale ← GetReal[];};
SScale: PROC = { sScale ← GetReal[];};
RLocateVS: PROC = {
y: REAL ← GetReal[];
x: REAL ← GetReal[];
LocateVS[x,y];
};
RLocateXY: PROC = {
v: REAL ← GetReal[];
s: REAL ← GetReal[];
LocateXY[s,v];
};
RPlotValues: PROCEDURE = {
PlotValues[values];
lastValues ← values;
};
RPlotDValues: PROCEDURE = {
PlotValues[dValues];
lastValues ← dValues;
};
RFilterValues: PROCEDURE = {
FilterValues[values];
};
FilterDValues: PROCEDURE = {
FilterValues[dValues];
};
DValues: PROC = {
i: INTEGER ← 0;
n: INTEGER ← values.l;
IF n<2 THEN RETURN;
maxDValue ← Real.SmallestNormalizedNumber;
minDValue ← Real.LargestNumber;
dValues ← NEW[ValueSeq[n]];
FOR i IN [0..n-1) DO
--dvalue.v is difference between adjacent values. dvalue.s is half way between
--the s of adjacent values
dValues[i].v ← values[i+1].v-values[i].v;
dValues[i].s ← (values[i+1].s+values[i].s)/2;
IF dValues[i].v >maxDValue THEN maxDValue ← dValues[i].v;
IF dValues[i].v <minDValue THEN minDValue ← dValues[i].v;
ENDLOOP;
dValues[n-1].v ← values[0].v-values[n-1].v;
--don't have the information to do this calculation exactly right
dValues[n-1].s ← values[n-1].s;
IF dValues[n-1].v >maxDValue THEN maxDValue ← dValues[n-1].v;
IF dValues[n-1].v <minDValue THEN minDValue ← dValues[n-1].v;
PushReal[maxDValue];
PushReal[minDValue];
};
DValuesToValues: PROCEDURE = {
values ← dValues;
dValues ← NIL;
maxValue ← maxDValue;
minValue ← minDValue;
};
ValuesToDValues: PROCEDURE = {
dValues ← values;
values ← NIL;
maxDValue ← maxValue;
minDValue ← minValue;
};
ValuesToSamples: PROC = {
sa: SampleHandle ← sList.samples;
FOR i: INTEGER IN [0..values.l) DO
IF sa=NIL THEN EXIT;
sa.value ← values[i].v;
sa ← sa.next;
ENDLOOP;
};
SamplesToValues: PROCEDURE = {
sa: SampleHandle ← sList.samples;
i: INTEGER ← 0;
values ← NEW[ValueSeq[sList.nsamples]];
maxValue ← Real.SmallestNormalizedNumber;
minValue ← Real.LargestNumber;
FOR i IN [0..sList.nsamples) DO
values[i] ← [s: sa.s, v: sa.value];
IF values[i].v >maxValue THEN maxValue ← values[i].v;
IF values[i].v <minValue THEN minValue ← values[i].v;
sa ← sa.next;
ENDLOOP;
};
SamplesToSequence: PROCEDURE [s: CurveDefs.SampleHandle]
RETURNS [z: Seq.ComplexSequence] =
BEGIN
sa: CurveDefs.SampleHandle;
i: NAT;
n:NAT ← 0;
FOR sa:CurveDefs.SampleHandle ← s, sa.next UNTIL sa=NIL DO
n ← n+1;
ENDLOOP;
z ← NEW[Seq.ComplexSequenceRec[n]];
i ← 0;
FOR sa←s, sa.next UNTIL sa=NIL DO
z[i] ← sa.xy;
i←i+1;
ENDLOOP;
END;
--JaM setting of analysis procedures
SetCos: PROCEDURE = {
sampleProc ← CheckCos;
};
SetDX: PROCEDURE = {
sampleProc ← CheckDx;
};
SetDY: PROCEDURE = {
sampleProc ← CheckDy;
};
SetMag: PROCEDURE = {
sampleProc ← CheckMag;
};
SetAngle: PROCEDURE = {
sampleProc ← CheckAngle;
};
SetDxAngle: PROCEDURE = {
sampleProc ← CheckDxAngle;
};
SetDyAngle: PROCEDURE = {
sampleProc ← CheckDyAngle;
};
SetArcLength: PROCEDURE = {
sampleProc ← ArcLength;
};
SetCurvature: PROCEDURE = {
sampleProc ← CheckCurvature;
};
SetCCurve: PROCEDURE = {
sampleProc ← CCurve;
};
SetCCurveAngle: PROCEDURE = {
sampleProc ← CCurveAngle;
};
--analysis procedures
SampleProc: TYPE = PROC[sa: SampleHandle];
sampleProc: SampleProc ← CheckAngle;
Prev: PROC [sa: SampleHandle] RETURNS[SampleHandle] = {
IF sa.prev#NIL THEN RETURN[sa.prev];
IF sList.closed THEN RETURN[sList.lastSample] ELSE RETURN[sa];
};
Next: PROC [sa: SampleHandle] RETURNS[SampleHandle] = {
IF sa.next#NIL THEN RETURN[sa.next];
IF sList.closed THEN RETURN[sList.samples] ELSE RETURN[sa];
};
CheckCos: SampleProc = {
v0: Vec ← Sub[sa.xy,Prev[sa].xy];
v1: Vec ← Sub[Next[sa].xy,sa.xy];
value: REAL ← Dot[v0,v1];
sa.value ← value/(Mag[v0]*Mag[v1]);
};
GetSin: SampleProc = {
v0: Vec ← Sub[sa.xy,Prev[sa].xy];
v1: Vec ← Sub[Next[sa].xy,sa.xy];
value: REAL ← Cross[v0,v1];
sa.value ← value/(Mag[v0]*Mag[v1]);
};
CheckMag: SampleProc = {
sa.value ←sa.s;
};
CheckAngle: SampleProc = {
x,y: REAL;
CheckCos[sa];
x ← sa.value;
GetSin[sa];
y ← sa.value;
sa.value ← RealFns.ArcTanDeg[y,x];
};
CheckDxAngle: SampleProc = {
dx,ds: REAL;
dx ← sa.xy.x-Prev[sa].xy.x;
ds ← sa.s-Prev[sa].s;
sa.value ← RealFns.ArcTanDeg[dx,ds];
};
CheckDyAngle: SampleProc = {
dy,ds: REAL;
dy ← sa.xy.y-Prev[sa].xy.y;
ds ← sa.s-Prev[sa].s;
sa.value ← RealFns.ArcTanDeg[dy,ds];
};
--this is angle/(m0+m1)/2 in degrees
CheckCurvature: SampleProc = {
v0: Vec ← Sub[sa.xy,Prev[sa].xy];
v1: Vec ← Sub[Next[sa].xy,sa.xy];
m0: REAL ← ABS[sa.s-Prev[sa].s];
m1: REAL ← ABS[Next[sa].s-sa.s];
x: REAL ← Dot[v0,v1];
y: REAL ← Cross[v0,v1];
sa.value ← RealFns.ArcTanDeg[y,x];
sa.value ← .5*sa.value/(m0+m1);
};
--this is angle/m0+m1/2 in radians
CCurveAngle: SampleProc = {
v0: Vec ← Sub[sa.xy,Prev[sa].xy];
v1: Vec ← Sub[Next[sa].xy,sa.xy];
m0: REAL ← ABS[sa.s-Prev[sa].s];
m1: REAL ← ABS[Next[sa].s-sa.s];
x: REAL ← Dot[v0,v1];
y: REAL ← Cross[v0,v1];
sa.value ← RealFns.ArcTan[y,x];
sa.value ← .5*sa.value/(m0+m1);
};
signal: BOOLEAN ← FALSE;
debug: BOOLEAN ← FALSE;
Debug: PROC = {debug ← ~debug};
CCurve: SampleProc = {
c,k: Vec;
valid: BOOLEAN;
r: REAL;
[c,valid] ← FindCircleCenter[Prev[sa].xy,sa.xy,Next[sa].xy];
IF signal AND ~valid THEN Runtime.CallDebugger["points too close"];
k ← Sub[c,sa.xy];
r ← Mag[k];
CCurveAngle[sa]; --compute the sign of the curvature
IF sa.value<0 THEN r ← -r;
IF ~valid OR r=0 THEN sa.value ← 0 ELSE sa.value ← 1/r;
IF debug THEN {
Paint: PROC[dc: Graphics.Context] = {
xy: Vec ← XFormSamplePt[sa.xy];
Graphics.MoveTo[dc,xy.x,xy.y];
xy ← XFormSamplePt[c];
Graphics.DrawTo[dc,xy.x,xy.y];
};
TJaMGraphics.Painter[Paint];
};
};
FindCircleCenter: PROCEDURE[p0,p1,p2:Vector.Vec] RETURNS [o:Vector.Vec,valid:BOOLEAN]=
{d1,d2:Vec;
d20,d21,det:REAL;
x20,y20,x21,y21,x22,y22:REAL;
eps:REAL←0.001;
d1←Mul[Sub[p0,p1],2];
d2←Mul[Sub[p1,p2],2];
x20←p0.x*p0.x; y20←p0.y*p0.y;
x21←p1.x*p1.x; y21←p1.y*p1.y;
x22←p2.x*p2.x; y22←p2.y*p2.y;
d20←x20-x21+y20-y21;
d21←x21-x22+y21-y22;
det←Det[Matrix[d1.x,d1.y,d2.x,d2.y]];
IF ABS [det] < eps THEN
{RETURN[Vec[0,0],FALSE]} ELSE
{o.x←(d20*d2.y-d21*d1.y)/det; o.y←(d1.x*d21-d2.x*d20)/det;
RETURN[o,TRUE];};
};
ArcLength: SampleProc = {
sa.value ← (Next[sa].s+sa.s)/2;
};
CheckDx: SampleProc = {
sa.value ← (sa.xy.x-Prev[sa].xy.x)/(sa.s-Prev[sa].s);
};
CheckDy: SampleProc = {
sa.value ← (sa.xy.y-Prev[sa].xy.y)/(sa.s-Prev[sa].s);
};
--plotting routines for Values sequences
sScale: REAL ← 1.5;
vScale: REAL ← 60;
sOff,vOff: REAL ← 0;
saScale: REAL ← 4;
saOff: Vec ← [0,0];
nLocs: INTEGER ← 20;
drawContour: BOOLEAN ← TRUE;
absValues: BOOLEAN ← FALSE;
baseLine: BOOLEAN ← TRUE;
values,dValues,lastValues: Values ← NIL;
maxValue,minValue,maxDValue,minDValue: REAL ← 0;
GetS: PROC[val: Values,index: INTEGER] RETURNS[s: REAL] = {
IF index >= values.l THEN RETURN[0];
s ← sScale*val[index].s+sOff;
IF absValues THEN s ← ABS[s];
};
GetValue: PROC[val: Values,index: INTEGER] RETURNS[value: REAL] = {
IF index >= values.l THEN RETURN[0];
value ← vScale*val[index].v+vOff;
IF absValues THEN value ← ABS[value];
};
XFormSamplePt: PROC[xy: Vec] RETURNS[p: Vec] = {
p.x ←saScale*xy.x+saOff.x;
p.y ←saScale*xy.y+saOff.y;
};
PlotValues: PROC[val: Values] = {
Paint: PROC[dc: Graphics.Context] = {OPEN Graphics;
length: INTEGER;
IF val=NIL THEN RETURN;
length ← val.l;
MoveTo[dc,GetS[val,0],GetValue[val,0]];
FOR i: INTEGER IN [1..length) DO DrawTo[dc,GetS[val,i],GetValue[val,i]]; ENDLOOP;
IF baseLine THEN {MoveTo[dc,sOff, vOff]; DrawTo[dc,GetS[val,length-1],vOff]};
};
TJaMGraphics.Painter[Paint];
};
DrawLocs: PROC = {
Paint: PROC[dc: Graphics.Context] = {OPEN Graphics;
i: INTEGER ← 0;
v: Vec;
drawlocs: PROC[sa: SampleHandle] = {
IF i MOD nLocs = 0 THEN {
v ← XFormSamplePt[sa.xy];
MoveTo[dc,v.x,v.y];
DrawTo[dc,GetS[lastValues,i],GetValue[lastValues,i]];
};
i ← i+1;
};
ForAllSamples[drawlocs];
};
TJaMGraphics.Painter[Paint];
};
LocateVS: PROC[x,y: REAL] = {
s: REAL ← -1;
tol: REAL ← 2/saScale;
findS: PROC [sa: SampleHandle] = {
IF x IN [sa.xy.x-tol..sa.xy.x+tol] AND y IN [sa.xy.y-tol..sa.xy.y+tol] THEN s ← sa.s;
};
Paint: PROC[dc: Graphics.Context] = {OPEN Graphics;
MoveTo[dc,x,y];
--find s by searching the contour for xy, then find v by searching the values
x ← (x-saOff.x)/saScale;
y ← (y-saOff.y)/saScale;
ForAllSamples[findS];
IF s=-1 THEN RETURN; --not found
FOR i: INTEGER IN [0..lastValues.l) DO
IF lastValues[i].s=s THEN DrawTo[dc,GetS[lastValues,i],GetValue[lastValues,i]];
ENDLOOP;
};
TJaMGraphics.Painter[Paint];
};
LocateXY: PROC[s,v: REAL] = {
xy: Vec ← [-10000,-10000];
pt: Vec ← [0,0];
vs: Vec ← [s,v];
findXY: PROC [sa: SampleHandle] = {
IF s IN [Prev[sa].s..Next[sa].s] THEN xy ← sa.xy;
};
Paint: PROC[dc: Graphics.Context] = {OPEN Graphics;
MoveTo[dc,s,v];
s ← (s-sOff)/sScale;
v ← (v-vOff)/vScale;
JaMFnsDefs.PushReal[s];
JaMFnsDefs.PushReal[v];
--find xy by searching the contour for s
ForAllSamples[findXY];
IF xy=[-10000,-10000] THEN RETURN;
pt ← XFormSamplePt[xy];
DrawTo[dc,pt.x,pt.y];
PushPoint[vs];
PushPoint[pt];
};
TJaMGraphics.Painter[Paint];
};
DrawContour: PROC = {OPEN Graphics;
Paint: PROC[dc: Graphics.Context] = {
v: Vec;
drawSample: PROC[sa: SampleHandle] = {
v ← XFormSamplePt[sa.xy];
DrawTo[dc,v.x,v.y];
};
IF sList=NIL THEN RETURN;
v ← XFormSamplePt[sList.samples.xy];
MoveTo[dc,v.x,v.y];
ForAllSamples[drawSample];
};
TJaMGraphics.Painter[Paint];
};
abs: BOOLEAN ← TRUE;
SetAbs: PROC = {abs ← JaMFnsDefs.PopBoolean[]};
ClearKnots: PROC = {cps.samples ← cps.lastSample ← NIL; nknots ← 0};
PickSumKnots: PROC = {
sa: SampleHandle ← sList.samples.next; --first point is duplicated on end
nextknot: SampleHandle ← cps.samples;
sum: REAL ← 0;
addKnot: PROC[sa: SampleHandle] = {
NewCPoint[sa];
sum ← 0;
};
eps: REAL ← GetReal[]; --change in angle in degrees
cps.arcLen ← sList.arcLen;
addKnot[sa];
UNTIL sa=NIL DO
--may have preset knots in list
IF nextknot#NIL AND sa.s>nextknot.s THEN {sum ← 0; nextknot ← nextknot.next};
sum ← sum + (IF abs THEN ABS[sa.value] ELSE sa.value);
IF ABS[sum]>eps THEN addKnot[sa];
sa ← sa.next;
ENDLOOP;
};
--pick one knot per peak. If peak on first/last boundary, may get double knot
PickKnots: PROC = {
sa: SampleHandle ← sList.samples.next; --first point is duplicated on end
sign: PROC[r: REAL] RETURNS[INTEGER] = {
IF r>0 THEN RETURN[1];
IF r<0 THEN RETURN[-1];
RETURN[0];
};
--sa is the left edge of a peak
addKnot: PROC[sa: SampleHandle] RETURNS [SampleHandle]= {
rt: SampleHandle ← sa;
max: SampleHandle ← sa;
UNTIL rt=NIL OR ABS[rt.value]<eps DO
IF sign[rt.value]#sign[sa.value] THEN EXIT;
IF ABS[rt.value]>ABS[max.value] THEN max ← rt;
rt ← rt.next;
ENDLOOP;
--rt is the right edge of a peak, max is max value
NewCPoint[max];
RETURN[rt];
};
eps: REAL ← GetReal[];
cps.arcLen ← sList.arcLen;
UNTIL sa=NIL DO
IF ABS[sa.value]>eps THEN sa ← addKnot[sa] ELSE sa ← sa.next;
ENDLOOP;
};
WriteKnots: PROC= {
s: STRING ← [40];
JaMFnsDefs.PopString[s];
IF cps.samples = NIL THEN RETURN;
CurveDefs.OpenFile[ConvertUnsafe.ToRope[s]];
CurveDefs.WriteSamples[cps.samples];
CurveDefs.CloseFile[];
};
NewCPoint: PROC[sa: SampleHandle] = {
s: SampleHandle ← NIL;
new: SampleHandle ← NEW[Sample];
new↑ ← [NIL,NIL,sa.xy,sa.s,sa.value];
IF cps.lastSample=NIL THEN {cps.samples ← cps.lastSample ← new}
ELSE
IF new.s<cps.samples.s THEN { --head of the list
IF TooClose[sa, cps.samples] THEN RETURN;
new.next ← cps.samples;
cps.samples.prev ← new;
cps.samples ← new;
}
ELSE {
FOR s ← cps.samples, s.next UNTIL s=cps.lastSample OR s.next.s>new.s DO ENDLOOP;
IF s=cps.lastSample AND cps.lastSample.s<new.s THEN { --tail of the list
IF TooClose[cps.lastSample,sa] THEN RETURN;
cps.lastSample.next ← new;
new.prev ← cps.lastSample;
cps.lastSample ← new;
}
ELSE {
IF TooClose[s,sa] THEN RETURN;
IF TooClose[sa,s.next] THEN RETURN;
new.next ← s.next; --middle of the list
s.next ← new;
new.next.prev ← new;
new.prev ← s;
};
};
nknots ← nknots+1;
Mark[XFormSamplePt[new.xy]];
};
TooClose: PROC[k0,k1: SampleHandle] RETURNS[BOOLEAN] = {
sa0,sa1: SampleHandle;
count: NAT ← 0;
FOR sa0 ← sList.samples, sa0.next UNTIL sa0.s >= k0.s DO ENDLOOP;
FOR sa1 ← sa0, sa1.next UNTIL sa1.s >= k1.s DO count ← count+1; ENDLOOP;
IF count<4 THEN RETURN[TRUE] ELSE RETURN[FALSE];
};
DeleteCPoint: PROC[sa: SampleHandle] = {
s: SampleHandle ← NIL;
IF cps.lastSample=NIL THEN RETURN;
IF sa=cps.samples THEN {
cps.samples ← cps.samples.next;
IF cps.samples#NIL THEN cps.samples.prev ← NIL ELSE cps.lastSample←NIL;
}
ELSE {
FOR s ← cps.samples, s.next UNTIL s=NIL OR s=sa DO ENDLOOP;
IF s=NIL THEN RETURN;
s.prev.next ← s.next;
IF sa#cps.lastSample THEN sa.next.prev ← sa.prev ELSE cps.lastSample ← sa.prev;
};
nknots ← nknots-1;
EraseMark[XFormSamplePt[sa.xy]];
};
InsertKnot: PROC = {
s: SampleHandle ← NIL;
tol: REAL ← 2/saScale;
y: REAL ← GetReal[];
x: REAL ← GetReal[];
findS: PROC [sa: SampleHandle] = {
IF x IN [sa.xy.x-tol..sa.xy.x+tol] AND y IN [sa.xy.y-tol..sa.xy.y+tol] THEN s ← sa;
};
x ← (x-saOff.x)/saScale;
y ← (y-saOff.y)/saScale;
ForAllSamples[findS];
IF s#NIL THEN NewCPoint[s];
};
RemoveKnot: PROC = {
tol: REAL ← 2/saScale;
y: REAL ← GetReal[];
x: REAL ← GetReal[];
x ← (x-saOff.x)/saScale;
y ← (y-saOff.y)/saScale;
FOR sa: SampleHandle ← cps.samples, sa.next UNTIL sa=NIL DO
IF x IN [sa.xy.x-tol..sa.xy.x+tol] AND y IN [sa.xy.y-tol..sa.xy.y+tol] THEN {DeleteCPoint[sa]; EXIT};
ENDLOOP;
};
nknots: INTEGER ← 0;
cps: SListHandle ← NEW[SList];
lsHandle: LSFit.Handle;
InitLSFit: PROC = {ENABLE ABORTED => GOTO Quit;
sa: SampleHandle;
slist: SListHandle;
scale,initlS: REAL ← 0;
FOR sa ← sList.samples, sa.next UNTIL sa=NIL DO
IF cps.samples.s IN [sa.s..Next[sa].s) THEN EXIT;
ENDLOOP;
IF sa=NIL THEN RETURN;
--sa now points to the break point. Make a new slist that starts at sa
initlS ← sa.s; --will need this to translate the knots
slist ← CopySamples[sList,sa];
lsHandle ← LSFit.Create[SamplesToSequence[slist.samples]];
lsHandle.closedCurve ← sList.closed;
lsHandle.knots ← NEW[Seq.RealSequenceRec[nknots+1]];
sa ← cps.samples;
scale ← slist.arcLen/(IF cps.arcLen=0.0 THEN 1.0 ELSE cps.arcLen);
FOR i: INTEGER IN [0..nknots) DO
new: REAL ← scale*sa.s-initlS;
IF new<0 THEN new ← slist.arcLen+new;
lsHandle.knots[i] ← new;
sa ← sa.next;
ENDLOOP;
lsHandle.knots[0] ← 0; --might want to flag this if it isn't already true
lsHandle.knots[nknots] ← slist.arcLen;
LSFit.InitialTValues[lsHandle];
LSFit.FitXAndY[lsHandle];
EXITS Quit => {}
};
ShowKnots: PROC = {
FOR cp: SampleHandle ← cps.samples, cp.next UNTIL cp=NIL DO
FOR sa : SampleHandle ← sList.samples, sa.next UNTIL sa=NIL DO
IF cp.s IN [sa.s..Next[sa].s) THEN {Mark[XFormSamplePt[sa.xy]]; EXIT};
ENDLOOP;
ENDLOOP
};
Mark: PROC[pt: Vec] = {
Paint: PROC[dc: Graphics.Context] = {
Graphics.DrawBox[dc,[pt.x-2,pt.y-2,pt.x+2,pt.y+2]];
};
TJaMGraphics.Painter[Paint];
};
EraseMark: PROC[pt: Vec] = {
Paint: PROC[dc: Graphics.Context] = {
Graphics.SetColor[dc,Graphics.white];
Graphics.DrawBox[dc,[pt.x-2,pt.y-2,pt.x+2,pt.y+2]];
Graphics.SetColor[dc,Graphics.black];
};
TJaMGraphics.Painter[Paint];
};
sList: SListHandle ← NIL;
AnalyzeContour: PUBLIC PROCEDURE[contour: ContourHandle] = {
i: INTEGER ← 0;
--check all the null cases
IF contour=NIL THEN RETURN ELSE sList ← CopySamples[contour.sLists];
DoAnalysis[];
};
CopySamples: PROC[from: SListHandle, first: SampleHandle ← NIL ] RETURNS[to: SListHandle] = {
start,sa: SampleHandle;
old: SListHandle ← sList;
IF first=from.samples THEN first ← NIL; --this is the convention
IF first=NIL THEN start ← from.samples ELSE start ← first;
StartSamples[start.xy.x,start.xy.y];
FOR sa ← start.next, sa.next UNTIL sa=NIL DO
NewSample[sa.xy.x,sa.xy.y];
ENDLOOP;
IF first#NIL THEN FOR sa ← from.samples, sa.next UNTIL sa.next=first DO
NewSample[sa.xy.x,sa.xy.y];
ENDLOOP;
sList.closed ← from.closed;
SetArcLen[];
to ← sList;
sList ← old;
};
SetArcLen: PROC = {
s: REAL ← 0;
sList.nsamples ← 0;
sList.samples.s ← 0;
FOR sa: SampleHandle ← sList.samples.next, sa.next UNTIL sa=NIL DO
s ← s + Mag[Sub[sa.xy, sa.prev.xy]];
sa.s ← s;
sList.nsamples ← sList.nsamples+1;
ENDLOOP;
IF sList.closed THEN s ← s + Mag[Sub[sList.samples.xy, sList.lastSample.xy]];
sList.arcLen ← s;
};
DoAnalysis: PROC = {
IF sList=NIL THEN RETURN;
IF sList.nsamples <= 3 THEN RETURN;
ForAllSamples[sampleProc];
SamplesToValues[];
PushInteger[sList.nsamples];
};
ForAllSamples: PROC[proc: SampleProc] = {
FOR sa: SampleHandle ← sList.samples, sa.next UNTIL sa=NIL DO
proc[sa ! Real.RealException => {sa.value ← 0; CONTINUE}];
ENDLOOP;
};
Dynfilter: PROCEDURE =
BEGIN ENABLE ABORTED => GOTO Quit;
tolerance: REAL = GetReal[];
totalBadness: REAL ← 0;
segments: DynFit.Segments;
newZ: Seq.ComplexSequence;
lsHandle: LSFit.Handle;
len: NAT;
IF sList=NIL THEN RETURN;
lsHandle ← LSFit.Create[SamplesToSequence[sList.samples]];
[segments,totalBadness] ← DynFit.FitSegments[lsHandle.z,tolerance];
[newZ,len] ← DynFit.SampleSegments[segments,lsHandle.z];
StartSamples[newZ[0].x,newZ[0].y];
FOR i:NAT IN (0..len) DO NewSample[newZ[i].x,newZ[i].y] ENDLOOP;
SetArcLen[];
EXITS Quit => {}
END;
StartSamples: PROC[x,y: REAL] = {
sList ← NEW[SList];
sList.samples ← NEW[Sample];
sList.samples.xy ← [x,y];
sList.lastSample ← sList.samples;
};
sLen: REAL ← 1.0/256.0;
SetSLen: PROC = {sLen ← GetReal[]};
NewSample: PROC[x,y: REAL] = {OPEN Vector;
new: SampleHandle;
s: REAL ← Mag[Sub[[x,y],sList.lastSample.xy]];
IF s>sLen THEN {
new ← NEW[Sample];
sList.lastSample.next ← new;
new.prev ← sList.lastSample;
sList.lastSample ← sList.lastSample.next;
new.xy ← [x,y];
new.s ← s + new.prev.s;
};
};
FilterValues: PROC[val: Values] = {
new,old: REAL ← 0;
i: INTEGER ← 0;
length: INTEGER ← val.l;
closed: BOOLEAN ← sList.closed;
IF val=NIL THEN RETURN;
length ← val.l;
IF closed THEN new ← (val[length-1].v+val[0].v+val[1].v)/3
ELSE new ← (val[0].v+val[1].v)/2;
FOR i IN [1..length-1) DO
old ← new;
new ← (val[i-1].v+val[i].v+val[i+1].v)/3;
val[i-1].v ← old;
ENDLOOP;
old ← new;
IF closed THEN val[length-1].v ← (val[length-2].v+val[length-1].v+val[0].v)/3
ELSE val[length-1].v ← (val[length-2].v+val[length-1].v)/2;
val[length-2].v ← old;
};
WriteContour: PROC = {
lastContour: ContourHandle ← NEW[Contour];
s: REAL;
minX ← 10000; minY ← 10000;
maxX ← 0; maxY ← 0;
SetMinMax[];
s ← maxY-minY;
IF maxX-minX > s THEN s ← maxX-minX;
lastContour.sLists ← sList;
CurveDefs.WriteContour[lastContour,1/s,[-minY,-minX]]
};
minX,maxX,minY,maxY: REAL ← 0;
SetMinMax: PROC [] = {
compare: PROC [sa: SampleHandle] = {
IF sa.xy.x>maxX THEN maxX ← sa.xy.x;
IF sa.xy.y>maxY THEN maxY ← sa.xy.y;
IF sa.xy.x<minX THEN minX ← sa.xy.x;
IF sa.xy.y<minY THEN minY ← sa.xy.y;
};
ForAllSamples[compare];
};
GetVS: PROC =
BEGIN ENABLE ABORTED => GOTO Quit;
i:INTEGER ← JaMFnsDefs.PopInteger[];
v:Vec;
v.x ← GetS[values,i];
v.y ← GetValue[values,i];
PushPoint[v];
EXITS Quit => {}
END;
NValues: PROC = {PushInteger[values.l]};
PushPoint: PROC[p: Vec] = {JaMFnsDefs.PushReal[p.x]; JaMFnsDefs.PushReal[p.y]};
--the following routines are exported to AnalyzeDefs
PushKnots: PROC = {
FOR sa: SampleHandle ← cps.samples, sa.next UNTIL sa=NIL DO
PushPoint[sa.xy];
ENDLOOP;
};
-- Initialization starts here
Register[".nvalues"L,NValues];
Register[".getvs"L,GetVS];
Register[".analyze"L,DoAnalysis];
Register[".baseline"L,BaseLine];
Register[".dcon"L,DrawContour];
Register[".drawlocs"L,RDrawLocs];
Register[".locatevs"L,RLocateVS];
Register[".locatexy"L,RLocateXY];
Register[".dfcon"L,Dynfilter];
Register[".pk"L,PickKnots];
Register[".psk"L,PickSumKnots];
Register[".insertknot"L,InsertKnot];
Register[".removeknot"L,RemoveKnot];
Register[".clearknots"L,ClearKnots];
Register[".startls"L,InitLSFit];
Register[".saoff"L,SAOff];
Register[".vsoff"L,VSOff];
Register[".sscale"L,SScale];
Register[".sascale"L,SaScale];
Register[".vscale"L,VScale];
Register[".anslen"L,SetSLen];
Register[".abs"L,SetAbs];
Register[".plotvalues"L,RPlotValues];
Register[".plotdvalues"L,RPlotDValues];
Register[".dvalues"L,DValues];
Register[".filtervalues"L,RFilterValues];
Register[".filterdvalues"L,FilterDValues];
Register[".valuestodvalues"L,ValuesToDValues];
Register[".dvaluestovalues"L,DValuesToValues];
Register[".samplestovalues"L,SamplesToValues];
Register[".valuestosamples"L,ValuesToSamples];
Register[".dx"L,SetDX];
Register[".dy"L,SetDY];
Register[".setmag"L,SetMag];
Register[".setcos"L,SetCos];
Register[".angle"L,SetAngle];
Register[".dxangle"L,SetDxAngle];
Register[".dyangle"L,SetDyAngle];
Register[".arclength"L,SetArcLength];
Register[".curvature"L,SetCurvature];
Register[".ccurve"L,SetCCurve];
Register[".ccurveangle"L,SetCCurveAngle];
Register[".andebug"L,Debug];
Register[".showknots"L,ShowKnots];
Register[".pushknots"L,PushKnots];
}.