yapcad.assembly package

Submodules

yapcad.assembly.assembly module

Assembly orchestrator for the yapCAD constraint-based assembly system.

This module provides the main Assembly class that manages parts, mates, and constraints, and orchestrates the constraint solving and validation process.

The Assembly class is the primary user-facing interface for creating and validating assemblies in yapCAD. It coordinates between the datum, mate, and constraint subsystems to ensure assemblies meet design intent.

Key Features:
  • Add parts with initial transforms

  • Define mate relationships between parts

  • Add design constraints to validate intent

  • Solve mate constraints to compute part positions

  • Validate all design constraints

  • Export to URDF, Blender rigs, or other formats

  • Integration with existing kinematic_chain.py transforms

Example

>>> from yapcad.assembly import Assembly, PartDefinition, Datum, DatumType
>>> from yapcad.assembly import Mate, MateType, Constraint, ConstraintType
>>> import numpy as np
>>>
>>> # Create part definitions
>>> motor = PartDefinition("DDSM115_MOTOR")
>>> motor.add_datum(Datum("motor_axis", DatumType.AXIS,
...                       origin=(0, 0, 0), direction=(0, 1, 0)))
>>> motor.add_datum(Datum("stator_face", DatumType.PLANE,
...                       origin=(0, 10, 0), normal=(0, 1, 0)))
>>>
>>> bracket = PartDefinition("WHEEL_BRACKET")
>>> bracket.add_datum(Datum("bore_axis", DatumType.AXIS,
...                         origin=(0, 0, 0), direction=(0, 0, 1)))
>>> bracket.add_datum(Datum("motor_interface", DatumType.PLANE,
...                         origin=(0, 0, 5), normal=(0, 0, 1)))
>>>
>>> # Create assembly
>>> assembly = Assembly("wheel_module")
>>> assembly.add_part(motor, name="motor_1")
>>> assembly.add_part(bracket, name="bracket_1")
>>>
>>> # Add mates to position parts
>>> assembly.add_mate(Mate("mount_mate", MateType.FLUSH,
...                        part1="bracket_1", datum1="motor_interface",
...                        part2="motor_1", datum2="stator_face"))
>>> assembly.add_mate(Mate("axis_mate", MateType.CONCENTRIC,
...                        part1="bracket_1", datum1="bore_axis",
...                        part2="motor_1", datum2="motor_axis"))
>>>
>>> # Add design constraints
>>> assembly.add_constraint(Constraint(
...     "motor_tangent", ConstraintType.TANGENT_TO_CIRCLE,
...     part="motor_1", datum="motor_axis",
...     center=(0, 0, 0), radius=124.5,
...     description="Motor axis must be tangent to chassis for rolling motion"
... ))
>>>
>>> # Validate assembly
>>> result = assembly.validate()
>>> if result.is_valid:
...     print("Assembly is valid!")
...     motor_transform = assembly.transforms["motor_1"]
... else:
...     for error in result.failed_constraints:
...         print(f"ERROR: {error}")
...     for warning in result.warnings:
...         print(f"WARNING: {warning}")

See also

  • datum.py: Datum feature definitions

  • mate.py: Mate constraint definitions

  • constraint.py: Design constraint definitions

class yapcad.assembly.assembly.Assembly(name: str)[source]

Bases: object

Main assembly orchestrator for the yapCAD constraint system.

The Assembly class manages a collection of parts with their transforms, mate relationships that position parts, and design constraints that validate assembly intent.

Workflow:
  1. Create assembly: assembly = Assembly(“my_assembly”)

  2. Add parts: assembly.add_part(part_def, name=”part_1”)

  3. Add mates: assembly.add_mate(mate)

  4. Add constraints: assembly.add_constraint(constraint)

  5. Validate: result = assembly.validate()

The assembly can also import transforms from existing kinematic chains and export to various formats (URDF, Blender, etc.).

name

Unique identifier for this assembly

parts

Dictionary mapping part name to PartDefinition

transforms

Dictionary mapping part name to 4x4 transform matrix

mates

List of Mate objects defining part relationships

constraints

List of Constraint objects to validate

Integration with yapCAD:
  • Transforms are 4x4 numpy arrays compatible with yapcad.xform.Matrix

  • Can import transforms from kinematic_chain.py JSON exports

  • Datums transform correctly through coordinate systems

Example

>>> # Create assembly
>>> assembly = Assembly("robot_wheel")
>>>
>>> # Add parts
>>> assembly.add_part(motor_def, name="motor")
>>> assembly.add_part(bracket_def, name="bracket")
>>>
>>> # Define relationships
>>> assembly.add_mate(Mate("align", MateType.CONCENTRIC,
...                        "bracket", "hole", "motor", "shaft"))
>>>
>>> # Validate design intent
>>> assembly.add_constraint(Constraint(
...     "tangent", ConstraintType.TANGENT_TO_CIRCLE,
...     "motor", "axis", center=(0,0,0), radius=124.5
... ))
>>>
>>> # Check if valid
>>> result = assembly.validate()
>>> if not result.is_valid:
...     assembly.validate_and_raise()  # Raises AssemblyError
add_constraint(constraint: Constraint) None[source]

Add a design constraint to validate assembly intent.

Constraints validate high-level design requirements that go beyond simple positioning. They ensure the assembly meets its intended functional requirements.

Parameters:

constraint – Constraint object to validate

Raises:

AssemblyError – If referenced part or datum doesn’t exist

Example

>>> # Ensure motor axis is tangent to chassis
>>> assembly.add_constraint(Constraint(
...     "motor_tangent", ConstraintType.TANGENT_TO_CIRCLE,
...     part="motor_1", datum="motor_axis",
...     center=(0, 0, 0), radius=124.5,
...     description="Motor must be tangent for rolling motion"
... ))
add_mate(mate: Mate) None[source]

Add a mate relationship between two parts.

Mates define geometric relationships between datum features on different parts. They are used to position parts relative to each other.

Parameters:

mate – Mate object defining the relationship

Raises:

AssemblyError – If referenced parts or datums don’t exist, or if mate is invalid for the given datums

Example

>>> # Align motor shaft with bracket hole
>>> assembly.add_mate(Mate(
...     "shaft_alignment", MateType.CONCENTRIC,
...     part1="bracket", datum1="bore_axis",
...     part2="motor", datum2="shaft_axis"
... ))
add_part(part: PartDefinition, name: str = None, transform: ndarray = None) None[source]

Add a part to the assembly.

Parameters:
  • part – PartDefinition with datum features

  • name – Name for this part instance (default: part.name)

  • transform – Initial 4x4 transform matrix (default: identity)

Raises:

AssemblyError – If part name already exists in assembly

Example

>>> motor = PartDefinition("DDSM115")
>>> assembly.add_part(motor, name="motor_1")
>>> # Or with initial position
>>> T = np.eye(4)
>>> T[0:3, 3] = [100, 0, 0]  # Translate 100mm in X
>>> assembly.add_part(motor, name="motor_2", transform=T)
export_to_blender_rig(output_path: str | Path) None[source]

Export assembly to Blender armature rig for animation.

Creates a Blender Python script that sets up an armature with bones positioned according to the assembly transforms. Useful for visualizing kinematics and creating animations.

Parameters:

output_path – Path where Blender Python script will be written

Note

This is currently a stub implementation. A full implementation would generate a Blender Python script that creates bones, constraints, and animations.

Example

>>> assembly.export_to_blender_rig("robot_rig.py")
>>> # In Blender: File > Import > Python Script > robot_rig.py
export_to_urdf(output_path: str | Path) None[source]

Export assembly to URDF format for ROS integration.

URDF (Unified Robot Description Format) is used by ROS for robot kinematics and dynamics. This export enables simulation and control of yapCAD assemblies in ROS.

Parameters:

output_path – Path where URDF file will be written

Note

This is currently a stub implementation. A full implementation would generate proper URDF XML with links, joints, and meshes.

Example

>>> assembly.export_to_urdf("robot.urdf")
>>> # Use in ROS: roslaunch robot_description display.launch
get_degrees_of_freedom() Dict[str, int][source]

Calculate remaining degrees of freedom for each part after mates.

Analyzes the mate constraints to determine how many degrees of freedom remain unconstrained for each part. A fully constrained part has 0 DOF.

Returns:

Dictionary mapping part name to DOF count (0-6)

Note

This is a simplified implementation. A full implementation would analyze the constraint graph to detect over/under-constrained situations and redundant constraints.

Example

>>> dof = assembly.get_degrees_of_freedom()
>>> for part, dof_count in dof.items():
...     if dof_count > 0:
...         print(f"{part} has {dof_count} DOF remaining")
get_transformed_datum(part_name: str, datum_name: str) Datum[source]

Get a datum feature transformed to world coordinates.

Applies the part’s current transform to a datum to obtain its position and orientation in world coordinates.

Parameters:
  • part_name – Name of the part

  • datum_name – Name of the datum on that part

Returns:

Datum transformed to world coordinates

Raises:

AssemblyError – If part or datum not found

Example

>>> # Get motor axis in world coordinates
>>> motor_axis_world = assembly.get_transformed_datum(
...     "motor_1", "motor_axis"
... )
>>> print(f"Axis origin: {motor_axis_world.origin}")
>>> print(f"Axis direction: {motor_axis_world.direction}")
import_transforms_from_kinematic_chain(json_path: str | Path) None[source]

Import part transforms from a kinematic_chain.py JSON export.

The kinematic chain system computes transforms through a hierarchical tree. This method imports those transforms into the assembly so constraints can be validated against them.

Parameters:

json_path – Path to JSON file with kinematic chain transforms

Raises:

AssemblyError – If JSON cannot be loaded or part names don’t match

Example

>>> # Export from kinematic_chain.py
>>> chain.export_transforms("chain_transforms.json")
>>>
>>> # Import into assembly
>>> assembly.import_transforms_from_kinematic_chain(
...     "chain_transforms.json"
... )
>>>
>>> # Now validate against those transforms
>>> result = assembly.validate()
report() str[source]

Generate a comprehensive human-readable assembly report.

Returns:

Multi-line string with assembly status and validation results

Example

>>> print(assembly.report())
Assembly: robot_wheel
====================================
Parts: 2
  - motor_1 (DDSM115_MOTOR)
  - bracket_1 (WHEEL_BRACKET)
Mates: 2
  • shaft_alignment (CONCENTRIC)

  • mount_surface (FLUSH)

Constraints: 1
  • motor_tangent (TANGENT_TO_CIRCLE)

Validation: VALID

validate() AssemblyValidationResult[source]

Validate all design constraints against current part transforms.

Evaluates each constraint in the assembly and returns a comprehensive validation result. This does not solve mates - it validates the current state of the assembly.

Returns:

AssemblyValidationResult with detailed status

Example

>>> result = assembly.validate()
>>> if result.is_valid:
...     print("Assembly is valid!")
... else:
...     print(result.report())
validate_and_raise() None[source]

Validate assembly and raise AssemblyError if invalid.

Convenience method for strict validation where you want an exception on any constraint failure.

Raises:

AssemblyError – If any constraint fails

Example

>>> try:
...     assembly.validate_and_raise()
...     print("Assembly is valid, proceeding...")
... except AssemblyError as e:
...     print(f"Invalid assembly: {e}")
exception yapcad.assembly.assembly.AssemblyError(message: str, assembly_name: str = None, part_name: str = None, constraint_name: str = None)[source]

Bases: Exception

Exception raised when assembly operations fail.

This exception is raised for various assembly-related errors such as: - Invalid part names or references - Missing datums on parts - Invalid mate configurations - Constraint validation failures - Solver convergence failures

message

Description of the error

assembly_name

Name of the assembly where error occurred

part_name

Name of the part involved (if applicable)

constraint_name

Name of the constraint that failed (if applicable)

class yapcad.assembly.assembly.AssemblyValidationResult(is_valid: bool, constraint_results: Dict[str, ~yapcad.assembly.constraint.ConstraintResult]=<factory>, failed_constraints: List[str] = <factory>, warnings: List[str] = <factory>)[source]

Bases: object

Result of validating an assembly’s constraints.

Contains the complete validation status including which constraints passed, failed, or produced warnings, along with detailed diagnostic information for each constraint.

is_valid

True if all constraints are satisfied, False otherwise

Type:

bool

constraint_results

Dictionary mapping constraint name to ConstraintResult

Type:

Dict[str, yapcad.assembly.constraint.ConstraintResult]

failed_constraints

List of names of constraints that failed

Type:

List[str]

warnings

List of warning messages from constraint validation

Type:

List[str]

Example

>>> result = assembly.validate()
>>> if not result.is_valid:
...     print(f"Found {len(result.failed_constraints)} failures")
...     for name in result.failed_constraints:
...         cr = result.constraint_results[name]
...         print(f"  {name}: {cr.message}")
constraint_results: Dict[str, ConstraintResult]
failed_constraints: List[str]
is_valid: bool
report() str[source]

Generate detailed validation report.

warnings: List[str]

yapcad.assembly.constraint module

Assembly constraint validation for yapCAD.

This module provides high-level design constraints that validate assembly intent after mates have positioned parts. Constraints check that the resulting assembly meets design requirements like tangency, radial orientation, and clearances.

Constraints differ from mates: - Mates POSITION parts (determine transforms) - Constraints VALIDATE the result (check design intent)

Example

from yapcad.assembly.constraint import (

Constraint, ConstraintType, ConstraintResult

)

# Motor axis must be tangent to chassis (not radial) constraint = Constraint(

name=”wheel_axis_tangent”, constraint_type=ConstraintType.TANGENT_TO_CIRCLE, part=”DDSM115_MOTOR_1”, datum=”motor_axis”, center=(0.0, 0.0, 0.0), radius=124.5, description=”Motor axis tangent to wheel path for rolling motion”

)

# Evaluate against a datum in world coordinates result = constraint.evaluate(datum_world) if not result.passed:

print(f”Constraint violated: {result.error_message}”) print(f”Error: {result.error_value:.2f}°”)

class yapcad.assembly.constraint.Constraint(name: str, constraint_type: ConstraintType, part: str, datum: str, center: Tuple[float, float, float] | None = None, radius: float | None = None, direction: str | None = None, axis: Tuple[float, float, float] | None = None, reference_datum: str | None = None, plane_normal: Tuple[float, float, float] | None = None, min_value: float | None = None, max_value: float | None = None, validator: Callable[[Any], bool] | None = None, tolerance_deg: float = 1.0, tolerance_mm: float = 0.1, description: str = '', severity: str = 'error')[source]

Bases: object

A design constraint that validates assembly intent.

Constraints are evaluated after mates have positioned all parts. They verify that the resulting assembly meets design requirements.

Unlike mates (which determine part positions), constraints validate that the positions satisfy high-level design intent.

name

Unique identifier for this constraint

Type:

str

constraint_type

Type of constraint to evaluate

Type:

yapcad.assembly.constraint.ConstraintType

part

Name of the part containing the datum to check

Type:

str

datum

Name of the datum feature to evaluate

Type:

str

center

Reference center point for circular/radial constraints

Type:

Tuple[float, float, float] | None

radius

Reference radius for circular constraints (mm)

Type:

float | None

direction

Reference direction (“inward”, “outward”, “+x”, “-z”, etc.)

Type:

str | None

axis

Reference axis vector for parallel/perpendicular constraints

Type:

Tuple[float, float, float] | None

reference_datum

Reference datum for relative constraints

Type:

str | None

plane_normal

Normal vector for plane constraints

Type:

Tuple[float, float, float] | None

min_value

Minimum allowed value for range constraints

Type:

float | None

max_value

Maximum allowed value for range constraints

Type:

float | None

validator

Custom validation function for CUSTOM constraint type

Type:

Callable[[Any], bool] | None

tolerance_deg

Angular tolerance in degrees (default: 1.0°)

Type:

float

tolerance_mm

Linear tolerance in millimeters (default: 0.1mm)

Type:

float

description

Human-readable description of design intent

Type:

str

severity

“error”, “warning”, or “info”

Type:

str

Examples

# Motor axis tangent to wheel path (perpendicular to radial) >>> tangent = Constraint( … name=”wheel_axis_tangent”, … constraint_type=ConstraintType.TANGENT_TO_CIRCLE, … part=”DDSM115_MOTOR_1”, … datum=”motor_axis”, … center=(0.0, 0.0, 0.0), … radius=124.5, … tolerance_deg=2.0, … description=”Motor axis must be tangent for rolling motion” … )

# Tire tread must face outward toward tube wall >>> radial = Constraint( … name=”tire_faces_outward”, … constraint_type=ConstraintType.RADIAL_FROM_CENTER, … part=”DDSM115_MOTOR_1”, … datum=”tire_face”, … center=(0.0, 0.0, 0.0), … direction=”outward”, … tolerance_deg=5.0, … description=”Tire tread faces tube wall for traction” … )

# Mounting face must face upward >>> facing = Constraint( … name=”mount_faces_up”, … constraint_type=ConstraintType.FACING, … part=”BRACKET”, … datum=”mounting_face”, … direction=”+z”, … tolerance_deg=1.0, … description=”Mounting face must be horizontal” … )

# Part at specific radius from center >>> at_radius = Constraint( … name=”motor_at_wheel_radius”, … constraint_type=ConstraintType.AT_RADIUS, … part=”MOTOR”, … datum=”motor_center”, … center=(0.0, 0.0, 0.0), … radius=124.5, … tolerance_mm=0.5, … description=”Motor positioned on wheel path circle” … )

axis: Tuple[float, float, float] | None = None
center: Tuple[float, float, float] | None = None
constraint_type: ConstraintType
datum: str
description: str = ''
direction: str | None = None
evaluate(datum_world: Datum) ConstraintResult[source]

Evaluate this constraint against a datum in world coordinates.

Parameters:

datum_world – The datum feature transformed to world coordinates

Returns:

ConstraintResult indicating whether constraint is satisfied

Raises:
  • ValueError – If required parameters are missing

  • ImportError – If numpy is required but not available

