<<>> <> <> <> 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 <> 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; <> 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 nBranches\n\n"]; WriteTube[s, tube, matrix]; IO.Close[s]; }; }; <> 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; <> 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]; }; <> 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]; }; <> 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]; }; <> 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]]; }; }; <> 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]; }; <> 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]; }; <> 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]; }; <> 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; }; }; <> 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 ~ { <> <> IF tube.selected THEN G3dDraw.Curve[context, tube.spline, view, viewport] ELSE G3dDraw.DotCurve[context, tube.spline, view, viewport]; }; <> G3dTube.ApplyToTube[tube, ApplyDraw]; <> }; 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] ~ { <> 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. .. <> 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]; }; <<>>