yapcad.collision package

Submodules

yapcad.collision.detector module

Main collision detection class with BREP/mesh/AABB methods.

This module defines the CollisionDetector class and GeometryProvider protocol for performing collision detection on assemblies.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.detector.CollisionDetector(geometry_provider: GeometryProvider, interface_registry: InterfaceRegistry | None = None, verbose: bool = False, min_collision_volume: float = 0.1, mesh_sample_points: int = 1500, penetration_threshold: float = 0.1)[source]

Bases: object

Main collision detection class with multiple detection methods.

CollisionDetector provides collision detection between assembly parts using the best available method:

  1. BREP detection (if pythonocc available): Uses boolean intersection for exact collision volume

  2. Mesh detection (if trimesh available): Uses point sampling and containment checks

  3. AABB detection (always available): Fast bounding box checks (used as pre-filter)

The detector integrates with InterfaceRegistry to distinguish expected overlaps (gear mesh, threads) from actual collisions.

geometry_provider

Provider for part geometry

interface_registry

Registry of allowed overlap interfaces

verbose

Enable detailed logging

min_collision_volume

Minimum volume to report (mm^3)

mesh_sample_points

Number of sample points for mesh detection

penetration_threshold

Minimum depth to count as collision (mm)

Example

>>> provider = MyGeometryProvider()
>>> detector = CollisionDetector(provider)
>>>
>>> # Optional: register allowed overlaps
>>> registry = InterfaceRegistry()
>>> registry.register(GearMeshInterface(...))
>>> detector.set_interface_registry(registry)
>>>
>>> # Check single pair
>>> result = detector.check_collision(
...     "PART_A", transform_a,
...     "PART_B", transform_b
... )
>>>
>>> # Check full assembly
>>> transforms = {"PART_A": tf_a, "PART_B": tf_b, "PART_C": tf_c}
>>> results = detector.check_assembly(transforms)
>>> collisions = [r for r in results if r.is_error]
check_assembly(world_transforms: Dict[str, Any], skip_pairs: List[Tuple[str, str]] | None = None) List[CollisionResult][source]

Check all pairs in an assembly for collisions.

Parameters:
  • world_transforms – Dictionary mapping part names to 4x4 transforms

  • skip_pairs – Optional list of (part_a, part_b) pairs to skip

Returns:

List of CollisionResult for all pairs checked

check_collision(part_a: str, transform_a: Any, part_b: str, transform_b: Any, method: CollisionMethod | None = None) CollisionResult[source]

Check for collision between two parts.

Parameters:
  • part_a – Name of first part

  • transform_a – 4x4 transformation matrix for part A

  • part_b – Name of second part

  • transform_b – 4x4 transformation matrix for part B

  • method – Specific detection method (default: best available)

Returns:

CollisionResult with collision details

clear_cache() None[source]

Clear the geometry cache.

get_collision_summary(results: List[CollisionResult]) Dict[str, Any][source]

Generate summary statistics from collision results.

Parameters:

results – List of CollisionResult from check_assembly

Returns:

Dictionary with summary statistics

property preferred_method: CollisionMethod

Get the preferred detection method based on available libraries.

set_interface_registry(registry: InterfaceRegistry) None[source]

Set the interface registry for allowed overlaps.

Parameters:

registry – InterfaceRegistry with interface definitions

class yapcad.collision.detector.GeometryProvider(*args, **kwargs)[source]

Bases: Protocol

Protocol for providing geometry data for collision detection.

Implementations must provide a way to retrieve geometry for parts by name. The geometry can be returned as:

  • Path to STEP file (preferred for BREP detection)

  • Path to STL file (for mesh detection)

  • trimesh.Trimesh object (pre-loaded mesh)

  • TopoDS_Shape object (pre-loaded BREP)

Example

>>> class FileBasedProvider:
...     def __init__(self, base_dir: Path):
...         self.base_dir = base_dir
...
...     def get_geometry(self, part_name: str) -> Optional[str]:
...         step_path = self.base_dir / f"{part_name}.step"
...         if step_path.exists():
...             return str(step_path)
...         stl_path = self.base_dir / f"{part_name}.stl"
...         if stl_path.exists():
...             return str(stl_path)
...         return None
get_geometry(part_name: str) Any | None[source]

Get geometry for a part by name.

Parameters:

part_name – Name/identifier of the part

Returns:

Path string, mesh object, or BREP shape for the part, or None if geometry not available

yapcad.collision.gear_interface module

Gear mesh interface for involute gear teeth overlap.

This module defines the GearMeshInterface class for representing gear teeth meshing regions where controlled overlap is expected.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.gear_interface.GearMeshInterface(name: str, part_name: str, center: Tuple[float, float, float]=(0.0, 0.0, 0.0), axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), description: str = '', metadata: Dict[str, ~typing.Any]=<factory>, module: float = 1.0, teeth: int = 20, pressure_angle: float = 20.0, face_width: float = 10.0, helix_angle: float = 0.0, backlash: float = 0.0)[source]

Bases: InterfaceVolume

Interface volume for involute gear teeth meshing.

Defines the region where gear teeth overlap during meshing. Two gears are compatible if they have the same module and pressure angle (standard involute tooth profile).

module

Gear module in mm (pitch_diameter / teeth)

Type:

float

teeth

Number of teeth on the gear

Type:

int

pressure_angle

Pressure angle in degrees (typically 20.0)

Type:

float

face_width

Width of gear face in mm (axial engagement length)

Type:

float

helix_angle

Helix angle in degrees for helical gears (0 for spur)

Type:

float

backlash

Design backlash in mm (0.0 for theoretical)

Type:

float

Derived Properties:

pitch_diameter: module * teeth addendum: module (tooth height above pitch circle) dedendum: 1.25 * module (tooth depth below pitch circle) outside_diameter: pitch_diameter + 2 * addendum root_diameter: pitch_diameter - 2 * dedendum base_diameter: pitch_diameter * cos(pressure_angle)

Example

>>> sun = GearMeshInterface(
...     name="sun_teeth", part_name="SUN_GEAR",
...     module=0.75, teeth=18, pressure_angle=20.0, face_width=10.0
... )
>>> planet = GearMeshInterface(
...     name="planet_teeth", part_name="PLANET_GEAR",
...     module=0.75, teeth=36, pressure_angle=20.0, face_width=10.0
... )
>>> result = sun.check_compatibility(planet)
>>> print(result)  # COMPATIBLE: Gears compatible: m=0.75mm, PA=20.0deg
>>> print(f"Center distance: {sun.get_center_distance(planet):.2f}mm")

Notes

  • Module must match exactly between meshing gears (within 0.1% tolerance)

  • Pressure angle must match (within 0.5 degree tolerance)

  • For helical gears, helix angles must be opposite (LH + RH = 0)

  • Face widths must have sufficient overlap for load transfer

property addendum: float

Addendum (tooth height above pitch circle) = module.

backlash: float = 0.0
property base_diameter: float

Base diameter for involute profile.

check_compatibility(other: InterfaceVolume) CompatibilityResult[source]

Check if this gear can mesh with another interface.

Compatible conditions:
  1. Other must be a GearMeshInterface

  2. Modules must match (within 0.1% tolerance)

  3. Pressure angles must match (within 0.5 degree tolerance)

  4. Helix angles must be compatible (opposite for external mesh)

  5. Face widths must have sufficient overlap

Parameters:

other – Another interface volume to check against

Returns:

CompatibilityResult indicating if gears can properly mesh

property dedendum: float

Dedendum (tooth depth below pitch circle) = 1.25 * module.

face_width: float = 10.0
get_bounding_cylinder() Tuple[float, float][source]

Get cylindrical bounding volume (radius, height).

Returns:

Tuple of (radius, height) where radius extends to outside diameter and height is the face width.

get_center_distance(other: GearMeshInterface) float[source]

Calculate theoretical center distance for meshing with another gear.

Parameters:

other – Another gear to mesh with

Returns:

Center-to-center distance for proper meshing in mm

Raises:

ValueError – If modules don’t match

get_engagement_depth() float[source]

Face width is the engagement depth for gears.

Returns:

Face width in mm

get_expected_overlap_volume(other: GearMeshInterface) float[source]

Calculate expected overlap volume when meshing with another gear.

This provides an estimate of how much volume the gear teeth should overlap when properly meshed, useful for validating collision detection results.

Parameters:

other – Another gear to mesh with

Returns:

Estimated overlap volume in mm^3

get_mesh_overlap_depth() float[source]