max_value: float | None = None
min_value: float | None = None
name: str
part: str
plane_normal: Tuple[float, float, float] | None = None
radius: float | None = None
reference_datum: str | None = None
severity: str = 'error'
tolerance_deg: float = 1.0
tolerance_mm: float = 0.1
validator: Callable[[Any], bool] | None = None
class yapcad.assembly.constraint.ConstraintResult(passed: bool, error_value: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of evaluating a design constraint.

passed

True if constraint is satisfied within tolerance

Type:

bool

error_value

Numeric measure of constraint violation (degrees or mm)

Type:

float

error_message

Human-readable description of the result

Type:

str

details

Additional information about the evaluation

Type:

Dict[str, Any]

details: Dict[str, Any]
error_message: str = ''
error_value: float = 0.0
passed: bool
class yapcad.assembly.constraint.ConstraintType(*values)[source]

Bases: Enum

High-level design constraints that validate assembly intent.

These constraint types capture common design requirements in mechanical assemblies that are difficult to express with low-level mates alone.

TANGENT_TO_CIRCLE

Axis is tangent (perpendicular to radial) to a circle. Used for wheels that must roll along a circular path.

RADIAL_FROM_CENTER

Datum normal points toward (inward) or away from (outward) a center point. Used for features that must face the center or periphery of an assembly.

FACING

Datum normal points in a specified global direction (+x, -y, +z, etc.).

AT_RADIUS

Datum origin is at a specific radius from a center point.

PARALLEL_TO

Datum direction is parallel to a reference direction.

PERPENDICULAR_TO

Datum direction is perpendicular to a reference direction.

AT_RADIUS = 'at_radius'
CUSTOM = 'custom'
FACING = 'facing'
IN_PLANE = 'in_plane'
MIN_DISTANCE = 'min_distance'
NO_INTERFERENCE = 'no_interference'
ON_CIRCLE = 'on_circle'
PARALLEL_TO = 'parallel_to'
PERPENDICULAR_TO = 'perpendicular_to'
RADIAL_FROM_CENTER = 'radial_from_center'
TANGENT_TO_CIRCLE = 'tangent_to_circle'
yapcad.assembly.constraint.angle_between_vectors(v1: Tuple[float, float, float], v2: Tuple[float, float, float]) float[source]

Calculate angle between two vectors in degrees.

Parameters:
  • v1 – First vector (x, y, z)

  • v2 – Second vector (x, y, z)

Returns:

Angle in degrees [0, 180]

Example

>>> angle_between_vectors((1, 0, 0), (0, 1, 0))
90.0
>>> angle_between_vectors((1, 0, 0), (1, 0, 0))
0.0
yapcad.assembly.constraint.distance_to_point(p1: Tuple[float, float, float], p2: Tuple[float, float, float]) float[source]

Calculate Euclidean distance between two points.

Parameters:
  • p1 – First point (x, y, z)

  • p2 – Second point (x, y, z)

Returns:

Distance in same units as input

Example

>>> distance_to_point((0, 0, 0), (3, 4, 0))
5.0
yapcad.assembly.constraint.dot_product(v1: Tuple[float, float, float], v2: Tuple[float, float, float]) float[source]

Calculate dot product of two vectors.

Parameters:
  • v1 – First vector (x, y, z)

  • v2 – Second vector (x, y, z)

Returns:

Dot product (scalar)

Example

>>> dot_product((1, 0, 0), (0, 1, 0))
0.0
>>> dot_product((1, 2, 3), (1, 2, 3))
14.0
yapcad.assembly.constraint.is_radial_from_center(point: Tuple[float, float, float], normal: Tuple[float, float, float], center: Tuple[float, float, float], direction: str = 'outward', tolerance_deg: float = 1.0) bool[source]

Check if a normal vector points radially from a center point.

Parameters:
  • point – Location of the datum (x, y, z)

  • normal – Normal vector to check (x, y, z)

  • center – Center point (x, y, z)

  • direction – “outward” or “inward”

  • tolerance_deg – Angular tolerance in degrees

Returns:

True if normal is radial within tolerance

Example

>>> # Point at (10, 0, 0) with normal pointing in +x (outward from origin)
>>> is_radial_from_center((10, 0, 0), (1, 0, 0), (0, 0, 0), "outward")
True
yapcad.assembly.constraint.is_tangent_to_circle(axis_origin: Tuple[float, float, float], axis_direction: Tuple[float, float, float], circle_center: Tuple[float, float, float], circle_radius: float, tolerance_deg: float = 1.0) bool[source]

Check if an axis is tangent to a circle in the XY plane.

An axis is tangent when its direction is perpendicular to the radial vector from the circle center to the axis origin.

Parameters:
  • axis_origin – Point on the axis (x, y, z)

  • axis_direction – Direction vector of the axis (x, y, z)

  • circle_center – Center of the circle (x, y, z)

  • circle_radius – Radius of the circle

  • tolerance_deg – Angular tolerance in degrees

Returns:

True if axis is tangent within tolerance

Example

>>> # Axis at (10, 0, 0) pointing in +y direction, tangent to origin circle
>>> is_tangent_to_circle((10, 0, 0), (0, 1, 0), (0, 0, 0), 10.0)
True

yapcad.assembly.datum module

Datum feature system for constraint-based assembly in yapCAD.

This module provides datum features (geometric references) and part definitions that enable declarative assembly constraints. Datums are named geometric features (points, axes, planes, frames, circles) defined in a part’s local coordinate system that can be used to establish mates and constraints between parts.

Key concepts:
  • Datum: A named geometric reference on a part (point, axis, plane, etc.)

  • PartDefinition: A part with its named datum features

  • Datums transform with the part when assembled

  • Datums enable declarative mate constraints (FLUSH, CONCENTRIC, etc.)

Example usage:

from yapcad.assembly.datum import Datum, DatumType, PartDefinition
from yapcad.geom import point

# Define a motor with datum features
motor = PartDefinition(
    name="DDSM115_MOTOR",
    geometry_source="cots/motor.dsl",
    is_printable=False,
    material="Aluminum"
)

# Add mounting plane datum
motor.add_datum(Datum(
    name="stator_face",
    datum_type=DatumType.PLANE,
    origin=point(0, 10, 0),
    normal=point(0, 1, 0, 0),
    description="Stator mounting surface facing +Y"
))

# Add rotation axis datum
motor.add_datum(Datum(
    name="motor_axis",
    datum_type=DatumType.AXIS,
    origin=point(0, 0, 0),
    direction=point(0, 1, 0, 0),
    description="Motor rotation axis along Y"
))

# Add mounting hole circle datum
motor.add_datum(Datum(
    name="mounting_holes",
    datum_type=DatumType.CIRCLE,
    origin=point(0, 10, 0),
    normal=point(0, 1, 0, 0),
    radius=15.2,
    description="M2.5 mounting hole pattern at r=15.2mm"
))

# Validate all datums
issues = motor.validate_datums()
if issues:
    print("Datum validation issues:", issues)
class yapcad.assembly.datum.Datum(name: str, datum_type: DatumType, origin: List[float] = <factory>, direction: List[float] | None = None, normal: List[float] | None = None, x_axis: List[float] | None = None, y_axis: List[float] | None = None, radius: float | None = None, description: str = '', tags: List[str] = <factory>)[source]

Bases: object

A named geometric reference feature on a part.

Datums are defined in the part’s local coordinate system and transform with the part when assembled. They provide explicit geometric references that can be used to establish assembly mates and constraints.

All geometric parameters use yapCAD’s homogeneous coordinate convention:
  • Points: [x, y, z, 1] (w=1 for positions)

  • Directions/Normals: [x, y, z, 0] (w=0 for direction vectors)

name

Unique identifier for this datum within the part

Type:

str

datum_type

Type of geometric feature (POINT, AXIS, PLANE, etc.)

Type:

yapcad.assembly.datum.DatumType

origin

Origin point [x, y, z, 1] in part’s local coordinates

Type:

List[float]

direction

Direction vector [x, y, z, 0] for AXIS datum

Type:

List[float] | None

normal

Normal vector [x, y, z, 0] for PLANE or CIRCLE datum

Type:

List[float] | None

x_axis

X-axis direction [x, y, z, 0] for FRAME datum

Type:

List[float] | None

y_axis

Y-axis direction [x, y, z, 0] for FRAME datum

Type:

List[float] | None

radius

Radius value (in mm) for CIRCLE datum

Type:

float | None

description

Human-readable description of this datum’s purpose

Type:

str

tags

Optional metadata tags for filtering/searching datums

Type:

List[str]

Example:

# Mounting plane on motor stator
stator_face = Datum(
    name="stator_face",
    datum_type=DatumType.PLANE,
    origin=[0, 10, 0, 1],
    normal=[0, 1, 0, 0],
    description="Stator mounting surface facing +Y"
)

# Rotation axis through motor shaft
motor_axis = Datum(
    name="motor_axis",
    datum_type=DatumType.AXIS,
    origin=[0, 0, 0, 1],
    direction=[0, 1, 0, 0],
    description="Motor rotation axis along Y"
)

# Mounting hole pattern
holes = Datum(
    name="mounting_holes",
    datum_type=DatumType.CIRCLE,
    origin=[0, 10, 0, 1],
    normal=[0, 1, 0, 0],
    radius=15.2,
    description="M2.5 mounting holes at 15.2mm radius"
)
datum_type: DatumType
description: str = ''
direction: List[float] | None = None
name: str
normal: List[float] | None = None
origin: List[float]
radius: float | None = None
tags: List[str]
transform(matrix: Matrix | List[List[float]]) Datum[source]

Return a new Datum transformed by the given 4x4 matrix.

Transforms the datum’s geometric features by applying the given transformation matrix. Points are translated, direction vectors are rotated (but not translated), and the radius is preserved (assuming uniform scaling).

Parameters:

matrix – 4x4 transformation matrix, either as yapCAD Matrix object or as a list of 4 rows (each row is a 4-element list)

Returns:

A new Datum with transformed geometry

Example:

from yapcad.xform import Translation
from yapcad.geom import point

# Original datum at origin
datum = Datum("mount", DatumType.POINT, origin=point(0, 0, 0))

# Transform by translation
T = Translation(point(10, 20, 30))
new_datum = datum.transform(T)
# new_datum.origin is now [10, 20, 30, 1]
x_axis: List[float] | None = None
y_axis: List[float] | None = None
class yapcad.assembly.datum.DatumType(*values)[source]

Bases: Enum

Types of datum features that can be defined on a part.

Datum types correspond to common geometric references used in mechanical design and assembly constraints:

  • POINT: A single point in 3D space

  • AXIS: An infinite line defined by origin + direction

  • PLANE: An infinite plane defined by origin + normal

  • FRAME: A full coordinate frame with origin + 3 orthogonal axes

  • CIRCLE: A circle defined by center + normal + radius

AXIS = 'axis'
CIRCLE = 'circle'
FRAME = 'frame'
PLANE = 'plane'
POINT = 'point'
class yapcad.assembly.datum.PartDefinition(name: str, datums: Dict[str, ~yapcad.assembly.datum.Datum]=<factory>, geometry_source: str | None = None, is_printable: bool = True, material: str = 'PETG', description: str = '')[source]

Bases: object

Definition of a part with its datum features.

A PartDefinition extends the concept of a part to include explicit geometric datum features that can be used for assembly constraints. This enables declarative mate definitions (FLUSH, CONCENTRIC, etc.) that reference named datums rather than requiring manual transform calculations.

name

Unique identifier for this part

Type:

str

datums

Dictionary mapping datum names to Datum objects

Type:

Dict[str, yapcad.assembly.datum.Datum]

geometry_source

Path to STL file, DSL command, or other geometry

Type:

str | None

is_printable

Whether this part is 3D printed (vs. COTS)

Type:

bool

material

Material specification (e.g., “PETG”, “Aluminum”)

Type:

str

description

Human-readable description of part function

Type:

str

Example:

from yapcad.assembly.datum import PartDefinition, Datum, DatumType
from yapcad.geom import point

# Define a wheel bracket part
bracket = PartDefinition(
    name="WHEEL_BRACKET",
    geometry_source="parts/bracket.dsl",
    is_printable=True,
    material="PETG",
    description="Mounting bracket for drive wheel motor"
)

# Add datum for motor mounting face
bracket.add_datum(Datum(
    name="motor_interface",
    datum_type=DatumType.PLANE,
    origin=point(0, 0, 0),
    normal=point(0, 0, 1, 0),
    description="Motor mounting face normal to +Z"
))

# Add datum for motor bore axis
bracket.add_datum(Datum(
    name="bore_axis",
    datum_type=DatumType.AXIS,
    origin=point(0, 0, 0),
    direction=point(0, 0, 1, 0),
    description="Motor shaft bore along Z axis"
))

# Validate all datums
issues = bracket.validate_datums()
if issues:
    print("Warning:", issues)
add_datum(datum: Datum) PartDefinition[source]

Add a datum feature to this part.

Parameters:

datum – The Datum object to add

Returns:

Self (for method chaining)

Raises:

ValueError – If a datum with the same name already exists

Example:

bracket = PartDefinition("BRACKET")
bracket.add_datum(Datum("face", DatumType.PLANE, ...))
bracket.add_datum(Datum("axis", DatumType.AXIS, ...))
datums: Dict[str, Datum]
description: str = ''
geometry_source: str | None = None
get_datum(name: str) Datum[source]

Get a datum by name.

Parameters:

name – Name of the datum to retrieve

Returns:

The Datum object

Raises:

KeyError – If no datum with that name exists

Example:

bracket = PartDefinition("BRACKET")
bracket.add_datum(Datum("mount_face", ...))
face = bracket.get_datum("mount_face")
is_printable: bool = True
material: str = 'PETG'
name: str
validate_datums() List[str][source]

Validate all datum definitions, returning list of issues.

Performs validation checks on all datums to detect potential errors:
  • Duplicate definitions (same origin and type)

  • Overlapping features that might indicate copy-paste errors

Returns:

List of warning/error messages (empty if all valid)

Example:

bracket = PartDefinition("BRACKET")
bracket.add_datum(Datum("face1", DatumType.PLANE, origin=[0,0,0], ...))
bracket.add_datum(Datum("face2", DatumType.PLANE, origin=[0,0,0], ...))

issues = bracket.validate_datums()
# issues will contain warning about duplicate origin

yapcad.assembly.datum_registry module

Central datum registry for cross-DSL datum linking.

This module provides a unified interface to access datums from various sources: - DSL files (via sidecar JSON or metadata extraction) - COTS surrogate JSON files (for commercial off-the-shelf parts) - Programmatically defined datums

The registry enables face-to-face mate computation by providing a single point of access for datum definitions regardless of their source.

Example usage:

from yapcad.assembly.datum_registry import DatumRegistry

# Get a datum from a COTS surrogate file
stator_face = DatumRegistry.get_datum(
    "cots/xh430_surrogate.json",
    "stator_mounting_face"
)

# Get a datum by part name (searches registered sources)
servo_axis = DatumRegistry.get_datum(
    "AXIS3_SERVO_XH430",
    "rotation_axis"
)

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.assembly.datum_registry.DatumRegistry[source]

Bases: object

Central registry for datums from DSL files and COTS surrogates.

The registry provides a unified interface to access datums regardless of their source (DSL, STL metadata, surrogate JSON files).

This is implemented as a singleton to ensure consistent state across the application.

Class Methods:

get_datum(source, name): Get a datum by source and name register_source(source_id, datums): Register a source with datums register_datum(source_id, datum): Add a single datum to a source list_sources(): List all registered sources list_datums(source): List all datums in a source clear(): Clear all registered datums (for testing)

Example:

# Load from surrogate JSON
stator = DatumRegistry.get_datum(
    "cots/xh430_surrogate.json",
    "stator_mounting_face"
)

# Register programmatic datums
DatumRegistry.register_datum(
    "LINK_2_3",
    Datum("servo_mount_face", DatumType.PLANE, ...)
)
classmethod add_search_path(path: str) None[source]

Add a directory to search for datum source files.

Parameters:

path – Directory path to add to search paths

classmethod clear() None[source]

Clear all registered datums (useful for testing).

classmethod get_datum(source: str, name: str) Datum[source]

Get a datum by source and name.

Parameters:
  • source – Source identifier - can be: - DSL file path: “scara_arm/scara_arm.dsl” - Surrogate JSON: “cots/xh430_surrogate.json” - Part name: “AXIS3_SERVO_XH430”, “LINK_2_3”

  • name – Datum name within the source e.g., “servo_mount_face”, “stator_face”

Returns:

The Datum object

Raises:

KeyError – If datum not found

classmethod get_datums_for_source(source: str) Dict[str, Datum][source]

Get all datums from a source.

Parameters:

source – Source identifier

Returns:

Dictionary of datum name -> Datum object

classmethod list_datums(source: str) List[str][source]

List all datum names in a source.

Parameters:

source – Source identifier

Returns:

List of datum names

classmethod list_sources() List[str][source]

List all registered source identifiers.

classmethod register_datum(source_id: str, datum: Datum) None[source]

Add a single datum to a source.

Creates the source if it doesn’t exist.

Parameters:
  • source_id – Source identifier

  • datum – The Datum to register

classmethod register_part(part: PartDefinition) None[source]

Register a PartDefinition and all its datums.

Parameters:

part – PartDefinition with datum features

classmethod register_source(source_id: str, datums: Dict[str, Datum], source_type: str = 'programmatic', metadata: Dict[str, Any] | None = None) None[source]

Register a source with its datums.

Parameters:
  • source_id – Unique identifier for this source

  • datums – Dictionary of datum name -> Datum object

  • source_type – Type of source (“json”, “dsl”, “programmatic”)

  • metadata – Optional metadata about the source

class yapcad.assembly.datum_registry.DatumSource(source_id: str, source_type: str, file_path: str | None = None, datums: Dict[str, ~yapcad.assembly.datum.Datum]=<factory>, part_names: Set[str] = <factory>, metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Represents a source of datum definitions.

source_id

Unique identifier for this source (path or name)

Type:

str

source_type

Type of source (“json”, “dsl”, “programmatic”)

Type:

str

file_path

Absolute path to source file (if file-based)

Type:

str | None

datums

Dictionary of datum name -> Datum object

Type:

Dict[str, yapcad.assembly.datum.Datum]

part_names

Set of part names defined in this source

Type:

Set[str]

metadata

Additional source metadata

Type:

Dict[str, Any]

datums: Dict[str, Datum]
file_path: str | None = None
metadata: Dict[str, Any]
part_names: Set[str]
source_id: str
source_type: str
yapcad.assembly.datum_registry.datum_to_transform_matrix(datum: Datum) Any | None[source]

Convert a Datum to a 4x4 transformation matrix.

For FRAME datums, constructs the full transform from axes. For PLANE datums, constructs a frame with normal as Z-axis. For AXIS datums, constructs a frame with direction as Z-axis.

Parameters:

datum – The datum to convert

Returns:

4x4 numpy array (if numpy available), or None

yapcad.assembly.face_mate module

Face-to-face mate system with automatic transform computation.

This module provides the FaceToFaceMate class which computes transforms automatically from datum face constraints, replacing hardcoded transform values with explicit geometric relationships.

Key Features:
  • Automatic transform computation from datum geometry

  • Support for FLUSH, ALIGNED, OFFSET, and CONCENTRIC constraints

  • Validation of mate satisfaction with tolerances

  • Integration with DatumRegistry for cross-DSL datum lookup

Example usage:

from yapcad.assembly.face_mate import FaceToFaceMate
from yapcad.assembly.mate import MateType

# Define a mate between servo stator and link mounting face
servo_mate = FaceToFaceMate(
    name="axis3_servo_to_link",
    parent_part="LINK_2_3",
    parent_datum="servo_mount_face",
    parent_source="scara_arm/scara_arm.dsl",
    child_part="AXIS3_SERVO_XH430",
    child_datum="stator_mounting_face",
    child_source="cots/xh430_surrogate.json",
    constraint=MateType.COINCIDENT,
)

# Compute the transform automatically
transform_matrix = servo_mate.compute_transform()

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.assembly.face_mate.FaceToFaceMate(name: str, parent_part: str, parent_datum: str, child_part: str, child_datum: str, parent_source: str | None = None, child_source: str | None = None, constraint: MateType = MateType.COINCIDENT, offset_distance: float = 0.0, rotation_about_normal: float = 0.0, _computed_transform: Any | None = None, _parent_datum_obj: Datum | None = None, _child_datum_obj: Datum | None = None)[source]

Bases: object

Defines how two parts mate via their datum faces.

This is the key abstraction that replaces hardcoded transforms with explicit geometric constraints. The transform is computed automatically from the datum geometry.

Constraint Types:
  • COINCIDENT/FLUSH: Faces touch, normals point in opposite directions

  • CONCENTRIC: Axes are colinear (for cylindrical features)

  • PARALLEL: Faces parallel with optional offset

  • ALIGNED: Faces touch, normals point in same direction

name

Descriptive name for this mate

Type:

str

parent_part

Name of the parent (fixed) part

Type:

str

parent_datum

Datum name on parent part

Type:

str

parent_source

Source file for parent datums (DSL or surrogate JSON)

Type:

str | None

child_part

Name of the child (moving) part

Type:

str

child_datum

Datum name on child part

Type:

str

child_source

Source file for child datums

Type:

str | None

constraint

Constraint type (MateType enum)

Type:

yapcad.assembly.mate.MateType

offset_distance

Optional offset along normal (for OFFSET constraint)

Type:

float

rotation_about_normal

Optional rotation about mating axis (degrees)

Type:

float

child_datum: str
child_part: str
child_source: str | None = None
compute_transform(parent_world_transform: Any | None = None) Any | None[source]

Compute the transform that places child relative to parent.

This is the core algorithm that replaces hardcoded transforms.

Parameters:

parent_world_transform – Optional 4x4 world transform of parent (for computing absolute child position)

Returns:

4x4 numpy array transform, or None if numpy not available

Raises:
constraint: MateType = 'coincident'
invalidate_cache() None[source]

Clear cached transform (call if datums change).

name: str
offset_distance: float = 0.0
parent_datum: str
parent_part: str
parent_source: str | None = None
rotation_about_normal: float = 0.0
validate(tolerance_mm: float = 0.1, tolerance_deg: float = 1.0) MateValidationResult[source]

Validate that the mate constraint is satisfied.

Parameters:
  • tolerance_mm – Linear tolerance in millimeters

  • tolerance_deg – Angular tolerance in degrees

Returns:

MateValidationResult with satisfaction status and errors

class yapcad.assembly.face_mate.MateValidationResult(satisfied: bool, error_distance: float = 0.0, error_angle: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of validating a mate constraint.

satisfied

True if constraint is satisfied within tolerance

Type:

bool

error_distance

Position error in mm

Type:

float

error_angle

Angular error in degrees

Type:

float

error_message

Human-readable description

Type:

str

details

Additional validation information

Type:

Dict[str, Any]

details: Dict[str, Any]
error_angle: float = 0.0
error_distance: float = 0.0
error_message: str = ''
satisfied: bool
yapcad.assembly.face_mate.compute_concentric_transform(parent_datum: Datum, child_datum: Datum, offset: float = 0.0) Any[source]

Compute transform to make child axis colinear with parent axis.

For CONCENTRIC mates, the axes must be colinear but origins can differ.

Parameters:
  • parent_datum – Parent axis datum

  • child_datum – Child axis datum

  • offset – Offset along axis direction

Returns:

4x4 numpy transform matrix

yapcad.assembly.face_mate.compute_flush_transform(parent_datum: Datum, child_datum: Datum, offset: float = 0.0, rotation_deg: float = 0.0, flip_normal: bool = True) Any[source]

Compute transform to make child flush with parent.

The result is a transform T such that: - child_datum.origin moves to parent_datum.origin (+ offset along normal) - child_datum.normal aligns with parent_datum.normal

(opposite if flip_normal=True for face-to-face contact)

Uses Rodrigues’ rotation formula for efficient axis-angle rotation.

Parameters:
  • parent_datum – Parent datum (target)

  • child_datum – Child datum (to be transformed)

  • offset – Optional offset along parent normal

  • rotation_deg – Optional rotation about mating axis

  • flip_normal – If True, child normal opposes parent (face-to-face)

Returns:

4x4 numpy transform matrix

yapcad.assembly.face_mate.compute_parallel_transform(parent_datum: Datum, child_datum: Datum, offset: float = 0.0) Any[source]

Compute transform to make child parallel to parent.

For PARALLEL mates, normals align but origins don’t necessarily coincide.

Parameters:
  • parent_datum – Parent plane datum

  • child_datum – Child plane datum

  • offset – Offset along normal direction

Returns:

4x4 numpy transform matrix

yapcad.assembly.face_mate.create_axis_mate(name: str, parent_part: str, parent_datum: str, child_part: str, child_datum: str, parent_source: str | None = None, child_source: str | None = None, offset: float = 0.0) FaceToFaceMate[source]

Create a concentric (axis-aligned) mate.

Convenience function for coaxial alignment.

Parameters:
  • name – Mate name

  • parent_part – Parent part name

  • parent_datum – Parent axis datum name

  • child_part – Child part name

  • child_datum – Child axis datum name

  • parent_source – Optional source file for parent

  • child_source – Optional source file for child

  • offset – Optional offset along axis

Returns:

Configured FaceToFaceMate

yapcad.assembly.face_mate.create_face_mate(name: str, parent_part: str, parent_datum: str, child_part: str, child_datum: str, parent_source: str | None = None, child_source: str | None = None, offset: float = 0.0, rotation: float = 0.0) FaceToFaceMate[source]

Create a face-to-face (COINCIDENT) mate.

Convenience function for the most common mate type.

Parameters:
  • name – Mate name

  • parent_part – Parent part name

  • parent_datum – Parent datum name

  • child_part – Child part name

  • child_datum – Child datum name

  • parent_source – Optional source file for parent

  • child_source – Optional source file for child

  • offset – Optional offset along normal

  • rotation – Optional rotation about normal (degrees)

Returns:

Configured FaceToFaceMate

yapcad.assembly.intent module

Declarative Assembly Intent System for yapCAD.

This module provides a declarative approach to assembly design where designers specify WHAT parts must do (functional requirements) rather than HOW to achieve it (explicit transforms). The system derives geometry from requirements.

Key Concepts:

AssemblyIntent: Top-level container for declarative assembly specification FunctionalRequirement: What a part must DO (contact, roll, align, etc.) Connection: How parts relate (topology, not exact position) Clearance: What must NOT happen (collision avoidance) ReferenceGeometry: Fixed geometry that parts must relate to

Example - Wheel Assembly:
>>> tube = ReferenceGeometry(
...     name="tube_inner_wall",
...     geometry_type="cylinder",
...     center=(0, 0, 0),
...     axis=(0, 0, 1),
...     radius=175.0,
... )
>>>
>>> wheel_assembly = AssemblyIntent(
...     name="wheel_pod",
...     reference_geometry={"tube": tube},
...     functional_requirements=[
...         ContactRequirement(
...             name="wheel_contact",
...             part="motor",
...             surface="tire_outer",
...             target="tube_inner_wall",
...             contact_type="rolling",
...         ),
...         RollRequirement(
...             name="roll_along_z",
...             part="motor",
...             roll_direction="along_tube_axis",
...         ),
...     ],
...     connections=[
...         Connection(
...             parent="chassis.pivot_boss",
...             child="wheel_arm.pivot_bore",
...             joint_type="revolute",
...             axis="tangent",
...         ),
...     ],
...     clearances=[
...         Clearance("motor", "chassis", min_distance=5.0),
...     ],
...     derived_parameters=["wheel_arm.length", "wheel_center_radius"],
... )
>>>
>>> result = wheel_assembly.solve()
>>> print(result.derived["wheel_arm.length"])

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.assembly.intent.AssemblyIntent(name: str, description: str = '', functional_requirements: List[FunctionalRequirement] = <factory>, connections: List[Connection] = <factory>, clearances: List[Clearance] = <factory>, reference_geometry: Dict[str, ~yapcad.assembly.intent.ReferenceGeometry]=<factory>, part_definitions: Dict[str, ~yapcad.assembly.datum.PartDefinition]=<factory>, derived_parameters: List[str] = <factory>, explicit_constraints: List[Constraint] = <factory>)[source]

Bases: object

Declarative specification of assembly requirements.

AssemblyIntent is the top-level container for a declarative assembly. Instead of specifying transforms, designers specify: - What parts must DO (functional requirements) - How parts CONNECT (topology) - What must NOT happen (clearances)

The system derives the geometry that satisfies all requirements.

name

Unique identifier for this assembly

Type:

str

description

Human-readable description

Type:

str

functional_requirements

List of FunctionalRequirement objects

Type:

List[yapcad.assembly.intent.FunctionalRequirement]

connections

List of Connection objects

Type:

List[yapcad.assembly.intent.Connection]

clearances

List of Clearance objects

Type:

List[yapcad.assembly.intent.Clearance]

reference_geometry

Dict of ReferenceGeometry objects

Type:

Dict[str, yapcad.assembly.intent.ReferenceGeometry]

part_definitions

Dict of PartDefinition objects (optional)

Type:

Dict[str, yapcad.assembly.datum.PartDefinition]

derived_parameters

List of parameter names to compute

Type:

List[str]

explicit_constraints

Additional explicit constraints

Type:

List[yapcad.assembly.constraint.Constraint]

Example

>>> assembly = AssemblyIntent(
...     name="wheel_pod",
...     functional_requirements=[...],
...     connections=[...],
...     clearances=[...],
... )
>>> result = assembly.solve()
add_clearance(clearance: Clearance) AssemblyIntent[source]

Add a clearance constraint.

add_connection(conn: Connection) AssemblyIntent[source]

Add a connection.

add_reference_geometry(geom: ReferenceGeometry) AssemblyIntent[source]

Add reference geometry.

add_requirement(req: FunctionalRequirement) AssemblyIntent[source]

Add a functional requirement.

clearances: List[Clearance]
collect_constraints() List[Dict[str, Any]][source]

Collect all constraints from requirements.

Converts high-level functional requirements into low-level constraint specifications.

Returns:

List of constraint specification dicts

collect_derived_parameters() List[str][source]

Collect all parameters that need to be derived.

Returns:

List of parameter names

connections: List[Connection]
derived_parameters: List[str]
description: str = ''
explicit_constraints: List[Constraint]
functional_requirements: List[FunctionalRequirement]
name: str
part_definitions: Dict[str, PartDefinition]
reference_geometry: Dict[str, ReferenceGeometry]
report() str[source]

Generate human-readable summary of the assembly intent.

solve() SolveResult[source]

Solve the assembly intent to derive parameters.

This is the main entry point for converting a declarative specification into actual geometry.

Returns:

SolveResult with derived values and validation status

to_assembly() Assembly[source]

Convert solved intent to yapCAD Assembly object.

Returns:

Assembly object with parts, mates, and constraints

validate() List[str][source]

Validate the assembly intent specification.

Checks for: - Missing reference geometry - Invalid part references - Conflicting requirements - Under/over-constrained systems

Returns:

List of validation error/warning messages

class yapcad.assembly.intent.AxisOrientationRequirement(name: str, part: str, description: str = '', priority: int = 1, axis_datum: str = '', orientation: str = 'tangent', reference: str = 'global', pointing: str = 'toward')[source]

Bases: FunctionalRequirement

Part axis must have specific orientation relative to reference.

This requirement specifies that a datum axis on a part must point in a specific direction relative to the assembly or reference geometry.

axis_datum

Name of the axis datum on the part

Type:

str

orientation

Direction specification - “tangent”: Tangent to reference cylinder at part position - “radial”: Pointing toward/away from reference center - “axial”: Parallel to reference axis (e.g., tube Z-axis) - “+x”, “-y”, “+z”: Global direction - tuple: Custom direction vector

Type:

str

reference

Name of reference geometry or “global”

Type:

str

pointing

“toward” or “away” for radial orientation

Type:

str

Example

>>> axis_req = AxisOrientationRequirement(
...     name="motor_tangent",
...     part="motor",
...     axis_datum="motor_axis",
...     orientation="tangent",
...     reference="tube",
... )
axis_datum: str = ''
get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Convert orientation requirement to constraint.

orientation: str = 'tangent'
pointing: str = 'toward'
reference: str = 'global'
class yapcad.assembly.intent.Clearance(part_a: str, part_b: str, min_distance: float, check_type: str = 'bounding_box', critical: bool = True)[source]

Bases: object

Parts must maintain minimum distance (no collision).

Clearance constraints ensure that parts do not interfere with each other or with reference geometry.

part_a

First part name

Type:

str

part_b

Second part name (or reference geometry name)

Type:

str

min_distance

Minimum allowed distance (mm)

Type:

float

check_type

How to measure distance - “bounding_box”: Use axis-aligned bounding boxes (fast) - “convex_hull”: Use convex hull approximation - “mesh”: Use full mesh collision (slow, accurate)

Type:

str

critical

If True, violation is error; else warning

Type:

bool

Example

>>> clearance = Clearance(
...     part_a="motor",
...     part_b="chassis_plate",
...     min_distance=5.0,
... )
check_type: str = 'bounding_box'
critical: bool = True
min_distance: float
part_a: str
part_b: str
validate(transforms: Dict[str, Any], part_bounds: Dict[str, Any]) ClearanceResult[source]

Check if clearance is satisfied.

Parameters:
  • transforms – Part name -> transform matrix

  • part_bounds – Part name -> bounding box/mesh

Returns:

ClearanceResult with validation status

class yapcad.assembly.intent.ClearanceResult(satisfied: bool, actual_distance: float, required_distance: float, message: str = '', interference_point: Tuple[float, float, float] | None = None)[source]

Bases: object

Result of clearance validation.

actual_distance: float
interference_point: Tuple[float, float, float] | None = None
message: str = ''
required_distance: float
satisfied: bool
class yapcad.assembly.intent.Connection(parent: str, child: str, joint_type: str = 'rigid', axis: str | ~typing.Tuple[float, float, float] | None=None, limits: Tuple[float, float] | None=None, interface_type: str | None = None, interface_details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Defines how two parts connect (topology).

Connections specify the parent-child relationships between parts and the type of joint (rigid, revolute, prismatic, etc.).

parent

Parent datum in “part.datum” format

Type:

str

child

Child datum in “part.datum” format

Type:

str

joint_type

Type of connection - “rigid”: No relative motion - “revolute”: Rotation about axis - “prismatic”: Translation along axis - “cylindrical”: Rotation + translation on axis - “spherical”: Ball-and-socket

Type:

str

axis

Axis specification for joints - “tangent”, “radial”, “axial” for reference-relative - tuple (x, y, z) for explicit direction

Type:

str | Tuple[float, float, float] | None

limits

(min, max) for joint limits (degrees or mm)

Type:

Tuple[float, float] | None

interface_type

Physical connection type

Type:

str | None

interface_details

Additional interface parameters

Type:

Dict[str, Any]

Example

>>> conn = Connection(
...     parent="chassis.pivot_boss",
...     child="wheel_arm.pivot_bore",
...     joint_type="revolute",
...     axis="tangent",
...     limits=(-15, 15),
... )
axis: str | Tuple[float, float, float] | None = None
child: str
get_child_datum() str[source]

Extract child datum name.

get_child_part() str[source]

Extract child part name.

get_parent_datum() str[source]

Extract parent datum name.

get_parent_part() str[source]

Extract parent part name.

interface_details: Dict[str, Any]
interface_type: str | None = None
joint_type: str = 'rigid'
limits: Tuple[float, float] | None = None
parent: str
to_mate_spec() Dict[str, Any][source]

Convert to mate specification dict.

class yapcad.assembly.intent.ContactRequirement(name: str, part: str, description: str = '', priority: int = 1, surface: str = '', target: str = '', contact_type: str = 'static', preload_source: str | None = None)[source]

Bases: FunctionalRequirement

Part surface must contact a reference surface.

This requirement specifies that a datum surface on a part must touch a reference geometry surface. Common examples: - Wheel tire contacting tube inner wall - Mounting face flush with bracket surface - Ball bearing in socket

surface

Datum name on part (e.g., “tire_outer_diameter”)

Type:

str

target

Reference geometry name (e.g., “tube_inner_wall”)

Type:

str

contact_type

“static”, “rolling”, or “sliding”

Type:

str

preload_source

Optional name of what provides contact force

Type:

str | None

Example

>>> contact = ContactRequirement(
...     name="wheel_contact",
...     part="ddsm115_motor",
...     surface="tire_outer_diameter",
...     target="tube_inner_wall",
...     contact_type="rolling",
...     preload_source="suspension_spring",
... )
contact_type: str = 'static'
get_derived_parameters() List[str][source]

List parameters that can be derived from this requirement.

Returns:

List of parameter names in “part.parameter” format

get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Contact implies position at specific radius/distance.

preload_source: str | None = None
surface: str = ''
target: str = ''
class yapcad.assembly.intent.FunctionalRequirement(name: str, part: str, description: str = '', priority: int = 1)[source]

Bases: ABC

Base class for functional requirements.

Functional requirements describe WHAT a part must DO, not HOW to achieve it. The system derives constraints and parameters from these requirements.

Subclasses implement get_implied_constraints() to convert the high-level requirement into low-level constraints that the solver can work with.

name

Unique identifier for this requirement

Type:

str

part

Name of the part that must satisfy this requirement

Type:

str

description

Human-readable description of the requirement

Type:

str

priority

Relative importance (higher = more important)

Type:

int

description: str = ''
get_derived_parameters() List[str][source]

List parameters that can be derived from this requirement.

Returns:

List of parameter names in “part.parameter” format

abstractmethod get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Derive low-level constraints from this requirement.

Parameters:

reference_geometry – Dictionary of available reference geometry

Returns:

List of constraint specifications (dicts that can create Constraints)

name: str
part: str
priority: int = 1
class yapcad.assembly.intent.GeometryType(*values)[source]

Bases: Enum

Types of reference geometry.

CONE = 'cone'
CYLINDER = 'cylinder'
LINE = 'line'
PLANE = 'plane'
POINT = 'point'
SPHERE = 'sphere'
class yapcad.assembly.intent.ParallelAxesRequirement(name: str, part: str, description: str = '', priority: int = 1, axis_a: str = '', axis_b: str = '', allow_opposite: bool = True)[source]

Bases: FunctionalRequirement

Two axes must be parallel.

This requirement specifies that two datum axes (on same or different parts) must be parallel. Common use: pivot axis parallel to motor axis for clean suspension motion.

axis_a

First axis in “part.datum” format

Type:

str

axis_b

Second axis in “part.datum” format

Type:

str

allow_opposite

If True, axes can point in opposite directions

Type:

bool

Example

>>> parallel = ParallelAxesRequirement(
...     name="pivot_motor_parallel",
...     part="wheel_arm",  # Primary part
...     axis_a="wheel_arm.pivot_bore_axis",
...     axis_b="motor.motor_axis",
... )
allow_opposite: bool = True
axis_a: str = ''
axis_b: str = ''
get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Generate parallel constraint.

class yapcad.assembly.intent.ReachRequirement(name: str, part: str, description: str = '', priority: int = 1, end_effector: str = '', base: str = '', min_reach: float = 0.0, max_reach: float = 100.0)[source]

Bases: FunctionalRequirement

End effector must have specified reach envelope.

For serial manipulators (SCARA arms, etc.), specifies the minimum and maximum reach from the base.

end_effector

Datum name on end effector part (e.g., “tool_point”)

Type:

str

base

Datum name on base part (e.g., “base_center”)

Type:

str

min_reach

Minimum distance from base (mm)

Type:

float

max_reach

Maximum distance from base (mm)

Type:

float

Example

>>> reach = ReachRequirement(
...     name="scara_workspace",
...     part="wrist",
...     end_effector="wrist.tool_point",
...     base="tower.axis1_center",
...     min_reach=50.0,
...     max_reach=200.0,
... )
base: str = ''
end_effector: str = ''
get_derived_parameters() List[str][source]

List parameters that can be derived from this requirement.

Returns:

List of parameter names in “part.parameter” format

get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Reach implies link length constraints.

max_reach: float = 100.0
min_reach: float = 0.0
class yapcad.assembly.intent.ReferenceGeometry(name: str, geometry_type: str, center: Tuple[float, float, float] | None = None, axis: Tuple[float, float, float] | None = None, radius: float | None = None, normal: Tuple[float, float, float] | None = None, inner: bool = False)[source]

Bases: object

Fixed reference geometry that parts must relate to.

Reference geometry defines the environment or constraints that the assembly operates within. Examples include tube walls, mounting surfaces, and clearance envelopes.

name

Unique identifier for this geometry

Type:

str

geometry_type

Type of geometry (cylinder, plane, etc.)

Type:

str

center

Center point (x, y, z) for most geometry types

Type:

Tuple[float, float, float] | None

axis

Direction vector for cylinders, cones, lines

Type:

Tuple[float, float, float] | None

radius

Radius for cylinders, spheres, cones

Type:

float | None

normal

Normal vector for planes

Type:

Tuple[float, float, float] | None

inner

True if surface is inner (e.g., inner wall of tube)

Type:

bool

Example

>>> # 350mm ID tube
>>> tube = ReferenceGeometry(
...     name="tube_inner_wall",
...     geometry_type="cylinder",
...     center=(0, 0, 0),
...     axis=(0, 0, 1),
...     radius=175.0,
...     inner=True,
... )
axis: Tuple[float, float, float] | None = None
center: Tuple[float, float, float] | None = None
geometry_type: str
get_normal_at(point: Tuple[float, float, float]) Tuple[float, float, float][source]

Get the surface normal at a given point.

For cylinders: radial direction from axis. For planes: constant normal. For spheres: radial direction from center.

Returns:

Unit normal vector (x, y, z)

get_surface_point(theta: float = 0.0, z: float = 0.0) Tuple[float, float, float][source]

Get a point on the surface at given parameters.

For cylinders: theta is angle in radians, z is height along axis. For planes: theta and z are ignored. For spheres: theta is azimuth, z is elevation (radians).

Returns:

Point (x, y, z) on the surface

inner: bool = False
name: str
normal: Tuple[float, float, float] | None = None
radius: float | None = None
class yapcad.assembly.intent.RollRequirement(name: str, part: str, description: str = '', priority: int = 1, roll_direction: str = 'along_tube_axis', axis_datum: str = 'rotation_axis')[source]

Bases: FunctionalRequirement

Part must roll in a specified direction.

This requirement specifies that a rotating part (wheel, roller, etc.) must roll in a specific direction. This IMPLIES that the rotation axis must be perpendicular to the roll direction.

For a wheel rolling along the Z-axis (tube axis): - Roll direction: (0, 0, 1) - Rotation axis must be tangent (perpendicular to both Z and radial)

roll_direction

Direction of travel - “along_tube_axis” or “+z” for vertical tube - “(x, y, z)” tuple for custom direction

Type:

str

axis_datum

Name of rotation axis datum on part

Type:

str

Example

>>> roll = RollRequirement(
...     name="wheel_rolls_z",
...     part="motor",
...     roll_direction="along_tube_axis",
...     axis_datum="motor_axis",
... )
axis_datum: str = 'rotation_axis'
get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Rolling implies rotation axis perpendicular to roll direction.

roll_direction: str = 'along_tube_axis'
class yapcad.assembly.intent.SolveResult(success: bool, derived: Dict[str, float]=<factory>, transforms: Dict[str, ~typing.Any]=<factory>, constraints_generated: Dict[str, ~typing.Any]]=<factory>, validation: Any | None = None, errors: List[str] = <factory>, warnings: List[str] = <factory>)[source]

Bases: object

Result of solving an AssemblyIntent.

Contains derived parameters, computed transforms, and validation status.

success

True if all requirements could be satisfied

Type:

bool

derived

Dictionary of derived parameter values

Type:

Dict[str, float]

transforms

Dictionary of computed transforms (part name -> 4x4 matrix)

Type:

Dict[str, Any]

constraints_generated

List of constraint specifications generated

Type:

List[Dict[str, Any]]

validation

Validation result for all constraints

Type:

Any | None

errors

List of error messages if solve failed

Type:

List[str]

warnings

List of warning messages

Type:

List[str]

constraints_generated: List[Dict[str, Any]]
derived: Dict[str, float]
errors: List[str]
report() str[source]

Generate human-readable solve report.

success: bool
transforms: Dict[str, Any]
validation: Any | None = None
warnings: List[str]
yapcad.assembly.intent.create_scara_arm_intent() AssemblyIntent[source]

Create example SCARA arm assembly intent.

Returns:

AssemblyIntent for a 3-axis SCARA arm

yapcad.assembly.intent.create_wheel_assembly_intent() AssemblyIntent[source]

Create example wheel assembly intent for the tube robot.

This demonstrates how to declare a wheel assembly using functional requirements rather than explicit transforms.

Returns:

AssemblyIntent for a wheel pod assembly

yapcad.assembly.kinematic_integration module

Kinematic chain integration for yapCAD assembly system.

This module provides integration between the assembly constraint/mate system and the kinematic chain transform propagation system. It enables:

  1. Transform propagation from kinematic chains to assembly validation

  2. Constraint evaluation in world coordinates using chain transforms

  3. Forward kinematics: root-to-leaf transform computation

  4. Assembly-wide constraint validation with comprehensive reporting

Key Classes:

KinematicConstraint: Enhanced constraint that works with world transforms ConstraintEvaluator: Evaluates constraints given world transform dict ValidationReport: Comprehensive assembly validation results AssemblyValidator: Validates full assemblies against constraint sets

Example

>>> from yapcad.assembly.kinematic_integration import (
...     AssemblyValidator, KinematicConstraint, ConstraintType
... )
>>>
>>> # Create constraints
>>> tangent = KinematicConstraint(
...     name="wheel_tangent",
...     constraint_type=ConstraintType.TANGENT,
...     frame_a="DDSM115_MOTOR_1",
...     frame_b=None,  # World reference
...     reference_center=(0, 0, 0),
...     reference_radius=124.5,
...     tolerance_deg=2.0
... )
>>>
>>> # Get transforms from kinematic chain
>>> from yapcad.kinematics import KinematicChain
>>> chain = KinematicChain("my_assembly")
>>> transforms = chain.get_all_world_transforms()
>>>
>>> # Validate
>>> validator = AssemblyValidator()
>>> validator.add_constraint(tangent)
>>> report = validator.validate(transforms)
>>> print(report)

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.assembly.kinematic_integration.AssemblyValidator(name: str = 'assembly')[source]

Bases: object

Validates assemblies against a set of kinematic constraints.

The validator maintains a collection of constraints and evaluates them against world transforms from a kinematic chain.

Example

>>> validator = AssemblyValidator()
>>> validator.add_constraint(KinematicConstraint(...))
>>> validator.add_constraint(KinematicConstraint(...))
>>>
>>> # Get transforms from kinematic chain
>>> transforms = chain.get_all_world_transforms()
>>>
>>> # Validate
>>> report = validator.validate(transforms)
>>> if not report.is_valid:
...     print(report.detailed_report())
add_constraint(constraint: KinematicConstraint) None[source]

Add a constraint to the validator.

Parameters:

constraint – KinematicConstraint to add

add_constraints(constraints: List[KinematicConstraint]) None[source]

Add multiple constraints.

Parameters:

constraints – List of constraints to add

clear_constraints() None[source]

Remove all constraints.

validate(world_transforms: Dict[str, Any]) ValidationReport[source]

Validate all constraints against given world transforms.

Parameters:

world_transforms – Dictionary mapping part names to Transform objects or 4x4 numpy arrays

Returns:

ValidationReport with comprehensive evaluation results

validate_and_raise(world_transforms: Dict[str, Any]) None[source]

Validate and raise exception if any error-severity constraint fails.

Parameters:

world_transforms – Dictionary mapping part names to transforms

Raises:

ValueError – If any error-severity constraint fails

class yapcad.assembly.kinematic_integration.ConstraintEvaluationResult(satisfied: bool, error: float = 0.0, error_vector: Tuple[float, float, float] | None=None, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of evaluating a single constraint.

satisfied

True if constraint is satisfied within tolerance

Type:

bool

error

Numeric error value (distance in mm or angle in degrees)

Type:

float

error_vector

Optional direction vector of error for visualization

Type:

Tuple[float, float, float] | None

error_message

Human-readable description

Type:

str

details

Additional diagnostic information

Type:

Dict[str, Any]

details: Dict[str, Any]
error: float = 0.0
error_message: str = ''
error_vector: Tuple[float, float, float] | None = None
satisfied: bool
class yapcad.assembly.kinematic_integration.KinematicConstraint(name: str, constraint_type: KinematicConstraintType, frame_a: str, frame_b: str | None = None, face_a: str | None = None, face_b: str | None = None, axis_a: str | Tuple[float, float, float] = 'z', axis_b: str | Tuple[float, float, float] = 'z', reference_center: Tuple[float, float, float] | None = None, reference_radius: float | None = None, reference_axis: Tuple[float, float, float] | None = None, reference_normal: Tuple[float, float, float] | None = None, pattern_count: int = 0, pattern_radius: float = 0.0, pattern_offset_deg: float = 0.0, tolerance_mm: float = 0.1, tolerance_deg: float = 1.0, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, stl_path_a: str | None = None, stl_path_b: str | None = None, description: str = '', severity: str = 'error', validator: Callable[[Dict[str, Any]], ConstraintEvaluationResult] | None = None)[source]

Bases: object

A constraint that operates on world transforms from kinematic chains.

This constraint type is designed to work with Transform objects (4x4 matrices) from the kinematic chain system, enabling validation of assembly relationships in world coordinates.

name

Unique identifier for this constraint

Type:

str

constraint_type

Type of constraint to evaluate

Type:

yapcad.assembly.kinematic_integration.KinematicConstraintType

frame_a

Name of first frame (part name in kinematic chain)

Type:

str

frame_b

Name of second frame, or None for world reference

Type:

str | None

# Face-based constraint specification
Type:

optional

face_a

Face specification on frame_a. Can be: - “TOP” / “BOTTOM”: Calculated from bounds_a (max_z / min_z face center) - “FRONT” / “BACK”: Calculated from bounds_a (max_y / min_y face center) - “LEFT” / “RIGHT”: Calculated from bounds_a (min_x / max_x face center) - Frame name (e.g., “OUTPUT_SHAFT”, “SUN_INPUT”): Looked up from

world_transforms using key “part_name.frame_name”

When specified, constraint position is taken from face instead of part origin.

Type:

str | None

face_b

Face specification on frame_b (same options as face_a).

Type:

str | None

# Axis specification
Type:

which local axis to use for directional constraints

axis_a

Local axis on frame_a to use (“x”, “y”, “z”, or tuple)

Type:

str | Tuple[float, float, float]

axis_b

Local axis on frame_b to use (“x”, “y”, “z”, or tuple)

Type:

str | Tuple[float, float, float]

# Reference geometry for world-referenced constraints
reference_center

Center point for tangent/radial constraints

Type:

Tuple[float, float, float] | None

reference_radius

Radius for tangent/radial constraints

Type:

float | None

reference_axis

Reference axis direction for parallel/perpendicular

Type:

Tuple[float, float, float] | None

reference_normal

Reference normal for plane constraints

Type:

Tuple[float, float, float] | None

# Pattern parameters
pattern_count

Number of holes in bolt pattern

Type:

int

pattern_radius

Bolt circle radius

Type:

float

pattern_offset_deg

Angular offset between patterns

Type:

float

# Tolerances
tolerance_mm

Linear tolerance in mm (default: 0.1)

Type:

float

tolerance_deg

Angular tolerance in degrees (default: 1.0)

Type:

float

# Metadata
description

Human-readable description

Type:

str

severity

“error”, “warning”, or “info”

Type:

str

# Custom validator
validator

Optional custom validation function

Type:

Callable[[Dict[str, Any]], yapcad.assembly.kinematic_integration.ConstraintEvaluationResult] | None

Example with face-based constraints:

# Clearance measured from servo OUTPUT_SHAFT face to ring SUN_INPUT face
constraint = KinematicConstraint(
    name="servo_ring_interface",
    constraint_type=KinematicConstraintType.AT_DISTANCE,
    frame_a="AXIS2_SERVO_XH430",
    frame_b="AXIS2_RING_HOUSING",
    face_a="OUTPUT_SHAFT",    # Use servo's output shaft frame position
    face_b="SUN_INPUT",       # Use ring's sun input frame position
    reference_radius=0.0,     # Expected distance (touching)
    tolerance_mm=0.5
)

# Contact constraint using standard face names
constraint = KinematicConstraint(
    name="stacked_parts",
    constraint_type=KinematicConstraintType.COINCIDENT,
    frame_a="GEARBOX_TOP",
    frame_b="GEARBOX_BOTTOM",
    face_a="BOTTOM",          # Bottom face of top gearbox (from bounds_a)
    face_b="TOP",             # Top face of bottom gearbox (from bounds_b)
    bounds_a=((-30, -30, 0), (30, 30, 50)),
    bounds_b=((-30, -30, 0), (30, 30, 50)),
    tolerance_mm=0.1
)
axis_a: str | Tuple[float, float, float] = 'z'
axis_b: str | Tuple[float, float, float] = 'z'
bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None
bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None
constraint_type: KinematicConstraintType
description: str = ''
evaluate(world_transforms: Dict[str, Any]) ConstraintEvaluationResult[source]

Evaluate this constraint given world transforms of all parts.

Parameters:

world_transforms – Dictionary mapping part names to Transform objects or 4x4 numpy arrays

Returns:

ConstraintEvaluationResult with evaluation status and error metrics

face_a: str | None = None
face_b: str | None = None
frame_a: str
frame_b: str | None = None
name: str
pattern_count: int = 0
pattern_offset_deg: float = 0.0
pattern_radius: float = 0.0
reference_axis: Tuple[float, float, float] | None = None
reference_center: Tuple[float, float, float] | None = None
reference_normal: Tuple[float, float, float] | None = None
reference_radius: float | None = None
severity: str = 'error'
stl_path_a: str | None = None
stl_path_b: str | None = None
tolerance_deg: float = 1.0
tolerance_mm: float = 0.1
validator: Callable[[Dict[str, Any]], ConstraintEvaluationResult] | None = None
class yapcad.assembly.kinematic_integration.KinematicConstraintType(*values)[source]

Bases: Enum

Constraint types for kinematic chain validation.

These constraints operate on world-space transforms from kinematic chains and validate geometric relationships between frames.

AT_DISTANCE = 'at_distance'
BOLT_PATTERN = 'bolt_pattern'
COINCIDENT = 'coincident'
CONCENTRIC = 'concentric'
CUSTOM = 'custom'
MIN_DISTANCE = 'min_distance'
NO_OVERLAP = 'no_overlap'
PARALLEL = 'parallel'
PERPENDICULAR = 'perpendicular'
TANGENT = 'tangent'
Z_STACK_CLEARANCE = 'z_stack_clearance'
class yapcad.assembly.kinematic_integration.MeshCollisionResult(collides: bool, penetration_depth: float = 0.0, collision_volume: float = 0.0, contact_points: Tuple[float, float, float]]=<factory>, error_message: str = '')[source]

Bases: object

Result of mesh-based collision detection between two parts.

collides

True if the meshes intersect

Type:

bool

penetration_depth

Estimated penetration depth in mm (0 if no collision)

Type:

float

collision_volume

Volume of the intersection region in mm^3 (0 if no collision)

Type:

float

contact_points

List of approximate contact/intersection points

Type:

List[Tuple[float, float, float]]

error_message

Error message if collision check failed

Type:

str

collides: bool
collision_volume: float = 0.0
contact_points: List[Tuple[float, float, float]]
error_message: str = ''
penetration_depth: float = 0.0
class yapcad.assembly.kinematic_integration.ValidationReport(is_valid: bool, total_constraints: int = 0, passed_count: int = 0, failed_count: int = 0, warning_count: int = 0, constraint_results: Dict[str, ~yapcad.assembly.kinematic_integration.ConstraintEvaluationResult]=<factory>, failed_constraints: List[str] = <factory>, warnings: List[str] = <factory>, info: List[str] = <factory>)[source]

Bases: object

Comprehensive assembly validation report.

is_valid

True if all error-severity constraints pass

Type:

bool

total_constraints

Total number of constraints evaluated

Type:

int

passed_count

Number of constraints that passed

Type:

int

failed_count

Number of constraints that failed

Type:

int

warning_count

Number of warning-severity constraints that failed

Type:

int

constraint_results

Mapping of constraint name to evaluation result

Type:

Dict[str, yapcad.assembly.kinematic_integration.ConstraintEvaluationResult]

failed_constraints

List of names of failed constraints

Type:

List[str]

warnings

List of warning messages

Type:

List[str]

info

List of informational messages

Type:

List[str]

constraint_results: Dict[str, ConstraintEvaluationResult]
detailed_report() str[source]

Generate detailed multi-line report.

failed_constraints: List[str]
failed_count: int = 0
info: List[str]
is_valid: bool
passed_count: int = 0
total_constraints: int = 0
warning_count: int = 0
warnings: List[str]
yapcad.assembly.kinematic_integration.check_mesh_collision(stl_path_a: str, transform_a: Any, stl_path_b: str, transform_b: Any) MeshCollisionResult[source]

Check for collision between two STL meshes with applied transforms.

This function loads two STL files, applies 4x4 transformation matrices, and checks if the resulting meshes intersect. It provides detailed collision information including penetration depth and intersection volume.

Parameters:
  • stl_path_a – Path to the first STL file

  • transform_a – 4x4 transformation matrix (numpy array or Transform object)

  • stl_path_b – Path to the second STL file

  • transform_b – 4x4 transformation matrix (numpy array or Transform object)

Returns:

MeshCollisionResult with collision details

Example

>>> from yapcad.assembly.kinematic_integration import check_mesh_collision
>>> import numpy as np
>>>
>>> # Identity transform (no transformation)
>>> identity = np.eye(4)
>>>
>>> # Translation by 100mm in X
>>> translated = np.eye(4)
>>> translated[0, 3] = 100.0
>>>
>>> result = check_mesh_collision(
...     "part_a.stl", identity,
...     "part_b.stl", translated
... )
>>> if result.collides:
...     print(f"Collision! Penetration: {result.penetration_depth}mm")
yapcad.assembly.kinematic_integration.create_coincident_constraint(name: str, frame_a: str, frame_b: str | None = None, reference_point: Tuple[float, float, float] | None = None, tolerance_mm: float = 0.1, description: str = '') KinematicConstraint[source]

Create a coincident constraint (origins at same location).

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name (or None for world reference)

  • reference_point – World reference point (if frame_b is None)

  • tolerance_mm – Distance tolerance in mm

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.kinematic_integration.create_face_coincident_constraint(name: str, frame_a: str, face_a: str, frame_b: str, face_b: str, tolerance_mm: float = 0.1, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, description: str = '') KinematicConstraint[source]

Create a face-to-face coincident constraint.

Validates that two faces are at the same position (touching/mating). This is a convenience wrapper around create_face_distance_constraint with expected_distance=0.

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • face_a – Face specification on frame_a

  • frame_b – Second part name

  • face_b – Face specification on frame_b

  • tolerance_mm – Allowed position deviation

  • bounds_a – Bounding box of part A (required for standard face names)

  • bounds_b – Bounding box of part B (required for standard face names)

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

Example

>>> # Servo output shaft should coincide with ring gear input
>>> constraint = create_face_coincident_constraint(
...     name="servo_ring_mate",
...     frame_a="AXIS2_SERVO",
...     face_a="OUTPUT_SHAFT",
...     frame_b="AXIS2_RING",
...     face_b="SUN_INPUT"
... )
yapcad.assembly.kinematic_integration.create_face_distance_constraint(name: str, frame_a: str, face_a: str, frame_b: str, face_b: str, expected_distance: float = 0.0, tolerance_mm: float = 0.5, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, description: str = '') KinematicConstraint[source]

Create a face-to-face distance constraint.

Measures distance between specific faces on two parts, rather than part origins. Useful for validating mating interfaces like servo output shafts to gearbox inputs.

Face specifications can be: - Standard faces: “TOP”, “BOTTOM”, “FRONT”, “BACK”, “LEFT”, “RIGHT”

(requires bounds_a/bounds_b to calculate face center positions)

  • Named frames: e.g., “OUTPUT_SHAFT”, “SUN_INPUT” (looked up from world_transforms using “part_name.frame_name”)

Parameters:
  • name – Constraint name

  • frame_a – First part name (e.g., “AXIS2_SERVO_XH430”)

  • face_a – Face specification on frame_a (e.g., “OUTPUT_SHAFT” or “TOP”)

  • frame_b – Second part name (e.g., “AXIS2_RING_HOUSING”)

  • face_b – Face specification on frame_b (e.g., “SUN_INPUT” or “BOTTOM”)

  • expected_distance – Expected distance between faces in mm (0.0 = touching)

  • tolerance_mm – Allowed deviation from expected distance

  • bounds_a – Bounding box of part A (required for standard face names)

  • bounds_b – Bounding box of part B (required for standard face names)

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

Example

>>> # Servo output shaft should mate with ring gear input
>>> constraint = create_face_distance_constraint(
...     name="servo_ring_interface",
...     frame_a="AXIS2_SERVO_XH430",
...     face_a="OUTPUT_SHAFT",
...     frame_b="AXIS2_RING_HOUSING",
...     face_b="SUN_INPUT",
...     expected_distance=0.0,  # Should be touching
...     tolerance_mm=0.5
... )
>>>
>>> # Top face of lower part should meet bottom face of upper part
>>> constraint = create_face_distance_constraint(
...     name="stacked_contact",
...     frame_a="LOWER_PART",
...     face_a="TOP",
...     frame_b="UPPER_PART",
...     face_b="BOTTOM",
...     expected_distance=0.0,
...     bounds_a=((-25, -25, 0), (25, 25, 30)),
...     bounds_b=((-25, -25, 0), (25, 25, 40))
... )
yapcad.assembly.kinematic_integration.create_mesh_collision_constraint(name: str, frame_a: str, frame_b: str, stl_path_a: str, stl_path_b: str, description: str = '') KinematicConstraint[source]

Create a mesh-based collision constraint.

Uses trimesh library for accurate collision detection with actual part geometry. This is more accurate than AABB-based detection but requires the trimesh library and STL files for both parts.

Parameters:
  • name – Constraint name

  • frame_a – First part name (must match kinematic chain frame name)

  • frame_b – Second part name (must match kinematic chain frame name)

  • stl_path_a – Path to STL file for part A

  • stl_path_b – Path to STL file for part B

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint for mesh collision detection

Example

>>> from yapcad.assembly.kinematic_integration import (
...     create_mesh_collision_constraint, AssemblyValidator
... )
>>>
>>> constraint = create_mesh_collision_constraint(
...     "gearbox_servo_collision",
...     "AXIS1_RING_HOUSING",
...     "AXIS1_SERVO_XH430",
...     "/path/to/ring_housing.stl",
...     "/path/to/servo.stl",
...     description="Check servo doesn't penetrate ring housing"
... )
>>>
>>> validator = AssemblyValidator()
>>> validator.add_constraint(constraint)
>>> report = validator.validate(world_transforms)
>>> if not report.is_valid:
...     print(report.detailed_report())
yapcad.assembly.kinematic_integration.create_min_distance_constraint(name: str, frame_a: str, frame_b: str, min_distance: float, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, description: str = '') KinematicConstraint[source]

Create a minimum distance constraint.

Parts must be at least min_distance mm apart. If bounding boxes are provided, uses them for more accurate separation calculation.

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name

  • min_distance – Minimum required separation in mm

  • bounds_a – Optional bounding box of part A

  • bounds_b – Optional bounding box of part B

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.kinematic_integration.create_no_overlap_constraint(name: str, frame_a: str, frame_b: str, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, stl_path_a: str | None = None, stl_path_b: str | None = None, description: str = '') KinematicConstraint[source]

Create a no-overlap constraint.

When STL paths are provided, uses accurate mesh-based collision detection. Otherwise uses axis-aligned bounding boxes (fast but conservative).

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name

  • bounds_a – Bounding box of part A ((min_x, min_y, min_z), (max_x, max_y, max_z))

  • bounds_b – Bounding box of part B

  • stl_path_a – Path to STL file for part A (enables mesh collision)

  • stl_path_b – Path to STL file for part B (enables mesh collision)

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

Example

>>> # AABB-based collision (fast, conservative)
>>> constraint = create_no_overlap_constraint(
...     "servo_ring_no_overlap",
...     "SERVO",
...     "RING_HOUSING",
...     bounds_a=((-15, -25, -17), (15, 25, 17)),
...     bounds_b=((-30, -30, 0), (30, 30, 8))
... )
>>>
>>> # Mesh-based collision (accurate, requires trimesh)
>>> constraint = create_no_overlap_constraint(
...     "servo_ring_no_overlap",
...     "SERVO",
...     "RING_HOUSING",
...     stl_path_a="parts/servo.stl",
...     stl_path_b="parts/ring_housing.stl"
... )
yapcad.assembly.kinematic_integration.create_parallel_constraint(name: str, frame_a: str, frame_b: str | None = None, axis_a: str = 'z', axis_b: str = 'z', reference_axis: Tuple[float, float, float] | None = None, tolerance_deg: float = 1.0, description: str = '') KinematicConstraint[source]

Create a parallel constraint between two axes.

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name (or None for world reference)

  • axis_a – Local axis on frame_a

  • axis_b – Local axis on frame_b

  • reference_axis – World reference axis (if frame_b is None)

  • tolerance_deg – Angular tolerance

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.kinematic_integration.create_tangent_constraint(name: str, frame: str, center: Tuple[float, float, float], axis: str = 'y', tolerance_deg: float = 2.0, description: str = '') KinematicConstraint[source]

Create a tangent constraint for wheel/motor assemblies.

Parameters:
  • name – Constraint name

  • frame – Part name in kinematic chain

  • center – Center point for tangent calculation

  • axis – Which local axis to check (“x”, “y”, or “z”)

  • tolerance_deg – Angular tolerance

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.kinematic_integration.create_z_stack_clearance_constraint(name: str, frame_a: str, frame_b: str, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]], bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]], min_clearance: float = 0.0, description: str = '') KinematicConstraint[source]

