#pragma once #include "vde/sdf/sdf_tree.h" #include "vde/core/point.h" #include #include namespace vde::sdf { using core::Point3D; using core::Vector3D; // ──────────────────────────────────────────────── // Shape Optimization // ──────────────────────────────────────────────── /// Optimization result struct OptimizeResult { SdfNodePtr optimized_shape; double final_loss; int iterations; bool converged; std::vector loss_history; }; /// Loss function type: takes SDF evaluator + target, returns scalar loss using LossFn = std::function&)>; // ── Shape Fitting ─────────────────────────────── /// Fit a parameterized shape to a target point cloud by minimizing /// distance of surface to target points. /// @param initial Initial shape (sphere, box, cylinder, etc.) /// @param target_points Target point cloud (surface samples) /// @param learning_rate Gradient descent step size /// @param max_iterations Maximum iterations /// @return Optimized shape + convergence info [[nodiscard]] OptimizeResult fit_to_point_cloud( const SdfNodePtr& initial, const std::vector& target_points, double learning_rate = 0.01, int max_iterations = 100); /// Fit a shape to minimize SDF values at target points (drive surface to points) /// Loss = mean(|sdf(p_i)|) for p_i in targets [[nodiscard]] OptimizeResult fit_surface_to_points( const SdfNodePtr& initial, const std::vector& surface_points, double learning_rate = 0.01, int max_iterations = 100); /// Fit a shape to match another SDF (shape matching) /// Loss = mean((sdf_a(p_i) - sdf_b(p_i))^2) over sample grid [[nodiscard]] OptimizeResult fit_to_sdf( const SdfNodePtr& source, const std::function& target_sdf, const Point3D& bmin, const Point3D& bmax, int grid_resolution = 16, double learning_rate = 0.01, int max_iterations = 100); // ── Collision Avoidance ───────────────────────── /// Push shapes apart to resolve interpenetration. /// Modifies shapes in-place by translating them. /// @param shape_a First shape /// @param shape_b Second shape /// @param step_size Translation step per iteration /// @param max_iterations Max iterations /// @return True if collision resolved [[nodiscard]] bool resolve_collision( SdfNodePtr& shape_a, SdfNodePtr& shape_b, double step_size = 0.1, int max_iterations = 50); /// Find minimum translation distance to avoid collision [[nodiscard]] double penetration_depth( const SdfNodePtr& shape_a, const SdfNodePtr& shape_b, const Point3D& bmin, const Point3D& bmax, int samples = 1000); // ── Reachability / Accessibility ───────────────── /// Compute accessibility score at point p on surface of shape. /// Returns 0 (inaccessible) to 1 (fully accessible). /// Uses hemisphere sampling + occlusion testing. [[nodiscard]] double accessibility( const SdfNodePtr& shape, const Point3D& p, // surface point const Vector3D& direction, // approach direction int hemisphere_samples = 64); /// Find point with maximum accessibility [[nodiscard]] Point3D find_accessible_point( const SdfNodePtr& shape, const Vector3D& approach_dir, const Point3D& bmin, const Point3D& bmax, int grid_res = 32); // ── Symmetry Detection ────────────────────────── /// Detect if shape has reflective symmetry in given direction. /// Returns symmetry score: 0 = asymmetric, 1 = perfectly symmetric. [[nodiscard]] double symmetry_score( const SdfNodePtr& shape, const Point3D& bmin, const Point3D& bmax, const Vector3D& plane_normal, double plane_offset = 0.0, int samples = 1000); /// Find the best symmetry plane struct SymmetryResult { double score; Vector3D normal; double offset; }; [[nodiscard]] SymmetryResult find_symmetry_plane( const SdfNodePtr& shape, const Point3D& bmin, const Point3D& bmax, int samples = 1000); // ── SDF Volume Computation ────────────────────── /// Approximate volume of implicit shape via Monte Carlo sampling [[nodiscard]] double estimate_volume( const SdfNodePtr& shape, const Point3D& bmin, const Point3D& bmax, int samples = 10000); /// Compute center of mass via Monte Carlo [[nodiscard]] Point3D center_of_mass( const SdfNodePtr& shape, const Point3D& bmin, const Point3D& bmax, int samples = 10000); // ── Parameter Collection ──────────────────────── /// A reference to a mutable double parameter inside an SDF tree struct ParamRef { double* value; std::string name; }; /// Collect all mutable numeric parameters from primitive leaf nodes [[nodiscard]] std::vector collect_params(SdfNodePtr& root); /// Compute numerical gradient of a scalar loss w.r.t. collected parameters [[nodiscard]] std::vector numerical_gradient( const std::vector& params, const std::function& loss_fn, double eps = 1e-6); // ── Gradient Descent Utility ──────────────────── /// Simple gradient descent with fixed learning rate class GradientDescent { public: explicit GradientDescent(double lr) : lr_(lr) {} /// Step parameters toward minimizing loss. /// @param params Parameter vector (mutable) /// @param grad_fn Function that computes gradient for given params; /// returns loss as scalar and fills gradient vector. /// @return Current loss double step(std::vector& params, const std::function&, std::vector&)>& grad_fn); void set_learning_rate(double lr) { lr_ = lr; } [[nodiscard]] double learning_rate() const { return lr_; } [[nodiscard]] int iteration() const { return iteration_; } private: double lr_; int iteration_ = 0; }; } // namespace vde::sdf