Files
ViewDesignEngine/examples/08_3d_print/main.cpp
T
茂之钳 080776930f
CI / Build & Test (push) Failing after 42s
CI / Release Build (push) Failing after 32s
feat(examples): add 3D print pipeline, B-Rep fab, and SDF optimization demo
- 08_3d_print: Complete SDF→marching cubes→STL pipeline with mesh stats
  (volume, bounding box) and both binary + ASCII STL exports
- 09_brep_fab: Fabrication-oriented B-Rep modeling: box→shell→STEP+GLB
  with AP214 header preview
- python_examples/sdf_optimize_demo.py: SDF parameter optimization
  from point clouds with analytic gradient descent
- Update examples/CMakeLists.txt to include new subdirectories
- Add __pycache__/ to .gitignore
2026-07-24 10:04:15 +00:00

144 lines
5.9 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)
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<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 = 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;
}