Create a Z-stack clearance constraint for stacked parts.

Validates that vertically stacked parts (like servo + gearbox) have proper Z-axis separation to avoid collision. Useful for detecting servo bodies penetrating ring housings.

Parameters:
  • name – Constraint name

  • frame_a – First part name (e.g., “AXIS2_SERVO_XH430”)

  • frame_b – Second part name (e.g., “AXIS2_RING_HOUSING”)

  • bounds_a – Bounding box of part A ((min_x, min_y, min_z), (max_x, max_y, max_z))

  • bounds_b – Bounding box of part B

  • min_clearance – Minimum required clearance in mm (default: 0 = touching OK)

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

Example

>>> # XH430 servo: 28.5 x 46.5 x 34mm, origin at body center
>>> servo_bounds = ((-14.25, -23.25, -17), (14.25, 23.25, 17))
>>> # Ring housing: ~60mm diameter x 8mm height
>>> ring_bounds = ((-30, -30, 0), (30, 30, 8))
>>> constraint = create_z_stack_clearance_constraint(
...     "axis2_servo_ring_clearance",
...     "AXIS2_SERVO_XH430",
...     "AXIS2_RING_HOUSING",
...     servo_bounds,
...     ring_bounds,
...     min_clearance=1.0
... )
yapcad.assembly.kinematic_integration.validate_assembly(constraints: List[KinematicConstraint], world_transforms: Dict[str, Any]) ValidationReport[source]

