Source code for yapcad.fasteners

"""Fastener generation with catalog-based dimensions.

This package provides parametric fastener generation (hex bolts, hex nuts)
with dimensions from YAML catalog files. Bundled catalogs cover common
metric (ISO) and unified (ASME) sizes; users can extend with custom catalogs.

Quick Start:
    >>> from yapcad.fasteners import metric_hex_bolt, metric_hex_nut
    >>> bolt = metric_hex_bolt("M8", 25.0)
    >>> nut = metric_hex_nut("M8")

Available Functions:
    Metric (ISO):
        metric_hex_bolt(size, length, tolerance_class="6g")
        metric_hex_nut(size, tolerance_class="6H")

    Unified (ASME):
        unified_hex_bolt(size, length, tolerance_class="2A")
        unified_hex_nut(size, tolerance_class="2B")

Catalog Customization:
    Set YAPCAD_FASTENER_DATA environment variable to add custom catalog
    directories. These are searched before bundled data.

    Example:
        export YAPCAD_FASTENER_DATA="/path/to/my/fasteners"

    See docs/fastener_catalog_schema.md for YAML format specification.
"""

from __future__ import annotations

from pathlib import Path
from typing import Optional

from .catalog import (
    YAPCAD_FASTENER_DATA,
    THREAD_SERIES,
    load_catalog,
    list_available_sizes,
    list_tolerance_classes,
    get_thread_data,
    get_bolt_data,
    get_nut_data,
    clear_cache,
)

# Re-export legacy API for backward compatibility
from yapcad.fasteners_legacy import (
    HexCapScrewSpec,
    HexNutSpec,
    build_hex_cap_screw,
    build_hex_nut,
    metric_hex_cap_catalog,
    metric_hex_cap_screw,
    metric_hex_nut_catalog,
    unified_hex_cap_catalog,
    unified_hex_cap_screw,
    unified_hex_nut_catalog,
    # Note: metric_hex_nut and unified_hex_nut are redefined below
    # with improved catalog-based API
)

__all__ = [
    # Environment variable
    "YAPCAD_FASTENER_DATA",
    "THREAD_SERIES",
    # Catalog functions
    "load_catalog",
    "list_available_sizes",
    "list_tolerance_classes",
    "get_thread_data",
    "get_bolt_data",
    "get_nut_data",
    "clear_cache",
    # New catalog-based constructors
    "metric_hex_bolt",
    "metric_hex_nut",
    "unified_hex_bolt",
    "unified_hex_nut",
    # Legacy API (backward compatibility)
    "HexCapScrewSpec",
    "HexNutSpec",
    "build_hex_cap_screw",
    "build_hex_nut",
    "metric_hex_cap_catalog",
    "metric_hex_cap_screw",
    "metric_hex_nut_catalog",
    "unified_hex_cap_catalog",
    "unified_hex_cap_screw",
    "unified_hex_nut_catalog",
]


