routing.core.geometry#

Routing geometry primitives: the voxel frame (world/grid chart) and shapes that voxelize themselves into it.

class routing.core.geometry.Box(minimums: ndarray, maximums: ndarray)[source]#

Bases: RoutingShape

A class to handle boxes in routing context.

property bounding_box: ndarray#

Box vertices as [minimums, maximums].

property center: ndarray#

Box geometric center.

contains(points: ndarray) → ndarray[source]#

Whether each of points (n, 3) lies in the box, its faces included.

classmethod cubes_at(centers: ndarray, edge: float, color: tuple = (0.5, 0.5, 0.5), alpha: float = 1.0) → list[Solid][source]#

Render one cube solid of side edge centred on each of centers.

Parameters:
  • centers – World centres, as any array of (x, y, z) rows.

  • edge – The side of every cube.

classmethod from_bounding_box(bounding_box: ndarray) → Box[source]#

Create a box from its extreme vertices.

classmethod from_points(points: ndarray) → Box[source]#

Create a box from points.

grown(margin: float | ndarray) → Box[source]#

Give the box widened by margin on each of its six faces, one value per axis or one for all.

maximums: ndarray#
meets(boxes: ndarray) → ndarray[source]#

Whether each of boxes, (n, 2, 3) lowest and highest corners, shares a point with this box.

minimums: ndarray#
property origin: ndarray#

Box origin (min corner).

property points: ndarray#

All box’s points.

property size: ndarray#

Box extent along each axis.

property size_x: float#

Box extent along x.

property size_y: float#

Box extent along y.

property size_z: float#

Box extent along z.

volmdlr_primitive(color: tuple = (0.5, 0.5, 0.5), alpha: float = 1.0) → Solid[source]#

Render this box as a volmdlr CAD solid.

property volume: float#

The volume of the box.

voxelize_into(volume: ndarray, frame: VoxelFrame, dilation_radius: float = 0) → None[source]#

Fill the index block the box spans, widened by dilation_radius and clipped to the grid.

Parameters:
  • volume – The grid to write into.

  • frame – The chart volume is sampled on.

  • dilation_radius – Extra half-width in voxel units (expands the filled index range).

class routing.core.geometry.Cylinder(start: ndarray, end: ndarray, radius: float)[source]#

Bases: RoutingShape

A class to handle cylinders in routing context.

ball_radius(voxel_size: float, dilation_radius: float = 0) → int[source]#

Integer thickening radius in voxels: ceil(radius + dilation).

The thickening ball needs an INTEGER radius: a non-integer yields an even-sized footprint with no centre voxel, so thickening shifts the tube half a voxel off the fiber and brings the skeletonization vanish straight back.

property bounding_box: ndarray#

Get the bounding box of the cylinder.

end: ndarray#
fiber_indices(frame: VoxelFrame) → ndarray[source]#

Voxel-snapped Bresenham rasterization of the axis (neutral fiber), clipped to the grid.

property length: float#

Cylinder axis length.

radius: float#
start: ndarray#
volmdlr_primitive(color: tuple = (0.5, 0.5, 0.5), alpha: float = 1.0) → Cylinder[source]#

Render this cylinder as a volmdlr CAD primitive.

voxelize_into(volume: ndarray, frame: VoxelFrame, dilation_radius: float = 0) → None[source]#

Rasterize the axis (neutral fiber) via Bresenham, then thicken it into an exact-Euclidean-ball tube.

class routing.core.geometry.OrientedBox(origin: ndarray, axes: ndarray, extent: Box)[source]#

Bases: RoutingShape

A box lying in a frame of its own: the tightest box around points, seen from there.

An oriented box is an aligned one seen from its frame, so it carries that frame and the Box it spans in it. A shape lying along a diagonal keeps a tight box, where a world-aligned one would swallow the space around it.

Parameters:
  • origin – Where the frame sits in the world.

  • axes – The frame’s three unit axes, as columns.

  • extent – The box the shape spans in that frame.

axes: ndarray#
bounding_box() → ndarray[source]#

Axis-aligned bounding box of the eight vertices, as [minimums, maximums].

cells(frame: VoxelFrame, dilation_radius: float = 0) → ndarray[source]#

Give the cells of frame whose centre lies in the box, widened by dilation_radius voxels.

The candidates are read off the aligned block the box spans, so an oblique box costs its own neighbourhood and not the grid.

contains(points: ndarray) → ndarray[source]#

Whether each of points (n, 3) lies in the box, its faces included.

extent: Box#
classmethod from_points(points: ndarray, origin: ndarray, axes: ndarray) → OrientedBox[source]#

Build the tightest box around points in the frame (origin, axes).

grown(margin: float | ndarray) → OrientedBox[source]#

Give the box widened by margin on each of its six faces, one value per axis or one for all.

local_coordinates(points: ndarray) → ndarray[source]#

Give points in the box’s own frame.

origin: ndarray#
property points: ndarray#

The box’s eight vertices, in the world.

voxelize_into(volume: ndarray, frame: VoxelFrame, dilation_radius: float = 0) → None[source]#

Fill the cells whose centre lies in the box, widened by dilation_radius voxels.

class routing.core.geometry.Polyline(points: ndarray)[source]#

Bases: RoutingShape

