11from __future__ import annotations
22
3+ import copy
34from typing import Tuple
45
56import numpy as np
@@ -20,12 +21,20 @@ class TriMesh(_TriMesh):
2021 Inherits from the base TriMesh class and provides additional functionality.
2122 """
2223
23- def __init__ (self , surface : pv .PolyData ):
24+ def __init__ (self , surface ):
25+ if isinstance (surface , _TriMesh ):
26+ # Copy-construct directly from another TriMesh at the C++ level.
27+ # This is used by read_from_file and anywhere a TriMesh-to-TriMesh
28+ # copy is needed without a pyvista round-trip.
29+ super ().__init__ (surface )
30+ return
31+
2432 # Validate input surface
2533 validate_pyvista_polydata (surface , "input surface" )
2634
2735 # Triangulate to ensure we have triangular faces
28- surface = surface .triangulate ()
36+ if not surface .is_all_triangles :
37+ surface = surface .triangulate ()
2938
3039 # Extract vertices and triangles
3140 verts = np .array (surface .points , dtype = np .float64 ).copy ()
@@ -34,6 +43,7 @@ def __init__(self, surface: pv.PolyData):
3443 raise ValueError ("Invalid surface geometry" )
3544
3645 super ().__init__ (verts , faces )
46+
3747 @classmethod
3848 def from_vertices_and_triangles (
3949 cls , vertices : np .ndarray , triangles : np .ndarray
@@ -56,7 +66,10 @@ def from_vertices_and_triangles(
5666 # Create a temporary PyVista PolyData object for validation
5767 if not validate_vertices_and_faces (vertices , triangles ):
5868 raise ValueError ("Invalid vertices or triangles" )
59- surface = pv .PolyData (vertices , np .hstack ((np .full ((triangles .shape [0 ], 1 ), 3 ), triangles )).flatten ())
69+ surface = pv .PolyData (
70+ vertices ,
71+ np .hstack ((np .full ((triangles .shape [0 ], 1 ), 3 ), triangles )).flatten (),
72+ )
6073 return cls (surface )
6174
6275 def get_vertices_and_triangles (
@@ -107,13 +120,62 @@ def vtk(
107120 """
108121 return self .to_pyvista (area_threshold , duplicate_vertex_threshold )
109122
110- def copy (self ) -> TriMesh :
123+ @property
124+ def area (self ) -> float :
125+ """Surface area computed directly from the CGAL mesh (no pyvista conversion)."""
126+ return self ._cgal_area ()
127+
128+ @property
129+ def points (self ) -> np .ndarray :
130+ """Vertex coordinates as (n, 3) numpy array (no pyvista conversion)."""
131+ return self ._cgal_points ()
132+
133+ @property
134+ def n_cells (self ) -> int :
135+ """Number of faces in the CGAL mesh (no pyvista conversion)."""
136+ return self ._cgal_n_faces ()
137+
138+ @property
139+ def n_points (self ) -> int :
140+ """Number of vertices in the CGAL mesh (no pyvista conversion)."""
141+ return self ._cgal_n_vertices ()
142+
143+ @classmethod
144+ def read_from_file (cls , path : str ) -> "TriMesh" :
145+ """Read a mesh from a binary file written by :meth:`write_to_file`.
146+
147+ Bypasses the pyvista/VTK stack entirely — much faster for temp files
148+ passed between CGAL operations.
111149 """
112- Create a copy of the TriMesh.
150+ return cls (_TriMesh ._read_from_file (path ))
151+
152+ def clone (self ) -> TriMesh :
153+ """
154+ Return a deep copy of this TriMesh as a Python ``TriMesh`` instance.
155+
156+ Uses the C++ copy-constructor binding to clone the CGAL mesh directly,
157+ with no numpy array roundtrip.
158+ """
159+ return TriMesh (self )
160+
161+ def copy (self , deep : bool = True ) -> TriMesh :
162+ """
163+ Return a deep copy of this TriMesh.
164+
165+ Uses the C++-level ``clone()`` to copy the CGAL mesh directly without
166+ a pyvista round-trip, preserving all vertices, faces, and fixed edges.
113167
114168 Returns
115169 -------
116170 TriMesh
117- A copy of the TriMesh object .
171+ A deep copy of this TriMesh.
118172 """
119- return TriMesh (self .to_pyvista ())
173+ return self .clone ()
174+
175+ def __copy__ (self ) -> TriMesh :
176+ return self .clone ()
177+
178+ def __deepcopy__ (self , memo : dict ) -> TriMesh :
179+ result = self .clone ()
180+ memo [id (self )] = result
181+ return result
0 commit comments