<<>> <> <> <> <> DIRECTORY Cubic2, G2dBasic, G2dContour, G2dSpline, G3dBasic, G3dCurve, G3dMatrix, G3dPlane, G3dSpline, G3dVector, MessageWindow, Real, RealFns, G3dTube, Vector2; G3dTubeGeometryImpl: CEDAR PROGRAM IMPORTS Cubic2, G2dBasic, G2dContour, G3dCurve, G3dMatrix, G3dPlane, G3dSpline, G3dVector, MessageWindow, Real, RealFns, G3dTube, Vector2 EXPORTS G3dTube ~ BEGIN <> Contour: TYPE ~ G2dContour.Contour; ContourRep: TYPE ~ G2dContour.ContourRep; ContourSequence: TYPE ~ G2dContour.ContourSequence; ContourSequenceRep: TYPE ~ G2dContour.ContourSequenceRep; NearSpline2d: TYPE ~ G2dSpline.NearSpline; Pair: TYPE ~ G3dBasic.Pair; PairSequence: TYPE ~ G3dBasic.PairSequence; Ray: TYPE ~ G3dBasic.Ray; Triad: TYPE ~ G3dBasic.Triad; Triple: TYPE ~ G3dBasic.Triple; TripleSequence: TYPE ~ G3dBasic.TripleSequence; Curve: TYPE ~ G3dCurve.Curve; Matrix: TYPE ~ G3dMatrix.Matrix; SplineSequence: TYPE ~ G3dSpline.SplineSequence; Spline: TYPE ~ G3dSpline.Spline; Bezier: TYPE ~ G3dSpline.Bezier; XSection: TYPE ~ G3dTube.XSection; XSectionRep: TYPE ~ G3dTube.XSectionRep; XSectionProc: TYPE ~ G3dTube.XSectionProc; XSectionSequence: TYPE ~ G3dTube.XSectionSequence; XSectionSequenceRep: TYPE ~ G3dTube.XSectionSequenceRep; RadiusMode: TYPE ~ G3dTube.RadiusMode; Tube: TYPE ~ G3dTube.Tube; TubePlace: TYPE ~ G3dTube.TubePlace; TubeProc: TYPE ~ G3dTube.TubeProc; TubeRep: TYPE ~ G3dTube.TubeRep; origin: Triple ¬ []; <> NewTube: PUBLIC PROC [ knots: TripleSequence, tension: REAL ¬ 1.0, tangent0, tangent1: Triple ¬ [], cyclic: BOOL ¬ FALSE] RETURNS [head: Tube ¬ NIL] ~ { IF knots # NIL AND knots.length > 0 THEN { SetBez: TubeProc ~ { tube.bezier ¬ G3dSpline.BezierFromHermite[tube.p0, tube.p1, tube.v0, tube.v1]; }; MakeTube: PROC [p0: Triple, spline: Spline, parent: Tube] RETURNS [new: Tube] ~ { v0: Triple ¬ G3dSpline.Velocity[spline, 0.0]; new ¬ NEW[TubeRep ¬ [ p0: p0, v0: v0, spline: spline, prev: parent, tens0: tension, tens1: tension, r0: 1.0, r1: 1.0, splineValid: TRUE ]]; IF parent # NIL THEN {parent.p1 ¬ p0; parent.v1 ¬ v0}; }; splines: SplineSequence ¬ IF cyclic THEN G3dSpline.InterpolateCyclic[knots, tension] ELSE G3dSpline.Interpolate[knots, tangent0, tangent1, tension]; tube: Tube ¬ head ¬ MakeTube[knots[0], splines[0], NIL]; FOR n: NAT IN [1..splines.length) DO tube.next ¬ MakeTube[knots[n], splines[n], tube]; tube ¬ tube.next; ENDLOOP; tube.p1 ¬ knots[knots.length-1]; tube.v1 ¬ G3dSpline.Velocity[splines[splines.length-1], 1.0]; G3dTube.ApplyToTube[head, SetBez]; }; }; <> Spot: TYPE ~ RECORD [tube: Tube, t: REAL]; GetSpot: PROC [tube: Tube, t: REAL] RETURNS [s: Spot] ~ { nTubes: INTEGER ¬ G3dTube.NTubes[tube]; seg: INT ¬ IF t >= 1.0 THEN nTubes-1 ELSE Real.Floor[t*nTubes]; s.tube ¬ tube; s.t ¬ IF t >= 1.0 THEN 1.0 ELSE t-REAL[seg]/REAL[nTubes]; FOR n: INT IN [0..seg) DO s.tube ¬ s.tube.next; ENDLOOP; }; EvalPosition: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [Triple] ~ { spot: Spot ¬ GetSpot[tube, t]; RETURN[G3dSpline.Position[spot.tube.spline, spot.t]]; }; <<>> EvalXSection: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [XSection] ~ { spot: Spot ¬ GetSpot[tube, t]; IF NOT tube.xSectionsValid THEN MakeXSections[tube]; RETURN[GetXSection[spot.tube, spot.t]]; }; <> SetSpline: PUBLIC PROC [tube: Tube] ~ { tubeProc: TubeProc ~ {SetSectionSpline[tube]}; G3dTube.ApplyToTube[tube, tubeProc]; }; SetSectionSpline: PUBLIC PROC [tube: Tube] ~ { IF tube.splineValid THEN RETURN; IF tube.v0 = [] THEN tube.v0 ¬ G3dVector.Sub[tube.p1, tube.p0]; IF tube.v1 = [] THEN tube.v1 ¬ tube.v0; tube.spline ¬ G3dSpline.SplineFromHermite[ [tube.p0, tube.p1, G3dVector.Mul[tube.v0, tube.tens0], G3dVector.Mul[tube.v1, tube.tens1]], tube.spline]; tube.bezier ¬ G3dSpline.BezierFromHermite[tube.p0, tube.p1, tube.v0, tube.v1]; tube.splineValid ¬ TRUE; tube.xSectionsValid ¬ tube.lengthsValid ¬ FALSE; }; ExtractPartTube: PUBLIC PROC [in: Tube, t0, t1: REAL, out: Tube ¬ NIL] RETURNS [Tube] ~ { IF in # out THEN out ¬ G3dTube.CopyTubeSection[in, out]; out.spline ¬ G3dSpline.Reparameterize[in.spline, t0, t1, in.spline]; out.p0 ¬ G3dSpline.Position[in.spline, 0.0]; out.p1 ¬ G3dSpline.Position[in.spline, 1.0]; out.v0 ¬ G3dSpline.Velocity[in.spline, 0.0]; out.v1 ¬ G3dSpline.Velocity[in.spline, 1.0]; out.r0 ¬ Radius[in, t0]; out.r1 ¬ Radius[in, t1]; out.tens0 ¬ Tension[in, t0]; out.tens1 ¬ Tension[in, t1]; out.tw0 ¬ Twist[in, t0]; out.tw1 ¬ Twist[in, t1]; RETURN[out]; }; CurveFromTube: PUBLIC PROC [tube: Tube] RETURNS [Curve] ~ { nSections: NAT ¬ 0; splines: G3dSpline.SplineSequence; FOR t: Tube ¬ tube, t.next WHILE t # NIL DO nSections ¬ nSections+1; ENDLOOP; splines ¬ NEW[G3dSpline.SplineSequenceRep[nSections]]; FOR n: NAT IN [0..splines.length ¬ nSections) DO splines[n] ¬ tube.spline; tube ¬ tube.next; ENDLOOP; RETURN[G3dCurve.CurveFromSplines[splines]]; }; <> SetTubeProperties: PUBLIC PROC [ tube: Tube, scale, taper, epsilon: REAL, recursively: BOOL ¬ TRUE] ~ { Set: TubeProc ~ { tube.scale ¬ scale; tube.taper ¬ taper; tube.epsilon ¬ epsilon; }; IF recursively THEN G3dTube.ApplyToTube[tube, Set] ELSE [] ¬ Set[tube]; }; PropagateEpsilon: PUBLIC PROC [tube: Tube, epsilon: REAL] ~ { tubeProc: TubeProc ~ {tube.epsilon ¬ epsilon}; G3dTube.ApplyToTube[tube, tubeProc]; }; PropagateScale: PUBLIC PROC [tube: Tube, scale: REAL] ~ { tubeProc: TubeProc ~ {tube.scale ¬ scale}; G3dTube.ApplyToTube[tube, tubeProc]; }; Tension: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [REAL] ~ { RETURN[(1.0-t)*tube.tens0+t*tube.tens1]; }; Twist: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [REAL] ~ { RETURN[(1.0-t)*tube.tw0+t*tube.tw1]; }; <> NContours: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ { RETURN[IF tube.contours # NIL THEN tube.contours.length ELSE 0]; }; TestContourSequence: PROC [contours: ContourSequence] RETURNS [ContourSequence] ~ { IF contours = NIL OR contours.length >= contours.maxLength THEN contours ¬ LengthenContourSequence[contours]; RETURN[contours]; }; LengthenContourSequence: PROC [contours: ContourSequence] RETURNS [ContourSequence] ~ { nContours: CARDINAL ¬ IF contours = NIL THEN 0 ELSE contours.length; new: ContourSequence ¬ NEW[ContourSequenceRep[MAX[5, Real.Round[1.3*nContours]]]]; FOR i: CARDINAL IN[0..nContours) DO new[i] ¬ contours[i]; ENDLOOP; FOR i: CARDINAL IN[nContours..new.maxLength) DO new[i] ¬ NEW[ContourRep]; ENDLOOP; new.length ¬ nContours; RETURN[new]; }; AddContour: PUBLIC PROC [tube: Tube, contour: Contour] ~ { InsertContour: PROC [contour: Contour, contours: ContourSequence, n: NAT] ~ { FOR i: INT DECREASING IN [n..contours.length) DO contours[n+1] ¬ contours[n]; ENDLOOP; contours[n] ¬ contour; }; new: Contour ¬ NEW[ContourRep ¬ contour­]; tube.contours ¬ TestContourSequence[tube.contours]; SELECT TRUE FROM tube.contours.length = 0 => tube.contours[0] ¬ new; new.t < tube.contours[0].t => InsertContour[new, tube.contours, 0]; new.t > tube.contours[tube.contours.length-1].t => tube.contours[tube.contours.length] ¬ new; ENDCASE => { n: NAT ¬ 0; WHILE tube.contours[n].t < new.t DO n ¬ n+1; ENDLOOP; IF tube.contours[n].t = new.t THEN { tube.contours[n] ¬ new; RETURN; }; InsertContour[new, tube.contours, n]; }; tube.contours.length ¬ tube.contours.length+1; }; DivideContours: PUBLIC PROC [tube, tube0, tube1: Tube, t: REAL] ~ { circle: Contour ¬ G2dContour.Circle[tube.circleRes]; NewTs: PROC [tube: Tube, t0, t1: REAL] ~ { div: REAL ¬ IF t0 = t1 THEN 1.0 ELSE 1.0/(t1-t0); FOR n: NAT IN [0..tube.contours.length) DO tube.contours[n].t ¬ (tube.contours[n].t-t0)*div; ENDLOOP; }; IF tube.contours # NIL THEN { contours: ContourSequence ¬ tube.contours; -- tube may = tube0 or tube1 divideContour: Contour ¬ TContour[contours, circle, t]; tube0.contours ¬ tube1.contours ¬ NIL; AddContour[tube1, divideContour]; FOR n: NAT IN [0..contours.length) DO SELECT contours[n].t FROM <= t => AddContour[tube0, contours[n]]; >= t => AddContour[tube1, contours[n]]; ENDCASE; ENDLOOP; AddContour[tube0, divideContour]; NewTs[tube0, 0.0, t]; NewTs[tube1, t, 1.0]; }; }; Alpha: PROC [t, t0, t1: REAL] RETURNS [REAL] ~ { RETURN[IF t0 = t1 THEN t0 ELSE (t-t0)/(t1-t0)]; }; TContour: PUBLIC PROC [contours: ContourSequence, circle: Contour, t: REAL] RETURNS [Contour] ~ { IF contours = NIL THEN RETURN[circle] ELSE { c0, c1: Contour ¬ NIL; FOR n: NAT IN [0..contours.length) WHILE contours[n].t <= t DO c0 ¬ contours[n]; ENDLOOP; FOR n: NAT DECREASING IN [0..contours.length) WHILE contours[n].t >= t DO c1 ¬ contours[n]; ENDLOOP; RETURN[SELECT TRUE FROM c0 = NIL AND c1 = NIL => circle, c0 = NIL => G2dContour.Interpolate[circle, c1, Alpha[t, 0.0, c1.t]], c1 = NIL => G2dContour.Interpolate[c0, circle, Alpha[t, c0.t, 1.0]], ENDCASE => G2dContour.Interpolate[c0, c1, Alpha[t, c0.t, c1.t]]]; }; }; Skin: PUBLIC PROC [tube: Tube] ~ { InnerSkin: TubeProc ~ { circle: Contour ¬ G2dContour.Circle[tube.circleRes]; FOR n: INTEGER IN [0..G3dTube.NXSections[tube]) DO f: XSection ¬ tube.xSections[n]; f.contour ¬ TContour[tube.contours, circle, f.t]; f.normals ¬ SELECT TRUE FROM f.contour = circle => f.contour.pairs, ENDCASE => G2dContour.Normals[f.contour]; ENDLOOP; }; G3dTube.ApplyToTube[tube, InnerSkin]; }; PropagateCircleRes: PUBLIC PROC [tube: Tube, circleRes: INTEGER] ~ { tubeProc: TubeProc ~ { IF tube = NIL THEN RETURN; tube.circleRes ¬ circleRes; FOR n: NAT IN [0..G3dTube.NXSections[tube]) DO f: XSection ¬ tube.xSections[n]; IF f.contour # NIL AND f.contour.circle THEN f.contour ¬ circle; ENDLOOP; }; circle: Contour ¬ G2dContour.Circle[circleRes]; G3dTube.ApplyToTube[tube, tubeProc]; }; ScreenCircleRes: PUBLIC PROC [tube: Tube, view: Matrix] RETURNS [INTEGER] ~ { CircleRes: PROC [p: Triple, r: REAL] RETURNS [NAT] ~ { q: Vector2.VEC ¬ G3dMatrix.TransformD[p, view]; q1: Vector2.VEC ¬ G3dMatrix.TransformD[[p.x+r, p.y, p.z], view]; q2: Vector2.VEC ¬ G3dMatrix.TransformD[[p.x, p.y+r, p.z], view]; l: REAL ¬ MAX[Vector2.Length[Vector2.Sub[q, q1]], Vector2.Length[Vector2.Sub[q, q2]]]; RETURN[3+Real.Round[0.4*l]]; }; res: INTEGER ¬ IF tube.xSections = NIL THEN 0 ELSE MAX[ CircleRes[tube.p0, tube.xSections[0].scale], CircleRes[tube.p1, tube.xSections[tube.xSections.length-1].scale]]; IF res MOD 2 = 1 THEN res ¬ res+1; RETURN[res]; }; <> RoundTipInfo: TYPE ~ RECORD [needTo: BOOL, end: Triple]; GetRoundTipInfo: PROC [tube: Tube, t: REAL] RETURNS [i: RoundTipInfo] ~ { i ¬ IF t < 0.5 THEN IF tube.prev # NIL THEN [FALSE, origin] ELSE [TRUE, tube.p0] ELSE IF tube.next # NIL OR G3dTube.NBranches[tube] # 0 THEN [FALSE, origin] ELSE [TRUE, tube.p1]; }; Radius: PUBLIC PROC [tube: Tube, t: REAL, roundTip: BOOL ¬ FALSE] RETURNS [r: REAL] ~ { r ¬ (1.0-t)*tube.r0+t*tube.r1; IF tube.scale # 1.0 THEN r ¬ r*tube.scale; IF tube.taper # 0.0 THEN r ¬ r*(1.0-tube.taper*Length[tube, t]); IF roundTip THEN { i: RoundTipInfo ¬ GetRoundTipInfo[tube, t]; IF i.needTo THEN { p: Triple ¬ G3dSpline.Position[tube.spline, t]; d, dSq, rSq: REAL; IF (dSq ¬ G3dVector.SquareDistance[i.end, p]) > (rSq ¬ r*r) THEN RETURN; IF (d ¬ r-RealFns.SqRt[dSq]) > 0.0 THEN r ¬ RealFns.SqRt[rSq-d*d]; }; }; }; SetRadii: PUBLIC PROC [tube: Tube, radius: REAL] ~ { tubeProc: TubeProc ~ { tube.r0 ¬ tube.r1 ¬ radius; IF tube.xSectionsValid THEN ReScale[tube, tube.scale, tube.taper]; }; G3dTube.ApplyToTube[tube, tubeProc]; }; SetDescendantRadii: PUBLIC PROC [tube: Tube, mode: RadiusMode] ~ { tubeProc: TubeProc ~ { summer: TubeProc ~ { radius0: REAL ¬ Radius[tube, 0.0]; sum ¬ sum+(IF mode = square THEN radius0*radius0 ELSE radius0)}; sum: REAL ¬ 0.0; G3dTube.ApplyToBranches[tube, summer]; IF sum # 0.0 THEN { radius1: REAL ¬ Radius[tube, 1.0]; factor: REAL~IF mode=square THEN RealFns.SqRt[radius1*radius1/sum] ELSE radius1/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; IF tube.xSectionsValid THEN ReScale[tube, tube.scale, tube.taper]; }; G3dTube.ApplyToBranches[tube, setter]; }; }; G3dTube.ApplyToTube[tube, tubeProc]; }; <> Length: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [REAL] ~ { IF NOT tube.lengthsValid THEN SetSectionLengths[tube]; RETURN[tube.length0+t*tube.length]; }; SetLengths: PUBLIC PROC [tube: Tube] ~ { tubeProc: TubeProc ~ {SetSectionLengths[tube]}; G3dTube.ApplyToTube[tube, tubeProc]; }; SetSectionLengths: PUBLIC PROC [tube: Tube] ~ { IF tube = NIL OR tube.lengthsValid THEN RETURN; tube.length0 ¬ IF tube.prev = NIL THEN 0.0 ELSE tube.prev.length1; tube.length ¬ G3dSpline.Length[tube.spline]; tube.length1 ¬ tube.length0+tube.length; tube.lengthsValid ¬ TRUE; AdjustXSectionLengths[tube]; }; AdjustXSectionLengths: PUBLIC PROC [tube: Tube] ~ { IF tube.length # 0.0 AND tube.xSections # NIL AND tube.xSections.length > 0 THEN { factor: REAL ¬ tube.length/tube.xSections[tube.xSections.length-1].length; FOR n: NAT IN [0..tube.xSections.length) DO tube.xSections[n].length ¬ factor*tube.xSections[n].length; ENDLOOP; }; }; <> SetPlanes: PUBLIC PROC [tube: Tube] ~ { tubeProc: TubeProc ~ {SetPlane[tube]}; G3dTube.ApplyToTube[tube, tubeProc]; }; SetPlane: PUBLIC PROC [tube: Tube] ~ { tube.plane ¬ G3dPlane.FromPointAndNormal[tube.p1, tube.v1, TRUE]; tube.planeValid ¬ TRUE; }; <> Make: PUBLIC PROC [ tube: Tube, skin: BOOL ¬ FALSE, roundTip: BOOL ¬ FALSE, view: Matrix ¬ NIL, epsilon: REAL ¬ 0.03, xSectionProc: XSectionProc ¬ NIL] ~ { tube.epsilon ¬ epsilon; SetSpline[tube]; SetLengths[tube]; SetPlanes[tube]; MakeXSections[tube, skin, roundTip, view, xSectionProc]; }; MakeXSections: PUBLIC PROC [ tube: Tube, skin: BOOL ¬ FALSE, roundTip: BOOL ¬ FALSE, view: Matrix ¬ NIL, xSectionProc: XSectionProc ¬ NIL] ~ { tubeProc: TubeProc ~ {MakeSectionXSections[tube, skin, roundTip, view, xSectionProc]}; G3dTube.ApplyToTube[tube, tubeProc]; }; MakeSectionXSections: PUBLIC PROC [ tube: Tube, skin: BOOL ¬ FALSE, roundTip: BOOL ¬ FALSE, view: Matrix ¬ NIL, xSectionProc: XSectionProc ¬ NIL] ~ { tEpsilon: REAL ~ 0.0005; <> <> bez: Bezier ¬ tube.bezier; maxLen: REAL ¬ IF view = NIL THEN 0.01*G3dSpline.ConvexHullLength[bez] ELSE 0.0; vv: Triple ¬ G3dVector.Unit[IF G3dVector.Equal[bez.b1, bez.b0, 0.0001] THEN G3dVector.Sub[bez.b2, bez.b1] ELSE G3dVector.Sub[bez.b1, bez.b0]]; circle: Contour ¬ G2dContour.Circle[tube.circleRes]; c0: Contour ¬ TContour[tube.contours, circle, 0.0]; c1: Contour ¬ TContour[tube.contours, circle, 1.0]; MakeXSection: PROC [position, velocity: Triple, t: REAL, c: Contour] ~ { velocity ¬ G3dVector.Unit[velocity]; tube.xSections ¬ TestXSectionSequence[tube.xSections]; IF xSectionProc # NIL THEN tube.xSections[tube.xSections.length] ¬ xSectionProc[position, velocity, t] ELSE { fPrevious: XSection ¬ IF tube.xSections.length = 0 THEN NIL ELSE tube.xSections[tube.xSections.length-1]; f: XSection ¬ tube.xSections[tube.xSections.length]; f.t ¬ t; f.frame.position ¬ position; f.length ¬ IF fPrevious = NIL THEN 0.0 ELSE fPrevious.length+ G3dVector.Distance[f.frame.position, fPrevious.frame.position]; f.frame.triad ¬ Basis[velocity, vv, tube.refVec]; <> f.scale ¬ MAX[.000001, Radius[tube, t, roundTip]]; f.twist ¬ tube.tw0+t*(tube.tw1-tube.tw0); f.matrix ¬ RefMatrix[f.frame.position, f.frame.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 => G2dContour.Normals[c]; }; }; tube.xSections.length ¬ tube.xSections.length+1; vv ¬ velocity; tube.refVec ¬ tube.xSections[tube.xSections.length-1].frame.triad.n; }; Walker: PROC [bez: Bezier, t0, t1: REAL, c0, c1: Contour] ~ { IF DividedEnough[bez, t0, t1, c0, c1] THEN MakeXSection[ bez.b0, IF G3dVector.Equal[bez.b1, bez.b0, 0.0001] -- degenerate end tangent check THEN G3dVector.Sub[bez.b2, bez.b1] ELSE G3dVector.Sub[bez.b1, bez.b0], t0, c0] ELSE { bez0, bez1: Bezier; t: REAL ¬ 0.5*(t0+t1); c: Contour ¬ IF skin THEN TContour[tube.contours, circle, t] ELSE NIL; [bez0, bez1] ¬ G3dSpline.SplitBezier[bez]; Walker[bez0, t0, t, c0, c]; Walker[bez1, 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 G3dSpline.ConvexHullLength[bez] < maxLen OR G3dSpline.FlatBezier[bez, tube.epsilon] ELSE Cubic2.Flat[ [G3dMatrix.TransformD[bez.b0, view], G3dMatrix.TransformD[bez.b1, view], G3dMatrix.TransformD[bez.b2, view], G3dMatrix.TransformD[bez.b3, view]], 100.0*tube.epsilon]]; }; TwistSmallEnough: PROC [t0, t1: REAL] RETURNS[BOOL] ~ { RETURN[ABS[(t1-t0)*(tube.tw1-tube.tw0)] < 20.0]; }; ContoursCloseEnough: PROC [c0, c1: Contour] RETURNS [ret: BOOL] ~ { AllCircles: PROC [contours: ContourSequence] RETURNS [BOOL] ~ { IF contours # NIL THEN FOR n: NAT IN [0..contours.length) DO IF NOT contours[n].circle THEN RETURN [FALSE]; ENDLOOP; RETURN[TRUE]; }; IF AllCircles[tube.contours] THEN { IF roundTip THEN { i0: RoundTipInfo ¬ GetRoundTipInfo[tube, t0]; i1: RoundTipInfo ¬ GetRoundTipInfo[tube, t1]; IF NOT i0.needTo AND NOT i1.needTo THEN RETURN[TRUE] ELSE { Angle: PROC [t: REAL] RETURNS [REAL] ~ { p: Triple ¬ G3dSpline.Position[tube.spline, t]; d: REAL ¬ r-G3dVector.Distance[p, i0.end]; RETURN[IF d < 0.0 THEN 90.0 ELSE G2dBasic.ArcCosDeg[d/r]]; }; r, ang0, ang1: REAL ¬ IF t1 > 0.5 THEN tube.r1 ELSE tube.r0; RETURN[r = 0.0 OR ABS[Angle[t0]-Angle[t1]] < 15.0+1500.0*tube.epsilon]; } } ELSE RETURN[TRUE]; }; RETURN[G2dContour.Similar[c0, c1] > 1.0-10.0*tube.epsilon]; }; <> IF t1-t0 < tEpsilon THEN RETURN[TRUE]; RETURN[ BezSmallEnough[bez] AND TwistSmallEnough[t0, t1] AND ContoursCloseEnough[c0, c1] ]; }; IF tube.xSections # NIL THEN tube.xSections.length ¬ 0; tube.refVec ¬ SELECT TRUE FROM tube.prev # NIL => tube.prev.refVec, -- maintain continuity with parent tube.refVec # [] => tube.refVec, -- client-defined xSection orientation ENDCASE => G3dVector.Unit[ G3dSpline.RefVec[tube.spline, 0.0]]; -- a vector othogonal to base direction Walker[bez, 0.0, 1.0, c0, c1]; MakeXSection[ bez.b3, IF G3dVector.Equal[bez.b3, bez.b2, 0.0001] -- test for degenerate end tangent THEN G3dVector.Sub[bez.b2, bez.b1] ELSE G3dVector.Sub[bez.b3, bez.b2], 1.0, c1]; IF view # NIL THEN tube.circleRes ¬ Real.Round[ScreenCircleRes[tube, view]/MAX[0.01, 100*tube.epsilon]]; AdjustXSectionLengths[tube]; tube.xSectionsValid ¬ TRUE; }; <> ReScale: PUBLIC PROC [tube: Tube, scale: REAL ¬ 1.0, taper: REAL ¬ 0.0] ~ { tubeProc: TubeProc ~ { scale0: REAL ¬ scale*tube.r0; scale1: REAL ¬ scale*tube.r1; tube.scale ¬ scale; tube.taper ¬ taper; IF taper # 0.0 THEN { scale0 ¬ scale0*(1.0-taper*tube.length0); scale1 ¬ scale1*(1.0-taper*tube.length1); }; FOR n: NAT IN [0..G3dTube.NXSections[tube]) DO f: XSection ¬ tube.xSections[n]; f.scale ¬ MAX[0.000001, scale0+f.t*(scale1-scale0)]; f.matrix ¬ RefMatrix[f.frame.position, f.frame.triad, f.scale, f.twist, f.matrix]; ENDLOOP; }; G3dTube.ApplyToTube[tube, tubeProc]; }; SetCircles: PUBLIC PROC [tube: Tube, circleRes: NAT] ~ { tubeProc: TubeProc ~ { tube.circleRes ¬ circleRes; FOR n: NAT IN [0..G3dTube.NXSections[tube]) DO f: XSection ¬ tube.xSections[n]; f.contour ¬ circle; f.normals ¬ circle.normals; ENDLOOP; }; circle: Contour ¬ G2dContour.Circle[circleRes]; G3dTube.ApplyToTube[tube, tubeProc]; }; <> NearestTube: PUBLIC PROC [q: Triple, tube: Tube, nearValid: BOOL ¬ TRUE] RETURNS [nearest: TubePlace] ~ { tubeProc: TubeProc ~ { IF NOT nearValid THEN tube.nearSpline ¬ G3dSpline.NearestPoint[q, tube.spline, 0.0, 1.0, 0.0]; IF tube.nearSpline.distance > minDist THEN RETURN; minDist ¬ tube.nearSpline.distance; nearest.tube ¬ tube; nearest.t ¬ tube.nearSpline.t; }; minDist: REAL ¬ 10000.0; G3dTube.ApplyToTube[tube, tubeProc]; }; <> <> <<~ {>> <> <> <> <> < minDist THEN RETURN;>> <> <> <> <<};>> <> <<}>> <> <> <> <> <> <> <<};>> <> <> <> <> <> <> <> <> <<};>> <> <> <> <> <<};>> <> <> <> <> <> <> < minDist THEN EXIT;>> <> <> <> <<};>> <> <<};>> <<};>> <<>> NearestTube2d: PUBLIC PROC [tube: Tube, screen: Pair, mouseDown: BOOL, view: Matrix] RETURNS [near: TubePlace] ~ { nilXSpline: BOOL ¬ FALSE; t, dist, minDist: REAL ¬ 1000.0; hasPersp: BOOL ¬ G3dMatrix.HasPerspective[view]; tubeProc: TubeProc ~ { near2d: NearSpline2d; IF tube.xSpline = NIL THEN { tube.xSpline ¬ G3dMatrix.Mul[tube.spline, view, tube.xSpline]; nilXSpline ¬ TRUE; }; near2d ¬ G3dSpline.NearestPair[screen, tube.xSpline, hasPersp]; IF near2d.distance < minDist THEN { minDist ¬ near2d.distance; near.t ¬ SELECT near2d.t FROM < 0.05 => 0.0, > 0.95 => 1.0, ENDCASE => near2d.t; near.tube ¬ tube; }; }; IF mouseDown THEN { tubeProc: TubeProc ~ {tube.xSpline ¬ G3dMatrix.Mul[tube.spline, view, tube.xSpline]}; G3dTube.ApplyToTube[tube, tubeProc]; }; G3dTube.ApplyToTube[tube, tubeProc]; IF nilXSpline THEN {MessageWindow.Append["NilXSpline", TRUE]; MessageWindow.Blink[]}; }; <> Rotate: PUBLIC PROC [tube: Tube, line: Ray, angle: REAL, degrees, tubeAlso: BOOL ¬ TRUE] ~ { IF tube # NIL THEN { tubeProc: TubeProc ~ { tube.p0 ¬ G3dMatrix.Transform[tube.p0, m]; tube.p1 ¬ G3dMatrix.Transform[tube.p1, m]; tube.v0 ¬ G3dMatrix.TransformVec[tube.v0, m]; tube.v1 ¬ G3dMatrix.TransformVec[tube.v1, m]; G3dTube.ApplyToBranches[tube, tubeProc]; }; m: Matrix ¬ G3dMatrix.ObtainMatrix[]; m ¬ G3dMatrix.MakeRotateAbout[line.axis, angle, degrees, line.base, m]; IF tubeAlso THEN [] ¬ tubeProc[tube]; G3dTube.ApplyToBranches[tube, tubeProc]; G3dMatrix.ReleaseMatrix[m]; }; }; <> TotalNXSections: PUBLIC PROC [tube: Tube] RETURNS [nXSections: NAT ¬ 0] ~ { CountNXSections: TubeProc ~ {nXSections ¬ nXSections+G3dTube.NXSections[tube]}; G3dTube.ApplyToTube[tube, CountNXSections]; }; TestXSectionSequence: PROC [xSections: XSectionSequence] RETURNS [XSectionSequence] ~ { IF xSections = NIL OR xSections.length >= xSections.maxLength THEN xSections ¬ LengthenXSectionSequence[xSections]; RETURN[xSections]; }; LengthenXSectionSequence: PROC [xSections: XSectionSequence] RETURNS [XSectionSequence] ~ { nXSections: INTEGER ¬ IF xSections = NIL THEN 0 ELSE xSections.length; temp: XSectionSequence ¬ NEW[XSectionSequenceRep[MAX[5, Real.Round[1.3*nXSections]]]]; FOR i: NAT IN [0..nXSections) DO temp[i] ¬ xSections[i]; ENDLOOP; FOR i: NAT IN [nXSections..temp.maxLength) DO temp[i] ¬ NEW[XSectionRep]; ENDLOOP; temp.length ¬ nXSections; RETURN[temp]; }; GetXSection: PUBLIC PROC [tube: Tube, t: REAL] RETURNS [XSection] ~ { nXSections: INTEGER ¬ G3dTube.NXSections[tube]; xSections: XSectionSequence ¬ tube.xSections; SELECT TRUE FROM nXSections = 0 => RETURN[NIL]; t < xSections[0].t => RETURN[xSections[0]]; t > tube.xSections[nXSections-1].t => RETURN[xSections[nXSections-1]]; ENDCASE => { n: NAT ¬ 0; WHILE xSections[n].t < t DO n ¬ n+1; ENDLOOP; IF xSections[n].t = t THEN RETURN[xSections[n]] ELSE { xSection: XSection ¬ NEW[XSectionRep]; xSection0: XSection ¬ xSections[n-1]; xSection1: XSection ¬ xSections[n]; alpha: REAL ¬ (t-xSection0.t)/(xSection1.t-xSection0.t); unitTangent: Triple ¬ G3dVector.Unit[G3dSpline.Velocity[tube.spline, t]]; xSection.t ¬ t; xSection.frame.position ¬ G3dSpline.Position[tube.spline, t]; xSection.frame.triad ¬ Basis[unitTangent, xSection0.frame.triad.v, xSection0.frame.triad.n]; xSection.scale ¬ xSection0.scale+alpha*(xSection1.scale-xSection0.scale); xSection.twist ¬ xSection0.twist+alpha*(xSection1.twist-xSection0.twist); xSection.length ¬ xSection0.length+alpha*(xSection1.length-xSection0.length); xSection.matrix ¬ RefMatrix[ xSection.frame.position, xSection.frame.triad, xSection.scale, xSection.twist]; xSection.contour ¬ G2dContour.Interpolate[xSection0.contour, xSection1.contour, alpha]; xSection.normals ¬ G2dContour.Normals[xSection.contour]; RETURN[xSection]; }; }; }; PutXSection: PUBLIC PROC [tube: Tube, xSection: XSection] ~ { InsertXSection: PROC [xSection: XSection, xSections: XSectionSequence, n: NAT] ~ { FOR i: NAT DECREASING IN [n..xSections.length) DO xSections[n+1] ¬ xSections[n]; ENDLOOP; xSections[n] ¬ xSection; }; tube.xSections ¬ TestXSectionSequence[tube.xSections]; SELECT TRUE FROM tube.xSections.length = 0 => tube.xSections[0] ¬ xSection; xSection.t < tube.xSections[0].t => InsertXSection[xSection, tube.xSections, 0]; xSection.t > tube.xSections[tube.xSections.length-1].t => tube.xSections[tube.xSections.length] ¬ xSection; ENDCASE => { n: NAT ¬ 0; WHILE tube.xSections[n].t < xSection.t DO n ¬ n+1; ENDLOOP; IF tube.xSections[n].t = xSection.t THEN { tube.xSections[n] ¬ xSection; RETURN; }; InsertXSection[xSection, tube.xSections, n]; }; tube.xSections.length ¬ tube.xSections.length+1; }; CopyXSection: PUBLIC PROC [xSection: XSection] RETURNS [XSection] ~ { copy: XSection ¬ NIL; IF xSection # NIL THEN { copy ¬ NEW[XSectionRep]; copy.t ¬ xSection.t; copy.frame ¬ xSection.frame; copy.length ¬ xSection.length; copy.scale ¬ xSection.scale; copy.twist ¬ xSection.twist; copy.matrix ¬ G3dMatrix.CopyMatrix[xSection.matrix]; copy.contour ¬ G2dContour.Copy[xSection.contour]; copy.normals ¬ G2dBasic.CopyPairSequence[xSection.normals]; }; RETURN[copy]; }; CopyXSections: PUBLIC PROC [xSections: XSectionSequence] RETURNS [XSectionSequence] ~ { copy: XSectionSequence ¬ NIL; IF xSections # NIL THEN { copy ¬ NEW[XSectionSequenceRep[xSections.length]]; copy.length ¬ xSections.length; FOR n: NAT IN [0..copy.length) DO copy[n] ¬ CopyXSection[xSections[n]]; ENDLOOP; }; RETURN[copy]; }; Basis: PUBLIC PROC [v, vv, rv: Triple] RETURNS [Triad] ~ { UnitTest: PROC [v: Triple] ~ {IF ABS[G3dVector.Square[v]-1.0] > 0.001 THEN ERROR}; <> dot: REAL ¬ G3dVector.Dot[v, vv]; b, n: Triple; <> <> <> UnitTest[v]; UnitTest[vv]; UnitTest[rv]; 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 ¬ G3dVector.V90[v, n]; -- ensure orthogonality } ELSE { a: REAL ¬ G2dBasic.ArcCos[dot]; axis: Triple ¬ G3dVector.Cross[vv, v]; n ¬ G3dVector.Unit[G3dVector.RotateAbout[rv, axis, a, FALSE]]; -- the kludge }; b ¬ G3dVector.Unit[G3dVector.Cross[v, n]]; RETURN[[v, n, b]]; }; RefMatrix: PUBLIC PROC [position: Triple, triad: Triad, scale, twist: REAL, out: Matrix ¬ NIL] RETURNS [m: Matrix] ~ { x: Triple ¬ G3dVector.Unit[triad.n]; y: Triple ¬ G3dVector.Unit[triad.b]; z: Triple ¬ G3dVector.Unit[triad.v]; IF ABS[twist] > 0.001 THEN { out ¬ G3dMatrix.MakeRotate[z, twist,, out]; x ¬ G3dMatrix.TransformVec[x, out]; y ¬ G3dMatrix.TransformVec[y, out]; }; m ¬ G3dMatrix.LocalScale[G3dMatrix.MakeFromTriad[x, y, z, position], scale, out]; }; XYFromXSection: PUBLIC PROC [point: Triple, tube: Tube, t: REAL] RETURNS [Pair] ~ { ThreeTriples: TYPE ~ RECORD [o, x, y: Triple]; -- origin, x and y vectors GetOXY: PROC [tube: Tube, t: REAL] RETURNS [tt: ThreeTriples] ~ { OXYFromXSection: PROC [f: XSection] RETURNS [tt: ThreeTriples] ~ { m: Matrix ~ f.matrix; tt.o ¬ f.frame.position; tt.x ¬ G3dVector.Unit[[m[0][0], m[0][1], m[0][2]]]; tt.y ¬ G3dVector.Unit[[m[1][0], m[1][1], m[1][2]]]; }; nXSections: INTEGER ~ G3dTube.NXSections[tube]; SELECT TRUE FROM nXSections = 0 => RETURN[[origin, origin, origin]]; t < tube.xSections[0].t => RETURN[OXYFromXSection[tube.xSections[0]]]; t > tube.xSections[nXSections-1].t => RETURN[OXYFromXSection[tube.xSections[nXSections-1]]]; ENDCASE => { n: NAT ¬ 0; WHILE tube.xSections[n].t < t DO n ¬ n+1; ENDLOOP; IF tube.xSections[n].t = t THEN RETURN[OXYFromXSection[tube.xSections[n]]] ELSE { f0: XSection ¬ tube.xSections[n-1]; f1: XSection ¬ tube.xSections[n]; alpha: REAL ~ (t-f0.t)/(f1.t-f0.t); x0: Triple ~ [f0.matrix[0][0], f0.matrix[0][1], f0.matrix[0][2]]; x1: Triple ~ [f1.matrix[0][0], f1.matrix[0][1], f1.matrix[0][2]]; y0: Triple ~ [f0.matrix[1][0], f0.matrix[1][1], f0.matrix[1][2]]; y1: Triple ~ [f1.matrix[1][0], f1.matrix[1][1], f1.matrix[1][2]]; tt.o ¬ G3dSpline.Position[tube.spline, t]; tt.x ¬ G3dVector.Unit[G3dVector.Combine[x0, 1.0-alpha, x1, alpha]]; tt.y ¬ G3dVector.Unit[G3dVector.Combine[y0, 1.0-alpha, y1, alpha]]; }; }; }; tt: ThreeTriples ¬ GetOXY[tube, t]; q: Triple ¬ G3dVector.Sub[point, tt.o]; -- q now in tt space RETURN[[G3dVector.Dot[q, tt.x], G3dVector.Dot[q, tt.y]]]; }; END. .. <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<[n, b] _ Misc3d.Basis[v, vv, s.refVec];>> <> <> <> <> <<};>> <<>> <> <> <> <<>> <