138 lines
4.5 KiB
C++
138 lines
4.5 KiB
C++
#pragma once
|
|
#include "vde/core/point.h"
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
|
|
namespace vde::sdf {
|
|
|
|
using core::Point3D;
|
|
using core::Vector3D;
|
|
|
|
// ── Boolean operations on distance values ──
|
|
|
|
[[nodiscard]] inline double op_union(double d1, double d2) {
|
|
return std::min(d1, d2);
|
|
}
|
|
|
|
[[nodiscard]] inline double op_intersection(double d1, double d2) {
|
|
return std::max(d1, d2);
|
|
}
|
|
|
|
[[nodiscard]] inline double op_difference(double d1, double d2) {
|
|
return std::max(d1, -d2);
|
|
}
|
|
|
|
[[nodiscard]] inline double op_smooth_union(double d1, double d2, double k) {
|
|
if (k <= 0.0) return std::min(d1, d2);
|
|
double h = std::clamp(0.5 + 0.5 * (d1 - d2) / k, 0.0, 1.0);
|
|
return d1 * (1.0 - h) + d2 * h - k * h * (1.0 - h);
|
|
}
|
|
|
|
[[nodiscard]] inline double op_smooth_intersection(double d1, double d2, double k) {
|
|
if (k <= 0.0) return std::max(d1, d2);
|
|
double h = std::clamp(0.5 - 0.5 * (d1 - d2) / k, 0.0, 1.0);
|
|
return d1 * (1.0 - h) + d2 * h + k * h * (1.0 - h);
|
|
}
|
|
|
|
[[nodiscard]] inline double op_smooth_difference(double d1, double d2, double k) {
|
|
if (k <= 0.0) return std::max(d1, -d2);
|
|
double h = std::clamp(0.5 - 0.5 * (d2 + d1) / k, 0.0, 1.0);
|
|
return d1 * (1.0 - h) + (-d2) * h + k * h * (1.0 - h);
|
|
}
|
|
|
|
// ── Modifiers (operate on a distance value) ──
|
|
|
|
[[nodiscard]] inline double op_round(double d, double r) {
|
|
return d - r;
|
|
}
|
|
|
|
[[nodiscard]] inline double op_onion(double d, double thickness) {
|
|
return std::abs(d) - thickness;
|
|
}
|
|
|
|
// ── Domain deformation operators (transform point space) ──
|
|
|
|
/// Infinite repetition in a cell
|
|
[[nodiscard]] inline Point3D op_repeat(const Point3D& p, const Point3D& cell) {
|
|
auto wrap = [](double v, double c) {
|
|
return c > 0.0 ? v - c * std::round(v / c) : v;
|
|
};
|
|
return Point3D(wrap(p.x(), cell.x()),
|
|
wrap(p.y(), cell.y()),
|
|
wrap(p.z(), cell.z()));
|
|
}
|
|
|
|
/// Mirror across X=0 plane, with optional offset
|
|
[[nodiscard]] inline Point3D op_mirror_x(const Point3D& p, double offset = 0.0) {
|
|
return Point3D(offset + std::abs(p.x() - offset), p.y(), p.z());
|
|
}
|
|
|
|
/// Mirror across Y=0
|
|
[[nodiscard]] inline Point3D op_mirror_y(const Point3D& p) {
|
|
return Point3D(p.x(), std::abs(p.y()), p.z());
|
|
}
|
|
|
|
/// Mirror across Z=0
|
|
[[nodiscard]] inline Point3D op_mirror_z(const Point3D& p) {
|
|
return Point3D(p.x(), p.y(), std::abs(p.z()));
|
|
}
|
|
|
|
/// Translate space (inverse for SDF: move the world, not the object)
|
|
[[nodiscard]] inline Point3D op_translate(const Point3D& p, const Point3D& offset) {
|
|
return p - offset;
|
|
}
|
|
|
|
/// Rotate around Y axis by angle_rad (inverse for SDF)
|
|
[[nodiscard]] inline Point3D op_rotate(const Point3D& p, double angle_rad) {
|
|
double c = std::cos(-angle_rad);
|
|
double s = std::sin(-angle_rad);
|
|
return Point3D(p.x() * c - p.z() * s, p.y(), p.x() * s + p.z() * c);
|
|
}
|
|
|
|
/// Non-uniform scale (inverse for SDF)
|
|
[[nodiscard]] inline Point3D op_scale(const Point3D& p, const Point3D& s) {
|
|
return Point3D(p.x() / s.x(), p.y() / s.y(), p.z() / s.z());
|
|
}
|
|
|
|
/// Twist space around Y axis
|
|
[[nodiscard]] inline Point3D op_twist(const Point3D& p, double amount) {
|
|
double c = std::cos(amount * p.y());
|
|
double s = std::sin(amount * p.y());
|
|
return Point3D(p.x() * c - p.z() * s, p.y(), p.x() * s + p.z() * c);
|
|
}
|
|
|
|
/// Bend space
|
|
[[nodiscard]] inline Point3D op_bend(const Point3D& p, double k) {
|
|
double c = std::cos(k * p.x());
|
|
double s = std::sin(k * p.x());
|
|
return Point3D(c * p.x() - s * p.y(), s * p.x() + c * p.y(), p.z());
|
|
}
|
|
|
|
/// Elongate (stretch space, eliminating interior along major axes)
|
|
[[nodiscard]] inline Point3D op_elongate(const Point3D& p, const Point3D& h) {
|
|
Point3D q = p.cwiseAbs() - h;
|
|
return Point3D(
|
|
q.x() > 0.0 ? p.x() - std::copysign(h.x(), p.x()) : 0.0,
|
|
q.y() > 0.0 ? p.y() - std::copysign(h.y(), p.y()) : 0.0,
|
|
q.z() > 0.0 ? p.z() - std::copysign(h.z(), p.z()) : 0.0
|
|
);
|
|
}
|
|
|
|
/// Cheap bend (simplified bending)
|
|
[[nodiscard]] inline Point3D op_cheap_bend(const Point3D& p, double k) {
|
|
double c = std::cos(k * p.x());
|
|
double s = std::sin(k * p.x());
|
|
return Point3D(p.x(), p.y() * c - p.z() * s, p.y() * s + p.z() * c);
|
|
}
|
|
|
|
/// Displace distance field with sinusoidal noise
|
|
[[nodiscard]] inline double op_displace(double d, const Point3D& p,
|
|
double amplitude, double frequency) {
|
|
double noise = std::sin(p.x() * frequency) *
|
|
std::sin(p.y() * frequency) *
|
|
std::sin(p.z() * frequency);
|
|
return d + amplitude * noise;
|
|
}
|
|
|
|
} // namespace vde::sdf
|