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: BOOL ← FALSE,
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: BOOL ← FALSE,
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: BOOL ← FALSE,
view: Matrix ← NIL,
frameProc: FrameProc ← NIL]
~ {
bez: Bezier ← Spline3d.BezierFromCoeffs[tube.coeffs];
maxLen: REAL ← IF 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]];
};
LengthenFrameSequence:
PROC [frames: FrameSequence]
RETURNS [FrameSequence] ~ {
nFrames: INTEGER ← IF 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];
};
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;
};
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]];
};