Files
ViewDesignEngine/include/vde/sdf/sdf_primitives.h
T
茂之钳 b7419a7881
CI / Build & Test (push) Failing after 29s
CI / Release Build (push) Failing after 35s
feat(sdf): S11 — SDF 隐式建模完整模块
S11-A: SDF 图元库
- 14 种基础形状(inline header)
- sphere/box/round_box/torus/capsule/cylinder/cone/plane/ellipsoid
- triangular_prism/hex_prism/link/wedge
- 2D 挤出(extrusion/extrusion_bounded/revolution)

S11-B: SDF 操作 + 域变形
- 锐利布尔: union/intersection/difference
- 平滑布尔: smooth_union/smooth_intersection/smooth_difference
- 修饰器: round/onion
- 域变形: repeat/mirror/rotate/translate/scale/twist/bend/elongate/displace/cheap_bend
- 24 项测试

S11-C: CSG 表达式树 + 网格转换 + Python 绑定
- SdfNode 树结构 — 工厂构造函数,递归求值
- sdf_to_mesh — 基于 marching_cubes 的 SDF→网格
- bind_sdf — pybind11 Python 绑定

文件: 14 文件,2,545 行(测试 1,296 行)
2026-07-24 07:04:55 +00:00

164 lines
7.6 KiB
C++

