Still WIP

This commit is contained in:
tamasmeszaros 2019-07-01 18:22:07 +02:00
parent cb3a586deb
commit 253ec07cb2
9 changed files with 242 additions and 210 deletions

View File

@ -19,7 +19,7 @@ AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false AlwaysBreakTemplateDeclarations: false
BinPackArguments: false BinPackArguments: true
BinPackParameters: false BinPackParameters: false
BraceWrapping: BraceWrapping:
AfterClass: true AfterClass: true

View File

@ -901,7 +901,7 @@ private:
selector_.template packItems<PlacementStrategy>( selector_.template packItems<PlacementStrategy>(
from, to, bin_, pconfig_); 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(); item.removeOffset();
}); });

View File

@ -1,4 +1,4 @@
#include "ModelArrange.hpp" #include "Arrange.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "SVG.hpp" #include "SVG.hpp"
#include "MTUtils.hpp" #include "MTUtils.hpp"
@ -46,7 +46,14 @@ template<class S> struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
namespace Slic3r { namespace Slic3r {
namespace arr { template<class Tout = double, class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR EigenVec<Tout, 2> unscaled(
const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT
{
return EigenVec<Tout, 2>{unscaled<Tout>(v.X), unscaled<Tout>(v.Y)};
}
namespace arrangement {
using namespace libnest2d; using namespace libnest2d;
namespace clppr = ClipperLib; namespace clppr = ClipperLib;
@ -328,7 +335,6 @@ void fillConfig(PConf& pcfg) {
template<class TBin> template<class TBin>
class AutoArranger {}; class AutoArranger {};
// A class encapsulating the libnest2d Nester class and extending it with other // A class encapsulating the libnest2d Nester class and extending it with other
// management and spatial index structures for acceleration. // management and spatial index structures for acceleration.
template<class TBin> template<class TBin>
@ -360,7 +366,7 @@ public:
std::function<void(unsigned)> progressind, std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond): std::function<bool(void)> stopcond):
m_pck(bin, dist), m_bin_area(sl::area(bin)), 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); fillConfig(m_pconf);
@ -391,9 +397,9 @@ public:
m_smallsrtree.insert({itm.boundingBox(), idx}); m_smallsrtree.insert({itm.boundingBox(), idx});
} }
}; };
m_pck.progressIndicator(progressind); if (progressind) m_pck.progressIndicator(progressind);
m_pck.stopCondition(stopcond); if (stopcond) m_pck.stopCondition(stopcond);
} }
template<class...Args> inline PackGroup operator()(Args&&...args) { template<class...Args> 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<bool>: public _ArrBase<Box> {
public:
AutoArranger(bool, Distance dist, std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
_ArrBase<Box>(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. // Get the type of bed geometry from a simple vector of points.
BedShapeHint bedShape(const Polyline &bed) { BedShapeHint bedShape(const Polyline &bed) {
BedShapeHint ret; BedShapeHint ret;
@ -653,19 +630,6 @@ BedShapeHint bedShape(const Polyline &bed) {
return ret; return ret;
} }
//static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1;
//template<class BinT>
//PackGroup _arrange(std::vector<Item> & items,
// const BinT & bin,
// coord_t minobjd,
// std::function<void(unsigned)> prind,
// std::function<bool()> stopfn)
//{
// AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
// return arranger(items.begin(), items.end());
//}
template<class BinT> template<class BinT>
PackGroup _arrange(std::vector<Item> & shapes, PackGroup _arrange(std::vector<Item> & shapes,
const PackGroup & preshapes, const PackGroup & preshapes,
@ -673,45 +637,35 @@ PackGroup _arrange(std::vector<Item> & shapes,
coord_t minobjd, coord_t minobjd,
std::function<void(unsigned)> prind, std::function<void(unsigned)> prind,
std::function<bool()> stopfn) std::function<bool()> stopfn)
{ {
// auto binbb = sl::boundingBox(bin);
AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn}; AutoArranger<BinT> 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); arranger.preload(preshapes);
auto binbb = sl::boundingBox(bin);
// Try to put the first item to the center, as the arranger will not // Try to put the first item to the center, as the arranger will not
// do this for us. // do this for us.
// auto shptrit = minstances.begin(); for (auto it = shapes.begin(); it != shapes.end(); ++it) {
// for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit) Item &itm = *it;
// { auto ibb = itm.boundingBox();
// // Try to place items to the center auto d = binbb.center() - ibb.center();
// Item& itm = *shit; itm.translate(d);
// auto ibb = itm.boundingBox();
// auto d = binbb.center() - ibb.center(); if (!arranger.is_colliding(itm)) {
// itm.translate(d); arranger.preload({{itm}});
// if(!arranger.is_colliding(itm)) {
// arranger.preload({{itm}});
// auto offset = itm.translation(); // Write the transformation data into the item. The callback
// Radians rot = itm.rotation(); // was set on the instantiation of Item and calls the
// ModelInstance *minst = *shptrit; // Arrangeable interface.
it->callApplyFunction(0);
// 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);
// shit = shapes.erase(shit); // Remove this item, as it is arranged now
// shptrit = minstances.erase(shptrit); it = shapes.erase(it);
// break; break;
// } }
// } }
} }
return arranger(shapes.begin(), shapes.end()); 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 //// The final client function to arrange the Model. A progress indicator and
//// a stop predicate can be also be passed to control the process. //// a stop predicate can be also be passed to control the process.
bool arrange(Arrangeables & arrangables, bool arrange(ArrangeablePtrs & arrangables,
const Arrangeables & excludes, const ArrangeablePtrs & excludes,
coord_t min_obj_distance, coord_t min_obj_distance,
const BedShapeHint & bedhint, const BedShapeHint & bedhint,
std::function<void(unsigned)> progressind, std::function<void(unsigned)> progressind,
@ -741,29 +695,29 @@ bool arrange(Arrangeables & arrangables,
PackGroup preshapes{ {} }; // pack group with one initial bin for preloading PackGroup preshapes{ {} }; // pack group with one initial bin for preloading
auto process_arrangeable = auto process_arrangeable =
[](const Arrangeable * arrangeable, [](const Arrangeable * arrangeable,
std::vector<Item> & outp, std::vector<Item> & outp,
std::function<void(const Item &, unsigned)> applyfn) std::function<void(const Item &, unsigned)> applyfn)
{ {
assert(arrangeable); assert(arrangeable);
auto arrangeitem = arrangeable->get_arrange_polygon(); auto arrangeitem = arrangeable->get_arrange_polygon();
Polygon & p = std::get<0>(arrangeitem); Polygon & p = std::get<0>(arrangeitem);
const Vec2crd &offs = std::get<1>(arrangeitem); const Vec2crd &offs = std::get<1>(arrangeitem);
double rotation = std::get<2>(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(); auto firstp = clpath.Contour.front();
clpath.Contour.emplace_back(firstp); clpath.Contour.emplace_back(firstp);
outp.emplace_back(applyfn, std::move(clpath)); outp.emplace_back(applyfn, std::move(clpath));
outp.front().rotation(rotation); outp.front().rotation(rotation);
outp.front().translation({offs.x(), offs.y()}); outp.front().translation({offs.x(), offs.y()});
}; };
for (Arrangeable *arrangeable : arrangables) { for (Arrangeable *arrangeable : arrangables) {
process_arrangeable( process_arrangeable(
@ -774,8 +728,7 @@ bool arrange(Arrangeables & arrangables,
clppr::cInt stride = binidx * stride_padding(binwidth); clppr::cInt stride = binidx * stride_padding(binwidth);
clppr::IntPoint offs = itm.translation(); clppr::IntPoint offs = itm.translation();
arrangeable->apply_arrange_result({unscaled(offs.X + arrangeable->apply_arrange_result({unscaled(offs.X + stride),
stride),
unscaled(offs.Y)}, unscaled(offs.Y)},
itm.rotation()); itm.rotation());
}); });
@ -797,7 +750,6 @@ bool arrange(Arrangeables & arrangables,
// Create the arranger for the box shaped bed // Create the arranger for the box shaped bed
BoundingBox bbb = bedhint.shape.box; BoundingBox bbb = bedhint.shape.box;
bbb.min -= Point{md, md}, bbb.max += Point{md, md}; bbb.min -= Point{md, md}, bbb.max += Point{md, md};
Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}};
binwidth = coord_t(binbb.width()); binwidth = coord_t(binbb.width());
@ -808,6 +760,7 @@ bool arrange(Arrangeables & arrangables,
auto c = bedhint.shape.circ; auto c = bedhint.shape.circ;
auto cc = to_lnCircle(c); auto cc = to_lnCircle(c);
binwidth = scaled(c.radius()); binwidth = scaled(c.radius());
_arrange(items, preshapes, cc, min_obj_distance, progressind, cfn); _arrange(items, preshapes, cc, min_obj_distance, progressind, cfn);
break; break;
} }
@ -816,11 +769,21 @@ bool arrange(Arrangeables & arrangables,
auto irrbed = sl::create<clppr::Polygon>(std::move(ctour)); auto irrbed = sl::create<clppr::Polygon>(std::move(ctour));
BoundingBox polybb(bedhint.shape.polygon); BoundingBox polybb(bedhint.shape.polygon);
binwidth = (polybb.max(X) - polybb.min(X)); binwidth = (polybb.max(X) - polybb.min(X));
_arrange(items, preshapes, irrbed, min_obj_distance, progressind, cfn); _arrange(items, preshapes, irrbed, min_obj_distance, progressind, cfn);
break; break;
} }
case BedShapeType::WHO_KNOWS: { case BedShapeType::INFINITE: {
_arrange(items, preshapes, false, min_obj_distance, progressind, cfn); // 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; break;
} }
}; };
@ -831,7 +794,7 @@ bool arrange(Arrangeables & arrangables,
} }
/// Arrange, without the fixed items (excludes) /// Arrange, without the fixed items (excludes)
bool arrange(Arrangeables & inp, bool arrange(ArrangeablePtrs & inp,
coord_t min_d, coord_t min_d,
const BedShapeHint & bedhint, const BedShapeHint & bedhint,
std::function<void(unsigned)> prfn, std::function<void(unsigned)> prfn,

View File

@ -6,7 +6,7 @@
namespace Slic3r { namespace Slic3r {
namespace arr { namespace arrangement {
/// A geometry abstraction for a circular print bed. Similarly to BoundingBox. /// A geometry abstraction for a circular print bed. Similarly to BoundingBox.
class CircleBed { class CircleBed {
@ -22,21 +22,26 @@ public:
inline operator bool() { return !std::isnan(radius_); } inline operator bool() { return !std::isnan(radius_); }
}; };
/// Representing an unbounded bin
struct InfiniteBed { Point center; };
/// Types of print bed shapes. /// Types of print bed shapes.
enum class BedShapeType { enum class BedShapeType {
BOX, BOX,
CIRCLE, CIRCLE,
IRREGULAR, IRREGULAR,
WHO_KNOWS INFINITE,
UNKNOWN
}; };
/// Info about the print bed for the arrange() function. /// Info about the print bed for the arrange() function.
struct BedShapeHint { struct BedShapeHint {
BedShapeType type = BedShapeType::WHO_KNOWS; BedShapeType type = BedShapeType::INFINITE;
/*union*/ struct { // I know but who cares... TODO: use variant from cpp17? /*union*/ struct { // I know but who cares... TODO: use variant from cpp17?
CircleBed circ; CircleBed circ;
BoundingBox box; BoundingBox box;
Polyline polygon; Polyline polygon;
InfiniteBed infinite;
} shape; } shape;
}; };
@ -59,7 +64,7 @@ public:
virtual std::tuple<Polygon, Vec2crd, double> get_arrange_polygon() const = 0; virtual std::tuple<Polygon, Vec2crd, double> get_arrange_polygon() const = 0;
}; };
using Arrangeables = std::vector<Arrangeable*>; using ArrangeablePtrs = std::vector<Arrangeable*>;
/** /**
* \brief Arranges the model objects on the screen. * \brief Arranges the model objects on the screen.
@ -92,20 +97,20 @@ using Arrangeables = std::vector<Arrangeable*>;
* *
* \param stopcondition A predicate returning true if abort is needed. * \param stopcondition A predicate returning true if abort is needed.
*/ */
bool arrange(Arrangeables &items, bool arrange(ArrangeablePtrs &items,
coord_t min_obj_distance, coord_t min_obj_distance,
const BedShapeHint& bedhint, const BedShapeHint& bedhint,
std::function<void(unsigned)> progressind, std::function<void(unsigned)> progressind = nullptr,
std::function<bool(void)> stopcondition); std::function<bool(void)> stopcondition = nullptr);
/// Same as the previous, only that it takes unmovable items as an /// Same as the previous, only that it takes unmovable items as an
/// additional argument. /// additional argument.
bool arrange(Arrangeables &items, bool arrange(ArrangeablePtrs &items,
const Arrangeables &excludes, const ArrangeablePtrs &excludes,
coord_t min_obj_distance, coord_t min_obj_distance,
const BedShapeHint& bedhint, const BedShapeHint& bedhint,
std::function<void(unsigned)> progressind, std::function<void(unsigned)> progressind = nullptr,
std::function<bool(void)> stopcondition); std::function<bool(void)> stopcondition = nullptr);
} // arr } // arr
} // Slic3r } // Slic3r

View File

@ -107,8 +107,8 @@ add_library(libslic3r STATIC
Line.hpp Line.hpp
Model.cpp Model.cpp
Model.hpp Model.hpp
ModelArrange.hpp Arrange.hpp
ModelArrange.cpp Arrange.cpp
MotionPlanner.cpp MotionPlanner.cpp
MotionPlanner.hpp MotionPlanner.hpp
MultiPoint.cpp MultiPoint.cpp

View File

@ -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 // get the (transformed) size of each instance so that we take
// into account their different transformations when packing // into account their different transformations when packing
Pointfs instance_sizes; // Pointfs instance_sizes;
Pointfs instance_centers; // Pointfs instance_centers;
for (const ModelObject *o : this->objects) // for (const ModelObject *o : this->objects)
for (size_t i = 0; i < o->instances.size(); ++ i) { // for (size_t i = 0; i < o->instances.size(); ++ i) {
// an accurate snug bounding box around the transformed mesh. // // an accurate snug bounding box around the transformed mesh.
BoundingBoxf3 bbox(o->instance_bounding_box(i, true)); // BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
instance_sizes.emplace_back(to_2d(bbox.size())); // instance_sizes.emplace_back(to_2d(bbox.size()));
instance_centers.emplace_back(to_2d(bbox.center())); // instance_centers.emplace_back(to_2d(bbox.center()));
} // }
Pointfs positions; // Pointfs positions;
if (! _arrange(instance_sizes, dist, bb, positions)) // if (! _arrange(instance_sizes, dist, bb, positions))
return false; // return false;
size_t idx = 0; // size_t idx = 0;
for (ModelObject *o : this->objects) { // for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances) { // for (ModelInstance *i : o->instances) {
Vec2d offset_xy = positions[idx] - instance_centers[idx]; // Vec2d offset_xy = positions[idx] - instance_centers[idx];
i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z))); // i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z)));
++idx; // ++idx;
} // }
o->invalidate_bounding_box(); // 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. // Duplicate the entire model preserving instance relative positions.
@ -1804,31 +1819,27 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
std::tuple<Polygon, Vec2crd, double> ModelInstance::get_arrange_polygon() const std::tuple<Polygon, Vec2crd, double> ModelInstance::get_arrange_polygon() const
{ {
static const double SIMPLIFY_TOLERANCE_MM = 0.1; static const double SIMPLIFY_TOLERANCE_MM = 0.1;
assert(m_inst); Vec3d rotation = get_rotation();
rotation.z() = 0.;
Vec3d rotation = get_rotation(); Transform3d trafo_instance =
rotation.z() = 0.; Geometry::assemble_transform(Vec3d::Zero(), rotation,
Transform3d trafo_instance = Geometry:: get_scaling_factor(), get_mirror());
assemble_transform(Vec3d::Zero(),
rotation,
get_scaling_factor(),
get_mirror());
Polygon p = get_object()->convex_hull_2d(trafo_instance); Polygon p = get_object()->convex_hull_2d(trafo_instance);
assert(!p.points.empty()); assert(!p.points.empty());
// this may happen for malformed models, see: // this may happen for malformed models, see:
// https://github.com/prusa3d/PrusaSlicer/issues/2209 // https://github.com/prusa3d/PrusaSlicer/issues/2209
if (p.points.empty()) return {}; if (p.points.empty()) return {};
Polygons pp{p}; Polygons pp{p};
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM)); pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
if (!pp.empty()) p = pp.front(); if (!pp.empty()) p = pp.front();
return std::make_tuple(p, Vec2crd{scaled(get_offset(X)), return std::make_tuple(p,
scaled(get_offset(Y))}, Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))},
get_rotation(Z)); get_rotation(Z));
} }

