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 ← G3dSpline.BezierFromSpline[tube.spline];
bez: Bezier ← BezierFromHermite[tube.p0, tube.p1, tube.v0, tube.v1];
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];
Keep scale non-zero (to avoid subsequent surface normal computation problems):
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];
};
Note the tEpsilon may not be small enough for long, thin cylinders:
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;
};