General Testing Program
ContoursTest: Commander.CommandProc ~ {
p: ProgramData ¬ NEW[ProgramDataRec];
p.contour ¬ Controls.NewControl[
name: "Contour and Normals", type: contour, clientData: p, w: 180, flavor: $Normals];
p.scaler ¬ Controls.NewControl["Scale", vSlider, p, 0.0, 5.0, 1.0, Scale,,,,,,, 138];
p.outerData ¬ Controls.OuterViewer[
name: "Contours Test",
column: left,
controls: LIST[p.contour, p.scaler],
buttons:
LIST[
Controls.ClickButton["Normals-Off", ToggleNormals, p, 0],
Controls.ClickButton["Read", Read, p, 0],
Controls.ClickButton["Write", Write, p, 0],
Controls.ClickButton["Undo", Undo, p, 1],
Controls.ClickButton["Center", Center, p, 1],
Controls.ClickButton["Thin", Thin, p, 1],
Controls.ClickButton["Smooth", Smooth, p, 1]],
typescriptHeight: 18,
clientData: p];
};
Scale: Controls.ControlProc ~ {
p: ProgramData ¬ NARROW[control.clientData];
IF control.mouse.state = down
THEN {
ips: IntegerPairSequence ¬ Controls.GetContour[p.contour];
IF ips #
NIL
AND (p.save =
NIL
OR p.save.length # ips.length)
THEN {
p.save ¬ Controls.GetContour[p.contour];
p.center ¬ G2dContour.Centroid[G2dContour.FromIntegerPairs[p.save, TRUE]];
};
};
IF p.save #
NIL
THEN {
c: Contour ¬ G2dContour.FromIntegerPairs[p.save, TRUE];
c ¬ G2dContour.Offset[c, [-p.center.x, -p.center.y]];
c ¬ G2dContour.Scale[c, [p.scaler.value, p.scaler.value]];
c ¬ G2dContour.Offset[c, p.center];
ToControl[p.contour, c];
};
};
Center: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
p.center ¬ [p.contour.w/2, p.contour.h/2];
ToControl[p.contour,G2dContour.Offset[G2dContour.Center[FromControl[p.contour]],p.center]];
p.save ¬ Controls.GetContour[p.contour];
};
Thin: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
p.save ¬ Controls.GetContour[p.contour];
ToControl[p.contour, G2dContour.Thin[FromControl[p.contour]]];
};
Smooth: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
p.save ¬ Controls.GetContour[p.contour];
ToControl[p.contour, G2dContour.Smooth[FromControl[p.contour]]];
};
Undo: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
Controls.SetContour[p.contour, p.save, TRUE];
p.center ¬ G2dContour.Centroid[FromControl[p.contour]];
};
ToggleNormals: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
Controls.ButtonToggle[p.outerData, p.normals ¬ NOT p.normals, "Normals-On", "Normals-Off"];
ViewerOps.PaintViewer[p.contour.viewer, client, FALSE, NIL];
};
PaintNormals: ViewerClasses.PaintProc ~ {
control: Control ¬ NARROW[self.data];
p: ProgramData ¬ NARROW[control.clientData];
IF p.normals
THEN {
contour: Contour ¬ FromControl[p.contour];
IF contour = NIL THEN RETURN;
contour.normals ¬ G2dContour.Normals[contour];
G2dContour.Paint[contour, context, TRUE];
}
ELSE [] ¬ ControlsPrivate.PaintContour[self, context, whatChanged, clear];
};
Read: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
ts: Controls.Typescript ¬ p.outerData.typescript;
name: ROPE ¬ Controls.TypescriptReadFileName[ts];
IF name #
NIL
THEN {
stream: IO.STREAM ¬ FS.StreamOpen[name ! FS.Error => GOTO Bad];
ToControl[p.contour, G2dContour.Read[stream]];
EXITS Bad => Controls.TypescriptWrite[ts, IO.PutFR1["Can't open %g\n", IO.rope[name]]];
};
};
Write: Controls.ClickProc ~ {
p: ProgramData ¬ NARROW[clientData];
r: ROPE ¬ Controls.TypescriptReadFileName[p.outerData.typescript];
IF r # NIL THEN G2dContour.Write[FS.StreamOpen[r, $create], FromControl[p.contour]];
};
FromControl:
PROC [control: Control, t:
REAL ¬ 0.0]
RETURNS [contour: Contour] ~ {
ips: IntegerPairSequence ¬ Controls.GetContour[control];
IF ips # NIL THEN contour ¬ G2dContour.FromIntegerPairs[ips, TRUE, t];
};
ToControl:
PROC [control: Control, contour: Contour, repaint:
BOOL ¬
TRUE] ~ {
IF contour =
NIL
THEN Controls.Reset[control]
ELSE Controls.SetContour[
control, G2dContour.IntegerPairsFromPairs[contour.pairs], TRUE, repaint];
};
Contour Similarity Command
SimilarData: TYPE ~ REF SimilarDataRec;
SimilarDataRec: TYPE ~ RECORD [
outerData: Controls.OuterData ← NIL, -- some good stuff here
alpha: Control ← NIL, -- interpolation value
box0: Control ← NIL, -- control for contour0
box1: Control ← NIL, -- control for contour1
boxI0: Control ← NIL, -- control for misc. display
boxI1: Control ← NIL, -- control for misc. display
contour: Contour ← NIL, -- interpolated contour
contour0: Contour ← NIL, -- contour0
contour1: Contour ← NIL, -- contour1
save0: Contour ← NIL, -- to undo contour0
save1: Contour ← NIL -- to undo contour1
];
ContoursSimilar: Commander.CommandProc ~ {
NewControl: PROC [name: ROPE, row: NAT, proc: Controls.ControlProc, flavor: ATOM]
RETURNS [Control] ~ {
RETURN[Controls.NewControl[
name: name, type: contour, w: 150, proc: proc, clientData: p, row: row, flavor: flavor]];
};
p: SimilarData ← NEW[SimilarDataRec];
p.alpha ← Controls.NewControl["Alpha", vSlider, p, 0.0, 1.0, 0.5, Alpha, , , , , , , 120];
p.boxI0 ← NewControl["Interp Vs. Contour0", 0, NIL, $Similar];
p.boxI1 ← NewControl["Interp Vs. Contour1", 0, NIL, $Similar];
p.box0 ← NewControl["Contour0", 1, Contour0Changed, $Nil];
p.box1 ← NewControl["Contour1", 1, Contour1Changed, $Nil];
p.outerData ← Controls.OuterViewer[
name: "Contours Similar",
controls: LIST[p.boxI0, p.boxI1, p.box0, p.box1, p.alpha],
buttons: LIST[
Controls.ClickButton["Undo", SimilarUndo],
Controls.ClickButton["Circles", SimilarCircles],
Controls.ClickButton["Smooth", SimilarSmooth],
Controls.ClickButton["Thin", SimilarThin]],
clientData: p];
};
PrepContour: PROC [contour: Contour] RETURNS [Contour] ~ {
IF contour = NIL THEN RETURN[NIL];
contour ← G2dContour.Orient[contour];
contour.normals ← G2dContour.Normals[contour];
contour.percents ← G2dContour.Percents[contour];
RETURN[contour];
};
Contour0Changed: Controls.ControlProc ~ {
p: SimilarData ← NARROW[control.clientData];
p.contour0 ← PrepContour[FromControl[p.box0]];
Similar[p];
};
Contour1Changed: Controls.ControlProc ~ {
p: SimilarData ← NARROW[control.clientData];
p.contour1 ← PrepContour[FromControl[p.box1]];
Similar[p];
};
Alpha: Controls.ControlProc ~ {
IF control.mouse.state # up THEN Similar[NARROW[control.clientData, SimilarData]];
};
Similar: PROC [p: SimilarData] ~ {
IF G2dContour.ContourOK[p.contour0] AND G2dContour.ContourOK[p.contour1] THEN {
p.contour ← G2dContour.Interpolate[p.contour0, p.contour1, p.alpha.value];
p.contour.normals ← G2dContour.Normals[p.contour];
}
ELSE p.contour ← NIL;
ViewerOps.PaintViewer[p.boxI0.viewer, client, FALSE, NIL];
ViewerOps.PaintViewer[p.boxI1.viewer, client, FALSE, NIL];
};
PaintSimilar: ViewerClasses.PaintProc ~ {
ShowNormalsRange: PROC [c, cc: Contour] ~ {
Label: PROC [y: NAT, rope: ROPE] ~ {Draw2d.Label[context, [5, y], rope]};
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[wScale*(p.x+1), hScale*(p.y+1)]]};
DrawNormal: PROC [base, normal: Pair, scale: REAL] ~ {
Draw2d.Arrow[context, base, Vector2.Add[base, Vector2.Mul[normal, scale]]];
};
IF G2dContour.ContourOK[c] AND G2dContour.ContourOK[cc] THEN {
min: REAL ← 10000.0;
p0: Pair ← Xform[c.pairs[c.pairs.length-1]];
FOR i: NAT IN [0..c.pairs.length) DO
p1: Pair ← Xform[c.pairs[i]];
n: Pair ← c.normals[i];
nn: Pair ← G2dContour.PercentNormal[cc, c.percents[i]];
min ← MIN[min, Vector2.Dot[n, nn]];
DrawNormal[p1, n, 20.0];
DrawNormal[p1, nn, 30.0];
Draw2d.Line[context, p0, p1, dotted];
p0 ← p1;
ENDLOOP;
Label[135, IO.PutFR["min = %6.3f", IO.real[min]]];
Label[125, IO.PutFR["%g vs %g points", IO.int[c.pairs.length], IO.int[cc.pairs.length]]];
};
};
p: SimilarData ← NARROW[NARROW[self.data, Control].clientData];
bounds: Imager.Rectangle ← ImagerBackdoor.GetBounds[context];
wScale: REAL ← 0.5*(bounds.w-1.0);
hScale: REAL ← 0.5*(bounds.h-1.0);
Draw2d.Clear[context];
SELECT self FROM
p.boxI0.viewer => ShowNormalsRange[p.contour, p.contour0];
p.boxI1.viewer => ShowNormalsRange[p.contour, p.contour1];
ENDCASE;
};
ActionProc: TYPE ~ PROC [contour: Contour] RETURNS [Contour];
DoToContours: PROC [p: SimilarData, action: ActionProc] ~ {
p.save0 ← G2dContour.Copy[p.contour0];
p.save1 ← G2dContour.Copy[p.contour1];
p.contour0 ← PrepContour[action[p.contour0]];
p.contour1 ← PrepContour[action[p.contour1]];
ToControl[p.box0, p.contour0];
ToControl[p.box1, p.contour1];
Similar[p];
};
SimilarUndo: Controls.ClickProc ~ {
p: SimilarData ← NARROW[NARROW[clientData, Controls.OuterData].clientData];
ToControl[p.box0, p.contour0 ← p.save0];
ToControl[p.box1, p.contour1 ← p.save1];
Similar[p];
};
SimilarClear: Controls.ClickProc ~ {
Action: ActionProc ~ {RETURN[NIL]};
DoToContours[NARROW[clientData], Action];
};
SimilarCircles: Controls.ClickProc ~ {
action: ActionProc ~ {
return: Contour ← G2dContour.Scale[G2dContour.Circle[4], [0.5, 0.5]];
return.circle ← FALSE;
RETURN[return];
};
DoToContours[NARROW[NARROW[clientData, Controls.OuterData].clientData], action];
};
SimilarThin: Controls.ClickProc ~ {
Action: ActionProc ~ {RETURN[G2dContour.Thin[contour]]};
DoToContours[NARROW[clientData], Action];
};
SimilarSmooth: Controls.ClickProc ~ {
Action: ActionProc ~ {RETURN[G2dContour.Smooth[contour]]};
DoToContours[NARROW[clientData], Action];
};
testUsage: ROPE ¬ "\nDraw a contour.";
interpolateUsage: ROPE ¬ "\nDraw two contours and interpolate between them.";
ViewerOps.RegisterViewerClass[$Normals,
NEW[ViewerClasses.ViewerClassRec ¬ [
notify: Controls.NotifyControl,
paint: PaintNormals,
tipTable: TIPUser.InstantiateNewTIPTable["../Controls/Controls.tip"]]]];
ViewerOps.RegisterViewerClass[$Similar, NEW[ViewerClasses.ViewerClassRec ← [
notify: Controls.NotifyControl,
paint: PaintSimilar,
tipTable: TIPUser.InstantiateNewTIPTable["Controls.tip"]]]];
G2dTool.Register["ContoursTest", ContoursTest, testUsage];
G2dTool.Register["ContoursInterpolate", ContoursInterpolate, interpolateUsage];
G2dTool.Register["ContoursSimilar", ContoursSimilar, "Test Similar[]\n"];
..
Compose Comand
SplineData: TYPE ~ SplineStructure.SplineData;
ComposeData: TYPE ~ REF ComposeDataRec;
ComposeDataRec: TYPE ~ RECORD [
out: IO.STREAM ← NIL,
outer, graphics: Controls.Viewer ← NIL,
entries: LIST OF Controls.Entry ← NIL,
cam: Controls3d.Camera, -- camera
c: Coeffs ← NIL,
composed: BOOL ← FALSE,
graphicsData: Controls.GraphicsData ← NIL,
view: Matrix3d.Matrix ← NIL
];
ContoursCompose: Commander.CommandProc ~ {
d: ComposeData ← NEW[ComposeDataRec];
d.out ← cmd.err;
d.view ← NEW[Matrix3d.MatrixRep];
d.cam ← Controls3d.InitCamera[scale: 3.0, proc: Controller, data: d];
d.c ← Spline3d.CoeffsFromHermite[[[-0.5,0.,0.], [1.,1.,0.], [0.5,0.,0.], [1.,-1.,0.]]];
Controls.ControlRow[d.cam.pan, 2];
d.outer ← Controls.OuterViewer[
name: "Cotour Composer",
column: left,
entries: LIST[["Compose", Compose], ["Reset", Reset]],
controls: LIST[
d.cam.xRot, d.cam.yRot, d.cam.zRot, d.cam.scale, d.cam.x, d.cam.y, d.cam.z, d.cam.fov],
graphicsHeight: 300,
graphicsShow: Display,
data: d];
d.entries ← NARROW[d.outer.data, Controls.OuterData].entries;
d.graphics ← NARROW[d.outer.data, Controls.OuterData].graphics;
d.graphicsData ← NARROW[d.graphics.data];
};
Controller: PUBLIC Controls.ControlProc ~ {
d: ComposeData ← NARROW[control.data];
SELECT control FROM
d.cam.pan, d.cam.tilt, d.cam.roll, d.cam.zoom, d.cam.dx, d.cam.dy, d.cam.dz, d.cam.fov => Controls3d.SetCamera[d.cam];
ENDCASE => NULL;
ViewerOps.PaintViewer[control.graphics, client, FALSE, control];
};
Reset: Controls.ClickProc ~ {
d: ComposeData ← NARROW[NARROW[clientData, Controls.OuterData].data];
d.c ← Spline3d.CoeffsFromHermite[[[-0.5,0.,0.], [1.,1.,0.], [0.5,0.,0.], [1.,-1.,0.]]];
ViewerOps.PaintViewer[d.graphics, client, FALSE, d];
};
Compose: Controls.ClickProc ~ {
d: ComposeData ← NARROW[NARROW[clientData, Controls.OuterData].data];
d.composed ← TRUE;
ViewerOps.PaintViewer[d.graphics, client, FALSE, d];
d.composed ← FALSE;
};
Display: Controls.GraphicsShow ~ {
d: ComposeData ← NARROW[data];
Action: PROC ~ {
d.view ← Controls3d.InitPix[context, w, h];
IF NOT d.composed
THEN FOR s: SplineData ← SplineStructure.contour, s.next WHILE s # NIL DO
Draw3d.DrawBezierPolygon[Spline3d.BezierFromCoeffs[s.c], context, d.view, dot];
Draw3d.DrawCurve[s.c, context, d.view];
ENDLOOP
ELSE Draw3d.DrawCurve[d.c, context, d.view];
};
Draw2d.DoWithBuffer[context, Action, w, h]
};
Random Contours
gaussArray: Spline3d.RealSequence ← InitGaussSequence[1024];
Triple: TYPE ~ Vector3d.Triple;
Coeffs: TYPE ~ Spline3d.Coeffs;
Bezier: TYPE ~ Spline3d.Bezier;
Box: TYPE ~ RECORD [a, b, c, d: Triple];
BoxSequence: TYPE ~ REF BoxSequenceRec;
BoxSequenceRec: TYPE ~ RECORD [element: SEQUENCE maxLength: NAT OF Box];
SplineData: TYPE ~ SplineStructure.SplineData;
SplineDataRec: TYPE ~ SplineStructure.SplineDataRec;
ProgramData: TYPE ~ REF ProgramDataRec;
ProgramDataRec: TYPE ~ RECORD [
out: IO.STREAM ← NIL,
outer, graphics: Controls.Viewer ← NIL,
entries: LIST OF Controls.Entry ← NIL,
mouse: SplineStructure.Mouse ← [0, 0, none, left],
cam: Controls3d.Camera, -- camera
hold: Controls3d.Hold, -- handle on a point and its tangent
nSpl: Controls.ControlData ← NIL, -- number of new splines from old one
nRec: Controls.ControlData ← NIL, -- number of recursive levels
graphicsData: Controls.GraphicsData ← NIL,
view: Matrix3d.Matrix ← NIL,
recurse: BOOL ← FALSE,
noPick: BOOL ← TRUE,
s: SplineData ← NIL, -- all the contour splines
pd: SplineStructure.PickData ← NIL
];
Contour: Commander.CommandProc ~ {
d: ProgramData ← NEW[ProgramDataRec];
d.out ← cmd.err;
d.pd ← NEW[SplineStructure.PickDataRec];
d.view ← NEW[Matrix3d.MatrixRep];
d.s ← NEW[SplineDataRec];
d.cam ← Controls3d.InitCamera[dz: 0.5, zoom: 3., fov: 0., proc: Controller, data: d];
d.hold ← Controls3d.InitHold[Controller, d];
d.pd.s ← SplineStructure.contour ← d.s;
d.s.c ← Spline3d.CoeffsFromHermite[[[-0.5, 0., 0.], [0.5, 0., 0.], [1., 1., 0.], [1., -1., 0.]]];
d.nSpl ← Controls.NewControl["nSpl", vert, , 2.0, 15.0, 4.0, TRUE, , , Controller, d];
d.nRec ← Controls.NewControl["nRec", vert, , 1.0, 15.0, 1.0, TRUE, , , Controller, d];
SplineStructure.PointPick[d.pd];
Controls3d.FocusHold[d.pd.tan, d.hold];
Controls.ControlRow[d.cam.pan, 2];
d.outer ← Controls.OuterViewer[
name: "Contour",
column: left,
entries: LIST[
["Recurse", Recurse], ["Randomize", Randomize],
["PickToggle", PickToggle], ["Reset", Reset], ["PrintC", PrintC]],
controls: LIST[
d.hold.x, d.hold.y, d.hold.z, d.hold.lng, d.hold.lat, d.hold.mag, d.nSpl, d.nRec,
d.cam.xRot, d.cam.yRot, d.cam.zRot, d.cam.scale, d.cam.x, d.cam.y, d.cam.z, d.cam.fov],
graphicsHeight: 300,
graphicsProc: ScreenPick,
graphicsShow: Display,
data: d];
d.entries ← NARROW[d.outer.data, Controls.OuterData].entries;
d.graphics ← NARROW[d.outer.data, Controls.OuterData].graphics;
d.graphicsData ← NARROW[d.graphics.data];
};
Controller: PUBLIC Controls.ControlProc ~ {
d: ProgramData ← NARROW[control.data];
IF d.pd.dividePending THEN {
SplineStructure.DivideSpline[d.pd];
Controls.ControlVal[d.hold.mag, Vector3d.Mag[d.pd.sPt0.v1]];
};
SELECT control FROM
d.hold.x, d.hold.y, d.hold.z =>
SplineStructure.ChangePosition[d.s, d.pd, [d.hold.x.val, d.hold.y.val, d.hold.z.val]];
d.hold.lng, d.hold.lat, d.hold.mag =>
IF NOT d.noPick THEN SplineStructure.ChangeTangent[
d.s, d.pd, Vector3d.CartesianFromPolar[[d.hold.lng.val, d.hold.lat.val, d.hold.mag.val]]];
d.cam.xRot, d.cam.yRot, d.cam.zRot, d.cam.scale, d.cam.x, d.cam.y, d.cam.z, d.cam.fov => Controls3d.UpDateCamera[d.cam];
ENDCASE => NULL;
ViewerOps.PaintViewer[control.graphics, client, FALSE, control];
};
ScreenPick: Controls.GraphicsProc ~ {
d: ProgramData ← NARROW[graphics.data];
IF graphics.mouse.state = up THEN RETURN;
IF graphics.mouse.button = left THEN {
SplineStructure.ScreenPick[d.s, graphics.mouse, d.view, d.pd];
Controls3d.FocusHold[d.pd.tan, d.hold];
};
};
PickToggle: Controls.ClickProc ~ {
d: ProgramData ← NARROW[NARROW[clientData, Controls.OuterData].data];
d.noPick ← NOT d.noPick;
ViewerOps.PaintViewer[d.graphics, client, FALSE, d];
};
PrintC: Controls.ClickProc ~ {
d: ProgramData ← NARROW[NARROW[clientData, Controls.OuterData].data];
FOR s: SplineData ← d.s, s.next WHILE s # NIL DO Print3d.Matrix[d.out, s.c]; ENDLOOP;
};
Reset: Controls.ClickProc ~ {
d: ProgramData ← NARROW[NARROW[clientData, Controls.OuterData].data];
SplineStructure.contour ← d.s ← NEW[SplineDataRec];
(d.pd.s ← d.s).c ← Spline3d.CoeffsFromHermite[[[-0.5,0.,0.], [0.5,0.,0.], [1.,1.,0.], [1.,-1.,0.]]];
ViewerOps.PaintViewer[d.graphics, client, FALSE, d];
};
Recurse: Controls.ClickProc ~ {
d: ProgramData ← NARROW[NARROW[clientData, Controls.OuterData].data];
d.recurse ← TRUE;
ViewerOps.PaintViewer[d.graphics, client, FALSE, d];
d.recurse ← FALSE;
};
Randomize: Controls.ClickProc ~ {
d: ProgramData ← NARROW[NARROW[clientData, Controls.OuterData].data];
s: SplineData ← d.s;
WHILE s # NIL DO
r: SplineData ← RandomizeSpline[s.c, Real.RoundI[d.nSpl.val]];
r.prev ← s.prev;
IF s.prev # NIL THEN s.prev.next ← r ELSE SplineStructure.contour ← d.s ← r;
WHILE r.next # NIL DO r ← r.next; ENDLOOP;
IF s.next # NIL THEN s.next.prev ← r;
s ← r.next ← s.next;
ENDLOOP;
ViewerOps.PaintViewer[d.graphics, client, FALSE, d];
};
InsertSpline: PROC [insert, splines: SplineData] RETURNS [SplineData] ~ {
Insert new SplineData (insert) at end of splines:
s: SplineData ← splines;
IF splines = NIL THEN RETURN[insert];
WHILE s.next # NIL DO s ← s.next; ENDLOOP;
insert.prev ← s;
insert.next ← s.next;
s.next ← insert;
IF insert.next # NIL THEN insert.next.prev ← insert;
RETURN[splines];
};
RandomizeSpline: PROC [c: Coeffs, nSplines: INTEGER ← 2] RETURNS [SplineData] ~ {
splines: SplineData ← NIL;
coeffs: Spline3d.CoeffsSequence;
bez: Bezier ← Spline3d.BezierFromCoeffs[c];
boxes: BoxSequence ← BoxesFromBezier[bez, nSplines];
knots: Spline3d.KnotSequence ← NEW[Spline3d.KnotSequenceRep[nSplines+1]];
scale: REAL ← 3.0/REAL[MAX[1, nSplines]];
tan0: Triple ← Vector3d.Mul[Vector3d.Sub[bez.b1, bez.b0], scale];
tan1: Triple ← Vector3d.Mul[Vector3d.Sub[bez.b3, bez.b2], scale];
IF nSplines = 0 THEN RETURN[splines];
knots[0] ← bez.b0;
FOR n: NAT IN [1..nSplines) DO knots[n] ← RandomPtInBox[boxes[n-1]]; ENDLOOP;
knots[nSplines] ← bez.b3;
knots.length ← nSplines+1;
coeffs ← Spline3d.Interpolate[knots, tan0, tan1];
FOR n: NAT IN [0..nSplines) DO
insert: SplineData ← NEW[SplineDataRec];
insert.c ← coeffs[n];
splines ← InsertSpline[insert, splines];
ENDLOOP;
RETURN[splines];
};
BoxesFromBezier: PROC [bez: Bezier, nBoxes: NAT] RETURNS [BoxSequence] ~ {
boxes: BoxSequence ← NEW[BoxSequenceRec[nBoxes]];
p0: Triple ← bez.b0;
p1: Triple ← bez.b1;
dp0: Triple ← Vector3d.Div[Vector3d.Sub[bez.b3, bez.b0], REAL[nBoxes]];
dp1: Triple ← Vector3d.Div[Vector3d.Sub[bez.b2, bez.b1], REAL[nBoxes]];
FOR n: NAT IN [0..nBoxes) DO
pp0: Triple ← Vector3d.Add[p0, dp0];
pp1: Triple ← Vector3d.Add[p1, dp1];
boxes[n] ← [p0, p1, pp1, pp0];
p0 ← pp0;
p1 ← pp1;
ENDLOOP;
RETURN[boxes];
};
RanR: PROC RETURNS [REAL] ~ {RETURN[gaussArray[Random.ChooseInt[min: 0, max: 1023]]]};
RandomPtInSegment: PROC [p0, p1: Triple] RETURNS[Triple] ~ {
w: REAL ← RanR[];
RETURN[Vector3d.Add[Vector3d.Mul[p0, w], Vector3d.Mul[p1, 1.0-w]]];
RandomPtInBox: PROC [box: Box] RETURNS[t: Triple] ~ {
w0: REAL ← RanR[];
w1: REAL ← RanR[];
w2: REAL ← RanR[];
w3: REAL ← RanR[];
n: REAL ← 1.0/(w0+w1+w2+w3);
w0 ← w0*n; w1 ← w1*n; w2 ← w2*n; w3 ← w3*n;
t.x ← box.a.x*w0+box.b.x*w1+box.c.x*w2+box.d.x*w3;
t.y ← box.a.y*w0+box.b.y*w1+box.c.y*w2+box.d.y*w3;
t.z ← box.a.z*w0+box.b.z*w1+box.c.z*w2+box.d.z*w3;
Function: PROC [x: REAL] RETURNS [REAL] ~ {
Input and output range [0..1].
x ← 2.0*3.1415926535*(x-0.5);
RETURN[0.5*(1.0+RealFns.Sin[x]+RealFns.Cos[x])];
};
Display: Controls.GraphicsShow ~ {
d: ProgramData ← NARROW[data];
Action: PROC ~ {
d.view ← Controls3d.InitPix[context, w, h];
IF d.recurse THEN {
p, q: Triple ← [0.0, 0.0, 0.0];
FOR i: NAT IN [0..100) DO
q.x ← REAL[i-50]/100.0;
q.y ← 0.5+q.x;
FOR n: NAT IN[1..Real.RoundI[d.nRec.val]] DO
q.y ← Function[q.y];
ENDLOOP;
q.y ← 0.5*q.y;
IF i > 0 THEN Draw3d.PP[p, q, context, d.view];
p ← q;
ENDLOOP;
}
ELSE FOR s: SplineData ← d.s, s.next WHILE s # NIL DO
Draw3d.DrawBezierPolygon[Spline3d.BezierFromCoeffs[s.c], context, d.view, dot];
Draw3d.DrawCurve[s.c, context, d.view];
ENDLOOP;
IF NOT d.noPick THEN Draw3d.PV[d.pd.pos, d.pd.tan,, context, d.view, IF d.pd.t IN(0.0..1.0) THEN cross ELSE none];
};
Draw2d.DoWithBuffer[context, Action, w, h]
};
InitGaussSequence: PROC [nEntries: NAT] RETURNS [Spline3d.RealSequence] ~ {
x: REAL ← 0.0;
sigma: REAL ~ 0.16;
dx: REAL ← 1.0/REAL[nEntries];
ret: Spline3d.RealSequence ← NEW[Spline3d.RealSequenceRec[nEntries]];
FOR i: NAT IN [0..1024) DO
xx: REAL ← x-0.5;
ret[i] ← RealFns.Exp[-xx*xx/(2.0*sigma*sigma)];
x ← x+dx;
ENDLOOP;
RETURN[ret];
};
Commander.Register["Contour", Contour, "\nTest some contour ideas."];
RandomizeSpline: PROC [c: Coeffs, nSplines: INTEGER ← 2] RETURNS [SplineData] ~ {
splines: SplineData ← NIL;
nBoxes: NAT ← 2*nSplines-2;
bez: Bezier ← Spline3d.BezierFromCoeffs[c];
boxes: BoxSequence ← BoxesFromBezier[bez, nBoxes];
pts: TripleSequence ← NEW[TripleSequenceRec[nBoxes+2]];
ends: TripleSequence ← NEW[TripleSequenceRec[nSplines+1]];
pts[0] ← RandomPtInSegment[bez.b0, bez.b1];
pts[nBoxes+1] ← RandomPtInSegment[bez.b2, bez.b3];
FOR n: NAT IN [0..nBoxes) DO pts[n+1] ← RandomPtInBox[boxes[n]]; ENDLOOP;
ends[0] ← bez.b0;
ends[nSplines] ← bez.b3;
FOR n: NAT IN [1..nSplines) DO
ends[n] ← Ave[pts[n+n-1], pts[n+n]];
ENDLOOP;
FOR n: NAT IN [0..nSplines) DO
insert: SplineData ← NEW[SplineDataRec];
insert.c ← Spline3d.CoeffsFromBezier[[ends[n], pts[n+n], pts[n+n+1], ends[n+1]]];
splines ← InsertSpline[insert, splines];
ENDLOOP;
RETURN[splines];
};
MinMaxOfCurve: PROC [c: Coeffs, i: NAT] RETURNS [min, max: REAL] ~ {
CheckI: PROC [t: REAL] ~ {
t2: REAL ← t*t;
y: REAL ← t2*t*c[0][i]+t2*c[1][i]+t*c[2][i]+c[3][i];
min ← MIN[min, y];
max ← MAX[max, y];
};
roots: Quadratic.Roots ← Quadratic.RealRoots[3.0*c[0][i], 2.0*c[1][i], c[2][i]];
min ← max ← c[3][i];
CheckI[1.0];
IF roots.nRoots > 0 AND roots.r1 IN [0.0..1.0] THEN CheckI[roots.r1];
IF roots.nRoots > 1 AND roots.r2 IN [0.0..1.0] THEN CheckI[roots.r2];
};
Ave: PROC [a, b: Triple] RETURNS [Triple] ~ {
RETURN[[0.5*(a.x+b.x), 0.5*(a.y+b.y), 0.5*(a.z+b.z)]];
};