TubeFrameImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, September 30, 1986 12:39:39 pm PDT
DIRECTORY Contours, Cubic2, Matrix3d, Real, RealFns, Spline3d, TubeContour, Vector3d, TubeDefs, TubeFrame, TubeMisc;
TubeFrameImpl: CEDAR PROGRAM
IMPORTS Contours, Cubic2, Matrix3d, Real, RealFns, Spline3d, TubeContour, TubeMisc, Vector3d
EXPORTS TubeFrame
~ BEGIN
OPEN TubeDefs;
ReScale: PUBLIC PROC [tube: Tube, scale: REAL ← 1.0, taper: REAL ← 0.0] ~ {
DoScale: PROC [tube: Tube, scale0, scale1: REAL] ~ {
sc0: REAL ← tube.r0*scale0;
sc1: REAL ← tube.r1*scale1;
FOR n: NAT IN [0..NFrames[tube]) DO
f: Frame ← tube.frames[n];
f.scale ← MAX[0.000001, sc0+f.t*(sc1-sc0)];
f.matrix ← RefMatrix[f.position, f.triad, f.scale, f.twist, f.matrix];
ENDLOOP;
};
InnerReScale: PROC [tube: Tube, length, scale0: REAL] ~ {
IF tube # NIL THEN {
scale1: REAL ← scale0;
IF taper # 0.0 THEN {
length ← length+Spline3d.Length[tube.coeffs];
scale1 ← scale*(1.0-taper*length);
};
DoScale[tube, scale0, scale1];
InnerReScale[tube.next, length, scale1];
FOR n: NAT IN [0..TubeMisc.NBranches[tube]) DO
InnerReScale[tube.branches[n], length, scale1];
ENDLOOP;
};
};
InnerReScale[tube, 0.0, scale];
};
Make: PUBLIC PROC [
tube: Tube,
epsilon: REAL ← 0.03,
scale: REAL ← 1.0,
taper: REAL ← 0.0,
skin: BOOLFALSE,
view: Matrix ← NIL,
frameProc: FrameProc ← NIL]
~ {
InnerMake: TubeProc ~ {
tube.coeffs ← Spline3d.CoeffsFromHermite[[tube.p0, tube.p1, tube.v0, tube.v1]];
};
TubeMisc.ApplyToTube[tube, InnerMake];
MakeFrames[tube, epsilon, scale, taper, skin, view, frameProc];
};
MakeFrames: PUBLIC PROC [
tube: Tube,
epsilon: REAL ← 0.03,
scale: REAL ← 1.0,
taper: REAL ← 0.0,
skin: BOOLFALSE,
view: Matrix ← NIL,
frameProc: FrameProc ← NIL]
~ {
InnerMakeFrames: PROC [tube: Tube, length, scale0: REAL] ~ {
IF tube # NIL THEN {
scale1: REAL ← scale0;
IF taper # 0.0 THEN {
length ← length+Spline3d.Length[tube.coeffs];
scale1 ← scale*(1.0-taper*length);
};
MakeSectionFrames[tube, epsilon, scale0, scale1, skin, view, frameProc];
InnerMakeFrames[tube.next, length, scale1];
FOR n: NAT IN [0..TubeMisc.NBranches[tube]) DO
InnerMakeFrames[tube.branches[n], length, scale1];
ENDLOOP;
};
};
InnerMakeFrames[tube, 0.0, scale];
};
MakeSectionFrames: PUBLIC PROC [
tube: Tube,
epsilon: REAL ← 0.03,
scale0, scale1: REAL ← 1.0,
skin: BOOLFALSE,
view: Matrix ← NIL,
frameProc: FrameProc ← NIL]
~ {
bez: Bezier ← Spline3d.BezierFromCoeffs[tube.coeffs];
maxLen: REALIF view = NIL THEN 0.01*Spline3d.ConvexHullLength[bez] ELSE 0.0;
vv: Triple ← IF Vector3d.Equal[bez.b1, bez.b0, 0.0001]
THEN Vector3d.Sub[bez.b2, bez.b1]
ELSE Vector3d.Sub[bez.b1, bez.b0];
sc0: REAL ← tube.r0*scale0;
sc1: REAL ← tube.r1*scale1;
circle: Contour ← Contours.Circle[tube.circleRes];
c0: Contour ← TubeContour.TContour[tube.contours, circle, 0.0];
c1: Contour ← TubeContour.TContour[tube.contours, circle, 1.0];
MakeFrame: PROC [position, velocity: Triple, t: REAL, c: Contour] ~ {
tube.frames ← TestFrameSequence[tube.frames];
IF frameProc # NIL
THEN tube.frames[tube.frames.length] ← frameProc[position, velocity, t]
ELSE {
f: Frame ← tube.frames[tube.frames.length];
f.t ← t;
f.position ← position;
f.triad ← Basis[velocity, vv, tube.refVec];
Keep scale non-zero (avoid subsequent surface normal computation problems):
f.scale ← MAX[0.000001, sc0+t*(sc1-sc0)];
f.twist ← tube.tw0+t*(tube.tw1-tube.tw0);
f.matrix ← RefMatrix[f.position, f.triad, f.scale, f.twist, f.matrix];
IF skin THEN {
f.contour ← c;
f.normals ← SELECT TRUE FROM
c.circle => c.pairs,
c.normals # NIL => c.normals,
ENDCASE => Contours.Normals[c];
};
};
tube.frames.length ← tube.frames.length+1;
vv ← velocity;
tube.refVec ← tube.frames[tube.frames.length-1].triad.n;
};
Walker: PROC [bez: Bezier, t0, t1: REAL, c0, c1: Contour] ~ {
IF DividedEnough[bez, t0, t1, c0, c1]
THEN MakeFrame[
bez.b0,
IF Vector3d.Equal[bez.b1, bez.b0, 0.0001]  -- degenerate end tangent check
THEN Vector3d.Sub[bez.b2, bez.b1]
ELSE Vector3d.Sub[bez.b1, bez.b0],
t0,
c0]
ELSE {
left, rite: Bezier;
t: REAL ← 0.5*(t0+t1);
c: Contour ← IF skin THEN TubeContour.TContour[tube.contours, circle, t] ELSE NIL;
[left, rite] ← Spline3d.SplitBezier[bez];
Walker[left, t0, t, c0, c];
Walker[rite, t, t1, c, c1];
};
};
DividedEnough: PROC [bez: Bezier, t0, t1: REAL, c0, c1: Contour] RETURNS [BOOL] ~ {
BezSmallEnough: PROC [bez: Bezier] RETURNS[BOOL] ~ {
RETURN[IF view = NIL
THEN Spline3d.ConvexHullLength[bez] < maxLen OR Spline3d.FlatBezier[bez, epsilon]
ELSE Cubic2.Flat[
[Matrix3d.TransformD[bez.b0, view], Matrix3d.TransformD[bez.b1, view],
Matrix3d.TransformD[bez.b2, view], Matrix3d.TransformD[bez.b3, view]],
100.0*epsilon]];
};
TwistSmallEnough: PROC [t0, t1: REAL] RETURNS[BOOL] ~ {
RETURN[ABS[(t1-t0)*(tube.tw1-tube.tw0)] < 20.0];
};
ContoursCloseEnough: PROC [c0, c1: Contour] RETURNS [BOOL] ~ {
AllCircles: PROC [contours: ContourSequence] RETURNS [BOOL] ~ {
FOR n: NAT IN [0..contours.length) DO
IF NOT contours[n].circle THEN RETURN [FALSE];
ENDLOOP;
RETURN[TRUE];
};
IF tube.contours = NIL OR AllCircles[tube.contours]
THEN RETURN[TRUE]
ELSE {
similar: REAL ← Contours.Similar[c0, c1];
RETURN[similar > 1.0-10.0*epsilon];
};
};
IF t1-t0 < 0.01 THEN RETURN[TRUE];
RETURN[
BezSmallEnough[bez] AND
TwistSmallEnough[t0, t1] AND
(IF NOT skin THEN TRUE ELSE ContoursCloseEnough[c0, c1])
];
};
IF tube # NIL AND tube.frames # NIL THEN tube.frames.length ← 0;
tube.refVec ← IF tube.prev # NIL
THEN tube.prev.refVec
ELSE Spline3d.RefVec[tube.coeffs, 0.0];
Walker[bez, 0.0, 1.0, c0, c1];
MakeFrame[
bez.b3,
IF Vector3d.Equal[bez.b3, bez.b2, 0.0001] -- test for degenerate end tangent
THEN Vector3d.Sub[bez.b2, bez.b1]
ELSE Vector3d.Sub[bez.b3, bez.b2],
1.0,
c1];
IF view # NIL THEN tube.circleRes ←
Real.RoundI[TubeContour.ScreenCircleRes[tube, view]/MAX[0.01, 100*epsilon]];
};
NFrames: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ {
RETURN[IF tube.frames # NIL THEN tube.frames.length ELSE 0];
};
TestFrameSequence: PROC [frames: FrameSequence] RETURNS [FrameSequence] ~ {
IF frames = NIL OR frames.length >= frames.maxLength
THEN frames ← LengthenFrameSequence[frames];
RETURN[frames];
};
LengthenFrameSequence: PROC [frames: FrameSequence] RETURNS [FrameSequence] ~ {
nFrames: INTEGERIF frames = NIL THEN 0 ELSE frames.length;
temp: FrameSequence ← NEW[FrameSequenceRec[MAX[5, Real.RoundI[1.3*nFrames]]]];
FOR i: NAT IN[0..nFrames) DO temp[i] ← frames[i]; ENDLOOP;
FOR i: NAT IN[nFrames..temp.maxLength) DO temp[i] ← NEW[FrameRec]; ENDLOOP;
temp.length ← nFrames;
RETURN[temp];
};
GetFrame: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [Frame] ~ {
nFrames: INTEGER ← NFrames[tube];
frames: FrameSequence ← tube.frames;
SELECT TRUE FROM
nFrames = 0 =>
RETURN[NIL];
t < frames[0].t =>
RETURN[tube.frames[0]];
t > tube.frames[nFrames-1].t =>
RETURN[tube.frames[nFrames-1]];
ENDCASE => {
n: NAT ← 0;
WHILE frames[n].t < t DO tube.frames[n] ← frames[n]; n ← n+1; ENDLOOP;
IF tube.frames[n].t = t
THEN RETURN[tube.frames[n]]
ELSE {
frame: Frame ← NEW[FrameRec];
frame0: Frame ← tube.frames[n-1];
frame1: Frame ← tube.frames[n];
alpha: REAL ← (frame.t-frame0.t)/(frame1.t-frame0.t);
frame.t ← t;
frame.position ← Spline3d.Position[tube.coeffs, t];
frame.triad ← Basis[Spline3d.Velocity[tube.coeffs, t], frame0.triad.v, frame0.triad.n];
frame.scale ← frame0.scale+alpha*(frame1.scale-frame0.scale);
frame.twist ← frame0.twist+alpha*(frame1.twist-frame0.twist);
frame.matrix ← RefMatrix[frame.position, frame.triad, frame.scale, frame.twist];
frame.contour ← Contours.Interpolate[frame0.contour, frame1.contour, alpha];
frame.normals ← Contours.Normals[frame.contour];
RETURN[frame];
};
};
};
PutFrame: PUBLIC PROC [tube: Tube, frame: Frame] ~ {
InsertFrame: PROC [frame: Frame, frames: FrameSequence, n: NAT] ~ {
FOR i: NAT DECREASING IN [n..frames.length) DO frames[n+1] ← frames[n]; ENDLOOP;
frames[n] ← frame;
};
tube.frames ← TestFrameSequence[tube.frames];
SELECT TRUE FROM
tube.frames.length = 0 =>
tube.frames[0] ← frame;
frame.t < tube.frames[0].t =>
InsertFrame[frame, tube.frames, 0];
frame.t > tube.frames[tube.frames.length-1].t =>
tube.frames[tube.frames.length] ← frame;
ENDCASE => {
n: NAT ← 0;
WHILE tube.frames[n].t < frame.t DO n ← n+1; ENDLOOP;
IF tube.frames[n].t = frame.t THEN {
tube.frames[n] ← frame;
RETURN;
};
InsertFrame[frame, tube.frames, n];
};
tube.frames.length ← tube.frames.length+1;
};
CopyFrame: PUBLIC PROC [frame: Frame] RETURNS [Frame] ~ {
copy: Frame ← NIL;
IF frame # NIL THEN {
copy ← NEW[FrameRec];
copy.t ← frame.t;
copy.position ← frame.position;
copy.triad ← frame.triad;
copy.scale ← frame.scale;
copy.matrix ← Matrix3d.CopyMatrix[frame.matrix];
copy.contour ← Contours.Copy[frame.contour];
};
RETURN[copy];
};
CopyFrames: PUBLIC PROC [frames: FrameSequence] RETURNS [FrameSequence] ~ {
copy: FrameSequence ← NIL;
IF frames # NIL THEN {
copy ← NEW[FrameSequenceRec[frames.length]];
copy.length ← frames.length;
FOR n: NAT IN [0..copy.length) DO copy[n] ← CopyFrame[frames[n]]; ENDLOOP;
};
RETURN[copy];
};
Basis: PUBLIC PROC [v, vv, rv: Triple] RETURNS [Triad] ~ {
dot: REAL;
b, n: Triple;
vv ← Vector3d.Normalize[vv];
v ← Vector3d.Normalize[v];
rv ← Vector3d.Normalize[rv];
dot ← Vector3d.Dot[v, vv];
IF ABS[dot] > 0.9999 THEN {        -- tangents v, vv colinear
n ← IF dot > 0.0 THEN rv ELSE [-rv.x, -rv.y, -rv.z];
n ← Vector3d.V90[v, n];       -- ensure orthogonality
}
ELSE {
a: REAL ← RealFns.ArcTanDeg[RealFns.SqRt[1.0-dot*dot], dot]; -- == Acos[]
axis: Triple ← Vector3d.Cross[v, vv];
n ← Vector3d.Normalize[Vector3d.RotateAbout[rv, axis, a]]; -- incremental kludge
};
b ← Vector3d.Normalize[Vector3d.Cross[v, n]];
RETURN[[v, n, b]];
};
RefMatrix: PUBLIC PROC [position: Triple, triad: Triad, scale, twist: REAL, out: Matrix ← NIL]
RETURNS [Matrix] ~ {
IF ABS[twist] > 0.001 THEN {
out ← Matrix3d.MakePureRotate[triad.v, twist, , out];
triad.n ← Matrix3d.TransformVec[triad.n, out];
triad.b ← Matrix3d.TransformVec[triad.b, out];
};
RETURN[
Matrix3d.LocalScale[Matrix3d.MakeFromTriad[triad.n, triad.b, triad.v, position], scale, out]];
};
END.
..
MakeMatrices: PUBLIC PROC [s: SplineData, scale: REAL] ~ {
sc0: REAL ← s.rad0*scale;
sc1: REAL ← s.rad1*scale;
tw0: REAL ← s.twist0;
tw1: REAL ← s.twist1;
vv: Triple ← s.v0;
mats: MatrixSeq ← s.mats;
s.refVec ← IF s.prev # NIL THEN s.prev.refVec ELSE Spline3d.RefVec[s.c, 0.0];
mats[0] ←Misc3d.RefMatrix[s.p0, s.refVec, Vector3d.Cross[vv, s.refVec], vv, sc0, tw0, mats[0]];
FOR i: NAT IN[1..s.axialRes) DO
n, b: Triple;
t: REAL ← i/REAL[s.axialRes-1.0];
p: Triple ← Spline3d.Position[s.c, t];
v: Triple ← Spline3d.Tangent[s.c, t];
[n, b] ← Misc3d.Basis[v, vv, s.refVec];
mats[i] ← Misc3d.RefMatrix[p, n, b, v, (1.0-t)*sc0+t*sc1, (1.0-t)*tw0+t*tw1, mats[i]];
vv ← v;
s.refVec ← n;
ENDLOOP;
};
GetFrame: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [Frame] ~ {
frames: FrameSequence ← tube.frames;
numFrames: INTIF frames # NIL THEN frames.maxLength ELSE 0;
SELECT TRUE FROM
numFrames = 0 =>
RETURN[NIL];
frame.t < frames[0].t =>
RETURN[tube.frames[0]];
frame.t > tube.frames[numFrames-1].t =>
RETURN[tube.frames[numFrames-1];
ENDCASE => {
n: NAT ← 0;
WHILE frames[n].t < frame.t DO tube.frames[n] ← frames[n]; n ← n+1; ENDLOOP;
IF tube.frames[n].t = t
THEN RETURN[tube.frames[n]]
ELSE {    -- the following is sloppy:
frame0: Frame ← tube.frames[n-1];
frame1: Frame ← tube.frames[n];
m0: Matrix ← frame0.m;
m1: Matrix ← frame1.m;
alpha: REAL ← (frame.t-frame0.t)/(frame1.t-frame0.t);
beta: REAL ← 1.0-alpha;
x: Triple ← Vector3d.Combine[
[m0[0][0], m0[0][1], m0[0][2]], alpha, [m1[0][0], m1[0][1], m1[0][2]], beta];
y: Triple ← Vector3d.Combine[
[m0[1][0], m0[1][1], m0[1][2]], alpha, [m1[1][0], m1[1][1], m1[1][2]], beta];
z: Triple ← Vector3d.Combine[
[m0[2][0], m0[2][1], m0[2][2]], alpha, [m1[2][0], m1[2][1], m1[2][2]], beta];
v: Triple ← Vector3d.SameMag[z, Spline3d.Velocity[tube.coeffs, t]];
n: Triple ← Vector3d.SameMag[x, Vector3d.Cross[v, y]];
b: Triple ← Vector3d.SameMag[y, Vector3d.Cross[v, n]];
p: Triple ← Spline3d.Position[tube.coeffs, t];
frame: Frame ← NEW[FrameRec];
frame.t ← t;
frame.c ← Contour.Interpolate[frame0.c, frame1.c, alpha];
frame.m ← RefMatrix[p, n, b, v, 1.0, 0.0];
RETURN[frame];
};
};
};