Calculate the theoretical tooth mesh overlap depth.

This is the radial depth where teeth from two meshing gears actually overlap, used for collision solver exceptions.

Returns:

Depth of tooth engagement zone in mm (approximately 2 * module)

helix_angle: float = 0.0
interface_type: InterfaceType = 1
module: float = 1.0
property outside_diameter: float

Outside diameter (tip of teeth).

property pitch_diameter: float

Pitch diameter = module * teeth.

pressure_angle: float = 20.0
property root_diameter: float

Root diameter (bottom of teeth).

teeth: int = 20
to_dict() Dict[str, Any][source]

Convert to dictionary for serialization.

Returns:

Dictionary with all gear parameters

yapcad.collision.gear_interface.check_gear_mesh_collision(gear_a: GearMeshInterface, gear_b: GearMeshInterface, actual_center_distance: float, tolerance: float = 0.1) CompatibilityResult[source]

Check if two gears are correctly positioned for meshing.

This function verifies both gear compatibility and proper positioning by comparing actual center distance to theoretical.

Parameters:
  • gear_a – First gear interface

  • gear_b – Second gear interface

  • actual_center_distance – Measured center-to-center distance in mm

  • tolerance – Allowable deviation from theoretical in mm

Returns:

CompatibilityResult with mesh status and any warnings

Example

>>> sun = GearMeshInterface(name="sun", part_name="SUN", module=0.75, teeth=18)
>>> planet = GearMeshInterface(name="planet", part_name="PLANET", module=0.75, teeth=36)
>>> result = check_gear_mesh_collision(sun, planet, actual_center_distance=20.25)
>>> if result.is_compatible:
...     print("Gears properly meshed")
yapcad.collision.gear_interface.create_planetary_gearbox_interfaces(gearbox_name: str, module: float, sun_teeth: int, planet_teeth: int, ring_teeth: int, face_width: float, num_planets: int = 3, pressure_angle: float = 20.0, mesh_plane_z: float = 0.0) List[GearMeshInterface][source]

Create interface volumes for a complete planetary gearbox.

This factory function creates GearMeshInterface objects for all gears in a planetary gearbox configuration: one sun, one ring, and multiple planet gears at the correct orbital positions.

Parameters:
  • gearbox_name – Base name for the gearbox (e.g., “AXIS1”)

  • module – Gear module in mm

  • sun_teeth – Number of teeth on sun gear

  • planet_teeth – Number of teeth on planet gears

  • ring_teeth – Number of teeth on ring gear (internal)

  • face_width – Gear face width in mm

  • num_planets – Number of planet gears (typically 3)

  • pressure_angle – Pressure angle in degrees (typically 20.0)

  • mesh_plane_z – Z coordinate of the gear mesh plane

Returns:

List of GearMeshInterface objects for all gears

Example

>>> interfaces = create_planetary_gearbox_interfaces(
...     gearbox_name="AXIS1",
...     module=0.75,
...     sun_teeth=18,
...     planet_teeth=36,
...     ring_teeth=90,
...     face_width=10.0,
...     num_planets=3
... )
>>> for iface in interfaces:
...     print(f"{iface.name}: {iface.teeth}T at {iface.center}")

Notes

  • Verifies planetary gear geometry: ring_teeth = sun_teeth + 2 * planet_teeth

  • Places planets at equal angular intervals

  • All gears centered at Z = mesh_plane_z

yapcad.collision.interface module

Base interface volume classes for controlled overlap regions.

This module defines the base InterfaceVolume class and InterfaceType enum for describing regions where parts are designed to overlap (gear teeth, threads, press fits, etc.).

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.interface.CompatibilityResult(is_compatible: bool, reason: str, warnings: List[str] = <factory>, overlap_volume: float = 0.0, required_clearance: float = 0.0)[source]

Bases: object

Result of checking interface compatibility.

When two interface volumes overlap, this result indicates whether they are designed to mate properly (compatible) or represent an unintended collision (incompatible).

is_compatible

True if interfaces can mate correctly

Type:

bool

reason

Explanation of compatibility status

Type:

str

warnings

Non-fatal compatibility concerns (e.g., fit tolerance warnings)

Type:

List[str]

overlap_volume

Estimated overlap volume in mm^3 (if compatible)

Type:

float

required_clearance

Minimum clearance needed for assembly in mm

Type:

float

Example

>>> result = sun_gear.check_compatibility(planet_gear)
>>> if result.is_compatible:
...     print(f"Gears can mesh: {result.reason}")
...     if result.warnings:
...         print(f"Warnings: {result.warnings}")
... else:
...     print(f"Incompatible: {result.reason}")
is_compatible: bool
overlap_volume: float = 0.0
reason: str
required_clearance: float = 0.0
warnings: List[str]
class yapcad.collision.interface.InterfaceType(*values)[source]

Bases: Enum

Types of interface volumes for controlled overlap.

Each type represents a specific mechanical interface where geometric overlap is expected and designed.

GEAR_MESH

Involute gear teeth meshing

THREAD

Screw thread engagement (bolt/nut, tap/screw)

PRESS_FIT

Interference fit (shaft/bore)

BEARING

Bearing race contact regions

SPLINE

Spline coupling interface

KEY

Key/keyway interface

SNAP_FIT

Snap-fit detent mechanism

CUSTOM

User-defined interface type

BEARING = 4
CUSTOM = 8
GEAR_MESH = 1
KEY = 6
PRESS_FIT = 3
SNAP_FIT = 7
SPLINE = 5
THREAD = 2
class yapcad.collision.interface.InterfaceVolume(name: str, part_name: str, interface_type: InterfaceType, center: Tuple[float, float, float]=(0.0, 0.0, 0.0), axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), description: str = '', metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: ABC

Base class for interface volumes that allow controlled overlap.

Interface volumes define regions where parts are DESIGNED to overlap, such as gear teeth meshing, threads engaging, or press fits. The collision detector uses these to distinguish expected overlap from actual collisions.

Subclasses must implement:
  • get_bounding_cylinder(): Return (radius, height) of enclosing cylinder

  • check_compatibility(): Check if another interface can mate properly

  • get_engagement_depth(): Return the engagement/overlap depth

name

Unique identifier for this interface

Type:

str

part_name

Name of the part this interface belongs to

Type:

str

interface_type

Type of interface (GEAR_MESH, THREAD, etc.)

Type:

yapcad.collision.interface.InterfaceType

center

Center point of interface volume as (x, y, z) tuple

Type:

Tuple[float, float, float]

axis

Primary axis direction as (x, y, z) tuple (rotation/engagement axis)

Type:

Tuple[float, float, float]

description

Human-readable description

Type:

str

metadata

Additional type-specific parameters

Type:

Dict[str, Any]

Example

>>> class MyInterface(InterfaceVolume):
...     def get_bounding_cylinder(self):
...         return (10.0, 5.0)  # radius=10mm, height=5mm
...     def check_compatibility(self, other):
...         if isinstance(other, MyInterface):
...             return CompatibilityResult(True, "Compatible")
...         return CompatibilityResult(False, "Type mismatch")
...     def get_engagement_depth(self):
...         return 5.0
axis: Tuple[float, float, float] = (0.0, 0.0, 1.0)
center: Tuple[float, float, float] = (0.0, 0.0, 0.0)
abstractmethod check_compatibility(other: InterfaceVolume) CompatibilityResult[source]

Check if this interface is compatible with another.

Two interfaces are compatible if they are designed to mate properly when overlapping (e.g., matching gear modules, matching thread pitches).

Parameters:

other – Another interface volume to check against

Returns:

CompatibilityResult indicating if interfaces can properly mate

description: str = ''
abstractmethod get_bounding_cylinder() Tuple[float, float][source]

Get cylindrical bounding volume for the interface.

Returns:

Tuple of (radius, height) in mm defining the minimum enclosing cylinder aligned with the interface axis.

abstractmethod get_engagement_depth() float[source]

Get the depth/length of interface engagement in mm.

For different interface types:
  • Gears: face width

  • Threads: engagement length

  • Press fits: engagement length

  • Bearings: bearing width

Returns:

Engagement depth in mm

interface_type: InterfaceType
metadata: Dict[str, Any]
name: str
overlaps_with(other: InterfaceVolume, tolerance: float = 0.1) bool[source]

Check if two interface volumes spatially overlap.

This is a geometric overlap check, not a compatibility check. Two interfaces can overlap geometrically but be incompatible (e.g., mismatched gear modules).

