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:
objectMain 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:
Create assembly: assembly = Assembly(“my_assembly”)
Add parts: assembly.add_part(part_def, name=”part_1”)
Add mates: assembly.add_mate(mate)
Add constraints: assembly.add_constraint(constraint)
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:
ExceptionException 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:
objectResult 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.
- constraint_results
Dictionary mapping constraint name to ConstraintResult
- Type:
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]
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:
objectA 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.
- constraint_type
Type of constraint to evaluate
- center
Reference center point for circular/radial constraints
- axis
Reference axis vector for parallel/perpendicular constraints
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” … )
- constraint_type: ConstraintType
- 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
- class yapcad.assembly.constraint.ConstraintResult(passed: bool, error_value: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectResult of evaluating a design constraint.
- class yapcad.assembly.constraint.ConstraintType(*values)[source]
Bases:
EnumHigh-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:
objectA 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)
- datum_type
Type of geometric feature (POINT, AXIS, PLANE, etc.)
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" )
- 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]
- class yapcad.assembly.datum.DatumType(*values)[source]
Bases:
EnumTypes 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:
objectDefinition 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.
- datums
Dictionary mapping datum names to Datum objects
- Type:
Dict[str, yapcad.assembly.datum.Datum]
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, ...))
- 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")
- 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:
objectCentral 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 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 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:
objectRepresents a source of datum definitions.
- datums
Dictionary of datum name -> Datum object
- Type:
Dict[str, yapcad.assembly.datum.Datum]
- 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:
objectDefines 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
- constraint
Constraint type (MateType enum)
- 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:
KeyError – If datums cannot be found
ValueError – If constraint type not supported
- 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:
objectResult of validating a mate constraint.
- 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:
objectDeclarative 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.
- functional_requirements
List of FunctionalRequirement objects
- Type:
- connections
List of Connection objects
- Type:
- clearances
List of Clearance objects
- Type:
- reference_geometry
Dict of ReferenceGeometry objects
- Type:
- part_definitions
Dict of PartDefinition objects (optional)
- Type:
- explicit_constraints
Additional explicit constraints
- Type:
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.
- 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]
- explicit_constraints: List[Constraint]
- functional_requirements: List[FunctionalRequirement]
- part_definitions: Dict[str, PartDefinition]
- reference_geometry: Dict[str, ReferenceGeometry]
- 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
- 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:
FunctionalRequirementPart 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.
- 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:
Example
>>> axis_req = AxisOrientationRequirement( ... name="motor_tangent", ... part="motor", ... axis_datum="motor_axis", ... orientation="tangent", ... reference="tube", ... )
- class yapcad.assembly.intent.Clearance(part_a: str, part_b: str, min_distance: float, check_type: str = 'bounding_box', critical: bool = True)[source]
Bases:
objectParts must maintain minimum distance (no collision).
Clearance constraints ensure that parts do not interfere with each other or with reference geometry.
- 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:
Example
>>> clearance = Clearance( ... part_a="motor", ... part_b="chassis_plate", ... min_distance=5.0, ... )
- 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:
objectResult of clearance validation.
- 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:
objectDefines how two parts connect (topology).
Connections specify the parent-child relationships between parts and the type of joint (rigid, revolute, prismatic, etc.).
- 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:
- axis
Axis specification for joints - “tangent”, “radial”, “axial” for reference-relative - tuple (x, y, z) for explicit direction
Example
>>> conn = Connection( ... parent="chassis.pivot_boss", ... child="wheel_arm.pivot_bore", ... joint_type="revolute", ... axis="tangent", ... limits=(-15, 15), ... )
- 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:
FunctionalRequirementPart 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
Example
>>> contact = ContactRequirement( ... name="wheel_contact", ... part="ddsm115_motor", ... surface="tire_outer_diameter", ... target="tube_inner_wall", ... contact_type="rolling", ... preload_source="suspension_spring", ... )
- get_derived_parameters() List[str][source]
List parameters that can be derived from this requirement.
- Returns:
List of parameter names in “part.parameter” format
- class yapcad.assembly.intent.FunctionalRequirement(name: str, part: str, description: str = '', priority: int = 1)[source]
Bases:
ABCBase 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.
- 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)
- class yapcad.assembly.intent.GeometryType(*values)[source]
Bases:
EnumTypes 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:
FunctionalRequirementTwo 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.
Example
>>> parallel = ParallelAxesRequirement( ... name="pivot_motor_parallel", ... part="wheel_arm", # Primary part ... axis_a="wheel_arm.pivot_bore_axis", ... axis_b="motor.motor_axis", ... )
- 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:
FunctionalRequirementEnd effector must have specified reach envelope.
For serial manipulators (SCARA arms, etc.), specifies the minimum and maximum reach from the base.
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, ... )
- get_derived_parameters() List[str][source]
List parameters that can be derived from this requirement.
- Returns:
List of parameter names in “part.parameter” format
- 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:
objectFixed 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.
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, ... )
- 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
- 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:
FunctionalRequirementPart 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:
Example
>>> roll = RollRequirement( ... name="wheel_rolls_z", ... part="motor", ... roll_direction="along_tube_axis", ... axis_datum="motor_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:
objectResult of solving an AssemblyIntent.
Contains derived parameters, computed transforms, and validation status.
- validation
Validation result for all constraints
- Type:
Any | None
- 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:
Transform propagation from kinematic chains to assembly validation
Constraint evaluation in world coordinates using chain transforms
Forward kinematics: root-to-leaf transform computation
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:
objectValidates 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
- 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:
objectResult of evaluating a single constraint.
- error_vector
Optional direction vector of error for visualization
- 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:
objectA 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.
- constraint_type
Type of constraint to evaluate
- # 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
- # 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)
- axis_b
Local axis on frame_b to use (“x”, “y”, “z”, or tuple)
- # Reference geometry for world-referenced constraints
- reference_center
Center point for tangent/radial constraints
- reference_axis
Reference axis direction for parallel/perpendicular
- # Pattern parameters
- # Tolerances
- # Metadata
- # 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 )
- constraint_type: KinematicConstraintType
- 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
- class yapcad.assembly.kinematic_integration.KinematicConstraintType(*values)[source]
Bases:
EnumConstraint 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:
objectResult of mesh-based collision detection between two parts.
- contact_points
List of approximate contact/intersection points
- 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:
objectComprehensive assembly validation report.
- constraint_results
Mapping of constraint name to evaluation result
- constraint_results: Dict[str, ConstraintEvaluationResult]
- 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:
objectResult of evaluating a COINCIDENT constraint.
- 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:
objectKinematic 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.
- mate_type
Type of constraint from MateType enum
- limits
Optional position/velocity/effort limits
- Type:
- dynamics
Optional friction/damping/stiffness parameters
- Type:
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()
- 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
- 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
- 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:
objectPhysical 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
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
)
- 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:
objectPosition, 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
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
)
- class yapcad.assembly.mate.MateType(*values)[source]
Bases:
EnumKinematic 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_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:
Where is the output shaft axis?
Where is the mounting face?
What’s the reference frame for orientation?
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:
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
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
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
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:
objectA named geometric feature on a part.
- feature_type
Type of geometric feature
- feature_type: FeatureType
- class yapcad.assembly.mate_surrogates.FeatureType(*values)[source]
Bases:
EnumTypes 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:
objectComplete reference frame definition for a COTS part.
The surrogate defines a local coordinate system and named features that can be used for assembly mating.
- features
Dictionary of named features
- Type:
- 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.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:
objectGeneric 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:
objectResult of solving a mate constraint.
- transform
4x4 homogeneous transform matrix (child in parent frame)
- Type:
Any | 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:
objectResult of validating a transform matrix.
- 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:
objectMain 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:
Create assembly: assembly = Assembly(“my_assembly”)
Add parts: assembly.add_part(part_def, name=”part_1”)
Add mates: assembly.add_mate(mate)
Add constraints: assembly.add_constraint(constraint)
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:
- transforms
Dictionary mapping part name to 4x4 transform matrix
- Type:
Dict[str, numpy.ndarray]
- mates
List of Mate objects defining part relationships
- Type:
- constraints
List of Constraint objects to validate
- Type:
- 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()
- 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
- 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:
ExceptionException 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:
objectDeclarative 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.
- functional_requirements
List of FunctionalRequirement objects
- Type:
- connections
List of Connection objects
- Type:
- clearances
List of Clearance objects
- Type:
- reference_geometry
Dict of ReferenceGeometry objects
- Type:
- part_definitions
Dict of PartDefinition objects (optional)
- Type:
- explicit_constraints
Additional explicit constraints
- Type:
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.
- 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]
- explicit_constraints: List[Constraint]
- functional_requirements: List[FunctionalRequirement]
- part_definitions: Dict[str, PartDefinition]
- reference_geometry: Dict[str, ReferenceGeometry]
- 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
- 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:
objectResult 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.
- constraint_results
Dictionary mapping constraint name to ConstraintResult
- Type:
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]
- class yapcad.assembly.AssemblyValidator(name: str = 'assembly')[source]
Bases:
objectValidates 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
- constraints: List[KinematicConstraint]
- 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:
FunctionalRequirementPart 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.
- 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:
Example
>>> axis_req = AxisOrientationRequirement( ... name="motor_tangent", ... part="motor", ... axis_datum="motor_axis", ... orientation="tangent", ... reference="tube", ... )
- class yapcad.assembly.Clearance(part_a: str, part_b: str, min_distance: float, check_type: str = 'bounding_box', critical: bool = True)[source]
Bases:
objectParts must maintain minimum distance (no collision).
Clearance constraints ensure that parts do not interfere with each other or with reference geometry.
- 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:
Example
>>> clearance = Clearance( ... part_a="motor", ... part_b="chassis_plate", ... min_distance=5.0, ... )
- class yapcad.assembly.ClearanceResult(satisfied: bool, actual_distance: float, required_distance: float, message: str = '', interference_point: Tuple[float, float, float] | None = None)[source]
Bases:
objectResult of clearance validation.
- 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:
objectResult of evaluating a COINCIDENT constraint.
- 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:
objectDefines how two parts connect (topology).
Connections specify the parent-child relationships between parts and the type of joint (rigid, revolute, prismatic, etc.).
- 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:
- axis
Axis specification for joints - “tangent”, “radial”, “axial” for reference-relative - tuple (x, y, z) for explicit direction
Example
>>> conn = Connection( ... parent="chassis.pivot_boss", ... child="wheel_arm.pivot_bore", ... joint_type="revolute", ... axis="tangent", ... limits=(-15, 15), ... )
- 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:
objectA 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.
- constraint_type
Type of constraint to evaluate
- center
Reference center point for circular/radial constraints
- axis
Reference axis vector for parallel/perpendicular constraints
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” … )
- constraint_type: ConstraintType
- 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
- 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:
objectResult of evaluating a single constraint.
- error_vector
Optional direction vector of error for visualization
- class yapcad.assembly.ConstraintResult(passed: bool, error_value: float = 0.0, error_message: str = '', details: Dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectResult of evaluating a design constraint.
- class yapcad.assembly.ConstraintType(*values)[source]
Bases:
EnumHigh-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:
FunctionalRequirementPart 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
Example
>>> contact = ContactRequirement( ... name="wheel_contact", ... part="ddsm115_motor", ... surface="tire_outer_diameter", ... target="tube_inner_wall", ... contact_type="rolling", ... preload_source="suspension_spring", ... )
- get_derived_parameters() List[str][source]
List parameters that can be derived from this requirement.
- Returns:
List of parameter names in “part.parameter” format
- 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:
objectA 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)
- datum_type
Type of geometric feature (POINT, AXIS, PLANE, etc.)
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" )
- 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]
- class yapcad.assembly.DatumRegistry[source]
Bases:
objectCentral 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 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 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:
objectRepresents a source of datum definitions.
- datums
Dictionary of datum name -> Datum object
- Type:
Dict[str, yapcad.assembly.datum.Datum]
- class yapcad.assembly.DatumType(*values)[source]
Bases:
EnumTypes 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:
objectDefines 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
- constraint
Constraint type (MateType enum)
- 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:
KeyError – If datums cannot be found
ValueError – If constraint type not supported
- 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:
ABCBase 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.
- 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)
- class yapcad.assembly.GeometryType(*values)[source]
Bases:
EnumTypes 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:
objectA 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.
- constraint_type
Type of constraint to evaluate
- # 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
- # 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)
- axis_b
Local axis on frame_b to use (“x”, “y”, “z”, or tuple)
- # Reference geometry for world-referenced constraints
- reference_center
Center point for tangent/radial constraints
- reference_axis
Reference axis direction for parallel/perpendicular
- # Pattern parameters
- # Tolerances
- # Metadata
- # 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 )
- constraint_type: KinematicConstraintType
- 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
- class yapcad.assembly.KinematicConstraintType(*values)[source]
Bases:
EnumConstraint 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:
objectKinematic 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.
- mate_type
Type of constraint from MateType enum
- limits
Optional position/velocity/effort limits
- Type:
- dynamics
Optional friction/damping/stiffness parameters
- Type:
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()
- 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
- 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
- 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:
objectPhysical 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
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
)
- 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:
objectPosition, 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
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
)
- class yapcad.assembly.MateType(*values)[source]
Bases:
EnumKinematic 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:
objectResult of validating a mate constraint.
- 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:
FunctionalRequirementTwo 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.
Example
>>> parallel = ParallelAxesRequirement( ... name="pivot_motor_parallel", ... part="wheel_arm", # Primary part ... axis_a="wheel_arm.pivot_bore_axis", ... axis_b="motor.motor_axis", ... )
- 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:
objectDefinition 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.
- datums
Dictionary mapping datum names to Datum objects
- Type:
Dict[str, yapcad.assembly.datum.Datum]
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, ...))
- 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")
- 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:
FunctionalRequirementEnd effector must have specified reach envelope.
For serial manipulators (SCARA arms, etc.), specifies the minimum and maximum reach from the base.
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, ... )
- get_derived_parameters() List[str][source]
List parameters that can be derived from this requirement.
- Returns:
List of parameter names in “part.parameter” format
- 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:
objectFixed 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.
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, ... )
- 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
- 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:
FunctionalRequirementPart 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:
Example
>>> roll = RollRequirement( ... name="wheel_rolls_z", ... part="motor", ... roll_direction="along_tube_axis", ... axis_datum="motor_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:
objectResult of solving an AssemblyIntent.
Contains derived parameters, computed transforms, and validation status.
- validation
Validation result for all constraints
- Type:
Any | None
- 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:
objectComprehensive assembly validation report.
- constraint_results
Mapping of constraint name to evaluation result
- constraint_results: Dict[str, ConstraintEvaluationResult]
- 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