Tutorial: Routing Inside a Closed Volume ========================================== By default a :class:`~routing.core.core.RuledVolume` voxelizes only the *shell* of a solid: the volume acts as an obstacle and pipes are routed *around* it. Sometimes you want the opposite — to route a pipe *through* the interior of a closed body, such as a chamber, a duct or an enclosure. Setting ``voxel_inside=True`` fills the enclosed voxels so the solver can route inside the solid. This tutorial contrasts the two modes on a single cube: one route outside it, one route through it. The runnable version is ``scripts/simple_test_cases/cube_case.py``. .. contents:: On this page :local: :depth: 1 The ``voxel_inside`` Flag ------------------------- :meth:`~routing.core.core.RuledVolume.from_volume_model` accepts a ``voxel_inside`` flag: .. list-table:: :header-rows: 1 :widths: 25 75 * - ``voxel_inside`` - Behavior * - ``False`` (default) - Only the solid's surface is voxelized. The body is an obstacle; pipes route around it. * - ``True`` - Enclosed voxels are filled. The interior becomes routable space; pipes can pass through. Shared Setup ------------ Both runs use the same volume model, weighting rule and design rules: .. code-block:: python import os from pathlib import Path from volmdlr.model import VolumeModel from routing.core.cadmap import CadMap from routing.core.core import PipeType, Port, RuledVolume, Specification from routing.core.design_rules import PoolingRule, TurnPenalizer, WeightingRule from routing.core.finders import SmartFinder from routing.core.legacy_piping import Section from routing.core.route_planner import RoutePlanner current_folder = Path(__file__).parent voxel_size = 0.005 volume_model = VolumeModel.from_json( os.path.join(current_folder, "../../data/json/cubic_volume_model.json") ) weighting_rule = WeightingRule( function="linear", min_length=0.0, max_length=1.0, min_value=1, max_value=10 ) pooling_rule = PoolingRule( pooling_specs=[], min_distance=0.0, distance_multiplier=500, volume_indices=[], name="pooling_rule", ) design_rules = [TurnPenalizer(cost=3.0), pooling_rule] pipe_type = PipeType(section=Section(0.005, name="section"), name="test_pipe", color=(0, 0.8, 0.8)) Routing Outside the Cube (default) ---------------------------------- With ports placed just outside the cube and the volume left as an obstacle (``voxel_inside`` defaults to ``False``), the pipe routes around the body: .. code-block:: python ruled_volume = RuledVolume.from_volume_model(volume_model, [weighting_rule], name="cubic_volume") input_port = Port(coordinates=(0.11, 0, 0), direction=(1, 0, 0), length=0.02) output_port = Port(coordinates=(-0.11, 0, 0), direction=(-1, 0, 0), length=0.02) specifications = [Specification(input_port, output_port, pipe_type, "test")] ports_prims = [input_port.volmdlr_primitives(radius=0.01), output_port.volmdlr_primitives(radius=0.01)] ports_model = RuledVolume.from_volume_model(VolumeModel(ports_prims), [], name="ports") bounding_box = CadMap.build_bounding_volume( [ruled_volume, ports_model], [], voxel_size, voxel_margin=30 ) cadmap = CadMap.from_project_inputs( design_rules=design_rules, ruled_volumes=[ruled_volume], bounding_box=bounding_box, voxel_size=voxel_size, exact_distances=True, weights_operator="min_distance", build_cad=False, ) finder = SmartFinder("AStar", "manhattan", "never") routing_result = RoutePlanner(cadmap, finder).generate(specifications, 0, False) routing_result.display_3d() Routing Through the Cube (``voxel_inside=True``) ------------------------------------------------ Now place the ports *inside* the cube and rebuild the ``RuledVolume`` with ``voxel_inside=True`` so the interior is routable. Note the ``voxel_margin=0`` — the route stays within the body: .. code-block:: python input_port = Port(coordinates=(0.1, 0, 0), direction=(-1, 0, 0), length=0.005) output_port = Port(coordinates=(-0.1, 0, 0), direction=(1, 0, 0), length=0.005) specifications = [Specification(input_port, output_port, pipe_type, "test")] ports_prims = [input_port.volmdlr_primitives(radius=0.01), output_port.volmdlr_primitives(radius=0.01)] ports_model = RuledVolume.from_volume_model(VolumeModel(ports_prims), [], name="ports") bounding_box = CadMap.build_bounding_volume( [ruled_volume, ports_model], [], voxel_size, voxel_margin=0 ) ruled_volume = RuledVolume.from_volume_model( volume_model, [weighting_rule], voxel_inside=True, name="" ) cadmap = CadMap.from_project_inputs( design_rules=design_rules, ruled_volumes=[ruled_volume], bounding_box=bounding_box, voxel_size=voxel_size, exact_distances=True, weights_operator="min_distance", build_cad=True, ) finder = SmartFinder("AStar", "manhattan", "never") routing_result = RoutePlanner(cadmap, finder).generate(specifications, 0, False) routing_result.display_3d() The only change that switches outside vs inside routing is the ``voxel_inside`` flag on ``from_volume_model`` (together with placing the ports inside the body and using a zero voxel margin). Next Steps ---------- - :doc:`basic_routing` — the full routing workflow. - :doc:`large_room_obstacles` — routing around obstacles in an open space. - :doc:`../user_guide/core_concepts` — ``RuledVolume`` and ``CadMap`` reference.