routing.core.voxels#
Routing voxelizations: matrix-backed 3D grids with world/index conversions, morphology and components.
- class routing.core.voxels.RoomVoxelization(primitives: list[Primitive3D], voxel_matrix: NDArray[bool], voxel_size: float, voxel_origin: tuple[float, float, float], name: str = '')[source]#
Bases:
RoutingVoxelizationDeprecated: use
RoutingVoxelization, which merges this class withVoxelVolume.Construction shim only. This class keeps its historical argument order, which no longer matches the positional layout
MatrixBasedVoxelizationrebuilds instances with, so operations that reconstruct —extract_connected_matrix_based_voxelizations,crop_matrix, the boolean operators — must be called on aRoutingVoxelizationinstead.
- class routing.core.voxels.RoutingVoxelization(matrix: ndarray, origin: ndarray | tuple[float, float, float], voxel_size: float, name: str = '', primitives: list[Primitive3D] | None = None)[source]#
Bases:
MatrixBasedVoxelizationA class representing a 3D boolean grid of voxels, placed in space by an origin and a voxel size.
Carries the world/index conversions, the morphology operators and the connected components that every routing stage needs.
Truemarks the voxels the grid owns: matter for a voxelized shape, free space for a carved room. Which of the two it means comes from the factory that built it, not from the type.- Parameters:
matrix – The 3D numpy boolean array where True marks an owned voxel.
origin – The centre of voxel (0, 0, 0) in global coordinates.
voxel_size – The edge length of a voxel, in the same unit as the primitives.
name – The name to identify this voxelization. Defaults to ‘’.
primitives – The CAD view;
Nonedefers it to first access,[]states there is none.
- apply_closing(radius: int) RoutingVoxelization[source]#
Dilation then erosion. Fills small holes and gaps.
- apply_dilation(radius: int) RoutingVoxelization[source]#
Expand all occupied regions outward by
radiusvoxels.
- apply_erosion(radius: int) RoutingVoxelization[source]#
Shrink all occupied regions inward by
radiusvoxels.
- apply_opening(radius: int) RoutingVoxelization[source]#
Erosion then dilation. Removes small protrusions and noise.
- biggest_component() RoutingVoxelization[source]#
Get the connected component holding the most voxels.
- property border_centers: ndarray#
World centres of the border voxels, as an
(N, 3)array.
- border_contact_pairs(other: RoutingVoxelization) ndarray[source]#
Pairs of border-voxel centres of the two grids lying within one voxel step of each other.
- Returns:
Array of shape
(N, 6)where each row is[x1, y1, z1, x2, y2, z2].
- property border_indices: ndarray#
Indices of the border voxels — those not fully surrounded by matter, as an
(N, 3)array.
- cleaned(opening_radius: int = 0, closing_radius: int = 0, erosion_radius: int = 0) RoutingVoxelization[source]#
Apply opening, then closing, then erosion;
selfunchanged when all radii are 0 (no allocation).
- clip_coord(coordinates: ndarray) ndarray[source]#
Clip a point onto the closest voxel centre inside the grid.
- component_carrying(point: ndarray) RoutingVoxelization[source]#
Get the connected component occupied at
pointexactly; the whole voxelization, logged, when none is.The strict sibling of
component_containing(): no quantization slack, and a point on no component falls back to the whole voxelization instead of raising.
- component_containing(point: ndarray, tolerance_voxels: int = 2) RoutingVoxelization[source]#
Return the connected component carrying the occupied voxel nearest to
point.- Parameters:
point – The world position to seed the search from.
tolerance_voxels – How far
pointmay penetrate the carved material (voxel-quantization slack); beyond that it is genuinely unroutable and this raises rather than selecting a wrong domain.
- component_labels(components: list[RoutingVoxelization]) ndarray[source]#
Label each voxel of this grid with the index of the component covering it,
-1elsewhere.- Parameters:
components – Voxelizations lying on this grid, typically its connected components.
- property coords: ndarray#
world_coords()transposed to(3, n_voxels).
- copy(deep: bool = True, memo: dict | None = None) RoutingVoxelization[source]#
Deep copy of the voxel matrix;
originandvoxel_sizeare shared (immutable).- Parameters:
deep – If False, perform a shallow copy. If True, perform a deep copy.
memo – A dict that keep track of references.
- static covering_frame(bounds: ndarray, voxel_size: float, dilation_voxels: int, origin: ndarray | None = None) VoxelFrame[source]#
Frame of the grid covering
bounds, padded to survive adilation_voxelsdilation.- Parameters:
bounds – World points the grid must contain, as any array of
(x, y, z)rows.voxel_size – The edge length of a voxel.
dilation_voxels – The dilation the padding must leave room for.
origin – Anchor the grid on an existing one;
Nonesnaps a fresh origin onto whole voxel steps, so repeated round-trips do not accumulate sub-voxel drift.
- crossed_indices(start_position: ndarray, end_position: ndarray) ndarray[source]#
Return the voxel indices crossed by the
[start, end]segment (Bresenham), dropping out-of-grid ones.
- display(title: str = '', overlay_prims: list[Primitive3D] | None = None, decimation: int = 0, color: tuple[float, float, float] = (0.5, 0.5, 0.5), alpha: float = 0.4) None[source]#
Visualize this volume as small box primitives, with optional overlay.
- classmethod enclosing(routing_shapes: list[RoutingShape], voxel_size: float, dilation_voxels: int) RoutingVoxelization[source]#
Voxelize
routing_shapeson a fresh grid auto-sized to enclose them.
- filled_with(matrix: ndarray) RoutingVoxelization[source]#
Get the same grid under the same name, carrying
matrixas its matter.
- property frame: VoxelFrame#
The chart this grid is sampled on.
- classmethod from_routing_shapes(routing_shapes: list[RoutingShape], frame: VoxelFrame, dilation_voxels: int) RoutingVoxelization[source]#
Let every shape write itself into one shared
framegrid, each touching only the cells it occupies.
- classmethod from_voxels(voxels: MatrixBasedVoxelization, build_cad: bool = False) RoutingVoxelization[source]#
Adopt an existing voxelization, keeping its origin, its voxel size, its matter and its name.
- Parameters:
voxels – The voxelization to adopt.
build_cad – Rebuild the CAD view now; otherwise the result states it has none, rather than deferring an expensive mesh build to the first attribute access.
- property homogeneous_indices: ndarray#
indiceswith a row of ones appended, as a(4, n_voxels)array.
- homogeneous_indices_in_box(lower: ndarray, upper: ndarray) ndarray[source]#
homogeneous_indicesrestricted to the[lower, upper)index box, computed on that window only.
- property indices: ndarray#
voxel_indicestransposed to(3, n_voxels).
- indices_are_active(indices: list[tuple[int, int, int]] | ndarray) bool[source]#
Whether every index in
indicesis in-grid and occupied.
- indices_to_world(indices: ndarray) ndarray[source]#
Convert integer or fractional voxel indices,
(3,)or(N, 3), to world coordinates.
- is_adjacent_to(other: RoutingVoxelization) bool[source]#
Whether the two voxelizations touch: some pair of border voxels lies within one voxel step.
- labelled_components() tuple[ndarray, int][source]#
Label each voxel with the index of its connected component,
-1off matter, and count the components.The 6-connectivity and the numbering of
extract_connected_matrix_based_voxelizations(), without materialising one voxelization per component.
- maximal_boxes(min_fill: float = 1.0) list[dict[str, tuple[int, int]]][source]#
Greedy cover of the occupied voxels by maximal index boxes, each box carved out as it is taken.
Every seed is grown along the six axis orders and the largest box wins, so a cluttered room yields fewer, fatter boxes — which is the point: a
SmartGridallocates its node container over the whole box, so a box that is all matter wastes nothing.- Parameters:
min_fill – Smallest share of free matter in every slab a box grows by: 1 keeps the boxes all matter; below, a box takes in some empty space and the room splits into fewer boxes. A box never takes a cell of another, empty or not, so the boxes never overlap.
- nearest_active_center(point: ndarray, tolerance_voxels: int | None) ndarray[source]#
Centre of the occupied voxel nearest to
point.- Parameters:
point – The world position to search from.
tolerance_voxels – Search budget: the nearest centre within a
±tolerance_voxelswindow, raising beyond it.Noneremoves the budget — the window grows until the nearest occupied voxel of the whole grid is found (raising only on an empty grid).
- nesting_in(coarser: RoutingVoxelization) tuple[int, ndarray][source]#
Get the two numbers mapping a
coarservoxel onto the block of this grid’s voxels it covers.Together they are the conversion: the block covering coarse voxel
(i, j, k)starts atoffset + (i, j, k) * ratiohere and spansratiovoxels per axis. The multi-scale counterpart ofoffset_in(), which needs only the offset because both grids share a voxel size.A coarser voxel must cover a whole block and sit centred on it, so ratio and offset both have to land on integers; anything else means the two grids were not built from one another and the caller is about to read the wrong voxels.
- Parameters:
coarser – The grid this one nests into, of a larger voxel size.
- Returns:
The size ratio, and the index here at which
coarser’s first voxel starts.
- offset_in(parent: RoutingVoxelization) ndarray[source]#
Integer index offset of this grid’s origin inside
parent.
- property origin: ndarray#
Grid origin as ndarray (alias for
min_grid_center).
- property primitives: list[Primitive3D]#
CAD primitives reconstructed from voxels, computed on first access.
- refined_box(bounds: dict[str, tuple[int, int]], factor: int) RoutingVoxelization[source]#
Build a fully-occupied voxelization,
factortimes finer, covering the inclusive index boxbounds.- Parameters:
bounds – Per-axis inclusive
(lowest, highest)indices on this grid.factor – How many fine voxels a voxel of this grid spans per axis.
- restrict_field(parent: RoutingVoxelization, field: NDArray[float64]) NDArray[float64][source]#
Sample a field defined on
parent’s grid over this sub-grid: parent values on occupied voxels,infelsewhere.selfmust lie onparent’s grid — the relationintersection()withparentproduces (same voxel size, integer offset). Occupied voxels ofselfare then a subset ofparent’s, so each maps to a finitefieldentry; theinffill only reaches unoccupied voxels, which carry no value by convention.
- sample_segment(start_position: ndarray, end_position: ndarray, samples_per_voxel: float = 2.0) ndarray[source]#
Sample the
[start, end]segment atsamples_per_voxeldensity (>= 2 samples, endpoints included).
- set_distances_direct(distances_array: ndarray) ndarray[source]#
Scatter one distance per occupied voxel into a full grid,
infon the unoccupied ones.
- property shape: tuple#
Voxel-grid dimensions (i, j, k).
- sub_voxelization(bounds: dict[str, tuple[int, int]], copy: bool = True) RoutingVoxelization[source]#
Extract the voxelization spanning the inclusive index box
bounds, re-anchored on its own origin.- Parameters:
bounds – Per-axis inclusive
(lowest, highest)indices.copy – Copy the extracted block.
Falseshares memory with this grid’s matrix — cheaper for a short-lived window, but it keeps the whole parent matrix alive.
- subtract_coarser(coarser: RoutingVoxelization) RoutingVoxelization[source]#
Clear every voxel covered by an occupied voxel of
coarser— a multi-scale boolean difference.Each occupied coarse voxel clears the whole block of this grid’s voxels it covers, following the block mapping of
nesting_in().
- to_matrix_based_voxelization() MatrixBasedVoxelization[source]#
Downcast to the plain volmdlr voxelization, dropping every routing-side attribute.
- touching_border_pairs(other: RoutingVoxelization) ndarray[source]#
Pairs of border voxels of the two grids lying within one voxel step of each other.
The predicate of
is_adjacent_to(), evaluated for every pair at once: grids whose bounding boxes are apart touch nowhere, and otherwise each side’s border is first reduced to the shell the other side’s border can reach (_border_facing()), so the distance search only ever sees the contact, whatever the size of the grids.- Returns:
Array of shape
(K, 2): rows ofself.border_indicesand ofother.border_indices.
- voxel_boxes(centers: ndarray | None = None, scale: float = 1.0, color: tuple[float, float, float] = (0.5, 0.5, 0.5), alpha: float = 0.8, decimation: int = 1) list[Solid][source]#
Render voxels as cube solids of side
scale * voxel_size— the inspection view of the grid.Distinct from the inherited
volmdlr_primitives(), which returns one surface mesh for the whole volume: here each voxel stays a cube, so the discretization itself is visible.- Parameters:
centers – World centres to draw;
Nonedraws this volume’s own occupied voxels.scale – Cube side as a multiple of the voxel size, to make markers stand out.
decimation – Keep one centre every
decimation, to stay under a viewable count.
- property voxel_count: int#
Number of occupied voxels.
- property voxel_origin: tuple[float, float, float]#
Grid origin as tuple (alias for
min_grid_center).
- within_bounding_box(bounding_box: BoundingBox) RoutingVoxelization | None[source]#
Keep the occupied voxels whose centres fall inside
bounding_box, cropped to them;Nonewhen empty.
- world_coords() ndarray[source]#
World coordinates of the occupied voxel centres, as an
(n_voxels, 3)array.
- class routing.core.voxels.VoxelVolume(*args, **kwargs)[source]#
Bases:
RoutingVoxelizationDeprecated: use
RoutingVoxelization, which merges this class withRoomVoxelization.