Files
ViewDesignEngine/examples/08_3d_print/main.cpp
T
茂之钳 583ff3ae2e
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 36s
fix: remove undefined compute_volume from 3d_print example
2026-07-24 10:37:31 +00:00

142 lines
5.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// \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 <vde/sdf/sdf_primitives.h>
#include <vde/sdf/sdf_operations.h>
#include <vde/sdf/sdf_to_mesh.h>
#include <vde/mesh/marching_cubes.h>
#include <vde/mesh/halfedge_mesh.h>
#include <vde/foundation/io_stl.h>
#include <vde/core/aabb.h>
#include <iostream>
#include <iomanip>
#include <cmath>
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)
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<StlTriangle> to_stl_triangles(const HalfedgeMesh& m) {
std::vector<StlTriangle> 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<int>(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 = 30;
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"
<< " 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;
}