routing.core.bundling#
Bundle data model: BundleVariable hierarchy, BundlePoint, BundleLine and Bundle.
- class routing.core.bundling.AnchorVariable(position: tuple[float, float, float])[source]#
Bases:
BundleVariableVariable pinning a point to rigid imposed geometry (a port on a CAD wall); it never moves.
- COLOR = (1.0, 0.0, 1.0)#
- bounds_in_cadmap(cadmap: CadMap, max_shift: float) None[source]#
No bounds: an anchor is never optimized.
- can_merge_with(other: BundleVariable) bool[source]#
Never: rigid imposed geometry keeps its own point.
- property is_updatable: bool#
Anchors are imposed geometry; the optimizer never mutates them.
- neighbor_values(voxel_size: float, max_search: int, lower: ndarray | float, upper: ndarray | float) list[ndarray | float][source]#
No neighbor values: an anchor never moves.
- property position: ndarray#
Anchor’s pinned 3D position.
- snap_in_cadmap(cadmap: CadMap) None[source]#
No-op: an anchor may legitimately sit on a CAD wall outside the navigable domain.
- property value: float | ndarray#
Current variable value.
- class routing.core.bundling.Bundle(lines: list[BundleLine], points: list[BundlePoint], pipes: list[Refinement], rules: list[DesignRule] | None = None, cadmap: CadMap | None = None, settings: SkeletonSettings | None = None, name: str = '')[source]#
Bases:
DessiaObject,Displayable3DBundle class representing a collection of pipes, packs and ports.
Built from a fitted Skeleton — body lines from its cylinders, entry geometry from its resolved entries — and wired by
HarnessWiring(seefrom_skeleton()).- attach_local_cadmaps(cadmap: CadMap | None = None, voxel_size: float | None = None, *, max_shift: float) None[source]#
Build a local CadMap on each line, sized to cover
±max_shiftof movement.cadmapdefaults to the bundle’s own environment (cadmap);voxel_sizeis the optimizer-window voxel step and defaults to the cadmap’s own step. The line-independent inputs — the global navigable domain and each ruled volume’s CAD-distance field on that grid — come fromCadMap.navigable_domain(): a direct read of the cadmap’s stored state at its own step, a one-shot recomputation at a divergent step. A voxel’s distance to CAD depends only on its position, not on the line owning it, so each line slices these fields to its sub-grid rather than rebuilding the distance accelerator once per line. Rigid lines are skipped: imposed geometry needs no navigable domain.Every domain is built first, then the points are snapped onto them, and the optimizer windows are clipped last — around the posed values: clipping before snapping would accumulate a window around a position the snap then abandons, leaving the seed outside its own bounds.
The windows are per-preparation: every point’s bounds are reset first, so the intersection accumulated by
BundlePoint.clip_in_cadmap()only spans this preparation’s incident lines — carried across passes it keeps cropping the domain, stranding moved points outside their own bounds.
- capture_values() list[list[float] | float][source]#
Snapshot the points’ variable values as plain floats/lists (JSON-portable; topology shared by reference).
- clear_port_cad_margin(alpha: float) None[source]#
Pose every PortLine’s leader base outside the CAD safety margin (see
PortLine.clear_base_margin()).
- collision_candidate_pairs(max_shift: float, exclude_tree_hops: int = 1) list[CollisionPair][source]#
Disjoint line pairs whose
±max_shift-widened AABBs overlap — the candidate set for collision tests.Candidacy is geometric (the widened boxes intersect); topology then exempts pairs that legitimately touch. Adjacent segments (a shared endpoint) are degenerate contact, never a collision pair. Free-line pairs are further exempted by solution kinship (segments of a same route); within
exclude_tree_hops, junction cohabitation (branches forking from a shared junction zone — the trunk splitting) exempts any pair, leaders included. A pair of imposed lines (two leaders, a leader with a rigid entry chain, two rigid chains) is never a candidate: their geometry is imposed by the CAD, so an overlap there is irreducible and constraining it would only poison G. Kinship never exempts a leader: a route may fold back onto its own entry, which must stay constrained (see_pair_is_exempt()).- Parameters:
max_shift – AABB widening margin (added to each line’s packed radius), so the pair set snapshotted at the seed state covers every position the optimizer can reach.
exclude_tree_hops –
1= adjacency only;2= also exempt free-line pairs one intermediate line apart (junction cohabitation).
- distinct_solutions() list[Solution][source]#
Solutions carried by the lines, de-duplicated in first-appearance order.
- classmethod from_skeleton(skeleton: Skeleton, smoothing_window: int = 2, tangent_window: int = 3, min_turn_angle: float = 0.5235987755982988, rules: list[DesignRule] | None = None, cadmap: CadMap | None = None, settings: SkeletonSettings | None = None, name: str = '') Bundle[source]#
Build a Bundle from a fitted Skeleton: body lines from its cylinders, entry lines from its entries.
Each body cylinder becomes one
FreeLine; each resolved entry births its chain, leader and bridge (_lines_of_entry()) — an entry none of whose members fitted is skipped.HarnessWiringthen clusters the endpoints and materializes the multi-port junction connectors.
- interactions(max_shift: float, exclude_tree_hops: int = 1) BundleInteractions[source]#
Snapshot both optimizer-controlled interaction sets for a
max_shiftmovement budget.
- invalidate_topology_caches() None[source]#
Drop the cached topology views after a wiring mutation (lines or points changed).
- property lines_by_point: dict[BundlePoint, list[BundleLine]]#
Map each canonical BundlePoint to the lines incident to it (topology, fixed post-wiring).
- materialize_values(values: list[list[float] | float]) Bundle[source]#
Write captured variable values onto the updatable points and return self (mutates in place).
Inverse of
capture_values(): values and points are index-aligned on the same topology.
- path_measures() BundlePathMeasures[source]#
Along-path branch and lead-in lengths of the current geometry (see
BundlePathMeasures).
- repaired() Bundle[source]#
Return a new Bundle re-consolidated through the skeleton round-trip (topology cleaned), ready to re-optimize.
- show() dict[str, Any]#
Implementation of a tag for displays.
- show_lines_and_local_voxels() VolumeModel#
Implementation of a tag for displays.
- successive_line_pairs() list[Turn][source]#
Pairs
(line_a, line_b, shared_point)sharing a BundlePoint and at least one Solution.Captures successive segments of a same pipe route — the angle at their common point must remain non-acute to avoid a back-bend on a real pipe. Two incident lines that share no solution are considered independent branches and are not paired here.
- to_cylinders() list[Cylinder][source]#
Non-degenerate body lines as plain Cylinders (packed radius): the voxelization input for re-skeletonization.
Imposed geometry stays out: entry geometry is re-derived from the ports at rebirth (entry resolution in
Skeleton.fit_to(), then_lines_of_entry()), and voxelizing the entry corridor would re-skeletonize it as a body branch superposed with the reborn leaders.
- to_skeleton(settings: SkeletonSettings | None = None) Skeleton[source]#
Re-derive a voxel Skeleton from the current geometry, re-attaching each solution by its A* polyline.
Voxelizes the bundle’s cylinders (packed radius consolidates co-located lines), thins to the medial axis, then fits the original routing solutions:
Skeleton.fit_to()hugs eachSolution.polyline_pointsand tags the walked nodes, soextract_cylindersrecovers which solutions traverse each segment. The fit starts from the leaders’ current tips (_port_extensions()), so the optimized leader lengths carry over to the rebuilt bundle instead of being re-derived from the raw routing endpoints.
- topology_signature() tuple[int, int, int, int, int][source]#
Hashable fingerprint of the current topology; constant across two reruns ⇒
repaired()is idempotent ⇒ frozen.Counts (lines, points, junctions = points of incidence > 2, branches, lead-ins). Position-independent, so a GA pass (which only moves points) never changes it — only
repaired()does.
- verify_topology_signature(signature: tuple[int, ...] | list[int] | None, record_label: str) None[source]#
Fail loud when this bundle’s topology no longer matches a recorded
signature.Replaying values captured on another topology (repair, consolidation) silently corrupts the bundle — meter-long lines, variables outside every bound.
None(a record predating the signatures) passes unchecked, as before.
- class routing.core.bundling.BundleElement(name: str = '')[source]#
Bases:
DessiaObjectPlatform-serializable element of the bundle graph: compared by data, identified by instance.
_eq_is_data_eq = Falsekeeps DessiaObject’s equality and hash on instance identity, which the graph depends on: elements are dict keys, cluster and rewire by identity, andBundle.__deepcopy__()orchestrates their structural copy._data_eq/_data_eq_dictstay the hooks dessia’s walkers (data_eq,diff) consume, read over the serialization surface.nameis a class attribute: an element is identified by its role in the graph, not by a label, and none is stored per instance.- name = ''#
- class routing.core.bundling.BundleInteractions(collision_pairs: list[CollisionPair], successive_pairs: list[Turn])[source]#
Bases:
objectLine interactions the optimizer controls on a bundle, snapshotted for a movement budget.
Two controlled sets, built once by
Bundle.interactions()and shared by the seed relaxation and the optimization terms:collision_pairs(interpenetration) andsuccessive_pairs(back-bend). Snapshotting at the seed state is valid for every position the optimizer can reach: the pairs widen each line’s AABB bymax_shift(seeBundle.collision_candidate_pairs()), which bounds the reachable box.- collision_pairs: list[CollisionPair]#
- class routing.core.bundling.BundleLine(start: BundlePoint, end: BundlePoint, solutions: tuple[Solution, ...])[source]#
Bases:
BundleElement,Displayable3D,ABCA segment of the bundle defined by its two endpoints and the solutions traversing it.
Abstract family:
FreeLineis the plain harness segment,PortLinethe port leader. Subclasses define the optimizer-controlled span, the routable seed and the minimum admissible length.- Parameters:
- build_local_cadmap(global_cadmap: CadMap, max_shift: float, cropping_voxels: RoutingVoxelization, cropping_distance_matrices: list[ndarray]) None[source]#
Build the CadMap local to this segment’s
±max_shiftAABB, with packed-radius clearance carved in.The AABB is voxelized by carving out
ruled_volumes, intersected withcropping_voxels(the global navigable domain at the local resolution, prepared once by the caller and shared by every line), reduced to the navigable component carrying the routable endpoint, then weighted once: a constant clearanceWeightingRulezeroes voxels whose min CAD distance is belowself.radius. The fixed port anchor is rigid imposed geometry and is deliberately not required to be navigable.cropping_distance_matriceshold each ruled volume’s CAD distances overcropping_voxels’s grid. This segment’s sub-grid sits at an integer voxel offset inside that grid, so its distances are sliced out (masked to its active voxels) and injected, sparing a per-line distance recomputation.Domain construction only: posing the points in it (snap) and clipping the optimizer windows are orchestrated by
Bundle.attach_local_cadmaps(), once every line’s domain exists.
- cad_penetration(alpha: float = 0.25, samples_per_voxel: float = 1.0) float[source]#
Deepest pipe-surface penetration into CAD along the scored span;
0.0= clean.Reads the continuous CAD clearance along
_scoring_segment()(CadMap.clearance_along()), returningmax(0, max_t((radius + alpha*voxel) - clearance))in world units. Evaluated at the continuous centreline, it catches a centreline grazing CAD inside an otherwise-legal voxel — which a voxel-crossing count cannot.- Parameters:
alpha – safety margin in voxel units, padding
radiusto absorb the trilinear optimism near reentrant CAD corners;0tests the bare packed radius.samples_per_voxel – span sampling density;
>= 2keeps a CAD feature slipping between two samples negligible at voxel scale.
- clip_bounds_in_cadmap(max_shift: float) None[source]#
Clip points bounds into routable domain in order not to test obviously bad candidates.
- property direction: ndarray#
Unit vector from start to end; zero when degenerate.
- enlarged_pipe_line(margin: float) PipeLine[source]#
PipeLine with
marginadded to the packed radius — a clearance footprint around the line.
- property integral_radii: float#
Experimental length weightening depending on radii.
- interpenetration(other: BundleLine) float[source]#
Radii sum minus segment-to-segment distance; positive = the two lines interpenetrate.
- property is_rigid: bool#
Imposed geometry: no optimizer-controlled endpoint — outside every optimization term.
- property length: float#
Euclidean length between the two posed endpoints.
- mean_traversed_weight() float[source]#
Mean of the non-zero
traversed_voxel_weights();0.0when none.A span traversing only zero-weight voxels (a zero-length articulation line in the carved clearance band, a chord inside an inactive pocket) carries no navigation cost here — its infeasibility is CadInterference’s verdict, not WeightedLength’s.
- abstract property min_length: float#
Minimum admissible length of this segment.
- property min_required_length: float#
Largest
min_straight_length(incl. 2x curvature_radius) over the carried solutions.
- outgoing_alignment(other: BundleLine, shared_point: BundlePoint) float[source]#
Dot of the two outgoing unit vectors at
shared_point; positive = back-bend (acute angle).
- outgoing_unit(shared_point: BundlePoint) ndarray[source]#
Return the unit vector from
shared_pointtoward this line’s other endpoint; zero when degenerate.
- property pipe_line: PipeLine#
PipeLine derived from the current endpoint positions and packed radius.
- property points: tuple[BundlePoint, BundlePoint]#
The two endpoint BundlePoints of this line.
- property radii: tuple[float, ...]#
Pipe radii of the distinct solutions walking this segment.
- property radius: float#
Packed radius derived from the carried solutions.
- rewire(canonical_by_point: dict[int, BundlePoint]) None[source]#
Replace this line’s endpoints by their cluster canonicals.
Whether the two lines are adjacent segments (at least one common endpoint).
Whether the two lines carry at least one common Solution (segments of one route).
- show() dict[str, Any]#
Implementation of a tag for displays.
- traversed_voxel_weights() ndarray[source]#
Weights of every voxel traversed by the scored span in the local CadMap.
Delegates to
CadMap.traversed_weights()along_scoring_segment()(the optimizer-controlled span).
- volmdlr_primitives(**kwargs) list[Cylinder | None][source]#
Get 3D cylinder primitives for visualization.
- property voxel_size: float#
Voxel size of the local grid.
- class routing.core.bundling.BundlePoint(skeleton_position: tuple[float, float, float], variable: BundleVariable)[source]#
Bases:
BundleElementA point in the bundle whose posed 3D position is driven by a BundleVariable.
- Parameters:
skeleton_position – Birth position on the skeleton — topological identity used for endpoint clustering; immutable.
variable – Variable posing this point’s physical position.
- classmethod anchored(position: ndarray) BundlePoint[source]#
Build a rigid imposed point born at
position(AnchorVariable).
- property base_position: ndarray#
Rigid-prefix end of the underlying variable.
- can_merge_with(other: BundlePoint) bool[source]#
Whether the two points may collapse onto one canonical: both variables must accept.
- clip_in_cadmap(cadmap: CadMap, max_shift: float) None[source]#
Accumulate this point’s optimizer bounds from one incident line’s local CadMap.
- classmethod free(position: ndarray) BundlePoint[source]#
Build a free point born at
position(PointVariable).
- property is_updatable: bool#
Whether the optimizer is allowed to mutate this point.
- lower: ndarray | float | None#
- classmethod on_port_axis(port: Port, position: ndarray, min_length: float) BundlePoint[source]#
Build a point born at
positionwhose pose slides onport’s axis (PortVariable).
- property position: ndarray#
Current posed 3D position, derived from the variable.
- snap_in_cadmap(cadmap: CadMap) None[source]#
Snap the point off a forbidden or off-grid voxel onto the nearest active one.
- upper: ndarray | float | None#
- class routing.core.bundling.BundleVariable(value: tuple[float, float, float] | float)[source]#
Bases:
BundleElement,ABCAbstract variable controlling a point’s position in the bundle.
Subclasses define how the variable value maps to a 3D position (anchored to rigid geometry, free point, or distance along a port line).
- Parameters:
value – Current variable value (scalar or 3D array depending on subclass).
- COLOR = (0.1, 0.1, 0.1)#
- property base_position: ndarray#
Position where this variable’s rigid prefix ends; equals
positionwhen nothing is rigid.
- abstractmethod bounds_in_cadmap(cadmap: CadMap, max_shift: float) tuple[ndarray, ndarray] | tuple[float, float] | None[source]#
Admissible optimizer bounds in
cadmapfor amax_shiftmove;Nonewhen immovable.
- can_merge_with(other: BundleVariable) bool[source]#
Whether this variable accepts collapsing onto a shared point with
other.
- copied_value() float | ndarray[source]#
Return an independent copy of the current value (array copied, scalar returned as-is).
- property is_updatable: bool#
Whether the variable’s value can be mutated by the optimizer.
- abstractmethod neighbor_values(voxel_size: float, max_search: int, lower: ndarray | float, upper: ndarray | float) list[ndarray | float][source]#
Neighbor values within
max_searchvoxel steps of the current value, clipped to[lower, upper].
- abstract property position: ndarray#
3D position derived from the variable’s value.
- abstractmethod snap_in_cadmap(cadmap: CadMap) None[source]#
Move onto the nearest active voxel when off
cadmap’s navigable domain; no-op when already active.
- abstract property value: float | ndarray#
Current variable value.
- class routing.core.bundling.CollisionPair(line_a: BundleLine, line_b: BundleLine)[source]#
Bases:
NamedTupleTwo disjoint bundle lines whose interpenetration the optimizer controls.
- line_a: BundleLine#
Alias for field number 0
- line_b: BundleLine#
Alias for field number 1
- class routing.core.bundling.FixedLine(start: BundlePoint, end: BundlePoint, solutions: tuple[Solution, ...])[source]#
Bases:
BundleLineRigid segment between two anchors along a shared entry corridor — imposed geometry.
Born by the entry birth (
Bundle._lines_of_entry()): along a row of coaxial connectors, the stretch between two successive anchor stations is walked by the cables already entered but offers no degree of freedom. Only its length enters the path measures; every optimization term skips rigid lines.- property min_length: float#
Rigid: the current length is the only admissible one.
- class routing.core.bundling.FreeLine(start: BundlePoint, end: BundlePoint, solutions: tuple[Solution, ...])[source]#
Bases:
BundleLineFree segment of the harness.
- classmethod from_cylinder(cylinder: SkeletonCylinder) FreeLine[source]#
Build a FreeLine from a port-less cylinder.
- property min_length: float#
Minimum admissible length: the carried solutions’ requirement.
- class routing.core.bundling.HarnessWiring(lines: list[BundleLine], clustering_threshold: float)[source]#
Bases:
objectWire born lines into a harness: cluster endpoints, resolve multi-port junctions, connect.
Consumes the lines as built by the factories — one per body cylinder plus each entry’s chain, whose joints are the only endpoints born shared — and exposes the wired result:
lines(extended in place with the junction connectors) andharness_points(the canonical points, in cluster-resolution order).- harness_points: list[BundlePoint]#
- wire() HarnessWiring[source]#
Wire the born lines in three stages: proximity zones, fork lifting, normalization.
Proximity clustering resolves each zone of coincident endpoints (
_resolve_component()): ports fan out, free points centroid-merge; a sub-voxel line whose endpoints fell in one cluster collapses to a self-edge and is dropped by the shared redirection gesture (its canonical survives and still links the real neighbours). The forks left on lone tips are lifted (_lift_port_axis_forks()) — after which a tip carries at most one body line, so no cycle can ride a port axis. Normalization (_normalize()) finally drives the graph to its invariants: simple, no dead end, no filled cycle.
- class routing.core.bundling.JunctionCycle(points: list[BundlePoint], ring_lines: list[BundleLine], incident_counts: list[int])[source]#
Bases:
NamedTupleA bundle cycle evaluated for degeneracy: a delta encloses no void; a real loop does.
- classmethod cycles_in(lines: list[BundleLine]) list[JunctionCycle][source]#
Enumerate the independent cycles of the lines’ point graph (
nx.cycle_basis).
- property has_transit_node: bool#
A pass-through (degree-2) point: the loop travels through, so it is a real loop, not a collapsed junction.
- property hole_fraction: float#
Polygon area left uncovered by the ring tubes: ~0 a filled delta, ~1 a loop around a real void.
- incident_counts: list[int]#
Alias for field number 2
- property is_degenerate: bool#
A junction-only cycle of free points enclosing no void — a skeletonization delta to collapse.
No port guard: entry birth and fork lifting leave every tip with at most one body line before cycles are examined, so a port tip cannot be a cycle node here. Were one ever passed, the shared resolver fans it out like any other zone and the collapse integrates its connectors.
- property is_parasitic: bool#
A degree-2 ring closed back onto a single junction — physically meaningless in a harness.
Born as an artifact of fusing two junctions linked by two parallel chains. Its transit nodes make it a “real loop” for
is_degenerate, so the birth normalization spares it; the repair dissolves it instead.
- points: list[BundlePoint]#
Alias for field number 0
- ring_lines: list[BundleLine]#
Alias for field number 1
- property touches_anchor: bool#
A rigid wall point — never moved or merged; a cheap rigidity guard (anchors are degree-1, so unreachable here).
- property transit_points: list[BundlePoint]#
The ring’s pass-through points — the loop’s interior, removed when a parasitic ring dissolves.
- class routing.core.bundling.PointVariable(value: tuple[float, float, float])[source]#
Bases:
BundleVariableVariable representing a free 3D point in space.
- COLOR = (0.0, 1.0, 1.0)#
- bounds_in_cadmap(cadmap: CadMap, max_shift: float) tuple[ndarray, ndarray][source]#
3D
±max_shiftbox around the current position, clipped to the grid extent.The clip lands on border-voxel centers, so a position in the outer half of a border voxel would fall outside its own window (up to half a voxel); the box is widened to contain it — a point’s window always holds its current position.
- neighbor_values(voxel_size: float, max_search: int, lower: ndarray, upper: ndarray) list[ndarray][source]#
Face/edge/corner neighbors within
max_searchvoxels, clipped to the bounds box.
- property position: ndarray#
Point’s 3D position (the value itself).
- snap_in_cadmap(cadmap: CadMap) None[source]#
Jump to the nearest active voxel center; no-op when already on an active voxel.
- property value: float | ndarray#
Current variable value.
- class routing.core.bundling.PortLine(start: BundlePoint, end: BundlePoint, solutions: tuple[Solution, ...], tip: BundlePoint, entry_ports: tuple[Port, ...] | None = None)[source]#
Bases:
BundleLinePort leader: anchored on the front port wall, its tip slides on the port axis.
Born once per entry corridor (
Bundle._lines_of_entry()), carrying every cable of its entry;entry_portslists the specification ports entering through it, under which the round-trip (Bundle._port_extensions()) re-keys the tip. A measurement view over its tip’s PortVariable — the leader’s imposed geometry (port, axis, minimum length) has a single owner,tip_variable.- Parameters:
tip – The PortVariable endpoint (one of
start/end), resolved at birth and rewired with clustering — never re-derived from the endpoint types.entry_ports – Specification ports entering through this leader; defaults to the tip’s port alone.
- property anchor: BundlePoint#
Wall-anchored endpoint of the leader (the endpoint that is not the tip).
- clear_base_margin(alpha: float) None[source]#
Pose the leader base at the first clear abscissa of its axis, so the scored span
[base, tip]is satisfiable.The base sits at
port + min_length*directionand never moves with the optimizer: born where the axis clearance is under the marginradius + alpha*voxel, it is an irreducible penetration. Clearance along a port axis is not monotonic — an entry stretch may cross a neighbouring body past the wall — so the first clear abscissa is measured in the local CadMap rather than assumed to sit one margin from the wall. Scans forward only (min_lengthalso carries the cables’ own entry requirements), within the tip’s optimizer bounds; reports and leaves the leader as born when the whole admissible axis is obstructed. Part of the pass preparation: requires a prepared line (domain built, bounds clipped).
- property min_length: float#
Minimum admissible length of the leader: the tip’s minimum distance from the port.
- rewire(canonical_by_point: dict[int, BundlePoint]) None[source]#
Endpoints plus the tip role follow their cluster canonicals.
- snap_points_in_cadmap() None[source]#
Pose the tip so the whole scored span fits: walkable fast path, clearance-field refinement.
The anchor keeps the generic snap. The tip is kept when every voxel from
min_lengthto it is walkable (the rigid prefix beforemin_lengthmay legitimately interfere). Otherwise the clearance field arbitrates — immune to the navigable-component carving — and the tip is posed at the admissible abscissa closest to it: the end of the first fitting run frommin_length. A span fitting nowhere is a port-placement fault: reported, left as born (Spec territory).
- property tip_variable: PortVariable#
The leader’s single degree of freedom (the tip’s PortVariable).
- class routing.core.bundling.PortVariable(value: float, port: Port, direction: tuple[float, float, float], min_length: float)[source]#
Bases:
BundleVariableVariable representing a distance from a port along a fixed direction into the bundle.
- COLOR = (0.0, 0.8, 0.0)#
- property base_position: ndarray#
Position at the variable’s lower bound; the [port, base] span is rigid imposed geometry.
- bounds_in_cadmap(cadmap: CadMap, max_shift: float) tuple[float, float][source]#
Scalar
±max_shiftwindow around the current value along the port axis, floored atmin_length.
- can_merge_with(other: BundleVariable) bool[source]#
Merge freely, except with another port’s tip: a point cannot ride two axes.
The same-port exception is a guard completing the rule, not an expected case: entry resolution (
Skeleton.fit_to()) births at most one leader per entry corridor, so two tips reaching this test belong to different entries whatever their Port objects.
- classmethod from_port(port: Port, extension_position: ndarray, min_length: float) PortVariable[source]#
Build a PortVariable for an extension endpoint at
extension_position, posed on the port axis.
- neighbor_values(voxel_size: float, max_search: int, lower: float, upper: float) list[float][source]#
Return steps of
±voxel_sizealong the port axis, clipped to the admissible distance range.
- property position: ndarray#
3D position obtained by moving
valuefrom the port alongdirection.
- snap_in_cadmap(cadmap: CadMap) None[source]#
Pose the tip on the active abscissa closest to it, reachable contiguously from
min_length.The leader is a straight tube from its base: an active tip beyond an inactive stretch still spans through it, so the pose walks the axis from
min_lengthand stops at the end of the first active run. No-op when the whole span is active; left as born when the span is inactive from the base on (embedded port base —PortLine.clear_base_margin()territory).
- property value: float | ndarray#
Current variable value.
- class routing.core.bundling.Turn(line_a: BundleLine, line_b: BundleLine, shared_point: BundlePoint)[source]#
Bases:
NamedTupleTwo successive segments of one route meeting at a shared point; their angle must stay non-acute.
- line_a: BundleLine#
Alias for field number 0
- line_b: BundleLine#
Alias for field number 1
Alias for field number 2
- routing.core.bundling.merge_parallel_edges(lines: list[BundleLine]) bool[source]#
Fuse multigraph parallel edges: free lines sharing both endpoints are one physical segment.
Two skeleton strands of one corridor (the per-solution Dijkstra of
Skeleton.fit_to()assigns, never fuses) whose endpoints clustered onto the same two canonicals form a 2-node cycle — unrepresentable in the simple line graph, hence invisible to the delta resolver. A junction fusion leaves the same object: two direct lines landing on the same point pair. Each parallel group is rebuilt as one carrier line taking the order-preserving union of the solutions (the packed radius re-derives from them). Leaders never fuse (imposed geometry) — structurally their rigid anchor refuses every merge, so a PortLine cannot share both canonicals anyway. Returns whether at least one group fused.
- routing.core.bundling.redirect_points(redirections: dict[int, BundlePoint], lines: list[BundleLine], rewired_lines: list[BundleLine] | None = None) None[source]#
Rewire lines through
redirections— identity elsewhere — and drop the collapsed self-edges.The single point-redirection gesture of the harness graph, shared by the wiring (zone collapse, leader consolidation, fork lifting) and the repair (junction fusion):
BundleLine.rewire()demands a total mapping, so the overrides are laid over an identity map of every endpoint in play.linesis the full inventory (identity domain, filtered in place);rewired_linesrestricts which lines are rewired (default: all).