Parameters:
  • other – Another interface volume to check

  • tolerance – Distance tolerance for overlap detection in mm

Returns:

True if bounding volumes overlap within tolerance

part_name: str
to_dict() Dict[str, Any][source]

Convert to dictionary for serialization.

Returns:

Dictionary representation suitable for JSON export

transform_axis(transform_matrix: Any) Tuple[float, float, float][source]

Transform the interface axis by a 4x4 transformation matrix.

Parameters:

transform_matrix – 4x4 transformation matrix (numpy array or list)

Returns:

Transformed axis direction as (x, y, z) tuple (normalized)

transform_center(transform_matrix: Any) Tuple[float, float, float][source]

Transform the interface center by a 4x4 transformation matrix.

Parameters:

transform_matrix – 4x4 transformation matrix (numpy array or list)

Returns:

Transformed center point as (x, y, z) tuple

yapcad.collision.registry module

Interface registry for managing allowed overlap regions.

This module defines the InterfaceRegistry class for tracking and querying interface volumes across an assembly.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.registry.InterfaceRegistry[source]

Bases: object

Registry for managing interface volumes in an assembly.

The registry tracks all interface volumes and provides lookup methods for the collision detector to find compatible interfaces when overlap is detected between parts.

Key Features:
  • Register/unregister interface volumes by name

  • Query interfaces by part name or interface type

  • Check overlap compatibility between parts

  • Find all compatible interfaces for a given interface

Usage:
>>> registry = InterfaceRegistry()
>>>
>>> # Register gear interfaces
>>> registry.register(GearMeshInterface(
...     name="sun_teeth", part_name="SUN_GEAR",
...     module=0.75, teeth=18, pressure_angle=20.0, face_width=10.0
... ))
>>> registry.register(GearMeshInterface(
...     name="planet_teeth", part_name="PLANET_GEAR",
...     module=0.75, teeth=36, pressure_angle=20.0, face_width=10.0
... ))
>>>
>>> # During collision detection
>>> is_compatible, results = registry.check_overlap_compatibility(
...     "SUN_GEAR", "PLANET_GEAR"
... )
>>> if is_compatible:
...     print("Overlap is expected (gear mesh)")

Notes

  • Interface names must be unique within the registry

  • Parts can have multiple interfaces (e.g., gear teeth + bearing seat)

  • The registry does not store geometry, only interface definitions

check_overlap_compatibility(part_a: str, part_b: str, overlap_region: Tuple[Any, Any] | None = None) Tuple[bool, List[CompatibilityResult]][source]

Check if overlap between two parts is due to compatible interfaces.

This is the main entry point for the collision detector to check whether detected overlap should be allowed (expected interface) or flagged as a collision error.

Parameters:
  • part_a – First part name

  • part_b – Second part name

  • overlap_region – Optional (center, size) of overlap bounding box for spatial filtering (not yet implemented)

Returns:

  • bool: True if ALL overlapping interfaces are compatible

  • List[CompatibilityResult]: Results for each interface pair checked

Return type:

Tuple of

Example

>>> is_ok, results = registry.check_overlap_compatibility("SUN", "PLANET")
>>> if is_ok:
...     print("Expected gear mesh overlap")
... else:
...     for r in results:
...         if not r.is_compatible:
...             print(f"Incompatible: {r.reason}")
clear() None[source]

Remove all registered interfaces.

find_compatible_interfaces(interface: InterfaceVolume, candidates: List[InterfaceVolume] = None) List[Tuple[InterfaceVolume, CompatibilityResult]][source]

Find all interfaces compatible with the given interface.

Parameters:
  • interface – Interface to find matches for

  • candidates – List of candidates to check (default: all registered)

Returns:

List of (interface, compatibility_result) tuples for compatible interfaces

get(name: str) InterfaceVolume | None[source]

Get interface by name.

Parameters:

name – Interface name to look up

Returns:

InterfaceVolume if found, None otherwise

get_all_interfaces() List[InterfaceVolume][source]

Get all registered interfaces.

Returns:

List of all InterfaceVolume objects in the registry

get_all_parts() List[str][source]

Get names of all parts with registered interfaces.

Returns:

List of part names

get_compatible_interface_names(part_a: str, part_b: str) List[str][source]

Get names of compatible interfaces between two parts.

Convenience method to get just the interface names for parts that have compatible overlap.

Parameters:
  • part_a – First part name

  • part_b – Second part name

Returns:

List of compatible interface names (from both parts)

get_interfaces_by_type(interface_type: InterfaceType) List[InterfaceVolume][source]

Get all interfaces of a specific type.

Parameters:

interface_type – Type of interface to find

Returns:

List of matching InterfaceVolume objects (may be empty)

get_interfaces_for_part(part_name: str) List[InterfaceVolume][source]

Get all interfaces belonging to a part.

Parameters:

part_name – Name of the part

Returns:

List of InterfaceVolume objects for the part (may be empty)

has_interfaces(part_name: str) bool[source]

Check if a part has any registered interfaces.

Parameters:

part_name – Name of the part

Returns:

True if part has one or more registered interfaces

register(interface: InterfaceVolume) None[source]

Register an interface volume.

Parameters:

interface – InterfaceVolume to register

Raises:

ValueError – If interface with same name already registered

register_many(interfaces: List[InterfaceVolume]) None[source]

Register multiple interface volumes.

Parameters:

interfaces – List of InterfaceVolume objects to register

to_dict() Dict[str, Any][source]

Convert registry to dictionary for serialization.

Returns:

Dictionary with all interfaces serialized

unregister(name: str) InterfaceVolume | None[source]

Remove an interface from the registry.

Parameters:

name – Interface name to remove

Returns:

The removed interface, or None if not found

yapcad.collision.result module

Collision detection result data structures.

This module defines the CollisionResult dataclass and CollisionMethod enum that represent the output of collision detection operations.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.result.CollisionMethod(*values)[source]

Bases: Enum

Method used for collision detection.

BREP

Exact BREP boolean intersection (pythonocc)

MESH

Mesh-based sampling and containment (trimesh)

AABB

Axis-aligned bounding box check only

FCL

Flexible Collision Library (via trimesh)

UNKNOWN

Method not specified or hybrid approach

AABB = 3
BREP = 1
FCL = 4
MESH = 2
UNKNOWN = 5
class yapcad.collision.result.CollisionResult(part_a: str, part_b: str, collides: bool, method: CollisionMethod = CollisionMethod.UNKNOWN, intersection_volume: float | None = None, penetration_depth: float = 0.0, contact_points: Tuple[float, float, float]]=<factory>, compatible_interface: bool = False, interface_names: List[str] = <factory>, error_message: str = '', metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of collision detection between two parts.

This dataclass captures all information about a collision check between two assembly parts, including whether they collide, the detection method used, collision metrics, and interface compatibility status.

part_a

Name/identifier of the first part

Type:

str

part_b

Name/identifier of the second part

Type:

str

collides

True if the parts geometrically intersect

Type:

bool

method

Detection method used (BREP, MESH, AABB, FCL)

Type:

yapcad.collision.result.CollisionMethod

intersection_volume

Volume of intersection region in mm^3 (if computed)

Type:

float | None

penetration_depth

Maximum penetration depth in mm (if computed)

Type:

float

contact_points

List of contact/intersection points as (x, y, z) tuples

Type:

List[Tuple[float, float, float]]

compatible_interface

True if overlap is due to compatible interfaces (e.g., meshing gears with matching module/pressure angle)

Type:

bool

interface_names

Names of compatible interfaces that explain the overlap

Type:

List[str]

error_message

Error message if detection failed

Type:

str

metadata

Additional method-specific data

Type:

Dict[str, Any]

Example

>>> result = CollisionResult(
...     part_a="SUN_GEAR",
...     part_b="PLANET_GEAR_1",
...     collides=True,
...     method=CollisionMethod.BREP,
...     intersection_volume=15.3,
...     compatible_interface=True,
...     interface_names=["sun_teeth", "planet_1_teeth"]
... )
>>> if result.collides and not result.compatible_interface:
...     print(f"ERROR: Unintended collision between {result.part_a} and {result.part_b}")
... elif result.collides:
...     print(f"OK: Expected overlap (gear mesh) between {result.part_a} and {result.part_b}")

Notes

  • collides=True with compatible_interface=True indicates expected overlap (e.g., gear teeth meshing, threads engaging)

  • collides=True with compatible_interface=False indicates an actual collision that needs to be resolved

  • intersection_volume and penetration_depth may be None/0 if not computed by the detection method used

