pub struct Voxels { /* private fields */ }Expand description
A mesh rasterised onto a grid of cubes.
The grid is the mesh’s own bounding box, grown to whole cells, with the shape sitting inside it. Cells are inside or outside — there is no partial cell, because the domains this feeds have no partial cell either.
Implementations§
Source§impl Voxels
impl Voxels
Sourcepub fn of(mesh: &Mesh, cell: Length) -> Result<Voxels, String>
pub fn of(mesh: &Mesh, cell: Length) -> Result<Voxels, String>
Rasterise mesh onto cubes of side cell.
§By scanline, and why the crossings are counted a row at a time
A point-in-mesh test per cell would cast one ray per cell and cost cells × triangles. Casting one
ray along x for each (j, k) row and filling between sorted pairs of crossings costs
rows × triangles — the same answer for a factor of nx less work, and it is not only faster: a
whole row decided from one sorted list of crossings cannot disagree with itself about parity,
where per-cell tests can and do near a surface.
§Degenerate rays are detected, perturbed deterministically, and then admitted
Detected, and that word is doing the work. Parity is not enough to find them. A ray through the edge two triangles share hits both, so the count stays even and the two crossings coincide; the row pairs them against each other, fills nothing, and reports success. A cube loses a whole diagonal plane of rows to this at every resolution — 64 cells of 512 on an eight-cell cube — with no error anywhere. So a hit on an edge is a case of its own, distinct from both a miss and a clean crossing, and a row that produces one is retried whatever its parity says.
The retry moves the ray, and the offsets are fixed — a short list of small multiples of the cell — so the same mesh and cell size give bit-for-bit the same voxels on every platform and at every optimisation level, which is a promise the whole workspace makes.
A row still degenerate or still odd after all of them is left empty and counted in
Loss::ambiguous_rows. Empty is the honest failure: a hole is visible, and a row filled by
guessing which crossing to drop is a wrong shape that looks right.
Refuses an open mesh, because parity has no meaning through a surface with a hole in it.
Sourcepub fn onto(
mesh: &Mesh,
origin: LengthVec,
counts: (usize, usize, usize),
cell: Length,
) -> Result<Voxels, String>
pub fn onto( mesh: &Mesh, origin: LengthVec, counts: (usize, usize, usize), cell: Length, ) -> Result<Voxels, String>
Rasterise mesh onto a grid somebody else chose: a stated origin, cell count and cell.
This is what assembly needs. Voxels::of gives every mesh its own box and its own
origin, so two parts come back on two grids that share no cell and cannot be adjacent to
each other — ARCHITECTURE.md names that as the gap that arrives first in practice.
Rasterised onto one grid, two parts occupy neighbouring cells of the same array, and a
domain that fills from both gets a conducting interface between them for free, because
its stencil already crosses a face between cells of different materials.
The grid is the caller’s and the mesh’s coordinates are read as they are written: an STL
carries absolute positions, so where two parts sit relative to each other is what their
files already say. No pose is applied here — Pose places a domain, and both parts are
inside one domain now.
A mesh that does not fit is refused, with both boxes named. Cropping it silently is the failure this workspace keeps finding: a part with its corner cut off runs, audits, renders and answers a question about a different shape.