#pragma once #include "vde/core/point.h" #include #include 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