collides: bool
compatible_interface: bool = False
contact_points: List[Tuple[float, float, float]]
classmethod error(part_a: str, part_b: str, message: str) CollisionResult[source]

Factory for creating an error result.

Parameters:
  • part_a – First part name

  • part_b – Second part name

  • message – Error message describing what went wrong

Returns:

CollisionResult with error_message set

error_message: str = ''
classmethod from_dict(data: Dict[str, Any]) CollisionResult[source]

Create CollisionResult from dictionary.

Parameters:

data – Dictionary with collision result data

Returns:

CollisionResult instance

interface_names: List[str]
intersection_volume: float | None = None
property is_error: bool

True if collision represents an actual error (not expected interface).

property is_interface_overlap: bool

True if collision is due to compatible interface (expected overlap).

metadata: Dict[str, Any]
method: CollisionMethod = 5
classmethod no_collision(part_a: str, part_b: str, method: CollisionMethod = CollisionMethod.UNKNOWN) CollisionResult[source]

Factory for creating a no-collision result.

Parameters:
  • part_a – First part name

  • part_b – Second part name

  • method – Detection method used

Returns:

CollisionResult with collides=False

property pair_key: Tuple[str, str]

Canonical pair key (sorted alphabetically) for deduplication.

part_a: str
part_b: str
penetration_depth: float = 0.0
to_dict() Dict[str, Any][source]

Convert to dictionary for JSON serialization.

Returns:

Dictionary representation suitable for JSON export

yapcad.collision.thread_interface module

Thread interface for screw thread engagement overlap.

This module defines the ThreadInterface class for representing screw thread engagement regions where controlled overlap is expected.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.thread_interface.ThreadClass(*values)[source]

Bases: Enum

Thread fit classes per ISO 965-1.

Defines the tolerance grade for thread fits, affecting clearance/interference between mating threads.

ISO_6H_6g

Standard fit (normal clearance)

ISO_6G_6h

Close fit (reduced clearance)

ISO_5H_4h

Tight fit (precision applications)

ISO_5H_4h = '5H/4h'
ISO_6G_6h = '6G/6h'
ISO_6H_6g = '6H/6g'
class yapcad.collision.thread_interface.ThreadInterface(name: str, part_name: str, center: Tuple[float, float, float]=(0.0, 0.0, 0.0), axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), description: str = '', metadata: Dict[str, ~typing.Any]=<factory>, pitch: float = 0.5, major_diameter: float = 3.0, thread_type: ThreadType = ThreadType.ISO_METRIC, thread_class: ThreadClass = ThreadClass.ISO_6H_6g, engagement_length: float = 6.0, is_internal: bool = False)[source]

Bases: InterfaceVolume

Interface volume for screw thread engagement.

Defines the region where external threads (bolt/screw) engage with internal threads (tapped hole/nut). Compatible threads must have matching pitch and diameter.

pitch

Thread pitch in mm (distance between threads)

Type:

float

major_diameter

Major (nominal) diameter in mm

Type:

float

thread_type

Thread standard (ISO_METRIC, UNC, etc.)

Type:

yapcad.collision.thread_interface.ThreadType

thread_class

Fit class (tolerance grade)

Type:

yapcad.collision.thread_interface.ThreadClass

engagement_length

Length of thread engagement in mm

Type:

float

is_internal

True for tapped hole/nut, False for bolt/screw

Type:

bool

Derived Properties:

minor_diameter: Root diameter of external thread pitch_diameter: Diameter where thread flanks meet thread_depth: Radial depth of thread profile

Common ISO Metric Threads:

M2: pitch=0.4mm, major=2.0mm M2.5: pitch=0.45mm, major=2.5mm M3: pitch=0.5mm, major=3.0mm M4: pitch=0.7mm, major=4.0mm M5: pitch=0.8mm, major=5.0mm M6: pitch=1.0mm, major=6.0mm

Example

>>> bolt = ThreadInterface.from_metric_size("M3", is_internal=False)
>>> hole = ThreadInterface.from_metric_size("M3", is_internal=True)
>>> result = bolt.check_compatibility(hole)
>>> print(result)  # COMPATIBLE: Threads compatible: M3.0x0.5
check_compatibility(other: InterfaceVolume) CompatibilityResult[source]

Check if this thread can engage with another interface.

Compatible conditions:
  1. Other must be a ThreadInterface

  2. One must be internal, one external

  3. Major diameters must match (within 0.1% tolerance)

  4. Pitches must match (within 0.1% tolerance)

  5. Thread types should be compatible

Parameters:

other – Another interface volume to check against

Returns:

CompatibilityResult indicating if threads can properly engage

engagement_length: float = 6.0
classmethod from_metric_size(size: str, engagement_length: float = None, is_internal: bool = False, name: str = None, part_name: str = '', **kwargs) ThreadInterface[source]

Create ThreadInterface from metric size string.

Parameters:
  • size – Metric size string (e.g., “M3”, “M2.5”, “M4x0.5”)

  • engagement_length – Thread engagement in mm (default: 1.5 * diameter)

  • is_internal – True for tapped hole

  • name – Interface name (default: “thread_{size}”)

  • part_name – Name of the part this interface belongs to

  • **kwargs – Additional arguments passed to constructor

Returns:

ThreadInterface configured for the specified size

Raises:

ValueError – If size string cannot be parsed

Example

>>> bolt = ThreadInterface.from_metric_size("M3", is_internal=False)
>>> fine_bolt = ThreadInterface.from_metric_size("M4x0.5")
get_bounding_cylinder() Tuple[float, float][source]

Get cylindrical bounding volume (radius, height).

Returns:

Tuple of (radius, height) where radius is major_diameter/2 and height is engagement_length.

get_engagement_depth() float[source]

Engagement length is the depth for threads.

Returns:

Thread engagement length in mm

interface_type: InterfaceType = 2
is_internal: bool = False
major_diameter: float = 3.0
property minor_diameter: float

Minor diameter (root of external thread).

Approximation for ISO metric: D_minor = D_major - 1.0825 * pitch

pitch: float = 0.5
property pitch_diameter: float

Pitch diameter (where thread flanks meet).

Approximation for ISO metric: D_pitch = D_major - 0.6495 * pitch

thread_class: ThreadClass = '6H/6g'
property thread_depth: float

Thread depth (radial distance from major to minor diameter).

thread_type: ThreadType = 'ISO'
to_dict() Dict[str, Any][source]

Convert to dictionary for serialization.

Returns:

Dictionary with all thread parameters

class yapcad.collision.thread_interface.ThreadType(*values)[source]

Bases: Enum

Standard thread types.

ISO_METRIC

ISO metric thread (M2, M3, M4, etc.)

ISO_METRIC_FINE

ISO metric fine pitch thread

UNC

Unified National Coarse (imperial)

UNF

Unified National Fine (imperial)

ACME

Trapezoidal/ACME thread for power transmission

CUSTOM

User-defined thread profile

ACME = 'ACME'
CUSTOM = 'CUSTOM'
ISO_METRIC = 'ISO'
ISO_METRIC_FINE = 'ISO_F'
UNC = 'UNC'
UNF = 'UNF'
yapcad.collision.thread_interface.create_bolt_pattern_interfaces(pattern_name: str, bolt_part_name: str, hole_part_name: str, thread_size: str, positions: List[Tuple[float, float, float]], engagement_length: float = None) List[ThreadInterface][source]

Create interface volumes for a bolt pattern.

Creates matching pairs of external (bolt) and internal (hole) thread interfaces at specified positions.

Parameters:
  • pattern_name – Base name for the bolt pattern

  • bolt_part_name – Part containing the bolts

  • hole_part_name – Part containing the tapped holes

  • thread_size – Thread size (e.g., “M3”, “M2.5”)

  • positions – List of (x, y, z) positions for each bolt

  • engagement_length – Thread engagement in mm (default: from size)

Returns:

List of ThreadInterface objects (bolts and holes)

Example

>>> positions = [(10, 0, 0), (-10, 0, 0), (0, 10, 0), (0, -10, 0)]
>>> interfaces = create_bolt_pattern_interfaces(
...     "mounting_bolts",
...     "BRACKET", "BASE_PLATE",
...     "M3", positions
... )

Module contents

Collision detection system for yapCAD assemblies.

