1- use numpy:: PyUntypedArray ;
1+ use numpy:: prelude:: * ;
2+ use numpy:: { Element , PyArray3 , PyUntypedArray } ;
23use pyo3:: prelude:: * ;
34use pyo3_stub_gen:: derive:: * ;
5+ use splashsurf_lib:: nalgebra:: Vector3 ;
6+ use splashsurf_lib:: { DensityMap , Real , UniformGrid } ;
47
58use crate :: mesh:: { PyTriMesh3d , get_triangle_mesh_generic} ;
69use crate :: uniform_grid:: PyUniformGrid ;
7- use crate :: utils:: * ;
10+ use crate :: utils;
11+ use crate :: utils:: IndexT ;
812
913/// Checks the consistency of a reconstructed surface mesh (watertightness, manifoldness), optionally returns a string with details if problems are found
1014#[ gen_stub_pyfunction]
@@ -22,7 +26,7 @@ pub fn check_mesh_consistency<'py>(
2226 let py = mesh. py ( ) ;
2327
2428 // Try to extract the triangle mesh;
25- let mesh = get_triangle_mesh_generic ( & mesh) . ok_or_else ( pyerr_only_triangle_mesh) ?;
29+ let mesh = get_triangle_mesh_generic ( & mesh) . ok_or_else ( utils :: pyerr_only_triangle_mesh) ?;
2630 let mesh = mesh. borrow ( py) ;
2731
2832 if let ( Some ( grid) , Some ( mesh) ) = ( grid. as_f32 ( ) , mesh. as_f32 ( ) ) {
@@ -44,6 +48,66 @@ pub fn check_mesh_consistency<'py>(
4448 )
4549 . err ( ) )
4650 } else {
47- Err ( pyerr_scalar_type_mismatch ( ) )
51+ Err ( utils:: pyerr_scalar_type_mismatch ( ) )
52+ }
53+ }
54+
55+ /// Performs a standard marching cubes triangulation of a 3D array of values
56+ #[ gen_stub_pyfunction]
57+ #[ pyfunction]
58+ #[ pyo3( name = "marching_cubes" ) ]
59+ #[ pyo3( signature = ( values, * , cube_size, iso_surface_threshold, translation = None ) ) ]
60+ pub fn marching_cubes < ' py > (
61+ values : & Bound < ' py , PyUntypedArray > ,
62+ cube_size : f64 ,
63+ iso_surface_threshold : f64 ,
64+ translation : Option < [ f64 ; 3 ] > ,
65+ ) -> PyResult < ( PyTriMesh3d , PyUniformGrid ) > {
66+ assert_eq ! ( values. shape( ) . len( ) , 3 , "values must be a 3D array" ) ;
67+
68+ fn triangulate_density_map_generic < ' py , R : Real + Element > (
69+ values : & Bound < ' py , PyArray3 < R > > ,
70+ cube_size : R ,
71+ iso_surface_threshold : R ,
72+ translation : Option < [ R ; 3 ] > ,
73+ ) -> PyResult < ( PyTriMesh3d , PyUniformGrid ) > {
74+ let shape = values. shape ( ) ;
75+ let translation = Vector3 :: from ( translation. unwrap_or ( [ R :: zero ( ) ; 3 ] ) ) ;
76+ let n_cells_per_dim = [
77+ shape[ 0 ] as IndexT - 1 ,
78+ shape[ 1 ] as IndexT - 1 ,
79+ shape[ 2 ] as IndexT - 1 ,
80+ ] ;
81+
82+ let grid = UniformGrid :: new ( & translation, & n_cells_per_dim, cube_size)
83+ . map_err ( anyhow:: Error :: from) ?;
84+
85+ // TODO: Replace with borrow
86+ let values = values. try_readonly ( ) ?. as_slice ( ) ?. to_vec ( ) ;
87+ let density_map = DensityMap :: from ( values) ;
88+
89+ let mesh = splashsurf_lib:: marching_cubes:: triangulate_density_map (
90+ & grid,
91+ & density_map,
92+ iso_surface_threshold,
93+ )
94+ . map_err ( anyhow:: Error :: from) ?;
95+ Ok ( (
96+ PyTriMesh3d :: try_from_generic ( mesh) ?,
97+ PyUniformGrid :: try_from_generic ( grid) ?,
98+ ) )
99+ }
100+
101+ if let Ok ( values) = values. downcast :: < PyArray3 < f32 > > ( ) {
102+ triangulate_density_map_generic (
103+ & values,
104+ cube_size as f32 ,
105+ iso_surface_threshold as f32 ,
106+ translation. map ( |t| t. map ( |t| t as f32 ) ) ,
107+ )
108+ } else if let Ok ( values) = values. downcast :: < PyArray3 < f64 > > ( ) {
109+ triangulate_density_map_generic ( & values, cube_size, iso_surface_threshold, translation)
110+ } else {
111+ Err ( utils:: pyerr_unsupported_scalar ( ) )
48112 }
49113}
0 commit comments