Quickstart#
routing is a Python package for automatically routing pipes, tubes, and wires through
3D CAD environments. Given a 3D model of your assembly, port connection points, and a set
of design rules, routing finds and optimizes feasible paths that respect your engineering
constraints.
Installation#
Install with pip:
pip install routing
Requirements: Python ≥ 3.9.
All core dependencies (volmdlr, numpy, scipy, piping_3d, etc.) are installed automatically.
Verify the installation:
import routing
print(routing.__version__)
Your First Routing Problem#
This example routes multiple pipes through a window frame assembly — a 3D CAD model with
two volumes (a border frame and an outer shell). We define where each pipe starts and ends,
what type of pipe it is, and let routing find the paths automatically.
Step 1 — Load a CAD Model#
routing uses volmdlr to load STEP/STP files:
from volmdlr.step import Step
from volmdlr.model import VolumeModel
model = Step.from_file("path/to/your_assembly.step")
volume_model = model.to_volume_model()
Step 2 — Wrap Volumes with Routing Rules#
A RuledVolume wraps a CAD volume and attaches design rules
to it — for example, a WeightingRule that tells the
router to prefer paths near the surface (lower cost = preferred path).
In this example, it is constituted with 2 parts: a outer volume (a box) and a inner volume (a box with window).
The step file can be found in data/ in the root directory of the routing package.
from routing.core.core import RuledVolume
from routing.core.design_rules import WeightingRule
# Penalize routing far from the surface (paths stay closer to the assembly)
weighting_rule = WeightingRule(
function="linear",
min_length=0.0, # start applying at distance 0
max_length=0.2, # stop applying at distance 0.2 m
min_value=1, # cost multiplier at min_length
max_value=10, # cost multiplier at max_length
)
# Wrap each CAD volume
outer_volume = RuledVolume.from_volume_model(
VolumeModel([volume_model.primitives[1]]),
design_rules=[weighting_rule],
name="outer",
)
border_volume = RuledVolume.from_volume_model(
VolumeModel([volume_model.primitives[0]]),
design_rules=[weighting_rule],
name="border",
)
Step 3 — Define Pipe Types#
A PipeType describes the physical properties of a pipe:
its outer radius and minimum bend radius:
from piping_3d import piping
from routing.core.core import PipeType
# A 30 mm diameter pipe (radius = 0.015 m)
section = piping.Section(radius_equivalent=0.015)
pipe_type_A = PipeType(
section=section,
name="PipeA",
radius_of_curvature_ratio_min=1.5, # min bend radius = 1.5 × diameter
length_before_turn_min=0.0, # no minimum straight segment required
color=(0, 100, 200), # RGB display color
type_="TypeA", # group label for pooling
)
Step 4 — Define Ports#
A Port is a connection point — a position in 3D space with
an orientation (the direction the pipe must leave/enter the port) and an optional minimum
straight length before the first turn:
from routing.core.core import Port
port_start = Port(
coordinates=(0.05, -0.4, 0.1), # (x, y, z) in metres
direction=(0, 1, 0), # pipe exits in +Y direction
length=0.1, # 100 mm straight before first turn
name="start_port",
)
port_end = Port(
coordinates=(0.05, 0.8, 0.1),
direction=(0, -1, 0), # pipe enters from +Y direction
length=0.1,
name="end_port",
)
Step 5 — Create Specifications#
A Specification pairs a start port, end port, and pipe type
into a routing requirement. Create one per pipe to route:
from routing.core.core import Specification
spec_A = Specification(
start=port_start,
end=port_end,
pipe_type=pipe_type_A,
name="PipeA_route",
)
Step 6 — Build the CadMap#
The CadMap voxelizes the routing space. The voxel_size
parameter controls resolution — smaller values are more accurate but slower.
A good starting point is 2–3× the largest pipe diameter:
from routing.core.cadmap import CadMap
from routing.core.design_rules import TurnPenalizer
voxel_size = 0.021 # 21 mm voxels
# Global design rules (apply to all pipe types)
design_rules = [TurnPenalizer(cost=3.0)]
# Build a bounding box around the routing space
bounding_box = CadMap.build_bounding_volume(
[outer_volume, border_volume],
border_rules=[],
voxel_size=voxel_size,
voxel_margin=1,
)
cadmap = CadMap.from_project_inputs(
design_rules=design_rules,
ruled_volumes=[outer_volume, border_volume],
bounding_box=bounding_box,
voxel_size=voxel_size,
exact_distances=True,
)
Step 7 — Generate Routing Paths#
Create a RoutePlanner and call
generate() with your list of specifications:
from routing.core.finders import SmartFinder
from routing.core.route_planner import RoutePlanner
# A* pathfinding with Manhattan heuristic, no diagonal movement
finder = SmartFinder("AStar", "manhattan", "never")
route_planner = RoutePlanner(cadmap, finder)
routing_result = route_planner.generate(
specifications=[spec_A],
n_iterations=1, # 0 = single best path (default); N = best + N alternatives
steady_mode=False, # stop on first failure
)
routing_result is a RoutingResults object
containing the found paths.
Step 8 — Optimize the Paths (optional)#
The paths from generate() follow the voxel grid and may look “staircase-like”.
The optimize() step refines them into
smooth geometry respecting port tangency:
from routing.core.optimizer import Costs, Settings
costs = Costs(
length=1000,
sideways=100,
short_line=5000,
gravity=5000,
bend=250,
constraints=5000,
interferences=1e8,
)
settings = Settings(
voxel_size=0.01,
max_shift=0.1,
stabilization_threshold=5.0,
n_iterations=3,
max_iter=1000,
)
routing_result = route_planner.optimize(
costs=costs,
settings=settings,
picked_path="best",
)
Step 9 — Visualize Results#
Display the result in a 3D viewer:
# Show generated paths
routing_result.plot_data_generated().plot()
# Collect and display pipe packs (bundled pipes)
packs = routing_result.collect_packs(only_optimized=False)
routing_result.specific_packs_cad_view(packs)
# Show optimized paths
routing_result.plot_data_optimized().plot()
Controlling Verbosity#
Use routing.core.verbose to control the amount of output:
from routing.core.verbose import VerboseLevel, verbose
verbose.set_level(VerboseLevel.INFO) # options: SILENT, WARNING, ERROR, INFO, DEBUG, TRACE
Set VerboseLevel.TRACE to see detailed pathfinding progress, or VerboseLevel.SILENT
to suppress all output.
Next Steps#
Now that you have the basics working, explore more features:
Core Concepts — understand every class in the routing pipeline
Tutorial: Routing Pipes Through a CAD Assembly — full step-by-step tutorial with the window test case
Tutorial: Engineering Design Rules — control routing with engineering constraints
Tutorial: Shaped Routing Zones — define attractive, repulsive, forbidden, and forced zones
Tutorial: 3D Pathfinding with routing.pathfinding — use the standalone 3D pathfinding module
User Guide: Path Geometry Optimization — tune the optimizer for your use case
Example Scripts — browse all example scripts