Expand description
Swept disks along chains of straight segments and circular arcs (#232, #245).
A pipe run, a rebar or a cable tray edge is a disk swept along legs
joined by bends or corners: authoring formats write it as a disk swept
along a composite of lines and arcs, or along a polyline whose corners
are mitred or rounded to a fillet radius.
swept_disk_along_pieces meshes all of them so that every point of
the exact tube lies within the chord budget of the triangles.
§The bound
Each piece is bounded on its own, with the split crate::sweep uses
for one arc (#231): half the budget c chords the disk, so a ring of
n points inscribed in the circle of radius r lies within
r (1 - cos(pi/n)) <= c/2 of it, and every point of the exact tube is
within c/2 of the tube the ring polygon traces when it is carried
rigidly along the exact centreline by the piece’s own frames (each
exact section circle and its polygon lie in the same plane). The
other half bounds that polygon tube against the triangles:
- Along a segment the frames do not turn, so the polygon tube is a prism whose walls are the mesh’s own planar rectangles: zero.
- Along an arc of radius
Rthe frames are rotation-minimising, which along a circle means they turn with it: consecutive stations are rotations of one ring about the arc’s axis, and the ring’s plane contains that axis. The polygon tube between two stations is then the revolutioncrate::revolvebounds, and its triangles are within(R + r)(1 - cos(phi/2))of it for a stepphi: the sagitta of the outer side of the bend, not of the centreline. Its wall quads are planar trapezoids, so their twist adds nothing; the span boundcrate::sweepcomputes, directrix sagitta plus the section’s reach pluscrate::loft’s quad bound, is exactly that sum here. The step halves until every span fitsc/2; more than 4096 steps on a bend is refused withBudgetExceeded.
§Joints
Consecutive pieces share one station, so the walls meet watertight and
wound one way: the incoming piece’s end section. The outgoing piece’s
own start frame is that frame turned by the least rotation that takes
the incoming tangent to the outgoing one, moved to its start point, and
its stations follow from it. Where the pieces are tangent continuous
and meet, the two frames coincide. Where they turn by theta or leave
a gap g, every vertex of the shared station lies at most
delta <= g + 2 r sin(theta/2) from the station the outgoing piece’s
bound was proved for; moving a triangle’s vertices by at most delta
moves each of its points by at most delta (same barycentric weights),
so the first span of the outgoing piece adds the measured delta to its
bound and is refined until the sum fits.
A joint turning by at most
theta_max = 2 asin(c / (8 r)) (pi once c >= 8 r)
is tangent continuous at the requested accuracy: its shared section
leans off the outgoing piece’s own by at most a quarter of the budget,
which that piece’s first span pays as above. A gap above c/4 is
refused.
§Mitres
A sharper joint between two straight segments is a corner, and a disk
swept round a corner is mitred at half angle (#245): both tubes are cut
by the plane through the corner whose normal n = (u + v) / |u + v|
bisects the incoming and outgoing tangents u and v. Reflection in
that plane maps u to -v, so it swaps the two axis lines and with
them the two cylinders of radius r (and the two bores); it fixes the
plane, so both cylinders cut it in the same ellipse. The outgoing frame
is the incoming one turned by the least rotation about u x v, which
agrees with that reflection on the plane square to u. So profile point
k of the incoming end section and of the outgoing start section, each
projected along its own axis onto the mitre plane, land on one point:
the two pieces share that mitre ring, and every vertex of it lies on
both exact cylinders. Each wall quad of a segment joins two generator
lines of the prism over the ring polygon, so it is a planar trapezoid,
exactly a face of that prism cut by the end planes: the walls add
nothing, and the span bound measures them as zero.
The section pays instead. A point w + s u of the exact cylinder (w
square to u, |w| = r; the mitre plane cuts its generator at
s_cut(w) = -w.n / u.n, at most r tan(theta/2)) lies within
(1 - l) r <= e = r (1 - cos(pi/m)) of the prism point w' + s u at
its azimuth, w' = l w on the ring polygon of m points. When that
point lies past the plane, the plane crosses the prism’s generator at
l s_cut(w), which moves the point by at most
(1 - l) s_cut(w) <= (1 - l) r tan(theta/2) along u, square to the
first move: the distance is at most e / cos(theta/2), attained where
the ellipse’s major axis ends. The rings are therefore chorded to
(c/2) cos(theta_m/2) for the sharpest mitre theta_m (only finer
elsewhere), and a mitred pipe keeps every point of the exact tube
within c of its triangles, as a smooth one does. A hollow disk’s bore
is mitred by the same plane and bounded the same way.
Along a segment of length L the tube runs, on the generator at w,
from its start plane to its end plane; both cuts are linear in w, so
the shortest generator is L - r |g_perp| with
g = n1 / u.n1 - n0 / u.n0 (n0 = u at a square end), and it must be
positive (one mitre: L > r tan(theta/2)). Refused by name: a segment
whose mitres meet inside the tube, which would cut through itself; a
reversal (theta = pi, where the mitre plane holds both legs); and a
corner beside an arc, because the mitre plane cuts the torus round an
arc in a different curve than the tube on its other side, so no ring
lies on both exact surfaces and the bound above does not carry over.
§Fillets
A fillet radius f rounds every corner between two straight segments
(the swept disk’s fillet_radius): a corner turning by
theta is replaced by the arc of radius f tangent to both segments,
which cuts f tan(theta/2) off each. Segments shortened to nothing are
dropped (two fillets then meet tangentially). Refused by name: a fillet
that needs more of a segment than it has, a corner beside an arc (only
corners between straight segments are rounded), a disk radius above the
fillet radius, or any bend radius, whose tube folds through the bend’s
axis on its inside, and a reversal. A disk radius equal to the fillet
(or bend) radius is refused by name too, although the format rule
permits it (fillet radius at least the disk radius): the bend is then a
horn torus whose inner wall pinches to one point on the bend’s axis,
where every section touches. That boundary is not a two-manifold; a
closed mesh within the budget would have to either meet itself there
(degenerate triangles) or stand off the pinch and misstate it.
Enums§
- Path
Piece - One smooth piece of a swept disk’s directrix.
Functions§
- joint_
tolerance - The largest turn a joint may make and still be tangent continuous at
chord budget
chordfor a disk of radiusradius:2 asin(c / (8 r))(see the module notes). - swept_
disk_ along_ pieces - Sweep a disk, optionally hollow, along a chain of segments and arcs so
that every point of the exact tube lies within
chordof the mesh.