Convenience function to validate constraints against transforms.

Parameters:
  • constraints – List of KinematicConstraint objects

  • world_transforms – Dictionary mapping part names to transforms

Returns:

ValidationReport with evaluation results

yapcad.assembly.mate module

Assembly mate/constraint system for yapCAD.

This module provides kinematic constraints for defining relationships between components in an assembly. Mates define both static positioning and allowed motion, supporting mechanical design, animation export, and physics simulation.

Based on industry standards from SolidWorks, Fusion 360, CATIA, Siemens NX, and PTC Creo, adapted for yapCAD’s programmatic workflow.

Key concepts:
  • Mate: Geometric relationship that constrains degrees of freedom (DOF)

  • DOF Removal: Each mate removes one or more of 6 DOF (3 trans + 3 rot)

  • Limits: Min/max position or angle constraints for joints

  • Dynamics: Friction, damping, stiffness for motion simulation

  • Coupling: Gear ratios and other motion relationships

Example usage:

from yapcad.assembly.mate import Mate, MateType, MateLimits from yapcad.geom import point, vect

# Define a revolute joint (hinge) for a robot arm shoulder = Mate(

name=”shoulder_pitch”, mate_type=MateType.REVOLUTE, part_a=”base”, datum_a=”shoulder_axis”, part_b=”upper_arm”, datum_b=”arm_root_axis”, offset=0.0, angle=0.0, limits=MateLimits(

min_value=-1.57, # -90 degrees max_value=1.57, # +90 degrees max_velocity=2.0 # rad/s

)

)

# Check degrees of freedom dof = shoulder.degrees_of_freedom() # Returns 1 (rotation only)

# Evaluate constraint satisfaction result = shoulder.evaluate() print(f”DOF remaining: {result[‘dof_remaining’]}”) print(f”Constraint error: {result[‘error’]}”)

For animation and simulation export, see the CAD_MATE_SYSTEMS_RESEARCH.md document for URDF, SDF, and Blender armature generation strategies.

class yapcad.assembly.mate.CoincidentResult(satisfied: bool, error_distance: float = 0.0, error_angle: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of evaluating a COINCIDENT constraint.

satisfied

True if constraint is satisfied within tolerance

Type:

bool

error_distance

Distance error in mm (for point/origin alignment)

Type:

float

error_angle

Angular error in degrees (for plane/axis alignment)

Type:

float

error_message

Human-readable description of the result

Type:

str

details

Additional information about the evaluation

Type:

Dict[str, Any]

details: Dict[str, Any]
error_angle: float = 0.0
error_distance: float = 0.0
error_message: str = ''
satisfied: bool
class yapcad.assembly.mate.Mate(name: str, mate_type: MateType, part_a: str, datum_a: str, part_b: str, datum_b: str, offset: float = 0.0, angle: float = 0.0, axis: List[float] = <factory>, secondary_axis: List[float] = <factory>, limits: MateLimits | None = None, dynamics: MateDynamics | None = None, coupling_ratio: float = 1.0, coupling_offset: float = 0.0, coupling_reverse: bool = False, coupling_pitch: float | None = None, metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Kinematic constraint defining relationship between two assembly components.

A mate constrains the relative position and/or orientation of two parts by removing degrees of freedom (DOF). Mates can be purely geometric (COINCIDENT, PARALLEL) or define motion primitives (REVOLUTE, PRISMATIC).

The mate references two parts via named datum features (points, axes, planes, or surfaces). The constraint solver uses these datums to compute the relative transformation that satisfies the mate.

name

Human-readable identifier (e.g., “shoulder_pitch”, “wheel_axle”)

Type:

str

mate_type

Type of constraint from MateType enum

Type:

yapcad.assembly.mate.MateType

part_a

Identifier of first (parent/base) component

Type:

str

datum_a

Named datum feature on part_a (e.g., “mounting_axis”)

Type:

str

part_b

Identifier of second (child/moving) component

Type:

str

datum_b

Named datum feature on part_b (e.g., “joint_axis”)

Type:

str

offset

Distance offset between datums (mm, used by DISTANCE mate)

Type:

float

angle

Angular offset between datums (radians, used by ANGLE mate)

Type:

float

axis

Primary motion axis for joints (e.g., [0,0,1,0] for Z-axis)

Type:

List[float]

secondary_axis

Secondary reference axis for compound joints

Type:

List[float]

limits

Optional position/velocity/effort limits

Type:

yapcad.assembly.mate.MateLimits | None

dynamics

Optional friction/damping/stiffness parameters

Type:

yapcad.assembly.mate.MateDynamics | None

coupling_ratio

Motion ratio for coupled mates (GEAR, SCREW)

Type:

float

coupling_offset

Phase offset for coupled motion (radians or mm)

Type:

float

coupling_reverse

Reverse direction of coupled motion

Type:

bool

coupling_pitch

Thread pitch for SCREW mates (mm per revolution)

Type:

float | None

metadata

Additional application-specific data

Type:

Dict[str, Any]

Example

# Revolute joint for robot shoulder shoulder_mate = Mate(

name=”shoulder_pitch”, mate_type=MateType.REVOLUTE, part_a=”robot_base”, datum_a=”shoulder_mount_axis”, part_b=”upper_arm”, datum_b=”arm_root_axis”, offset=0.0, angle=0.0, axis=[0, 1, 0, 0], # Y-axis rotation limits=MateLimits(

min_value=-math.pi/2, max_value=math.pi/2, max_velocity=1.5, max_effort=100.0

), dynamics=MateDynamics(

friction_static=0.05, damping=0.2

)

)

# Gear coupling between two shafts gear_mate = Mate(

name=”gear_1_to_2”, mate_type=MateType.GEAR, part_a=”gear_1”, datum_a=”gear_1_axis”, part_b=”gear_2”, datum_b=”gear_2_axis”, coupling_ratio=2.5, # gear_2 rotates 2.5x for each rotation of gear_1 coupling_reverse=True # Opposite rotation direction

)

# Check constraint properties dof = shoulder_mate.degrees_of_freedom() # Returns 1 result = shoulder_mate.evaluate()

angle: float = 0.0
axis: List[float]
compute_coupled_motion(driver_position: float) float[source]

Compute driven position from driver position for coupled mates.

For coupled mates (GEAR, SCREW, RACK_PINION), compute the position of the driven component given the position of the driving component.

Parameters:

driver_position – Position of driving component (mm or radians)

Returns:

Position of driven component (mm or radians)

Raises:

ValueError – If mate is not a coupled type

Example

# Gear mate with 3:1 ratio gear_mate = Mate(mate_type=MateType.GEAR, coupling_ratio=3.0, …) driven_angle = gear_mate.compute_coupled_motion(1.0) # Returns 3.0

# Screw mate with 2mm pitch screw_mate = Mate(mate_type=MateType.SCREW, coupling_pitch=2.0, …) linear_pos = screw_mate.compute_coupled_motion(math.pi) # Returns 2.0

coupling_offset: float = 0.0
coupling_pitch: float | None = None
coupling_ratio: float = 1.0
coupling_reverse: bool = False
property datum1: str

Alias for datum_a (datum on first component).

property datum2: str

Alias for datum_b (datum on second component).

datum_a: str
datum_b: str
degrees_of_freedom() int[source]

Return the number of degrees of freedom (DOF) remaining after this mate.

An unconstrained rigid body has 6 DOF (3 translational + 3 rotational). Each mate removes one or more DOF. This method returns how many DOF remain after applying this mate constraint.

Returns:

Number of remaining DOF (0-6)

DOF by mate type:

RIGID: 0 DOF (fully constrained) REVOLUTE, PRISMATIC, SCREW: 1 DOF CYLINDRICAL, PIN_SLOT, UNIVERSAL: 2 DOF SPHERICAL, PLANAR: 3 DOF COINCIDENT: depends on geometry (1-3 DOF removed) CONCENTRIC: 2 DOF (translation along + rotation about axis) DISTANCE, ANGLE, PARALLEL, PERPENDICULAR, TANGENT: varies GEAR, RACK_PINION, CAM: coupled (creates relationship, not DOF)

Example

shoulder = Mate(name=”shoulder”, mate_type=MateType.REVOLUTE, …) dof = shoulder.degrees_of_freedom() # Returns 1

dynamics: MateDynamics | None = None
evaluate(current_position: float | None = None, current_velocity: float | None = None, current_effort: float | None = None) Dict[str, Any][source]

Evaluate constraint satisfaction and compute error metrics.

This method checks if the mate is satisfied given current state, computes constraint violation errors, and validates limits.

Parameters:
  • current_position – Current position (mm) or angle (radians) of primary DOF

  • current_velocity – Current velocity (mm/s or rad/s)

  • current_effort – Current force (N) or torque (N*m)

Returns:

  • dof_remaining: Number of DOF after constraint (int)

  • error: Constraint violation error magnitude (float)

  • satisfied: True if constraint is satisfied within tolerance (bool)

  • limit_violations: List of violated limits (List[str])

  • dynamics_active: True if dynamics parameters are defined (bool)

Return type:

Dictionary containing

Example

result = mate.evaluate(current_position=0.5, current_velocity=1.2) if not result[‘satisfied’]:

print(f”Constraint error: {result[‘error’]}”)

if result[‘limit_violations’]:

print(f”Limit violations: {result[‘limit_violations’]}”)

classmethod from_dict(data: Dict[str, Any]) Mate[source]

Deserialize mate from dictionary.

Parameters:

data – Dictionary representation of mate

Returns:

Mate instance

Example

import json mate_dict = json.load(f) mate = Mate.from_dict(mate_dict)

is_coupled() bool[source]

Return True if this mate defines a coupled motion relationship.

Coupled mates (GEAR, RACK_PINION, SCREW, CAM) create motion relationships between components rather than removing DOF.

Returns:

True if mate is a coupled motion type

Example

if mate.is_coupled():

print(f”Coupling ratio: {mate.coupling_ratio}”)

limits: MateLimits | None = None
mate_type: MateType
metadata: Dict[str, Any]
name: str
offset: float = 0.0
property part1: str

Alias for part_a (first/parent component).

property part2: str

Alias for part_b (second/child component).

part_a: str
part_b: str
secondary_axis: List[float]
to_dict() Dict[str, Any][source]

Serialize mate to dictionary for export to JSON, YAML, or other formats.

Returns:

Dictionary representation of mate with all parameters

Example

mate_dict = mate.to_dict() import json json.dump(mate_dict, f, indent=2)

validate(datum_a: Datum, datum_b: Datum) List[str][source]

Validate that this mate is compatible with the given datums.

Checks that the mate type is appropriate for the datum types. For example, CONCENTRIC requires two AXIS or CIRCLE datums, COINCIDENT can work with POINT, PLANE, or CIRCLE datums.

Parameters:
  • datum_a – First datum feature

  • datum_b – Second datum feature

Returns:

List of validation error messages (empty if valid)

Example

>>> mate = Mate("test", MateType.CONCENTRIC, ...)
>>> issues = mate.validate(axis_datum, point_datum)
>>> if issues:
...     print(f"Invalid: {issues}")
class yapcad.assembly.mate.MateDynamics(friction_static: float = 0.0, friction_kinetic: float = 0.0, friction_viscous: float = 0.0, damping: float = 0.0, stiffness: float = 0.0, rest_position: float = 0.0)[source]

Bases: object

Physical dynamics parameters for motion simulation.

Defines friction, damping, and compliance for realistic motion behavior. Used for physics simulation, animation, and digital twin applications.

Friction types:
  • Static (Coulomb): Resistance to start motion

  • Kinetic (dynamic): Resistance during motion

  • Viscous: Velocity-dependent resistance (linear with speed)

Typical values:
  • Lubricated metal bearings: friction_static=0.01-0.05

  • Dry metal: friction_static=0.15-0.30

  • Typical joint damping: 0.1-1.0

  • Stiffness: 0 for rigid, >0 for compliant/springy

friction_static

Static (Coulomb) friction coefficient

Type:

float

friction_kinetic

Kinetic/dynamic friction coefficient

Type:

float

friction_viscous

Viscous friction coefficient (velocity-dependent)

Type:

float

damping

Viscous damping coefficient (energy dissipation)

Type:

float

stiffness

Spring stiffness for compliant joints (N/m or N*m/rad)

Type:

float

rest_position

Equilibrium position for spring (mm or radians)

Type:

float

Example

# Low-friction bearing with slight damping dynamics = MateDynamics(

friction_static=0.02, friction_kinetic=0.015, damping=0.5

)

# Compliant joint with spring return dynamics = MateDynamics(

stiffness=1000.0, # Spring constant rest_position=0.0, # Returns to center damping=50.0 # Energy dissipation

)

damping: float = 0.0
friction_kinetic: float = 0.0
friction_static: float = 0.0
friction_viscous: float = 0.0
rest_position: float = 0.0
stiffness: float = 0.0
class yapcad.assembly.mate.MateLimits(min_value: float | None = None, max_value: float | None = None, min_velocity: float | None = None, max_velocity: float | None = None, min_effort: float | None = None, max_effort: float | None = None, min_secondary: float | None = None, max_secondary: float | None = None, limit_stiffness: float = 1000000.0, limit_damping: float = 1000.0, restitution: float = 0.0)[source]

Bases: object

Position, velocity, and effort limits for mate degrees of freedom.

Used for both mechanical design (hard stops) and motion simulation (soft limits, compliance). All limits are optional; None means unlimited.

For revolute joints:
  • min_value/max_value in radians

  • max_velocity in rad/s

  • max_effort in N*m (torque)

For prismatic joints:
  • min_value/max_value in mm

  • max_velocity in mm/s

  • max_effort in N (force)

For multi-DOF joints (cylindrical, spherical, planar):
  • Use min_value/max_value for primary DOF

  • Use min_secondary/max_secondary for secondary DOF

min_value

Minimum position (mm) or angle (radians) for primary DOF

Type:

float | None

max_value

Maximum position (mm) or angle (radians) for primary DOF

Type:

float | None

min_velocity

Minimum velocity (can be negative for reversal)

Type:

float | None

max_velocity

Maximum velocity (mm/s or rad/s)

Type:

float | None

min_effort

Minimum force/torque (N or N*m)

Type:

float | None

max_effort

Maximum force/torque (N or N*m)

Type:

float | None

min_secondary

Minimum value for secondary DOF (multi-DOF joints)

Type:

float | None

max_secondary

Maximum value for secondary DOF (multi-DOF joints)

Type:

float | None

limit_stiffness

Stiffness of soft limit (N/m or N*m/rad), 1e6 = hard

Type:

float

limit_damping

Damping at limit stop (N*s/m or N*m*s/rad)

Type:

float

restitution

Bounce coefficient at limits (0.0 = no bounce, 1.0 = perfect)

Type:

float

Example

# Revolute joint with ±90 degree limits limits = MateLimits(

min_value=-math.pi/2, max_value=math.pi/2, max_velocity=2.0, # 2 rad/s max_effort=50.0 # 50 N*m torque limit

)

# Prismatic joint (linear slide) with hard stops limits = MateLimits(

min_value=0.0, max_value=100.0, # 100mm travel max_velocity=50.0, # 50 mm/s limit_stiffness=1e6 # Very stiff stop

)

limit_damping: float = 1000.0
limit_stiffness: float = 1000000.0
max_effort: float | None = None
max_secondary: float | None = None
max_value: float | None = None
max_velocity: float | None = None
min_effort: float | None = None
min_secondary: float | None = None
min_value: float | None = None
min_velocity: float | None = None
restitution: float = 0.0
class yapcad.assembly.mate.MateType(*values)[source]

Bases: Enum

Kinematic mate types based on industry CAD standards.

Each mate type removes specific degrees of freedom (DOF) from a rigid body. An unconstrained body has 6 DOF: 3 translational + 3 rotational.

Basic Mates (geometric constraints):

COINCIDENT: Points, edges, or faces share same location (1-3 DOF removed) CONCENTRIC: Cylindrical axes are colinear (4 DOF removed: 2t + 2r) PARALLEL: Directions remain parallel (2 DOF removed) PERPENDICULAR: Directions maintain 90-degree angle (1 DOF removed) TANGENT: Surfaces remain tangent (1 DOF removed) DISTANCE: Fixed offset between datums (1 DOF removed) ANGLE: Fixed angular relationship (1 DOF removed)

Standard Joints (motion primitives):

RIGID: No relative motion (6 DOF removed, 0 remaining) REVOLUTE: Rotation about single axis (5 DOF removed, 1 remaining) PRISMATIC: Translation along single axis (5 DOF removed, 1 remaining) CYLINDRICAL: Rotation + translation on same axis (4 DOF removed, 2 remaining) SPHERICAL: Ball-and-socket, rotation only (3 DOF removed, 3 remaining) PLANAR: Two translation + one rotation in plane (3 DOF removed, 3 remaining)

Compound Joints:

PIN_SLOT: Translation along slot + rotation about pin (4 DOF removed, 2 remaining) UNIVERSAL: Two perpendicular rotation axes (4 DOF removed, 2 remaining) SCREW: Coupled rotation and translation (5 DOF removed, 1 coupled)

Coupled Mates (motion relationships):

GEAR: Coupled rotation with ratio (no DOF removed, creates relationship) RACK_PINION: Couples rotation to translation (no DOF removed, creates relationship) CAM: Follower constrained to cam profile path (path-following) SLOT: Linear sliding constraint along trajectory (varies by slot type)

References

  • SolidWorks Mates Overview (2025)

  • Fusion 360 Joint Types

  • CATIA DMU Kinematics

  • Siemens NX Motion Joints

  • PTC Creo Mechanism Design

ANGLE = 'angle'
CAM = 'cam'
COINCIDENT = 'coincident'
CONCENTRIC = 'concentric'
CYLINDRICAL = 'cylindrical'
DISTANCE = 'distance'
GEAR = 'gear'
PARALLEL = 'parallel'
PERPENDICULAR = 'perpendicular'
PIN_SLOT = 'pin_slot'
PLANAR = 'planar'
PRISMATIC = 'prismatic'
RACK_PINION = 'rack_pinion'
REVOLUTE = 'revolute'
RIGID = 'rigid'
SCREW = 'screw'
SLOT = 'slot'
SPHERICAL = 'spherical'
TANGENT = 'tangent'
UNIVERSAL = 'universal'
yapcad.assembly.mate.check_bolt_circle_alignment(bolt_circle_a: Datum, bolt_circle_b: Datum, hole_count: int, tolerance_mm: float = 0.1, angular_offset_deg: float = 0.0) CoincidentResult[source]

Check if two bolt circles align for proper mating.

This validates that mounting hole patterns match for servo horn to gear hub interfaces, motor mounts, etc.

Parameters:
  • bolt_circle_a – First bolt circle datum (e.g., servo horn)

  • bolt_circle_b – Second bolt circle datum (e.g., sun gear hub)

  • hole_count – Number of holes in each pattern

  • tolerance_mm – Position tolerance for hole centers

  • angular_offset_deg – Expected angular offset between patterns (e.g., 45 degrees)

Returns:

CoincidentResult with alignment status

Example

>>> # XH540 servo horn (4 holes at 45 deg offset) to sun gear
>>> result = check_bolt_circle_alignment(
...     horn_bolt_circle,
...     sun_gear_bolt_circle,
...     hole_count=4,
...     angular_offset_deg=45.0
... )
yapcad.assembly.mate.create_coincident_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, offset: float = 0.0) Mate[source]

Convenience function to create a COINCIDENT mate.

A COINCIDENT mate constrains two features to occupy the same location: - Points share the same position - Planes are coplanar - Bolt circles align (centers coincide)

Parameters:
  • name – Descriptive name (e.g., “horn_to_sun_gear”)

  • part_a – First component identifier

  • datum_a – Datum on first component

  • part_b – Second component identifier

  • datum_b – Datum on second component

  • offset – Optional offset distance along normal (default: 0)

Returns:

Mate configured as COINCIDENT constraint

Example

>>> # Align servo horn bolt holes with sun gear bolt holes
>>> mate = create_coincident_mate(
...     name="horn_bolt_alignment",
...     part_a="xh540_servo",
...     datum_a="horn_bolt_circle",
...     part_b="axis1_sun_gear",
...     datum_b="hub_bolt_circle"
... )
yapcad.assembly.mate.create_gear_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, ratio: float, reverse: bool = False) Mate[source]

Convenience function to create a gear coupling mate.

Couples rotation of two components with a fixed ratio. The ratio is defined as: ratio = driven_rotations / driver_rotations

Parameters:
  • name – Descriptive name (e.g., “gear_1_to_2”, “pulley_coupling”)

  • part_a – Driver (input) component identifier

  • datum_a – Rotation axis on driver

  • part_b – Driven (output) component identifier

  • datum_b – Rotation axis on driven

  • ratio – Motion ratio (driven_rotations / driver_rotations)

  • reverse – If True, gears rotate in opposite directions

Returns:

Mate configured as gear coupling

Example

# 3:1 reduction gearbox (output rotates 1/3 speed of input) gearbox = create_gear_mate(

name=”motor_to_output”, part_a=”motor_shaft”, datum_a=”motor_axis”, part_b=”output_shaft”, datum_b=”output_axis”, ratio=1.0/3.0, reverse=False

)

yapcad.assembly.mate.create_prismatic_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, axis: List[float] | None = None, min_position: float | None = None, max_position: float | None = None, max_velocity: float | None = None, max_force: float | None = None, friction: float = 0.0, damping: float = 0.0) Mate[source]

Convenience function to create a prismatic (slider) joint mate.

A prismatic joint allows translation along a single axis with no rotation. Common for linear actuators, pistons, and drawer slides.

Parameters:
  • name – Descriptive name (e.g., “piston_stroke”, “drawer_slide”)

  • part_a – Parent/base component identifier

  • datum_a – Axis datum on parent component

  • part_b – Child/moving component identifier

  • datum_b – Axis datum on child component

  • axis – Translation axis direction [x,y,z,w], defaults to [0,0,1,0] (Z-axis)

  • min_position – Minimum position in mm (None = unlimited)

  • max_position – Maximum position in mm (None = unlimited)

  • max_velocity – Maximum velocity in mm/s (None = unlimited)

  • max_force – Maximum force in N (None = unlimited)

  • friction – Static friction coefficient (0.0 = frictionless)

  • damping – Viscous damping coefficient (0.0 = no damping)

Returns:

Mate configured as prismatic joint

Example

# Linear actuator with 100mm stroke actuator = create_prismatic_mate(

name=”z_axis_slide”, part_a=”base”, datum_a=”rail_axis”, part_b=”carriage”, datum_b=”slider_axis”, min_position=0.0, max_position=100.0, max_velocity=50.0, friction=0.05, damping=5.0

)

yapcad.assembly.mate.create_revolute_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, axis: List[float] | None = None, min_angle: float | None = None, max_angle: float | None = None, max_velocity: float | None = None, max_torque: float | None = None, friction: float = 0.0, damping: float = 0.0) Mate[source]

Convenience function to create a revolute (hinge) joint mate.

A revolute joint allows rotation about a single axis. This is the most common joint type for mechanisms, robots, and articulated assemblies.

Parameters:
  • name – Descriptive name (e.g., “shoulder_pitch”, “door_hinge”)

  • part_a – Parent/base component identifier

  • datum_a – Axis datum on parent component

  • part_b – Child/moving component identifier

  • datum_b – Axis datum on child component

  • axis – Rotation axis direction [x,y,z,w], defaults to [0,0,1,0] (Z-axis)

  • min_angle – Minimum rotation angle in radians (None = unlimited)

  • max_angle – Maximum rotation angle in radians (None = unlimited)

  • max_velocity – Maximum angular velocity in rad/s (None = unlimited)

  • max_torque – Maximum torque in N*m (None = unlimited)

  • friction – Static friction coefficient (0.0 = frictionless)

  • damping – Viscous damping coefficient (0.0 = no damping)

Returns:

Mate configured as revolute joint

Example

# Robot elbow with 180-degree range elbow = create_revolute_mate(

name=”elbow_flex”, part_a=”upper_arm”, datum_a=”elbow_axis”, part_b=”forearm”, datum_b=”forearm_root_axis”, min_angle=0.0, max_angle=math.pi, max_velocity=2.0, friction=0.02, damping=0.1

)

yapcad.assembly.mate.evaluate_coincident(datum_a: Datum, datum_b: Datum, tolerance_mm: float = 0.1, tolerance_deg: float = 1.0) CoincidentResult[source]

Evaluate a COINCIDENT constraint between two datums.

COINCIDENT means two geometric features occupy the same location/orientation: - Point-to-Point: Origins coincide - Point-to-Plane: Point lies on plane - Plane-to-Plane: Planes are coplanar (same origin + parallel normals) - Circle-to-Circle: Centers coincide (for bolt circle alignment) - Axis-to-Axis: Axes are colinear

Parameters:
  • datum_a – First datum in world coordinates

  • datum_b – Second datum in world coordinates

  • tolerance_mm – Linear tolerance in millimeters (default: 0.1mm)

  • tolerance_deg – Angular tolerance in degrees (default: 1.0 deg)

Returns:

CoincidentResult with evaluation status and error metrics

Example

>>> # Check if bolt holes align
>>> result = evaluate_coincident(
...     horn_bolt_circle,
...     sun_gear_bolt_circle,
...     tolerance_mm=0.05
... )
>>> if result.satisfied:
...     print("Bolt holes align!")

yapcad.assembly.mate_examples module

Example usage of the yapCAD assembly mate system.

This file demonstrates how to define kinematic constraints for mechanical assemblies using the mate module.

yapcad.assembly.mate_examples.example_gearbox()[source]

Define a 3-stage planetary gearbox with gear mates.

Each stage has a 3:1 reduction, resulting in 27:1 overall.

yapcad.assembly.mate_examples.example_linear_actuator()[source]

Define a linear actuator with prismatic joint and screw coupling.

yapcad.assembly.mate_examples.example_robot_arm()[source]

Define a simple 2-DOF robot arm with revolute joints.

The arm consists of: - Base (fixed) - Shoulder joint (revolute, ±90°) - Upper arm - Elbow joint (revolute, 0-180°) - Forearm

yapcad.assembly.mate_examples.example_serialization()[source]

Demonstrate mate serialization to/from dictionary for JSON export.

yapcad.assembly.mate_examples.example_spherical_joint()[source]

Define a spherical (ball-and-socket) joint with multi-axis limits.

yapcad.assembly.mate_examples.main()[source]

Run all examples.

yapcad.assembly.mate_surrogates module

Mate Surrogates for COTS (Commercial Off-The-Shelf) Parts.

This module defines explicit reference frame definitions for COTS parts that lack native CAD mate features. These “mate surrogates” provide unambiguous interface definitions for kinematic chain assembly.

PROBLEM STATEMENT