This package provides collision detection capabilities for validating assembly configurations. It supports multiple detection methods with automatic fallback:

  1. BREP-based detection (requires pythonocc-core): Uses exact boolean intersection via BRepAlgoAPI_Common for precise collision volumes. This is the most accurate method.

  2. Mesh-based detection (requires trimesh): Uses point sampling and containment checks for collision detection. Works with STL/mesh geometry when BREP is unavailable.

  3. AABB-based detection: Fast bounding box checks for quick rejection of non-colliding pairs. Always available as a first-pass filter.

Key Features:
  • Pluggable geometry source via GeometryProvider protocol

  • Interface volume system for allowed overlaps (gear mesh, threads, etc.)

  • Detailed collision results with volumes, penetration depths, contact points

  • Integration with assembly constraint system

Design Principles:
  • No hardcoded part-to-file mappings (use GeometryProvider)

  • No project-specific exclusion lists (use InterfaceRegistry)

  • Works with any assembly via pluggable interfaces

  • Graceful degradation when optional dependencies unavailable

Quick Start:
>>> from yapcad.collision import (
...     CollisionDetector,
...     CollisionResult,
...     InterfaceRegistry,
...     GearMeshInterface,
... )
>>>
>>> # Create detector with geometry provider
>>> class MyGeometryProvider:
...     def get_geometry(self, part_name: str) -> Optional[Any]:
...         # Return STEP/STL path or mesh object
...         return f"parts/{part_name}.step"
...
>>> detector = CollisionDetector(MyGeometryProvider())
>>>
>>> # Register allowed overlaps for gear meshing
>>> registry = InterfaceRegistry()
>>> registry.register(GearMeshInterface(
...     name="sun_teeth", part_name="SUN_GEAR",
...     module=0.75, teeth=18, pressure_angle=20.0, face_width=10.0
... ))
>>> detector.set_interface_registry(registry)
>>>
>>> # Check assembly for collisions
>>> world_transforms = {"SUN_GEAR": tf1, "PLANET_GEAR": tf2, ...}
>>> results = detector.check_assembly(world_transforms)
>>> for result in results:
...     if result.collides and not result.compatible_interface:
...         print(f"COLLISION: {result.part_a} <-> {result.part_b}")
Available Classes:
Detection:

CollisionDetector - Main collision detection class CollisionResult - Detailed collision result data GeometryProvider - Protocol for geometry source

Interface Volumes (allowed overlaps):

InterfaceVolume - Base class for interface volumes InterfaceType - Enum of interface types GearMeshInterface - Gear teeth meshing interface ThreadInterface - Screw thread engagement interface InterfaceRegistry - Registry for managing interfaces

See also

  • yapcad.assembly: Constraint-based assembly system

  • yapcad.octtree: Spatial indexing for acceleration

Collision detection system contributed by Jeremy Mika.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.collision.CollisionDetector(geometry_provider: GeometryProvider, interface_registry: InterfaceRegistry | None = None, verbose: bool = False, min_collision_volume: float = 0.1, mesh_sample_points: int = 1500, penetration_threshold: float = 0.1)[source]

Bases: object

Main collision detection class with multiple detection methods.

CollisionDetector provides collision detection between assembly parts using the best available method:

  1. BREP detection (if pythonocc available): Uses boolean intersection for exact collision volume

  2. Mesh detection (if trimesh available): Uses point sampling and containment checks

  3. AABB detection (always available): Fast bounding box checks (used as pre-filter)

The detector integrates with InterfaceRegistry to distinguish expected overlaps (gear mesh, threads) from actual collisions.

geometry_provider

Provider for part geometry

interface_registry

Registry of allowed overlap interfaces

verbose

Enable detailed logging

min_collision_volume

Minimum volume to report (mm^3)

mesh_sample_points

Number of sample points for mesh detection

penetration_threshold

Minimum depth to count as collision (mm)

Example

>>> provider = MyGeometryProvider()
>>> detector = CollisionDetector(provider)
>>>
>>> # Optional: register allowed overlaps
>>> registry = InterfaceRegistry()
>>> registry.register(GearMeshInterface(...))
>>> detector.set_interface_registry(registry)
>>>
>>> # Check single pair
>>> result = detector.check_collision(
...     "PART_A", transform_a,
...     "PART_B", transform_b
... )
>>>
>>> # Check full assembly
>>> transforms = {"PART_A": tf_a, "PART_B": tf_b, "PART_C": tf_c}
>>> results = detector.check_assembly(transforms)
>>> collisions = [r for r in results if r.is_error]
check_assembly(world_transforms: Dict[str, Any], skip_pairs: List[Tuple[str, str]] | None = None) List[CollisionResult][source]

Check all pairs in an assembly for collisions.

Parameters:
  • world_transforms – Dictionary mapping part names to 4x4 transforms

  • skip_pairs – Optional list of (part_a, part_b) pairs to skip

Returns:

List of CollisionResult for all pairs checked

check_collision(part_a: str, transform_a: Any, part_b: str, transform_b: Any, method: CollisionMethod | None = None) CollisionResult[source]

Check for collision between two parts.

Parameters:
  • part_a – Name of first part

  • transform_a – 4x4 transformation matrix for part A

  • part_b – Name of second part

  • transform_b – 4x4 transformation matrix for part B

  • method – Specific detection method (default: best available)

Returns:

CollisionResult with collision details

clear_cache() None[source]

Clear the geometry cache.

get_collision_summary(results: List[CollisionResult]) Dict[str, Any][source]

Generate summary statistics from collision results.

Parameters:

results – List of CollisionResult from check_assembly

Returns:

Dictionary with summary statistics

property preferred_method: CollisionMethod

Get the preferred detection method based on available libraries.

set_interface_registry(registry: InterfaceRegistry) None[source]

Set the interface registry for allowed overlaps.

Parameters:

registry – InterfaceRegistry with interface definitions

class yapcad.collision.CollisionMethod(*values)[source]

Bases: Enum

Method used for collision detection.

BREP

Exact BREP boolean intersection (pythonocc)

MESH

Mesh-based sampling and containment (trimesh)

AABB

Axis-aligned bounding box check only

FCL

Flexible Collision Library (via trimesh)

UNKNOWN

Method not specified or hybrid approach

AABB = 3
BREP = 1
FCL = 4
MESH = 2
UNKNOWN = 5
class yapcad.collision.CollisionResult(part_a: str, part_b: str, collides: bool, method: CollisionMethod = CollisionMethod.UNKNOWN, intersection_volume: float | None = None, penetration_depth: float = 0.0, contact_points: Tuple[float, float, float]]=<factory>, compatible_interface: bool = False, interface_names: List[str] = <factory>, error_message: str = '', metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of collision detection between two parts.

This dataclass captures all information about a collision check between two assembly parts, including whether they collide, the detection method used, collision metrics, and interface compatibility status.

part_a

Name/identifier of the first part

Type:

str

part_b

Name/identifier of the second part

Type:

str

collides

True if the parts geometrically intersect

Type:

bool

method

Detection method used (BREP, MESH, AABB, FCL)

Type:

yapcad.collision.result.CollisionMethod

intersection_volume

Volume of intersection region in mm^3 (if computed)

Type:

float | None

penetration_depth

Maximum penetration depth in mm (if computed)

Type:

float

contact_points

List of contact/intersection points as (x, y, z) tuples

Type:

List[Tuple[float, float, float]]

compatible_interface

True if overlap is due to compatible interfaces (e.g., meshing gears with matching module/pressure angle)

Type:

bool

interface_names

Names of compatible interfaces that explain the overlap

Type:

List[str]

error_message

Error message if detection failed

Type:

str

metadata

Additional method-specific data

Type:

Dict[str, Any]

Example

>>> result = CollisionResult(
...     part_a="SUN_GEAR",
...     part_b="PLANET_GEAR_1",
...     collides=True,
...     method=CollisionMethod.BREP,
...     intersection_volume=15.3,
...     compatible_interface=True,
...     interface_names=["sun_teeth", "planet_1_teeth"]
... )
>>> if result.collides and not result.compatible_interface:
...     print(f"ERROR: Unintended collision between {result.part_a} and {result.part_b}")
... elif result.collides:
...     print(f"OK: Expected overlap (gear mesh) between {result.part_a} and {result.part_b}")

Notes

  • collides=True with compatible_interface=True indicates expected overlap (e.g., gear teeth meshing, threads engaging)

  • collides=True with compatible_interface=False indicates an actual collision that needs to be resolved

  • intersection_volume and penetration_depth may be None/0 if not computed by the detection method used

collides: bool
compatible_interface: bool = False
contact_points: List[Tuple[float, float, float]]
classmethod error(part_a: str, part_b: str, message: str) CollisionResult[source]

