From f3c0bf46d4573bc8592a8dab70695eb1fe8aaef1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 17 Jul 2020 14:09:28 +0200 Subject: [PATCH] finish optimizer interface and remove commented code --- src/libslic3r/Optimizer.hpp | 73 ++++++++++++--------- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 23 +++---- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/Optimizer.hpp b/src/libslic3r/Optimizer.hpp index dc70abe33..6495ae7ff 100644 --- a/src/libslic3r/Optimizer.hpp +++ b/src/libslic3r/Optimizer.hpp @@ -103,6 +103,16 @@ public: bool stop_condition() { return m_stop_condition(); } }; +// Helper class to use optimization methods involving gradient. +template struct ScoreGradient { + double score; + std::optional> gradient; + + ScoreGradient(double s, const std::array &grad) + : score{s}, gradient{grad} + {} +}; + // Helper to be used in static_assert. template struct always_false { enum { value = false }; }; @@ -112,13 +122,13 @@ public: Optimizer(const StopCriteria &) { - static_assert(always_false::value, - "Optimizer unimplemented for given method!"); + static_assert (always_false::value, + "Optimizer unimplemented for given method!"); } - Optimizer &to_min() { return *this; } - Optimizer &to_max() { return *this; } - Optimizer &set_criteria(const StopCriteria &) { return *this; } + Optimizer &to_min() { return *this; } + Optimizer &to_max() { return *this; } + Optimizer &set_criteria(const StopCriteria &) { return *this; } StopCriteria get_criteria() const { return {}; }; template @@ -156,35 +166,20 @@ struct IsNLoptAlg> { template using NLoptOnly = std::enable_if_t::value, T>; -// Convert any collection to tuple. This is useful for object functions taking -// an argument list of doubles. Make things cleaner on the call site of -// optimize(). -template struct to_tuple_ { - static auto call(const C &c) - { - return std::tuple_cat(std::tuple(c[N-I]), - to_tuple_::call(c)); - } -}; - -template struct to_tuple_<0, N, T, C> { - static auto call(const C &c) { return std::tuple<>(); } -}; - -// C array to tuple -template auto carray_tuple(const T *v) -{ - return to_tuple_::call(v); -} - -// Helper to convert C style array to std::array -template auto to_arr(const T (&a) [N]) +// Helper to convert C style array to std::array. The copy should be optimized +// away with modern compilers. +template auto to_arr(const T *a) { std::array r; - std::copy(std::begin(a), std::end(a), std::begin(r)); + std::copy(a, a + N, std::begin(r)); return r; } +template auto to_arr(const T (&a) [N]) +{ + return to_arr(static_cast(a)); +} + enum class OptDir { MIN, MAX }; // Where to optimize struct NLopt { // Helper RAII class for nlopt_opt @@ -227,9 +222,19 @@ protected: nlopt_force_stop(std::get<2>(*tdata)); auto fnptr = std::get<0>(*tdata); - auto funval = carray_tuple(params); + auto funval = to_arr(params); - return std::apply(*fnptr, funval); + double scoreval = 0.; + using RetT = decltype((*fnptr)(funval)); + if constexpr (std::is_convertible_v>) { + ScoreGradient score = (*fnptr)(funval); + for (size_t i = 0; i < n; ++i) gradient[i] = (*score.gradient)[i]; + scoreval = score.score; + } else { + scoreval = (*fnptr)(funval); + } + + return scoreval; } template @@ -354,12 +359,18 @@ public: template Bounds bounds(const Bound (&b) [N]) { return detail::to_arr(b); } template Input initvals(const double (&a) [N]) { return detail::to_arr(a); } +template auto score_gradient(double s, const double (&grad)[N]) +{ + return ScoreGradient(s, detail::to_arr(grad)); +} // Predefinded NLopt algorithms that are used in the codebase using AlgNLoptGenetic = detail::NLoptAlgComb; using AlgNLoptSubplex = detail::NLoptAlg; using AlgNLoptSimplex = detail::NLoptAlg; +// TODO: define others if needed... + // Helper defs for pre-crafted global and local optimizers that work well. using DefaultGlobalOptimizer = Optimizer; using DefaultLocalOptimizer = Optimizer; diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 7ed410802..2b40f0082 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -496,7 +496,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &hjp, search_widening_path(jp, dir, radius, m_cfg.head_back_radius_mm); if (diffbr && diffbr->endp.z() > jp_gnd) { - auto &br = m_builder.add_diffbridge(diffbr.value()); + auto &br = m_builder.add_diffbridge(*diffbr); if (head_id >= 0) m_builder.head(head_id).bridge_id = br.id; endp = diffbr->endp; radius = diffbr->end_r; @@ -589,7 +589,9 @@ std::optional SupportTreeBuildsteps::search_widening_path( double fallback_ratio = radius / m_cfg.head_back_radius_mm; auto oresult = solver.to_max().optimize( - [this, jp, radius, new_radius](double plr, double azm, double t) { + [this, jp, radius, new_radius](const opt::Input<3> &input) { + auto &[plr, azm, t] = input; + auto d = spheric_to_dir(plr, azm).normalized(); double ret = pinhead_mesh_intersect(jp, d, radius, new_radius, t) .distance(); @@ -705,24 +707,22 @@ void SupportTreeBuildsteps::filter() // viable normal that doesn't collide with the model // geometry and its very close to the default. - // stc.stop_score = w; // space greater than w is enough Optimizer solver(get_criteria(m_cfg)); - solver.seed(0); - //solver.seed(0); // we want deterministic behavior + solver.seed(0); // we want deterministic behavior auto oresult = solver.to_max().optimize( - [this, pin_r, back_r, hp](double plr, double azm, double l) + [this, pin_r, back_r, hp](const opt::Input<3> &input) { + auto &[plr, azm, l] = input; + auto dir = spheric_to_dir(plr, azm).normalized(); - double score = pinhead_mesh_intersect( + return pinhead_mesh_intersect( hp, dir, pin_r, back_r, l).distance(); - - return score; }, initvals({polar, azimuth, (lmin + lmax) / 2.}), // start with what we have bounds({ - {PI - m_cfg.bridge_slope, PI}, // Must not exceed the tilt limit + {PI - m_cfg.bridge_slope, PI}, // Must not exceed the slope limit {-PI, PI}, // azimuth can be a full search {lmin, lmax} })); @@ -924,7 +924,8 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head) double r_back = head.r_back_mm; Vec3d hjp = head.junction_point(); auto oresult = solver.to_max().optimize( - [this, hjp, r_back](double plr, double azm) { + [this, hjp, r_back](const opt::Input<2> &input) { + auto &[plr, azm] = input; Vec3d n = spheric_to_dir(plr, azm).normalized(); return bridge_mesh_distance(hjp, n, r_back); },