diff --git a/.clang-format b/.clang-format index 9cbcf26f2..e0a41be5f 100644 --- a/.clang-format +++ b/.clang-format @@ -19,7 +19,7 @@ AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false -BinPackArguments: false +BinPackArguments: true BinPackParameters: false BraceWrapping: AfterClass: true diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 4831c1fb6..8d3a2272d 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -901,7 +901,7 @@ private: selector_.template packItems( from, to, bin_, pconfig_); - if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { + if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { item.removeOffset(); }); diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/Arrange.cpp similarity index 85% rename from src/libslic3r/ModelArrange.cpp rename to src/libslic3r/Arrange.cpp index 6d9c6007f..3abe49531 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -1,4 +1,4 @@ -#include "ModelArrange.hpp" +#include "Arrange.hpp" #include "Geometry.hpp" #include "SVG.hpp" #include "MTUtils.hpp" @@ -46,7 +46,14 @@ template struct NfpImpl namespace Slic3r { -namespace arr { +template> +inline SLIC3R_CONSTEXPR EigenVec unscaled( + const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT +{ + return EigenVec{unscaled(v.X), unscaled(v.Y)}; +} + +namespace arrangement { using namespace libnest2d; namespace clppr = ClipperLib; @@ -328,7 +335,6 @@ void fillConfig(PConf& pcfg) { template class AutoArranger {}; - // A class encapsulating the libnest2d Nester class and extending it with other // management and spatial index structures for acceleration. template @@ -360,7 +366,7 @@ public: std::function progressind, std::function stopcond): m_pck(bin, dist), m_bin_area(sl::area(bin)), - m_norm(std::sqrt(sl::area(bin))) + m_norm(std::sqrt(m_bin_area)) { fillConfig(m_pconf); @@ -391,9 +397,9 @@ public: m_smallsrtree.insert({itm.boundingBox(), idx}); } }; - - m_pck.progressIndicator(progressind); - m_pck.stopCondition(stopcond); + + if (progressind) m_pck.progressIndicator(progressind); + if (stopcond) m_pck.stopCondition(stopcond); } template inline PackGroup operator()(Args&&...args) { @@ -545,35 +551,6 @@ public: } }; -// Specialization with no bin. In this case the arranger should just arrange -// all objects into a minimum sized pile but it is not limited by a bin. A -// consequence is that only one pile should be created. -template<> class AutoArranger: public _ArrBase { -public: - - AutoArranger(bool, Distance dist, std::function progressind, - std::function stopcond): - _ArrBase(Box(0, 0), dist, progressind, stopcond) - { - this->m_pconf.object_function = [this] (const Item &item) { - - auto result = objfunc({0, 0}, - m_merged_pile, - m_pilebb, - m_items, - item, - 0, - m_norm, - m_rtree, - m_smallsrtree, - m_remaining); - return std::get<0>(result); - }; - - this->m_pck.configure(m_pconf); - } -}; - // Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { BedShapeHint ret; @@ -653,19 +630,6 @@ BedShapeHint bedShape(const Polyline &bed) { return ret; } -//static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; - -//template -//PackGroup _arrange(std::vector & items, -// const BinT & bin, -// coord_t minobjd, -// std::function prind, -// std::function stopfn) -//{ -// AutoArranger arranger{bin, minobjd, prind, stopfn}; -// return arranger(items.begin(), items.end()); -//} - template PackGroup _arrange(std::vector & shapes, const PackGroup & preshapes, @@ -673,45 +637,35 @@ PackGroup _arrange(std::vector & shapes, coord_t minobjd, std::function prind, std::function stopfn) -{ - -// auto binbb = sl::boundingBox(bin); - +{ AutoArranger arranger{bin, minobjd, prind, stopfn}; - if(!preshapes.front().empty()) { // If there is something on the plate + // If there is something on the plate + if(!preshapes.empty() && !preshapes.front().empty()) { arranger.preload(preshapes); + auto binbb = sl::boundingBox(bin); // Try to put the first item to the center, as the arranger will not // do this for us. -// auto shptrit = minstances.begin(); -// for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit) -// { -// // Try to place items to the center -// Item& itm = *shit; -// auto ibb = itm.boundingBox(); -// auto d = binbb.center() - ibb.center(); -// itm.translate(d); -// if(!arranger.is_colliding(itm)) { -// arranger.preload({{itm}}); + for (auto it = shapes.begin(); it != shapes.end(); ++it) { + Item &itm = *it; + auto ibb = itm.boundingBox(); + auto d = binbb.center() - ibb.center(); + itm.translate(d); + + if (!arranger.is_colliding(itm)) { + arranger.preload({{itm}}); -// auto offset = itm.translation(); -// Radians rot = itm.rotation(); -// ModelInstance *minst = *shptrit; - -// Vec3d foffset(unscaled(offset.X), -// unscaled(offset.Y), -// minst->get_offset()(Z)); - -// // write the transformation data into the model instance -// minst->set_rotation(Z, rot); -// minst->set_offset(foffset); + // Write the transformation data into the item. The callback + // was set on the instantiation of Item and calls the + // Arrangeable interface. + it->callApplyFunction(0); -// shit = shapes.erase(shit); -// shptrit = minstances.erase(shptrit); -// break; -// } -// } + // Remove this item, as it is arranged now + it = shapes.erase(it); + break; + } + } } return arranger(shapes.begin(), shapes.end()); @@ -724,8 +678,8 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) //// The final client function to arrange the Model. A progress indicator and //// a stop predicate can be also be passed to control the process. -bool arrange(Arrangeables & arrangables, - const Arrangeables & excludes, +bool arrange(ArrangeablePtrs & arrangables, + const ArrangeablePtrs & excludes, coord_t min_obj_distance, const BedShapeHint & bedhint, std::function progressind, @@ -741,29 +695,29 @@ bool arrange(Arrangeables & arrangables, PackGroup preshapes{ {} }; // pack group with one initial bin for preloading auto process_arrangeable = - [](const Arrangeable * arrangeable, - std::vector & outp, - std::function applyfn) + [](const Arrangeable * arrangeable, + std::vector & outp, + std::function applyfn) { - assert(arrangeable); + assert(arrangeable); - auto arrangeitem = arrangeable->get_arrange_polygon(); + auto arrangeitem = arrangeable->get_arrange_polygon(); - Polygon & p = std::get<0>(arrangeitem); - const Vec2crd &offs = std::get<1>(arrangeitem); - double rotation = std::get<2>(arrangeitem); + Polygon & p = std::get<0>(arrangeitem); + const Vec2crd &offs = std::get<1>(arrangeitem); + double rotation = std::get<2>(arrangeitem); - if (p.is_counter_clockwise()) p.reverse(); + if (p.is_counter_clockwise()) p.reverse(); - clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); + clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); - auto firstp = clpath.Contour.front(); - clpath.Contour.emplace_back(firstp); + auto firstp = clpath.Contour.front(); + clpath.Contour.emplace_back(firstp); - outp.emplace_back(applyfn, std::move(clpath)); - outp.front().rotation(rotation); - outp.front().translation({offs.x(), offs.y()}); - }; + outp.emplace_back(applyfn, std::move(clpath)); + outp.front().rotation(rotation); + outp.front().translation({offs.x(), offs.y()}); + }; for (Arrangeable *arrangeable : arrangables) { process_arrangeable( @@ -774,8 +728,7 @@ bool arrange(Arrangeables & arrangables, clppr::cInt stride = binidx * stride_padding(binwidth); clppr::IntPoint offs = itm.translation(); - arrangeable->apply_arrange_result({unscaled(offs.X + - stride), + arrangeable->apply_arrange_result({unscaled(offs.X + stride), unscaled(offs.Y)}, itm.rotation()); }); @@ -797,7 +750,6 @@ bool arrange(Arrangeables & arrangables, // Create the arranger for the box shaped bed BoundingBox bbb = bedhint.shape.box; bbb.min -= Point{md, md}, bbb.max += Point{md, md}; - Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; binwidth = coord_t(binbb.width()); @@ -808,6 +760,7 @@ bool arrange(Arrangeables & arrangables, auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); binwidth = scaled(c.radius()); + _arrange(items, preshapes, cc, min_obj_distance, progressind, cfn); break; } @@ -816,11 +769,21 @@ bool arrange(Arrangeables & arrangables, auto irrbed = sl::create(std::move(ctour)); BoundingBox polybb(bedhint.shape.polygon); binwidth = (polybb.max(X) - polybb.min(X)); + _arrange(items, preshapes, irrbed, min_obj_distance, progressind, cfn); break; } - case BedShapeType::WHO_KNOWS: { - _arrange(items, preshapes, false, min_obj_distance, progressind, cfn); + case BedShapeType::INFINITE: { + // const InfiniteBed& nobin = bedhint.shape.infinite; + //Box infbb{{nobin.center.x(), nobin.center.y()}}; + Box infbb; + + _arrange(items, preshapes, infbb, min_obj_distance, progressind, cfn); + break; + } + case BedShapeType::UNKNOWN: { + // We know nothing about the bed, let it be infinite and zero centered + _arrange(items, preshapes, Box{}, min_obj_distance, progressind, cfn); break; } }; @@ -831,7 +794,7 @@ bool arrange(Arrangeables & arrangables, } /// Arrange, without the fixed items (excludes) -bool arrange(Arrangeables & inp, +bool arrange(ArrangeablePtrs & inp, coord_t min_d, const BedShapeHint & bedhint, std::function prfn, diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/Arrange.hpp similarity index 82% rename from src/libslic3r/ModelArrange.hpp rename to src/libslic3r/Arrange.hpp index 306081eb8..87514a600 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -6,7 +6,7 @@ namespace Slic3r { -namespace arr { +namespace arrangement { /// A geometry abstraction for a circular print bed. Similarly to BoundingBox. class CircleBed { @@ -22,21 +22,26 @@ public: inline operator bool() { return !std::isnan(radius_); } }; +/// Representing an unbounded bin +struct InfiniteBed { Point center; }; + /// Types of print bed shapes. enum class BedShapeType { BOX, CIRCLE, IRREGULAR, - WHO_KNOWS + INFINITE, + UNKNOWN }; /// Info about the print bed for the arrange() function. struct BedShapeHint { - BedShapeType type = BedShapeType::WHO_KNOWS; + BedShapeType type = BedShapeType::INFINITE; /*union*/ struct { // I know but who cares... TODO: use variant from cpp17? - CircleBed circ; + CircleBed circ; BoundingBox box; - Polyline polygon; + Polyline polygon; + InfiniteBed infinite; } shape; }; @@ -59,7 +64,7 @@ public: virtual std::tuple get_arrange_polygon() const = 0; }; -using Arrangeables = std::vector; +using ArrangeablePtrs = std::vector; /** * \brief Arranges the model objects on the screen. @@ -92,20 +97,20 @@ using Arrangeables = std::vector; * * \param stopcondition A predicate returning true if abort is needed. */ -bool arrange(Arrangeables &items, +bool arrange(ArrangeablePtrs &items, coord_t min_obj_distance, const BedShapeHint& bedhint, - std::function progressind, - std::function stopcondition); + std::function progressind = nullptr, + std::function stopcondition = nullptr); /// Same as the previous, only that it takes unmovable items as an /// additional argument. -bool arrange(Arrangeables &items, - const Arrangeables &excludes, +bool arrange(ArrangeablePtrs &items, + const ArrangeablePtrs &excludes, coord_t min_obj_distance, const BedShapeHint& bedhint, - std::function progressind, - std::function stopcondition); + std::function progressind = nullptr, + std::function stopcondition = nullptr); } // arr } // Slic3r diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index dc52257aa..736290489 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -107,8 +107,8 @@ add_library(libslic3r STATIC Line.hpp Model.cpp Model.hpp - ModelArrange.hpp - ModelArrange.cpp + Arrange.hpp + Arrange.cpp MotionPlanner.cpp MotionPlanner.hpp MultiPoint.cpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 18f3f2f5e..fd48dcec4 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -375,31 +375,46 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) { // get the (transformed) size of each instance so that we take // into account their different transformations when packing - Pointfs instance_sizes; - Pointfs instance_centers; - for (const ModelObject *o : this->objects) - for (size_t i = 0; i < o->instances.size(); ++ i) { - // an accurate snug bounding box around the transformed mesh. - BoundingBoxf3 bbox(o->instance_bounding_box(i, true)); - instance_sizes.emplace_back(to_2d(bbox.size())); - instance_centers.emplace_back(to_2d(bbox.center())); - } +// Pointfs instance_sizes; +// Pointfs instance_centers; +// for (const ModelObject *o : this->objects) +// for (size_t i = 0; i < o->instances.size(); ++ i) { +// // an accurate snug bounding box around the transformed mesh. +// BoundingBoxf3 bbox(o->instance_bounding_box(i, true)); +// instance_sizes.emplace_back(to_2d(bbox.size())); +// instance_centers.emplace_back(to_2d(bbox.center())); +// } - Pointfs positions; - if (! _arrange(instance_sizes, dist, bb, positions)) - return false; +// Pointfs positions; +// if (! _arrange(instance_sizes, dist, bb, positions)) +// return false; - size_t idx = 0; - for (ModelObject *o : this->objects) { - for (ModelInstance *i : o->instances) { - Vec2d offset_xy = positions[idx] - instance_centers[idx]; - i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z))); - ++idx; - } - o->invalidate_bounding_box(); +// size_t idx = 0; +// for (ModelObject *o : this->objects) { +// for (ModelInstance *i : o->instances) { +// Vec2d offset_xy = positions[idx] - instance_centers[idx]; +// i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z))); +// ++idx; +// } +// o->invalidate_bounding_box(); +// } + size_t count = 0; + for (auto obj : objects) count += obj->instances.size(); + + arrangement::ArrangeablePtrs input; + input.reserve(count); + for (ModelObject *mo : objects) + for (ModelInstance *minst : mo->instances) + input.emplace_back(minst); + + arrangement::BedShapeHint bedhint; + + if (bb) { + bedhint.type = arrangement::BedShapeType::BOX; + bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max)); } - - return true; + + return arrangement::arrange(input, scaled(dist), bedhint); } // Duplicate the entire model preserving instance relative positions. @@ -1804,31 +1819,27 @@ void ModelInstance::transform_polygon(Polygon* polygon) const std::tuple ModelInstance::get_arrange_polygon() const { static const double SIMPLIFY_TOLERANCE_MM = 0.1; - - assert(m_inst); - - Vec3d rotation = get_rotation(); - rotation.z() = 0.; - Transform3d trafo_instance = Geometry:: - assemble_transform(Vec3d::Zero(), - rotation, - get_scaling_factor(), - get_mirror()); - + + Vec3d rotation = get_rotation(); + rotation.z() = 0.; + Transform3d trafo_instance = + Geometry::assemble_transform(Vec3d::Zero(), rotation, + get_scaling_factor(), get_mirror()); + Polygon p = get_object()->convex_hull_2d(trafo_instance); - + assert(!p.points.empty()); - + // this may happen for malformed models, see: // https://github.com/prusa3d/PrusaSlicer/issues/2209 if (p.points.empty()) return {}; - + Polygons pp{p}; pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); if (!pp.empty()) p = pp.front(); - - return std::make_tuple(p, Vec2crd{scaled(get_offset(X)), - scaled(get_offset(Y))}, + + return std::make_tuple(p, + Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, get_rotation(Z)); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c02280827..51759640c 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -7,7 +7,7 @@ #include "Point.hpp" #include "TriangleMesh.hpp" #include "Slicing.hpp" -#include "ModelArrange.hpp" +#include "Arrange.hpp" #include #include @@ -491,7 +491,7 @@ private: // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance : public ModelBase, public arr::Arrangeable +class ModelInstance : public ModelBase, public arrangement::Arrangeable { public: enum EPrintVolumeState : unsigned char diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2d3c3b27f..524e0c883 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -4,7 +4,7 @@ #include #include -#include "libslic3r/ModelArrange.hpp" +#include "libslic3r/Arrange.hpp" #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" @@ -612,7 +612,7 @@ public: int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } - class WipeTowerInfo: public arr::Arrangeable { + class WipeTowerInfo: public arrangement::Arrangeable { Vec2d m_pos = {std::nan(""), std::nan("")}; Vec2d m_bb_size; double m_rotation; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index aba8ae71d..08c70dbe0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -32,7 +32,6 @@ #include "libslic3r/Format/3mf.hpp" #include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/ModelArrange.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" @@ -1218,6 +1217,28 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi return true; } +namespace { +arrangement::ArrangeablePtrs get_arrange_input(Model &model, const Selection &sel) { + auto selmap = sel.get_content(); + + size_t count = 0; + for (auto obj : model.objects) count += obj->instances.size(); + + arrangement::ArrangeablePtrs ret; ret.reserve(count); + + if (selmap.empty()) + for (ModelObject *mo : model.objects) + for (ModelInstance *minst : mo->instances) + ret.emplace_back(minst); + else + for (auto &s : selmap) + for (auto &instid : s.second) + ret.emplace_back(model.objects[s.first]->instances[instid]); + + return ret; +} +} + // Plater / private struct Plater::priv { @@ -1425,50 +1446,91 @@ struct Plater::priv class ArrangeJob : public Job { - int m_count = 0; GLCanvas3D::WipeTowerInfo m_wti; + arrangement::ArrangeablePtrs m_selected, m_unselected; + + static std::array collect( + Model &model, const Selection &sel) + { + auto selmap = sel.get_content(); + + size_t count = 0; + for (auto obj : model.objects) count += obj->instances.size(); + + arrangement::ArrangeablePtrs selected, unselected; + selected.reserve(count + 1 /* for optional wti */); + unselected.reserve(count + 1 /* for optional wti */); + + for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { + auto oit = selmap.find(int(oidx)); + + if (oit != selmap.end()) { + auto &iids = oit->second; + + for (size_t iidx = 0; + iidx < model.objects[oidx]->instances.size(); + ++iidx) + { + auto instit = iids.find(iidx); + ModelInstance *inst = model.objects[oidx] + ->instances[iidx]; + instit == iids.end() ? + unselected.emplace_back(inst) : + selected.emplace_back(inst); + } + } else // object not selected, all instances are unselected + for (auto inst : model.objects[oidx]->instances) + unselected.emplace_back(inst); + } + + if (selected.empty()) selected.swap(unselected); + + return {selected, unselected}; + } + protected: void prepare() override { m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); - m_count = bool(m_wti); - for (auto obj : plater().model.objects) - m_count += int(obj->instances.size()); + const Selection& sel = plater().get_selection(); + auto arrinput = collect(plater().model, sel); + m_selected.swap(arrinput[0]); + m_unselected.swap(arrinput[1]); + + if (m_wti) + sel.is_wipe_tower() ? + m_selected.emplace_back(&m_wti) : + m_unselected.emplace_back(&m_wti); } public: - //using Job::Job; - ArrangeJob(priv * pltr): Job(pltr) {} - int status_range() const override { return m_count; } - void set_count(int c) { m_count = c; } + using Job::Job; + int status_range() const override + { + return int(m_selected.size()); + } void process() override; - } arrange_job/*{m_plater}*/; + } arrange_job{m_plater}; class RotoptimizeJob : public Job { public: - //using Job::Job; - RotoptimizeJob(priv * pltr): Job(pltr) {} + using Job::Job; void process() override; - } rotoptimize_job/*{m_plater}*/; + } rotoptimize_job{m_plater}; // To create a new job, just define a new subclass of Job, implement // the process and the optional prepare() and finalize() methods // Register the instance of the class in the m_jobs container // if it cannot run concurrently with other jobs in this group - std::vector> m_jobs/*{arrange_job, - rotoptimize_job}*/; + std::vector> m_jobs{arrange_job, + rotoptimize_job}; public: - ExclusiveJobGroup(priv *_plater) - : m_plater(_plater) - , arrange_job(m_plater) - , rotoptimize_job(m_plater) - , m_jobs({arrange_job, rotoptimize_job}) - {} + ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {} void start(Jobs jid) { m_plater->background_process.stop(); @@ -1528,7 +1590,7 @@ struct Plater::priv std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; BoundingBox scaled_bed_shape_bb() const; - arr::BedShapeHint get_bed_shape_hint() const; + arrangement::BedShapeHint get_bed_shape_hint() const; std::vector load_files(const std::vector& input_files, bool load_model, bool load_config); std::vector load_model_objects(const ModelObjectPtrs &model_objects); wxString get_export_file(GUI::FileType file_type); @@ -2404,8 +2466,8 @@ void Plater::priv::sla_optimize_rotation() { m_ui_jobs.start(Jobs::Rotoptimize); } -arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { - arr::BedShapeHint bedshape; +arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const { + arrangement::BedShapeHint bedshape; const auto *bed_shape_opt = config->opt("bed_shape"); assert(bed_shape_opt); @@ -2414,7 +2476,7 @@ arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { auto &bedpoints = bed_shape_opt->values; Polyline bedpoly; bedpoly.points.reserve(bedpoints.size()); for (auto &v : bedpoints) bedpoly.append(scaled(v)); - bedshape = arr::bedShape(bedpoly); + bedshape = arrangement::bedShape(bedpoly); } return bedshape; @@ -2423,15 +2485,6 @@ arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { static const auto arrangestr = _(L("Arranging")); - // Collect the model instances and place them into the input vector - arr::Arrangeables arrangeinput; arrangeinput.reserve(m_count); - for(ModelObject *mo : plater().model.objects) - for(ModelInstance *minst : mo->instances) - arrangeinput.emplace_back(minst); - - // Place back the wipe tower if that's available. - if (m_wti) arrangeinput.emplace_back(&m_wti); - // FIXME: I don't know how to obtain the minimum distance, it depends // on printer technology. I guess the following should work but it crashes. double dist = 6; // PrintConfig::min_object_distance(config); @@ -2440,25 +2493,25 @@ void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { } coord_t min_obj_distance = scaled(dist); - - arr::BedShapeHint bedshape = plater().get_bed_shape_hint(); + auto count = unsigned(m_selected.size()); + arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); try { - arr::arrange(arrangeinput, - min_obj_distance, - bedshape, - [this](unsigned st) { - if (st > 0) - update_status(m_count - int(st), arrangestr); - }, - [this]() { return was_canceled(); }); + arrangement::arrange(m_selected, m_unselected, min_obj_distance, + bedshape, + [this, count](unsigned st) { + if (st > 0) // will not finalize after last one + update_status(count - st, arrangestr); + }, + [this]() { return was_canceled(); }); } catch (std::exception & /*e*/) { GUI::show_error(plater().q, _(L("Could not arrange model objects! " "Some geometries may be invalid."))); } - update_status(m_count, + // finalize just here. + update_status(int(count), was_canceled() ? _(L("Arranging canceled.")) : _(L("Arranging done."))); } @@ -2466,7 +2519,7 @@ void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { void find_new_position(const Model & model, ModelInstancePtrs instances, coord_t min_d, - const arr::BedShapeHint &bedhint) + const arrangement::BedShapeHint &bedhint) { // TODO