Factory for creating an error result.

Parameters:
  • part_a – First part name

  • part_b – Second part name

  • message – Error message describing what went wrong

Returns:

CollisionResult with error_message set

error_message: str = ''
classmethod from_dict(data: Dict[str, Any]) CollisionResult[source]

Create CollisionResult from dictionary.

Parameters:

data – Dictionary with collision result data

Returns:

CollisionResult instance

interface_names: List[str]
intersection_volume: float | None = None
property is_error: bool

True if collision represents an actual error (not expected interface).

property is_interface_overlap: bool

True if collision is due to compatible interface (expected overlap).

metadata: Dict[str, Any]
method: CollisionMethod = 5
classmethod no_collision(part_a: str, part_b: str, method: CollisionMethod = CollisionMethod.UNKNOWN) CollisionResult[source]

Factory for creating a no-collision result.

Parameters:
  • part_a – First part name

  • part_b – Second part name

  • method – Detection method used

Returns:

CollisionResult with collides=False

property pair_key: Tuple[str, str]

Canonical pair key (sorted alphabetically) for deduplication.

part_a: str
part_b: str
penetration_depth: float = 0.0
to_dict() Dict[str, Any][source]

Convert to dictionary for JSON serialization.

Returns:

Dictionary representation suitable for JSON export

class yapcad.collision.CompatibilityResult(is_compatible: bool, reason: str, warnings: List[str] = <factory>, overlap_volume: float = 0.0, required_clearance: float = 0.0)[source]

Bases: object

Result of checking interface compatibility.

When two interface volumes overlap, this result indicates whether they are designed to mate properly (compatible) or represent an unintended collision (incompatible).

is_compatible

True if interfaces can mate correctly

Type:

bool

reason

Explanation of compatibility status

Type:

str

warnings

Non-fatal compatibility concerns (e.g., fit tolerance warnings)

Type:

List[str]

overlap_volume

Estimated overlap volume in mm^3 (if compatible)

Type:

float

required_clearance

Minimum clearance needed for assembly in mm

Type:

float

Example

>>> result = sun_gear.check_compatibility(planet_gear)
>>> if result.is_compatible:
...     print(f"Gears can mesh: {result.reason}")
...     if result.warnings:
...         print(f"Warnings: {result.warnings}")
... else:
...     print(f"Incompatible: {result.reason}")
is_compatible: bool
overlap_volume: float = 0.0
reason: str
required_clearance: float = 0.0
warnings: List[str]
class yapcad.collision.GearMeshInterface(name: str, part_name: str, center: Tuple[float, float, float]=(0.0, 0.0, 0.0), axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), description: str = '', metadata: Dict[str, ~typing.Any]=<factory>, module: float = 1.0, teeth: int = 20, pressure_angle: float = 20.0, face_width: float = 10.0, helix_angle: float = 0.0, backlash: float = 0.0)[source]

Bases: InterfaceVolume

Interface volume for involute gear teeth meshing.

Defines the region where gear teeth overlap during meshing. Two gears are compatible if they have the same module and pressure angle (standard involute tooth profile).

module

Gear module in mm (pitch_diameter / teeth)

Type:

float

teeth

Number of teeth on the gear

Type:

int

pressure_angle

Pressure angle in degrees (typically 20.0)

Type:

float

face_width

Width of gear face in mm (axial engagement length)

Type:

float

helix_angle

Helix angle in degrees for helical gears (0 for spur)

Type:

float

backlash

Design backlash in mm (0.0 for theoretical)

Type:

float

Derived Properties:

pitch_diameter: module * teeth addendum: module (tooth height above pitch circle) dedendum: 1.25 * module (tooth depth below pitch circle) outside_diameter: pitch_diameter + 2 * addendum root_diameter: pitch_diameter - 2 * dedendum base_diameter: pitch_diameter * cos(pressure_angle)

Example

>>> sun = GearMeshInterface(
...     name="sun_teeth", part_name="SUN_GEAR",
...     module=0.75, teeth=18, pressure_angle=20.0, face_width=10.0
... )
>>> planet = GearMeshInterface(
...     name="planet_teeth", part_name="PLANET_GEAR",
...     module=0.75, teeth=36, pressure_angle=20.0, face_width=10.0
... )
>>> result = sun.check_compatibility(planet)
>>> print(result)  # COMPATIBLE: Gears compatible: m=0.75mm, PA=20.0deg
>>> print(f"Center distance: {sun.get_center_distance(planet):.2f}mm")

Notes

  • Module must match exactly between meshing gears (within 0.1% tolerance)

  • Pressure angle must match (within 0.5 degree tolerance)

  • For helical gears, helix angles must be opposite (LH + RH = 0)

  • Face widths must have sufficient overlap for load transfer

property addendum: float

Addendum (tooth height above pitch circle) = module.

backlash: float = 0.0
property base_diameter: float

Base diameter for involute profile.

check_compatibility(other: InterfaceVolume) CompatibilityResult[source]

Check if this gear can mesh with another interface.

Compatible conditions:
  1. Other must be a GearMeshInterface

  2. Modules must match (within 0.1% tolerance)

  3. Pressure angles must match (within 0.5 degree tolerance)

  4. Helix angles must be compatible (opposite for external mesh)

  5. Face widths must have sufficient overlap

Parameters:

other – Another interface volume to check against

Returns:

CompatibilityResult indicating if gears can properly mesh

property dedendum: float

Dedendum (tooth depth below pitch circle) = 1.25 * module.

face_width: float = 10.0
get_bounding_cylinder() Tuple[float, float][source]

Get cylindrical bounding volume (radius, height).

Returns:

Tuple of (radius, height) where radius extends to outside diameter and height is the face width.

get_center_distance(other: GearMeshInterface) float[source]

Calculate theoretical center distance for meshing with another gear.

Parameters:

other – Another gear to mesh with

Returns:

Center-to-center distance for proper meshing in mm

Raises:

ValueError – If modules don’t match

get_engagement_depth() float[source]

Face width is the engagement depth for gears.

Returns:

Face width in mm

get_expected_overlap_volume(other: GearMeshInterface) float[source]

Calculate expected overlap volume when meshing with another gear.

This provides an estimate of how much volume the gear teeth should overlap when properly meshed, useful for validating collision detection results.

Parameters:

other – Another gear to mesh with

Returns:

Estimated overlap volume in mm^3

get_mesh_overlap_depth() float[source]

Calculate the theoretical tooth mesh overlap depth.

This is the radial depth where teeth from two meshing gears actually overlap, used for collision solver exceptions.

Returns:

Depth of tooth engagement zone in mm (approximately 2 * module)

helix_angle: float = 0.0
interface_type: InterfaceType = 1
module: float = 1.0
property outside_diameter: float

Outside diameter (tip of teeth).

property pitch_diameter: float

Pitch diameter = module * teeth.

pressure_angle: float = 20.0
property root_diameter: float

Root diameter (bottom of teeth).

teeth: int = 20
to_dict() Dict[str, Any][source]

Convert to dictionary for serialization.

Returns:

Dictionary with all gear parameters

class yapcad.collision.GeometryProvider(*args, **kwargs)[source]

Bases: Protocol

Protocol for providing geometry data for collision detection.

Implementations must provide a way to retrieve geometry for parts by name. The geometry can be returned as:

  • Path to STEP file (preferred for BREP detection)

  • Path to STL file (for mesh detection)

  • trimesh.Trimesh object (pre-loaded mesh)

  • TopoDS_Shape object (pre-loaded BREP)

Example

>>> class FileBasedProvider:
...     def __init__(self, base_dir: Path):
...         self.base_dir = base_dir
...
...     def get_geometry(self, part_name: str) -> Optional[str]:
...         step_path = self.base_dir / f"{part_name}.step"
...         if step_path.exists():
...             return str(step_path)
...         stl_path = self.base_dir / f"{part_name}.stl"
...         if stl_path.exists():
...             return str(stl_path)
...         return None
get_geometry(part_name: str) Any | None[source]

Get geometry for a part by name.

Parameters:

part_name – Name/identifier of the part

Returns:

Path string, mesh object, or BREP shape for the part, or None if geometry not available

class yapcad.collision.InterfaceRegistry[source]

Bases: object

Registry for managing interface volumes in an assembly.

The registry tracks all interface volumes and provides lookup methods for the collision detector to find compatible interfaces when overlap is detected between parts.

Key Features:
  • Register/unregister interface volumes by name

  • Query interfaces by part name or interface type

  • Check overlap compatibility between parts

  • Find all compatible interfaces for a given interface

