G3dTubeMiscImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, August 26, 1992 5:31 pm PDT
DIRECTORY Draw2d, FileNames, FS, G2dContour, G3dDraw, G3dBasic, G3dIO, G3dMatrix, G3dQuaternion, G3dShape, G3dSpline, G3dVector, G3dTube, Imager, ImagerColor, ImagerFont, ImagerInterpress, IO, Real, Rope;
G3dTubeMiscImpl: CEDAR PROGRAM
IMPORTS Draw2d, FileNames, FS, G2dContour, G3dDraw, G3dIO, G3dMatrix, G3dQuaternion, G3dSpline, G3dVector, G3dTube, Imager, ImagerColor, ImagerFont, ImagerInterpress, IO, Real, Rope
EXPORTS G3dTube
~ BEGIN
Types
DrawType:   TYPE ~ Draw2d.DrawType;
MarkType:   TYPE ~ Draw2d.MarkType;
Hull:     TYPE ~ G3dBasic.Hull;
Pair:     TYPE ~ G3dBasic.Pair;
PairSequence:  TYPE ~ G3dBasic.PairSequence;
Triple:    TYPE ~ G3dBasic.Triple;
Matrix:    TYPE ~ G3dMatrix.Matrix;
Viewport:    TYPE ~ G3dMatrix.Viewport;
Quaternion:   TYPE ~ G3dQuaternion.Quaternion;
Shape:     TYPE ~ G3dShape.Shape;
Bezier:    TYPE ~ G3dSpline.Bezier;
Details:    TYPE ~ G3dTube.Details;
DetailsRep:   TYPE ~ G3dTube.DetailsRep;
XSection:    TYPE ~ G3dTube.XSection;
XSectionSequence: TYPE ~ G3dTube.XSectionSequence;
PointProc:   TYPE ~ G3dTube.PointProc;
PolyProc:    TYPE ~ G3dTube.PolyProc;
Tube:     TYPE ~ G3dTube.Tube;
TubeProc:    TYPE ~ G3dTube.TubeProc;
TubeRep:    TYPE ~ G3dTube.TubeRep;
TubeSequence:  TYPE ~ G3dTube.TubeSequence;
TubeSequenceRep: TYPE ~ G3dTube.TubeSequenceRep;
Context:    TYPE ~ Imager.Context;
STREAM:    TYPE ~ IO.STREAM;
ROPE:     TYPE ~ Rope.ROPE;
PI:      REAL ~ G3dBasic.PI;
origin:    Triple ~ G3dBasic.origin;
IO
Line: TYPE ~ G3dIO.Line;
ReadTube: PUBLIC PROC [stream: STREAM] RETURNS [tube: Tube] ~ {
IF stream = NIL
THEN RETURN[NIL]
ELSE {
InnerReadTube: PROC [prev: Tube] RETURNS [tube: Tube] ~ {
TubeLineOK: PROC RETURNS [ok: BOOL ¬ TRUE] ~ {
DO
line ¬ G3dIO.GetLine[stream, line ! IO.EndOfStream => GOTO eof];
IF Rope.Equal[G3dIO.GetWord[line], "tube:", FALSE] THEN RETURN;
REPEAT
eof => RETURN[FALSE];
ENDLOOP;
};
IF TubeLineOK[] THEN {
next: BOOL;
tube ¬ NEW[TubeRep];
tube.prev ¬ prev;
tube.p0 ¬ G3dIO.GetTriple[line];
tube.p1 ¬ G3dIO.GetTriple[line];
tube.v0 ¬ G3dIO.GetTriple[line];
tube.v1 ¬ G3dIO.GetTriple[line];
tube.r0 ¬ G3dIO.GetReal[line];
tube.r1 ¬ G3dIO.GetReal[line];
tube.tw0 ¬ G3dIO.GetReal[line];
tube.tw1 ¬ G3dIO.GetReal[line];
tube.scale ¬ G3dIO.GetReal[line];
tube.taper ¬ G3dIO.GetReal[line];
tube.epsilon ¬ G3dIO.GetReal[line];
tube.circleRes ¬ G3dIO.GetInteger[line];
next ¬ Rope.Equal[G3dIO.GetWord[line], "next", FALSE];
tube.branches ¬ NEW[TubeSequenceRep[G3dIO.GetInteger[line]]];
tube.branches.length ¬ tube.branches.maxLength;
G3dTube.SetSpline[tube];
IF next THEN tube.next ¬ InnerReadTube[tube];
IF tube.branches # NIL THEN FOR n: NAT IN [0..tube.branches.length) DO
tube.branches[n] ¬ InnerReadTube[tube];
ENDLOOP;
};
};
line: Line ¬ G3dIO.ObtainLine[];
tube ¬ InnerReadTube[NIL];
G3dIO.ReleaseLine[line];
};
};
WriteTube: PUBLIC PROC [stream: STREAM, tube: Tube, m: Matrix ¬ NIL] ~ {
Write: TubeProc ~ {
IO.PutRope[stream, "Tube: "];
G3dIO.WriteTriple[stream, IF m = NIL THEN tube.p0 ELSE G3dMatrix.Transform[tube.p0, m]];
G3dIO.WriteTriple[stream, IF m = NIL THEN tube.p1 ELSE G3dMatrix.Transform[tube.p1, m]];
G3dIO.WriteTriple[stream,
IF m = NIL THEN tube.v0 ELSE G3dMatrix.TransformVec[tube.v0, m]];
G3dIO.WriteTriple[stream,
IF m = NIL THEN tube.v1 ELSE G3dMatrix.TransformVec[tube.v1, m]];
IO.PutF[stream, "%5.4f %5.4f\t", IO.real[tube.r0], IO.real[tube.r1]];
IO.PutF[stream, "%5.4f %5.4f\t", IO.real[tube.tw0], IO.real[tube.tw1]];
IO.PutF[stream, "%5.4f %5.4f\t", IO.real[tube.scale], IO.real[tube.taper]];
IO.PutF[stream, "%5.4f %g ", IO.real[tube.epsilon], IO.int[tube.circleRes]];
IO.PutRope[stream, IF tube.next # NIL THEN "next " ELSE "no "];
IO.PutF1[stream, "%g", IO.int[NBranches[tube]]];
IO.PutRope[stream, "\n"];
};
ApplyToTube[tube, Write];
};
WritePointsPolys: PUBLIC PROC [tube: Tube, fileName: ROPE, m: Matrix ¬ NIL]
RETURNS [nPoints, nPolys: INT] ~ {
Point: PointProc ~ {
IO.PutF1[out, "%g\t", IO.int[id]];
G3dIO.WriteTriple[out, position];
G3dIO.WriteTriple[out, normal];
IO.PutF[out, "%6.5f %6.5f\n", IO.real[texture.x], IO.real[texture.y]];
};
Poly: PolyProc ~ {
IO.PutFL[out,"%g\t%4g %4g %4g\n", LIST[IO.int[id], IO.int[p0], IO.int[p1], IO.int[p2]]];
};
out: STREAM ¬ FS.StreamOpen[fileName, $create];
nPoints ¬ NPoints[tube];
nPolys ¬ NPolys[tube];
IO.PutF1[out, "-- %g\n\n", IO.rope[tube.name]];
IO.PutF[out, "DataSize~ vertices: %g, surfaces: %g\n\n", IO.int[nPoints], IO.int[nPolys]];
IO.PutRope[out,
"Vertices~ index: integer xyzCoords: triple normalVec: triple textureCoords: pair\n\n"];
[] ¬ Points[tube, Point, m];
IO.PutRope[out, "\nSurfaces~ index: integer vertices: nats\n\n"];
[] ¬ Polys[tube, Poly];
IO.Close[out];
};
WriteIP: PUBLIC PROC [ref: ImagerInterpress.Ref, tube: Tube, details: Details, m: Matrix] ~ {
font: ImagerFont.Font ¬ ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/helvetica-mrr"], 12.0];
ContextProc: PROC [context: Context] ~ {
metersPerPoint: REAL ~ .0254/72.0;
Imager.ScaleT[context, metersPerPoint];
Imager.SetStrokeWidth[context, 1.0];
Imager.SetStrokeEnd[context, round];
Imager.SetFont[context, font];
Imager.TranslateT[context, [0.0, 0.5*11.0*72.0]];
G3dTube.DrawTube[context, tube, details, m];
};
ImagerInterpress.DoPage[ref, ContextProc];
};
TubeFromFile: PUBLIC PROC [fileName: ROPE] RETURNS [tube: Tube] ~ {
name: ROPE ¬ FileNames.ResolveRelativePath[fileName];
IF name # NIL THEN {
stream: STREAM ¬ FS.StreamOpen[name ! FS.Error => GOTO noOpen];
IF (tube ¬ ReadTube[stream]) = NIL THEN RETURN;
IO.Close[stream];
name ¬ FileNames.GetShortName[name];
tube.name ¬ Rope.Substr[name, 0, Rope.Index[name, 0, "."]];
EXITS noOpen => NULL;
};
RETURN[tube];
};
TubeToFile: PUBLIC PROC [tube: Tube, fileName: ROPE, matrix: Matrix ¬ NIL] ~ {
IF fileName # NIL THEN {
s: STREAM ¬ FS.StreamOpen[FileNames.ResolveRelativePath[fileName], $create];
IO.PutF1[s, "\n-- %g, format for each line:\n", IO.rope[fileName]];
IO.PutRope[s,
"--Tube: p0 p1 v0 v1 r0 r1 tw0 tw1 scale taper eps cres <next|no> nBranches\n\n"];
WriteTube[s, tube, matrix];
IO.Close[s];
};
};
Points and Polygons
Points: PUBLIC PROC [tube: Tube, pointProc: PointProc, view: Matrix ¬ NIL]
RETURNS [nPoints: INT]
~ {
pointId: INT ¬ 0;
uRangeStart: REAL ¬ 2.0*PI*tube.r0;
OneSpline: PROC [tube: Tube, iStart: NAT, uRange0, v: REAL] ~ {
IF tube # NIL AND tube.xSections # NIL THEN { 
uRange1: REAL ¬ uRange0*(0.5+0.5*(tube.r1/tube.r0)); -- a compromise
duRange: REAL ¬ (uRange1-uRange0)/(tube.xSections.length-1.0);
circle: PairSequence ¬ G2dContour.CirclePairs[tube.circleRes];
tapered: BOOL ¬ tube.r1 # tube.r0;
vFactor: REAL ¬ IF tapered
THEN (tube.r0-tube.r1)/G3dVector.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..NXSections[tube]) DO
uRange: REAL ¬ uRange0+i*duRange;
u: REAL ¬ 0.0;
du: REAL ¬ 0.5*uRange/tube.circleRes;
m: Matrix ¬ tube.xSections[i].matrix;
mm: Matrix ¬ IF view # NIL THEN G3dMatrix.Mul[m, view] ELSE m;
pPrev ¬ pNow;
pNow ¬ [m[3][0], m[3][1], m[3][2]];
IF i # 0 THEN v ¬ v+G3dVector.Distance[pNow, pPrev];
FOR j: NAT IN[0..tube.circleRes) DO
p: Triple ¬ G3dMatrix.TransformPair[circle[j], mm];
n: Triple ¬ G3dMatrix.TransformVec[[circle[j].x, circle[j].y, 0.0], mm];
IF tapered THEN {
vv: Triple ¬ G3dSpline.Velocity[tube.spline, tube.xSections[i].t];
len: REAL ¬ G3dVector.Length[n];
n ¬ G3dVector.Add[n, G3dVector.Mul[vv, len*vFactor/G3dVector.Length[vv]]];
};
n ¬ G3dVector.Unit[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 xSection
ELSE OneSpline[tube.next, 0, uRange1, v];  -- do first xSection
FOR n: NAT IN [0..NBranches[tube]) DO
OneSpline[tube.branches[n], 0, uRangeStart, v]; -- use 1st xSection of 1st branch tube
ENDLOOP;
};
};
OneSpline[tube, 0, uRangeStart, 0.0];     -- start with first xSection 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 xSection to (i+1)th xSection:
FOR i: NAT IN [0..NXSections[tube]-1) DO
n: NAT ¬ pointId;
FOR ii: NAT IN[0..cRes) DO
nn: NAT ¬ IF 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];
};
ShapeFromTube: PUBLIC PROC [tube: Tube] RETURNS [shape: Shape] ~ {
Point: PointProc ~ {
v: G3dShape.Vertex ¬ shape.vertices[id] ¬ NEW[G3dShape.VertexRep];
v­ ¬ [point: position, normal: normal, texture: texture];
};
Poly: PolyProc ~ {
p: G3dBasic.NatSequence ¬ NEW[G3dBasic.NatSequenceRep[3]];
p.length ¬ 3;
p[0] ¬ p0;
p[1] ¬ p1;
p[2] ¬ p2;
};
shape ¬ NEW[G3dShape.ShapeRep ¬ [name: tube.name]];
shape.vertices ¬ NEW[G3dShape.VertexSequenceRep[NPoints[tube]]];
shape.vertices.length ¬ shape.vertices.maxLength;
[] ¬ Points[tube, Point];
shape.surfaces ¬ NEW[G3dBasic.SurfaceSequenceRep[NPolys[tube]]];
shape.surfaces.length ¬ shape.surfaces.maxLength;
[] ¬ Polys[tube, Poly];
};
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];
};
Where: PUBLIC PROC [searchee, search: Tube] RETURNS [rope: ROPE] ~ {
whereError: ERROR = CODE;
Inner: PROC [tube: Tube] ~ {
IF tube.prev = NIL THEN ERROR whereError;
IF tube.prev.next = tube
THEN rope ¬ Rope.Concat[".next", rope]
ELSE FOR n: NAT IN [0..NBranches[tube.prev]) DO
IF tube.prev.branches[n] = tube THEN {
rope ¬ IO.PutFR[".branches[%g]%g", IO.int[n], IO.rope[rope]];
EXIT;
};
REPEAT
FINISHED => ERROR whereError;
ENDLOOP;
IF tube.prev # search THEN Inner[tube.prev];
};
IF NOT Find[searchee, search] THEN RETURN[".not found"];
IF searchee = search THEN RETURN[".same"];
Inner[searchee ! whereError => {rope ¬ ".error findng tube"; CONTINUE}];
};
lastTubePicked: Tube ¬ NIL;
SetLastTubePicked: PUBLIC PROC [tube: Tube] ~ {
lastTubePicked ¬ tube;
};
LastTubePicked: PUBLIC PROC [copy: BOOL ¬ TRUE] RETURNS [Tube] ~ {
RETURN[IF copy THEN CopyEntireTube[lastTubePicked] ELSE lastTubePicked];
};
First: PUBLIC PROC [tube: Tube] RETURNS [t: Tube] ~ {
IF (t ¬ tube) = NIL THEN RETURN;
WHILE t.prev # NIL DO t ¬ t.prev; ENDLOOP;
};
Last: PUBLIC PROC [tube: Tube] RETURNS [t: Tube] ~ {
IF (t ¬ tube) = NIL THEN RETURN;
WHILE t.next # NIL DO t ¬ t.next; ENDLOOP;
};
Divide: PUBLIC PROC [tube: Tube, t: REAL] ~ {
new: Tube ¬ CopyEntireTube[tube];
new ¬ G3dTube.ExtractPartTube[new, 0.0, t];
tube ¬ G3dTube.ExtractPartTube[tube, t, 1.0, tube];
IF tube.prev # NIL THEN {
IF tube.prev.next = tube
THEN tube.prev.next ¬ new
ELSE FOR n: NAT IN [0..tube.prev.branches.length) DO
IF tube.prev.branches[n] # tube THEN LOOP;
tube.prev.branches[n] ¬ new;
EXIT;
ENDLOOP;
};
new.prev ¬ tube.prev;
new.next ¬ tube;
tube.prev ¬ new;
};
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;
};
};
SelectAll: PUBLIC PROC [tube: Tube] ~ {
tubeProc: TubeProc ~ {tube.selected ¬ TRUE};
ApplyToTube[tube, tubeProc];
};
UnSelectAll: PUBLIC PROC [tube: Tube] ~ {
tubeProc: TubeProc ~ {tube.selected ¬ FALSE};
ApplyToTube[tube, tubeProc];
};
ExtractTubeName: PUBLIC PROC [fileName: ROPE] RETURNS [name: ROPE] ~ {
rope: ROPE ¬ FileNames.ResolveRelativePath[fileName];
rope ¬ FileNames.GetShortName[rope];
name ¬ Rope.Substr[rope, 0, Rope.Index[rope, 0, "."]];
};
SetTubeColors: PUBLIC PROC [tube: Tube] ~ {
SetColor: G3dTube.TubeProc ~ {
hue: REAL ¬ 0.15*Depth[tube];
hue ¬ hue-Real.Floor[hue];
tube.color ¬ ImagerColor.ColorFromHSV[hue, 1.0, 1.0];
};
ApplyToTube[tube, SetColor];
};
Interpolation
Average: PUBLIC PROC [tube0, tube1: Tube] RETURNS [tube: Tube] ~ {
tube ¬ NEW[TubeRep];
tube.v0 ¬ G3dVector.Midpoint[tube0.v0, tube1.v0];
tube.v1 ¬ G3dVector.Midpoint[tube0.v1, tube1.v1];
tube.p0 ¬ G3dVector.Midpoint[tube0.p0, tube1.p0];
tube.p1 ¬ G3dVector.Midpoint[tube0.p1, tube1.p1];
tube.circleRes ¬ (tube0.circleRes+tube1.circleRes)/2;
tube.epsilon ¬ 0.5*(tube0.epsilon+tube1.epsilon);
tube.tw0 ¬ 0.5*(tube0.tw0+tube1.tw0);
tube.tw1 ¬ 0.5*(tube0.tw1+tube1.tw1);
tube.tens0 ¬ 0.5*(tube0.tens0+tube1.tens0);
tube.tens1 ¬ 0.5*(tube0.tens1+tube1.tens1);
tube.r0 ¬ 0.5*(tube0.r0+tube1.r0);
tube.r1 ¬ 0.5*(tube0.r1+tube1.r1);
tube.scale ¬ 0.5*(tube0.scale+tube1.scale);
tube.taper ¬ 0.5*(tube0.taper+tube1.taper);
tube.refVec ¬ G3dVector.Unit[G3dVector.Midpoint[tube0.refVec, tube1.refVec]];
G3dTube.SetSpline[tube];
};
InterpTubes: PUBLIC PROC [tube1, tube2, interp: Tube, alpha: REAL] ~ {
GetQuaternion: PROC [t: Tube] RETURNS [Quaternion] ~ {
z: Triple ¬ G3dVector.Unit[G3dVector.Sub[t.p1, t.p0]];
x: Triple ¬ G3dVector.Unit[G3dVector.V90[z, t.refVec]];
y: Triple ¬ G3dVector.Unit[G3dVector.Cross[z, x]];
m ¬ G3dMatrix.MakeFromTriad[x, y, z,, FALSE, m];
RETURN[G3dQuaternion.FromMatrix[m]];
};
Set: PROC [t, t1, t2: Tube] ~ {
q1: Quaternion ¬ GetQuaternion[t1];
q2: Quaternion ¬ GetQuaternion[t2];
q: Quaternion ¬ G3dQuaternion.Slerp[q1, q2, alpha];
len1: REAL ¬ G3dVector.Distance[t1.p0, t1.p1];
len2: REAL ¬ G3dVector.Distance[t2.p0, t2.p1];
len: REAL ¬ len1+alpha*(len2-len1);
temp: Matrix ¬ m ¬ G3dQuaternion.ToMatrix[q, m];
z: Triple ¬ G3dMatrix.TransformVec[[0.0, 0.0, 1.0], m];
IF t.prev # NIL THEN t.p0 ¬ t.prev.p1;
t.p1 ¬ G3dVector.Add[t.p0, G3dVector.SetVectorLength[z, len]];
};
DoTube: PROC [t, t1, t2: Tube] ~ {
Set[t, t1, t2];
IF t.next # NIL THEN DoTube[t.next, t1.next, t2.next];
IF t.branches # NIL THEN
FOR n: NAT IN [0..t.branches.length) DO
DoTube[t.branches[n], t1.branches[n], t2.branches[n]];
ENDLOOP;
};
m: Matrix;
DoTube[interp, tube1, tube2];
};
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 ~ G3dVector.Sub[tube.next.p1, tube.next.p0];
v: Triple ~ G3dVector.SameLength[vDif, tube.v1];
branch.p1 ¬ G3dVector.Add[branch.p0, G3dVector.Combine[v, 1.5, vDif, -0.5]];
}
ELSE branch.p1 ¬ G3dVector.Add[branch.p0, G3dVector.Sub[branch.p0, tube.p0]];
branch.v1 ¬ G3dVector.SameLength[branch.v0, G3dVector.Sub[branch.p1, branch.p0]];
G3dTube.SetSpline[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;
};
GetBranch: PUBLIC PROC [tube: Tube, n: NAT] RETURNS [branch: Tube ¬ NIL] ~ {
IF tube.next = NIL
THEN {
IF tube.branches # NIL AND n < tube.branches.length
THEN RETURN[tube.branches[n]];
}
ELSE {
IF n = 0 THEN RETURN[tube.next];
IF tube.branches # NIL AND n-1 < tube.branches.length
THEN RETURN[tube.branches[n-1]];
};
};
Attributes
TubeHull: PUBLIC PROC [tube: Tube] RETURNS [hull: Hull] ~ {
TubeMinMax: PROC [tube: Tube] RETURNS [hull: Hull] ~ {
tubeProc: TubeProc ~ {
b: Bezier ¬ G3dSpline.BezierFromSpline[tube.spline];
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 ¬ G3dVector.Mul[G3dVector.Add[hull.pMin, hull.pMax], 0.5];
};
InvalidateTubeAttributes: PUBLIC PROC [tube: Tube] ~ {
tubeProc: TubeProc ~ {
tube.splineValid ¬ tube.lengthsValid ¬ tube.xSectionsValid ¬ FALSE;
};
ApplyToTube[tube, tubeProc];
};
Information
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, nextAlso: BOOL ¬ FALSE] RETURNS [n: INTEGER ¬0] ~ {
IF tube = NIL THEN RETURN;
IF tube.branches # NIL THEN n ¬ tube.branches.length;
IF nextAlso AND tube.next # NIL THEN n ¬ n+1;
};
NSiblings: PUBLIC PROC [tube: Tube, tubeAlso: BOOL ¬ TRUE] RETURNS [n: INTEGER] ~ {
tubeProc: TubeProc ~ {n ¬ n+1};
n ¬ 0;
ApplyToSiblings[tube, tubeProc, tubeAlso];
};
NXSections: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ {
RETURN[IF tube.xSections # NIL THEN tube.xSections.length ELSE 0];
};
NPoints: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ {
OneSpline: PROC [tube: Tube, iStart: NAT] ~ {
IF tube # NIL AND tube.xSections # NIL THEN { 
nPoints ¬ nPoints+(tube.xSections.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 xSection
ELSE OneSpline[tube.next, 0];    -- do first xSection
FOR n: NAT IN [0..NBranches[tube]) DO
OneSpline[tube.branches[n], 0];    -- use 1st xSection of 1st branch tube
ENDLOOP;
};
};
nPoints: INT ¬ 0;
OneSpline[tube, 0];         -- start with first xSection 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.xSections # NIL THEN n ¬ n+2*(t.xSections.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];
};
Depth: PUBLIC PROC [tube: Tube] RETURNS [n: NAT ¬ 0] ~ {
IF tube = NIL THEN RETURN;
WHILE tube.prev # NIL DO tube ¬ tube.prev; n ¬ n+1; ENDLOOP;
};
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];
};
Copying
CopyTubeSection: PUBLIC PROC [tube: Tube, copy: Tube ¬ NIL] RETURNS [Tube] ~ {
IF tube = NIL THEN RETURN[NIL];
IF copy = NIL THEN copy ¬ NEW[TubeRep];
copy­ ¬ tube­;
copy.xSections ¬ G3dTube.CopyXSections[tube.xSections];
copy.spline ¬ G3dSpline.CopySpline[tube.spline];
copy.xSpline ¬ G3dSpline.CopySpline[tube.xSpline];
IF tube.branches # NIL THEN {
copy.branches ¬ NEW[TubeSequenceRep[tube.branches.length]];
copy.branches.length ¬ tube.branches.length;
FOR n: NAT IN [0..copy.branches.length) DO
copy.branches[n] ¬ tube.branches[n];
ENDLOOP;
};
RETURN[copy];
};
CopyEntireTube: PUBLIC PROC [tube: Tube] RETURNS [copy: Tube] ~ {
InnerCopy: PROC [tube, prev: Tube] RETURNS [copy: Tube] ~ {
IF tube = NIL THEN RETURN[NIL];
copy ¬ NEW[TubeRep ¬ tube­];
copy.xSections ¬ G3dTube.CopyXSections[tube.xSections];
copy.spline ¬ G3dSpline.CopySpline[tube.spline];
copy.xSpline ¬ G3dSpline.CopySpline[tube.xSpline];
copy.prev ¬ prev;
IF tube.branches # NIL THEN {
copy.branches ¬ NEW[TubeSequenceRep[tube.branches.length]];
copy.branches.length ¬ tube.branches.length;
FOR n: NAT IN [0..copy.branches.length) DO
copy.branches[n] ¬ InnerCopy[tube.branches[n], copy];
ENDLOOP;
};
copy.next ¬ InnerCopy[tube.next, copy];
};
copy ¬ InnerCopy[tube, NIL];
};
Call Backs
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: BOOL ¬ TRUE] ~{
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: BOOL ¬ TRUE] ~ {
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;
};
};
Drawing
SetColor: PROC [context: Context, tube: Tube] ~ INLINE {
Imager.SetColor[context, IF tube.color = NIL THEN Imager.black ELSE tube.color];
};
DrawTube: PUBLIC PROC [
context: Context,
tube: Tube,
details: Details,
view: Matrix,
viewport: Viewport ¬ [],
drawType: DrawType ¬ solid]
~ {
IF details = NIL THEN RETURN;
IF details[skeleton]
THEN DrawSkeleton[context, tube, view, viewport, drawType];
IF details[spline]
THEN DrawSplines[context, tube, view, viewport, drawType];
IF details[lines]
THEN DrawLines[context, tube, view, viewport, drawType];
IF details[contours]
THEN DrawContours[context, tube, view, viewport, drawType];
IF details[xSections]
THEN DrawXSections[context, tube, view, viewport, details[label], drawType];
IF details[curvature]
THEN DrawCurvature[context, tube, view, viewport, details[label], drawType];
IF details[velocity]
THEN DrawVelocity[context, tube, view, viewport, details[label], drawType];
IF details[acceleration]
THEN DrawAcceleration[context, tube, view, viewport, details[label], drawType];
IF details[normals]
THEN DrawNormals[context, tube, view, viewport, details[label], drawType];
IF details[points]
THEN DrawPoints[context, tube, view, viewport, dot];
};
DrawSkeleton: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
SetColor[context, tube];
G3dDraw.Segment[context, tube.p0, tube.p1, view, viewport, IF tube.selected THEN drawType ELSE dotted];
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawSplines: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
SetColor[context, tube];
tube.selected ← TRUE;  -- for filming day
IF tube.selected
THEN G3dDraw.Curve[context, tube.spline, view, viewport]
ELSE G3dDraw.DotCurve[context, tube.spline, view, viewport];
};
Imager.SetStrokeWidth[context, 2.0];   -- for film day
G3dTube.ApplyToTube[tube, ApplyDraw];
Imager.SetStrokeWidth[context, 1.0];   -- for film day
};
DrawSplineEnds: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
mark: MarkType ¬ dot]
~ {
ApplyDraw: TubeProc ~ {
SetColor[context, tube];
G3dDraw.Mark[context, tube.p0, view, viewport,, mark];
G3dDraw.Mark[context, tube.p1, view, viewport,, mark];
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
AxialDrawRes: PROC [tube: Tube] RETURNS [INTEGER] ~ {
nXSections: NAT ¬ G3dTube.NXSections[tube];
RETURN[IF tube.next = NIL THEN nXSections ELSE nXSections-1];
};
DrawPoints: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
markType: MarkType ¬ dot]
~ {
ApplyDraw: TubeProc ~ {
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
f: XSection ¬ tube.xSections[i];
IF f.contour # NIL AND f.contour.pairs # NIL THEN {
pairs: PairSequence ¬ f.contour.pairs;
FOR j: NAT IN [0..pairs.length) DO
G3dDraw.Mark[
context, G3dMatrix.TransformPair[pairs[j], f.matrix], view, viewport,, markType];
ENDLOOP;
};
ENDLOOP;
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawContours: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ {
x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y];
};
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
f: XSection ¬ tube.xSections[i];
IF f.contour # NIL AND f.contour.pairs # NIL THEN {
pairs: PairSequence ¬ f.contour.pairs;
p0, psave: Pair ¬
VP[G3dMatrix.TransformD[G3dMatrix.TransformPair[pairs[0], f.matrix], view]];
FOR j: NAT IN [1..pairs.length) DO
p1: Pair ¬
VP[G3dMatrix.TransformD[G3dMatrix.TransformPair[pairs[j], f.matrix], view]];
Draw2d.Line[context, p0, p1, drawType];
p0 ¬ p1;
ENDLOOP;
Draw2d.Line[context, p0, psave];
};
ENDLOOP;
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawLines: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ {
x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y];
};
Xform: PROC [p: Pair, m: Matrix] RETURNS [Pair] ~ {
RETURN[VP[G3dMatrix.TransformD[G3dMatrix.TransformPair[p, m], view]]];
};
allCircles: BOOL ¬ TRUE;
xSections: XSectionSequence ¬ tube.xSections;
nXSections: NAT ¬ G3dTube.NXSections[tube];
SetColor[context, tube];
FOR i: NAT IN [0..nXSections) DO
IF xSections[i].contour # NIL AND NOT xSections[i].contour.circle
THEN {allCircles ¬ FALSE; EXIT};
ENDLOOP;
IF allCircles
THEN {
circle: PairSequence ¬ G2dContour.CirclePairs[tube.circleRes];
FOR i: NAT IN [0..tube.circleRes) DO
p0: Pair ¬ Xform[circle[i], xSections[0].matrix];
FOR j: NAT IN [1..nXSections) DO
p1: Pair ¬ Xform[circle[i], xSections[j].matrix];
Draw2d.Line[context, p0, p1, drawType];
p0 ¬ p1;
ENDLOOP;
ENDLOOP;
}
ELSE {
pc: REAL ¬ 0.0;
dpc: REAL ¬ 1.0/MAX[1, tube.circleRes];
FOR i: NAT IN [0..tube.circleRes) DO
p0: Pair ¬
Xform[G2dContour.PercentPair[xSections[0].contour, pc], xSections[0].matrix];
FOR j: NAT IN [1..nXSections) DO
p1: Pair ¬ Xform[G2dContour.PercentPair[xSections[j].contour, pc], xSections[j].matrix];
Draw2d.Line[context, p0, p1, drawType];
p0 ¬ p1;
ENDLOOP;
pc ¬ pc+dpc;
ENDLOOP;
};
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawXSections: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
label: BOOL,
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ {
x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y];
};
DrawMatrix: PROC [m: Matrix] ~ {
o: Pair ¬ VP[G3dMatrix.TransformD[[m[3][0], m[3][1], m[3][2]], view]];
FOR i: NAT IN [0..2] DO
p: Pair ¬ VP[G3dMatrix.TransformD[
[m[3][0]+scale*m[i][0], m[3][1]+scale*m[i][1], m[3][2]+scale*m[i][2]], view]];
Draw2d.Line[context, o, p, drawType];
IF label THEN Draw2d.Label[context, p, names[i]];
ENDLOOP;
};
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
IF tube.xSections[i].matrix # NIL THEN DrawMatrix[tube.xSections[i].matrix];
ENDLOOP;
};
scale: REAL ~ 0.8;
names: ARRAY[0..2] OF Rope.ROPE ¬ ["x", "y", "z"];
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawNormals: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
label: BOOL,
drawType: DrawType ¬ solid]
~ {
See TubeIO.Points
tapered: BOOL;
vFactor: REAL;
ApplyDraw: TubeProc ~ {
DrawN: PROC [f: XSection] ~ {
FOR j: NAT IN [0..f.contour.pairs.length) DO
p: Triple ¬ G3dMatrix.TransformPair[f.contour.pairs[j], f.matrix];
n: Triple ¬ G3dMatrix.TransformVec[[f.normals[j].x, f.normals[j].y, 0], f.matrix];
IF tapered THEN {
v: Triple ¬ G3dSpline.Velocity[tube.spline, f.t];
len: REAL ¬ G3dVector.Length[n];
n ¬ G3dVector.Add[n, G3dVector.Mul[v, len*vFactor/G3dVector.Length[v]]];
};
n ¬ G3dVector.Unit[n];
G3dDraw.Vector[context, p, n, view, viewport, IF label THEN "n" ELSE NIL, 0.1];
ENDLOOP;
};
tapered ¬ tube.r1 # tube.r0;
IF tapered THEN vFactor ¬ (tube.r0-tube.r1)/G3dVector.Distance[tube.p0, tube.p1];
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
IF tube.xSections[i].matrix # NIL THEN DrawN[tube.xSections[i]];
ENDLOOP;
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawCurvature: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
label: BOOL,
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
DrawK: PROC [f: XSection] ~ {
p: Triple ¬ [f.matrix[3][0], f.matrix[3][1], f.matrix[3][2]];
k: Triple ¬ G3dSpline.Curvature[tube.spline, f.t];
G3dDraw.Vector[context, p, k, view, viewport, IF label THEN "k" ELSE NIL];
};
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
IF tube.xSections[i].matrix # NIL THEN DrawK[tube.xSections[i]];
ENDLOOP;
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawVelocity: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
label: BOOL,
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
DrawV: PROC [f: XSection] ~ {
p: Triple ¬ [f.matrix[3][0], f.matrix[3][1], f.matrix[3][2]];
v: Triple ¬ G3dSpline.Velocity[tube.spline, f.t];
G3dDraw.Vector[context, p, v, view, viewport, IF label THEN "v" ELSE NIL];
};
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
IF tube.xSections[i].matrix # NIL THEN DrawV[tube.xSections[i]];
ENDLOOP;
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
DrawAcceleration: PUBLIC PROC [
context: Context,
tube: Tube,
view: Matrix,
viewport: Viewport ¬ [],
label: BOOL,
drawType: DrawType ¬ solid]
~ {
ApplyDraw: TubeProc ~ {
DrawA: PROC [f: XSection] ~ {
p: Triple ¬ [f.matrix[3][0], f.matrix[3][1], f.matrix[3][2]];
a: Triple ¬ G3dSpline.Acceleration[tube.spline, f.t];
G3dDraw.Vector[context, p, a, view, viewport, IF label THEN "a" ELSE NIL];
};
SetColor[context, tube];
FOR i: NAT IN [0..AxialDrawRes[tube]) DO
IF tube.xSections[i].matrix # NIL THEN DrawA[tube.xSections[i]];
ENDLOOP;
};
G3dTube.ApplyToTube[tube, ApplyDraw];
};
NVectors: PUBLIC PROC [tube: Tube, details: Details] RETURNS [INTEGER] ~ {
sum: INTEGER ¬ 0;
IF details[enabled] THEN FOR t: Tube ¬ tube, t.next WHILE t # NIL DO
nXSections: INTEGER ¬ G3dTube.NXSections[t];
sum ¬ sum
+(IF details[lines] THEN t.circleRes*nXSections ELSE 0)
+(IF details[xSections] THEN 3*nXSections ELSE 0);
FOR n: NAT IN [0..nXSections) DO
f: XSection ¬ t.xSections[n];
sum ¬ sum+(IF f.contour # NIL THEN f.contour.pairs.length ELSE t.circleRes);
ENDLOOP;
ENDLOOP;
RETURN[sum];
};
END.
..
Writing Tube in Nelson Max Format
WriteMaxPointsPolys: PUBLIC PROC [tube: Tube, fileName: ROPE, m: Matrix ¬ NIL]
RETURNS [nPoints, nPolys: INT] ~ {
MaxPoint: PointProc ~ {
IO.PutF[out, "%g,", IO.int[id]];
IO.PutF[out, "%6.5f,%6.5f,%6.5f,", IO.real[position.x], IO.real[position.y], IO.real[position.z]];
IO.PutF[out, "%6.5f,%6.5f,%6.5f,", IO.real[normal.x], IO.real[normal.y], IO.real[normal.z]];
IO.PutF[out, "%6.5f,%6.5f,\n", IO.real[texture.x], IO.real[texture.y]];
};
MaxPoly: PolyProc ~ {
IO.PutF[out, "%g,%g,%g,%g,\n", IO.int[id], IO.int[p0], IO.int[p1], IO.int[p2]];
};
out: STREAM ¬ FS.StreamOpen[fileName, $create];
nPoints ¬ Points[tube, MaxPoint, m];
IO.PutF[out, "-1\n"];
nPolys ¬ Polys[tube, MaxPoly];
IO.PutF[out, "-1\n"];
IO.Close[out];
};