COTS parts like Dynamixel servos and DDSM115 hub motors come with STL/STEP files that have geometry but not explicit mate features (datum planes, reference axes). When assembling them into a kinematic chain, we need clear interface definitions:

  1. Where is the output shaft axis?

  2. Where is the mounting face?

  3. What’s the reference frame for orientation?

  4. Where are bolt patterns relative to the origin?

Without explicit surrogates, these interfaces are implicitly defined in code (e.g., kinematic_chain.py), making them: - Hard to verify - Prone to errors when updating - Scattered across multiple files

SOLUTION: MATE SURROGATES

A mate surrogate defines a reference frame attached to a COTS part with: - origin: The reference point (typically mounting face center) - x_axis, y_axis, z_axis: Orthonormal frame axes - features: Named geometric features with positions and orientations

This is similar to how professional CAD systems (SolidWorks, Creo) handle imported geometry - you create reference geometry on top of the dumb solid.

EXISTING GEOMETRY ANALYSIS

FINDING: The existing DSL files and kinematic_chain.py already contain most of the information needed for proper mating, but it’s IMPLICIT rather than EXPLICIT:

  1. DDSM115 Motor (ddsm115_motor.dsl): - EXCELLENT documentation in comments (lines 1-71) - Origin at mounting face (Z=0), wheel in +Z, stator body in -Z - Motor rotation axis is ALONG the Y-axis in STL coordinates (not Z!) - Triangular boss pattern: 3x at 120deg, radius 10.5mm - Bolt circle: 15.2mm diameter, 3x M2.5 at 120deg - ISSUE: The DSL defines the geometry but kinematic_chain.py has to

    manually apply transforms (Ry(-90) @ Rz(30)) to orient correctly

  2. Dynamixel Servos (XH540, XH430, XL330): - STL files exist but no DSL definitions - Interface specs are in gearbox_mates.py (ServoHornSpec class) - Horn bolt circles, protrusion dimensions are documented - Servo body dimensions are in scara_arm.dsl (lines 41-86) - ISSUE: No explicit coordinate system definition in STL files

  3. Planetary Gearboxes (planetary_gearbox.dsl): - Sun gear interface matches servo horn bolt patterns - Center counterbore clears horn protrusion - Output carrier has bolt pattern for next stage - ISSUE: Mating relies on correct Z-stack positioning

  4. Existing Mate Files: - gearbox_mates.py: Excellent! Defines ServoHornSpec and SunGearHubSpec - scara_assembly_mates.py: Complete interface catalog - HOWEVER: These define CONSTRAINTS between parts, not the reference

    frames on individual parts

CONCLUSION

The existing geometry IS sufficient for unambiguous mating, BUT the reference frame information is scattered and implicit. Mate surrogates consolidate this information into a single source of truth.

RECOMMENDATION: - For Dynamixel servos: Minimal surrogates needed (bolt patterns well-documented) - For DDSM115: Essential - the Y-axis rotation is non-obvious and critical - For gearboxes: Already well-defined in planetary_gearbox.dsl, just need to

consolidate frame definitions


class yapcad.assembly.mate_surrogates.Feature(name: str, feature_type: FeatureType, point: Tuple[float, float, float] = (0.0, 0.0, 0.0), direction: Tuple[float, float, float] = (0.0, 0.0, 1.0), radius: float | None = None, count: int | None = None, angle_offset_deg: float = 0.0, vertices: List[Tuple[float, float, float]] | None = None, height: float | None = None, description: str = '')[source]

Bases: object

A named geometric feature on a part.

name

Feature name (e.g., “stator_face”, “bolt_circle”)

Type:

str

feature_type

Type of geometric feature

Type:

yapcad.assembly.mate_surrogates.FeatureType

point

Reference point [x, y, z] in part local coordinates

Type:

Tuple[float, float, float]

direction

Direction vector [x, y, z] (for axes, normals)

Type:

Tuple[float, float, float]

radius

Radius for circular features (mm)

Type:

float | None

count

Number of items (for hole patterns)

Type:

int | None

angle_offset_deg

Angular offset of first item from +X (degrees)

Type:

float

vertices

List of vertex positions (for polygons)

Type:

List[Tuple[float, float, float]] | None

height

Height for cylindrical features (mm)

Type:

float | None

description

Human-readable description

Type:

str

angle_offset_deg: float = 0.0
count: int | None = None
description: str = ''
direction: Tuple[float, float, float] = (0.0, 0.0, 1.0)
feature_type: FeatureType
get_hole_positions() List[Tuple[float, float, float]][source]

Get positions of holes in a bolt circle pattern.

height: float | None = None
name: str
point: Tuple[float, float, float] = (0.0, 0.0, 0.0)
radius: float | None = None
vertices: List[Tuple[float, float, float]] | None = None
class yapcad.assembly.mate_surrogates.FeatureType(*values)[source]

Bases: Enum

Types of geometric features on COTS parts.

AXIS = 'axis'
CIRCLE = 'circle'
CYLINDER = 'cylinder'
PLANE = 'plane'
POLYGON = 'polygon'
class yapcad.assembly.mate_surrogates.MateSurrogate(part_name: str, description: str = '', origin: Tuple[float, float, float]=(0.0, 0.0, 0.0), x_axis: Tuple[float, float, float]=(1.0, 0.0, 0.0), y_axis: Tuple[float, float, float]=(0.0, 1.0, 0.0), z_axis: Tuple[float, float, float]=(0.0, 0.0, 1.0), features: Dict[str, ~yapcad.assembly.mate_surrogates.Feature]=<factory>, stl_file: str | None = None, step_file: str | None = None, notes: str = '')[source]

Bases: object

Complete reference frame definition for a COTS part.

The surrogate defines a local coordinate system and named features that can be used for assembly mating.

part_name

Name of the COTS part (e.g., “DDSM115”)

Type:

str

description

Part description

Type:

str

origin

Origin point in the part’s STL/native coordinate system

Type:

Tuple[float, float, float]

x_axis

X-axis direction vector

Type:

Tuple[float, float, float]

y_axis

Y-axis direction vector

Type:

Tuple[float, float, float]

z_axis

Z-axis direction vector

Type:

Tuple[float, float, float]

features

Dictionary of named features

Type:

Dict[str, yapcad.assembly.mate_surrogates.Feature]

stl_file

Path to STL file (relative to project output directory)

Type:

str | None

step_file

Path to STEP file if available

Type:

str | None

notes

Additional notes about the part

Type:

str

add_feature(feature: Feature) None[source]

Add a feature to this surrogate.

description: str = ''
features: Dict[str, Feature]
get_feature(name: str) Feature[source]

Get a feature by name.

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

Get transform parameters to convert from STL coords to standard frame.

Returns dict with: - rotation_rpy: (roll, pitch, yaw) in degrees - translation: (x, y, z) offset

notes: str = ''
origin: Tuple[float, float, float] = (0.0, 0.0, 0.0)
part_name: str
step_file: str | None = None
stl_file: str | None = None
x_axis: Tuple[float, float, float] = (1.0, 0.0, 0.0)
y_axis: Tuple[float, float, float] = (0.0, 1.0, 0.0)
z_axis: Tuple[float, float, float] = (0.0, 0.0, 1.0)
yapcad.assembly.mate_surrogates.create_ddsm115_surrogate() MateSurrogate[source]

Create mate surrogate for Waveshare DDSM115 hub motor.

CRITICAL COORDINATE SYSTEM (from STEP file analysis): - The motor rotation axis is the STL Y-AXIS (not Z!) - Tire (rotor/wheel) is at Y < 0 (faces -Y direction) - Stator (mounting face) is at Y > 0 (faces +Y direction)

This is non-intuitive because most parts have their rotation axis along Z. The kinematic_chain.py applies Ry(-90) to map motor Y-axis to tangent direction.

PHYSICAL STRUCTURE: - Hub motor: outer rotor (tire), inner stator (mounting hub) - Tire OD: 100.7mm, Width: 50mm - Motor body diameter: 67mm - Triangular anti-rotation bosses on stator side

STL ORIGIN: - At the interface between tire and stator body - Y=0 is approximately at the tire-stator boundary - Stator mounting face is at Y=+10mm - Tire center is at Y=-26.5mm (50mm wide tire)

yapcad.assembly.mate_surrogates.create_sun_gear_surrogate(axis_name: str, sun_teeth: int, module_mm: float, face_width: float, hub_height: float, bolt_circle: float, servo_type: str) MateSurrogate[source]

Create mate surrogate for a planetary sun gear.

Sun gears have a flanged hub that mates with servo horns. The hub extends BELOW the gear teeth (in -Z).

Parameters:
  • axis_name – Axis identifier (e.g., “AXIS1”)

  • sun_teeth – Number of teeth on sun gear

  • module_mm – Gear module in mm

  • face_width – Gear face width in mm

  • hub_height – Total hub height (extends below gear)

  • bolt_circle – Bolt circle diameter for servo horn interface

  • servo_type – Servo model (e.g., “XH540”, “XH430”, “XL330”)

yapcad.assembly.mate_surrogates.create_xh430_surrogate() MateSurrogate[source]

Create mate surrogate for Robotis Dynamixel XH430-W350 servo.

XH430 is the mid-size servo used for Axis 2 and Axis 3. Smaller than XH540 but still high-torque.

yapcad.assembly.mate_surrogates.create_xh540_surrogate() MateSurrogate[source]

Create mate surrogate for Robotis Dynamixel XH540-W270 servo.

XH540 is the largest Dynamixel X-series servo used in this robot. It drives Axis 1 (shoulder yaw) through a 6:1 planetary gearbox.

COORDINATE SYSTEM: - STL origin at servo body center - Z-axis is servo output shaft axis (horn faces +Z) - Servo body is symmetric about Z-axis in XY plane

PHYSICAL DIMENSIONS (from datasheet): - Body: 33.5 x 58.5 x 44mm (W x D x H) - Horn bolt circle: 18mm diameter, 4x M2.5 at 45deg offset - Output shaft at one end, back shell at other

yapcad.assembly.mate_surrogates.create_xl330_surrogate() MateSurrogate[source]

Create mate surrogate for Robotis Dynamixel XL330-M288 servo.

XL330 is the compact servo used for Axis 4 (wrist rotation). Lightweight and suitable for end effector mounting.

yapcad.assembly.mate_surrogates.get_all_surrogates() Dict[str, MateSurrogate][source]

Get dictionary of all COTS part mate surrogates.

yapcad.assembly.mate_surrogates.print_surrogate_summary() None[source]

Print a summary of all mate surrogates.

yapcad.assembly.mate_surrogates.validate_surrogates() List[str][source]

Validate all mate surrogates for consistency.

Returns:

List of validation issues (empty if all valid)

yapcad.assembly.solver module

Generic Mate Constraint Solver for Multi-Body Assemblies.

OVERVIEW

This module provides a generic constraint solver that computes 6DOF transforms from mate constraints between parts. It serves as the core computation engine for datum-driven assembly positioning, eliminating hardcoded transform values.

Key Concepts:
  • Mate Constraint: Geometric relationship between two datum features

  • Datum: Named geometric reference (point, axis, plane, frame) on a part

  • Transform: 4x4 homogeneous transformation matrix positioning a part

  • Mate Axis: Coordinate remapping for rotated reference frames

Quick Start:

from yapcad.assembly.solver import MateConstraintSolver, MateMateSolveResult
from yapcad.assembly.mate import Mate, MateType
from yapcad.assembly.datum_registry import DatumRegistry

# Register datums for parts
DatumRegistry.register_source("servo", servo_datums)
DatumRegistry.register_source("bracket", bracket_datums)

# Create mate constraint
mate = Mate(
    name="servo_to_bracket",
    mate_type=MateType.COINCIDENT,
    part_a="bracket",
    datum_a="servo_mount_face",
    part_b="servo",
    datum_b="stator_face",
)

# Solve for child transform
solver = MateConstraintSolver()
result = solver.solve_mate(mate)

if result.success:
    child_transform = result.transform  # 4x4 numpy array

Note

This module’s MateMateSolveResult is distinct from yapcad.assembly.intent.MateSolveResult.

Mate constraint solver contributed by Jeremy Mika.

Copyright (c) 2026 yapCAD contributors License: MIT

class yapcad.assembly.solver.MateConstraintSolver(tolerance: float = 0.001, angle_tolerance: float = 0.1, use_cache: bool = True)[source]

Bases: object

Generic constraint solver for computing transforms from mates.

This solver takes Mate constraint definitions and computes the 4x4 homogeneous transform that positions the child part relative to the parent part such that the constraint is satisfied.

Supported constraint types:
  • COINCIDENT: Face-to-face contact (normals opposed)

  • CONCENTRIC: Axes colinear

  • PARALLEL: Directions aligned

  • PERPENDICULAR: Directions at 90 degrees

  • DISTANCE: Fixed offset along normal

  • ANGLE: Fixed angular relationship

  • REVOLUTE: Rotation about axis (returns base transform)

Example:

solver = MateConstraintSolver()

# Solve single mate
result = solver.solve_mate(my_mate)

# Solve assembly
results = solver.solve_all([mate1, mate2, mate3], base_transforms)
cache

Dict mapping mate names to cached transform results

tolerance

Position tolerance in mm (default 0.001)

angle_tolerance

Angular tolerance in degrees (default 0.1)

invalidate_cache(mate_name: str | None = None) None[source]

Invalidate cached transforms.

Parameters:

mate_name – Specific mate to invalidate, or None for all

solve_all(mates: List[Mate], parent_transforms: Dict[str, Any] | None = None) Dict[str, MateMateSolveResult][source]

Solve multiple mate constraints.

Parameters:
  • mates – List of Mate constraints to solve

  • parent_transforms – Optional dict of {part_name: 4x4 transform}

Returns:

Dict mapping mate names to MateMateSolveResults

solve_mate(mate: Mate, parent_world_transform: Any | None = None, mate_axis: str = 'Z') MateMateSolveResult[source]

Solve a mate constraint to compute the child transform.

Parameters:
  • mate – Mate constraint definition

  • parent_world_transform – Optional 4x4 world transform of parent

  • mate_axis – Coordinate axis for mate (“X”, “Y”, “Z”, “-Z”)

Returns:

MateSolveResult with transform or error

The returned transform positions the child part origin in the parent part’s coordinate frame such that the mate constraint is satisfied.

validate_transform(transform: Any, tolerance_det: float = 1e-06) ValidationResult[source]

Validate a 4x4 transformation matrix.

Parameters:
  • transform – 4x4 numpy array to validate

  • tolerance_det – Tolerance for determinant check

Returns:

ValidationResult with validation details

Checks:
  • Shape is (4, 4)

  • Bottom row is [0, 0, 0, 1]

  • Rotation part is orthonormal (R^T R = I)

  • Determinant is +1 (proper rotation, not reflection)