View File

@ -7,7 +7,7 @@
#include "Point.hpp" #include "Point.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "Slicing.hpp" #include "Slicing.hpp"
#include "ModelArrange.hpp" #include "Arrange.hpp"
#include <map> #include <map>
#include <memory> #include <memory>
@ -491,7 +491,7 @@ private:
// A single instance of a ModelObject. // A single instance of a ModelObject.
// Knows the affine transformation of an object. // Knows the affine transformation of an object.
class ModelInstance : public ModelBase, public arr::Arrangeable class ModelInstance : public ModelBase, public arrangement::Arrangeable
{ {
public: public:
enum EPrintVolumeState : unsigned char enum EPrintVolumeState : unsigned char

View File

@ -4,7 +4,7 @@
#include <stddef.h> #include <stddef.h>
#include <memory> #include <memory>
#include "libslic3r/ModelArrange.hpp" #include "libslic3r/Arrange.hpp"
#include "3DScene.hpp" #include "3DScene.hpp"
#include "GLToolbar.hpp" #include "GLToolbar.hpp"
#include "Event.hpp" #include "Event.hpp"
@ -612,7 +612,7 @@ public:
int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } 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(); } 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_pos = {std::nan(""), std::nan("")};
Vec2d m_bb_size; Vec2d m_bb_size;
double m_rotation; double m_rotation;