#pragma once
#include "vde/core/point.h"
#include <cmath>
#include <algorithm>
namespace vde::sdf {
using core::Point3D;
using core::Vector3D;
// ═══════════════════════════════════════════════
// Basic Primitives
// ═══════════════════════════════════════════════
/// Signed distance to sphere centered at origin
[[nodiscard]] inline double sphere(const Point3D& p, double radius) {
return p.norm() - radius;
}
/// Signed distance to axis-aligned box centered at origin
/// half_extents = (hx, hy, hz) — half-dimensions in each axis
[[nodiscard]] inline double box(const Point3D& p, const Point3D& half_extents) {
Point3D q = p.cwiseAbs() - half_extents;
return q.cwiseMax(0.0).norm()
+ std::min(std::max({q.x(), q.y(), q.z()}), 0.0);
}
/// Signed distance to rounded box centered at origin
/// Chamfer radius r is subtracted from the box distance
[[nodiscard]] inline double round_box(const Point3D& p, const Point3D& half_extents, double r) {
Point3D q = p.cwiseAbs() - half_extents;
return q.cwiseMax(0.0).norm()
+ std::min(std::max({q.x(), q.y(), q.z()}), 0.0)
- r;
}
/// Signed distance to torus in XZ plane, Y-axis symmetry
/// major_radius = distance from origin to tube center
/// minor_radius = tube thickness radius
[[nodiscard]] inline double torus(const Point3D& p, double major_radius, double minor_radius) {
double qx = std::sqrt(p.x() * p.x() + p.z() * p.z()) - major_radius;
return std::sqrt(qx * qx + p.y() * p.y()) - minor_radius;
}
/// Signed distance to capsule (line segment swept with sphere of given radius)
/// a, b = segment endpoints
[[nodiscard]] inline double capsule(const Point3D& p, const Point3D& a, const Point3D& b, double radius) {
Point3D pa = p - a;
Point3D ba = b - a;
double h = std::clamp(pa.dot(ba) / ba.squaredNorm(), 0.0, 1.0);
return (pa - ba * h).norm() - radius;
}
/// Signed distance to capped cylinder along Y axis, centered at origin
/// radius = cylinder radius, height = full height (from -h/2 to +h/2)
[[nodiscard]] inline double cylinder(const Point3D& p, double radius, double height) {
double d_xz = std::sqrt(p.x() * p.x() + p.z() * p.z()) - radius;
double d_y = std::abs(p.y()) - height * 0.5;
return std::min(std::max(d_xz, d_y), 0.0)
+ std::sqrt(std::max(d_xz, 0.0) * std::max(d_xz, 0.0)
+ std::max(d_y, 0.0) * std::max(d_y, 0.0));
}
/// Signed distance to plane through origin with given normal
/// Normal must be unit length; offset shifts plane along normal
[[nodiscard]] inline double plane(const Point3D& p, const Vector3D& normal, double offset) {
return p.dot(normal) - offset;
}
/// Signed distance to ellipsoid centered at origin
/// radii = semi-axis lengths (rx, ry, rz)
/// Uses the standard bounded-gradient approximation: (|p/radii| - 1) * min(radii)
[[nodiscard]] inline double ellipsoid(const Point3D& p, const Point3D& radii) {
Point3D pr(p.x() / radii.x(), p.y() / radii.y(), p.z() / radii.z());
double k0 = pr.norm();
double k1 = Point3D(p.x() / (radii.x() * radii.x()),
p.y() / (radii.y() * radii.y()),
p.z() / (radii.z() * radii.z())).norm();
return k0 * (k0 - 1.0) / k1;
}
// ═══════════════════════════════════════════════
// Parametric Primitives
// ═══════════════════════════════════════════════
/// Signed distance to regular hexagonal prism in XZ plane, extruded along Y
/// radius = circumradius of hexagon (center to vertex)
/// height = full extrusion height (±h/2 along Y)
[[nodiscard]] inline double hex_prism(const Point3D& p, double radius, double height) {
double px = std::abs(p.x());
double pz = std::abs(p.z());
// 2D hexagon SDF (pointy-top in XZ plane)
double d_hex = std::max(px + pz * 0.577350269189626, pz * 1.154700538379252) - radius;
// Extrude along Y
return std::max(d_hex, std::abs(p.y()) - height * 0.5);
}
/// Signed distance to infinite cylinder through origin along given axis
/// Axis must be unit length
[[nodiscard]] inline double infinite_cylinder(const Point3D& p, const Vector3D& axis, double radius) {
return p.cross(axis).norm() - radius;
}
/// Signed distance to wedge — half of a box cut diagonally in XZ plane
/// w = width (X), h = height (Y), d = depth (Z)
/// The wedge occupies the half where z ≥ x inside the box
[[nodiscard]] inline double wedge(const Point3D& p, double w, double h, double d) {
Point3D q = p.cwiseAbs() - Point3D(w * 0.5, h * 0.5, d * 0.5);
double d_box = q.cwiseMax(0.0).norm()
+ std::min(std::max({q.x(), q.y(), q.z()}), 0.0);
// Diagonal plane x - z ≤ 0 (i.e., z ≥ x)
double d_cut = (p.x() - p.z()) * 0.707106781186548; // 1/√2
return std::max(d_box, d_cut);
}
// ═══════════════════════════════════════════════
// 2D Extrusions & Revolution
// ═══════════════════════════════════════════════
/// Extrude a 2D SDF infinitely along Z axis
/// sdf_2d = pre-computed 2D SDF value at (p.x, p.y)
[[nodiscard]] inline double extrusion(const Point3D& /*p*/, double sdf_2d) {
return sdf_2d;
}
/// Bounded extrusion: 2D SDF intersected with Z slab ±half_height
/// sdf_2d = pre-computed 2D SDF value at (p.x, p.y)
[[nodiscard]] inline double extrusion_bounded(const Point3D& p, double sdf_2d, double half_height) {
return std::max(sdf_2d, std::abs(p.z()) - half_height);
}
/// Revolve a 2D profile around Y axis
/// sdf_2d = pre-computed 2D SDF value at (|p.xz|-offset, p.y)
/// offset = radial offset from Y axis
[[nodiscard]] inline double revolution(const Point3D& /*p*/, double sdf_2d, double /*offset*/) {
return sdf_2d;
}
// ═══════════════════════════════════════════════
// Complex Primitives (implemented in src/sdf/)
// ═══════════════════════════════════════════════
/// Signed distance to capped cone, centered at origin along Y axis
/// Apex at y = +height/2, base at y = -height/2
/// angle_rad = half-angle at apex, height = full height
[[nodiscard]] double cone(const Point3D& p, double angle_rad, double height);
/// Signed distance to triangular prism with arbitrary triangle cross-section
/// Triangle defined by vertices a,b,c in XY plane, extruded ±height/2 along Z
[[nodiscard]] double triangular_prism(const Point3D& p, const Point3D& a, const Point3D& b,
const Point3D& c, double height);
/// Signed distance to link (two parallel toruses connected along X)
/// length = spacing between torus centers along X
/// major_r = major radius of each torus
/// minor_r = minor (tube) radius of each torus
[[nodiscard]] double link(const Point3D& p, double length, double major_r, double minor_r);
/// Signed distance to infinite cone from apex along axis
/// axis must be unit length, angle_rad = half-angle
[[nodiscard]] double infinite_cone(const Point3D& p, const Point3D& apex,
const Vector3D& axis, double angle_rad);
} // namespace vde::sdf