class yapcad.assembly.solver.MateMateSolveResult(success: bool, transform: Any | None = None, error_message: str = '', residual: float = 0.0, details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of solving a mate constraint.

success

True if constraint was successfully solved

Type:

bool

transform

4x4 homogeneous transform matrix (child in parent frame)

Type:

Any | None

error_message

Human-readable error description if failed

Type:

str

residual

Constraint satisfaction error (0.0 = perfect)

Type:

float

details

Additional solver information

Type:

Dict[str, Any]

details: Dict[str, Any]
error_message: str = ''
residual: float = 0.0
success: bool
transform: Any | None = None
class yapcad.assembly.solver.ValidationResult(valid: bool, is_rigid: bool = True, is_orthonormal: bool = True, position_error: float = 0.0, orientation_error: float = 0.0, error_messages: List[str] = <factory>)[source]

Bases: object

Result of validating a transform matrix.

valid

True if transform is valid (orthonormal, proper shape)

Type:

bool

is_rigid

True if rotation is orthonormal with det=+1

Type:

bool

is_orthonormal

True if rotation columns are unit length

Type:

bool

position_error

Maximum deviation from orthonormality

Type:

float

orientation_error

Deviation from det=1

Type:

float

error_messages

List of validation failure reasons

Type:

List[str]

error_messages: List[str]
is_orthonormal: bool = True
is_rigid: bool = True
orientation_error: float = 0.0
position_error: float = 0.0
valid: bool
yapcad.assembly.solver.apply_mate_axis_rotation(transform: Any, mate_axis: str) Any[source]

Apply mate axis coordinate remapping to a transform.

Parameters:
  • transform – 4x4 numpy array

  • mate_axis – Target axis (“X”, “Y”, “Z”, “-X”, “-Y”, “-Z”)

Returns:

Modified 4x4 transform

Example:

# Remap Z axis to Y axis
new_tf = apply_mate_axis_rotation(transform, "Y")
yapcad.assembly.solver.solve_mate_chain(mates: List[Mate], base_transform: Any | None = None, solver: MateConstraintSolver | None = None) Dict[str, Any][source]

Solve a chain of mate constraints sequentially.

Parameters:
  • mates – Ordered list of mates (parent -> child order)

  • base_transform – Optional world transform for first parent

  • solver – Optional solver instance (creates one if not provided)

Returns:

Dict mapping part names to world transforms

Example:

world_transforms = solve_mate_chain(
    [base_to_link1, link1_to_link2, link2_to_tool],
    base_transform=np.eye(4)
)

Module contents

Constraint-based assembly system for yapCAD.

This package provides a declarative assembly system inspired by modern CAD tools (SolidWorks mates, Fusion 360 joints) but tailored for programmatic 3D generative design workflows. It enables definition and validation of assembly relationships to prevent common orientation errors.

Key Concepts:
Datum Features:

Named geometric references (points, axes, planes, frames, circles) defined on parts in local coordinates. Datums transform with parts.

Assembly Mates:

Relationships between datum features that position parts relative to each other. Types include coincident, concentric, parallel, perpendicular, tangent, and angle mates.

Design Constraints:

High-level rules that validate assembly intent beyond positioning. Examples: “motor axis must be tangent to chassis circle”, “stator face must point inward toward mounting surface”.

Constraint Solver:

Computes transforms from mates and validates all constraints, providing clear error messages when design intent is violated.

Design Principles:
  • Constraints are first-class executable objects, not documentation

  • Fail-fast validation detects invalid assemblies at definition time

  • Separation of concerns: mates position, constraints verify intent

  • Composability: complex assemblies built from validated subassemblies

  • Integration with existing kinematic chain transform system

Quick Start:
>>> from yapcad.assembly import (
...     Datum, DatumType, PartDefinition,
...     Mate, MateType,
...     Constraint, ConstraintType,
...     Assembly
... )
>>> # Define a part with datum features
>>> motor = PartDefinition("motor")
>>> motor.add_datum(Datum(
...     "axis", DatumType.AXIS,
...     origin=(0, 0, 0), direction=(0, 1, 0),
...     description="Motor rotation axis"
... ))
>>> motor.add_datum(Datum(
...     "stator_face", DatumType.PLANE,
...     origin=(0, 5, 0), normal=(0, 1, 0),
...     description="Mounting surface"
... ))
>>> # Define assembly with mates
>>> assembly = Assembly("wheel_assembly")
>>> assembly.add_part(motor)
>>> assembly.add_mate(Mate(
...     MateType.CONCENTRIC,
...     "motor", "axis",
...     "bracket", "hole_axis"
... ))
>>> # Add design constraints
>>> assembly.add_constraint(Constraint(
...     ConstraintType.TANGENT_TO_CIRCLE,
...     "motor", "axis",
...     "chassis", "wheel_orbit",
...     description="Motor axis tangent for proper wheel rolling"
... ))
>>> # Solve and validate
>>> result = assembly.solve()
>>> if result.is_valid:
...     transforms = result.transforms
>>> else:
...     print(result.errors)
Available Classes:
Datum Types:

Datum - Named geometric reference feature on a part DatumType - Enum: POINT, AXIS, PLANE, FRAME, CIRCLE PartDefinition - Part with datum features and metadata

Mate System:

Mate - Relationship between two datum features MateType - Enum: COINCIDENT, CONCENTRIC, PARALLEL, PERPENDICULAR,

TANGENT, ANGLE, ALIGNED, OPPOSED

Constraint System:

Constraint - Design rule that validates assembly intent ConstraintType - Enum: TANGENT_TO_CIRCLE, POINTS_TOWARD,

POINTS_AWAY, AXIS_PARALLEL_TO_PLANE, AXIS_PERPENDICULAR_TO_PLANE, CUSTOM

Assembly:

Assembly - Container for parts, mates, and constraints AssemblyResult - Validation result with transforms or errors

See also

  • Examples: examples/assembly_demo.py

  • Integration: src/yapcad/assembly/solver.py

class yapcad.assembly.Assembly(name: str)[source]

Bases: object

Main assembly orchestrator for the yapCAD constraint system.

The Assembly class manages a collection of parts with their transforms, mate relationships that position parts, and design constraints that validate assembly intent.

Workflow:
  1. Create assembly: assembly = Assembly(“my_assembly”)

  2. Add parts: assembly.add_part(part_def, name=”part_1”)

  3. Add mates: assembly.add_mate(mate)

  4. Add constraints: assembly.add_constraint(constraint)

  5. Validate: result = assembly.validate()

The assembly can also import transforms from existing kinematic chains and export to various formats (URDF, Blender, etc.).

name

Unique identifier for this assembly

parts

Dictionary mapping part name to PartDefinition

Type:

Dict[str, yapcad.assembly.datum.PartDefinition]

transforms

Dictionary mapping part name to 4x4 transform matrix

Type:

Dict[str, numpy.ndarray]

mates

List of Mate objects defining part relationships

Type:

List[yapcad.assembly.mate.Mate]

constraints

List of Constraint objects to validate

Type:

List[yapcad.assembly.constraint.Constraint]

Integration with yapCAD:
  • Transforms are 4x4 numpy arrays compatible with yapcad.xform.Matrix

  • Can import transforms from kinematic_chain.py JSON exports

  • Datums transform correctly through coordinate systems

Example

>>> # Create assembly
>>> assembly = Assembly("robot_wheel")
>>>
>>> # Add parts
>>> assembly.add_part(motor_def, name="motor")
>>> assembly.add_part(bracket_def, name="bracket")
>>>
>>> # Define relationships
>>> assembly.add_mate(Mate("align", MateType.CONCENTRIC,
...                        "bracket", "hole", "motor", "shaft"))
>>>
>>> # Validate design intent
>>> assembly.add_constraint(Constraint(
...     "tangent", ConstraintType.TANGENT_TO_CIRCLE,
...     "motor", "axis", center=(0,0,0), radius=124.5
... ))
>>>
>>> # Check if valid
>>> result = assembly.validate()
>>> if not result.is_valid:
...     assembly.validate_and_raise()  # Raises AssemblyError
add_constraint(constraint: Constraint) None[source]

Add a design constraint to validate assembly intent.

Constraints validate high-level design requirements that go beyond simple positioning. They ensure the assembly meets its intended functional requirements.

Parameters:

constraint – Constraint object to validate

Raises:

AssemblyError – If referenced part or datum doesn’t exist

Example

>>> # Ensure motor axis is tangent to chassis
>>> assembly.add_constraint(Constraint(
...     "motor_tangent", ConstraintType.TANGENT_TO_CIRCLE,
...     part="motor_1", datum="motor_axis",
...     center=(0, 0, 0), radius=124.5,
...     description="Motor must be tangent for rolling motion"
... ))
add_mate(mate: Mate) None[source]

Add a mate relationship between two parts.

Mates define geometric relationships between datum features on different parts. They are used to position parts relative to each other.

Parameters:

mate – Mate object defining the relationship

Raises:

AssemblyError – If referenced parts or datums don’t exist, or if mate is invalid for the given datums

Example

>>> # Align motor shaft with bracket hole
>>> assembly.add_mate(Mate(
...     "shaft_alignment", MateType.CONCENTRIC,
...     part1="bracket", datum1="bore_axis",
...     part2="motor", datum2="shaft_axis"
... ))
add_part(part: PartDefinition, name: str = None, transform: ndarray = None) None[source]

Add a part to the assembly.

Parameters:
  • part – PartDefinition with datum features

  • name – Name for this part instance (default: part.name)

  • transform – Initial 4x4 transform matrix (default: identity)

Raises:

AssemblyError – If part name already exists in assembly

Example

>>> motor = PartDefinition("DDSM115")
>>> assembly.add_part(motor, name="motor_1")
>>> # Or with initial position
>>> T = np.eye(4)
>>> T[0:3, 3] = [100, 0, 0]  # Translate 100mm in X
>>> assembly.add_part(motor, name="motor_2", transform=T)
constraints: List[Constraint]
export_to_blender_rig(output_path: str | Path) None[source]

Export assembly to Blender armature rig for animation.

Creates a Blender Python script that sets up an armature with bones positioned according to the assembly transforms. Useful for visualizing kinematics and creating animations.

Parameters:

output_path – Path where Blender Python script will be written

Note

This is currently a stub implementation. A full implementation would generate a Blender Python script that creates bones, constraints, and animations.

Example

>>> assembly.export_to_blender_rig("robot_rig.py")
>>> # In Blender: File > Import > Python Script > robot_rig.py
export_to_urdf(output_path: str | Path) None[source]

Export assembly to URDF format for ROS integration.

URDF (Unified Robot Description Format) is used by ROS for robot kinematics and dynamics. This export enables simulation and control of yapCAD assemblies in ROS.

Parameters:

output_path – Path where URDF file will be written

Note

This is currently a stub implementation. A full implementation would generate proper URDF XML with links, joints, and meshes.

Example

>>> assembly.export_to_urdf("robot.urdf")
>>> # Use in ROS: roslaunch robot_description display.launch
get_degrees_of_freedom() Dict[str, int][source]

Calculate remaining degrees of freedom for each part after mates.

Analyzes the mate constraints to determine how many degrees of freedom remain unconstrained for each part. A fully constrained part has 0 DOF.

Returns:

Dictionary mapping part name to DOF count (0-6)

Note

This is a simplified implementation. A full implementation would analyze the constraint graph to detect over/under-constrained situations and redundant constraints.

Example

>>> dof = assembly.get_degrees_of_freedom()
>>> for part, dof_count in dof.items():
...     if dof_count > 0:
...         print(f"{part} has {dof_count} DOF remaining")
get_transformed_datum(part_name: str, datum_name: str) Datum[source]

Get a datum feature transformed to world coordinates.

Applies the part’s current transform to a datum to obtain its position and orientation in world coordinates.

Parameters:
  • part_name – Name of the part

  • datum_name – Name of the datum on that part

Returns:

Datum transformed to world coordinates

Raises:

AssemblyError – If part or datum not found

Example

>>> # Get motor axis in world coordinates
>>> motor_axis_world = assembly.get_transformed_datum(
...     "motor_1", "motor_axis"
... )
>>> print(f"Axis origin: {motor_axis_world.origin}")
>>> print(f"Axis direction: {motor_axis_world.direction}")
import_transforms_from_kinematic_chain(json_path: str | Path) None[source]

Import part transforms from a kinematic_chain.py JSON export.

The kinematic chain system computes transforms through a hierarchical tree. This method imports those transforms into the assembly so constraints can be validated against them.

Parameters:

json_path – Path to JSON file with kinematic chain transforms

Raises:

AssemblyError – If JSON cannot be loaded or part names don’t match

Example

>>> # Export from kinematic_chain.py
>>> chain.export_transforms("chain_transforms.json")
>>>
>>> # Import into assembly
>>> assembly.import_transforms_from_kinematic_chain(
...     "chain_transforms.json"
... )
>>>
>>> # Now validate against those transforms
>>> result = assembly.validate()
mates: List[Mate]
parts: Dict[str, PartDefinition]
report() str[source]

Generate a comprehensive human-readable assembly report.

Returns:

Multi-line string with assembly status and validation results

Example

>>> print(assembly.report())
Assembly: robot_wheel
====================================
Parts: 2
  - motor_1 (DDSM115_MOTOR)
  - bracket_1 (WHEEL_BRACKET)
Mates: 2
  • shaft_alignment (CONCENTRIC)

  • mount_surface (FLUSH)

Constraints: 1
  • motor_tangent (TANGENT_TO_CIRCLE)

Validation: VALID

transforms: Dict[str, ndarray]
validate() AssemblyValidationResult[source]

Validate all design constraints against current part transforms.

Evaluates each constraint in the assembly and returns a comprehensive validation result. This does not solve mates - it validates the current state of the assembly.

Returns:

AssemblyValidationResult with detailed status

Example

>>> result = assembly.validate()
>>> if result.is_valid:
...     print("Assembly is valid!")
... else:
...     print(result.report())
validate_and_raise() None[source]

Validate assembly and raise AssemblyError if invalid.

Convenience method for strict validation where you want an exception on any constraint failure.

Raises:

AssemblyError – If any constraint fails

Example

>>> try:
...     assembly.validate_and_raise()
...     print("Assembly is valid, proceeding...")
... except AssemblyError as e:
...     print(f"Invalid assembly: {e}")
exception yapcad.assembly.AssemblyError(message: str, assembly_name: str = None, part_name: str = None, constraint_name: str = None)[source]

Bases: Exception

Exception raised when assembly operations fail.

This exception is raised for various assembly-related errors such as: - Invalid part names or references - Missing datums on parts - Invalid mate configurations - Constraint validation failures - Solver convergence failures

message

Description of the error

assembly_name

Name of the assembly where error occurred

part_name

Name of the part involved (if applicable)

constraint_name

Name of the constraint that failed (if applicable)

class yapcad.assembly.AssemblyIntent(name: str, description: str = '', functional_requirements: List[FunctionalRequirement] = <factory>, connections: List[Connection] = <factory>, clearances: List[Clearance] = <factory>, reference_geometry: Dict[str, ~yapcad.assembly.intent.ReferenceGeometry]=<factory>, part_definitions: Dict[str, ~yapcad.assembly.datum.PartDefinition]=<factory>, derived_parameters: List[str] = <factory>, explicit_constraints: List[Constraint] = <factory>)[source]

Bases: object

Declarative specification of assembly requirements.

AssemblyIntent is the top-level container for a declarative assembly. Instead of specifying transforms, designers specify: - What parts must DO (functional requirements) - How parts CONNECT (topology) - What must NOT happen (clearances)

The system derives the geometry that satisfies all requirements.

name

Unique identifier for this assembly

Type:

str

description

Human-readable description

Type:

str

functional_requirements

List of FunctionalRequirement objects

Type:

List[yapcad.assembly.intent.FunctionalRequirement]

connections

List of Connection objects

Type:

List[yapcad.assembly.intent.Connection]

clearances

List of Clearance objects

Type:

List[yapcad.assembly.intent.Clearance]

reference_geometry

Dict of ReferenceGeometry objects

Type:

Dict[str, yapcad.assembly.intent.ReferenceGeometry]

part_definitions

Dict of PartDefinition objects (optional)

Type:

Dict[str, yapcad.assembly.datum.PartDefinition]

derived_parameters

List of parameter names to compute

Type:

List[str]

explicit_constraints

Additional explicit constraints

Type:

List[yapcad.assembly.constraint.Constraint]

Example

>>> assembly = AssemblyIntent(
...     name="wheel_pod",
...     functional_requirements=[...],
...     connections=[...],
...     clearances=[...],
... )
>>> result = assembly.solve()
add_clearance(clearance: Clearance) AssemblyIntent[source]

Add a clearance constraint.

add_connection(conn: Connection) AssemblyIntent[source]

Add a connection.

add_reference_geometry(geom: ReferenceGeometry) AssemblyIntent[source]

Add reference geometry.

add_requirement(req: FunctionalRequirement) AssemblyIntent[source]

Add a functional requirement.

clearances: List[Clearance]
collect_constraints() List[Dict[str, Any]][source]

Collect all constraints from requirements.

Converts high-level functional requirements into low-level constraint specifications.

Returns:

List of constraint specification dicts

collect_derived_parameters() List[str][source]

Collect all parameters that need to be derived.

Returns:

List of parameter names

connections: List[Connection]
derived_parameters: List[str]
description: str = ''
explicit_constraints: List[Constraint]
functional_requirements: List[FunctionalRequirement]
name: str
part_definitions: Dict[str, PartDefinition]
reference_geometry: Dict[str, ReferenceGeometry]
report() str[source]

Generate human-readable summary of the assembly intent.

solve() SolveResult[source]

Solve the assembly intent to derive parameters.

This is the main entry point for converting a declarative specification into actual geometry.

Returns:

SolveResult with derived values and validation status

to_assembly() Assembly[source]

Convert solved intent to yapCAD Assembly object.

Returns:

Assembly object with parts, mates, and constraints

validate() List[str][source]

Validate the assembly intent specification.

Checks for: - Missing reference geometry - Invalid part references - Conflicting requirements - Under/over-constrained systems

Returns:

List of validation error/warning messages

class yapcad.assembly.AssemblyValidationResult(is_valid: bool, constraint_results: Dict[str, ~yapcad.assembly.constraint.ConstraintResult]=<factory>, failed_constraints: List[str] = <factory>, warnings: List[str] = <factory>)[source]

Bases: object

Result of validating an assembly’s constraints.

Contains the complete validation status including which constraints passed, failed, or produced warnings, along with detailed diagnostic information for each constraint.

is_valid

True if all constraints are satisfied, False otherwise

Type:

bool

constraint_results

Dictionary mapping constraint name to ConstraintResult

Type:

Dict[str, yapcad.assembly.constraint.ConstraintResult]

failed_constraints

List of names of constraints that failed

Type:

List[str]

warnings

List of warning messages from constraint validation

Type:

List[str]

Example

>>> result = assembly.validate()
>>> if not result.is_valid:
...     print(f"Found {len(result.failed_constraints)} failures")
...     for name in result.failed_constraints:
...         cr = result.constraint_results[name]
...         print(f"  {name}: {cr.message}")
constraint_results: Dict[str, ConstraintResult]
failed_constraints: List[str]
is_valid: bool
report() str[source]

Generate detailed validation report.

warnings: List[str]
class yapcad.assembly.AssemblyValidator(name: str = 'assembly')[source]

Bases: object

Validates assemblies against a set of kinematic constraints.

The validator maintains a collection of constraints and evaluates them against world transforms from a kinematic chain.

Example

>>> validator = AssemblyValidator()
>>> validator.add_constraint(KinematicConstraint(...))
>>> validator.add_constraint(KinematicConstraint(...))
>>>
>>> # Get transforms from kinematic chain
>>> transforms = chain.get_all_world_transforms()
>>>
>>> # Validate
>>> report = validator.validate(transforms)
>>> if not report.is_valid:
...     print(report.detailed_report())
add_constraint(constraint: KinematicConstraint) None[source]

Add a constraint to the validator.

Parameters:

constraint – KinematicConstraint to add

add_constraints(constraints: List[KinematicConstraint]) None[source]

Add multiple constraints.

Parameters:

constraints – List of constraints to add

clear_constraints() None[source]

Remove all constraints.

constraints: List[KinematicConstraint]
validate(world_transforms: Dict[str, Any]) ValidationReport[source]

Validate all constraints against given world transforms.

Parameters:

world_transforms – Dictionary mapping part names to Transform objects or 4x4 numpy arrays

Returns:

ValidationReport with comprehensive evaluation results

validate_and_raise(world_transforms: Dict[str, Any]) None[source]

Validate and raise exception if any error-severity constraint fails.

Parameters:

world_transforms – Dictionary mapping part names to transforms

Raises:

ValueError – If any error-severity constraint fails

class yapcad.assembly.AxisOrientationRequirement(name: str, part: str, description: str = '', priority: int = 1, axis_datum: str = '', orientation: str = 'tangent', reference: str = 'global', pointing: str = 'toward')[source]

Bases: FunctionalRequirement

Part axis must have specific orientation relative to reference.

This requirement specifies that a datum axis on a part must point in a specific direction relative to the assembly or reference geometry.

axis_datum

Name of the axis datum on the part

Type:

str

orientation

Direction specification - “tangent”: Tangent to reference cylinder at part position - “radial”: Pointing toward/away from reference center - “axial”: Parallel to reference axis (e.g., tube Z-axis) - “+x”, “-y”, “+z”: Global direction - tuple: Custom direction vector

Type:

str

reference

Name of reference geometry or “global”

Type:

str

pointing

“toward” or “away” for radial orientation

Type:

str

Example

>>> axis_req = AxisOrientationRequirement(
...     name="motor_tangent",
...     part="motor",
...     axis_datum="motor_axis",
...     orientation="tangent",
...     reference="tube",
... )
axis_datum: str = ''
get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Convert orientation requirement to constraint.

orientation: str = 'tangent'
pointing: str = 'toward'
reference: str = 'global'
class yapcad.assembly.Clearance(part_a: str, part_b: str, min_distance: float, check_type: str = 'bounding_box', critical: bool = True)[source]

Bases: object

Parts must maintain minimum distance (no collision).

Clearance constraints ensure that parts do not interfere with each other or with reference geometry.

part_a

First part name

Type:

str

part_b

Second part name (or reference geometry name)

Type:

str

min_distance

Minimum allowed distance (mm)

Type:

float

check_type

How to measure distance - “bounding_box”: Use axis-aligned bounding boxes (fast) - “convex_hull”: Use convex hull approximation - “mesh”: Use full mesh collision (slow, accurate)

Type:

str

critical

If True, violation is error; else warning

Type:

bool

Example

>>> clearance = Clearance(
...     part_a="motor",
...     part_b="chassis_plate",
...     min_distance=5.0,
... )
check_type: str = 'bounding_box'
critical: bool = True
min_distance: float
part_a: str
part_b: str
validate(transforms: Dict[str, Any], part_bounds: Dict[str, Any]) ClearanceResult[source]

Check if clearance is satisfied.

Parameters:
  • transforms – Part name -> transform matrix

  • part_bounds – Part name -> bounding box/mesh

Returns:

ClearanceResult with validation status

class yapcad.assembly.ClearanceResult(satisfied: bool, actual_distance: float, required_distance: float, message: str = '', interference_point: Tuple[float, float, float] | None = None)[source]

Bases: object

Result of clearance validation.

actual_distance: float
interference_point: Tuple[float, float, float] | None = None
message: str = ''
required_distance: float
satisfied: bool
class yapcad.assembly.CoincidentResult(satisfied: bool, error_distance: float = 0.0, error_angle: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of evaluating a COINCIDENT constraint.

satisfied

True if constraint is satisfied within tolerance

Type:

bool

error_distance

Distance error in mm (for point/origin alignment)

Type:

float

error_angle

Angular error in degrees (for plane/axis alignment)

Type:

float

error_message

Human-readable description of the result

Type:

str

details

Additional information about the evaluation

Type:

Dict[str, Any]

details: Dict[str, Any]
error_angle: float = 0.0
error_distance: float = 0.0
error_message: str = ''
satisfied: bool
class yapcad.assembly.Connection(parent: str, child: str, joint_type: str = 'rigid', axis: str | ~typing.Tuple[float, float, float] | None=None, limits: Tuple[float, float] | None=None, interface_type: str | None = None, interface_details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Defines how two parts connect (topology).

Connections specify the parent-child relationships between parts and the type of joint (rigid, revolute, prismatic, etc.).

parent

Parent datum in “part.datum” format

Type:

str

child

Child datum in “part.datum” format

Type:

str

joint_type

Type of connection - “rigid”: No relative motion - “revolute”: Rotation about axis - “prismatic”: Translation along axis - “cylindrical”: Rotation + translation on axis - “spherical”: Ball-and-socket

Type:

str

axis

Axis specification for joints - “tangent”, “radial”, “axial” for reference-relative - tuple (x, y, z) for explicit direction

Type:

str | Tuple[float, float, float] | None

limits

(min, max) for joint limits (degrees or mm)

Type:

Tuple[float, float] | None

interface_type

Physical connection type

Type:

str | None

interface_details

Additional interface parameters

Type:

Dict[str, Any]

Example

>>> conn = Connection(
...     parent="chassis.pivot_boss",
...     child="wheel_arm.pivot_bore",
...     joint_type="revolute",
...     axis="tangent",
...     limits=(-15, 15),
... )
axis: str | Tuple[float, float, float] | None = None
child: str
get_child_datum() str[source]

Extract child datum name.

get_child_part() str[source]

Extract child part name.

get_parent_datum() str[source]

Extract parent datum name.

get_parent_part() str[source]

Extract parent part name.

interface_details: Dict[str, Any]
interface_type: str | None = None
joint_type: str = 'rigid'
limits: Tuple[float, float] | None = None
parent: str
to_mate_spec() Dict[str, Any][source]

Convert to mate specification dict.

class yapcad.assembly.Constraint(name: str, constraint_type: ConstraintType, part: str, datum: str, center: Tuple[float, float, float] | None = None, radius: float | None = None, direction: str | None = None, axis: Tuple[float, float, float] | None = None, reference_datum: str | None = None, plane_normal: Tuple[float, float, float] | None = None, min_value: float | None = None, max_value: float | None = None, validator: Callable[[Any], bool] | None = None, tolerance_deg: float = 1.0, tolerance_mm: float = 0.1, description: str = '', severity: str = 'error')[source]

Bases: object

A design constraint that validates assembly intent.

Constraints are evaluated after mates have positioned all parts. They verify that the resulting assembly meets design requirements.

Unlike mates (which determine part positions), constraints validate that the positions satisfy high-level design intent.

name

Unique identifier for this constraint

Type:

str

constraint_type

Type of constraint to evaluate

Type:

yapcad.assembly.constraint.ConstraintType

part

Name of the part containing the datum to check

Type:

str

datum

Name of the datum feature to evaluate

Type:

str

center

Reference center point for circular/radial constraints

Type:

Tuple[float, float, float] | None

radius

Reference radius for circular constraints (mm)

Type:

float | None

direction

Reference direction (“inward”, “outward”, “+x”, “-z”, etc.)

Type:

str | None

axis

Reference axis vector for parallel/perpendicular constraints

Type:

Tuple[float, float, float] | None

reference_datum

Reference datum for relative constraints

Type:

str | None

plane_normal

Normal vector for plane constraints

Type:

Tuple[float, float, float] | None

min_value

Minimum allowed value for range constraints

Type:

float | None

max_value

Maximum allowed value for range constraints

Type:

float | None

validator

Custom validation function for CUSTOM constraint type

Type:

Callable[[Any], bool] | None

tolerance_deg

Angular tolerance in degrees (default: 1.0°)

Type:

float

tolerance_mm

Linear tolerance in millimeters (default: 0.1mm)

Type:

float

description

Human-readable description of design intent

Type:

str

severity

“error”, “warning”, or “info”

Type:

str

Examples

# Motor axis tangent to wheel path (perpendicular to radial) >>> tangent = Constraint( … name=”wheel_axis_tangent”, … constraint_type=ConstraintType.TANGENT_TO_CIRCLE, … part=”DDSM115_MOTOR_1”, … datum=”motor_axis”, … center=(0.0, 0.0, 0.0), … radius=124.5, … tolerance_deg=2.0, … description=”Motor axis must be tangent for rolling motion” … )

# Tire tread must face outward toward tube wall >>> radial = Constraint( … name=”tire_faces_outward”, … constraint_type=ConstraintType.RADIAL_FROM_CENTER, … part=”DDSM115_MOTOR_1”, … datum=”tire_face”, … center=(0.0, 0.0, 0.0), … direction=”outward”, … tolerance_deg=5.0, … description=”Tire tread faces tube wall for traction” … )

# Mounting face must face upward >>> facing = Constraint( … name=”mount_faces_up”, … constraint_type=ConstraintType.FACING, … part=”BRACKET”, … datum=”mounting_face”, … direction=”+z”, … tolerance_deg=1.0, … description=”Mounting face must be horizontal” … )

# Part at specific radius from center >>> at_radius = Constraint( … name=”motor_at_wheel_radius”, … constraint_type=ConstraintType.AT_RADIUS, … part=”MOTOR”, … datum=”motor_center”, … center=(0.0, 0.0, 0.0), … radius=124.5, … tolerance_mm=0.5, … description=”Motor positioned on wheel path circle” … )

axis: Tuple[float, float, float] | None = None
center: Tuple[float, float, float] | None = None
constraint_type: ConstraintType
datum: str
description: str = ''
direction: str | None = None
evaluate(datum_world: Datum) ConstraintResult[source]

Evaluate this constraint against a datum in world coordinates.

Parameters:

datum_world – The datum feature transformed to world coordinates

Returns:

ConstraintResult indicating whether constraint is satisfied

Raises:
  • ValueError – If required parameters are missing

  • ImportError – If numpy is required but not available

max_value: float | None = None
min_value: float | None = None
name: str
part: str
plane_normal: Tuple[float, float, float] | None = None
radius: float | None = None
reference_datum: str | None = None
severity: str = 'error'
tolerance_deg: float = 1.0
tolerance_mm: float = 0.1
validator: Callable[[Any], bool] | None = None
class yapcad.assembly.ConstraintEvaluationResult(satisfied: bool, error: float = 0.0, error_vector: Tuple[float, float, float] | None=None, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of evaluating a single constraint.

satisfied

True if constraint is satisfied within tolerance

Type:

bool

error

Numeric error value (distance in mm or angle in degrees)

Type:

float

error_vector

Optional direction vector of error for visualization

Type:

Tuple[float, float, float] | None

error_message

Human-readable description

Type:

str

details

Additional diagnostic information

Type:

Dict[str, Any]

details: Dict[str, Any]
error: float = 0.0
error_message: str = ''
error_vector: Tuple[float, float, float] | None = None
satisfied: bool
class yapcad.assembly.ConstraintResult(passed: bool, error_value: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of evaluating a design constraint.

passed

True if constraint is satisfied within tolerance

Type:

bool

error_value

Numeric measure of constraint violation (degrees or mm)

Type:

float

error_message

Human-readable description of the result

Type:

str

details

Additional information about the evaluation

Type:

Dict[str, Any]

details: Dict[str, Any]
error_message: str = ''
error_value: float = 0.0
passed: bool
class yapcad.assembly.ConstraintType(*values)[source]

Bases: Enum

High-level design constraints that validate assembly intent.

These constraint types capture common design requirements in mechanical assemblies that are difficult to express with low-level mates alone.

TANGENT_TO_CIRCLE

Axis is tangent (perpendicular to radial) to a circle. Used for wheels that must roll along a circular path.

RADIAL_FROM_CENTER

Datum normal points toward (inward) or away from (outward) a center point. Used for features that must face the center or periphery of an assembly.

FACING

Datum normal points in a specified global direction (+x, -y, +z, etc.).

AT_RADIUS

Datum origin is at a specific radius from a center point.

PARALLEL_TO

Datum direction is parallel to a reference direction.

PERPENDICULAR_TO

Datum direction is perpendicular to a reference direction.

AT_RADIUS = 'at_radius'
CUSTOM = 'custom'
FACING = 'facing'
IN_PLANE = 'in_plane'
MIN_DISTANCE = 'min_distance'
NO_INTERFERENCE = 'no_interference'
ON_CIRCLE = 'on_circle'
PARALLEL_TO = 'parallel_to'
PERPENDICULAR_TO = 'perpendicular_to'
RADIAL_FROM_CENTER = 'radial_from_center'
TANGENT_TO_CIRCLE = 'tangent_to_circle'
class yapcad.assembly.ContactRequirement(name: str, part: str, description: str = '', priority: int = 1, surface: str = '', target: str = '', contact_type: str = 'static', preload_source: str | None = None)[source]

Bases: FunctionalRequirement

Part surface must contact a reference surface.

This requirement specifies that a datum surface on a part must touch a reference geometry surface. Common examples: - Wheel tire contacting tube inner wall - Mounting face flush with bracket surface - Ball bearing in socket

surface

Datum name on part (e.g., “tire_outer_diameter”)

Type:

str

target

Reference geometry name (e.g., “tube_inner_wall”)

Type:

str

contact_type

“static”, “rolling”, or “sliding”

Type:

str

preload_source

Optional name of what provides contact force

Type:

str | None

Example

>>> contact = ContactRequirement(
...     name="wheel_contact",
...     part="ddsm115_motor",
...     surface="tire_outer_diameter",
...     target="tube_inner_wall",
...     contact_type="rolling",
...     preload_source="suspension_spring",
... )
contact_type: str = 'static'
get_derived_parameters() List[str][source]

List parameters that can be derived from this requirement.

Returns:

List of parameter names in “part.parameter” format

get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Contact implies position at specific radius/distance.

preload_source: str | None = None
surface: str = ''
target: str = ''
class yapcad.assembly.Datum(name: str, datum_type: DatumType, origin: List[float] = <factory>, direction: List[float] | None = None, normal: List[float] | None = None, x_axis: List[float] | None = None, y_axis: List[float] | None = None, radius: float | None = None, description: str = '', tags: List[str] = <factory>)[source]

Bases: object

A named geometric reference feature on a part.

Datums are defined in the part’s local coordinate system and transform with the part when assembled. They provide explicit geometric references that can be used to establish assembly mates and constraints.

All geometric parameters use yapCAD’s homogeneous coordinate convention:
  • Points: [x, y, z, 1] (w=1 for positions)

  • Directions/Normals: [x, y, z, 0] (w=0 for direction vectors)

name

Unique identifier for this datum within the part

Type:

str

datum_type

Type of geometric feature (POINT, AXIS, PLANE, etc.)

Type:

yapcad.assembly.datum.DatumType

origin

Origin point [x, y, z, 1] in part’s local coordinates

Type:

List[float]

direction

Direction vector [x, y, z, 0] for AXIS datum

Type:

List[float] | None

normal

Normal vector [x, y, z, 0] for PLANE or CIRCLE datum

Type:

List[float] | None

x_axis

X-axis direction [x, y, z, 0] for FRAME datum

Type:

List[float] | None

y_axis

Y-axis direction [x, y, z, 0] for FRAME datum

Type:

List[float] | None

radius

Radius value (in mm) for CIRCLE datum

Type:

float | None

description

Human-readable description of this datum’s purpose

Type:

str

tags

Optional metadata tags for filtering/searching datums

Type:

List[str]

Example:

# Mounting plane on motor stator
stator_face = Datum(
    name="stator_face",
    datum_type=DatumType.PLANE,
    origin=[0, 10, 0, 1],
    normal=[0, 1, 0, 0],
    description="Stator mounting surface facing +Y"
)

# Rotation axis through motor shaft
motor_axis = Datum(
    name="motor_axis",
    datum_type=DatumType.AXIS,
    origin=[0, 0, 0, 1],
    direction=[0, 1, 0, 0],
    description="Motor rotation axis along Y"
)

# Mounting hole pattern
holes = Datum(
    name="mounting_holes",
    datum_type=DatumType.CIRCLE,
    origin=[0, 10, 0, 1],
    normal=[0, 1, 0, 0],
    radius=15.2,
    description="M2.5 mounting holes at 15.2mm radius"
)
datum_type: DatumType
description: str = ''
direction: List[float] | None = None
name: str
normal: List[float] | None = None
origin: List[float]
radius: float | None = None
tags: List[str]
transform(matrix: Matrix | List[List[float]]) Datum[source]

Return a new Datum transformed by the given 4x4 matrix.

Transforms the datum’s geometric features by applying the given transformation matrix. Points are translated, direction vectors are rotated (but not translated), and the radius is preserved (assuming uniform scaling).

Parameters:

matrix – 4x4 transformation matrix, either as yapCAD Matrix object or as a list of 4 rows (each row is a 4-element list)

Returns:

A new Datum with transformed geometry

Example:

from yapcad.xform import Translation
from yapcad.geom import point

# Original datum at origin
datum = Datum("mount", DatumType.POINT, origin=point(0, 0, 0))

# Transform by translation
T = Translation(point(10, 20, 30))
new_datum = datum.transform(T)
# new_datum.origin is now [10, 20, 30, 1]
x_axis: List[float] | None = None
y_axis: List[float] | None = None
class yapcad.assembly.DatumRegistry[source]

Bases: object

Central registry for datums from DSL files and COTS surrogates.

The registry provides a unified interface to access datums regardless of their source (DSL, STL metadata, surrogate JSON files).

This is implemented as a singleton to ensure consistent state across the application.

Class Methods:

get_datum(source, name): Get a datum by source and name register_source(source_id, datums): Register a source with datums register_datum(source_id, datum): Add a single datum to a source list_sources(): List all registered sources list_datums(source): List all datums in a source clear(): Clear all registered datums (for testing)

Example:

# Load from surrogate JSON
stator = DatumRegistry.get_datum(
    "cots/xh430_surrogate.json",
    "stator_mounting_face"
)

# Register programmatic datums
DatumRegistry.register_datum(
    "LINK_2_3",
    Datum("servo_mount_face", DatumType.PLANE, ...)
)
classmethod add_search_path(path: str) None[source]

Add a directory to search for datum source files.

Parameters:

path – Directory path to add to search paths

classmethod clear() None[source]

Clear all registered datums (useful for testing).

classmethod get_datum(source: str, name: str) Datum[source]

Get a datum by source and name.

Parameters:
  • source – Source identifier - can be: - DSL file path: “scara_arm/scara_arm.dsl” - Surrogate JSON: “cots/xh430_surrogate.json” - Part name: “AXIS3_SERVO_XH430”, “LINK_2_3”

  • name – Datum name within the source e.g., “servo_mount_face”, “stator_face”

Returns:

The Datum object

Raises:

KeyError – If datum not found

classmethod get_datums_for_source(source: str) Dict[str, Datum][source]

Get all datums from a source.

Parameters:

source – Source identifier

Returns:

Dictionary of datum name -> Datum object

classmethod list_datums(source: str) List[str][source]

List all datum names in a source.

Parameters:

source – Source identifier

Returns:

List of datum names

classmethod list_sources() List[str][source]

List all registered source identifiers.

classmethod register_datum(source_id: str, datum: Datum) None[source]

Add a single datum to a source.

Creates the source if it doesn’t exist.

Parameters:
  • source_id – Source identifier

  • datum – The Datum to register

classmethod register_part(part: PartDefinition) None[source]

Register a PartDefinition and all its datums.

Parameters:

part – PartDefinition with datum features

classmethod register_source(source_id: str, datums: Dict[str, Datum], source_type: str = 'programmatic', metadata: Dict[str, Any] | None = None) None[source]

Register a source with its datums.

Parameters:
  • source_id – Unique identifier for this source

  • datums – Dictionary of datum name -> Datum object

  • source_type – Type of source (“json”, “dsl”, “programmatic”)

  • metadata – Optional metadata about the source

class yapcad.assembly.DatumSource(source_id: str, source_type: str, file_path: str | None = None, datums: Dict[str, ~yapcad.assembly.datum.Datum]=<factory>, part_names: Set[str] = <factory>, metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Represents a source of datum definitions.

source_id

Unique identifier for this source (path or name)

Type:

str

source_type

Type of source (“json”, “dsl”, “programmatic”)

Type:

str

file_path

Absolute path to source file (if file-based)

Type:

str | None

datums

Dictionary of datum name -> Datum object

Type:

Dict[str, yapcad.assembly.datum.Datum]

part_names

Set of part names defined in this source

Type:

Set[str]

metadata

Additional source metadata

Type:

Dict[str, Any]

datums: Dict[str, Datum]
file_path: str | None = None
metadata: Dict[str, Any]
part_names: Set[str]
source_id: str
source_type: str
class yapcad.assembly.DatumType(*values)[source]

Bases: Enum

Types of datum features that can be defined on a part.

Datum types correspond to common geometric references used in mechanical design and assembly constraints:

  • POINT: A single point in 3D space

  • AXIS: An infinite line defined by origin + direction

  • PLANE: An infinite plane defined by origin + normal

  • FRAME: A full coordinate frame with origin + 3 orthogonal axes

  • CIRCLE: A circle defined by center + normal + radius

AXIS = 'axis'
CIRCLE = 'circle'
FRAME = 'frame'
PLANE = 'plane'
POINT = 'point'
class yapcad.assembly.FaceToFaceMate(name: str, parent_part: str, parent_datum: str, child_part: str, child_datum: str, parent_source: str | None = None, child_source: str | None = None, constraint: MateType = MateType.COINCIDENT, offset_distance: float = 0.0, rotation_about_normal: float = 0.0, _computed_transform: Any | None = None, _parent_datum_obj: Datum | None = None, _child_datum_obj: Datum | None = None)[source]

Bases: object

Defines how two parts mate via their datum faces.

This is the key abstraction that replaces hardcoded transforms with explicit geometric constraints. The transform is computed automatically from the datum geometry.

Constraint Types:
  • COINCIDENT/FLUSH: Faces touch, normals point in opposite directions

  • CONCENTRIC: Axes are colinear (for cylindrical features)

  • PARALLEL: Faces parallel with optional offset

  • ALIGNED: Faces touch, normals point in same direction

name

Descriptive name for this mate

Type:

str

parent_part

Name of the parent (fixed) part

Type:

str

parent_datum

Datum name on parent part

Type:

str

parent_source

Source file for parent datums (DSL or surrogate JSON)

Type:

str | None

child_part

Name of the child (moving) part

Type:

str

child_datum

Datum name on child part

Type:

str

child_source

Source file for child datums

Type:

str | None

constraint

Constraint type (MateType enum)

Type:

yapcad.assembly.mate.MateType

offset_distance

Optional offset along normal (for OFFSET constraint)

Type:

float

rotation_about_normal

Optional rotation about mating axis (degrees)

Type:

float

child_datum: str
child_part: str
child_source: str | None = None
compute_transform(parent_world_transform: Any | None = None) Any | None[source]

Compute the transform that places child relative to parent.

This is the core algorithm that replaces hardcoded transforms.

Parameters:

parent_world_transform – Optional 4x4 world transform of parent (for computing absolute child position)

Returns:

4x4 numpy array transform, or None if numpy not available

Raises:
constraint: MateType = 'coincident'
invalidate_cache() None[source]

Clear cached transform (call if datums change).

name: str
offset_distance: float = 0.0
parent_datum: str
parent_part: str
parent_source: str | None = None
rotation_about_normal: float = 0.0
validate(tolerance_mm: float = 0.1, tolerance_deg: float = 1.0) MateValidationResult[source]

Validate that the mate constraint is satisfied.

Parameters:
  • tolerance_mm – Linear tolerance in millimeters

  • tolerance_deg – Angular tolerance in degrees

Returns:

MateValidationResult with satisfaction status and errors

class yapcad.assembly.FunctionalRequirement(name: str, part: str, description: str = '', priority: int = 1)[source]

Bases: ABC

Base class for functional requirements.

Functional requirements describe WHAT a part must DO, not HOW to achieve it. The system derives constraints and parameters from these requirements.

Subclasses implement get_implied_constraints() to convert the high-level requirement into low-level constraints that the solver can work with.

name

Unique identifier for this requirement

Type:

str

part

Name of the part that must satisfy this requirement

Type:

str

description

Human-readable description of the requirement

Type:

str

priority

Relative importance (higher = more important)

Type:

int

description: str = ''
get_derived_parameters() List[str][source]

List parameters that can be derived from this requirement.

Returns:

List of parameter names in “part.parameter” format

abstractmethod get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Derive low-level constraints from this requirement.

Parameters:

reference_geometry – Dictionary of available reference geometry

Returns:

List of constraint specifications (dicts that can create Constraints)

name: str
part: str
priority: int = 1
class yapcad.assembly.GeometryType(*values)[source]

Bases: Enum

Types of reference geometry.

CONE = 'cone'
CYLINDER = 'cylinder'
LINE = 'line'
PLANE = 'plane'
POINT = 'point'
SPHERE = 'sphere'
class yapcad.assembly.KinematicConstraint(name: str, constraint_type: KinematicConstraintType, frame_a: str, frame_b: str | None = None, face_a: str | None = None, face_b: str | None = None, axis_a: str | Tuple[float, float, float] = 'z', axis_b: str | Tuple[float, float, float] = 'z', reference_center: Tuple[float, float, float] | None = None, reference_radius: float | None = None, reference_axis: Tuple[float, float, float] | None = None, reference_normal: Tuple[float, float, float] | None = None, pattern_count: int = 0, pattern_radius: float = 0.0, pattern_offset_deg: float = 0.0, tolerance_mm: float = 0.1, tolerance_deg: float = 1.0, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, stl_path_a: str | None = None, stl_path_b: str | None = None, description: str = '', severity: str = 'error', validator: Callable[[Dict[str, Any]], ConstraintEvaluationResult] | None = None)[source]

Bases: object

A constraint that operates on world transforms from kinematic chains.

This constraint type is designed to work with Transform objects (4x4 matrices) from the kinematic chain system, enabling validation of assembly relationships in world coordinates.

name

Unique identifier for this constraint

Type:

str

constraint_type

Type of constraint to evaluate

Type:

yapcad.assembly.kinematic_integration.KinematicConstraintType

frame_a

Name of first frame (part name in kinematic chain)

Type:

str

frame_b

Name of second frame, or None for world reference

Type:

str | None

# Face-based constraint specification
Type:

optional

face_a

Face specification on frame_a. Can be: - “TOP” / “BOTTOM”: Calculated from bounds_a (max_z / min_z face center) - “FRONT” / “BACK”: Calculated from bounds_a (max_y / min_y face center) - “LEFT” / “RIGHT”: Calculated from bounds_a (min_x / max_x face center) - Frame name (e.g., “OUTPUT_SHAFT”, “SUN_INPUT”): Looked up from

world_transforms using key “part_name.frame_name”

When specified, constraint position is taken from face instead of part origin.

Type:

str | None

face_b

Face specification on frame_b (same options as face_a).

Type:

str | None

# Axis specification
Type:

which local axis to use for directional constraints

axis_a

Local axis on frame_a to use (“x”, “y”, “z”, or tuple)

Type:

str | Tuple[float, float, float]

axis_b

Local axis on frame_b to use (“x”, “y”, “z”, or tuple)

Type:

str | Tuple[float, float, float]

# Reference geometry for world-referenced constraints
reference_center

Center point for tangent/radial constraints

Type:

Tuple[float, float, float] | None

reference_radius

Radius for tangent/radial constraints

Type:

float | None

reference_axis

Reference axis direction for parallel/perpendicular

Type:

Tuple[float, float, float] | None

reference_normal

Reference normal for plane constraints

Type:

Tuple[float, float, float] | None

# Pattern parameters
pattern_count

Number of holes in bolt pattern

Type:

int

pattern_radius

Bolt circle radius

Type:

float

pattern_offset_deg

Angular offset between patterns

Type:

float

# Tolerances
tolerance_mm

Linear tolerance in mm (default: 0.1)

Type:

float

tolerance_deg

Angular tolerance in degrees (default: 1.0)

Type:

float

# Metadata
description

Human-readable description

Type:

str

severity

“error”, “warning”, or “info”

Type:

str

# Custom validator
validator

Optional custom validation function

Type:

Callable[[Dict[str, Any]], yapcad.assembly.kinematic_integration.ConstraintEvaluationResult] | None

Example with face-based constraints:

# Clearance measured from servo OUTPUT_SHAFT face to ring SUN_INPUT face
constraint = KinematicConstraint(
    name="servo_ring_interface",
    constraint_type=KinematicConstraintType.AT_DISTANCE,
    frame_a="AXIS2_SERVO_XH430",
    frame_b="AXIS2_RING_HOUSING",
    face_a="OUTPUT_SHAFT",    # Use servo's output shaft frame position
    face_b="SUN_INPUT",       # Use ring's sun input frame position
    reference_radius=0.0,     # Expected distance (touching)
    tolerance_mm=0.5
)

# Contact constraint using standard face names
constraint = KinematicConstraint(
    name="stacked_parts",
    constraint_type=KinematicConstraintType.COINCIDENT,
    frame_a="GEARBOX_TOP",
    frame_b="GEARBOX_BOTTOM",
    face_a="BOTTOM",          # Bottom face of top gearbox (from bounds_a)
    face_b="TOP",             # Top face of bottom gearbox (from bounds_b)
    bounds_a=((-30, -30, 0), (30, 30, 50)),
    bounds_b=((-30, -30, 0), (30, 30, 50)),
    tolerance_mm=0.1
)
axis_a: str | Tuple[float, float, float] = 'z'
axis_b: str | Tuple[float, float, float] = 'z'
bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None
bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None
constraint_type: KinematicConstraintType
description: str = ''
evaluate(world_transforms: Dict[str, Any]) ConstraintEvaluationResult[source]

Evaluate this constraint given world transforms of all parts.

Parameters:

world_transforms – Dictionary mapping part names to Transform objects or 4x4 numpy arrays

Returns:

ConstraintEvaluationResult with evaluation status and error metrics

face_a: str | None = None
face_b: str | None = None
frame_a: str
frame_b: str | None = None
name: str
pattern_count: int = 0
pattern_offset_deg: float = 0.0
pattern_radius: float = 0.0
reference_axis: Tuple[float, float, float] | None = None
reference_center: Tuple[float, float, float] | None = None
reference_normal: Tuple[float, float, float] | None = None
reference_radius: float | None = None
severity: str = 'error'
stl_path_a: str | None = None
stl_path_b: str | None = None
tolerance_deg: float = 1.0
tolerance_mm: float = 0.1
validator: Callable[[Dict[str, Any]], ConstraintEvaluationResult] | None = None
class yapcad.assembly.KinematicConstraintType(*values)[source]

Bases: Enum

Constraint types for kinematic chain validation.

These constraints operate on world-space transforms from kinematic chains and validate geometric relationships between frames.

AT_DISTANCE = 'at_distance'
BOLT_PATTERN = 'bolt_pattern'
COINCIDENT = 'coincident'
CONCENTRIC = 'concentric'
CUSTOM = 'custom'
MIN_DISTANCE = 'min_distance'
NO_OVERLAP = 'no_overlap'
PARALLEL = 'parallel'
PERPENDICULAR = 'perpendicular'
TANGENT = 'tangent'
Z_STACK_CLEARANCE = 'z_stack_clearance'
class yapcad.assembly.Mate(name: str, mate_type: MateType, part_a: str, datum_a: str, part_b: str, datum_b: str, offset: float = 0.0, angle: float = 0.0, axis: List[float] = <factory>, secondary_axis: List[float] = <factory>, limits: MateLimits | None = None, dynamics: MateDynamics | None = None, coupling_ratio: float = 1.0, coupling_offset: float = 0.0, coupling_reverse: bool = False, coupling_pitch: float | None = None, metadata: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Kinematic constraint defining relationship between two assembly components.

A mate constrains the relative position and/or orientation of two parts by removing degrees of freedom (DOF). Mates can be purely geometric (COINCIDENT, PARALLEL) or define motion primitives (REVOLUTE, PRISMATIC).

The mate references two parts via named datum features (points, axes, planes, or surfaces). The constraint solver uses these datums to compute the relative transformation that satisfies the mate.

name

Human-readable identifier (e.g., “shoulder_pitch”, “wheel_axle”)

Type:

str

mate_type

Type of constraint from MateType enum

Type:

yapcad.assembly.mate.MateType

part_a

Identifier of first (parent/base) component

Type:

str

datum_a

Named datum feature on part_a (e.g., “mounting_axis”)

Type:

str

part_b

Identifier of second (child/moving) component

Type:

str

datum_b

Named datum feature on part_b (e.g., “joint_axis”)

Type:

str

offset

Distance offset between datums (mm, used by DISTANCE mate)

Type:

float

angle

Angular offset between datums (radians, used by ANGLE mate)

Type:

float

axis

Primary motion axis for joints (e.g., [0,0,1,0] for Z-axis)

Type:

List[float]

secondary_axis

Secondary reference axis for compound joints

Type:

List[float]

limits

Optional position/velocity/effort limits

Type:

yapcad.assembly.mate.MateLimits | None

dynamics

Optional friction/damping/stiffness parameters

Type:

yapcad.assembly.mate.MateDynamics | None

coupling_ratio

Motion ratio for coupled mates (GEAR, SCREW)

Type:

float

coupling_offset

Phase offset for coupled motion (radians or mm)

Type:

float

coupling_reverse

Reverse direction of coupled motion

Type:

bool

coupling_pitch

Thread pitch for SCREW mates (mm per revolution)

Type:

float | None

metadata

Additional application-specific data

Type:

Dict[str, Any]

Example

# Revolute joint for robot shoulder shoulder_mate = Mate(

name=”shoulder_pitch”, mate_type=MateType.REVOLUTE, part_a=”robot_base”, datum_a=”shoulder_mount_axis”, part_b=”upper_arm”, datum_b=”arm_root_axis”, offset=0.0, angle=0.0, axis=[0, 1, 0, 0], # Y-axis rotation limits=MateLimits(

min_value=-math.pi/2, max_value=math.pi/2, max_velocity=1.5, max_effort=100.0

), dynamics=MateDynamics(

friction_static=0.05, damping=0.2

)

)

# Gear coupling between two shafts gear_mate = Mate(

name=”gear_1_to_2”, mate_type=MateType.GEAR, part_a=”gear_1”, datum_a=”gear_1_axis”, part_b=”gear_2”, datum_b=”gear_2_axis”, coupling_ratio=2.5, # gear_2 rotates 2.5x for each rotation of gear_1 coupling_reverse=True # Opposite rotation direction

)

# Check constraint properties dof = shoulder_mate.degrees_of_freedom() # Returns 1 result = shoulder_mate.evaluate()

angle: float = 0.0
axis: List[float]
compute_coupled_motion(driver_position: float) float[source]

Compute driven position from driver position for coupled mates.

For coupled mates (GEAR, SCREW, RACK_PINION), compute the position of the driven component given the position of the driving component.

Parameters:

driver_position – Position of driving component (mm or radians)

Returns:

Position of driven component (mm or radians)

Raises:

ValueError – If mate is not a coupled type

Example

# Gear mate with 3:1 ratio gear_mate = Mate(mate_type=MateType.GEAR, coupling_ratio=3.0, …) driven_angle = gear_mate.compute_coupled_motion(1.0) # Returns 3.0

# Screw mate with 2mm pitch screw_mate = Mate(mate_type=MateType.SCREW, coupling_pitch=2.0, …) linear_pos = screw_mate.compute_coupled_motion(math.pi) # Returns 2.0

coupling_offset: float = 0.0
coupling_pitch: float | None = None
coupling_ratio: float = 1.0
coupling_reverse: bool = False
property datum1: str

Alias for datum_a (datum on first component).

property datum2: str

Alias for datum_b (datum on second component).

datum_a: str
datum_b: str
degrees_of_freedom() int[source]

Return the number of degrees of freedom (DOF) remaining after this mate.

An unconstrained rigid body has 6 DOF (3 translational + 3 rotational). Each mate removes one or more DOF. This method returns how many DOF remain after applying this mate constraint.

Returns:

Number of remaining DOF (0-6)

DOF by mate type:

RIGID: 0 DOF (fully constrained) REVOLUTE, PRISMATIC, SCREW: 1 DOF CYLINDRICAL, PIN_SLOT, UNIVERSAL: 2 DOF SPHERICAL, PLANAR: 3 DOF COINCIDENT: depends on geometry (1-3 DOF removed) CONCENTRIC: 2 DOF (translation along + rotation about axis) DISTANCE, ANGLE, PARALLEL, PERPENDICULAR, TANGENT: varies GEAR, RACK_PINION, CAM: coupled (creates relationship, not DOF)

Example

shoulder = Mate(name=”shoulder”, mate_type=MateType.REVOLUTE, …) dof = shoulder.degrees_of_freedom() # Returns 1

dynamics: MateDynamics | None = None
evaluate(current_position: float | None = None, current_velocity: float | None = None, current_effort: float | None = None) Dict[str, Any][source]

Evaluate constraint satisfaction and compute error metrics.

This method checks if the mate is satisfied given current state, computes constraint violation errors, and validates limits.

Parameters:
  • current_position – Current position (mm) or angle (radians) of primary DOF

  • current_velocity – Current velocity (mm/s or rad/s)

  • current_effort – Current force (N) or torque (N*m)

Returns:

  • dof_remaining: Number of DOF after constraint (int)

  • error: Constraint violation error magnitude (float)

  • satisfied: True if constraint is satisfied within tolerance (bool)

  • limit_violations: List of violated limits (List[str])

  • dynamics_active: True if dynamics parameters are defined (bool)

Return type:

Dictionary containing

Example

result = mate.evaluate(current_position=0.5, current_velocity=1.2) if not result[‘satisfied’]:

print(f”Constraint error: {result[‘error’]}”)

if result[‘limit_violations’]:

print(f”Limit violations: {result[‘limit_violations’]}”)

classmethod from_dict(data: Dict[str, Any]) Mate[source]

Deserialize mate from dictionary.

Parameters:

data – Dictionary representation of mate

Returns:

Mate instance

Example

import json mate_dict = json.load(f) mate = Mate.from_dict(mate_dict)

is_coupled() bool[source]

Return True if this mate defines a coupled motion relationship.

Coupled mates (GEAR, RACK_PINION, SCREW, CAM) create motion relationships between components rather than removing DOF.

Returns:

True if mate is a coupled motion type

Example

if mate.is_coupled():

print(f”Coupling ratio: {mate.coupling_ratio}”)

limits: MateLimits | None = None
mate_type: MateType
metadata: Dict[str, Any]
name: str
offset: float = 0.0
property part1: str

Alias for part_a (first/parent component).

property part2: str

Alias for part_b (second/child component).

part_a: str
part_b: str
secondary_axis: List[float]
to_dict() Dict[str, Any][source]

Serialize mate to dictionary for export to JSON, YAML, or other formats.

Returns:

Dictionary representation of mate with all parameters

Example

mate_dict = mate.to_dict() import json json.dump(mate_dict, f, indent=2)

validate(datum_a: Datum, datum_b: Datum) List[str][source]

Validate that this mate is compatible with the given datums.

Checks that the mate type is appropriate for the datum types. For example, CONCENTRIC requires two AXIS or CIRCLE datums, COINCIDENT can work with POINT, PLANE, or CIRCLE datums.

Parameters:
  • datum_a – First datum feature

  • datum_b – Second datum feature

Returns:

List of validation error messages (empty if valid)

Example

>>> mate = Mate("test", MateType.CONCENTRIC, ...)
>>> issues = mate.validate(axis_datum, point_datum)
>>> if issues:
...     print(f"Invalid: {issues}")
class yapcad.assembly.MateDynamics(friction_static: float = 0.0, friction_kinetic: float = 0.0, friction_viscous: float = 0.0, damping: float = 0.0, stiffness: float = 0.0, rest_position: float = 0.0)[source]

Bases: object

Physical dynamics parameters for motion simulation.

Defines friction, damping, and compliance for realistic motion behavior. Used for physics simulation, animation, and digital twin applications.

Friction types:
  • Static (Coulomb): Resistance to start motion

  • Kinetic (dynamic): Resistance during motion

  • Viscous: Velocity-dependent resistance (linear with speed)

Typical values:
  • Lubricated metal bearings: friction_static=0.01-0.05

  • Dry metal: friction_static=0.15-0.30

  • Typical joint damping: 0.1-1.0

  • Stiffness: 0 for rigid, >0 for compliant/springy

friction_static

Static (Coulomb) friction coefficient

Type:

float

friction_kinetic

Kinetic/dynamic friction coefficient

Type:

float

friction_viscous

Viscous friction coefficient (velocity-dependent)

Type:

float

damping

Viscous damping coefficient (energy dissipation)

Type:

float

stiffness

Spring stiffness for compliant joints (N/m or N*m/rad)

Type:

float

rest_position

Equilibrium position for spring (mm or radians)

Type:

float

Example

# Low-friction bearing with slight damping dynamics = MateDynamics(

friction_static=0.02, friction_kinetic=0.015, damping=0.5

)

# Compliant joint with spring return dynamics = MateDynamics(

stiffness=1000.0, # Spring constant rest_position=0.0, # Returns to center damping=50.0 # Energy dissipation

)

damping: float = 0.0
friction_kinetic: float = 0.0
friction_static: float = 0.0
friction_viscous: float = 0.0
rest_position: float = 0.0
stiffness: float = 0.0
class yapcad.assembly.MateLimits(min_value: float | None = None, max_value: float | None = None, min_velocity: float | None = None, max_velocity: float | None = None, min_effort: float | None = None, max_effort: float | None = None, min_secondary: float | None = None, max_secondary: float | None = None, limit_stiffness: float = 1000000.0, limit_damping: float = 1000.0, restitution: float = 0.0)[source]

Bases: object

Position, velocity, and effort limits for mate degrees of freedom.

Used for both mechanical design (hard stops) and motion simulation (soft limits, compliance). All limits are optional; None means unlimited.

For revolute joints:
  • min_value/max_value in radians

  • max_velocity in rad/s

  • max_effort in N*m (torque)

For prismatic joints:
  • min_value/max_value in mm

  • max_velocity in mm/s

  • max_effort in N (force)

For multi-DOF joints (cylindrical, spherical, planar):
  • Use min_value/max_value for primary DOF

  • Use min_secondary/max_secondary for secondary DOF

min_value

Minimum position (mm) or angle (radians) for primary DOF

Type:

float | None

max_value

Maximum position (mm) or angle (radians) for primary DOF

Type:

float | None

min_velocity

Minimum velocity (can be negative for reversal)

Type:

float | None

max_velocity

Maximum velocity (mm/s or rad/s)

Type:

float | None

min_effort

Minimum force/torque (N or N*m)

Type:

float | None

max_effort

Maximum force/torque (N or N*m)

Type:

float | None

min_secondary

Minimum value for secondary DOF (multi-DOF joints)

Type:

float | None

max_secondary

Maximum value for secondary DOF (multi-DOF joints)

Type:

float | None

limit_stiffness

Stiffness of soft limit (N/m or N*m/rad), 1e6 = hard

Type:

float

limit_damping

Damping at limit stop (N*s/m or N*m*s/rad)

Type:

float

restitution

Bounce coefficient at limits (0.0 = no bounce, 1.0 = perfect)

Type:

float

Example

# Revolute joint with ±90 degree limits limits = MateLimits(

min_value=-math.pi/2, max_value=math.pi/2, max_velocity=2.0, # 2 rad/s max_effort=50.0 # 50 N*m torque limit

)

# Prismatic joint (linear slide) with hard stops limits = MateLimits(

min_value=0.0, max_value=100.0, # 100mm travel max_velocity=50.0, # 50 mm/s limit_stiffness=1e6 # Very stiff stop

)