View File

@ -32,7 +32,6 @@
#include "libslic3r/Format/3mf.hpp" #include "libslic3r/Format/3mf.hpp"
#include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/GCode/PreviewData.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/ModelArrange.hpp"
#include "libslic3r/Polygon.hpp" #include "libslic3r/Polygon.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
@ -1218,6 +1217,28 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
return true; 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 // Plater / private
struct Plater::priv struct Plater::priv
{ {
@ -1425,50 +1446,91 @@ struct Plater::priv
class ArrangeJob : public Job class ArrangeJob : public Job
{ {
int m_count = 0;
GLCanvas3D::WipeTowerInfo m_wti; GLCanvas3D::WipeTowerInfo m_wti;
arrangement::ArrangeablePtrs m_selected, m_unselected;
static std::array<arrangement::ArrangeablePtrs, 2> 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: protected:
void prepare() override void prepare() override
{ {
m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info();
m_count = bool(m_wti);
for (auto obj : plater().model.objects) const Selection& sel = plater().get_selection();
m_count += int(obj->instances.size()); 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: public:
//using Job::Job; using Job::Job;
ArrangeJob(priv * pltr): Job(pltr) {} int status_range() const override
int status_range() const override { return m_count; } {
void set_count(int c) { m_count = c; } return int(m_selected.size());
}
void process() override; void process() override;
} arrange_job/*{m_plater}*/; } arrange_job{m_plater};
class RotoptimizeJob : public Job class RotoptimizeJob : public Job
{ {
public: public:
//using Job::Job; using Job::Job;
RotoptimizeJob(priv * pltr): Job(pltr) {}
void process() override; void process() override;
} rotoptimize_job/*{m_plater}*/; } rotoptimize_job{m_plater};
// To create a new job, just define a new subclass of Job, implement // To create a new job, just define a new subclass of Job, implement
// the process and the optional prepare() and finalize() methods // the process and the optional prepare() and finalize() methods
// Register the instance of the class in the m_jobs container // Register the instance of the class in the m_jobs container
// if it cannot run concurrently with other jobs in this group // if it cannot run concurrently with other jobs in this group
std::vector<std::reference_wrapper<Job>> m_jobs/*{arrange_job, std::vector<std::reference_wrapper<Job>> m_jobs{arrange_job,
rotoptimize_job}*/; rotoptimize_job};
public: public:
ExclusiveJobGroup(priv *_plater) ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {}
: m_plater(_plater)
, arrange_job(m_plater)
, rotoptimize_job(m_plater)
, m_jobs({arrange_job, rotoptimize_job})
{}
void start(Jobs jid) { void start(Jobs jid) {
m_plater->background_process.stop(); m_plater->background_process.stop();
@ -1528,7 +1590,7 @@ struct Plater::priv
std::string get_config(const std::string &key) const; std::string get_config(const std::string &key) const;
BoundingBoxf bed_shape_bb() const; BoundingBoxf bed_shape_bb() const;
BoundingBox scaled_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<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config); std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config);
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects); std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
wxString get_export_file(GUI::FileType file_type); wxString get_export_file(GUI::FileType file_type);
@ -2404,8 +2466,8 @@ void Plater::priv::sla_optimize_rotation() {
m_ui_jobs.start(Jobs::Rotoptimize); m_ui_jobs.start(Jobs::Rotoptimize);
} }
arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const {
arr::BedShapeHint bedshape; arrangement::BedShapeHint bedshape;
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape"); const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
assert(bed_shape_opt); assert(bed_shape_opt);
@ -2414,7 +2476,7 @@ arr::BedShapeHint Plater::priv::get_bed_shape_hint() const {
auto &bedpoints = bed_shape_opt->values; auto &bedpoints = bed_shape_opt->values;
Polyline bedpoly; bedpoly.points.reserve(bedpoints.size()); Polyline bedpoly; bedpoly.points.reserve(bedpoints.size());
for (auto &v : bedpoints) bedpoly.append(scaled(v)); for (auto &v : bedpoints) bedpoly.append(scaled(v));
bedshape = arr::bedShape(bedpoly); bedshape = arrangement::bedShape(bedpoly);
} }
return bedshape; return bedshape;
@ -2423,15 +2485,6 @@ arr::BedShapeHint Plater::priv::get_bed_shape_hint() const {
void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() {
static const auto arrangestr = _(L("Arranging")); 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 // 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. // on printer technology. I guess the following should work but it crashes.
double dist = 6; // PrintConfig::min_object_distance(config); 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); coord_t min_obj_distance = scaled(dist);
auto count = unsigned(m_selected.size());
arr::BedShapeHint bedshape = plater().get_bed_shape_hint(); arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint();
try { try {
arr::arrange(arrangeinput, arrangement::arrange(m_selected, m_unselected, min_obj_distance,
min_obj_distance, bedshape,
bedshape, [this, count](unsigned st) {
[this](unsigned st) { if (st > 0) // will not finalize after last one
if (st > 0) update_status(count - st, arrangestr);
update_status(m_count - int(st), arrangestr); },
}, [this]() { return was_canceled(); });
[this]() { return was_canceled(); });
} catch (std::exception & /*e*/) { } catch (std::exception & /*e*/) {
GUI::show_error(plater().q, GUI::show_error(plater().q,
_(L("Could not arrange model objects! " _(L("Could not arrange model objects! "
"Some geometries may be invalid."))); "Some geometries may be invalid.")));
} }
update_status(m_count, // finalize just here.
update_status(int(count),
was_canceled() ? _(L("Arranging canceled.")) was_canceled() ? _(L("Arranging canceled."))
: _(L("Arranging done."))); : _(L("Arranging done.")));
} }
@ -2466,7 +2519,7 @@ void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() {
void find_new_position(const Model & model, void find_new_position(const Model & model,
ModelInstancePtrs instances, ModelInstancePtrs instances,
coord_t min_d, coord_t min_d,
const arr::BedShapeHint &bedhint) const arrangement::BedShapeHint &bedhint)
{ {
// TODO // TODO