""" Geometry abstraction layer for Fluency CAD. This module defines abstract interfaces for geometry operations, allowing different geometry kernels to be used interchangeably. """ from abc import ABC, abstractmethod from dataclasses import dataclass from typing import List, Tuple, Optional, Any, Dict import numpy as np @dataclass class Point2D: """2D point representation.""" x: float y: float def to_tuple(self) -> Tuple[float, float]: return (self.x, self.y) def to_array(self) -> np.ndarray: return np.array([self.x, self.y]) def distance_to(self, other: "Point2D") -> float: return np.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2) def __eq__(self, other: object) -> bool: if not isinstance(other, Point2D): return False return abs(self.x - other.x) < 1e-6 and abs(self.y - other.y) < 1e-6 @dataclass class Point3D: """3D point representation.""" x: float y: float z: float def to_tuple(self) -> Tuple[float, float, float]: return (self.x, self.y, self.z) def to_array(self) -> np.ndarray: return np.array([self.x, self.y, self.z]) def distance_to(self, other: "Point3D") -> float: return np.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2 + (self.z - other.z) ** 2) def __eq__(self, other: object) -> bool: if not isinstance(other, Point3D): return False return ( abs(self.x - other.x) < 1e-6 and abs(self.y - other.y) < 1e-6 and abs(self.z - other.z) < 1e-6 ) class GeometryObject: """Base class for geometry objects.""" def __init__(self, shape: Any = None, metadata: Optional[Dict] = None): self.shape = shape self.metadata = metadata or {} self._mesh_cache: Optional[Tuple[np.ndarray, np.ndarray]] = None def invalidate_cache(self) -> None: """Invalidate any cached data.""" self._mesh_cache = None class SketchEntity: """Base class for sketch entities (points, lines, circles).""" def __init__(self, entity_id: int, entity_type: str): self.id = entity_id self.entity_type = entity_type self.constraints: List[str] = [] self.is_construction: bool = False def add_constraint(self, constraint_type: str) -> None: self.constraints.append(constraint_type) class GeometryKernel(ABC): """ Abstract base class for geometry kernels. A geometry kernel provides primitives, operations, and export capabilities for CAD geometry. """ @abstractmethod def create_point(self, x: float, y: float) -> GeometryObject: """Create a 2D point.""" pass @abstractmethod def create_line(self, start: Point2D, end: Point2D) -> GeometryObject: """Create a 2D line segment.""" pass @abstractmethod def create_circle(self, center: Point2D, radius: float) -> GeometryObject: """Create a 2D circle.""" pass @abstractmethod def create_arc( self, center: Point2D, radius: float, start_angle: float, end_angle: float ) -> GeometryObject: """Create a 2D arc.""" pass @abstractmethod def create_polygon(self, points: List[Point2D]) -> GeometryObject: """Create a closed polygon from points.""" pass @abstractmethod def create_rectangle( self, width: float, height: float, center: Optional[Point2D] = None ) -> GeometryObject: """Create a rectangle.""" pass @abstractmethod def extrude( self, sketch: GeometryObject, height: float, direction: Tuple[float, float, float] = (0, 0, 1), symmetric: bool = False, ) -> GeometryObject: """Extrude a 2D sketch into a 3D solid.""" pass @abstractmethod def revolve( self, sketch: GeometryObject, angle: float = 360.0, axis: Tuple[float, float, float] = (0, 0, 1), origin: Tuple[float, float, float] = (0, 0, 0), ) -> GeometryObject: """Revolve a 2D sketch around an axis.""" pass @abstractmethod def loft(self, profiles: List[GeometryObject], ruled: bool = False) -> GeometryObject: """Create a loft between multiple profiles.""" pass @abstractmethod def sweep( self, profile: GeometryObject, path: GeometryObject, is_frenet: bool = False ) -> GeometryObject: """Sweep a profile along a path.""" pass @abstractmethod def boolean_union(self, *bodies: GeometryObject) -> GeometryObject: """Union multiple bodies.""" pass @abstractmethod def boolean_difference(self, base: GeometryObject, tool: GeometryObject) -> GeometryObject: """Subtract tool from base.""" pass @abstractmethod def boolean_intersection(self, body1: GeometryObject, body2: GeometryObject) -> GeometryObject: """Intersect two bodies.""" pass @abstractmethod def fillet( self, body: GeometryObject, radius: float, edges: Optional[List[Any]] = None ) -> GeometryObject: """Apply fillet to edges.""" pass @abstractmethod def chamfer( self, body: GeometryObject, size: float, edges: Optional[List[Any]] = None ) -> GeometryObject: """Apply chamfer to edges.""" pass @abstractmethod def shell( self, body: GeometryObject, thickness: float, faces_to_remove: Optional[List[Any]] = None ) -> GeometryObject: """Create a shell (hollow body).""" pass @abstractmethod def offset(self, face: GeometryObject, distance: float) -> GeometryObject: """Offset a face or surface.""" pass @abstractmethod def translate(self, body: GeometryObject, vector: Tuple[float, float, float]) -> GeometryObject: """Translate a body.""" pass @abstractmethod def rotate( self, body: GeometryObject, axis: Tuple[float, float, float], angle: float, origin: Tuple[float, float, float] = (0, 0, 0), ) -> GeometryObject: """Rotate a body around an axis.""" pass @abstractmethod def scale(self, body: GeometryObject, factor: float) -> GeometryObject: """Scale a body uniformly.""" pass @abstractmethod def mirror( self, body: GeometryObject, plane_normal: Tuple[float, float, float], plane_origin: Tuple[float, float, float] = (0, 0, 0), ) -> GeometryObject: """Mirror a body across a plane.""" pass @abstractmethod def export_step(self, body: GeometryObject, filepath: str, schema: str = "AP214") -> bool: """Export to STEP format.""" pass @abstractmethod def export_iges(self, body: GeometryObject, filepath: str) -> bool: """Export to IGES format.""" pass @abstractmethod def export_stl( self, body: GeometryObject, filepath: str, tolerance: float = 0.1, ascii_mode: bool = False ) -> bool: """Export to STL format.""" pass @abstractmethod def import_step(self, filepath: str) -> GeometryObject: """Import from STEP format.""" pass @abstractmethod def import_iges(self, filepath: str) -> GeometryObject: """Import from IGES format.""" pass @abstractmethod def get_mesh( self, body: GeometryObject, tolerance: float = 0.1 ) -> Tuple[np.ndarray, np.ndarray]: """ Get triangulated mesh for rendering. Returns: Tuple of (vertices, faces) where: - vertices: Nx3 numpy array of vertex positions - faces: Mx3 numpy array of triangle indices """ pass @abstractmethod def get_edges(self, body: GeometryObject) -> Tuple[np.ndarray, np.ndarray]: """ Get edge wireframe for rendering. Returns: Tuple of (vertices, edges) where: - vertices: Nx3 numpy array of vertex positions - edges: Mx2 numpy array of edge vertex indices """ pass @abstractmethod def get_bounding_box(self, body: GeometryObject) -> Tuple[Point3D, Point3D]: """Get the bounding box of a body.""" pass @abstractmethod def get_volume(self, body: GeometryObject) -> float: """Calculate the volume of a solid body.""" pass @abstractmethod def get_surface_area(self, body: GeometryObject) -> float: """Calculate the surface area of a body.""" pass @abstractmethod def get_center_of_mass(self, body: GeometryObject) -> Point3D: """Calculate the center of mass of a solid body.""" pass class SketchInterface(ABC): """ Abstract interface for 2D sketching with constraints. A sketch provides 2D geometry creation and constraint solving capabilities for parametric CAD. """ @abstractmethod def add_point(self, x: float, y: float) -> SketchEntity: """Add a point to the sketch.""" pass @abstractmethod def add_line(self, start: SketchEntity, end: SketchEntity) -> SketchEntity: """Add a line between two points.""" pass @abstractmethod def add_circle(self, center: SketchEntity, radius: float) -> SketchEntity: """Add a circle.""" pass @abstractmethod def add_arc( self, center: SketchEntity, radius: float, start_point: SketchEntity, end_point: SketchEntity, ) -> SketchEntity: """Add an arc.""" pass @abstractmethod def add_rectangle( self, corner1: Tuple[float, float], corner2: Tuple[float, float] ) -> List[SketchEntity]: """Add a rectangle, returning the created entities.""" pass @abstractmethod def constrain_coincident(self, *entities: SketchEntity) -> bool: """Make entities coincident.""" pass @abstractmethod def constrain_horizontal(self, line: SketchEntity) -> bool: """Constrain a line to be horizontal.""" pass @abstractmethod def constrain_vertical(self, line: SketchEntity) -> bool: """Constrain a line to be vertical.""" pass @abstractmethod def constrain_distance( self, entity1: SketchEntity, entity2: SketchEntity, distance: float ) -> bool: """Constrain distance between two entities.""" pass @abstractmethod def constrain_angle(self, line1: SketchEntity, line2: SketchEntity, angle: float) -> bool: """Constrain angle between two lines.""" pass @abstractmethod def constrain_parallel(self, line1: SketchEntity, line2: SketchEntity) -> bool: """Constrain two lines to be parallel.""" pass @abstractmethod def constrain_perpendicular(self, line1: SketchEntity, line2: SketchEntity) -> bool: """Constrain two lines to be perpendicular.""" pass @abstractmethod def constrain_midpoint(self, point: SketchEntity, line: SketchEntity) -> bool: """Constrain a point to be at the midpoint of a line.""" pass @abstractmethod def constrain_tangent(self, entity1: SketchEntity, entity2: SketchEntity) -> bool: """Constrain two entities to be tangent.""" pass @abstractmethod def constrain_equal_length(self, line1: SketchEntity, line2: SketchEntity) -> bool: """Constrain two lines to have equal length.""" pass @abstractmethod def constrain_equal_radius(self, circle1: SketchEntity, circle2: SketchEntity) -> bool: """Constrain two circles to have equal radius.""" pass @abstractmethod def constrain_diameter(self, circle: SketchEntity, diameter: float) -> bool: """Set the diameter of a circle.""" pass @abstractmethod def constrain_fixed(self, entity: SketchEntity) -> bool: """Fix an entity in place.""" pass @abstractmethod def solve(self) -> bool: """Solve all constraints.""" pass @abstractmethod def get_geometry(self) -> GeometryObject: """Get the solved geometry for operations.""" pass @abstractmethod def get_points(self) -> List[Point2D]: """Get all point positions.""" pass @abstractmethod def clear(self) -> None: """Clear all geometry and constraints.""" pass @abstractmethod def delete_entity(self, entity: SketchEntity) -> bool: """Delete an entity and its constraints.""" pass