/// \file 08_3d_print/main.cpp /// \brief Complete 3D printing pipeline: SDF → marching cubes → mesh stats → STL export /// /// Demonstrates end-to-end workflow from parametric SDF shape definition to /// a slicer-ready STL file, with mesh quality statistics along the way. #include #include #include #include #include #include #include #include #include #include using namespace vde::core; using namespace vde::sdf; using namespace vde::mesh; using namespace vde::foundation; // ── helpers ── /// Compute approximate volume via divergence theorem (loop over faces) static double compute_volume(const HalfedgeMesh& m) { double vol = 0.0; for (size_t fi = 0; fi < m.num_faces(); ++fi) { auto verts = m.face_vertices(fi); if (verts.size() < 3) continue; const Point3D& a = m.vertex(verts[0]); const Point3D& b = m.vertex(verts[1]); const Point3D& c = m.vertex(verts[2]); // Signed volume contribution: (1/6) * (a · (b × c)) vol += a.x() * (b.y() * c.z() - b.z() * c.y()) + a.y() * (b.z() * c.x() - b.x() * c.z()) + a.z() * (b.x() * c.y() - b.y() * c.x()); } return std::abs(vol) / 6.0; } /// Convert HalfedgeMesh faces to STL triangles static std::vector to_stl_triangles(const HalfedgeMesh& m) { std::vector tris; tris.reserve(m.num_faces()); for (size_t fi = 0; fi < m.num_faces(); ++fi) { auto verts = m.face_vertices(fi); if (verts.size() < 3) continue; StlTriangle t; t.normal = m.face_normal(static_cast(fi)); t.v0 = m.vertex(verts[0]); t.v1 = m.vertex(verts[1]); t.v2 = m.vertex(verts[2]); tris.push_back(t); } return tris; } // ═══════════════════════════════════════════════════════════════════════ // Main — 3D Print Pipeline // ═══════════════════════════════════════════════════════════════════════ int main() { std::cout << std::fixed << std::setprecision(4); std::cout << "╔══════════════════════════════════╗\n" << "║ 3D Print Pipeline ║\n" << "╚══════════════════════════════════╝\n\n"; // ── Step 1: Define the SDF shape ───────────────────────────────── // // A smooth blend between a box and a torus creates an organic-looking // shape that would be challenging to model with traditional CAD. std::cout << "Step 1: Define SDF shape\n"; // Use Point3D overloads from sdf_primitives.h for type safety auto shape_sdf = [](double x, double y, double z) -> double { Point3D p(x, y, z); double d_box = box(p, Point3D(2.0, 2.0, 2.0)); double d_torus = torus(p, 1.5, 0.3); return op_smooth_union(d_box, d_torus, 0.4); }; std::cout << " shape = op_smooth_union( box(2,2,2), torus(1.5, 0.3), k=0.4 )\n"; // ── Step 2: Marching cubes → triangle mesh ─────────────────────── std::cout << "\nStep 2: Extract isosurface via marching cubes\n"; const Point3D bmin(-3.0, -3.0, -3.0); const Point3D bmax( 3.0, 3.0, 3.0); const int resolution = 80; auto mc = marching_cubes(shape_sdf, 0.0, bmin, bmax, resolution); std::cout << " grid: " << resolution << "³ (" << resolution << " samples per axis)\n"; std::cout << " raw triangles: " << mc.triangles.size() << "\n"; // ── Step 3: Build half-edge mesh & print stats ─────────────────── std::cout << "\nStep 3: Build half-edge mesh + stats\n"; HalfedgeMesh mesh; mesh.build_from_triangles(mc.vertices, mc.triangles); mesh.update_normals(); AABB3D bb = mesh.bounds(); std::cout << " vertices: " << mesh.num_vertices() << "\n" << " faces: " << mesh.num_faces() << "\n" << " edges: " << mesh.num_edges() << "\n" << " volume: " << compute_volume(mesh) << " (approx)\n" << " bounding box:\n" << " min: (" << bb.min().x() << ", " << bb.min().y() << ", " << bb.min().z() << ")\n" << " max: (" << bb.max().x() << ", " << bb.max().y() << ", " << bb.max().z() << ")\n" << " size: (" << bb.max().x() - bb.min().x() << ", " << bb.max().y() - bb.min().y() << ", " << bb.max().z() - bb.min().z() << ")\n"; // ── Step 4: Export STL (binary, for slicers) ───────────────────── std::cout << "\nStep 4: Export STL for slicing\n"; const char* out_path = "output_3d_print.stl"; auto stl_tris = to_stl_triangles(mesh); write_stl(out_path, stl_tris); std::cout << " → " << out_path << " (" << stl_tris.size() << " triangles, binary format)\n"; std::cout << " Slicer-ready! Open in PrusaSlicer, Cura, or Bambu Studio.\n"; // ── Bonus: ASCII variant for inspection ────────────────────────── const char* ascii_path = "output_3d_print_ascii.stl"; write_stl_ascii(ascii_path, stl_tris); std::cout << " → " << ascii_path << " (ASCII, human-readable)\n"; std::cout << "\nDone.\n"; return 0; }