From 7ed78d9355e6dd1f15a1ef01b4869a3dd9344edc Mon Sep 17 00:00:00 2001 From: nash1111 Date: Sun, 8 Feb 2026 19:39:26 +0900 Subject: [PATCH] feat: add 3D mesh generation examples - simple_3d_tetrahedralization: Bowyer-Watson 3D on unit cube vertices - sphere_mesh_export: Marching cubes sphere exported to STL, OBJ, GLB, quantized GLB, and VTK formats - pipeline_example: All pipeline algorithms (marching cubes sphere/torus, surface_to_volume, octree_refined, voxel_refined) with file export - Update .gitignore for generated output files - Update README with 3D example instructions Closes #48 Co-Authored-By: Claude Opus 4.6 --- .gitignore | 6 ++ README.md | 19 ++++++ examples/pipeline_example.rs | 85 ++++++++++++++++++++++++ examples/simple_3d_tetrahedralization.rs | 64 ++++++++++++++++++ examples/sphere_mesh_export.rs | 66 ++++++++++++++++++ 5 files changed, 240 insertions(+) create mode 100644 examples/pipeline_example.rs create mode 100644 examples/simple_3d_tetrahedralization.rs create mode 100644 examples/sphere_mesh_export.rs diff --git a/.gitignore b/.gitignore index 47dbd4a..6649f40 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,9 @@ Cargo.lock # wasm-pack output pkg/ + +# Generated example outputs +examples/*.stl +examples/*.obj +examples/*.glb +examples/*.vtk diff --git a/README.md b/README.md index 47b5cb3..2898914 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ let tetrahedra = surface_to_volume(8, 8, 8, min, max, &sphere, 0.0); ## Examples +### 2D + ``` cargo run --example simple_2d_triangulation cargo run --example 2d_plot 100 @@ -123,6 +125,23 @@ cargo run --example 2d_plot 100 ![10000points](examples/delaunay_2d_10000_points.png) ![100000points](examples/delaunay_2d_100000_points.png) +### 3D + +``` +# Bowyer-Watson 3D on cube vertices +cargo run --example simple_3d_tetrahedralization + +# Marching cubes sphere → export to STL, OBJ, GLB, VTK +cargo run --example sphere_mesh_export + +# All pipeline algorithms (marching cubes, surface_to_volume, octree_refined, voxel_refined) +cargo run --example pipeline_example +``` + +Output files are written to `examples/` and can be viewed with: +- **STL/OBJ/GLB** - MeshLab, Blender, [glTF Viewer](https://gltf-viewer.donmccurdy.com/) +- **VTK** - ParaView + ## Benchmarks ``` diff --git a/examples/pipeline_example.rs b/examples/pipeline_example.rs new file mode 100644 index 0000000..203e28d --- /dev/null +++ b/examples/pipeline_example.rs @@ -0,0 +1,85 @@ +use meshing::export::{faces_to_stl, tetrahedra_to_stl, tetrahedra_to_vtk}; +use meshing::marching_cubes::marching_cubes; +use meshing::pipeline::{octree_refined, surface_to_volume, voxel_refined}; +use meshing::Point3D; +use std::fs; + +fn main() { + let min = Point3D { + index: 0, + x: -2.0, + y: -2.0, + z: -2.0, + }; + let max = Point3D { + index: 0, + x: 2.0, + y: 2.0, + z: 2.0, + }; + + // --- Marching Cubes: sphere isosurface --- + println!("=== Marching Cubes (sphere) ==="); + let sphere = |x: f64, y: f64, z: f64| x * x + y * y + z * z - 1.0; + let faces = marching_cubes(15, 15, 15, min, max, &sphere, 0.0); + println!(" Faces: {}", faces.len()); + let stl = faces_to_stl(&faces, "marching_cubes_sphere"); + fs::write("examples/marching_cubes_sphere.stl", &stl).unwrap(); + println!(" -> examples/marching_cubes_sphere.stl"); + + // --- Marching Cubes: torus isosurface --- + println!("\n=== Marching Cubes (torus) ==="); + let torus = |x: f64, y: f64, z: f64| { + let r_major = 1.0; + let r_minor = 0.4; + let q = ((x * x + y * y).sqrt() - r_major).powi(2) + z * z; + q - r_minor * r_minor + }; + let faces = marching_cubes(20, 20, 20, min, max, &torus, 0.0); + println!(" Faces: {}", faces.len()); + let stl = faces_to_stl(&faces, "marching_cubes_torus"); + fs::write("examples/marching_cubes_torus.stl", &stl).unwrap(); + println!(" -> examples/marching_cubes_torus.stl"); + + // --- Surface to Volume (sphere) --- + println!("\n=== Surface to Volume (sphere) ==="); + let tets = surface_to_volume(8, 8, 8, min, max, &sphere, 0.0); + println!(" Tetrahedra: {}", tets.len()); + let stl = tetrahedra_to_stl(&tets, "surface_to_volume"); + fs::write("examples/surface_to_volume.stl", &stl).unwrap(); + let vtk = tetrahedra_to_vtk(&tets, "surface_to_volume"); + fs::write("examples/surface_to_volume.vtk", &vtk).unwrap(); + println!(" -> examples/surface_to_volume.stl, .vtk"); + + // --- Octree Refined --- + println!("\n=== Octree Refined ==="); + let tets = octree_refined( + min, + max, + 2, + &|p| p.x * p.x + p.y * p.y + p.z * p.z < 1.5 * 1.5, + 2.0, + ); + println!(" Tetrahedra: {}", tets.len()); + let vtk = tetrahedra_to_vtk(&tets, "octree_refined"); + fs::write("examples/octree_refined.vtk", &vtk).unwrap(); + println!(" -> examples/octree_refined.vtk"); + + // --- Voxel Refined --- + println!("\n=== Voxel Refined ==="); + let tets = voxel_refined( + min, + max, + 3, + 3, + 3, + &|p| p.x * p.x + p.y * p.y + p.z * p.z < 1.5 * 1.5, + 2.0, + ); + println!(" Tetrahedra: {}", tets.len()); + let vtk = tetrahedra_to_vtk(&tets, "voxel_refined"); + fs::write("examples/voxel_refined.vtk", &vtk).unwrap(); + println!(" -> examples/voxel_refined.vtk"); + + println!("\nDone! View STL files with MeshLab/Blender, VTK files with ParaView."); +} diff --git a/examples/simple_3d_tetrahedralization.rs b/examples/simple_3d_tetrahedralization.rs new file mode 100644 index 0000000..4800c9a --- /dev/null +++ b/examples/simple_3d_tetrahedralization.rs @@ -0,0 +1,64 @@ +use meshing::{bowyer_watson_3d, Point3D}; + +fn main() { + let cube = vec![ + Point3D { + index: 0, + x: 0.0, + y: 0.0, + z: 0.0, + }, + Point3D { + index: 1, + x: 1.0, + y: 0.0, + z: 0.0, + }, + Point3D { + index: 2, + x: 0.0, + y: 1.0, + z: 0.0, + }, + Point3D { + index: 3, + x: 1.0, + y: 1.0, + z: 0.0, + }, + Point3D { + index: 4, + x: 0.0, + y: 0.0, + z: 1.0, + }, + Point3D { + index: 5, + x: 1.0, + y: 0.0, + z: 1.0, + }, + Point3D { + index: 6, + x: 0.0, + y: 1.0, + z: 1.0, + }, + Point3D { + index: 7, + x: 1.0, + y: 1.0, + z: 1.0, + }, + ]; + + let tetrahedra = bowyer_watson_3d(cube); + println!("3D Delaunay tetrahedralization of a unit cube:"); + println!(" Number of tetrahedra: {}", tetrahedra.len()); + for (i, tet) in tetrahedra.iter().enumerate() { + println!( + " Tet {}: ({}, {}, {}, {})", + i, tet.a.index, tet.b.index, tet.c.index, tet.d.index + ); + } +} diff --git a/examples/sphere_mesh_export.rs b/examples/sphere_mesh_export.rs new file mode 100644 index 0000000..3b8dd2f --- /dev/null +++ b/examples/sphere_mesh_export.rs @@ -0,0 +1,66 @@ +use meshing::export::{ + faces_to_glb, faces_to_glb_quantized, faces_to_obj, faces_to_stl, tetrahedra_to_vtk, +}; +use meshing::marching_cubes::marching_cubes; +use meshing::pipeline::surface_to_volume; +use meshing::Point3D; +use std::fs; + +fn main() { + let min = Point3D { + index: 0, + x: -2.0, + y: -2.0, + z: -2.0, + }; + let max = Point3D { + index: 0, + x: 2.0, + y: 2.0, + z: 2.0, + }; + let sphere = |x: f64, y: f64, z: f64| x * x + y * y + z * z - 1.0; + + // Generate sphere surface mesh with marching cubes + println!("Generating sphere surface with marching cubes (resolution 20x20x20)..."); + let faces = marching_cubes(20, 20, 20, min, max, &sphere, 0.0); + println!(" Surface faces: {}", faces.len()); + + // Export surface to STL + let stl = faces_to_stl(&faces, "sphere"); + fs::write("examples/sphere.stl", &stl).unwrap(); + println!(" Wrote examples/sphere.stl ({} bytes)", stl.len()); + + // Export surface to OBJ + let obj = faces_to_obj(&faces); + fs::write("examples/sphere.obj", &obj).unwrap(); + println!(" Wrote examples/sphere.obj ({} bytes)", obj.len()); + + // Export surface to GLB + let glb = faces_to_glb(&faces); + fs::write("examples/sphere.glb", &glb).unwrap(); + println!(" Wrote examples/sphere.glb ({} bytes)", glb.len()); + + // Export surface to quantized GLB + let glb_q = faces_to_glb_quantized(&faces); + fs::write("examples/sphere_quantized.glb", &glb_q).unwrap(); + println!( + " Wrote examples/sphere_quantized.glb ({} bytes, {:.0}% of regular)", + glb_q.len(), + glb_q.len() as f64 / glb.len() as f64 * 100.0 + ); + + // Generate volume mesh with surface_to_volume pipeline + println!("\nGenerating volume mesh with surface_to_volume pipeline (8x8x8)..."); + let tetrahedra = surface_to_volume(8, 8, 8, min, max, &sphere, 0.0); + println!(" Volume tetrahedra: {}", tetrahedra.len()); + + // Export volume to VTK + let vtk = tetrahedra_to_vtk(&tetrahedra, "sphere_volume"); + fs::write("examples/sphere_volume.vtk", &vtk).unwrap(); + println!(" Wrote examples/sphere_volume.vtk ({} bytes)", vtk.len()); + + println!("\nDone! Output files can be viewed with:"); + println!(" STL/OBJ/GLB -> MeshLab, Blender, https://gltf-viewer.donmccurdy.com/"); + println!(" VTK -> ParaView"); +}