diff --git a/src/libnest2d/include/libnest2d/nester.hpp b/src/libnest2d/include/libnest2d/nester.hpp index 52c738a4c1..78da28759c 100644 --- a/src/libnest2d/include/libnest2d/nester.hpp +++ b/src/libnest2d/include/libnest2d/nester.hpp @@ -70,6 +70,7 @@ class _Item { int binid_{BIN_ID_UNSET}, priority_{0}; bool fixed_{false}; + std::function on_packed_; public: @@ -205,6 +206,23 @@ public: sl::vertex(sh_, idx) = v; } + void setShape(RawShape rsh) + { + sh_ = std::move(rsh); + invalidateCache(); + } + + void setOnPackedFn(std::function onpackedfn) + { + on_packed_ = onpackedfn; + } + + void onPacked() + { + if (on_packed_) + on_packed_(*this); + } + /** * @brief Calculate the shape area. * diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index a17d549822..0ef0929e16 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -901,6 +901,7 @@ public: if(can_pack) { ret = PackResult(item); + item.onPacked(); merged_pile_ = nfp::merge(merged_pile_, item.transformedShape()); } else { ret = PackResult(best_overfit); diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 31751ab322..b3293e17f2 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -583,8 +583,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly, outp.emplace_back(std::move(p)); outp.back().rotation(rotation); outp.back().translation({offs.x(), offs.y()}); + outp.back().inflate(arrpoly.inflation); outp.back().binId(arrpoly.bed_idx); outp.back().priority(arrpoly.priority); + outp.back().setOnPackedFn([&arrpoly](Item &itm){ + itm.inflate(-arrpoly.inflation); + }); } template auto call_with_bed(const Points &bed, Fn &&fn) diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 6d4001a50c..4ed00668c9 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -71,7 +71,7 @@ static const constexpr int UNARRANGED = -1; /// polygon belongs: UNARRANGED means no place for the polygon /// (also the initial state before arrange), 0..N means the index of the bed. /// Zero is the physical bed, larger than zero means a virtual bed. -struct ArrangePolygon { +struct ArrangePolygon { ExPolygon poly; /// The 2D silhouette to be arranged Vec2crd translation{0, 0}; /// The translation of the poly double rotation{0.0}; /// The rotation of the poly in radians diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 4595ae2544..72a938ab33 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -2,6 +2,8 @@ #include "libslic3r/BuildVolume.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" @@ -11,6 +13,7 @@ #include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/format.hpp" + #include "libnest2d/common.hpp" #include @@ -76,6 +79,7 @@ void ArrangeJob::clear_input() m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); m_unprintable.reserve(cunprint /* for optional wti */); + m_min_inflation = 0; } void ArrangeJob::prepare_all() { @@ -145,6 +149,50 @@ void ArrangeJob::prepare_selected() { for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; } +static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, + const SLAPrintObject &po, + const ModelInstance &inst, + coord_t min_infl) +{ + auto laststep = po.last_completed_step(); + + if (laststep < slaposCount && laststep > slaposSupportTree) { + auto omesh = po.get_mesh_to_print(); + auto &smesh = po.support_mesh(); + + Vec3d rotation = inst.get_rotation(); + rotation.z() = 0.; + Transform3f trafo_instance = + Geometry::assemble_transform(inst.get_offset().z() * Vec3d::UnitZ(), + rotation, + inst.get_scaling_factor(), + inst.get_mirror()).cast(); + + trafo_instance = trafo_instance * po.trafo().cast().inverse(); + + auto polys = reserve_vector(3); + auto zlvl = -po.get_elevation(); + + if (omesh) { + polys.emplace_back(its_convex_hull_2d_above(*omesh, trafo_instance, zlvl)); + ret.poly.contour = polys.back(); + ret.poly.holes = {}; + } + + polys.emplace_back(its_convex_hull_2d_above(smesh.its, trafo_instance, zlvl)); + ret.poly.contour = Geometry::convex_hull(polys); + ret.poly.holes = {}; + + // The 1.1 multiplier is a safety gap, as the offset might be bigger + // in sharp edges of a polygon, depending on clipper's offset algorithm + coord_t infl = 1.1 * scaled(po.config().pad_brim_size.getFloat() + + po.config().pad_around_object.getBool() * + po.config().pad_object_gap.getFloat()); + + ret.inflation = std::max(infl, min_infl); + } +} + arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) { arrangement::ArrangePolygon ap = get_arrange_poly(mi, m_plater); @@ -156,12 +204,28 @@ arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) m_unarranged.emplace_back(mi); }; + if (m_plater->printer_technology() == ptSLA) { + auto obj_id = mi->get_object()->id(); + const SLAPrintObject *po = + m_plater->sla_print().get_print_object_by_model_object_id(obj_id); + + if (po) { + update_arrangepoly_slaprint(ap, *po, *mi, m_min_inflation); + m_min_inflation = std::max(m_min_inflation, ap.inflation); + } + } else { + // TODO: get fff supports outline + } + return ap; } void ArrangeJob::prepare() { wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); + for (auto &ap : m_selected) { + ap.inflation = m_min_inflation; + } } void ArrangeJob::process(Ctl &ctl) @@ -286,7 +350,9 @@ template<> arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, const Plater * plater) { - return get_arrange_poly(PtrWrapper{inst}, plater); + auto ap = get_arrange_poly(PtrWrapper{inst}, plater); + + return ap; } arrangement::ArrangeParams get_arrange_params(Plater *p) diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.hpp b/src/slic3r/GUI/Jobs/ArrangeJob.hpp index 106cc57ddf..4b75868a03 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.hpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.hpp @@ -21,6 +21,8 @@ class ArrangeJob : public Job ArrangePolygons m_selected, m_unselected, m_unprintable; std::vector m_unarranged; + coord_t m_min_inflation = 0; + Plater *m_plater; // clear m_selected and m_unselected, reserve space for next usage