routing.core.cadmap#
CadMap class and associated functions.
- class routing.core.cadmap.CadMap(design_rules: list[InstanceOf[DesignRule]], ruled_volumes: list[RuledVolume], bounding_box: RuledVolume | None = None, exact_distances: bool = False, weights_operator: str = 'min', room_voxels: RoutingVoxelization | None = None, distances: list[CompressedArray | ndarray] | None = None, weights: list[CompressedArray | ndarray] | None = None, voxel_primitives: list[Primitive3D] | None = None, name: str = '')[source]#
Bases:
DessiaObject,Displayable3DA class representing the central mapping object for routing that manages volumes, voxelization, and routing state.
The core data structure that combines ruled volumes with design rules, voxelized space representation, distance and weight matrices for pathfinding, pipe history and packs for optimization, and pooling rules for pipe grouping.
- Parameters:
design_rules – The list of DesignRule objects applied globally to all volumes.
ruled_volumes – The list of RuledVolume objects defining the routing space.
bounding_box – The optional RuledVolume defining the overall problem boundaries. If None, computed automatically from ruled_volumes with in_border=True. Defaults to None.
exact_distances – Flag indicating whether to use exact mesh-based distance calculation (slower but more accurate). Defaults to False.
weights_operator – The operator for combining weights from multiple volumes (‘min’, ‘max’, or ‘sum’). Defaults to ‘min’.
room_voxels – The optional RoutingVoxelization representing the traversable space. Defaults to None.
distances – The optional list of compressed distance matrices, one per ruled volume. Defaults to None.
weights – The optional list of compressed weight matrices, one per ruled volume. Defaults to None.
voxel_primitives – The optional list of primitives for voxel visualization. Defaults to None.
name – The name to identify this CadMap. Defaults to ‘’.
- apply_pooling_coefficients(coefficients: ndarray, forbidden_voxels: ndarray) ndarray[source]#
Bend the weight tensor to the pooling zones the packs painted.
- apply_zone_weights() None[source]#
Let each weighted zone reweight the voxels it covers, on every region.
- classmethod build_bounding_volume(ruled_volumes: list[RuledVolume], border_rules: list[InstanceOf[DesignRule]] | None = None, voxel_size: float = 1.0, voxel_margin: float = 0.0) RuledVolume[source]#
Build a bounding volume to voxelize CadMap only in volume of interest.
This method has to be explicitly called to be run.
- classmethod build_bounding_volume_light(ruled_volumes: list[RuledVolume], weighting_rules: list[WeightingRule] | None = None, voxel_size: float = 1.0, voxel_margin: float = 0.0) RuledVolume[source]#
Build a bounding volume (lightweight version using weighting rules).
Convenience method that calls build_bounding_volume with weighting_rules as border_rules. This is a simplified interface for common use cases.
- Parameters:
ruled_volumes – List of RuledVolume objects to compute bounding box from.
weighting_rules – Optional list of WeightingRule objects to apply as border rules. Defaults to None.
voxel_size – Voxel size for voxelizing the bounding box. Defaults to 1.0.
voxel_margin – Additional margin in voxel units to add to bounding box. Defaults to 0.0.
- Returns:
RuledVolume representing the bounding box.
- build_bundle_graph(axis_voxels: dict[tuple[int, int, int, int], tuple[int, int, int, int]], solutions: list[Solution] | None = None) Graph[source]#
Build the bundle (passage) graph from routed solutions and
axis_voxels.Each graph node is a 4-tuple
(x, y, z, grid_id)representing a voxel (or its pack-axis representative fromaxis_voxels). Edges connect consecutive samples along each solution path (including port voxel steps); edge attributedistancestores the Euclidean step length in world units.Uses
self.room_voxels.voxel_sizeandself.room_voxels.min_grid_centerfor voxel indexing and node coordinates.- Parameters:
axis_voxels – Map
(x, y, z, grid_id) ->representative 4-tuple.solutions – Solutions whose
best_pathis traversed. When None, usesself.solutions.
- Returns:
NetworkX graph of bundle passage nodes and segment edges.
- build_connectivity_graph(apply_weights: bool = False) tuple[Graph, dict[int, ndarray]][source]#
Build the adjacency graph of the connected components across every region.
Each node is a connected component of one region, each edge a spatial adjacency between components of two regions: some pair of their border voxels lies within one voxel step (
region_contacts), both voxels kept by the weights whenapply_weights.- Parameters:
apply_weights – Drop the voxels the weight tensor zeroed out before splitting.
- Returns:
The graph, whose nodes are
(region_index, component_index), and the per-region matrix labelling each voxel with its component index.
- build_pooling_matrices(types: list[str]) None[source]#
Build pooling matrices for PoolingRule to work.
Initializes the distance and coefficient matrices in the pooling rule for all pipe type combinations. These matrices define: - Minimum distances required between different pipe types - Pooling coefficients that modify routing costs when pipes are grouped
This method must be called before using pooling features in routing. The matrices are built for all combinations of pipe types in the provided list.
- Parameters:
types – List of pipe type identifiers (strings) to build matrices for. All pairwise combinations of these types will have entries in the matrices.
- Returns:
None
- property cad_distance_field: ndarray#
Per-voxel exact distance to the nearest CAD surface, read off the single region.
- clearance_along(start_position: ndarray, end_position: ndarray, samples_per_voxel: float = 2.0) ndarray[source]#
CAD clearance (
clearance_at()) along the[start, end]segment atsamples_per_voxeldensity.
- clearance_at(positions: ndarray) ndarray[source]#
Best available CAD clearance at continuous world
positions, across every region.A region’s field reads zero clearance outside its own active voxels (see
VoxelRegion.cad_distance_field), so wherever regions overlap — a multi-scale map’s fine region is blind exactly where the coarse one sees — each regional read is a lower bound of the true clearance. The elementwise maximum over the regions whose grid contains the query point is therefore the tightest estimate the map can give. Points outside every region fall back to the main region’s edge-clamped read.
- collect_packs(only_optimized: bool = True) list[Pack][source]#
Collect all best_pipe.pipe_lines in all generated packs for defining a Pack with its best pipe_lines.
- compose_weights() ndarray[source]#
Reduce the per-volume weight fields into the single tensor the pathfinder reads.
See
VoxelRegion.compose_weights()for the semantics of eachweights_operator.
- compute_distances(voxelization: MatrixBasedVoxelization)[source]#
Compute distance matrices for all ruled volumes in the CadMap.
Computes distance from each voxel to each ruled volume surface using the CadMap’s exact_distances setting. The bounding box is included if it has design rules.
- Parameters:
voxelization – The MatrixBasedVoxelization to compute distances for.
- Returns:
List of compressed distance arrays, one per volume.
- compute_weights() list[CompressedArray][source]#
Turn each distance field into a weight field by applying its volume’s design rules.
- decomposed(multi_scale: bool = False, size_factor: int = 3, bounding_box: BoundingBox | None = None, min_fill: float = 1.0) CadMap[source]#
Cut this map’s routing space into several regions, and get the map carrying them.
Without
multi_scalethe room is cut into maximal index boxes at the current voxel size; with it, a gridsize_factortimes finer completes the coarse one where it is blind.- Parameters:
bounding_box – Optional box limiting where the finer grid is built.
min_fill – Without
multi_scale, the smallest share of matter in every slab a box grows by (RoutingVoxelization.maximal_boxes()): 1 cuts all-matter boxes, below fewer boxes holding empty space.
- diagnose_pack_field(pack: Pack, pipe_type: PipeType, region_index: int = 0) None[source]#
Print the exact counts and distances behind
pack_field_primitives().
- property distances: list[CompressedArray] | None#
The compressed distance fields, one per ruled volume, read off the single region.
- property finest_voxel_size: float#
The finest voxel size in use, which sets the resolution the world resolves shapes at.
- property forced_zones: list[ForcedZone]#
The zones every route must pass through.
- classmethod from_cad_maps(cad_maps: list[CadMap], name: str = '') CadMap[source]#
Assemble maps sampled at the same voxel size into one map carrying all their regions.
- classmethod from_project_inputs(design_rules: list[InstanceOf[DesignRule]], ruled_volumes: list[RuledVolume], bounding_box: RuledVolume = None, voxel_size: float = 0.1, exact_distances: bool = False, weights_operator: str = 'min', build_cad: bool = False, name: str = '', crop_point: ndarray | None = None) CadMap[source]#
Build a CadMap from project inputs.
This is the main factory method for creating a CadMap from raw project data. It performs the complete initialization workflow: 1. Creates a CadMap instance with design rules and ruled volumes 2. Voxelizes the empty space (room) between volumes 3. Computes distance matrices from each volume to all voxels 4. Computes weight matrices by applying design rules to distances 5. Optionally builds CAD primitives for visualization
The resulting CadMap is ready for pathfinding operations after calling to_grid().
- Parameters:
design_rules – List of DesignRule objects that are common to all designed objects. Examples include TurnPenalizer, WeightingRule, etc. These rules are applied globally to all volumes.
ruled_volumes – List of RuledVolume objects representing material volumes (obstacles) in the routing space. A RuledVolume is essentially a VolumeModel with associated DesignRules.
bounding_box – Optional RuledVolume defining the BoundingBox of the voxelization domain. If None, computed automatically from volumes with in_border=True. Defaults to None.
voxel_size – Size of each voxel in length units. Smaller values give higher precision but require more memory and computation time. Defaults to 0.1.
exact_distances – Flag indicating distance computation method. If True, computes exact distances from triangle meshes (slower but more accurate). If False, uses faster voxel-based approximation. Defaults to False.
weights_operator – Method to compose weights from multiple volumes. Options: “min”, “max”, “sum”, “mean”, “min_distance”, “max_distance”. Note: Only “min_distance” typically gives expected results for routing. Defaults to “min”.
build_cad – Flag indicating whether to build CAD primitives for voxel visualization. If True, creates Box primitives for each voxel (memory intensive for large grids). Defaults to False.
name – Name identifier for this CadMap. Used for debugging and serialization. Defaults to ‘’.
crop_point – Optional 3D point. When provided, the connected component containing this point is kept (via
RoutingVoxelization.component_containing()) instead of the largest one. Defaults to None.
- Returns:
Fully initialized CadMap instance ready for routing operations.
- classmethod from_project_inputs_light(ruled_volumes: list[RuledVolume], weighting_rules: list[WeightingRule], pooling_rule: PoolingRule, turn_penalizer: TurnPenalizer, bounding_box: RuledVolume = None, voxel_size: float = 0.1, name: str = '') CadMap[source]#
Build a CadMap from project inputs (lightweight/convenience version).
This is a convenience wrapper around from_project_inputs() that uses recommended default settings for common routing scenarios: - exact_distances=True (more accurate distance computation) - weights_operator=”min_distance” (recommended for routing) - build_cad=True (enable visualization)
It automatically combines the provided design rules (weighting_rules, pooling_rule, turn_penalizer) into a single design_rules list.
Use this method when you want a fully configured CadMap with standard routing settings without specifying all parameters explicitly.
- Parameters:
weighting_rules – List of WeightingRule objects that define how routing costs vary with distance to CAD volumes. These rules convert distances to weights (costs) for pathfinding.
pooling_rule – PoolingRule object for managing pooling and segregation between conduits. Defines which pipe types can be grouped together and minimum distances between them.
turn_penalizer – TurnPenalizer object that adds cost to bends in the routing path. Encourages straighter paths.
ruled_volumes – List of RuledVolume objects representing material volumes (obstacles) in the routing space. A RuledVolume is essentially a VolumeModel with associated DesignRules.
bounding_box – Optional RuledVolume defining the BoundingBox of the voxelization domain. If None, computed automatically from volumes with in_border=True. Defaults to None.
voxel_size – Size of each voxel in length units. Smaller values give higher precision but require more memory. Defaults to 0.1.
name – Name identifier for this CadMap. Used for debugging and serialization. Defaults to ‘’.
- Returns:
Fully initialized CadMap instance with recommended settings, ready for routing operations.
- classmethod from_voxelization(design_rules: list[InstanceOf[DesignRule]], ruled_volumes: list[RuledVolume], voxelization: MatrixBasedVoxelization, weights_operator: str = 'min', exact_distances: bool = True, build_cad: bool = False, distances: list[ndarray] | None = None) CadMap[source]#
Build a CadMap from a pre-existing voxelization.
This method creates a CadMap when you already have a MatrixBasedVoxelization (e.g., from a previous computation or external source). It skips the voxelization step and directly uses the provided voxelization.
The method: 1. Creates a bounding box from the voxelization’s bounding box 2. Creates a RoutingVoxelization from the MatrixBasedVoxelization 3. Computes or uses provided distance matrices 4. Computes weight matrices from distances 5. Composes weights into a single tensor
This is useful when: - Reusing voxelizations from previous computations - Working with voxelizations from external sources - Avoiding redundant voxelization computations
- Parameters:
design_rules – List of DesignRule objects to apply globally. These rules affect how weights are computed from distances.
ruled_volumes – List of RuledVolume objects representing material volumes. These are used for distance computation and weight calculation.
voxelization – Pre-computed MatrixBasedVoxelization representing the traversable space. Must be compatible with the ruled volumes.
weights_operator – Method to compose weights from multiple volumes. Options: “min”, “max”, “sum”, “mean”, “min_distance”, “max_distance”. Defaults to “min”.
exact_distances – Flag indicating distance computation method when
distancesis not provided. Defaults to True.build_cad – Flag indicating whether to build CAD primitives for voxel visualization. If True, creates Box primitives for each voxel. Defaults to False.
distances – Optional list of pre-computed distance matrices (numpy arrays). If provided, these are compressed and used directly. If None, distances are computed from ruled volumes using
exact_distances. Defaults to None.
- Returns:
Fully initialized CadMap instance using the provided voxelization.
- get_axis_voxels_from_packs() dict[tuple[int, int, int, int], tuple[int, int, int, int]][source]#
Compute axis voxel mapping from all packs (fixation / alignment along pack axis).
Every pack painting a region projects its axis tube on that region’s grid — a generic pack on each region its window reaches, a tray on its own region only.
- Returns:
Dictionary mapping each voxel 4-tuple (x, y, z, region_index) to its representative 4-tuple.
- get_bifurcation_points(packs: list[Pack], tolerance: float | None = None, with_ports: bool = False) list[ndarray][source]#
Get all bifurcation points of the pack, at
tolerance(default: two coarse voxels).
- get_packs_zones_coefficients(pipe_type: PipeType) tuple[ndarray, ndarray][source]#
Let the main region’s packs paint their pooling zones, as coefficients and forbidden voxels.
- get_toggled_constraints() dict[str, tuple[ConstraintRule, ndarray, ndarray]][source]#
Pair each constraint with the mask and the distance field that switch it on.
- static get_volume_in_border(ruled_volumes: list[RuledVolume]) list[RuledVolume][source]#
Get all ruled volumes that define the bounding box.
Filters ruled volumes to return only those with in_border=True, which are used to compute the overall bounding box of the routing problem.
- Parameters:
ruled_volumes – List of RuledVolume objects to filter.
- Returns:
List of RuledVolume objects with in_border=True.
- property global_origin: tuple[float, float, float]#
The voxel centre, aligned on
global_voxel_size, that anchors every region.
- property global_shape: tuple[int, int, int]#
The shape, at
global_voxel_size, of the box covering every region.
- property global_voxel_size: float#
The coarsest voxel size in use, which sets the resolution of the world frame.
- grid_forced_zones() list[GridForcedZone][source]#
Lay the forced zones on the grids of the regions: what the finder routes through.
- is_active_at(position: ndarray) bool[source]#
Whether
positionfalls on an in-grid voxel with non-zero weight.
- property is_multi_scale: bool#
Whether the regions are sampled at more than one voxel size.
- load_ports_points(terminals: list[Terminals]) None[source]#
Register the conduits’ own port positions, those known before routing, skipping those already registered.
A tee’s tap port is built by the routing and never registered.
- static merge_packs(packs: list[Pack]) list[Pack][source]#
Merge all packs that share pipelines.
Iteratively merges packs that share at least one common pipe line or are compatible based on parallelism. Continues until no more merges are possible.
- Parameters:
packs – List of Pack objects to merge.
- Returns:
List of merged Pack objects (fewer than input if merges occurred).
Navigable room and per-ruled-volume CAD-distance fields on the
voxel_sizegrid.At the cadmap’s own step this is a direct read of
room_voxelsand the stored distances — no recomputation. Any other step re-voxelizes the room and recomputes each ruled volume’s distance field on the new grid (the distance accelerator cost is bound to the CAD triangle count).
- property nbytes: int#
Memory footprint of the voxel matrices and of every field carried on them, in bytes.
- property occupancy_ratio: float#
Fraction of the whole routing space that is traversable, between 0.0 and 1.0.
- pack_field_primitives(pack: Pack, pipe_type: PipeType, region_index: int = 0, decimation: int = 1) list[Primitive3D][source]#
Render, on one region, the field
packalone paints for a candidate ofpipe_type.Stamping a single pack on fresh tensors is what isolates it from the others; the stamp itself is the production one (
stamped_field()), so what is drawn is what routes see.
- packs_on() list[Pack][source]#
List the packs of the map, every one: the regions a pack paints depend on the conduit (
Pack.painted_regions()).
- property pipe_types: dict[str, PipeType]#
Get dictionary of pipe types from solutions.
Extracts all unique pipe types from the solutions list, indexed by their names.
- Returns:
Dictionary mapping pipe type names to PipeType objects.
- property pooling_rule: PoolingRule#
Get the PoolingRule from design rules.
Searches for a PoolingRule in the design rules. If found and history is enabled, returns it. Otherwise returns an empty PoolingRule.
- Returns:
PoolingRule instance from design rules or empty PoolingRule.
- region_boxes() ndarray[source]#
Give the box each region spans,
(n_regions, 2, 3)lowest and highest corners.
- property region_contacts: dict[tuple[int, int], ndarray]#
Pairs of border voxels through which the regions touch, as rows of each region’s
border_indices.Keyed by two region indices in increasing order, for the regions that touch only: a decomposition can hold thousands of regions, each touching a handful. The regions tile the space without overlapping, so two voxels touch across their frontier or not at all, whatever the weights of the day: the pairs are a property of the decomposition, computed once (
RoutingVoxelization.touching_border_pairs()) and filtered by the caller on the voxels the weights keep.
- remove_solutions(solutions: list[Solution]) None[source]#
Withdraw
solutionsand rebuild the pack topology the retained solutions imply.The retained solutions keep their routing order and are replayed through
restore_solutions()— so the withdrawn solutions’ pack contributions vanish. Solutions are matched by identity (two data-equal Solution objects are two physical cables).
- repel_zone(shape: Primitive3D, strength: float) None[source]#
Add a repulsive
WeightedZoneovershape(weights inside multiplied bystrength> 1).Steers subsequent routing away from a region a cylinder could not fit into; seen at the next grid refresh (
apply_zone_weights(), run per specification insideRoutePlanner.find_paths()).
- restore_solutions(solutions: list[Solution]) None[source]#
Rebuild the solution/pack state to exactly
solutions, replayed in the given order.A generation pass that routes nothing: clears the packs and replays
update_packs(). Restores a recorded generation (e.g. aBundleSnapshot’s solutions) on this mutable cadmap; the rules appended since (repel_zone()) are withdrawn separately (withdraw_zones()).
- revoxelize(voxel_size: float) RoutingVoxelization[source]#
Re-voxelize the routing space at
voxel_size; largest connected component.Duplicates
ruled_volumesandbounding_boxto avoid hitting their cached voxelizations (which were computed at this CadMap’s original voxel size).
- property room_voxels: RoutingVoxelization | None#
The traversable space, read off the single region.
- property routed_solutions: list[Solution]#
The solutions a route joins: the only ones with a geometry to read.
The voxels the current solutions occupy, read off the single region.
- show_with_points(points: list[ndarray]) None[source]#
Visualize CadMap with bifurcation points in a 3D viewer.
- show_with_solutions() dict[str, Any]#
Implementation of a tag for displays.
- solutions_with_crossed_voxels() None[source]#
Visualize all solutions with their crossed voxels highlighted.
Creates a 3D visualization showing all routing solutions along with the voxels they cross, each colored according to its pipe type.
- Returns:
None
- stamped_field(packs: list[Pack], pipe_type: PipeType, region_index: int) tuple[ndarray, ndarray, list[ndarray]][source]#
Let
packspaint one region and return the coefficients, the forbidden mask and the cells each pack lays.The clearance sampler, the isotropy step and the lattice belong to the map, not to the region — a pack straddling two regions must see one policy — so every caller comes through here instead of calling
VoxelRegion.stamp_packs()with a context of its own.
- support_normal(lines: list[PipeLine]) ndarray | None[source]#
Give the unit normal out of the support
linesrest on, None when they rest on none or disagree on it.At each sample of the fibres, at the finest voxel pitch, the nearest support volume gives its closest point: the direction from that point to the sample is the normal of the touched face, except on an edge. A sample is in reach when its gap to the support is under
_SUPPORT_REACH_IN_CELLSof the coarsest cell, the one that may have placed the conduit. With at least_SUPPORT_SAMPLES_IN_REACHof the samples in reach, their directions vote; the vote stands when_SUPPORT_AGREEMENTof them point within_SUPPORT_AGREEMENT_ANGLEof it, so a corner at equal distance or a line between two facing surfaces gives none.
- property support_volumes: list[RuledVolume]#
The volumes trays rest on: those flagged
support, every volume when none is.
- to_grid(grid_id: int = 0) SmartGrid[source]#
Expose one region to the pathfinder as a
SmartGridcarrying its weights and rules.- Parameters:
grid_id – Identifier carried by the produced grid, used to address it in a
SmartWorld.
- to_world(auto_connect: bool = True) SmartWorld[source]#
Expose every region as a
SmartWorld: one grid per region, stitched at their borders.The search engine keeps the frame of every grid it is given in one registry per process, and its largest voxel size is the unit of the traversal cost: the grids of the maps built before are dropped from it, so a map routes the same whatever was routed before it. The nodes of an earlier map read their position in that registry: pick or read its paths before building another world.
- Parameters:
auto_connect –
When True, border nodes of adjacent grids are connected in both directions. When False the grids stay isolated and must be connected manually:
node_1 = world.grids[grid_id_1].node(x1, y1, z1) node_2 = world.grids[grid_id_2].node(x2, y2, z2) node_1.connect(node_2) node_2.connect(node_1)
- traversed_weights(start_position: ndarray, end_position: ndarray) ndarray[source]#
Weights of the in-grid voxels crossed by the
[start, end]segment (Bresenham).
- unlock_line_crossed_voxels(line: PipeLine) None[source]#
Make every voxel the
linecrosses traversable at minimum cost, on every region it reaches.Each region rasterizes the line on its own grid (out-of-grid voxels are dropped), so a line straddling two regions unlocks its corridor on both.
- unlock_voxels_between_points(point_1: Point3D, point_2: Point3D, radius: float) None[source]#
Unlock (make traversable) voxels between two points.
Creates a temporary pipe line between the points and unlocks all voxels it crosses, setting their weights to 1 (minimum cost). Useful for forcing a path through specific regions.
- Parameters:
point_1 – Start point as Point3D.
point_2 – End point as Point3D.
radius – Pipe radius for computing crossed voxels.
- Returns:
None
- update_grid(world: SmartWorld, overweighted_voxels: ndarray = None) None[source]#
Refresh every grid of
worldfrom the current routing state.Recomputes each region’s toggled attributes, updates the rule state from the current solutions (shared zones, bundle graph), then pushes each region’s tensors into its grid and hands the world the routed solutions, the conduits a tee’s leg keeps clear of.
- Parameters:
overweighted_voxels – Optional
(n, 4)array of(i, j, k, grid_id)voxel indices to penalize; rows are dispatched to the region their grid id names.
- update_packs(solution: Solution) None[source]#
Catch the solution’s lines into the packs of its policy, found new ones for the rest, merge, settle the trays.
One decision, the policy of the routed type, chooses both the packs that may catch and the class that founds. Packs live in world coordinates and may straddle regions, so the catch is judged at the coarsest voxel step. A solution no route joins is recorded and pools nothing.
- Parameters:
solution – A solution object containing the best pipe and its segments.
- update_rules_with_solutions(toggled_attributes_by_region: dict[int, dict[str, ndarray]] | None = None) None[source]#
Update each design rule with the current routing state.
Builds
axis_voxelsfrom the packs, abundle_graphfromself.solutions, resolves the port and bifurcation graph nodes, then callsDesignRule.update_with_solutionson every rule that tracks the solutions (DesignRule.tracks_solutions); with no such rule nothing is built. Shared voxels are tagged with the index of the region they belong to.- Parameters:
toggled_attributes_by_region – Optional per-region toggled-attribute dicts. When None they are recomputed from every region.
- Returns:
None
- update_weights(specific_weighting_rules: list[WeightingRule]) None[source]#
Rebuild every region’s pathfinder tensor: the volumes’ composed weights, carved by the pipe-specific rules.
The weight fields and their composition are properties of the map, computed once (
VoxelRegion.composed_weights()); a pipe only carves its own rules on a copy.
- update_weights_from_connectivity() None[source]#
Zero the weights of every voxel outside the largest connected component of the space.
A single region is left alone: the graph then has no edge, so “largest connected component” would degenerate into “biggest pocket” — a policy the undecomposed path never applied.
- update_weights_with_history(pipe_type: PipeType) None[source]#
Bend every region’s weight tensor to the pooling zones its packs paint, and label their corridors.
The packs are numbered once over the whole map, each pack’s corridor is one box in the world built on what it laid on every region, and the sets pooling at a voxel are interned across every region: a corridor crossing two resolutions keeps one identity and one label on both sides.
- update_with_pipe_type(solution: Solution) None[source]#
Update CadMap with pipe type-specific constraints and weights.
Applies pipe type properties to volumes, extracts global weighting rules, and updates weights to reflect the current pipe type’s constraints. This prepares the CadMap for routing a specific pipe type.
- Parameters:
solution – Solution containing the pipe type to apply.
- Returns:
None
- update_with_voxelization(voxelization: MatrixBasedVoxelization, build_cad: bool = False) None[source]#
Add a voxelization (roomvoxelization) to current CadMap.
- volmdlr_primitives() list[Primitive3D][source]#
Get volmdlr primitives of CadMap (volumes + voxelization).
Returns all 3D geometric primitives representing the ruled volumes and optionally the voxelization for visualization.
- Returns:
List of Primitive3D objects from ruled volumes and voxelization.
- voxel_meshes(show_connections: bool = False, apply_weights: bool = False) list[Mesh3D][source]#
Mesh every region, one colour per voxel size.
- Parameters:
show_connections – Add a mesh of the stitches between regions.
apply_weights – Drop the voxels the weight tensor zeroed out.
- property voxel_sizes: set[float]#
Every voxel size in use across the regions.
- property weight_tensor: ndarray | None#
The composed field the pathfinder reads, read off the single region.
- property weighted_zones: list[WeightedZone]#
The zones weighing on the routing grid.
- property weighting_volumes: list[RuledVolume]#
The volumes the distance and weight fields are indexed by: the ruled volumes, then the border if it rules.
- property weights: list[CompressedArray] | None#
The compressed weight fields, one per ruled volume, read off the single region.
- property with_history: bool#
Check if CadMap has history tracking enabled.
History is enabled when a PoolingRule is present in the design rules, allowing tracking of pipe placement for optimization.
- Returns:
True if history tracking is enabled, False otherwise.
- withdraw_zones(shapes: list[Primitive3D]) None[source]#
Remove the zones carrying
shapes(matched by identity) — the inverse ofrepel_zone().
- property world_frame: VoxelFrame#
The frame every region is anchored on: the coarsest step, over the box covering every region.
- class routing.core.cadmap.CorridorLabels(component_of: list[int])[source]#
Bases:
objectThe sets of harness components whose corridors cover a voxel, each interned as one integer: what a corridor voxel carries.
A component is what “two packs share a solution” connects: a strand with its branches, what a route may join, travel along and leave, and must not rejoin once left. Label 0 is the empty set, outside every corridor; a voxel astride two components carries both. The labels are minted while the packs stamp (
claim()).- claim(field: ndarray, cells: ndarray, pack: int) None[source]#
Let the component of
packjoin the label of each ofcellsinfield: a corridor meets a handful of distinct sets, each joined once.
- component_of#
Per pack, in the map’s order, the component it belongs to.
- components_at: list[frozenset[int]]#
Per label, the components pooling there.
- classmethod from_packs(packs: list[Pack]) CorridorLabels[source]#
Read the components off the packs through the conduits they carry: the packs carrying one solution are one component.
- class routing.core.cadmap.DistanceDerivations(fields: list[~routing.core.functions.CompressedArray] | None, minimum_distance_field: ~numpy.ndarray | None = None, cad_distance_field: ~numpy.ndarray | None = None, attribute_masks: dict[tuple[int, ~routing.core.design_rules.DistanceAttributeToggler], ~numpy.ndarray] = <factory>)[source]#
Bases:
objectWhat a region derived from its distance fields, with the fields it derived them from.
- attribute_masks: dict[tuple[int, DistanceAttributeToggler], ndarray]#
- cad_distance_field: ndarray | None = None#
- fields: list[CompressedArray] | None#
- minimum_distance_field: ndarray | None = None#
- class routing.core.cadmap.LineFootprint(forbidden: ndarray, shell: ndarray, lane: ndarray, halo: ndarray, halo_value: float)[source]#
Bases:
NamedTupleWhat one member line lays for a pipe type on a grid, whatever the pack’s other members.
An attracting member (
0 < coefficient < 1) lays its exclusion zone (forbidden), its contactshelland its fiberlanecells; a segregating member lays the cells it occupies asforbiddenwhen the candidate cannot cohabit with it, and ahalopricedhalo_value.- forbidden: ndarray#
Alias for field number 0
- halo: ndarray#
Alias for field number 3
- halo_value: float#
Alias for field number 4
- lane: ndarray#
Alias for field number 2
- shell: ndarray#
Alias for field number 1
- class routing.core.cadmap.MultiCadMap(cad_maps: list[CadMap], name: str = '')[source]#
Bases:
CadMapDeprecated alias kept for the callers that still build a decomposed map through this class.
A
CadMapnow carries its own regions, so a decomposed map is just a CadMap with several of them. UseCadMap.decomposed()andCadMap.from_cad_maps()instead.- classmethod from_cad_map(cad_map: CadMap, multi_scale: bool = False, size_factor: int = 3, bounding_box: BoundingBox | None = None) CadMap[source]#
Decompose
cad_map— deprecated, callCadMap.decomposed()directly.
- classmethod from_cad_maps(cad_maps: list[CadMap], name: str = '') CadMap[source]#
Assemble
cad_maps— deprecated, callCadMap.from_cad_maps()directly.
- classmethod from_project_inputs(design_rules: list[InstanceOf[DesignRule]], ruled_volumes: list[RuledVolume], bounding_box: RuledVolume | None = None, voxel_size: float = 0.1, exact_distances: bool = False, weights_operator: str = 'min', build_cad: bool = True, multi_scale: bool = False, size_factor: int = 3, name: str = '') CadMap[source]#
Build a decomposed map — deprecated, chain
CadMap.from_project_inputs()andCadMap.decomposed().
- class routing.core.cadmap.Pack(pipe_lines: list[PipeLine], vector: ndarray, axis: ndarray | None = None, name: str = '')[source]#
Bases:
DessiaObject,Displayable3DA class representing a collection of pipelines grouped by proximity rules and pooling specifications.
Groups multiple PipeLine objects that are aligned along a common direction and located within close proximity. Packs are used for efficient collision detection, pooling optimization, and managing pipe clusters in voxelized space.
The pack state is one-dimensional: an
axisre-fitted to the cluster on every membership change (mean direction through the members’ centroid) and, per member line, an interval of curvilinear abscissa derived by projection. Every 3D quantity (influence field, occupancy tube, capacity profile) is a windowed view computed from that state — seestamp().- Parameters:
pipe_lines – The list of PipeLine objects to include in this pack.
vector – The unit direction vector (shape: (3,)) representing the common alignment direction of pipes in the pack.
axis – Optional axis polyline as a (2, 3) array. When None, lazily derived from the first pipe line’s endpoints.
name – The name to identify this pack. Defaults to ‘’.
- AXIS_COLOR = (0.1, 0.1, 0.1)#
- FORBIDDEN_COLOR = (0.85, 0.15, 0.15)#
- LANE_COLOR = (0.05, 0.25, 0.65)#
- MEMBER_COLOR = (0.3, 0.42, 0.58)#
- MIN_DISTANCE_IN_VOXEL = 1.5#
- MIN_LENGTH_IN_VOXEL = 2#
- RING_COLOR = (0.55, 0.85, 0.95)#
- SEGREGATED_COLOR = (0.95, 0.75, 0.15)#
- TRANSVERSE_U_COLOR = (0.95, 0.55, 0.1)#
- TRANSVERSE_V_COLOR = (0.55, 0.2, 0.65)#
- add_pipe_line(pipe_line: PipeLine, solution: Solution) None[source]#
Add a single PipeLine to the current pack.
Recalculates the pack’s direction vector, re-fits the cluster axis, and invalidates the cached 1D/3D derivations.
- Parameters:
pipe_line – The PipeLine to add.
solution – The Solution corresponding to the pipe line.
- Returns:
None
- add_pipe_lines(pipe_lines: list[PipeLine], solutions: list[Solution]) None[source]#
Add a list of PipeLine objects to the current pack.
- Parameters:
pipe_lines – List of PipeLine objects to add.
solutions – List of Solution objects corresponding to the pipe lines.
- Returns:
None
- alignment_candidates(others: list[Pack]) ndarray[source]#
Which of
othersmay be aligned with this pack, judged at once: a superset of whatmerge_with_other()accepts.
- static all_points_from_lines(pipe_lines: list[PipeLine]) ndarray[source]#
Get all points of pack from its lines.
- property all_types: set[str]#
Get all pipe types in the current pack.
- Returns:
Set of pipe type identifiers (strings) present in this pack.
- property average_ends: ndarray#
Get average ends of Pack.
- property axis: ndarray | None#
Frozen axis polyline (2, 3), lazily born from the founding line’s endpoints.
- property axis_length: float#
Length of the pack axis in world units (0.0 when no axis yet).
- bounding_box(offset: float = 0.0) ndarray[source]#
Get the axis-aligned bounding box of the pack.
Computes the bounding box that encompasses all pipe lines in the pack, including pipe radius and an optional offset.
- Parameters:
offset – Additional offset to add to the bounding box. Defaults to 0.0.
- Returns:
Array of shape (2, 3) containing [[min_x, min_y, min_z], [max_x, max_y, max_z]].
- catch_pipe_lines(pipe_lines: list[PipeLine], solution: Solution, pooling_rule: PoolingRule, clustering_step: float) list[int][source]#
Catch pipe lines that can be added to the current pack.
Checks each pipe line in the list to see if it can be clustered with the pack based on parallelism and proximity criteria. Adds compatible pipe lines to the pack and returns their indices.
- Parameters:
pipe_lines – List of PipeLine objects to check.
solution – Solution object associated with the pipe lines.
pooling_rule – PoolingRule defining clustering criteria.
clustering_step – World-unit step setting the catch tolerances (offset, minimum length).
- Returns:
List of indices of pipe lines that were successfully added to the pack.
- property center: ndarray#
Get center of Pack, computed with fixed lines when there are some.
- contains_pipe_line(pipe_line: PipeLine, pooling_rule: PoolingRule, offset: float, min_length: float) bool[source]#
Check if the current pack can cluster with a pipe line.
Determines if a pipe line can be added to this pack based on: - Parallelism: pipe line direction must be aligned with pack direction - Proximity: pipe line must be close enough to at least one pack line - Size: both lines must meet minimum size requirements
- Parameters:
pipe_line – The PipeLine to check for clustering compatibility.
pooling_rule – PoolingRule defining clustering distances and coefficients.
offset – Offset distance for proximity checking.
min_length – Minimum size requirement for both pipe lines.
- Returns:
True if pipe line can be clustered with this pack, False otherwise.
- property contains_port: bool#
True iff any pipe line touches its solution’s input or output port.
- coord_from_parametric_value(parametric_value: float) ndarray[source]#
Convert parametric value on Pack into 3D coordinate.
- corridor(laid_centers: ndarray, overhang: float) OrientedBox[source]#
Give the corridor the anti-loop rule reads for this pack: the oriented box of what it lays, in the world.
The box encloses the pack and one row all around it (
PackFootprint.laid_cells()), whichever sides it attracts on, so the pack always lies in its own corridor and the staircase of its contact shell is filled. It lies in the pack’s own frame, so an oblique pack keeps a tight corridor, and it is lengthened byoverhangalong the pack only, at both ends, so the corridor runs on from one pack to the next while a route beside the strand still reads as outside it. One box for every region: a strand crossing two resolutions keeps one corridor.- Parameters:
laid_centers – World centres of the cells the pack lays, over every region it paints.
overhang – World distance the corridor runs past the pack, at each end.
- field_primitives(coefficients: ndarray, forbidden_voxels: ndarray, room_voxels: RoutingVoxelization, decimation: int = 1) list[Primitive3D][source]#
Show what this pack wrote on one grid: its members, its own frame, and every cell it touched.
The two tensors are the ones
stamp()fills, so the view shows the production field and not a re-derivation of it. Members are steel blue, the axis black with its transverse basis in orange (u) and purple (v), forbidden cells red, attracted cells shaded from dark (the cheapest tier, where a lane is still free) to pale (the dearest), segregated cells amber. Cells no pack coefficient reached are simply not drawn — they keep their normal price.- Parameters:
decimation – Keep one cell in
decimation, to stay under a viewable count.
- property frame: ndarray#
Get a Pack’s frame from vector and center.
- classmethod from_pipe_line(pipe_line: PipeLine, solution: Solution) Pack[source]#
Found a pack of this class on a single pipe line: its axis is the line itself.
- classmethod from_pipe_lines(pipe_lines: list[PipeLine], solution: Solution, pooling_rule: PoolingRule, clustering_step: float) list[Pack][source]#
Group
pipe_linesinto generic packs by proximity and pooling rules.Successive lines are caught by the pack under construction when parallelism and proximity allow; a line that does not fit closes that pack and founds the next one.
- Parameters:
clustering_step – World-unit step setting the catch tolerances (offset, minimum length).
- get_axis_voxels(room_voxels: RoutingVoxelization, region_index: int) dict[tuple[int, int, int, int], tuple[int, int, int, int]][source]#
Build the axis-voxel map for this pack (fixation / pooling alignment) on one region’s grid.
The returned dict maps each
(x, y, z, region_index)voxel of the pack occupancy tube{d <= R_P(s) + sqrt(3)/2 * voxel}to the representative(x', y', z', region_index)of its quantized axis slot — same contract as the historical crossed-voxels grouping, derived from the axis state instead of the per-line voxel sets.- Parameters:
room_voxels – Room voxelization providing grid origin, size and shape.
region_index – Index of the region the voxels are projected on, carried by the 4-tuples.
- Returns:
Mapping each voxel 4-tuple to its representative 4-tuple along the pack axis.
- get_fixed_lines() list[PipeLine][source]#
Get all fixed lines in pack, i.e. all lines that are on a Port.
- property is_pack: bool#
Check if a Pack fits with Pack’s concept (WIP).
- property length: float#
Get total length of Pack from start to end.
- property local_axes: ndarray#
The pack’s three unit axes, its direction first — the frame’s own columns are not unit-long.
- property member_box: Box#
The box of the member lines and the axis, around which every stamp lies; kept until the pack changes.
- property member_intervals: list[tuple[float, float]]#
Curvilinear interval
[s_in, s_out]of each member line, by endpoint projection.
- merge_with_other(other: Pack) bool[source]#
Merge this pack with another pack if they meet compatibility criteria.
Two packs can be merged if: 1. Their direction vectors are sufficiently parallel (dot product > threshold) 2. They have the same parallel flag (both parallel or both non-parallel) 3. They share at least one common pipe line (same object reference)
When merged, all pipe lines and solutions from the other pack are added to this pack, and the pack’s direction vector is recalculated.
- Parameters:
other – The Pack to attempt merging with.
- Returns:
True if packs were successfully merged, False otherwise.
- static pack_radii(radii: list[float], min_distance: float = 0.0) float[source]#
Bounding-circle diameter packing
radiikeptmin_distanceapart.Closed-form for 1 or 2 pipes, voxel pack otherwise. The spacing rides on disks inflated by half of it and is taken back off the packed diameter — for two pipes exactly
2 * (R + r) + min_distance.
- painted_regions(region_boxes: ndarray, pipe_type: PipeType, pooling_rule: PoolingRule, step: float) ndarray[source]#
Give the indices of the regions this pack paints for
pipe_type: those its reach meets (stamp_reach()).- Parameters:
region_boxes – The box of each region,
(n, 2, 3)lowest and highest corners.step – The pack’s lattice step, the coarsest voxel size in use.
- parallel = False#
- points_matrix(offset: float = 0.0) ndarray[source]#
Get bounding box matrix of all points with radius and offset.
Computes the minimum and maximum points across all pipe lines in the pack, expanded by pipe radius and an optional offset.
- Parameters:
offset – Additional offset to add to the bounding box. Defaults to 0.0.
- Returns:
Array of shape (2, 3) containing [min_point, max_point] for all pipe lines.
- property radius: float#
Compute radius of current pack with pack_circles method.
- rebuilt(pipe_lines: list[PipeLine], name: str) Pack[source]#
Rebuild the pack on
pipe_lines, asCadMap.collect_packs()does; a subclass carries its own state over.
- section_deviation(room_voxels: RoutingVoxelization, region_index: int) ndarray[source]#
How much wider the cells this pack occupies are than its real section, slice by slice.
The check behind “the voxel section should match the real section to within one voxel”: the occupied cells come from
get_axis_voxels(), the real section from the members’ own positions and radii, and both are read in the pack’s transverse basis.- Returns:
Array
(m, 2)in voxels, one row per covered slice, columns u and v.
- stamp(coefficients: ndarray, forbidden_voxels: ndarray, pipe_type: PipeType, pooling_rule: PoolingRule, room_voxels: RoutingVoxelization, clearance_at: Callable | None = None, spread_tolerance: float | None = None, lattice: VoxelFrame | None = None) None[source]#
Stamp this pack’s influence onto the pooling
coefficients, member line by member line.Generic (poolable) policy in exact world distances. For the candidate of radius
rand a member of radiusRat pooling distancemin_distance,contact = R + r + min_distanceis the closest the two axes may get. Every member offers:exclusion zone: the cells whose centre lies closer than
contactto the member axis are FORBIDDEN — the candidate cannot run through them — its own fiber lane excepted, judged by capacity below;contact shell: the first row of the pack’s
latticebeyond contact, stamped atcoefficientwhere the growth policy keeps it (_admissible_growth(), the cross toward a square section). Off-policy cells are left untouched; cells whose centre cannot host the candidate (clearance < r) or that another member excludes are dropped;fiber lane: the cells the member axis runs through. A lane whose residents plus the candidate still pack in one lattice cell (
pack_radii()at pooling distance) hosts the candidate_FIBER_LANE_PREFERENCEbelow the shell, so a lane still free fills before the pack spreads. A rank, not a gradient — a field graded by distance strands the route on an intermediate layer (measured), a field ranked by availability has no intermediate layer. A full lane is FORBIDDEN: the corridor closes as it fills up;segregating member (
coefficient >= 1): its occupied gauge and halo priced at-coefficient— passage stays possible but dearer;coefficient == 0cancels it (the candidate cannot pass). Segregation carries no growth policy: it prices a neighbourhood, it does not host.
- Parameters:
coefficients – NaN-initialised tensor of pooling coefficients, modified in place.
forbidden_voxels – Boolean tensor of non-traversable voxels, modified in place.
pipe_type – PipeType of the pipe being routed.
pooling_rule – PoolingRule carrying the coefficient/distance matrices.
room_voxels – Room voxelization providing the grid geometry of the stamp window.
clearance_at – Optional CAD clearance sampler (
(n, 3)world positions ->(n,)distances). None means every shell cell can host the candidate.spread_tolerance – World-unit spread below which the section counts as isotropic, injected by the map so every region sees one pack-wide growth policy (the finest voxel size in use). None falls back to this grid’s voxel size.
lattice – Frame the pack’s rows are drawn on — the world’s, so a lane’s capacity and the contact shell are the same in every region the pack crosses and a follower keeps its row through a change of resolution. None falls back to this grid’s frame.
- Returns:
The cells the pack lays,
(n, 3), empty when it stamps nothing: every member’s body, lane and contact ring (PackFootprint.laid_cells()), what the pack’s corridor must enclose.
- stamp_reach(pipe_type: PipeType, pooling_rule: PoolingRule, step: float) float[source]#
Distance from
member_boxbeyond which a stamp forpipe_typelays nothing, on grids ofstepor finer.Every cell a member lays is read off a
VoxelFrame.cylindrical_window()around it, the box of the member widened by the window’s half-width and rounded out to whole cells: at contact plus one step for an attracting member, its shell drawn on the lattice; the band of_line_segregation()for a segregating one. Two steps more cover the rounding and the grid cells a lattice cell covers.- Parameters:
step – The pack’s lattice step, the coarsest voxel size in use.
- volmdlr_primitives(**kwargs) list[Cylinder][source]#
Get 3D shapes of all lines and the pack’s bounding box.
Creates Cylinder primitives for each pipe line and a Box primitive representing the pack’s bounding box.
- Parameters:
kwargs – Optional keyword arguments, including ‘color’ for visualization.
- Returns:
List of Cylinder primitives for pipe lines plus a Box primitive for the bounding box.
- class routing.core.cadmap.PackFootprint(members: list[PipeLine], line_footprints: list[LineFootprint], forbidden: ndarray, segregations: list[tuple[ndarray, float]], sites: list[tuple[ndarray, float, ndarray]], lanes: ndarray, lane_full: ndarray, lane_coefficients: ndarray, lane_hosts: ndarray)[source]#
Bases:
NamedTupleWhat a pack stamps for one pipe type on one grid, before any field is read: its geometry, laid once.
membersandline_footprints: the member lines and what each lays (LineFootprint), reused when the pack grows.forbidden: the cells the members exclude.segregations: per segregating member, its halo cells and the value they take.sites: per attracting member, its contact shell cells, their coefficient and the growth policy’s verdict on each.lanes: the fiber lane cells, with per lane whether the candidate no longer fits (lane_full), the value a hosting lane takes (lane_coefficients) and whether the CAD clearance lets the candidate sit there (lane_hosts).- forbidden: ndarray#
Alias for field number 2
- laid_cells() ndarray[source]#
Give every cell the members lay,
(n, 3): each one’s body (exclusion zone), lane and whole contact ring.The ring is taken before the growth policy’s verdict, so the enclosure is the pack itself plus one row all around, whichever sides it attracts on. A segregating member that cohabits lays nothing here.
- lane_coefficients: ndarray#
Alias for field number 7
- lane_full: ndarray#
Alias for field number 6
- lane_hosts: ndarray#
Alias for field number 8
- lanes: ndarray#
Alias for field number 5
- line_footprints: list[LineFootprint]#
Alias for field number 1
- segregations: list[tuple[ndarray, float]]#
Alias for field number 3
- sites: list[tuple[ndarray, float, ndarray]]#
Alias for field number 4
- class routing.core.cadmap.ParallelPack(pipe_lines: list[PipeLine], vector: ndarray, normal: ndarray | None = None, axis: ndarray | None = None, name: str = '')[source]#
Bases:
PackA tray: a generic pack whose members lie in planes of fibres stacked along the normal of its support.
The map gives the tray its
normalonce its members rest on a support (settle(),CadMap.support_normal()); without one the tray grows as a generic pack. With one, per slice, the members’ fibres are grouped by height into planes: the lowest group is plane 0, a higher group is a plane only if one of its members has matter below it on the plane underneath, otherwise its members are detached (they count nothing and offer nothing, their exclusion zones hold). A seat is on a plane when it is aligned on it, to the reading grid’s half cell: on plane 0 it is always open, on a higher plane it needs matter below or above, above the last plane it opens a new one while fewer than the specification’smax_layersexist. Once that many planes exist, the band above the last one is segregated, not forbidden.- Parameters:
pipe_lines – The list of PipeLine objects to include in this tray.
vector – The unit direction vector (shape: (3,)) of the tray.
normal – Unit vector out of the support the members rest on, None until the tray settles.
axis – Optional axis polyline as a (2, 3) array. When None, lazily derived from the first pipe line.
name – The name to identify this tray. Defaults to ‘’.
- layout(room_voxels: RoutingVoxelization, spread_tolerance: float) TrayLayout[source]#
Read the tray on
room_voxels’ grid: its slices atspread_tolerance, its members and their planes.The reading is kept per grid until the tray changes.
- merge_with_other(other: Pack) bool[source]#
Merge as a generic pack does, unless the two trays rest on different supports.
- static pack_radii(radii: list[float], min_distance: float = 0.0) float[source]#
Length of the row the pipes make side by side on the tray.
- parallel = True#
- rebuilt(pipe_lines: list[PipeLine], name: str) ParallelPack[source]#
Rebuild the tray on
pipe_lines, resting on the same support.
- settle(support_normal: Callable) None[source]#
Rest on the support the members lie on, once it is unambiguous; a settled tray keeps its normal.
support_normalreads the members and gives the normal out of their support, or None (CadMap.support_normal()). A support found along the axis, a riser ending on the floor, is not beside the tray: no normal. Members already asked are not asked again: the answer would be theirs.
- stamp(coefficients: ndarray, forbidden_voxels: ndarray, pipe_type: PipeType, pooling_rule: PoolingRule, room_voxels: RoutingVoxelization, clearance_at: Callable | None = None, spread_tolerance: float | None = None, lattice: VoxelFrame | None = None) ndarray[source]#
Stamp as a generic pack, then price up the band above the last plane where
max_layersplanes exist.- Returns:
The cells the tray lays, as
Pack.stamp()returns them.
- stamp_reach(pipe_type: PipeType, pooling_rule: PoolingRule, step: float) float[source]#
Widen the generic reach to the band above the last plane (
_segregated_band()).The band is read off the window around the axis at its extent plus a step: the farthest member skin on either side, the topmost skin, the candidate’s contact and
_CAP_BAND_IN_CELLScells, each skin no farther from the axis than the tray’s section. Two steps more cover the rounding to whole cells.
- class routing.core.cadmap.TrayLayout(s_samples: ndarray, covers: ndarray, offsets: ndarray, radii: ndarray, planes: ndarray, heights: ndarray, direction_u: ndarray, direction_v: ndarray, margin: float)[source]#
Bases:
NamedTupleWhat a tray reads of itself on one grid: its members per slice, grouped into planes of fibres.
planesgives each member’s plane on each slice, 0 the closest to the support, -1 where the member is absent, -2 where it is detached;heightsthet_vof each plane per slice, NaN beyond the slice’s planes;marginhalf a cell of the reading grid, what “aligned” means on it.- covers: ndarray#
Alias for field number 1
- direction_u: ndarray#
Alias for field number 6
- direction_v: ndarray#
Alias for field number 7
- heights: ndarray#
Alias for field number 5
- margin: float#
Alias for field number 8
- offsets: ndarray#
Alias for field number 2
- planes: ndarray#
Alias for field number 4
- radii: ndarray#
Alias for field number 3
- s_samples: ndarray#
Alias for field number 0
- class routing.core.cadmap.VoxelRegion(voxels: RoutingVoxelization | None = None, distances: list[CompressedArray] | None = None, weights: list[CompressedArray] | None = None, weight_tensor: ndarray | None = None, shared_voxels: ndarray | None = None)[source]#
Bases:
objectOne region of the routing space, with the CAD fields sampled on its own voxel grid.
A CadMap holds one region in the ordinary case, and several when the space is decomposed: into maximal index boxes at a single scale, or into a coarse grid plus refined ones. Every field below is shaped by
voxels, so none of them can be shared between regions.- Parameters:
voxels – The traversable space of the region. None while the CadMap is built but not yet voxelized.
distances – One compressed distance field per ruled volume.
weights – One compressed weight field per ruled volume.
weight_tensor – The single field the pathfinder reads:
weightscomposed (composed_weights()), then carved by the pipe being routed and by the packs.shared_voxels – Per voxel, the label in
PoolingRule.labelsof the set of components whose corridors cover it; 0 where no pack pools. Shaped like the grid.
- apply_pooling_coefficients(coefficients: ndarray, forbidden_voxels: ndarray) ndarray[source]#
Bend the weight tensor to the pooling zones the packs painted.
A positive coefficient scales the weight down (pooling is favoured), a negative one scales it up (segregation), and a forbidden voxel is zeroed out (non-traversable).
- apply_weighting_rules(weighting_rules: list[WeightingRule]) None[source]#
Override the weights of the voxels each rule’s distance range covers.
- apply_zone_weights(zones: list[WeightedZone]) None[source]#
Let each weighted zone reweight the voxels it covers.
- property cad_distance_field: ndarray#
Per-voxel exact distance to the nearest CAD surface (world units), for continuous sampling.
minimum_distance_fieldwith the inactive voxels reset fromnp.infto0.0, so a centreline reaching into CAD reads zero clearance — interference — rather than infinite clearance, and so the interpolation ofclearance_at()never foldsinfinto a finite neighbour (0 * inf = nan). Materialises the full grid in memory, so trigger it on a line’s local CadMap, never on a multi-gigabyte global one. Meaningful only withexact_distances=True.
- clearance_along(start_position: ndarray, end_position: ndarray, samples_per_voxel: float = 2.0) ndarray[source]#
CAD clearance (
clearance_at()) along the[start, end]segment atsamples_per_voxeldensity.
- clearance_at(positions: ndarray) ndarray[source]#
Trilinearly-interpolated distance to the nearest CAD surface at continuous world
positions.Shapes
(N, 3)->(N,), in world units. Readscad_distance_fieldat the exact positions rather than at voxel centres, so a query between centres returns a true intermediate clearance. This is what lets a pipe’s interference be tested along its continuous centreline instead of per Bresenham-crossed voxel.mode="nearest"edge-clamps to the grid: a query just outside the local AABB returns the boundary clearance instead of extrapolating or raising.
- compose_weights(weights_operator: str, weighting_volumes: list[RuledVolume]) ndarray[source]#
Reduce the per-volume weight fields into the single tensor the pathfinder reads.
“min” / “max”: the most restrictive / most permissive weight at each voxel
“min_distance” / “max_distance”: the weight of the closest / farthest volume
“sum” / “mean”: additive / balanced penalties
Only the volumes that weigh take part (
RuledVolume.weighs): a volume carrying only toggler rules switches attributes on, it does not weigh the space around it. A volume declared with anoverwrite_distancethen overrides the voxels closer to it than that distance with its own weights, whatever the operator composed there; where two such volumes overlap, the lowest weight wins.Note: only “min_distance” typically gives expected results for routing.
- Parameters:
weights_operator – The operator reducing the fields, one of the six above.
weighting_volumes – The volumes the fields are indexed by, in the same order.
- Raises:
NotImplementedError – If weights_operator is not recognized.
- composed_weights(weights_operator: str, weighting_volumes: list[RuledVolume]) ndarray[source]#
Compose the weight fields under
weights_operatoronce, and keep the tensor while its inputs are the same.The invariant part of the pathfinder’s tensor: a pipe carves its own rules (
apply_weighting_rules()) and the packs their zones (apply_pooling_coefficients()) on a copy of it, never on this tensor. The distance fields are inputs too: the*_distanceoperators pick, per voxel, the field of the closest or farthest volume, and a volume’soverwrite_distanceis measured on them.
- compute_weights(weighting_volumes: list[RuledVolume]) list[CompressedArray][source]#
Turn each distance field into a weight field by applying its volume’s design rules.
- Parameters:
weighting_volumes – The volumes the distance fields are indexed by, in the same order.
- property decompressed_distances: list[ndarray]#
The distance fields inflated to full numpy arrays, one per ruled volume.
Materialises the whole grid once per field, so it is memory-intensive on a large region; a chunked variant is possible if that ever becomes the bottleneck.
- property decompressed_weights: list[ndarray]#
The weight fields inflated to full numpy arrays, one per ruled volume.
- distances: list[CompressedArray] | None = None#
- is_active_at(position: ndarray) bool[source]#
Whether
positionfalls on an in-grid voxel with non-zero weight.
- label_corridors(corridors: dict[int, OrientedBox], labels: CorridorLabels) ndarray[source]#
Give per voxel the label of the components whose corridors cover it, 0 outside: the cells whose centre a box holds.
- Parameters:
corridors – Per pack index in the map’s own list, its corridor (
Pack.corridor()): the packs are numbered over the whole map, so one crossing two regions keeps one identity on both sides.
- property minimum_distance_field: ndarray#
Per-voxel distance to the nearest ruled volume: the min over the distance fields, decompressed once.
Inactive (obstacle / off-domain) voxels carry
np.inf, theempty_valueofdistance_matrix_from_voxelization. The tensorapply_weighting_rules()thresholds to carve a pipe’s radius clearance. Materialises the full grid in memory.
- property nbytes: int#
Memory footprint of the voxel matrix and of every field carried on it, in bytes.
- property occupancy_ratio: float#
Fraction of the grid that is traversable, between 0.0 and 1.0.
- refresh_grid(grid: SmartGrid, design_rules: list[InstanceOf[DesignRule]], toggled_attributes: dict[str, ndarray], overweighted_voxels: ndarray | None = None) None[source]#
Push this region’s current weights and the given rules into an existing
grid.overweighted_voxelsis an(n, 3)array of indices to penalise; repeated rows accumulate a stronger multiplier.
- stamp_packs(packs: list[Pack], pipe_type: PipeType, pooling_rule: PoolingRule, clearance_at: Callable | None = None, spread_tolerance: float | None = None, lattice: VoxelFrame | None = None) tuple[ndarray, ndarray, list[ndarray]][source]#
Let every pack paint its pooling zone on this grid.
- Parameters:
clearance_at – CAD clearance sampler for the capacity gating, injected by the map because a pack crossing this grid may live on another region’s voxels — the region’s own field reads zero clearance there.
spread_tolerance – Isotropy threshold of the packs’ growth policy, injected by the map (finest voxel size in use) so every region sees one policy per pack.
lattice – Frame the packs’ rows are drawn on, injected by the map (the world frame) so a pack keeps the same lane capacity and shell row in every region.
- Returns:
The per-voxel coefficients (NaN where no pack applies), the mask of the voxels the packs make non-traversable, and per pack the cells it lays, which its corridor encloses.
- to_grid(design_rules: list[InstanceOf[DesignRule]], toggled_attributes: dict[str, ndarray], grid_id: int) SmartGrid[source]#
Expose this region to the pathfinder as a
SmartGridcarrying its weights and rules.
- toggled_attributes(ruled_volumes: list[RuledVolume], with_history: bool) dict[str, ndarray][source]#
Collect, per attribute, the mask of the voxels its distance togglers switch on.
Volumes toggling the same attribute are OR-ed together. With
with_history, the packs’shared_voxelslabels are exposed underPoolingRule.POOLED_ATTRIBUTE, the attribute the rule prices moves on: a node carries the label of the components pooling at its voxel, 0 outside.
- toggled_constraints(ruled_volumes: list[RuledVolume], pipe_rules: list[InstanceOf[DesignRule]]) dict[str, tuple[ConstraintRule, ndarray, ndarray]][source]#
Pair each constraint with the mask and the distance field that switch it on.
A constraint is toggled by the volume rule sharing its attribute; volumes toggling the same attribute OR their masks and keep the per-voxel minimum distance.
- traversed_weights(start_position: ndarray, end_position: ndarray) ndarray[source]#
Weights of the in-grid voxels crossed by the
[start, end]segment (Bresenham).
- unlock_line_crossed_voxels(line: PipeLine) None[source]#
Make every voxel the
linecrosses traversable at minimum cost, its entry voxel included.
- voxels: RoutingVoxelization | None = None#
- weight_tensor: ndarray | None = None#
- weights: list[CompressedArray] | None = None#
- class routing.core.cadmap.WeightComposition(fields: list[CompressedArray], distances: list[CompressedArray], operator: str, tensor: ndarray)[source]#
Bases:
NamedTupleThe tensor a region composed from its weight fields, with the fields, distances and operator it was composed from.
- distances: list[CompressedArray]#
Alias for field number 1
- fields: list[CompressedArray]#
Alias for field number 0
- operator: str#
Alias for field number 2
- tensor: ndarray#
Alias for field number 3