* Non-crossing infill optimization * test triangles * test grid * cleaning * Align and clip rectilinear infill paths Generate infill coverage in the pattern's local frame, rotate triangular patterns by layer, and clip centerlines to the surface vicinity. Start closed outlines outside the surface so clipping splits them cleanly. * Update test_fill.cpp * Update multiline-infill.md --------- Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
7.5 KiB
Multiline infill — High Level Design
Purpose and scope
fill_multiline prints every sparse infill wall as N adjacent lines instead of
one, so a wall is d1 = N * spacing thick. Only internal sparse infill uses it.
Each pattern first builds its single-line centerlines at N times the usual line
spacing (so the density holds), and multiline_fill() then replaces each
centerline by the lines of that wall: the centerline itself when N is odd, and
closed outlines around it at every spacing out to d1 / 2. The outlines are
clipped to the fill region contracted by half a line width, then connected like
any other infill.
Outlines of centerlines that cross each other overlap at every crossing, which
over-extrudes the wall intersections. The line-crossing patterns Grid,
Triangles, Tri-hexagon and Cubic therefore build centerlines that never cross
(FillRectilinear::fill_surface_trapezoidal()), and so do Adaptive Cubic and
Support Cubic (FillAdaptive); the other patterns outline their usual
centerlines.
Non-crossing centerlines
The crossing lines are resolved into x-monotone paths, the levels of the line arrangement: walking along x, the k-th path is always the k-th line from the bottom. At every crossing, the two paths bounce off each other instead of passing through. Adjacent paths meet only at crossings, so their outlines touch there and nowhere overlap.
Where two paths meet, each is cut short by a line perpendicular to the bisector
of its bend, d1 / 2 from the crossing. The two cut segments are parallel and
d1 apart, so the outermost lines of the two walls sit exactly spacing apart,
like the lines inside a wall. Where three lines meet at one point, the middle
path runs straight through and the outer two are cut d1 from it.
Each pattern builds its rows along x in a rotated frame. Grid lines run at ±45°
there, and its rows are trapezoid waves that transpose on alternate layers. The three families of Triangles, Tri-hexagon and
Cubic run at 0°, 60° and 120°. Those rows rotate by 120° every layer about a
3-fold center of the arrangement, so each family takes every role in turn.
The pattern is phased on fixed positions, so it lines up across layers and
across the regions of one layer. Rounding the corners with
sparse_infill_smooth_factor happens before multiline_fill().
Each region builds only the rows over its bounding box in that frame, and
outlines only the centerlines within d1 / 2 of it, the ones whose outlines
reach it. Every row is monotone along its direction, so each outline is started
on the cap at the first end of its centerline, outside the region, and clipping
to the region cuts it only where it crosses the boundary.
Cubic
Single-line Cubic draws the three families at the same spacing h and shifts
them with z: by +dx, -dx and +dx, dx = z / sqrt(2). The multiline paths
follow the same lines. In the frame where one family is horizontal, the other two
cross in rows h apart, alternating by half a period, at height
tau = -3 * dx (mod h) above the horizontal line below them. The crossings split
every band between horizontal lines into up-pointing triangles of height tau,
down-pointing triangles of height h - tau, and hexagons. At tau = 0 (and h)
all three families meet at common points, as in Triangles. At tau = h / 2 the
triangles are equal, as in Tri-hexagon. The origin of that frame is always a
3-fold center, whatever z is, so the per-layer rotation keeps the lines in place.
Each band holds two paths that touch at its crossings: the upper one takes the
V below the crossing and runs along the top horizontal line, and the lower one
takes the inverted V above it and runs along the bottom line. Both are the same function
of tau, the lower one mirrored with h - tau. cubic_upper_level() builds one
period of the upper path as the lower envelope of five lines, clipped from below:
- the two slanted lines through the crossings,
- the horizontal line, lowered when the triangle above it is less than
1.5 * d1high, - the two chamfers where the path turns onto and off the horizontal line,
d1 / 2from those crossings, - the flat cut into the V at the crossing.
The cut height clamp(tau - d1 / 2, 0, h - d1) + d1 is what makes the pattern
continuous in z. While both triangles are at least 1.5 * d1 high, every
crossing is a pair of bends d1 / 2 from it, as in Tri-hexagon. When a triangle
is thinner, its three paths stack like a triple crossing. The path through it
flattens toward its base line and lies on it once the triangle is under d1 / 2
high, and the paths beside it are pushed d1 away. The layout thus reaches the
Triangles one where the families meet. Adjacent paths stay at least d1 apart
at every tau and at every density up to 100%.
Adaptive Cubic
Adaptive Cubic and Support Cubic take their lines from an octree of cubes
standing on a corner. On each layer every cube cuts its three mid-planes into
segments of the same three 60° families as Cubic, but the pattern is not
periodic. Smaller cubes near the surface add finer lines, and a finer line ends
where it meets the wall of its coarser cube, so the lines form crossings and
T-junctions. FillAdaptive::multiline_paths() builds the paths from these
segments directly, for each fill region and within 4 * d1 of it.
At a crossing the two paths bounce as in Cubic. At a T-junction the through line runs straight on and the path of the ending line stops there. Every path still runs left to right in the frame where one family is horizontal, and that family rotates with the layer.
Every line of every cube size lies on one fine lattice, so crossings closer than
a few d1 are the corners of one small triangle of that lattice, as in Cubic.
The cuts follow the Cubic rules without a closed formula:
- The two bends of a crossing are cut
d1apart,d1 / 2each, perpendicular to their bisector, so their walls touch. A cut goes no further than the path end, and the other bend takes the rest ofd1. - At the tip of a small triangle, between the two slanted families, a cut also goes no further than the neighbouring bend turning the other way, and the path beyond that bend is kept a wall away from it. The bends onto the horizontal family are not limited this way: pushing their paths apart would open gaps between walls that should touch.
- A cut moves the path only where the cut line lies beyond it, near its bend. The sharp bends between the two slanted families are cut after the bends onto the horizontal family, so the tip of a small triangle wins, as in Cubic.
- A path stopping at a T-junction is trimmed until it is
d1less half a line spacing from every other path, so that its end overlaps the wall it stops on by half a line and bonds to it. The paths are trimmed one at a time against the others as already trimmed, so two ends facing each other meet instead of both backing off. A path stopping on the line of another is trimmed before that one, so it gives way and the other still reaches the line it stops on. A second round trims every path again from its full length, so an end grows back where the ends it gave way to were trimmed later, and a last round only shortens them, keeping them that far apart. Paths shorter thand1are left out. - A line that ends on another less than
2 * d1past a crossing stops at that crossing instead, the shorter one where both do. The path along such a stub would be trimmed away, leaving a hole between the walls that were cut to touch it.
Short paths enclosed by coarser lines still print as closed outlines, but most paths run on across several cells.