limit_damping: float = 1000.0
limit_stiffness: float = 1000000.0
max_effort: float | None = None
max_secondary: float | None = None
max_value: float | None = None
max_velocity: float | None = None
min_effort: float | None = None
min_secondary: float | None = None
min_value: float | None = None
min_velocity: float | None = None
restitution: float = 0.0
class yapcad.assembly.MateType(*values)[source]

Bases: Enum

Kinematic mate types based on industry CAD standards.

Each mate type removes specific degrees of freedom (DOF) from a rigid body. An unconstrained body has 6 DOF: 3 translational + 3 rotational.

Basic Mates (geometric constraints):

COINCIDENT: Points, edges, or faces share same location (1-3 DOF removed) CONCENTRIC: Cylindrical axes are colinear (4 DOF removed: 2t + 2r) PARALLEL: Directions remain parallel (2 DOF removed) PERPENDICULAR: Directions maintain 90-degree angle (1 DOF removed) TANGENT: Surfaces remain tangent (1 DOF removed) DISTANCE: Fixed offset between datums (1 DOF removed) ANGLE: Fixed angular relationship (1 DOF removed)

Standard Joints (motion primitives):

RIGID: No relative motion (6 DOF removed, 0 remaining) REVOLUTE: Rotation about single axis (5 DOF removed, 1 remaining) PRISMATIC: Translation along single axis (5 DOF removed, 1 remaining) CYLINDRICAL: Rotation + translation on same axis (4 DOF removed, 2 remaining) SPHERICAL: Ball-and-socket, rotation only (3 DOF removed, 3 remaining) PLANAR: Two translation + one rotation in plane (3 DOF removed, 3 remaining)

Compound Joints:

PIN_SLOT: Translation along slot + rotation about pin (4 DOF removed, 2 remaining) UNIVERSAL: Two perpendicular rotation axes (4 DOF removed, 2 remaining) SCREW: Coupled rotation and translation (5 DOF removed, 1 coupled)

Coupled Mates (motion relationships):

GEAR: Coupled rotation with ratio (no DOF removed, creates relationship) RACK_PINION: Couples rotation to translation (no DOF removed, creates relationship) CAM: Follower constrained to cam profile path (path-following) SLOT: Linear sliding constraint along trajectory (varies by slot type)

References

  • SolidWorks Mates Overview (2025)

  • Fusion 360 Joint Types

  • CATIA DMU Kinematics

  • Siemens NX Motion Joints

  • PTC Creo Mechanism Design

ANGLE = 'angle'
CAM = 'cam'
COINCIDENT = 'coincident'
CONCENTRIC = 'concentric'
CYLINDRICAL = 'cylindrical'
DISTANCE = 'distance'
GEAR = 'gear'
PARALLEL = 'parallel'
PERPENDICULAR = 'perpendicular'
PIN_SLOT = 'pin_slot'
PLANAR = 'planar'
PRISMATIC = 'prismatic'
RACK_PINION = 'rack_pinion'
REVOLUTE = 'revolute'
RIGID = 'rigid'
SCREW = 'screw'
SLOT = 'slot'
SPHERICAL = 'spherical'
TANGENT = 'tangent'
UNIVERSAL = 'universal'
class yapcad.assembly.MateValidationResult(satisfied: bool, error_distance: float = 0.0, error_angle: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]

Bases: object

Result of validating a mate constraint.

satisfied

True if constraint is satisfied within tolerance

Type:

bool

error_distance

Position error in mm

Type:

float

error_angle

Angular error in degrees

Type:

float

error_message

Human-readable description

Type:

str

details

Additional validation information

Type:

Dict[str, Any]

details: Dict[str, Any]
error_angle: float = 0.0
error_distance: float = 0.0
error_message: str = ''
satisfied: bool
class yapcad.assembly.ParallelAxesRequirement(name: str, part: str, description: str = '', priority: int = 1, axis_a: str = '', axis_b: str = '', allow_opposite: bool = True)[source]

Bases: FunctionalRequirement

Two axes must be parallel.

This requirement specifies that two datum axes (on same or different parts) must be parallel. Common use: pivot axis parallel to motor axis for clean suspension motion.

axis_a

First axis in “part.datum” format

Type:

str

axis_b

Second axis in “part.datum” format

Type:

str

allow_opposite

If True, axes can point in opposite directions

Type:

bool

Example

>>> parallel = ParallelAxesRequirement(
...     name="pivot_motor_parallel",
...     part="wheel_arm",  # Primary part
...     axis_a="wheel_arm.pivot_bore_axis",
...     axis_b="motor.motor_axis",
... )
allow_opposite: bool = True
axis_a: str = ''
axis_b: str = ''
get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Generate parallel constraint.

class yapcad.assembly.PartDefinition(name: str, datums: Dict[str, ~yapcad.assembly.datum.Datum]=<factory>, geometry_source: str | None = None, is_printable: bool = True, material: str = 'PETG', description: str = '')[source]

Bases: object

Definition of a part with its datum features.

A PartDefinition extends the concept of a part to include explicit geometric datum features that can be used for assembly constraints. This enables declarative mate definitions (FLUSH, CONCENTRIC, etc.) that reference named datums rather than requiring manual transform calculations.

name

Unique identifier for this part

Type:

str

datums

Dictionary mapping datum names to Datum objects

Type:

Dict[str, yapcad.assembly.datum.Datum]

geometry_source

Path to STL file, DSL command, or other geometry

Type:

str | None

is_printable

Whether this part is 3D printed (vs. COTS)

Type:

bool

material

Material specification (e.g., “PETG”, “Aluminum”)

Type:

str

description

Human-readable description of part function

Type:

str

Example:

from yapcad.assembly.datum import PartDefinition, Datum, DatumType
from yapcad.geom import point

# Define a wheel bracket part
bracket = PartDefinition(
    name="WHEEL_BRACKET",
    geometry_source="parts/bracket.dsl",
    is_printable=True,
    material="PETG",
    description="Mounting bracket for drive wheel motor"
)