Usage:
>>> registry = InterfaceRegistry()
>>>
>>> # Register gear interfaces
>>> registry.register(GearMeshInterface(
...     name="sun_teeth", part_name="SUN_GEAR",
...     module=0.75, teeth=18, pressure_angle=20.0, face_width=10.0
... ))
>>> registry.register(GearMeshInterface(
...     name="planet_teeth", part_name="PLANET_GEAR",
...     module=0.75, teeth=36, pressure_angle=20.0, face_width=10.0
... ))
>>>
>>> # During collision detection
>>> is_compatible, results = registry.check_overlap_compatibility(
...     "SUN_GEAR", "PLANET_GEAR"
... )
>>> if is_compatible:
...     print("Overlap is expected (gear mesh)")

Notes

  • Interface names must be unique within the registry

  • Parts can have multiple interfaces (e.g., gear teeth + bearing seat)

  • The registry does not store geometry, only interface definitions

check_overlap_compatibility(part_a: str, part_b: str, overlap_region: Tuple[Any, Any] | None = None) Tuple[bool, List[CompatibilityResult]][source]

Check if overlap between two parts is due to compatible interfaces.

This is the main entry point for the collision detector to check whether detected overlap should be allowed (expected interface) or flagged as a collision error.

Parameters:
  • part_a – First part name

  • part_b – Second part name

  • overlap_region – Optional (center, size) of overlap bounding box for spatial filtering (not yet implemented)

Returns:

  • bool: True if ALL overlapping interfaces are compatible

  • List[CompatibilityResult]: Results for each interface pair checked

Return type:

Tuple of

Example

>>> is_ok, results = registry.check_overlap_compatibility("SUN", "PLANET")
>>> if is_ok:
...     print("Expected gear mesh overlap")
... else:
...     for r in results:
...         if not r.is_compatible:
...             print(f"Incompatible: {r.reason}")
clear() None[source]

Remove all registered interfaces.

find_compatible_interfaces(interface: InterfaceVolume, candidates: List[InterfaceVolume] = None) List[Tuple[InterfaceVolume, CompatibilityResult]][source]

Find all interfaces compatible with the given interface.

Parameters:
  • interface – Interface to find matches for

  • candidates – List of candidates to check (default: all registered)

Returns:

List of (interface, compatibility_result) tuples for compatible interfaces

get(name: str) InterfaceVolume | None[source]

Get interface by name.

Parameters:

name – Interface name to look up

Returns:

InterfaceVolume if found, None otherwise

get_all_interfaces() List[InterfaceVolume][source]

Get all registered interfaces.

Returns:

List of all InterfaceVolume objects in the registry

get_all_parts() List[str][source]

Get names of all parts with registered interfaces.

Returns:

List of part names

get_compatible_interface_names(part_a: str, part_b: str) List[str][source]

Get names of compatible interfaces between two parts.

Convenience method to get just the interface names for parts that have compatible overlap.

Parameters:
  • part_a – First part name

  • part_b – Second part name

Returns:

List of compatible interface names (from both parts)

get_interfaces_by_type(interface_type: InterfaceType) List[InterfaceVolume][source]

Get all interfaces of a specific type.

Parameters:

interface_type – Type of interface to find

Returns:

List of matching InterfaceVolume objects (may be empty)

get_interfaces_for_part(part_name: str) List[InterfaceVolume][source]

Get all interfaces belonging to a part.

Parameters:

part_name – Name of the part

Returns:

List of InterfaceVolume objects for the part (may be empty)

has_interfaces(part_name: str) bool[source]

Check if a part has any registered interfaces.

Parameters:

part_name – Name of the part

Returns:

True if part has one or more registered interfaces

register(interface: InterfaceVolume) None[source]

Register an interface volume.

Parameters:

interface – InterfaceVolume to register

Raises:

ValueError – If interface with same name already registered

register_many(interfaces: List[InterfaceVolume]) None[source]

Register multiple interface volumes.

Parameters:

interfaces – List of InterfaceVolume objects to register

to_dict() Dict[str, Any][source]

Convert registry to dictionary for serialization.

Returns:

Dictionary with all interfaces serialized

unregister(name: str) InterfaceVolume | None[source]

Remove an interface from the registry.

Parameters:

name – Interface name to remove

Returns:

The removed interface, or None if not found

class yapcad.collision.InterfaceType(*values)[source]

Bases: Enum

Types of interface volumes for controlled overlap.

Each type represents a specific mechanical interface where geometric overlap is expected and designed.

GEAR_MESH

Involute gear teeth meshing

THREAD

Screw thread engagement (bolt/nut, tap/screw)

PRESS_FIT

Interference fit (shaft/bore)

BEARING

Bearing race contact regions

SPLINE

Spline coupling interface

KEY

Key/keyway interface

SNAP_FIT

Snap-fit detent mechanism

CUSTOM

User-defined interface type

BEARING = 4
CUSTOM = 8
GEAR_MESH = 1
KEY = 6
PRESS_FIT = 3
SNAP_FIT = 7
SPLINE = 5
THREAD = 2
class yapcad.collision.InterfaceVolume(name: str, part_name: str, interface_type: InterfaceType, center: Tuple[float, float, float]=(0.0, 0.0, 0.0), axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), description: str = '', metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: ABC

Base class for interface volumes that allow controlled overlap.

Interface volumes define regions where parts are DESIGNED to overlap, such as gear teeth meshing, threads engaging, or press fits. The collision detector uses these to distinguish expected overlap from actual collisions.

Subclasses must implement:
  • get_bounding_cylinder(): Return (radius, height) of enclosing cylinder

  • check_compatibility(): Check if another interface can mate properly

  • get_engagement_depth(): Return the engagement/overlap depth

name

Unique identifier for this interface

Type:

str

part_name

Name of the part this interface belongs to

Type:

str

interface_type

Type of interface (GEAR_MESH, THREAD, etc.)

Type:

yapcad.collision.interface.InterfaceType

center

Center point of interface volume as (x, y, z) tuple

Type:

Tuple[float, float, float]

axis

Primary axis direction as (x, y, z) tuple (rotation/engagement axis)

Type:

Tuple[float, float, float]

description

Human-readable description

Type:

str

metadata

Additional type-specific parameters

Type:

Dict[str, Any]

Example

>>> class MyInterface(InterfaceVolume):
...     def get_bounding_cylinder(self):
...         return (10.0, 5.0)  # radius=10mm, height=5mm
...     def check_compatibility(self, other):
...         if isinstance(other, MyInterface):
...             return CompatibilityResult(True, "Compatible")
...         return CompatibilityResult(False, "Type mismatch")
...     def get_engagement_depth(self):
...         return 5.0
axis: Tuple[float, float, float] = (0.0, 0.0, 1.0)
center: Tuple[float, float, float] = (0.0, 0.0, 0.0)
abstractmethod check_compatibility(other: InterfaceVolume) CompatibilityResult[source]

Check if this interface is compatible with another.

Two interfaces are compatible if they are designed to mate properly when overlapping (e.g., matching gear modules, matching thread pitches).

Parameters:

other – Another interface volume to check against

Returns:

CompatibilityResult indicating if interfaces can properly mate

description: str = ''
abstractmethod get_bounding_cylinder() Tuple[float, float][source]

Get cylindrical bounding volume for the interface.

Returns:

Tuple of (radius, height) in mm defining the minimum enclosing cylinder aligned with the interface axis.

abstractmethod get_engagement_depth() float[source]

Get the depth/length of interface engagement in mm.

For different interface types:
  • Gears: face width

  • Threads: engagement length

  • Press fits: engagement length

  • Bearings: bearing width

Returns:

Engagement depth in mm

interface_type: InterfaceType
metadata: Dict[str, Any]
name: str
overlaps_with(other: InterfaceVolume, tolerance: float = 0.1) bool[source]

Check if two interface volumes spatially overlap.

This is a geometric overlap check, not a compatibility check. Two interfaces can overlap geometrically but be incompatible (e.g., mismatched gear modules).

Parameters:
  • other – Another interface volume to check

  • tolerance – Distance tolerance for overlap detection in mm

Returns:

True if bounding volumes overlap within tolerance

part_name: str
to_dict() Dict[str, Any][source]

Convert to dictionary for serialization.

Returns:

Dictionary representation suitable for JSON export

transform_axis(transform_matrix: Any) Tuple[float, float, float][source]

Transform the interface axis by a 4x4 transformation matrix.

Parameters:

