Source code for pyvista_cad._readers.brep

"""BREP (`.brep`, `.brp`) reader and writer (via OCP)."""

import os
from typing import Any

import pyvista as pv

from pyvista_cad._errors import CadReadError, CadWriteError, OptionalDependencyError


def _require_ocp() -> Any:
    try:
        import OCP  # noqa: F401
    except ImportError as exc:
        msg = 'OCP not installed; install pyvista-cad[step]'
        raise OptionalDependencyError(msg) from exc
    return None


[docs] @pv.register_reader('.brep') @pv.register_reader('.brp') def read_brep( path: str | os.PathLike[str], /, *, linear_deflection: float = 0.1, angular_deflection: float = 0.5, **_: Any, ) -> pv.PolyData: """Read a BREP file as a :class:`pyvista.PolyData`. BREP is OpenCASCADE's native shape serialization. The file is loaded with ``BRepTools.Read_s`` and tessellated with ``BRepMesh_IncrementalMesh``. Parameters ---------- path : str or os.PathLike Path to a ``.brep`` / ``.brp`` file. linear_deflection : float, default: 0.1 OCCT linear deflection (model units). angular_deflection : float, default: 0.5 OCCT angular deflection (radians). **_ : Any Forward-compat keyword arguments are accepted and ignored. Returns ------- pyvista.PolyData Tessellated surface mesh. """ _require_ocp() from OCP.BRep import BRep_Builder from OCP.BRepTools import BRepTools from OCP.TopoDS import TopoDS_Shape from pyvista_cad._bridges.topods import from_topods shape = TopoDS_Shape() builder = BRep_Builder() try: ok = BRepTools.Read_s(shape, str(path), builder) except Exception as exc: msg = f'OCP failed to read BREP {path}: {exc}' raise CadReadError(msg) from exc if not ok or shape.IsNull(): msg = f'OCP could not parse BREP file: {path}' raise CadReadError(msg) poly = from_topods( shape, linear_deflection=linear_deflection, angular_deflection=angular_deflection, ) import numpy as np # ``from_topods`` -> ``topods_to_polydata`` already registers the # originating ``TopoDS_Shape`` in ``_conversion._BREP_CACHE``, so # ``get_cached_topods(poly)`` will return the exact-curve shape and # downstream consumers (e.g. ``CadShape.tessellate``) can skip the # re-tessellation round-trip. poly.field_data['cad.source_format'] = np.array(['brep']) poly.field_data['cad.backend'] = np.array(['ocp']) poly.field_data['cad.has_brep_origin'] = np.array([True]) return poly
[docs] def write_brep( mesh: pv.PolyData, path: str | os.PathLike[str], /, **_: Any, ) -> None: """Write a :class:`pyvista.PolyData` as a faceted BREP file. The mesh is converted to a ``TopoDS_Compound`` of triangular faces and serialized with ``BRepTools.Write_s``. Parameters ---------- mesh : pyvista.PolyData Source mesh. path : str or os.PathLike Destination ``.brep`` / ``.brp`` path. **_ : Any Forward-compat keyword arguments are accepted and ignored. Raises ------ pyvista_cad.CadWriteError If ``mesh`` is not a :class:`pyvista.PolyData`, or if OCCT's ``BRepTools.Write_s`` reports a failed write (for example an unwritable destination path): the failure is never silent. pyvista_cad.OptionalDependencyError If OCP is not installed. """ _require_ocp() from OCP.BRepTools import BRepTools from pyvista_cad._conversion import polydata_to_topods if not isinstance(mesh, pv.PolyData): msg = f'write_brep requires a pv.PolyData; got {type(mesh).__name__}' raise CadWriteError(msg) shape = polydata_to_topods(mesh) # ``BRepTools.Write_s`` returns a bool status and never raises on an # unwritable destination: it just returns False and writes nothing. # Check the status (and that the file actually materialised) so a # failed write surfaces as a CadWriteError instead of vanishing. ok = BRepTools.Write_s(shape, str(path)) if not ok or not os.path.exists(path): msg = f'OCP failed to write BREP {path} (BRepTools.Write_s returned {ok!r})' raise CadWriteError(msg)