TubeStructureImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, August 12, 1986 4:31:53 pm PDT
DIRECTORY Cubic2, Matrix3d, Real, RealFns, Spline3d, Vector2, Vector3d, TubeDefs, TubeMisc, TubeStructure;
TubeStructureImpl: CEDAR PROGRAM
IMPORTS Cubic2, Matrix3d, Real, RealFns, Spline3d, TubeMisc, Vector2, Vector3d
EXPORTS TubeStructure
~ BEGIN
OPEN TubeDefs;
nCircles: INTEGER ~ 40;
circles: ARRAY [1..nCircles] OF PairSequence ← ALL[NIL];
MakeNormals: PROC [contour: Contour] ~ {
MakeNormal: PROC [pairs: PairSequence, n: INTEGER] RETURNS [Pair] ~ {
IF pairs.length < 2
THEN RETURN[[0, 0]]
ELSE {
first: INTEGER ← 0;
last: INTEGER ← pairs.length-1;
p0: Pair ← SELECT n FROM
first => IF contour.closed THEN pairs[last] ELSE pairs[first],
ENDCASE => pairs[n-1];
p1: Pair ← SELECT n FROM
last => IF contour.closed THEN pairs[first] ELSE pairs[last],
ENDCASE => pairs[n+1];
v: Pair ← [p1.x-p0.x, p1.y-p0.y];
RETURN[[-v.y, v.x]];
};
};
FOR n: NAT IN [0..contour.pairs.length) DO
contour.normals[n] ← MakeNormal[contour.pairs, n];
ENDLOOP;
};
ReScaleTube: PUBLIC PROC [tube: Tube, scale: REAL] ~ {
ApplyReScale: TubeProc ~ {
FOR n: NAT IN [0..tube.nFrames) DO
f: Frame ← tube.frames[n];
f.m ← Matrix3d.MakeFromTriad[
[f.m[0][0], f.m[0][1], f.m[0][2]],
[f.m[1][0], f.m[1][1], f.m[1][2]],
[f.m[2][0], f.m[2][1], f.m[2][2]],
[f.m[3][0], f.m[3][1], f.m[3][2]], f.m];
f.m ← Matrix3d.LocalScale[f.m, scale*(f.t*tube.r0+(1.0-f.t)*tube.r1), f.m];
ENDLOOP;
};
TubeMisc.ApplyProc[tube, ApplyReScale];
};
MakeTube: PUBLIC PROC [
tube: Tube, scale, epsilon: REAL ← 1.0, taper: REAL ← 0.0, view: Matrix ← NIL] ~ {
ApplyMake: TubeProc ~ {
tube.coeffs ← Spline3d.CoeffsFromHermite[[tube.p0, tube.p1, tube.v0, tube.v1]];
};
TubeMisc.ApplyProc[tube, ApplyMake];
PropagateFrames[tube, scale, epsilon, taper, view];
};
PropagateCircleRes: PUBLIC PROC [tube: Tube, circleRes: INTEGER] ~ {
IF tube = NIL THEN RETURN;
tube.circleRes ← circleRes;
FOR n: NAT IN [0..tube.nBranches) DO
PropagateCircleRes[tube.branches[n], circleRes];
ENDLOOP;
PropagateCircleRes[tube.next, circleRes];
};
PropagateFrames: PUBLIC PROC [
tube: Tube, scale, epsilon: REAL ← 1.0, taper: REAL ← 0.0, view: Matrix ← NIL] ~ {
InnerPropagate: PROC [tube: Tube, length, scale0: REAL] ~ {
scale1: REAL ← scale0;
IF tube = NIL THEN RETURN;
IF taper # 0.0 THEN {
length ← length+Spline3d.Length[tube.coeffs];
scale1 ← scale*(1.0-taper*length);
};
MakeFrames[tube, scale0, scale1, epsilon, view];
InnerPropagate[tube.next, length, scale1];
FOR n: NAT IN [0..tube.nBranches) DO
InnerPropagate[tube.branches[n], length, scale1];
ENDLOOP;
};
InnerPropagate[tube, 0.0, scale];
};
MakeFrames: PUBLIC PROC [
tube: Tube, scale0, scale1, epsilon: REAL ← 1.0, view: Matrix ← NIL]
~ {
bez: Spline3d.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];
tw0: REAL ← tube.tw0;
tw1: REAL ← tube.tw1;
sc0: REAL ← tube.r0*scale0;
sc1: REAL ← tube.r1*scale1;
ScreenCircleRes: PROC [tube: Tube, view: Matrix] RETURNS [res: INTEGER] ~ {
CircleRes: PROC [p: Triple, r: REAL] RETURNS [NAT] ~ {
q: Vector2.VEC ← Matrix3d.TransformD[p, view];
q1: Vector2.VEC ← Matrix3d.TransformD[[p.x+r, p.y, p.z], view];
q2: Vector2.VEC ← Matrix3d.TransformD[[p.x, p.y+r, p.z], view];
l: REALMAX[Vector2.Length[Vector2.Sub[q, q1]], Vector2.Length[Vector2.Sub[q, q2]]];
RETURN[3+Real.RoundI[0.4*l/MAX[0.01, 100.0*epsilon]]];
};
res ← MAX[CircleRes[tube.p0, scale0*tube.r0], CircleRes[tube.p1, scale1*tube.r1]];
IF res MOD 2 = 1 THEN res ← res+1;
};
LengthenFrameSequence: PROC [f: FrameSequence] RETURNS [FrameSequence] ~ {
temp: FrameSequence ← NEW[FrameSequenceRec[MAX[5, Real.RoundI[1.3*f.maxLength]]]];
FOR i: NAT IN[0..f.maxLength) DO temp[i] ← f[i]; ENDLOOP;
RETURN[temp];
};
TwistSmallEnough: PROC [t0, t1: REAL] RETURNS[BOOL] ~ {
RETURN[ABS[(t1-t0)*(tube.tw1-tube.tw0)] < 20.0];
};
BezSmallEnough: PROC [bez: Spline3d.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]];
};
Walker: PROC [bez: Spline3d.Bezier, t0, t1: REAL] ~ {
IF BezSmallEnough[bez] AND TwistSmallEnough[t0, t1]
THEN MakeFrame[
bez.b0,
IF Vector3d.Equal[bez.b1, bez.b0, 0.0001]  -- check for degenerate end tangent
THEN Vector3d.Sub[bez.b2, bez.b1]
ELSE Vector3d.Sub[bez.b1, bez.b0],
t0]
ELSE {
left, rite: Spline3d.Bezier;
t: REAL ← 0.5*(t0+t1);
[left, rite] ← Spline3d.SplitBezier[bez];
Walker[left, t0, t];
Walker[rite, t, t1];
};
};
MakeFrame: PROC [p, v: Triple, t: REAL] ~ {
n, b: Triple;
i: INTEGER ← tube.nFrames;
tt: REAL ← 1.0-t;
IF tube.nFrames >= tube.frames.maxLength
THEN tube.frames ← LengthenFrameSequence[tube.frames];
tube.nFrames ← i+1;
tube.frames[i].t ← t;
[n, b] ← Basis[v, vv, tube.refVec];
Keep scale non-zero (avoid subsequent surface normal computation problems):
tube.frames[i].m ←
RefMatrix[p, n, b, v, MAX[0.000001, tt*sc0+t*sc1], tt*tw0+t*tw1, tube.frames[i].m];
vv ← v;
tube.refVec ← n;
};
IF tube.frames = NIL THEN tube.frames ← NEW[FrameSequenceRec];
tube.nFrames ← 0;
tube.refVec ← IF tube.prev # NIL
THEN tube.prev.refVec
ELSE Spline3d.RefVec[tube.coeffs, 0.0];
Walker[bez, 0.0, 1.0];
MakeFrame[
bez.b3,
IF Vector3d.Equal[bez.b3, bez.b2, 0.0001] -- check for degenerate end tangent
THEN Vector3d.Sub[bez.b2, bez.b1]
ELSE Vector3d.Sub[bez.b3, bez.b2],
1.0];
IF view # NIL THEN tube.circleRes ScreenCircleRes[tube, view];
};
ResInfo: PUBLIC PROC [tube: Tube]
RETURNS [nPoints, nPolys, minCres, maxCres: INTEGER] ~ {
ApplyResInfo: TubeProc ~ {
IF tube.circleRes > maxCres THEN maxCres ← tube.circleRes;
IF tube.circleRes < minCres THEN minCres ← tube.circleRes;
nPoints ← nPoints+
tube.circleRes*(IF tube.next # NIL THEN tube.nFrames ELSE tube.nFrames-1);
nPolys ← nPolys+2*tube.circleRes*(tube.nFrames-1);
};
minCres ← 10000;
maxCres ← nPolys ← nPoints ← 0;
TubeMisc.ApplyProc[tube, ApplyResInfo];
};
GetContour: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [Contour] ~ {
contour0: PairSequence ← tube.contour0.pairs;
contour1: PairSequence ← tube.contour1.pairs;
IF contour0 = NIL AND contour1 = NIL THEN RETURN[GetCircle[tube.circleRes]];
IF contour0 = NIL THEN contour0 ← GetCircle[tube.circleRes];
IF contour1 = NIL THEN contour1 ← GetCircle[tube.circleRes];
RETURN[ContourInterpolate[tube.contour0, tube.contour1, t]];
};
ContourInterpolate: PROC [contour0, contour1: Contour] RETURNS [Contour] ~ {
};
GetCircle: PUBLIC PROC [res: INTEGER] RETURNS [PairSequence] ~ {
IF res <= 0 THEN RETURN[NIL];
IF res > nCircles THEN RETURN[NewCircle[res]];
IF circles[res] = NIL THEN circles[res] ← NewCircle[res];
RETURN[circles[res]];
};
NewCircle: PROC [res: INTEGER] RETURNS [PairSequence] ~ {
rad: REAL ← 0.0;
drad: REAL ← 2.0*3.1415926535/REAL[res];
circle: PairSequence ← NEW[PairSequenceRec[res]];
FOR i: NAT IN[0..circle.length ← res) DO
circle[i] ← [RealFns.Cos[rad], RealFns.Sin[rad]];
rad ← rad+drad;
ENDLOOP;
RETURN[circle];
};
Basis: PUBLIC PROC [v, vv, rv: Triple] RETURNS [n, b: Triple] ~ {
dot: REAL;
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]];
};
RefMatrix: PUBLIC PROC [p, x, y, z: Triple, s, t: REAL, out: Matrix←NIL] RETURNS [Matrix] ~ {
IF ABS[t] > 0.001 THEN {
out ← Matrix3d.MakePureRotate[z, t, , out];
x ← Matrix3d.TransformVec[x, out];
y ← Matrix3d.TransformVec[y, out];
};
RETURN[Matrix3d.LocalScale[Matrix3d.MakeFromTriad[x, y, z, p], s, out]];
};
NSegments: PUBLIC PROC [tube: Tube] RETURNS [n: INTEGER ← 0] ~ {
Return number of splines in limb:
FOR t: Tube ← tube, t.next WHILE t # NIL DO n ← n+1; ENDLOOP;
};
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;
};