Files
ViewDesignEngine/examples/11_gear/main.cpp
T
茂之钳 12db7d3e5a
CI / Build & Test (push) Failing after 1m31s
CI / Release Build (push) Failing after 31s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v3.5): perf caching + measure + flange/gear + feature tree + assembly constraints
2026-07-24 14:02:24 +00:00

175 lines
7.7 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 11_gear/main.cpp
/// \brief Parametric gear generator — SDF → Marching Cubes → STL export
///
/// Demonstrates a design-exploration workflow: define gear geometry as an
/// implicit surface (SDF), extract a triangle mesh via Marching Cubes,
/// and export to STL for 3D printing or further processing.
#include <vde/mesh/marching_cubes.h>
#include <vde/mesh/halfedge_mesh.h>
#include <vde/foundation/io_stl.h>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <array>
using namespace vde::mesh;
using namespace vde::foundation;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════════════════
// Gear SDF
// ═══════════════════════════════════════════════════════════════════════
/**
* @brief Build a parametric gear SDF function.
*
* The gear lies in the XZ plane and is extruded along the Y axis.
* Teeth are generated as sinusoidal bumps on the pitch circle,
* producing a simplified but visually recognizable gear profile.
*
* @param teeth Number of teeth
* @param pitch_r Pitch circle radius
* @param addendum Tooth height above pitch circle
* @param dedendum Tooth depth below pitch circle
* @param thickness Total thickness (Y-axis extrusion)
* @return SDF function f(x, y, z) → signed distance
*/
inline auto make_gear_sdf(int teeth, double pitch_r,
double addendum, double dedendum,
double thickness) {
return [=](double x, double y, double z) -> double {
// ── 2D gear profile in XZ plane ──
double r = std::sqrt(x * x + z * z);
double theta = std::atan2(z, x);
// Root circle (inner)
double root_r = pitch_r - dedendum;
double d_root = r - root_r;
// Tooth profile: sinusoidal bumps
// cos(N*theta) = +1 at tooth center, -1 at gap center
// radius at tooth center = pitch_r + addendum
// radius at gap center = pitch_r - dedendum
double bump = (addendum + dedendum) * 0.5 * (1.0 + std::cos(teeth * theta));
double prof_r = root_r + bump;
double d_tooth = r - prof_r;
// Union: point is inside if inside root circle OR inside tooth profile
double d_xy = std::min(d_root, d_tooth);
// ── Extrude along Y axis ──
double d_y = std::abs(y) - thickness * 0.5;
return std::max(d_xy, d_y);
};
}
// ═══════════════════════════════════════════════════════════════════════
// Main — Parametric Gear
// ═══════════════════════════════════════════════════════════════════════
int main() {
std::cout << std::fixed << std::setprecision(2);
std::cout << "╔══════════════════════════════════╗\n"
<< "║ Gear — Parametric SDF Demo ║\n"
<< "╚══════════════════════════════════╝\n\n";
// ── Parameters ───────────────────────────────────────────────────
//
// Standard involute gear parameters (simplified):
// module = pitch diameter / teeth → standard metric sizing
// tooth height = 2.25 × module (addendum + dedendum)
const int teeth = 16;
const double module = 2.0;
const double pitch_r = teeth * module / 2.0; // 16 mm
const double addendum = module; // 2 mm
const double dedendum = 1.25 * module; // 2.5 mm
const double thickness = 10.0;
std::cout << "Parameters:\n"
<< " teeth: " << teeth << "\n"
<< " module: " << module << " mm\n"
<< " pitch Ø: " << pitch_r * 2.0 << " mm\n"
<< " outer Ø: " << (pitch_r + addendum) * 2.0 << " mm\n"
<< " root Ø: " << (pitch_r - dedendum) * 2.0 << " mm\n"
<< " thickness: " << thickness << " mm\n\n";
// ── Step 1: Marching Cubes — SDF → triangle mesh ─────────────────
//
// Sample a bounding box slightly larger than the gear,
// at 128³ voxel resolution for smooth teeth.
const double bb_margin = addendum + dedendum + 2.0;
const double bb_r = pitch_r + addendum + bb_margin;
const int res = 128;
std::cout << "Step 1: Marching Cubes (resolution " << res << "³)\n";
auto gear_sdf = make_gear_sdf(teeth, pitch_r, addendum, dedendum, thickness);
MCMesh mc = marching_cubes(gear_sdf, 0.0,
Point3D(-bb_r, -thickness - 2, -bb_r),
Point3D( bb_r, thickness + 2, bb_r),
res);
std::cout << " vertices: " << mc.vertices.size() << "\n"
<< " triangles: " << mc.triangles.size() << "\n";
// ── Step 2: Build half-edge mesh ─────────────────────────────────
std::cout << "\nStep 2: Build HalfedgeMesh\n";
HalfedgeMesh mesh;
mesh.build_from_triangles(mc.vertices, mc.triangles);
mesh.update_normals();
std::cout << " vertices: " << mesh.num_vertices() << "\n"
<< " faces: " << mesh.num_faces() << "\n"
<< " edges: " << mesh.num_edges() << "\n";
// ── Step 3: Export to STL ────────────────────────────────────────
std::cout << "\nStep 3: Export STL (binary)\n";
std::vector<StlTriangle> stl_tris;
stl_tris.reserve(mesh.num_faces());
for (size_t fi = 0; fi < mesh.num_faces(); ++fi) {
auto verts = mesh.face_vertices(static_cast<int>(fi));
if (verts.size() < 3) continue;
const Point3D& a = mesh.vertex(static_cast<size_t>(verts[0]));
const Point3D& b = mesh.vertex(static_cast<size_t>(verts[1]));
const Point3D& c = mesh.vertex(static_cast<size_t>(verts[2]));
Vector3D n = (b - a).cross(c - a).normalized();
StlTriangle t;
t.normal = n;
t.v0 = a;
t.v1 = b;
t.v2 = c;
stl_tris.push_back(t);
}
const char* stl_path = "gear.stl";
write_stl(stl_path, stl_tris);
std::cout << "" << stl_path << " (" << stl_tris.size()
<< " triangles)\n";
// ── Summary ──────────────────────────────────────────────────────
std::cout << "\n╔══════════════════════════════════════════╗\n"
<< "║ Gear Export Summary ║\n"
<< "╠══════════════════════════════════════════╣\n"
<< "║ SDF → Marching Cubes → STL ✓ ║\n"
<< "╚══════════════════════════════════════════╝\n";
std::cout << "\nDone! Parametric gear exported.\n";
return 0;
}