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. .. contents:: On this page :local: :depth: 2 Installation ------------ Install with pip: .. code-block:: bash pip install routing **Requirements**: Python ≥ 3.9. All core dependencies (volmdlr, numpy, scipy, piping_3d, etc.) are installed automatically. Verify the installation: .. code-block:: python 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. .. image:: ../_static/index-images/getting_started.svg :align: center Step 1 — Load a CAD Model ~~~~~~~~~~~~~~~~~~~~~~~~~~ ``routing`` uses `volmdlr `_ to load STEP/STP files: .. code-block:: python 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 :class:`~routing.core.core.RuledVolume` wraps a CAD volume and attaches design rules to it — for example, a :class:`~routing.core.design_rules.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. .. code-block:: python 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 :class:`~routing.core.core.PipeType` describes the physical properties of a pipe: its outer radius and minimum bend radius: .. code-block:: python 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 :class:`~routing.core.core.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: .. code-block:: python 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 :class:`~routing.core.core.Specification` pairs a start port, end port, and pipe type into a routing requirement. Create one per pipe to route: .. code-block:: python 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 :class:`~routing.core.cadmap.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: .. code-block:: python 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 :class:`~routing.core.route_planner.RoutePlanner` and call :meth:`~routing.core.route_planner.RoutePlanner.generate` with your list of specifications: .. code-block:: python 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 :class:`~routing.core.route_planner.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 :meth:`~routing.core.route_planner.RoutePlanner.optimize` step refines them into smooth geometry respecting port tangency: .. code-block:: python 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: .. code-block:: python # 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 :mod:`routing.core.verbose` to control the amount of output: .. code-block:: python 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: - :doc:`user_guide/core_concepts` — understand every class in the routing pipeline - :doc:`tutorials/basic_routing` — full step-by-step tutorial with the window test case - :doc:`tutorials/design_rules` — control routing with engineering constraints - :doc:`tutorials/shaped_zones` — define attractive, repulsive, forbidden, and forced zones - :doc:`tutorials/pathfinding_basic` — use the standalone 3D pathfinding module - :doc:`user_guide/optimization` — tune the optimizer for your use case - :doc:`scripts` — browse all example scripts