An ordered sequence of points forming a broken line in 3D.

property bounding_box: ndarray#

Polyline AABB as [minimums, maximums].

distances_to(points: ndarray) → ndarray[source]#

Min distance from each of points to this polyline.

property length: float#

Sum of segment lengths.

local_angles(tangent_window: int) → ndarray[source]#

Per-vertex bending angle measured over a tangent_window-step neighborhood.

Returns an array of length len(points). NaN at vertices closer than tangent_window from the endpoints (no back/forward tangent available) or where back/forward is degenerate (zero-length).

points: ndarray#
smoothed(window: int) → Polyline[source]#

Moving-average smoothing with endpoints preserved.

to_cylinders(radius: float = 1e-06) → list[Cylinder][source]#

One cylinder per segment, all of radius; degenerate segments are dropped.

Parameters:

radius – A polyline carries no thickness of its own, so this states the scale it should be seen at; the default draws it as a hairline.

turn_indices(tangent_window: int, min_turn_angle: float) → list[int][source]#

Interior indices where local_angles() peaks above min_turn_angle.

Detected turns are at least tangent_window apart so a single messy corner yields a single cut.

voxelize_into(volume: ndarray, frame: VoxelFrame, dilation_radius: float = 0) → None[source]#

Rasterize each segment via Bresenham, then dilate over the polyline’s own bounding window.

class routing.core.geometry.RoutingShape[source]#

Bases: ABC

A class to handle shapes in routing context.

abstractmethod bounding_box() → ndarray[source]#

Axis-aligned bounding box as [minimums, maximums].

voxelize(frame: VoxelFrame, dilation_radius: float = 0) → ndarray[source]#

Boolean mask of the frame voxels intersecting the shape, on a grid of its own.

abstractmethod voxelize_into(volume: ndarray, frame: VoxelFrame, dilation_radius: float = 0) → None[source]#

Add this shape’s voxels to volume, sampled on frame, touching only the cells the shape occupies.

class routing.core.geometry.VoxelFrame(origin: tuple[float, float, float], voxel_size: float, shape: tuple[int, int, int])[source]#

Bases: object

Where a voxel grid sits in space: its origin, its step and its extent.

The one chart between world coordinates and grid indices, shared by everything shaped like a grid — a voxelization’s matrix, a pathfinding grid’s nodes, a region’s fields. Its distinct roundings are distinct operations: the voxel nearest to a point (nearest_indices()), the exact fractional position (fractional_indices()), and the covering bounds of a world box (floor_indices() / ceil_indices()).

Parameters:
  • origin – The centre of voxel (0, 0, 0) in world coordinates.

  • voxel_size – The edge length of a voxel, in world units.

  • shape – The grid extent in voxels per axis.

ceil_indices(points: ndarray) → ndarray[source]#

Lowest voxel indices at or above world points — the upper bound of a covering window.

cells_covered_by(coarser: VoxelFrame, indices: ndarray) → ndarray[source]#

Give the cells of this frame whose centre lies in the voxels indices (N, 3) of the coarser frame.

The multi-scale map anchors its grids on one centre and refines by an odd factor, so a coarse voxel covers a block of cells centred on its own centre; a frame covers itself cell for cell. Not clipped.

center(index: tuple[int, int, int] | ndarray) → tuple[float, float, float][source]#

World centre of the single voxel at index, as a plain float tuple.

centers(indices: ndarray) → ndarray[source]#

World centres of the voxels at integer or fractional indices, (3,) or (N, 3).

contains(points: ndarray) → ndarray[source]#

Whether world points, (3,) or (N, 3), fall inside the grid’s extent — the outer faces of its boundary voxels.

cylindrical_window(start: ndarray, end: ndarray, half_width: float) → tuple[ndarray, ndarray, ndarray][source]#

Dense grid window around the [start, end] segment, in cylindrical coordinates.

Covers the axis-aligned box enclosing the segment inflated by half_width, clipped to the grid. Radial distances and abscissas are separable along the grid axes, so no per-voxel point array is ever built.

Returns:

Tuple (lower (3,) int, radial_distances (ni, nj, nk), abscissas (ni, nj, nk)) — the window’s lower corner index and, per window voxel, the world distance to the segment’s supporting line and the world abscissa along it (0 at start). Empty arrays when the window misses the grid.

floor_indices(points: ndarray) → ndarray[source]#

Highest voxel indices at or below world points — the lower bound of a covering window.

fractional_indices(points: ndarray) → ndarray[source]#

Exact grid position of world points, (3,) or (N, 3), without rounding to a voxel.

indices_around(center: ndarray, radius: float) → ndarray[source]#

List the grid’s voxels within radius (in voxels) of the fractional index center, in C order.

nearest_index(point: tuple[float, float, float] | ndarray) → tuple[int, int, int][source]#

Index of the single voxel containing point, as a plain int tuple.

nearest_indices(points: ndarray) → ndarray[source]#

Convert world points, (3,) or (N, 3), to the indices of the voxels containing them.

classmethod of(voxelization: MatrixBasedVoxelization) → VoxelFrame[source]#

Read the frame a voxelization is sampled on.

origin: tuple[float, float, float]#
shape: tuple[int, int, int]#
voxel_size: float#