# Add datum for motor mounting face
bracket.add_datum(Datum(
    name="motor_interface",
    datum_type=DatumType.PLANE,
    origin=point(0, 0, 0),
    normal=point(0, 0, 1, 0),
    description="Motor mounting face normal to +Z"
))

# Add datum for motor bore axis
bracket.add_datum(Datum(
    name="bore_axis",
    datum_type=DatumType.AXIS,
    origin=point(0, 0, 0),
    direction=point(0, 0, 1, 0),
    description="Motor shaft bore along Z axis"
))

# Validate all datums
issues = bracket.validate_datums()
if issues:
    print("Warning:", issues)
add_datum(datum: Datum) PartDefinition[source]

Add a datum feature to this part.

Parameters:

datum – The Datum object to add

Returns:

Self (for method chaining)

Raises:

ValueError – If a datum with the same name already exists

Example:

bracket = PartDefinition("BRACKET")
bracket.add_datum(Datum("face", DatumType.PLANE, ...))
bracket.add_datum(Datum("axis", DatumType.AXIS, ...))
datums: Dict[str, Datum]
description: str = ''
geometry_source: str | None = None
get_datum(name: str) Datum[source]

Get a datum by name.

Parameters:

name – Name of the datum to retrieve

Returns:

The Datum object

Raises:

KeyError – If no datum with that name exists

Example:

bracket = PartDefinition("BRACKET")
bracket.add_datum(Datum("mount_face", ...))
face = bracket.get_datum("mount_face")
is_printable: bool = True
material: str = 'PETG'
name: str
validate_datums() List[str][source]

Validate all datum definitions, returning list of issues.

Performs validation checks on all datums to detect potential errors:
  • Duplicate definitions (same origin and type)

  • Overlapping features that might indicate copy-paste errors

Returns:

List of warning/error messages (empty if all valid)

Example:

bracket = PartDefinition("BRACKET")
bracket.add_datum(Datum("face1", DatumType.PLANE, origin=[0,0,0], ...))
bracket.add_datum(Datum("face2", DatumType.PLANE, origin=[0,0,0], ...))

issues = bracket.validate_datums()
# issues will contain warning about duplicate origin
class yapcad.assembly.ReachRequirement(name: str, part: str, description: str = '', priority: int = 1, end_effector: str = '', base: str = '', min_reach: float = 0.0, max_reach: float = 100.0)[source]

Bases: FunctionalRequirement

End effector must have specified reach envelope.

For serial manipulators (SCARA arms, etc.), specifies the minimum and maximum reach from the base.

end_effector

Datum name on end effector part (e.g., “tool_point”)

Type:

str

base

Datum name on base part (e.g., “base_center”)

Type:

str

min_reach

Minimum distance from base (mm)

Type:

float

max_reach

Maximum distance from base (mm)

Type:

float

Example

>>> reach = ReachRequirement(
...     name="scara_workspace",
...     part="wrist",
...     end_effector="wrist.tool_point",
...     base="tower.axis1_center",
...     min_reach=50.0,
...     max_reach=200.0,
... )
base: str = ''
end_effector: str = ''
get_derived_parameters() List[str][source]

List parameters that can be derived from this requirement.

Returns:

List of parameter names in “part.parameter” format

get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Reach implies link length constraints.

max_reach: float = 100.0
min_reach: float = 0.0
class yapcad.assembly.ReferenceGeometry(name: str, geometry_type: str, center: Tuple[float, float, float] | None = None, axis: Tuple[float, float, float] | None = None, radius: float | None = None, normal: Tuple[float, float, float] | None = None, inner: bool = False)[source]

Bases: object

Fixed reference geometry that parts must relate to.

Reference geometry defines the environment or constraints that the assembly operates within. Examples include tube walls, mounting surfaces, and clearance envelopes.

name

Unique identifier for this geometry

Type:

str

geometry_type

Type of geometry (cylinder, plane, etc.)

Type:

str

center

Center point (x, y, z) for most geometry types

Type:

Tuple[float, float, float] | None

axis

Direction vector for cylinders, cones, lines

Type:

Tuple[float, float, float] | None

radius

Radius for cylinders, spheres, cones

Type:

float | None

normal

Normal vector for planes

Type:

Tuple[float, float, float] | None

inner

True if surface is inner (e.g., inner wall of tube)

Type:

bool

Example

>>> # 350mm ID tube
>>> tube = ReferenceGeometry(
...     name="tube_inner_wall",
...     geometry_type="cylinder",
...     center=(0, 0, 0),
...     axis=(0, 0, 1),
...     radius=175.0,
...     inner=True,
... )
axis: Tuple[float, float, float] | None = None
center: Tuple[float, float, float] | None = None
geometry_type: str
get_normal_at(point: Tuple[float, float, float]) Tuple[float, float, float][source]

Get the surface normal at a given point.

For cylinders: radial direction from axis. For planes: constant normal. For spheres: radial direction from center.

Returns:

Unit normal vector (x, y, z)

get_surface_point(theta: float = 0.0, z: float = 0.0) Tuple[float, float, float][source]

Get a point on the surface at given parameters.

For cylinders: theta is angle in radians, z is height along axis. For planes: theta and z are ignored. For spheres: theta is azimuth, z is elevation (radians).

Returns:

Point (x, y, z) on the surface

inner: bool = False
name: str
normal: Tuple[float, float, float] | None = None
radius: float | None = None
class yapcad.assembly.RollRequirement(name: str, part: str, description: str = '', priority: int = 1, roll_direction: str = 'along_tube_axis', axis_datum: str = 'rotation_axis')[source]

Bases: FunctionalRequirement

Part must roll in a specified direction.

This requirement specifies that a rotating part (wheel, roller, etc.) must roll in a specific direction. This IMPLIES that the rotation axis must be perpendicular to the roll direction.

For a wheel rolling along the Z-axis (tube axis): - Roll direction: (0, 0, 1) - Rotation axis must be tangent (perpendicular to both Z and radial)

roll_direction

Direction of travel - “along_tube_axis” or “+z” for vertical tube - “(x, y, z)” tuple for custom direction

Type:

str

axis_datum

Name of rotation axis datum on part

Type:

str

Example

>>> roll = RollRequirement(
...     name="wheel_rolls_z",
...     part="motor",
...     roll_direction="along_tube_axis",
...     axis_datum="motor_axis",
... )
axis_datum: str = 'rotation_axis'
get_implied_constraints(reference_geometry: Dict[str, ReferenceGeometry]) List[Dict[str, Any]][source]

Rolling implies rotation axis perpendicular to roll direction.

roll_direction: str = 'along_tube_axis'
class yapcad.assembly.SolveResult(success: bool, derived: Dict[str, float]=<factory>, transforms: Dict[str, ~typing.Any]=<factory>, constraints_generated: Dict[str, ~typing.Any]]=<factory>, validation: Any | None = None, errors: List[str] = <factory>, warnings: List[str] = <factory>)[source]

Bases: object

Result of solving an AssemblyIntent.

Contains derived parameters, computed transforms, and validation status.

success

True if all requirements could be satisfied

Type:

bool

derived

Dictionary of derived parameter values

Type:

Dict[str, float]

transforms

Dictionary of computed transforms (part name -> 4x4 matrix)

Type:

Dict[str, Any]

constraints_generated

List of constraint specifications generated

Type:

List[Dict[str, Any]]

validation

Validation result for all constraints

Type:

Any | None

errors

List of error messages if solve failed

Type:

List[str]

warnings

List of warning messages

Type:

List[str]

constraints_generated: List[Dict[str, Any]]
derived: Dict[str, float]
errors: List[str]
report() str[source]

Generate human-readable solve report.

success: bool
transforms: Dict[str, Any]
validation: Any | None = None
warnings: List[str]
class yapcad.assembly.ValidationReport(is_valid: bool, total_constraints: int = 0, passed_count: int = 0, failed_count: int = 0, warning_count: int = 0, constraint_results: Dict[str, ~yapcad.assembly.kinematic_integration.ConstraintEvaluationResult]=<factory>, failed_constraints: List[str] = <factory>, warnings: List[str] = <factory>, info: List[str] = <factory>)[source]

Bases: object

Comprehensive assembly validation report.

is_valid

True if all error-severity constraints pass

Type:

bool

total_constraints

Total number of constraints evaluated

Type:

int

passed_count

Number of constraints that passed

Type:

int

failed_count

Number of constraints that failed

Type:

int

warning_count

Number of warning-severity constraints that failed

Type:

int

constraint_results

Mapping of constraint name to evaluation result

Type:

Dict[str, yapcad.assembly.kinematic_integration.ConstraintEvaluationResult]

failed_constraints

List of names of failed constraints

Type:

List[str]

warnings

List of warning messages

Type:

List[str]

info

List of informational messages

Type:

List[str]

constraint_results: Dict[str, ConstraintEvaluationResult]
detailed_report() str[source]

Generate detailed multi-line report.

failed_constraints: List[str]
failed_count: int = 0
info: List[str]
is_valid: bool
passed_count: int = 0
total_constraints: int = 0
warning_count: int = 0
warnings: List[str]
yapcad.assembly.angle_between_vectors(v1: Tuple[float, float, float], v2: Tuple[float, float, float]) float[source]

Calculate angle between two vectors in degrees.

Parameters:
  • v1 – First vector (x, y, z)

  • v2 – Second vector (x, y, z)

Returns:

Angle in degrees [0, 180]

Example

>>> angle_between_vectors((1, 0, 0), (0, 1, 0))
90.0
>>> angle_between_vectors((1, 0, 0), (1, 0, 0))
0.0
yapcad.assembly.check_bolt_circle_alignment(bolt_circle_a: Datum, bolt_circle_b: Datum, hole_count: int, tolerance_mm: float = 0.1, angular_offset_deg: float = 0.0) CoincidentResult[source]

Check if two bolt circles align for proper mating.

This validates that mounting hole patterns match for servo horn to gear hub interfaces, motor mounts, etc.

Parameters:
  • bolt_circle_a – First bolt circle datum (e.g., servo horn)

  • bolt_circle_b – Second bolt circle datum (e.g., sun gear hub)

  • hole_count – Number of holes in each pattern

  • tolerance_mm – Position tolerance for hole centers

  • angular_offset_deg – Expected angular offset between patterns (e.g., 45 degrees)

Returns:

CoincidentResult with alignment status

Example

>>> # XH540 servo horn (4 holes at 45 deg offset) to sun gear
>>> result = check_bolt_circle_alignment(
...     horn_bolt_circle,
...     sun_gear_bolt_circle,
...     hole_count=4,
...     angular_offset_deg=45.0
... )
yapcad.assembly.compute_concentric_transform(parent_datum: Datum, child_datum: Datum, offset: float = 0.0) Any[source]

Compute transform to make child axis colinear with parent axis.

For CONCENTRIC mates, the axes must be colinear but origins can differ.

Parameters:
  • parent_datum – Parent axis datum

  • child_datum – Child axis datum

  • offset – Offset along axis direction

Returns:

4x4 numpy transform matrix

yapcad.assembly.compute_flush_transform(parent_datum: Datum, child_datum: Datum, offset: float = 0.0, rotation_deg: float = 0.0, flip_normal: bool = True) Any[source]

Compute transform to make child flush with parent.

The result is a transform T such that: - child_datum.origin moves to parent_datum.origin (+ offset along normal) - child_datum.normal aligns with parent_datum.normal

(opposite if flip_normal=True for face-to-face contact)

Uses Rodrigues’ rotation formula for efficient axis-angle rotation.

Parameters:
  • parent_datum – Parent datum (target)

  • child_datum – Child datum (to be transformed)

  • offset – Optional offset along parent normal

  • rotation_deg – Optional rotation about mating axis

  • flip_normal – If True, child normal opposes parent (face-to-face)

Returns:

4x4 numpy transform matrix

yapcad.assembly.compute_parallel_transform(parent_datum: Datum, child_datum: Datum, offset: float = 0.0) Any[source]

Compute transform to make child parallel to parent.

For PARALLEL mates, normals align but origins don’t necessarily coincide.

Parameters:
  • parent_datum – Parent plane datum

  • child_datum – Child plane datum

  • offset – Offset along normal direction

Returns:

4x4 numpy transform matrix

yapcad.assembly.create_axis_mate(name: str, parent_part: str, parent_datum: str, child_part: str, child_datum: str, parent_source: str | None = None, child_source: str | None = None, offset: float = 0.0) FaceToFaceMate[source]

Create a concentric (axis-aligned) mate.

Convenience function for coaxial alignment.

Parameters:
  • name – Mate name

  • parent_part – Parent part name

  • parent_datum – Parent axis datum name

  • child_part – Child part name

  • child_datum – Child axis datum name

  • parent_source – Optional source file for parent

  • child_source – Optional source file for child

  • offset – Optional offset along axis

Returns:

Configured FaceToFaceMate

yapcad.assembly.create_coincident_constraint(name: str, frame_a: str, frame_b: str | None = None, reference_point: Tuple[float, float, float] | None = None, tolerance_mm: float = 0.1, description: str = '') KinematicConstraint[source]

Create a coincident constraint (origins at same location).

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name (or None for world reference)

  • reference_point – World reference point (if frame_b is None)

  • tolerance_mm – Distance tolerance in mm

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.create_coincident_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, offset: float = 0.0) Mate[source]

Convenience function to create a COINCIDENT mate.

A COINCIDENT mate constrains two features to occupy the same location: - Points share the same position - Planes are coplanar - Bolt circles align (centers coincide)

Parameters:
  • name – Descriptive name (e.g., “horn_to_sun_gear”)

  • part_a – First component identifier

  • datum_a – Datum on first component

  • part_b – Second component identifier

  • datum_b – Datum on second component

  • offset – Optional offset distance along normal (default: 0)

Returns:

Mate configured as COINCIDENT constraint

Example

>>> # Align servo horn bolt holes with sun gear bolt holes
>>> mate = create_coincident_mate(
...     name="horn_bolt_alignment",
...     part_a="xh540_servo",
...     datum_a="horn_bolt_circle",
...     part_b="axis1_sun_gear",
...     datum_b="hub_bolt_circle"
... )
yapcad.assembly.create_face_mate(name: str, parent_part: str, parent_datum: str, child_part: str, child_datum: str, parent_source: str | None = None, child_source: str | None = None, offset: float = 0.0, rotation: float = 0.0) FaceToFaceMate[source]

Create a face-to-face (COINCIDENT) mate.

Convenience function for the most common mate type.

Parameters:
  • name – Mate name

  • parent_part – Parent part name

  • parent_datum – Parent datum name

  • child_part – Child part name

  • child_datum – Child datum name

  • parent_source – Optional source file for parent

  • child_source – Optional source file for child

  • offset – Optional offset along normal

  • rotation – Optional rotation about normal (degrees)

Returns:

Configured FaceToFaceMate

yapcad.assembly.create_gear_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, ratio: float, reverse: bool = False) Mate[source]

Convenience function to create a gear coupling mate.

Couples rotation of two components with a fixed ratio. The ratio is defined as: ratio = driven_rotations / driver_rotations

Parameters:
  • name – Descriptive name (e.g., “gear_1_to_2”, “pulley_coupling”)

  • part_a – Driver (input) component identifier

  • datum_a – Rotation axis on driver

  • part_b – Driven (output) component identifier

  • datum_b – Rotation axis on driven

  • ratio – Motion ratio (driven_rotations / driver_rotations)

  • reverse – If True, gears rotate in opposite directions

Returns:

Mate configured as gear coupling

Example

# 3:1 reduction gearbox (output rotates 1/3 speed of input) gearbox = create_gear_mate(

name=”motor_to_output”, part_a=”motor_shaft”, datum_a=”motor_axis”, part_b=”output_shaft”, datum_b=”output_axis”, ratio=1.0/3.0, reverse=False

)

yapcad.assembly.create_min_distance_constraint(name: str, frame_a: str, frame_b: str, min_distance: float, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, description: str = '') KinematicConstraint[source]

Create a minimum distance constraint.

Parts must be at least min_distance mm apart. If bounding boxes are provided, uses them for more accurate separation calculation.

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name

  • min_distance – Minimum required separation in mm

  • bounds_a – Optional bounding box of part A

  • bounds_b – Optional bounding box of part B

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.create_no_overlap_constraint(name: str, frame_a: str, frame_b: str, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]] | None = None, stl_path_a: str | None = None, stl_path_b: str | None = None, description: str = '') KinematicConstraint[source]

Create a no-overlap constraint.

When STL paths are provided, uses accurate mesh-based collision detection. Otherwise uses axis-aligned bounding boxes (fast but conservative).

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name

  • bounds_a – Bounding box of part A ((min_x, min_y, min_z), (max_x, max_y, max_z))

  • bounds_b – Bounding box of part B

  • stl_path_a – Path to STL file for part A (enables mesh collision)

  • stl_path_b – Path to STL file for part B (enables mesh collision)

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

Example

>>> # AABB-based collision (fast, conservative)
>>> constraint = create_no_overlap_constraint(
...     "servo_ring_no_overlap",
...     "SERVO",
...     "RING_HOUSING",
...     bounds_a=((-15, -25, -17), (15, 25, 17)),
...     bounds_b=((-30, -30, 0), (30, 30, 8))
... )
>>>
>>> # Mesh-based collision (accurate, requires trimesh)
>>> constraint = create_no_overlap_constraint(
...     "servo_ring_no_overlap",
...     "SERVO",
...     "RING_HOUSING",
...     stl_path_a="parts/servo.stl",
...     stl_path_b="parts/ring_housing.stl"
... )
yapcad.assembly.create_parallel_constraint(name: str, frame_a: str, frame_b: str | None = None, axis_a: str = 'z', axis_b: str = 'z', reference_axis: Tuple[float, float, float] | None = None, tolerance_deg: float = 1.0, description: str = '') KinematicConstraint[source]

Create a parallel constraint between two axes.

Parameters:
  • name – Constraint name

  • frame_a – First part name

  • frame_b – Second part name (or None for world reference)

  • axis_a – Local axis on frame_a

  • axis_b – Local axis on frame_b

  • reference_axis – World reference axis (if frame_b is None)

  • tolerance_deg – Angular tolerance

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.create_prismatic_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, axis: List[float] | None = None, min_position: float | None = None, max_position: float | None = None, max_velocity: float | None = None, max_force: float | None = None, friction: float = 0.0, damping: float = 0.0) Mate[source]

Convenience function to create a prismatic (slider) joint mate.

A prismatic joint allows translation along a single axis with no rotation. Common for linear actuators, pistons, and drawer slides.

Parameters:
  • name – Descriptive name (e.g., “piston_stroke”, “drawer_slide”)

  • part_a – Parent/base component identifier

  • datum_a – Axis datum on parent component

  • part_b – Child/moving component identifier

  • datum_b – Axis datum on child component

  • axis – Translation axis direction [x,y,z,w], defaults to [0,0,1,0] (Z-axis)

  • min_position – Minimum position in mm (None = unlimited)

  • max_position – Maximum position in mm (None = unlimited)

  • max_velocity – Maximum velocity in mm/s (None = unlimited)

  • max_force – Maximum force in N (None = unlimited)

  • friction – Static friction coefficient (0.0 = frictionless)

  • damping – Viscous damping coefficient (0.0 = no damping)

Returns:

Mate configured as prismatic joint

Example

# Linear actuator with 100mm stroke actuator = create_prismatic_mate(

name=”z_axis_slide”, part_a=”base”, datum_a=”rail_axis”, part_b=”carriage”, datum_b=”slider_axis”, min_position=0.0, max_position=100.0, max_velocity=50.0, friction=0.05, damping=5.0

)

yapcad.assembly.create_revolute_mate(name: str, part_a: str, datum_a: str, part_b: str, datum_b: str, axis: List[float] | None = None, min_angle: float | None = None, max_angle: float | None = None, max_velocity: float | None = None, max_torque: float | None = None, friction: float = 0.0, damping: float = 0.0) Mate[source]

Convenience function to create a revolute (hinge) joint mate.

A revolute joint allows rotation about a single axis. This is the most common joint type for mechanisms, robots, and articulated assemblies.

Parameters:
  • name – Descriptive name (e.g., “shoulder_pitch”, “door_hinge”)

  • part_a – Parent/base component identifier

  • datum_a – Axis datum on parent component

  • part_b – Child/moving component identifier

  • datum_b – Axis datum on child component

  • axis – Rotation axis direction [x,y,z,w], defaults to [0,0,1,0] (Z-axis)

  • min_angle – Minimum rotation angle in radians (None = unlimited)

  • max_angle – Maximum rotation angle in radians (None = unlimited)

  • max_velocity – Maximum angular velocity in rad/s (None = unlimited)

  • max_torque – Maximum torque in N*m (None = unlimited)

  • friction – Static friction coefficient (0.0 = frictionless)

  • damping – Viscous damping coefficient (0.0 = no damping)

Returns:

Mate configured as revolute joint

Example

# Robot elbow with 180-degree range elbow = create_revolute_mate(

name=”elbow_flex”, part_a=”upper_arm”, datum_a=”elbow_axis”, part_b=”forearm”, datum_b=”forearm_root_axis”, min_angle=0.0, max_angle=math.pi, max_velocity=2.0, friction=0.02, damping=0.1

)

yapcad.assembly.create_scara_arm_intent() AssemblyIntent[source]

Create example SCARA arm assembly intent.

Returns:

AssemblyIntent for a 3-axis SCARA arm

yapcad.assembly.create_tangent_constraint(name: str, frame: str, center: Tuple[float, float, float], axis: str = 'y', tolerance_deg: float = 2.0, description: str = '') KinematicConstraint[source]

Create a tangent constraint for wheel/motor assemblies.

Parameters:
  • name – Constraint name

  • frame – Part name in kinematic chain

  • center – Center point for tangent calculation

  • axis – Which local axis to check (“x”, “y”, or “z”)

  • tolerance_deg – Angular tolerance

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

yapcad.assembly.create_wheel_assembly_intent() AssemblyIntent[source]

Create example wheel assembly intent for the tube robot.

This demonstrates how to declare a wheel assembly using functional requirements rather than explicit transforms.

Returns:

AssemblyIntent for a wheel pod assembly

yapcad.assembly.create_z_stack_clearance_constraint(name: str, frame_a: str, frame_b: str, bounds_a: Tuple[Tuple[float, float, float], Tuple[float, float, float]], bounds_b: Tuple[Tuple[float, float, float], Tuple[float, float, float]], min_clearance: float = 0.0, description: str = '') KinematicConstraint[source]

Create a Z-stack clearance constraint for stacked parts.

Validates that vertically stacked parts (like servo + gearbox) have proper Z-axis separation to avoid collision. Useful for detecting servo bodies penetrating ring housings.

Parameters:
  • name – Constraint name

  • frame_a – First part name (e.g., “AXIS2_SERVO_XH430”)

  • frame_b – Second part name (e.g., “AXIS2_RING_HOUSING”)

  • bounds_a – Bounding box of part A ((min_x, min_y, min_z), (max_x, max_y, max_z))

  • bounds_b – Bounding box of part B

  • min_clearance – Minimum required clearance in mm (default: 0 = touching OK)

  • description – Description of constraint purpose

Returns:

Configured KinematicConstraint

Example

>>> # XH430 servo: 28.5 x 46.5 x 34mm, origin at body center
>>> servo_bounds = ((-14.25, -23.25, -17), (14.25, 23.25, 17))
>>> # Ring housing: ~60mm diameter x 8mm height
>>> ring_bounds = ((-30, -30, 0), (30, 30, 8))
>>> constraint = create_z_stack_clearance_constraint(
...     "axis2_servo_ring_clearance",
...     "AXIS2_SERVO_XH430",
...     "AXIS2_RING_HOUSING",
...     servo_bounds,
...     ring_bounds,
...     min_clearance=1.0
... )
yapcad.assembly.datum_to_transform_matrix(datum: Datum) Any | None[source]

Convert a Datum to a 4x4 transformation matrix.

For FRAME datums, constructs the full transform from axes. For PLANE datums, constructs a frame with normal as Z-axis. For AXIS datums, constructs a frame with direction as Z-axis.

Parameters:

datum – The datum to convert

Returns:

4x4 numpy array (if numpy available), or None

yapcad.assembly.distance_to_point(p1: Tuple[float, float, float], p2: Tuple[float, float, float]) float[source]

Calculate Euclidean distance between two points.

Parameters:
  • p1 – First point (x, y, z)

  • p2 – Second point (x, y, z)

Returns:

Distance in same units as input

Example

>>> distance_to_point((0, 0, 0), (3, 4, 0))
5.0
yapcad.assembly.dot_product(v1: Tuple[float, float, float], v2: Tuple[float, float, float]) float[source]

Calculate dot product of two vectors.

Parameters:
  • v1 – First vector (x, y, z)

  • v2 – Second vector (x, y, z)

Returns:

Dot product (scalar)

Example

>>> dot_product((1, 0, 0), (0, 1, 0))
0.0
>>> dot_product((1, 2, 3), (1, 2, 3))
14.0
yapcad.assembly.evaluate_coincident(datum_a: Datum, datum_b: Datum, tolerance_mm: float = 0.1, tolerance_deg: float = 1.0) CoincidentResult[source]

Evaluate a COINCIDENT constraint between two datums.

COINCIDENT means two geometric features occupy the same location/orientation: - Point-to-Point: Origins coincide - Point-to-Plane: Point lies on plane - Plane-to-Plane: Planes are coplanar (same origin + parallel normals) - Circle-to-Circle: Centers coincide (for bolt circle alignment) - Axis-to-Axis: Axes are colinear

Parameters:
  • datum_a – First datum in world coordinates

  • datum_b – Second datum in world coordinates

  • tolerance_mm – Linear tolerance in millimeters (default: 0.1mm)

  • tolerance_deg – Angular tolerance in degrees (default: 1.0 deg)

Returns:

CoincidentResult with evaluation status and error metrics

Example

>>> # Check if bolt holes align
>>> result = evaluate_coincident(
...     horn_bolt_circle,
...     sun_gear_bolt_circle,
...     tolerance_mm=0.05
... )
>>> if result.satisfied:
...     print("Bolt holes align!")
yapcad.assembly.is_radial_from_center(point: Tuple[float, float, float], normal: Tuple[float, float, float], center: Tuple[float, float, float], direction: str = 'outward', tolerance_deg: float = 1.0) bool[source]

Check if a normal vector points radially from a center point.

Parameters:
  • point – Location of the datum (x, y, z)

  • normal – Normal vector to check (x, y, z)

  • center – Center point (x, y, z)

  • direction – “outward” or “inward”

  • tolerance_deg – Angular tolerance in degrees

Returns:

True if normal is radial within tolerance

Example

>>> # Point at (10, 0, 0) with normal pointing in +x (outward from origin)
>>> is_radial_from_center((10, 0, 0), (1, 0, 0), (0, 0, 0), "outward")
True
yapcad.assembly.is_tangent_to_circle(axis_origin: Tuple[float, float, float], axis_direction: Tuple[float, float, float], circle_center: Tuple[float, float, float], circle_radius: float, tolerance_deg: float = 1.0) bool[source]

Check if an axis is tangent to a circle in the XY plane.

An axis is tangent when its direction is perpendicular to the radial vector from the circle center to the axis origin.

Parameters:
  • axis_origin – Point on the axis (x, y, z)

  • axis_direction – Direction vector of the axis (x, y, z)

  • circle_center – Center of the circle (x, y, z)

  • circle_radius – Radius of the circle

  • tolerance_deg – Angular tolerance in degrees

Returns:

True if axis is tangent within tolerance

Example

>>> # Axis at (10, 0, 0) pointing in +y direction, tangent to origin circle
>>> is_tangent_to_circle((10, 0, 0), (0, 1, 0), (0, 0, 0), 10.0)
True
yapcad.assembly.validate_assembly(constraints: List[KinematicConstraint], world_transforms: Dict[str, Any]) ValidationReport[source]

Convenience function to validate constraints against transforms.

Parameters:
  • constraints – List of KinematicConstraint objects

  • world_transforms – Dictionary mapping part names to transforms

Returns:

ValidationReport with evaluation results