diff --git a/src/libslic3r/Optimize/NLoptOptimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp index 87aec4b36..46a0d0648 100644 --- a/src/libslic3r/Optimize/NLoptOptimizer.hpp +++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp @@ -141,7 +141,9 @@ protected: } template - void set_up(NLopt &nl, const Bounds& bounds) + static void set_up(NLopt &nl, + const Bounds &bounds, + const StopCriteria &stopcr) { std::array lb, ub; @@ -153,15 +155,15 @@ protected: nlopt_set_lower_bounds(nl.ptr, lb.data()); nlopt_set_upper_bounds(nl.ptr, ub.data()); - double abs_diff = m_stopcr.abs_score_diff(); - double rel_diff = m_stopcr.rel_score_diff(); - double stopval = m_stopcr.stop_score(); + double abs_diff = stopcr.abs_score_diff(); + double rel_diff = stopcr.rel_score_diff(); + double stopval = stopcr.stop_score(); if(!std::isnan(abs_diff)) nlopt_set_ftol_abs(nl.ptr, abs_diff); if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff); if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval); - if(m_stopcr.max_iterations() > 0) - nlopt_set_maxeval(nl.ptr, m_stopcr.max_iterations()); + if(stopcr.max_iterations() > 0) + nlopt_set_maxeval(nl.ptr, stopcr.max_iterations()); } template @@ -211,7 +213,7 @@ public: const std::tuple &inequalities) { NLopt nl{alg, N}; - set_up(nl, bounds); + set_up(nl, bounds, m_stopcr); return optimize(nl, std::forward(func), initvals, equalities, inequalities); @@ -230,6 +232,7 @@ template class NLoptOpt>: public NLoptOpt> { using Base = NLoptOpt>; + StopCriteria m_loc_stopcr; public: template @@ -241,15 +244,20 @@ public: { NLopt nl_glob{glob, N}, nl_loc{loc, N}; - Base::set_up(nl_glob, bounds); - Base::set_up(nl_loc, bounds); + Base::set_up(nl_glob, bounds, Base::get_criteria()); + Base::set_up(nl_loc, bounds, m_loc_stopcr); nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr); return Base::optimize(nl_glob, std::forward(f), initvals, equalities, inequalities); } - explicit NLoptOpt(StopCriteria stopcr = {}) : Base{stopcr} {} + explicit NLoptOpt(StopCriteria stopcr = {}) + : Base{stopcr}, m_loc_stopcr{stopcr} + {} + + void set_loc_criteria(const StopCriteria &cr) { m_loc_stopcr = cr; } + const StopCriteria &get_loc_criteria() const noexcept { return m_loc_stopcr; } }; } // namespace detail; @@ -285,6 +293,9 @@ public: const StopCriteria &get_criteria() const { return m_opt.get_criteria(); } void seed(long s) { m_opt.seed(s); } + + void set_loc_criteria(const StopCriteria &cr) { m_opt.set_loc_criteria(cr); } + const StopCriteria &get_loc_criteria() const noexcept { return m_opt.get_loc_criteria(); } }; // Predefinded NLopt algorithms diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index e0d7db97d..ed5725780 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -105,7 +105,7 @@ struct SupportTreeConfig static const double constexpr max_solo_pillar_height_mm = 15.0; static const double constexpr max_dual_pillar_height_mm = 35.0; static const double constexpr optimizer_rel_score_diff = 1e-10; - static const unsigned constexpr optimizer_max_iterations = 2000; + static const unsigned constexpr optimizer_max_iterations = 30000; static const unsigned constexpr pillar_cascade_neighbors = 3; }; diff --git a/src/libslic3r/SLA/SupportTreeUtils.hpp b/src/libslic3r/SLA/SupportTreeUtils.hpp index 2d0fd859e..0dffd0852 100644 --- a/src/libslic3r/SLA/SupportTreeUtils.hpp +++ b/src/libslic3r/SLA/SupportTreeUtils.hpp @@ -492,17 +492,24 @@ GroundConnection deepsearch_ground_connection( // Score is the total lenght of the route. Feasible routes will have // infinite length (rays not colliding with model), thus the stop score // should be a reasonably big number. - constexpr double StopScore = 1e6; + constexpr double Penality = 1e5; + constexpr double PenOffs = 1e2; const auto sd = sm.cfg.safety_distance(source.r); const auto gndlvl = ground_level(sm); - auto criteria = get_criteria(sm.cfg).stop_score(StopScore); + auto criteria = get_criteria(sm.cfg); + criteria.abs_score_diff(1.); + criteria.rel_score_diff(0.1); + criteria.max_iterations(5000); Optimizer solver(criteria); + solver.set_loc_criteria(criteria.max_iterations(100).abs_score_diff(1.)); solver.seed(0); // enforce deterministic behavior + size_t icnt = 0; auto optfn = [&](const opt::Input<3> &input) { + ++icnt; double ret = NaNd; // solver suggests polar, azimuth and bridge length values: @@ -511,7 +518,7 @@ GroundConnection deepsearch_ground_connection( Vec3d n = spheric_to_dir(plr, azm); Vec3d bridge_end = source.pos + bridge_len * n; - double full_len = bridge_len + bridge_end.z() - gndlvl; + double down_l = bridge_end.z() - gndlvl; double bridge_r = wideningfn(Ball{source.pos, source.r}, n, bridge_len); double brhit_dist = 0.; @@ -524,7 +531,7 @@ GroundConnection deepsearch_ground_connection( } if (brhit_dist < bridge_len) { - ret = brhit_dist; + ret = brhit_dist + Penality; } else { // check if pillar can be placed below auto gp = Vec3d{bridge_end.x(), bridge_end.y(), gndlvl}; @@ -532,28 +539,27 @@ GroundConnection deepsearch_ground_connection( Beam gndbeam {{bridge_end, bridge_r}, {gp, end_radius}}; auto gndhit = beam_mesh_hit(policy, sm.emesh, gndbeam, sd); + double gnd_hit_d = std::min(gndhit.distance(), down_l); + double penality = 0.; - if (std::isinf(gndhit.distance())) { - // Ground route is free with this bridge - - if (sm.cfg.object_elevation_mm < EPSILON) { - // Dealing with zero elevation mode, to not route pillars - // into the gap between the optional pad and the model - double gap = std::sqrt(sm.emesh.squared_distance(gp)); - double base_r = std::max(sm.cfg.base_radius_mm, end_radius); - double max_gap = sm.cfg.pillar_base_safety_distance_mm + base_r; - if (gap < max_gap) - ret = full_len - max_gap + gap; - else // success - ret = StopScore + EPSILON; - } else { - // No zero elevation, return success - ret = StopScore + EPSILON; + if (!std::isinf(gndhit.distance())) + penality = Penality; + else if (sm.cfg.object_elevation_mm < EPSILON) { + // Dealing with zero elevation mode, to not route pillars + // into the gap between the optional pad and the model + double gap = std::sqrt(sm.emesh.squared_distance(gp)); + double base_r = std::max(sm.cfg.base_radius_mm, end_radius); + double min_gap = sm.cfg.pillar_base_safety_distance_mm + base_r; + if (gap < min_gap) { + penality = Penality + PenOffs * (min_gap - gap); } - } else { - // Ground route is not free - ret = bridge_len + gndhit.distance(); +// gnd_hit_d += std::max(0., min_gap - gap); //penality = Penality + 100000. * (min_gap - gap); +// if (gap < min_gap) { +// penality = Penality; +// } } + + ret = bridge_len + gnd_hit_d + penality; } return ret; @@ -564,7 +570,7 @@ GroundConnection deepsearch_ground_connection( // Saturate the polar angle to max tilt defined in config plr_init = std::max(plr_init, PI - sm.cfg.bridge_slope); - auto oresult = solver.to_max().optimize( + auto oresult = solver.to_min().optimize( optfn, initvals({plr_init, azm_init, 0.}), // start with a zero bridge bounds({ {PI - sm.cfg.bridge_slope, PI}, // bounds for polar angle @@ -572,10 +578,12 @@ GroundConnection deepsearch_ground_connection( {0., sm.cfg.max_bridge_length_mm} }) // bounds bridge length ); + std::cout << "iters: " << icnt << std::endl; + GroundConnection conn; - if (oresult.score >= StopScore) { - // search was successful, extract and apply the result + if (oresult.score < Penality) { + // Extract and apply the result auto &[plr, azm, bridge_len] = oresult.optimum; Vec3d n = spheric_to_dir(plr, azm); diff --git a/tests/sla_print/sla_supptreeutils_tests.cpp b/tests/sla_print/sla_supptreeutils_tests.cpp index 8d1029152..58918db41 100644 --- a/tests/sla_print/sla_supptreeutils_tests.cpp +++ b/tests/sla_print/sla_supptreeutils_tests.cpp @@ -177,7 +177,7 @@ TEST_CASE("Avoid disk below junction", "[suptreeutils]") sla::SupportableMesh sm{disk, sla::SupportPoints{}, cfg}; - SECTION("without elevation") { + SECTION("with elevation") { sla::GroundConnection conn = sla::deepsearch_ground_connection(ex_seq, sm, j, EndRadius, sla::DOWN); @@ -191,7 +191,7 @@ TEST_CASE("Avoid disk below junction", "[suptreeutils]") REQUIRE(pR + FromRadius > CylRadius); } - SECTION("with elevation") { + SECTION("without elevation") { sm.cfg.object_elevation_mm = 0.; sla::GroundConnection conn = @@ -234,7 +234,7 @@ TEST_CASE("Avoid disk below junction with barrier on the side", "[suptreeutils]" sla::SupportableMesh sm{disk, sla::SupportPoints{}, cfg}; - SECTION("without elevation") { + SECTION("with elevation") { sla::GroundConnection conn = sla::deepsearch_ground_connection(ex_seq, sm, j, EndRadius, sla::DOWN);