Skip to content

Source: src/infill/README.md — this page is rendered directly from the file in the repository. Edit it there.

Infill — Pattern Generation Inside a Boundary

This module turns a closed boundary Paths into a Paths of infill line segments. It is pure 2D geometry: no slicing, no G-code, no per-layer bookkeeping.

Boundary in. Lines out. The boundary is sacred — never inflate it again.


Why it exists

Sparse infill is what fills the inside of a print so it isn't a pile of loose walls. There are several established patterns, each a tradeoff between print speed, strength, and isotropy. Rather than scatter that geometry across the slicing pipeline, all of it lives behind one entry point — generate_infill — that takes a pattern enum and produces line segments. The slicer doesn't know how a gyroid is computed; the gyroid doesn't know what a SliceLayer is.

The boundary handed in is the final infill region: it has already been inset to account for every wall bead and the configured infill overlap. The infill generators must fill it as-is, with no further offsetting.


The contract

  1. Boundaries are not inflated. core::infill::calculate_interior_region has already done that work. A second inward inflate inside this module was the cause of the "missing infill on cabin transition layers" bug — features narrower than ~2× the extra offset collapsed entirely.
  2. Output is line segments, not closed contours. Each emitted Path is a polyline that the G-code generator strokes once. They are clipped to the boundary by utils::clip_lines_to_region; patterns themselves emit the raw, unclipped pattern across the boundary's bounding box.
  3. Density is a fraction in [0.0, 1.0]. 0.0 returns empty paths; values outside the range are clamped. The mapping from density to line spacing is pattern-specific.
  4. Layer rotation comes in as angle_offset (radians). Callers alternate this per layer so rectilinear / grid lines cross between layers. 3D patterns (gyroid, TPMS-D) ignore it and use z_height instead.

Pattern catalog

PatternFileDimensionalityStrengths
Rectilinearrectilinear.rs2D, per layerFastest; default
Gridgrid.rs2D, per layerStronger than rectilinear; same layer cost
Honeycombhoneycomb.rs2D, per layerBest 2D strength-to-weight
Gyroidgyroid.rs3D, uses zIsotropic; experimental
TpmsDtpms_d.rs3D, uses zTriply-Periodic Minimal Surface (diamond)

InfillPattern::parse accepts a few aliases (linearRectilinear, hexagonalHoneycomb, tpmsd / tpms_dTpmsD) for CLI ergonomics.


Anatomy


Role in the wider system

The infill module sits at the leaves of the pipeline. Everything upstream — surface detection, wall stripping, region calculation — exists so the boundary handed to generate_infill is exactly right.


Critical invariants

1. Do not inflate the input boundary

The block comment in generate_infill calls this out explicitly. A second inward offset inside this module collapses thin features and silently loses infill. If a pattern needs internal padding, it must do so in its own coordinate space, not by shrinking the boundary.

2. Pattern outputs are unclipped polylines

Per-pattern generators (generate_rectilinear, generate_grid, …) emit straight line segments across the boundary's bounding box without any clipping. clip_lines_to_region does the boolean intersection at the end. This keeps each pattern simple and pushes the Clipper2 dependency to one shared utility.

3. Scanline fill correctly handles holes

For boundaries containing CW hole sub-paths (annular cross-sections like a hollow box), the scanline algorithm in rectilinear relies on Clipper2's standard winding to produce the right parity of crossings — solid CCW outer ring + CW hole naturally toggles the even-odd count to skip the void. No special-casing is needed as long as the input is canonical Clipper2 output.


What this module deliberately does not do

  • No slicing. It doesn't know what a layer is; it operates on a single boundary at a time.
  • No solid-surface infill. Top/bottom solid lines are generated by core::surfaces using a dedicated rectilinear fill. They live there because the boundary calculation is different (per-region, with overlap).
  • No G-code. Output is Paths. The G-code generator strokes them.
  • No path optimisation. Travel ordering, line connecting, infill combing — all out of scope. The G-code generator does what little ordering exists today.

See also