1- from typing import Optional
2-
31import numpy as np
42from compas .datastructures import Mesh
53from compas .plugins import plugin
1210
1311
1412@plugin (category = "trimesh" , pluggable_name = "trimesh_remesh" )
15- def mesh_remesh (
13+ def trimesh_remesh (
1614 mesh : VerticesFaces ,
1715 target_edge_length : float ,
1816 number_of_iterations : int = 10 ,
@@ -55,24 +53,71 @@ def mesh_remesh(
5553 V , F = mesh
5654 V = np .asarray (V , dtype = np .float64 , order = "C" )
5755 F = np .asarray (F , dtype = np .int32 , order = "C" )
58- return _meshing .remesh (V , F , target_edge_length , number_of_iterations , do_project )
56+ return _meshing .pmp_trimesh_remesh (V , F , target_edge_length , number_of_iterations , do_project )
57+
58+
59+ def trimesh_dual (
60+ mesh : VerticesFaces ,
61+ length_factor : float = 1.0 ,
62+ number_of_iterations : int = 10 ,
63+ angle_radians : float = 0.9 ,
64+ scale_factor : float = 1.0 ,
65+ fixed_vertices : list [int ] = [],
66+ ) -> tuple [np .ndarray , list [list [int ]]]:
67+ """Create a dual mesh from a triangular mesh with variable-length faces.
68+
69+ Parameters
70+ ----------
71+ mesh : :attr:`compas_cgal.types.VerticesFaces`
72+ The mesh to create a dual from.
73+ angle_radians : double, optional
74+ Angle limit in radians for boundary vertices to remove.
75+ length_factor : double, optional
76+ Length factor for remeshing.
77+ number_of_iterations : int, optional
78+ Number of remeshing iterations.
79+ scale_factor : double, optional
80+ Scale factor for inner vertices.
81+ fixed_vertices : list[int], optional
82+ List of vertex indices to keep fixed during remeshing.
83+
84+ Returns
85+ -------
86+ tuple
87+ A tuple containing:
88+
89+ - Remeshed mesh vertices as an Nx3 numpy array.
90+ - Remeshed mesh faces as an Mx3 numpy array.
91+ - Dual mesh vertices as an Nx3 numpy array.
92+ - Variable-length faces as a list of lists of vertex indices.
93+
94+ Notes
95+ -----
96+ This dual mesh implementation includes proper boundary handling by:
97+ 1. Creating vertices at face centroids of the primal mesh
98+ 2. Creating additional vertices at boundary edge midpoints
99+ 3. Creating proper connections for boundary edges
100+
101+ """
102+ V , F = mesh
103+ V = np .asarray (V , dtype = np .float64 , order = "C" )
104+ F = np .asarray (F , dtype = np .int32 , order = "C" )
105+ fixed_vertices = np .asarray (fixed_vertices , dtype = np .int32 , order = "C" )
106+ return _meshing .pmp_trimesh_remesh_dual (V , F , fixed_vertices , length_factor , number_of_iterations , angle_radians , scale_factor )
59107
60108
61109def project_mesh_on_mesh (
62110 mesh_source : VerticesFaces ,
63111 mesh_target : VerticesFaces ,
64- use_normals : bool = True ,
65112) -> VerticesFaces :
66- """Project mesh_target vertices onto mesh_source surface.
113+ """Project mesh_source vertices onto mesh_target surface.
67114
68115 Parameters
69116 ----------
70117 mesh_source : :attr:`compas_cgal.types.VerticesFaces`
71- The mesh to project onto ( target surface) .
118+ Mesh that is projected onto the target mesh .
72119 mesh_target : :attr:`compas_cgal.types.VerticesFaces`
73- The mesh whose vertices will be projected (source points).
74- use_normals : bool, optional
75- Whether to use normals for ray-based projection. If False, closest point projection is used.
120+ Mesh that is projected onto.
76121
77122 Returns
78123 -------
@@ -81,130 +126,89 @@ def project_mesh_on_mesh(
81126
82127 """
83128 V_source , F_source = mesh_source .to_vertices_and_faces ()
84- V_target , F_target = mesh_target .to_vertices_and_faces (triangulated = True )
129+ V_source = project_points_on_mesh (V_source , mesh_target )
130+ return Mesh .from_vertices_and_faces (V_source , F_source )
85131
86- # Convert inputs to numpy arrays
87- numpy_V_source = np .asarray (V_source , dtype = np .float64 , order = "C" )
88132
89- numpy_V_target = np .asarray (V_target , dtype = np .float64 , order = "C" )
90- numpy_F_target = np .asarray (F_target , dtype = np .int32 , order = "C" )
133+ def pull_mesh_on_mesh (
134+ mesh_source : VerticesFaces ,
135+ mesh_target : VerticesFaces ,
136+ ) -> VerticesFaces :
137+ """Pull mesh_source vertices onto mesh_target surface using mesh_source normals.
91138
92- # Handle normals calculation
93- if use_normals :
94- # Pre-allocate numpy array for normals with correct size
95- numpy_N_source = np .zeros ((mesh_source .number_of_vertices (), 3 ), dtype = np .float64 )
139+ Parameters
140+ ----------
141+ mesh_source : :attr:`compas_cgal.types.VerticesFaces`
142+ Mesh that is projected onto the target mesh.
143+ mesh_target : :attr:`compas_cgal.types.VerticesFaces`
144+ Mesh that is projected onto.
96145
97- # Fill the array with vertex normals
98- for i , vertex_key in enumerate (mesh_source .vertices ()):
99- normal = - mesh_source .vertex_normal (vertex_key )
100- numpy_N_source [i , 0 ] = normal .x
101- numpy_N_source [i , 1 ] = normal .y
102- numpy_N_source [i , 2 ] = normal .z
103- else :
104- # Don't use normals - create empty array
105- numpy_N_source = np .zeros ((0 , 0 ), dtype = np .float64 )
146+ Returns
147+ -------
148+ :attr:`compas_cgal.types.VerticesFaces`
149+ The projected mesh (vertices on mesh_source surface, original faces).
106150
107- # Call the C++ function
108- _meshing .project (numpy_V_target , numpy_F_target , numpy_V_source , numpy_N_source )
151+ """
152+ V_source , F_source = mesh_source .to_vertices_and_faces ()
153+ N_source = []
154+ for v in mesh_source .vertices ():
155+ N_source .append (mesh_source .vertex_normal (v ))
109156
110- return Mesh .from_vertices_and_faces (numpy_V_source , F_source )
157+ V_source = pull_points_on_mesh (V_source , N_source , mesh_target )
158+ return Mesh .from_vertices_and_faces (V_source , F_source )
111159
112160
113161def project_points_on_mesh (
114162 points : list [list [float ]],
115- mesh_target : VerticesFaces ,
116- normals : Optional [list [list [float ]]] = None ,
163+ mesh : VerticesFaces ,
117164) -> list [list [float ]]:
118- """Project points onto mesh_target surface .
165+ """Project points onto a mesh by closest perpendicular distance .
119166
120167 Parameters
121168 ----------
122169 points : list[list[float]]
123170 The points to project.
124- mesh_target : :attr:`compas_cgal.types.VerticesFaces`
125- The mesh whose vertices will be projected (source points).
126- normals : list[list[float]], optional
127- The normals of the points to project.
171+ mesh : :attr:`compas_cgal.types.VerticesFaces`
172+ Mesh that the points are projected onto.
128173
129174 Returns
130175 -------
131176 list[list[float]]
132- The projected points (vertices on mesh_target surface).
177+ The projected points (vertices on the mesh surface).
133178
134179 """
135- V_target , F_target = mesh_target .to_vertices_and_faces (triangulated = True )
136-
137- # Convert inputs to numpy arrays
180+ V_target , F_target = mesh .to_vertices_and_faces (triangulated = True )
138181 numpy_V_source = np .asarray (points , dtype = np .float64 , order = "C" )
139-
140182 numpy_V_target = np .asarray (V_target , dtype = np .float64 , order = "C" )
141183 numpy_F_target = np .asarray (F_target , dtype = np .int32 , order = "C" )
142-
143- # Handle normals calculation
144- if normals :
145- # Pre-allocate numpy array for normals with correct size
146- numpy_N_source = np .zeros ((len (points ), 3 ), dtype = np .float64 )
147-
148- # Fill the array with vertex normals
149- for i , normal in enumerate (normals ):
150- numpy_N_source [i , 0 ] = normal [0 ]
151- numpy_N_source [i , 1 ] = normal [1 ]
152- numpy_N_source [i , 2 ] = normal [2 ]
153- else :
154- # Don't use normals - create empty array
155- numpy_N_source = np .zeros ((0 , 0 ), dtype = np .float64 )
156-
157- # Call the C++ function
158- _meshing .project (numpy_V_target , numpy_F_target , numpy_V_source , numpy_N_source )
184+ _meshing .pmp_project (numpy_V_target , numpy_F_target , numpy_V_source )
159185
160186 return numpy_V_source
161187
162188
163- def remesh_dual (
164- mesh : VerticesFaces ,
165- length_factor : float = 1.0 ,
166- number_of_iterations : int = 10 ,
167- angle_radians : float = 0.9 ,
168- scale_factor : float = 1.0 ,
169- fixed_vertices : list [int ] = [],
170- ) -> tuple [np .ndarray , list [list [int ]]]:
171- """Create a dual mesh from a triangular mesh with variable-length faces.
189+ def pull_points_on_mesh (points : list [list [float ]], normals : list [list [float ]], mesh : VerticesFaces ) -> list [list [float ]]:
190+ """Pull points onto a mesh surface using ray-mesh intersection along normal vectors.
172191
173192 Parameters
174193 ----------
194+ points : list[list[float]]
195+ The points to pull.
196+ normals : list[list[float]]
197+ The normal vectors used for directing the projection.
175198 mesh : :attr:`compas_cgal.types.VerticesFaces`
176- The mesh to create a dual from.
177- angle_radians : double, optional
178- Angle limit in radians for boundary vertices to remove.
179- length_factor : double, optional
180- Length factor for remeshing.
181- number_of_iterations : int, optional
182- Number of remeshing iterations.
183- scale_factor : double, optional
184- Scale factor for inner vertices.
185- fixed_vertices : list[int], optional
186- List of vertex indices to keep fixed during remeshing.
199+ Mesh that the points are pulled onto.
187200
188201 Returns
189202 -------
190- tuple
191- A tuple containing:
192-
193- - Remeshed mesh vertices as an Nx3 numpy array.
194- - Remeshed mesh faces as an Mx3 numpy array.
195- - Dual mesh vertices as an Nx3 numpy array.
196- - Variable-length faces as a list of lists of vertex indices.
197-
198- Notes
199- -----
200- This dual mesh implementation includes proper boundary handling by:
201- 1. Creating vertices at face centroids of the primal mesh
202- 2. Creating additional vertices at boundary edge midpoints
203- 3. Creating proper connections for boundary edges
203+ list[list[float]]
204+ The pulled points (vertices on the mesh surface).
204205
205206 """
206- V , F = mesh
207- V = np .asarray (V , dtype = np .float64 , order = "C" )
208- F = np .asarray (F , dtype = np .int32 , order = "C" )
209- fixed_vertices = np .asarray (fixed_vertices , dtype = np .int32 , order = "C" )
210- return _meshing .remesh_dual (V , F , fixed_vertices , length_factor , number_of_iterations , angle_radians , scale_factor )
207+
208+ V_target , F_target = mesh .to_vertices_and_faces (triangulated = True )
209+ numpy_V_source = np .asarray (points , dtype = np .float64 , order = "C" )
210+ numpy_N_source = np .asarray (normals , dtype = np .float64 , order = "C" )
211+ numpy_V_target = np .asarray (V_target , dtype = np .float64 , order = "C" )
212+ numpy_F_target = np .asarray (F_target , dtype = np .int32 , order = "C" )
213+ _meshing .pmp_pull (numpy_V_target , numpy_F_target , numpy_V_source , numpy_N_source )
214+ return numpy_V_source
0 commit comments