transform_matrix – 4x4 transformation matrix (numpy array or list)

Returns:

Transformed axis direction as (x, y, z) tuple (normalized)

transform_center(transform_matrix: Any) Tuple[float, float, float][source]

Transform the interface center by a 4x4 transformation matrix.

Parameters:

transform_matrix – 4x4 transformation matrix (numpy array or list)

Returns:

Transformed center point as (x, y, z) tuple

class yapcad.collision.ThreadClass(*values)[source]

Bases: Enum

Thread fit classes per ISO 965-1.

Defines the tolerance grade for thread fits, affecting clearance/interference between mating threads.

ISO_6H_6g

Standard fit (normal clearance)

ISO_6G_6h

Close fit (reduced clearance)

ISO_5H_4h

Tight fit (precision applications)

ISO_5H_4h = '5H/4h'
ISO_6G_6h = '6G/6h'
ISO_6H_6g = '6H/6g'
class yapcad.collision.ThreadInterface(name: str, part_name: str, center: Tuple[float, float, float]=(0.0, 0.0, 0.0), axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), description: str = '', metadata: Dict[str, ~typing.Any]=<factory>, pitch: float = 0.5, major_diameter: float = 3.0, thread_type: ThreadType = ThreadType.ISO_METRIC, thread_class: ThreadClass = ThreadClass.ISO_6H_6g, engagement_length: float = 6.0, is_internal: bool = False)[source]

Bases: InterfaceVolume

Interface volume for screw thread engagement.

Defines the region where external threads (bolt/screw) engage with internal threads (tapped hole/nut). Compatible threads must have matching pitch and diameter.

pitch

Thread pitch in mm (distance between threads)

Type:

float

major_diameter

Major (nominal) diameter in mm

Type:

float

thread_type

Thread standard (ISO_METRIC, UNC, etc.)

Type:

yapcad.collision.thread_interface.ThreadType

thread_class

Fit class (tolerance grade)

Type:

yapcad.collision.thread_interface.ThreadClass

engagement_length

Length of thread engagement in mm

Type:

float

is_internal

True for tapped hole/nut, False for bolt/screw

Type:

bool

Derived Properties:

minor_diameter: Root diameter of external thread pitch_diameter: Diameter where thread flanks meet thread_depth: Radial depth of thread profile

Common ISO Metric Threads:

M2: pitch=0.4mm, major=2.0mm M2.5: pitch=0.45mm, major=2.5mm M3: pitch=0.5mm, major=3.0mm M4: pitch=0.7mm, major=4.0mm M5: pitch=0.8mm, major=5.0mm M6: pitch=1.0mm, major=6.0mm

Example

>>> bolt = ThreadInterface.from_metric_size("M3", is_internal=False)
>>> hole = ThreadInterface.from_metric_size("M3", is_internal=True)
>>> result = bolt.check_compatibility(hole)
>>> print(result)  # COMPATIBLE: Threads compatible: M3.0x0.5
check_compatibility(other: InterfaceVolume) CompatibilityResult[source]

Check if this thread can engage with another interface.

Compatible conditions:
  1. Other must be a ThreadInterface

  2. One must be internal, one external

  3. Major diameters must match (within 0.1% tolerance)

  4. Pitches must match (within 0.1% tolerance)

  5. Thread types should be compatible

Parameters:

other – Another interface volume to check against

Returns:

CompatibilityResult indicating if threads can properly engage

engagement_length: float = 6.0
classmethod from_metric_size(size: str, engagement_length: float = None, is_internal: bool = False, name: str = None, part_name: str = '', **kwargs) ThreadInterface[source]

Create ThreadInterface from metric size string.

Parameters:
  • size – Metric size string (e.g., “M3”, “M2.5”, “M4x0.5”)

  • engagement_length – Thread engagement in mm (default: 1.5 * diameter)

  • is_internal – True for tapped hole

  • name – Interface name (default: “thread_{size}”)

  • part_name – Name of the part this interface belongs to

  • **kwargs – Additional arguments passed to constructor

Returns:

ThreadInterface configured for the specified size

Raises:

ValueError – If size string cannot be parsed

Example

>>> bolt = ThreadInterface.from_metric_size("M3", is_internal=False)
>>> fine_bolt = ThreadInterface.from_metric_size("M4x0.5")
get_bounding_cylinder() Tuple[float, float][source]

Get cylindrical bounding volume (radius, height).

Returns:

Tuple of (radius, height) where radius is major_diameter/2 and height is engagement_length.

get_engagement_depth() float[source]

Engagement length is the depth for threads.

Returns:

Thread engagement length in mm

interface_type: InterfaceType = 2
is_internal: bool = False
major_diameter: float = 3.0
property minor_diameter: float

Minor diameter (root of external thread).

Approximation for ISO metric: D_minor = D_major - 1.0825 * pitch

pitch: float = 0.5
property pitch_diameter: float

Pitch diameter (where thread flanks meet).

Approximation for ISO metric: D_pitch = D_major - 0.6495 * pitch

thread_class: ThreadClass = '6H/6g'
property thread_depth: float

Thread depth (radial distance from major to minor diameter).

thread_type: ThreadType = 'ISO'
to_dict() Dict[str, Any][source]

Convert to dictionary for serialization.

Returns:

Dictionary with all thread parameters

class yapcad.collision.ThreadType(*values)[source]

Bases: Enum

Standard thread types.

ISO_METRIC

ISO metric thread (M2, M3, M4, etc.)

ISO_METRIC_FINE

ISO metric fine pitch thread

UNC

Unified National Coarse (imperial)

UNF

Unified National Fine (imperial)

ACME

Trapezoidal/ACME thread for power transmission

CUSTOM

User-defined thread profile

ACME = 'ACME'
CUSTOM = 'CUSTOM'
ISO_METRIC = 'ISO'
ISO_METRIC_FINE = 'ISO_F'
UNC = 'UNC'
UNF = 'UNF'
yapcad.collision.check_gear_mesh_collision(gear_a: GearMeshInterface, gear_b: GearMeshInterface, actual_center_distance: float, tolerance: float = 0.1) CompatibilityResult[source]

Check if two gears are correctly positioned for meshing.

This function verifies both gear compatibility and proper positioning by comparing actual center distance to theoretical.

Parameters:
  • gear_a – First gear interface

  • gear_b – Second gear interface

  • actual_center_distance – Measured center-to-center distance in mm

  • tolerance – Allowable deviation from theoretical in mm

Returns:

CompatibilityResult with mesh status and any warnings

Example

>>> sun = GearMeshInterface(name="sun", part_name="SUN", module=0.75, teeth=18)
>>> planet = GearMeshInterface(name="planet", part_name="PLANET", module=0.75, teeth=36)
>>> result = check_gear_mesh_collision(sun, planet, actual_center_distance=20.25)
>>> if result.is_compatible:
...     print("Gears properly meshed")
yapcad.collision.create_planetary_gearbox_interfaces(gearbox_name: str, module: float, sun_teeth: int, planet_teeth: int, ring_teeth: int, face_width: float, num_planets: int = 3, pressure_angle: float = 20.0, mesh_plane_z: float = 0.0) List[GearMeshInterface][source]

Create interface volumes for a complete planetary gearbox.

This factory function creates GearMeshInterface objects for all gears in a planetary gearbox configuration: one sun, one ring, and multiple planet gears at the correct orbital positions.

Parameters:
  • gearbox_name – Base name for the gearbox (e.g., “AXIS1”)

  • module – Gear module in mm

  • sun_teeth – Number of teeth on sun gear

  • planet_teeth – Number of teeth on planet gears

  • ring_teeth – Number of teeth on ring gear (internal)

  • face_width – Gear face width in mm

  • num_planets – Number of planet gears (typically 3)

  • pressure_angle – Pressure angle in degrees (typically 20.0)

  • mesh_plane_z – Z coordinate of the gear mesh plane

Returns:

List of GearMeshInterface objects for all gears

Example

>>> interfaces = create_planetary_gearbox_interfaces(
...     gearbox_name="AXIS1",
...     module=0.75,
...     sun_teeth=18,
...     planet_teeth=36,
...     ring_teeth=90,
...     face_width=10.0,
...     num_planets=3
... )
>>> for iface in interfaces:
...     print(f"{iface.name}: {iface.teeth}T at {iface.center}")

Notes

  • Verifies planetary gear geometry: ring_teeth = sun_teeth + 2 * planet_teeth

  • Places planets at equal angular intervals

  • All gears centered at Z = mesh_plane_z