TubeMiscImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, February 24, 1987 6:14:44 pm PST
DIRECTORY Controls, Contours, TubeDefs, TubeGeometry, TubeMisc, Matrix3d, Real, Spline3d, Vector3d;
TubeMiscImpl: CEDAR PROGRAM
IMPORTS Controls, Contours, Matrix3d, Real, Spline3d, TubeGeometry, Vector3d
EXPORTS TubeMisc
~ BEGIN
OPEN TubeDefs;
Points and Polygons Procedures
Points: PUBLIC PROC [
tube: Tube, pointProc: PointProc, view: Matrix ← NIL] RETURNS [nPoints: INT] ~ {
See TubeDisplay.DrawNormals
pointId: INT ← 0;
uRangeStart: REAL ← 2.0*PI*tube.r0;
OneSpline: PROC [tube: Tube, iStart: NAT, uRange0, v: REAL] ~ {
IF tube # NIL AND tube.frames # NIL THEN { 
uRange1: REAL ← uRange0*(0.5+0.5*(tube.r1/tube.r0)); -- a compromise
duRange: REAL ← (uRange1-uRange0)/(tube.frames.length-1.0);
circle: PairSequence ← Contours.CirclePairs[tube.circleRes];
tapered: BOOL ← tube.r1 # tube.r0;
vFactor: REALIF tapered
THEN (tube.r0-tube.r1)/Vector3d.Distance[tube.p0, tube.p1]
ELSE 1.0;
pNow, pPrev: Triple ← tube.p0;
odd: BOOL ← tube.circleRes MOD 2 = 1;
FOR i: NAT IN[iStart..NFrames[tube]) DO
uRange: REAL ← uRange0+i*duRange;
u: REAL ← 0.0;
du: REAL ← 0.5*uRange/tube.circleRes;
m: Matrix ← tube.frames[i].matrix;
mm: Matrix ← IF view # NIL THEN Matrix3d.Mul[m, view] ELSE m;
pPrev ← pNow;
pNow ← [m[3][0], m[3][1], m[3][2]];
IF i # 0 THEN v ← v+Vector3d.Distance[pNow, pPrev];
FOR j: NAT IN[0..tube.circleRes) DO
p: Triple ← Matrix3d.TransformPair[circle[j], mm];
n: Triple ← Matrix3d.TransformVec[[circle[j].x, circle[j].y, 0.0], mm];
IF tapered THEN {
vv: Triple ← Spline3d.Velocity[tube.coeffs, tube.frames[i].t];
len: REAL ← Vector3d.Length[n];
n ← Vector3d.Add[n, Vector3d.Mul[vv, len*vFactor/Vector3d.Length[vv]]];
};
n ← Vector3d.Normalize[n];
[] ← pointProc[pointId, p, n, u, v];
pointId ← pointId+1;
u ← IF j < tube.circleRes/2 OR odd THEN u+du ELSE u-du; -- u radial symmetry
ENDLOOP;
ENDLOOP;
IF tube.next # NIL AND tube.next.circleRes = tube.circleRes AND tube.next.r0 = tube.r1
THEN OneSpline[tube.next, 1, uRange1, v]  -- skip first frame
ELSE OneSpline[tube.next, 0, uRange1, v];  -- do first frame
FOR n: NAT IN [0..NBranches[tube]) DO
OneSpline[tube.branches[n], 0, uRangeStart, v]; -- use 1st frame of 1st branch tube
ENDLOOP;
};
};
OneSpline[tube, 0, uRangeStart, 0.0];     -- start with first frame of first tube
RETURN[pointId];
};
Polys: PUBLIC PROC [tube: Tube, polyProc: PolyProc] RETURNS [nPolys: INT] ~ {
polyId, pointId: INTEGER ← 0;
OneSpline: PROC [tube: Tube] ~ {
IF tube # NIL THEN { 
cRes: INTEGER ← tube.circleRes;
connect ith frame to (i+1)th frame:
FOR i: NAT IN [0..NFrames[tube]-1) DO
n: NAT ← pointId;
FOR ii: NAT IN[0..cRes) DO
nn: NATIF ii = cRes-1 THEN pointId ELSE n+1;
r: NAT ← n+cRes;
rr: NAT ← nn+cRes;
[] ← polyProc[polyId, n, nn, r];  -- reversed r and n, per Crow, 10/7/86
polyId ← polyId+1;
[] ← polyProc[polyId, r, nn, rr];  -- reversed r and rr, per Crow, 10/7/86
n ← n+1;
polyId ← polyId+1;
ENDLOOP;
pointId ← pointId+cRes;
ENDLOOP;
IF tube.next = NIL OR tube.next.circleRes # tube.circleRes OR tube.next.r0 # tube.r1
THEN pointId ← pointId+cRes;  -- end of branch or discontinuity from t to t.next
OneSpline[tube.next];
FOR n: NAT IN [0..NBranches[tube]) DO
OneSpline[tube.branches[n]];
ENDLOOP;
};
};
OneSpline[tube];
RETURN[polyId];
};
Miscellany
Find: PUBLIC PROC [searchee, search: Tube] RETURNS [found: BOOL] ~ {
tubeProc: TubeProc ~ {IF tube = searchee THEN found ← TRUE; RETURN[NOT found]};
found ← FALSE;
ApplyToTube[search, tubeProc];
};
First: PUBLIC PROC [tube: Tube] RETURNS [Tube] ~ {
t: Tube ← tube;
IF t = NIL THEN RETURN[NIL];
WHILE t.prev # NIL DO t ← t.prev; ENDLOOP;
RETURN[t];
};
Delete: PUBLIC PROC [tube: Tube] ~ {
IF tube # NIL AND tube.prev # NIL THEN {
IF tube.prev.next = tube
THEN tube.prev.next ← NIL
ELSE FOR n: NAT IN [0..NBranches[tube.prev]) DO
IF tube.prev.branches[n] # tube THEN LOOP;
tube.prev.branches[n] ← tube.prev.branches[NBranches[tube.prev]-1];
tube.prev.branches.length ← tube.prev.branches.length-1;
EXIT;
ENDLOOP;
};
};
UnSelectAll: PUBLIC PROC [tube: Tube] ~ {
tubeProc: TubeProc ~ {tube.selected ← FALSE};
ApplyToTube[tube, tubeProc];
};
Branching
NewBranch: PUBLIC PROC [tube: Tube] RETURNS [Tube] ~ {
branch: Tube ~ NEW[TubeRep ← [p0: tube.p1, v0: tube.v1]];
IF tube.next # NIL
THEN {
vDif: Triple ~ Vector3d.Sub[tube.next.p1, tube.next.p0];
v: Triple ~ Vector3d.SameLength[vDif, tube.v1];
branch.p1 ← Vector3d.Add[branch.p0, Vector3d.Combine[v, 1.5, vDif, -0.5]];
}
ELSE branch.p1 ← Vector3d.Add[branch.p0, Vector3d.Sub[branch.p0, tube.p0]];
branch.v1 ← Vector3d.SameLength[branch.v0, Vector3d.Sub[branch.p1, branch.p0]];
TubeGeometry.SetCoeffs[branch];
RETURN[branch];
};
AddBranch: PUBLIC PROC [tube, branch: Tube] ~ {
IF tube.branches = NIL THEN tube.branches ← NEW[TubeSequenceRep[1]];
IF tube.branches.length = tube.branches.maxLength THEN {
old: TubeSequence ~ tube.branches;
tube.branches ← NEW[TubeSequenceRep[old.length+1]];
tube.branches.length ← old.length;
FOR i: NAT IN [0..old.length) DO tube.branches[i] ← old[i]; ENDLOOP;
};
tube.branches[tube.branches.length] ← branch;
tube.branches.length ← tube.branches.length+1;
branch.prev ← tube;
};
Nearness Procedures
NearestTube: PUBLIC PROC [tube: Tube, point: Triple, tolerance: REAL ← 0.01]
RETURNS [nearest: Tube] ~ {
minDist: REAL ← 1000.0;
tubeProc: TubeProc ~ {
tube.near3d ← Spline3d.NearestPoint[point, tube.coeffs, 0.0, 1.0, tolerance];
IF tube.near3d.distance < minDist THEN {minDist ← tube.near3d.distance; nearest ← tube};
};
ApplyToTube[tube, tubeProc];
};
Radius Procedures
Radius: PUBLIC PROC [tube: Tube, t: REAL, roundTip: BOOLFALSE] RETURNS [REAL] ~ {
r: REAL ~ (1.0-t)*tube.r0+t*tube.r1;
IF roundTip AND ((t < 0.5 AND tube.prev = NIL) OR (t > 0.5 AND tube.next = NIL)) THEN {
r: REAL ~ IF t < 0.5 THEN tube.r0 ELSE tube.r1;
p: Triple ~ IF t < 0.5 THEN tube.p0 ELSE tube.p1;
d: REAL ~ r-Vector3d.Distance[Spline3d.Position[tube.coeffs, t], p];
IF d > 0.0 THEN RETURN[Real.SqRt[r*r-d*d]];
};
RETURN[r];
};
SetDescendantRadii: PUBLIC PROC [tube: Tube, mode: RadiusMode] ~ {
tubeProc: TubeProc ~ {
summer: TubeProc ~ {sum ← sum+(IF mode = square THEN tube.r0*tube.r0 ELSE tube.r0)};
sum: REAL ← 0.0;
ApplyToBranches[tube, summer];
IF sum # 0.0 THEN {
factor: REAL ~ IF mode = square THEN Real.SqRt[tube.r1*tube.r1/sum] ELSE tube.r1/sum;
setter: TubeProc ~ {
taper: REAL ~ IF tube.r0 # 0.0 THEN tube.r1/tube.r0 ELSE 1.0;
tube.r0 ← factor*tube.r0;
tube.r1 ← tube.r0*taper;
};
ApplyToBranches[tube, setter];
};
};
ApplyToTube[tube, tubeProc];
};
Attributes Procedures
TubeHull: PUBLIC PROC [tube: Tube] RETURNS [hull: Hull] ~ {
TubeMinMax: PROC [tube: Tube] RETURNS [hull: Hull] ~ {
tubeProc: TubeProc ~ {
b: Bezier ← Spline3d.BezierFromCoeffs[tube.coeffs];
points: ARRAY [0..4) OF Triple ← [b.b0, b.b1, b.b2, b.b3];
hull.rMin ← MIN[hull.rMin, tube.r0, tube.r1];
hull.rMax ← MAX[hull.rMax, tube.r0, tube.r1];
FOR n: NAT IN [0..4) DO
p: Triple ← points[n];
hull.pMin ← [MIN[p.x, hull.pMin.x], MIN[p.y, hull.pMin.y], MIN[p.z, hull.pMin.z]];
hull.pMax ← [MAX[p.x, hull.pMax.x], MAX[p.y, hull.pMax.y], MAX[p.z, hull.pMax.z]];
ENDLOOP;
};
hull ← [origin, [10000.0, 10000.0, 10000.0], [-10000.0, -10000.0, -10000.0], 10000.0, 0.0];
ApplyToTube[tube, tubeProc];
};
hull ← TubeMinMax[tube];
hull.center ← Vector3d.Mul[Vector3d.Add[hull.pMin, hull.pMax], 0.5];
};
Information Procedures
NTubes: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ {
nTubes: INTEGER ← 0;
tubeProc: TubeProc ~ {nTubes ← nTubes+1};
ApplyToTube[tube, tubeProc];
RETURN[nTubes];
};
NBranches: PUBLIC PROC [tube: Tube] RETURNS [n: INTEGER] ~ {
n ← IF tube.branches # NIL THEN tube.branches.length ELSE 0;
};
NSiblings: PUBLIC PROC [tube: Tube, tubeAlso: BOOLTRUE] RETURNS [n: INTEGER] ~ {
tubeProc: TubeProc ~ {n ← n+1};
n ← 0;
ApplyToSiblings[tube, tubeProc, tubeAlso];
};
NFrames: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ {
RETURN[IF tube.frames # NIL THEN tube.frames.length ELSE 0];
};
NPoints: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ {
OneSpline: PROC [tube: Tube, iStart: NAT] ~ {
IF tube # NIL AND tube.frames # NIL THEN { 
nPoints ← nPoints+(tube.frames.length-iStart)*tube.circleRes;
IF tube.next # NIL AND tube.next.circleRes = tube.circleRes AND tube.next.r0 = tube.r1
THEN OneSpline[tube.next, 1]    -- skip first frame
ELSE OneSpline[tube.next, 0];    -- do first frame
FOR n: NAT IN [0..NBranches[tube]) DO
OneSpline[tube.branches[n], 0];    -- use 1st frame of 1st branch tube
ENDLOOP;
};
};
nPoints: INT ← 0;
OneSpline[tube, 0];         -- start with first frame of first tube
RETURN[nPoints];
};
NPolys: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ {
n: INT ← 0;
FOR t: Tube ← tube, t.next WHILE t # NIL DO
IF t.frames # NIL THEN n ← n+2*(t.frames.length-1)*t.circleRes;
ENDLOOP;
RETURN[MAX[0, n]];
};
NSplinesInBranch: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ {
n: INT ← 0;
FOR t: Tube ← tube, t.next WHILE t # NIL DO n ← n+1; ENDLOOP;
RETURN[n];
};
Info: PUBLIC PROC [tube: Tube] RETURNS [nPoints, nPolys, minCres, maxCres: INT] ~ {
InnerResInfo: TubeProc ~ {
IF tube.circleRes > maxCres THEN maxCres ← tube.circleRes;
IF tube.circleRes < minCres THEN minCres ← tube.circleRes;
};
minCres ← 10000;
maxCres ← nPolys ← nPoints ← 0;
ApplyToTube[tube, InnerResInfo];
nPoints ← NPoints[tube];
nPolys ← NPolys[tube];
};
Copy Procedure
Copy: PUBLIC PROC [tube: Tube] RETURNS [copy: Tube] ~ {
InnerCopy: PROC [tube, prev: Tube] RETURNS [copy: Tube] ~ {
IF tube = NIL THEN RETURN[NIL];
copy ← NEW[TubeRep ← [
p0: tube.p0,
p1: tube.p1,
v0: tube.v0,
v1: tube.v1,
tw0: tube.tw0,
tw1: tube.tw1,
r0: tube.r0,
r1: tube.r1,
selected: tube.selected,
coeffs: Spline3d.CopyCoeffs[tube.coeffs],
xCoeffs: Spline3d.CopyCoeffs[tube.xCoeffs],
refVec: tube.refVec,
circleRes: tube.circleRes,
prev: prev,
refAny: tube.refAny
]];
copy.frames ← TubeGeometry.CopyFrames[tube.frames];
copy.branches ← NEW[TubeSequenceRep[NBranches[tube]]];
copy.branches.length ← copy.branches.maxLength;
FOR n: NAT IN [0..NBranches[tube]) DO
copy.branches[n] ← InnerCopy[tube.branches[n], copy];
ENDLOOP;
copy.next ← InnerCopy[tube.next, copy];
};
copy ← InnerCopy[tube, NIL];
};
Call Back Procedure for Tube Sections
ApplyToTube: PUBLIC PROC [tube: Tube, tubeProc: TubeProc] ~ {
Inner: PROC [tube: Tube] ~ {
IF tube = NIL OR NOT tubeProc[tube] THEN RETURN;
Inner[tube.next];
FOR n: NAT IN [0..NBranches[tube]) DO Inner[tube.branches[n]]; ENDLOOP;
};
Inner[tube];
};
ApplyToBranches: PUBLIC PROC [tube: Tube, tubeProc: TubeProc, nextAlso: BOOLTRUE] ~{
IF tube = NIL THEN RETURN;
IF nextAlso AND tube.next # NIL AND NOT tubeProc[tube.next] THEN RETURN;
FOR n: NAT IN [0..NBranches[tube]) DO
IF tube.branches[n] # NIL AND NOT tubeProc[tube.branches[n]] THEN RETURN;
ENDLOOP;
};
ApplyToSiblings: PUBLIC PROC [tube: Tube, tubeProc: TubeProc, tubeAlso: BOOLTRUE] ~ {
IF tube # NIL THEN {
prev: Tube ~ tube.prev;
IF prev = NIL THEN {
IF tubeAlso THEN [] ← tubeProc[tube];
RETURN;
};
IF (tubeAlso OR prev.next # tube) AND prev.next # NIL AND NOT tubeProc[prev.next]
THEN RETURN;
FOR n: NAT IN [0..NBranches[prev]) DO
IF (tubeAlso OR prev.branches[n] # tube) AND
prev.branches[n] # NIL AND NOT tubeProc[prev.branches[n]] THEN RETURN;
ENDLOOP;
};
};
Details Procedures
ToggleDetail: PUBLIC PROC [
details: Details, toToggle: DetailType, trueName, falseName: ROPE, outerData: OuterData] ~ {
state: BOOL;
SELECT toToggle FROM
autoSimplify => state ← details.autoSimplify ← NOT details.autoSimplify;
label   => state ← details.label   ← NOT details.label;
skeleton  => state ← details.skeleton  ← NOT details.skeleton;
ends   => state ← details.ends   ← NOT details.ends;
spline   => state ← details.spline   ← NOT details.spline;
pick   => state ← details.pick   ← NOT details.pick;
enabled  => state ← details.enabled  ← NOT details.enabled;
frames   => state ← details.frames  ← NOT details.frames;
lines   => state ← details.lines   ← NOT details.lines;
curvature  => state ← details.curvature  ← NOT details.curvature;
velocity   => state ← details.velocity  ← NOT details.velocity;
acceleration  => state ← details.acceleration ← NOT details.acceleration;
contours  => state ← details.contours  ← NOT details.contours;
normals   => state ← details.normals  ← NOT details.normals;
ENDCASE;
details.shape ← details.frames OR details.curvature OR details.velocity OR details.acceleration;
details.skin ← details.contours OR details.lines OR details.normals;
Controls.ButtonToggle[outerData, state, trueName, falseName];
};
DetailState: PUBLIC PROC [details: Details, type: DetailType] RETURNS [BOOL] ~ {
RETURN [SELECT type FROM
autoSimplify => details.autoSimplify,
label   => details.label,
skeleton  => details.skeleton,
spline   => details.spline,
pick   => details.pick,
enabled  => details.enabled,
frames   => details.frames,
lines   => details.lines,
curvature  => details.curvature,
velocity   => details.velocity,
acceleration  => details.acceleration,
contours  => details.contours,
normals   => details.normals,
shape   => details.shape,
skin    => details.skin,
ENDCASE  => FALSE];
};
Pendings Procedures
ClearPendings: PUBLIC PROC [pendings: Pendings] ~ {
pendings.epsilon ← pendings.scale ← pendings.r ← pendings.tw0 ← pendings.tw1 ←
pendings.cres ← pendings.holdPosition ← pendings.holdTangent ← pendings.shape ←
pendings.skin ← FALSE;
};
END.