Swept Shapes#

Recognize swept profiles (pipes, tubes, extruded channels) from BRep models.

Note

Prerequisites: Attributed Adjacency Graph (AAG)

Swept Shape Recognition#

This module identifies swept shapes (extruded profiles) from BRep models. It recognizes pipes, tubes, and other shapes formed by sweeping a cross-section along a path.


Overview#

A swept shape is characterized by:

  • Extremity faces: The start and end faces of the sweep

  • Neutral fiber: The centerline path along which the profile was swept

  • Section: The cross-sectional profile perpendicular to the sweep path

  • Smoothness: The neutral fiber has C1 continuity (no sharp corners)

Common examples:

  • Circular pipes and tubes

  • Extruded profiles (channels, I-beams)

  • Cable routing paths

  • Duct sections


Quick Start#

from volmdlr.model import VolumeModel
from volmdlr_tools.shapes.recognizers import SweptShapeRecognizer

# Load a STEP file
volume_model = VolumeModel.from_step("path/to/pipe.step")
shape = volume_model.primitives[0]

# Recognize swept shape
recognizer = SweptShapeRecognizer(shape=shape)

if recognizer.perform():
    # Get sweep properties
    neutral_fiber = recognizer.neutral_fiber
    section = recognizer.section
    radius_info = recognizer.get_radius()

    print(f"Neutral fiber length: {neutral_fiber.length()}")
    print(f"Outer radius: {radius_info['outer_contour']}")
else:
    print("Not a valid swept shape")

Core Class: SweptShapeRecognizer#

The main entry point for swept shape recognition. It analyzes a BRep shape to determine if it’s a swept profile and extracts its geometric properties.

Constructor#

SweptShapeRecognizer(
    shape: Union[shapes.Shell, shapes.Solid],
    aag: Optional[AttributedAdjacencyGraph] = None,
    name: str = ""
)

Parameters:

  • shape: The BRep shape to analyze (Shell or Solid)

  • aag: (Optional) Pre-computed AttributedAdjacencyGraph

  • name: (Optional) Name for the recognizer instance

Methods#

perform() -> bool#

Execute the recognition algorithm.

recognizer = SweptShapeRecognizer(shape=my_shape)
is_swept = recognizer.perform()

Returns: True if the shape is a valid swept shape, False otherwise.

get_radius() -> dict#

Get the radius information for circular sweeps.

radius_info = recognizer.get_radius()
print(f"Outer radius: {radius_info['outer_contour']}")
print(f"Inner radii: {radius_info['inner_contours']}")

Returns: Dictionary with keys:

  • outer_contour: Radius of outer contour (or None if not circular)

  • inner_contours: List of radii for inner contours (holes)

Properties#

Property

Type

Description

neutral_fiber

Wire3D

The centerline path of the sweep

section

Face3D

The cross-sectional profile

shape

Shell/Solid

The input BRep shape

aag

AttributedAdjacencyGraph

The face adjacency graph


Recognition Algorithm#

The recognition process:

  1. Find extremity candidates: Identify planar or BSpline faces that could be sweep endpoints

    • Faces with only convex neighbors

    • At most 1-2 smooth angles depending on neighbor count

  2. Validate extremities: Group candidates into two coplanar groups (start and end faces)

  3. Find shortest path: Compute the graph path between extremity faces

    • Uses the AAG to find the path through adjacent faces

    • Path represents the sweep body faces

  4. Validate path: Verify the path represents a valid sweep

    • All intermediate faces should be smooth-connected

    • Path should be continuous

  5. Extract properties: Compute neutral fiber and section from validated path


Examples#

Basic Pipe Recognition#

from volmdlr.model import VolumeModel
from volmdlr_tools.shapes.recognizers import SweptShapeRecognizer

# Load pipe model
volume_model = VolumeModel.from_step("pipe.step")
pipe_shape = volume_model.primitives[0]

# Recognize
recognizer = SweptShapeRecognizer(shape=pipe_shape)

if recognizer.perform():
    # Get neutral fiber (centerline)
    centerline = recognizer.neutral_fiber
    print(f"Pipe length: {centerline.length():.2f} mm")

    # Get cross-section
    section = recognizer.section
    print(f"Section area: {section.area():.2f} mm²")

    # Get radius for circular pipes
    radii = recognizer.get_radius()
    if radii['outer_contour']:
        outer_r = radii['outer_contour']
        inner_r = radii['inner_contours'][0] if radii['inner_contours'] else 0
        wall_thickness = outer_r - inner_r
        print(f"Wall thickness: {wall_thickness:.2f} mm")

Visualizing Results#

from volmdlr.model import VolumeModel

# After recognition
neutral_fiber = recognizer.neutral_fiber
section = recognizer.section

# Create visualization primitives
prims = [pipe_shape]

if neutral_fiber:
    # Display neutral fiber as colored wire
    neutral_fiber.color = (1.0, 0.0, 0.0)  # Red
    prims.append(neutral_fiber)

if section:
    # Display section face
    section.color = (0.0, 1.0, 0.0)  # Green
    prims.append(section)

VolumeModel(prims).babylonjs()

Working with Non-Circular Sections#

# For non-circular extruded profiles
recognizer = SweptShapeRecognizer(shape=channel_shape)

if recognizer.perform():
    section = recognizer.section

    # Get section contour
    outer_contour = section.surface2d.outer_contour

    # Analyze contour geometry
    print(f"Contour perimeter: {outer_contour.length()}")
    print(f"Number of edges: {len(outer_contour.primitives)}")

    # Check for holes
    inner_contours = section.surface2d.inner_contours
    print(f"Number of holes: {len(inner_contours)}")

Use Cases#

Pipe Routing Analysis#

def analyze_pipe_routing(shapes):
    """Analyze a collection of pipes for routing information."""
    pipe_data = []

    for shape in shapes:
        recognizer = SweptShapeRecognizer(shape=shape)
        if recognizer.perform():
            fiber = recognizer.neutral_fiber
            radii = recognizer.get_radius()

            pipe_data.append({
                'length': fiber.length(),
                'outer_radius': radii['outer_contour'],
                'start_point': fiber.primitives[0].start,
                'end_point': fiber.primitives[-1].end,
            })

    return pipe_data

Cable Tray Identification#

def is_cable_tray(shape):
    """Check if a shape is a cable tray (open channel profile)."""
    recognizer = SweptShapeRecognizer(shape=shape)

    if not recognizer.perform():
        return False

    section = recognizer.section
    if section is None:
        return False

    # Cable trays typically have U-shaped open profiles
    # Check if section has no inner contours (not a closed tube)
    inner_contours = section.surface2d.inner_contours
    return len(inner_contours) == 0

Limitations#

  • Works best with smooth sweeps (C1 continuous neutral fiber)

  • Extremity faces must be planar or BSpline surfaces

  • Complex multi-branch sweeps are not supported

  • Very short sweeps (fewer than 3 faces) may have limited property extraction


See Also#

See Also#