[docs] def metric_hex_bolt( size: str, length: float, *, tolerance_class: str = "6g", thread_length: Optional[float] = None, starts: int = 1, thread_arc_samples: int = 180, thread_samples_per_pitch: int = 6, catalog_path: Optional[Path] = None, ): """Create a metric hex bolt per ISO 4014/4017. Args: size: Thread size designation (e.g., "M8", "M10", "M12") length: Total shank length in mm tolerance_class: Thread tolerance class (default "6g") Common classes: "6g" (general), "4g6g" (close), "8g" (coarse) thread_length: Override automatic thread length calculation (mm) starts: Number of thread starts (default 1) thread_arc_samples: Angular resolution for thread generation thread_samples_per_pitch: Samples per pitch for thread profile catalog_path: Optional path to custom catalog YAML file Returns: yapCAD solid representing the hex bolt Raises: KeyError: If size or tolerance_class not found in catalog Examples: >>> bolt = metric_hex_bolt("M8", 25.0) >>> bolt = metric_hex_bolt("M10", 40.0, tolerance_class="4g6g") >>> bolt = metric_hex_bolt("M6", 20.0, thread_length=15.0) """ from yapcad.fasteners.builders import build_hex_bolt_from_catalog return build_hex_bolt_from_catalog( thread_series="metric_coarse", size=size, length=length, tolerance_class=tolerance_class, thread_length=thread_length, starts=starts, thread_arc_samples=thread_arc_samples, thread_samples_per_pitch=thread_samples_per_pitch, catalog_path=catalog_path, )
[docs] def metric_hex_nut( size: str, *, tolerance_class: str = "6H", handedness: str = "right", starts: int = 1, thread_arc_samples: int = 180, thread_samples_per_pitch: int = 6, catalog_path: Optional[Path] = None, ): """Create a metric hex nut per ISO 4032. Args: size: Thread size designation (e.g., "M8", "M10") tolerance_class: Thread tolerance class (default "6H" for internal) Common classes: "6H" (general), "5H" (close), "7H" (coarse) handedness: "right" or "left" hand thread starts: Number of thread starts (default 1) thread_arc_samples: Angular resolution for thread generation thread_samples_per_pitch: Samples per pitch for thread profile catalog_path: Optional path to custom catalog YAML file Returns: yapCAD solid representing the hex nut Raises: KeyError: If size or tolerance_class not found in catalog Examples: >>> nut = metric_hex_nut("M8") >>> nut = metric_hex_nut("M10", tolerance_class="5H") """ from yapcad.fasteners.builders import build_hex_nut_from_catalog return build_hex_nut_from_catalog( thread_series="metric_coarse", size=size, tolerance_class=tolerance_class, handedness=handedness, starts=starts, thread_arc_samples=thread_arc_samples, thread_samples_per_pitch=thread_samples_per_pitch, catalog_path=catalog_path, )
[docs] def unified_hex_bolt( size: str, length: float, *, tolerance_class: str = "2A", thread_length: Optional[float] = None, starts: int = 1, thread_arc_samples: int = 180, thread_samples_per_pitch: int = 6, catalog_path: Optional[Path] = None, ): """Create a unified (UNC/UNF) hex bolt per ASME B18.2.1. Args: size: Thread size designation (e.g., "1/4-20", "#10-24", "1/2-13") length: Total shank length in inches (will be converted to mm internally) tolerance_class: Thread class (default "2A") Classes: "1A" (loose), "2A" (general), "3A" (close) thread_length: Override automatic thread length in inches starts: Number of thread starts (default 1) thread_arc_samples: Angular resolution for thread generation thread_samples_per_pitch: Samples per pitch for thread profile catalog_path: Optional path to custom catalog YAML file Returns: yapCAD solid representing the hex bolt Raises: KeyError: If size or tolerance_class not found in catalog Examples: >>> bolt = unified_hex_bolt("1/4-20", 1.0) # 1 inch long >>> bolt = unified_hex_bolt("1/2-13", 2.0, tolerance_class="3A") """ from yapcad.fasteners.builders import build_hex_bolt_from_catalog # Convert inches to mm length_mm = length * 25.4 thread_length_mm = thread_length * 25.4 if thread_length is not None else None return build_hex_bolt_from_catalog( thread_series="unified_coarse", size=size, length=length_mm, tolerance_class=tolerance_class, thread_length=thread_length_mm, starts=starts, thread_arc_samples=thread_arc_samples, thread_samples_per_pitch=thread_samples_per_pitch, catalog_path=catalog_path, )
[docs] def unified_hex_nut( size: str, *, tolerance_class: str = "2B", handedness: str = "right", starts: int = 1, thread_arc_samples: int = 180, thread_samples_per_pitch: int = 6, catalog_path: Optional[Path] = None, ): """Create a unified (UNC/UNF) hex nut per ASME B18.2.2. Args: size: Thread size designation (e.g., "1/4-20", "#10-24", "1/2-13") tolerance_class: Thread class (default "2B" for internal) Classes: "1B" (loose), "2B" (general), "3B" (close) handedness: "right" or "left" hand thread starts: Number of thread starts (default 1) thread_arc_samples: Angular resolution for thread generation thread_samples_per_pitch: Samples per pitch for thread profile catalog_path: Optional path to custom catalog YAML file Returns: yapCAD solid representing the hex nut Raises: KeyError: If size or tolerance_class not found in catalog Examples: >>> nut = unified_hex_nut("1/4-20") >>> nut = unified_hex_nut("1/2-13", tolerance_class="3B") """ from yapcad.fasteners.builders import build_hex_nut_from_catalog return build_hex_nut_from_catalog( thread_series="unified_coarse", size=size, tolerance_class=tolerance_class, handedness=handedness, starts=starts, thread_arc_samples=thread_arc_samples, thread_samples_per_pitch=thread_samples_per_pitch, catalog_path=catalog_path, )