mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 16:57:53 -06:00
work in progress on new ModelArrange interface
This commit is contained in:
parent
f4ed0d8137
commit
e1d612d05f
3 changed files with 567 additions and 487 deletions
|
@ -1,5 +1,5 @@
|
||||||
#include "ModelArrange.hpp"
|
#include "ModelArrange.hpp"
|
||||||
#include "Model.hpp"
|
//#include "Model.hpp"
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
#include "MTUtils.hpp"
|
#include "MTUtils.hpp"
|
||||||
|
@ -43,87 +43,87 @@ namespace arr {
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
|
|
||||||
// Only for debugging. Prints the model object vertices on stdout.
|
// Only for debugging. Prints the model object vertices on stdout.
|
||||||
std::string toString(const Model& model, bool holes = true) {
|
//std::string toString(const Model& model, bool holes = true) {
|
||||||
std::stringstream ss;
|
// std::stringstream ss;
|
||||||
|
|
||||||
ss << "{\n";
|
// ss << "{\n";
|
||||||
|
|
||||||
for(auto objptr : model.objects) {
|
// for(auto objptr : model.objects) {
|
||||||
if(!objptr) continue;
|
// if(!objptr) continue;
|
||||||
|
|
||||||
auto rmesh = objptr->raw_mesh();
|
// auto rmesh = objptr->raw_mesh();
|
||||||
|
|
||||||
for(auto objinst : objptr->instances) {
|
// for(auto objinst : objptr->instances) {
|
||||||
if(!objinst) continue;
|
// if(!objinst) continue;
|
||||||
|
|
||||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
// Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||||
// CHECK_ME -> Is the following correct ?
|
// // CHECK_ME -> Is the following correct ?
|
||||||
tmpmesh.scale(objinst->get_scaling_factor());
|
// tmpmesh.scale(objinst->get_scaling_factor());
|
||||||
objinst->transform_mesh(&tmpmesh);
|
// objinst->transform_mesh(&tmpmesh);
|
||||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
// ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||||
for(auto& expoly_complex : expolys) {
|
// for(auto& expoly_complex : expolys) {
|
||||||
|
|
||||||
ExPolygons tmp = expoly_complex.simplify(scaled<double>(1.));
|
// ExPolygons tmp = expoly_complex.simplify(scaled<double>(1.));
|
||||||
if(tmp.empty()) continue;
|
// if(tmp.empty()) continue;
|
||||||
ExPolygon expoly = tmp.front();
|
// ExPolygon expoly = tmp.front();
|
||||||
expoly.contour.make_clockwise();
|
// expoly.contour.make_clockwise();
|
||||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
// for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||||
|
|
||||||
ss << "\t{\n";
|
// ss << "\t{\n";
|
||||||
ss << "\t\t{\n";
|
// ss << "\t\t{\n";
|
||||||
|
|
||||||
for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
// for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
||||||
<< v(0) << ", "
|
// << v(0) << ", "
|
||||||
<< v(1) << "},\n";
|
// << v(1) << "},\n";
|
||||||
{
|
// {
|
||||||
auto v = expoly.contour.points.front();
|
// auto v = expoly.contour.points.front();
|
||||||
ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n";
|
// ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n";
|
||||||
}
|
// }
|
||||||
ss << "\t\t},\n";
|
// ss << "\t\t},\n";
|
||||||
|
|
||||||
// Holes:
|
// // Holes:
|
||||||
ss << "\t\t{\n";
|
// ss << "\t\t{\n";
|
||||||
if(holes) for(auto h : expoly.holes) {
|
// if(holes) for(auto h : expoly.holes) {
|
||||||
ss << "\t\t\t{\n";
|
// ss << "\t\t\t{\n";
|
||||||
for(auto v : h.points) ss << "\t\t\t\t{"
|
// for(auto v : h.points) ss << "\t\t\t\t{"
|
||||||
<< v(0) << ", "
|
// << v(0) << ", "
|
||||||
<< v(1) << "},\n";
|
// << v(1) << "},\n";
|
||||||
{
|
// {
|
||||||
auto v = h.points.front();
|
// auto v = h.points.front();
|
||||||
ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n";
|
// ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n";
|
||||||
}
|
// }
|
||||||
ss << "\t\t\t},\n";
|
// ss << "\t\t\t},\n";
|
||||||
}
|
// }
|
||||||
ss << "\t\t},\n";
|
// ss << "\t\t},\n";
|
||||||
|
|
||||||
ss << "\t},\n";
|
// ss << "\t},\n";
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
ss << "}\n";
|
// ss << "}\n";
|
||||||
|
|
||||||
return ss.str();
|
// return ss.str();
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Debugging: Save model to svg file.
|
// Debugging: Save model to svg file.
|
||||||
void toSVG(SVG& svg, const Model& model) {
|
//void toSVG(SVG& svg, const Model& model) {
|
||||||
for(auto objptr : model.objects) {
|
// for(auto objptr : model.objects) {
|
||||||
if(!objptr) continue;
|
// if(!objptr) continue;
|
||||||
|
|
||||||
auto rmesh = objptr->raw_mesh();
|
// auto rmesh = objptr->raw_mesh();
|
||||||
|
|
||||||
for(auto objinst : objptr->instances) {
|
// for(auto objinst : objptr->instances) {
|
||||||
if(!objinst) continue;
|
// if(!objinst) continue;
|
||||||
|
|
||||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
// Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||||
tmpmesh.scale(objinst->get_scaling_factor());
|
// tmpmesh.scale(objinst->get_scaling_factor());
|
||||||
objinst->transform_mesh(&tmpmesh);
|
// objinst->transform_mesh(&tmpmesh);
|
||||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
// ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||||
svg.draw(expolys);
|
// svg.draw(expolys);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
namespace bgi = boost::geometry::index;
|
namespace bgi = boost::geometry::index;
|
||||||
|
|
||||||
|
@ -565,143 +565,143 @@ public:
|
||||||
|
|
||||||
// A container which stores a pointer to the 3D object and its projected
|
// A container which stores a pointer to the 3D object and its projected
|
||||||
// 2D shape from top view.
|
// 2D shape from top view.
|
||||||
using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
//using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||||
|
|
||||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model,
|
//ShapeData2D projectModelFromTop(const Slic3r::Model &model,
|
||||||
const WipeTowerInfo &wti,
|
// const WipeTowerInfo &wti,
|
||||||
double tolerance)
|
// double tolerance)
|
||||||
{
|
//{
|
||||||
ShapeData2D ret;
|
// ShapeData2D ret;
|
||||||
|
|
||||||
// Count all the items on the bin (all the object's instances)
|
// // Count all the items on the bin (all the object's instances)
|
||||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(),
|
// auto s = std::accumulate(model.objects.begin(), model.objects.end(),
|
||||||
size_t(0), [](size_t s, ModelObject* o)
|
// size_t(0), [](size_t s, ModelObject* o)
|
||||||
{
|
// {
|
||||||
return s + o->instances.size();
|
// return s + o->instances.size();
|
||||||
});
|
// });
|
||||||
|
|
||||||
ret.reserve(s);
|
// ret.reserve(s);
|
||||||
|
|
||||||
|
// for(ModelObject* objptr : model.objects) {
|
||||||
|
// if (! objptr->instances.empty()) {
|
||||||
|
|
||||||
for(ModelObject* objptr : model.objects) {
|
// // TODO export the exact 2D projection. Cannot do it as libnest2d
|
||||||
if (! objptr->instances.empty()) {
|
// // does not support concave shapes (yet).
|
||||||
|
// ClipperLib::Path clpath;
|
||||||
|
|
||||||
// TODO export the exact 2D projection. Cannot do it as libnest2d
|
// // Object instances should carry the same scaling and
|
||||||
// does not support concave shapes (yet).
|
// // x, y rotation that is why we use the first instance.
|
||||||
ClipperLib::Path clpath;
|
// {
|
||||||
|
// ModelInstance *finst = objptr->instances.front();
|
||||||
// Object instances should carry the same scaling and
|
// Vec3d rotation = finst->get_rotation();
|
||||||
// x, y rotation that is why we use the first instance.
|
// rotation.z() = 0.;
|
||||||
{
|
// Transform3d trafo_instance = Geometry::assemble_transform(
|
||||||
ModelInstance *finst = objptr->instances.front();
|
// Vec3d::Zero(),
|
||||||
Vec3d rotation = finst->get_rotation();
|
// rotation,
|
||||||
rotation.z() = 0.;
|
// finst->get_scaling_factor(),
|
||||||
Transform3d trafo_instance = Geometry::assemble_transform(
|
// finst->get_mirror());
|
||||||
Vec3d::Zero(),
|
// Polygon p = objptr->convex_hull_2d(trafo_instance);
|
||||||
rotation,
|
|
||||||
finst->get_scaling_factor(),
|
|
||||||
finst->get_mirror());
|
|
||||||
Polygon p = objptr->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()) continue;
|
// if (p.points.empty()) continue;
|
||||||
|
|
||||||
if(tolerance > EPSILON) {
|
// if(tolerance > EPSILON) {
|
||||||
Polygons pp { p };
|
// Polygons pp { p };
|
||||||
pp = p.simplify(scaled<double>(tolerance));
|
// pp = p.simplify(scaled<double>(tolerance));
|
||||||
if (!pp.empty()) p = pp.front();
|
// if (!pp.empty()) p = pp.front();
|
||||||
}
|
// }
|
||||||
|
|
||||||
p.reverse();
|
// p.reverse();
|
||||||
assert(!p.is_counter_clockwise());
|
// assert(!p.is_counter_clockwise());
|
||||||
clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
// clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||||
auto firstp = clpath.front(); clpath.emplace_back(firstp);
|
// auto firstp = clpath.front(); clpath.emplace_back(firstp);
|
||||||
}
|
// }
|
||||||
|
|
||||||
Vec3d rotation0 = objptr->instances.front()->get_rotation();
|
// Vec3d rotation0 = objptr->instances.front()->get_rotation();
|
||||||
rotation0(2) = 0.;
|
// rotation0(2) = 0.;
|
||||||
for(ModelInstance* objinst : objptr->instances) {
|
// for(ModelInstance* objinst : objptr->instances) {
|
||||||
ClipperLib::Polygon pn;
|
// ClipperLib::Polygon pn;
|
||||||
pn.Contour = clpath;
|
// pn.Contour = clpath;
|
||||||
|
|
||||||
// Efficient conversion to item.
|
// // Efficient conversion to item.
|
||||||
Item item(std::move(pn));
|
// Item item(std::move(pn));
|
||||||
|
|
||||||
// Invalid geometries would throw exceptions when arranging
|
// // Invalid geometries would throw exceptions when arranging
|
||||||
if(item.vertexCount() > 3) {
|
// if(item.vertexCount() > 3) {
|
||||||
item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()));
|
// item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()));
|
||||||
item.translation({
|
// item.translation({
|
||||||
scaled<ClipperLib::cInt>(objinst->get_offset(X)),
|
// scaled<ClipperLib::cInt>(objinst->get_offset(X)),
|
||||||
scaled<ClipperLib::cInt>(objinst->get_offset(Y))
|
// scaled<ClipperLib::cInt>(objinst->get_offset(Y))
|
||||||
});
|
// });
|
||||||
ret.emplace_back(objinst, item);
|
// ret.emplace_back(objinst, item);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// The wipe tower is a separate case (in case there is one), let's duplicate the code
|
// // The wipe tower is a separate case (in case there is one), let's duplicate the code
|
||||||
if (wti.is_wipe_tower) {
|
// if (wti.is_wipe_tower) {
|
||||||
Points pts;
|
// Points pts;
|
||||||
pts.emplace_back(coord_t(scale_(0.)), coord_t(scale_(0.)));
|
// pts.emplace_back(coord_t(scale_(0.)), coord_t(scale_(0.)));
|
||||||
pts.emplace_back(coord_t(scale_(wti.bb_size(0))), coord_t(scale_(0.)));
|
// pts.emplace_back(coord_t(scale_(wti.bb_size(0))), coord_t(scale_(0.)));
|
||||||
pts.emplace_back(coord_t(scale_(wti.bb_size(0))), coord_t(scale_(wti.bb_size(1))));
|
// pts.emplace_back(coord_t(scale_(wti.bb_size(0))), coord_t(scale_(wti.bb_size(1))));
|
||||||
pts.emplace_back(coord_t(scale_(-0.)), coord_t(scale_(wti.bb_size(1))));
|
// pts.emplace_back(coord_t(scale_(-0.)), coord_t(scale_(wti.bb_size(1))));
|
||||||
pts.emplace_back(coord_t(scale_(-0.)), coord_t(scale_(0.)));
|
// pts.emplace_back(coord_t(scale_(-0.)), coord_t(scale_(0.)));
|
||||||
Polygon p(std::move(pts));
|
// Polygon p(std::move(pts));
|
||||||
ClipperLib::Path clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
// ClipperLib::Path clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||||
ClipperLib::Polygon pn;
|
// ClipperLib::Polygon pn;
|
||||||
pn.Contour = clpath;
|
// pn.Contour = clpath;
|
||||||
// Efficient conversion to item.
|
// // Efficient conversion to item.
|
||||||
Item item(std::move(pn));
|
// Item item(std::move(pn));
|
||||||
item.rotation(wti.rotation),
|
// item.rotation(wti.rotation),
|
||||||
item.translation({
|
// item.translation({
|
||||||
scaled<ClipperLib::cInt>(wti.pos(0)),
|
// scaled<ClipperLib::cInt>(wti.pos(0)),
|
||||||
scaled<ClipperLib::cInt>(wti.pos(1))
|
// scaled<ClipperLib::cInt>(wti.pos(1))
|
||||||
});
|
// });
|
||||||
ret.emplace_back(nullptr, item);
|
// ret.emplace_back(nullptr, item);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Apply the calculated translations and rotations (currently disabled) to
|
// Apply the calculated translations and rotations (currently disabled) to
|
||||||
// the Model object instances.
|
// the Model object instances.
|
||||||
void applyResult(IndexedPackGroup::value_type &group,
|
//void applyResult(IndexedPackGroup::value_type &group,
|
||||||
ClipperLib::cInt batch_offset,
|
// ClipperLib::cInt batch_offset,
|
||||||
ShapeData2D & shapemap,
|
// ShapeData2D & shapemap,
|
||||||
WipeTowerInfo & wti)
|
// WipeTowerInfo & wti)
|
||||||
{
|
//{
|
||||||
for(auto& r : group) {
|
// for(auto& r : group) {
|
||||||
auto idx = r.first; // get the original item index
|
// auto idx = r.first; // get the original item index
|
||||||
Item& item = r.second; // get the item itself
|
// Item& item = r.second; // get the item itself
|
||||||
|
|
||||||
// Get the model instance from the shapemap using the index
|
// // Get the model instance from the shapemap using the index
|
||||||
ModelInstance *inst_ptr = shapemap[idx].first;
|
// ModelInstance *inst_ptr = shapemap[idx].first;
|
||||||
|
|
||||||
// Get the transformation data from the item object and scale it
|
// // Get the transformation data from the item object and scale it
|
||||||
// appropriately
|
// // appropriately
|
||||||
auto off = item.translation();
|
// auto off = item.translation();
|
||||||
Radians rot = item.rotation();
|
// Radians rot = item.rotation();
|
||||||
|
|
||||||
Vec3d foff(unscaled(off.X + batch_offset) ,
|
// Vec3d foff(unscaled(off.X + batch_offset),
|
||||||
unscaled(off.Y),
|
// unscaled(off.Y),
|
||||||
inst_ptr ? inst_ptr->get_offset()(Z) : 0.);
|
// inst_ptr ? inst_ptr->get_offset()(Z) : 0.);
|
||||||
|
|
||||||
if (inst_ptr) {
|
// if (inst_ptr) {
|
||||||
// write the transformation data into the model instance
|
// // write the transformation data into the model instance
|
||||||
inst_ptr->set_rotation(Z, rot);
|
// inst_ptr->set_rotation(Z, rot);
|
||||||
inst_ptr->set_offset(foff);
|
// inst_ptr->set_offset(foff);
|
||||||
}
|
// }
|
||||||
else { // this is the wipe tower - we will modify the struct with the info
|
// else { // this is the wipe tower - we will modify the struct with the info
|
||||||
// and leave it up to the called to actually move the wipe tower
|
// // and leave it up to the called to actually move the wipe tower
|
||||||
wti.pos = Vec2d(foff(0), foff(1));
|
// wti.pos = Vec2d(foff(0), foff(1));
|
||||||
wti.rotation = rot;
|
// wti.rotation = rot;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -784,254 +784,254 @@ BedShapeHint bedShape(const Polyline &bed) {
|
||||||
|
|
||||||
static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1;
|
static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1;
|
||||||
|
|
||||||
template<class BinT>
|
//template<class BinT>
|
||||||
IndexedPackGroup _arrange(std::vector<std::reference_wrapper<Item>> &shapes,
|
//IndexedPackGroup _arrange(std::vector<std::reference_wrapper<Item>> &shapes,
|
||||||
const BinT & bin,
|
// const BinT & bin,
|
||||||
coord_t minobjd,
|
// coord_t minobjd,
|
||||||
std::function<void(unsigned)> prind,
|
// std::function<void(unsigned)> prind,
|
||||||
std::function<bool()> stopfn)
|
// std::function<bool()> stopfn)
|
||||||
{
|
//{
|
||||||
AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
|
// AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
|
||||||
return arranger(shapes.begin(), shapes.end());
|
// return arranger(shapes.begin(), shapes.end());
|
||||||
}
|
//}
|
||||||
|
|
||||||
template<class BinT>
|
//template<class BinT>
|
||||||
IndexedPackGroup _arrange(std::vector<std::reference_wrapper<Item>> &shapes,
|
//IndexedPackGroup _arrange(std::vector<std::reference_wrapper<Item>> &shapes,
|
||||||
const PackGroup & preshapes,
|
// const PackGroup & preshapes,
|
||||||
std::vector<ModelInstance *> &minstances,
|
// std::vector<ModelInstance *> &minstances,
|
||||||
const BinT & bin,
|
// const BinT & bin,
|
||||||
coord_t minobjd)
|
// coord_t minobjd)
|
||||||
{
|
//{
|
||||||
|
|
||||||
auto binbb = sl::boundingBox(bin);
|
// auto binbb = sl::boundingBox(bin);
|
||||||
|
|
||||||
AutoArranger<BinT> arranger{bin, minobjd};
|
// AutoArranger<BinT> arranger{bin, minobjd};
|
||||||
|
|
||||||
if(!preshapes.front().empty()) { // If there is something on the plate
|
// if(!preshapes.front().empty()) { // If there is something on the plate
|
||||||
arranger.preload(preshapes);
|
// arranger.preload(preshapes);
|
||||||
|
|
||||||
// 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();
|
// auto shptrit = minstances.begin();
|
||||||
for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit)
|
// for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit)
|
||||||
{
|
// {
|
||||||
// Try to place items to the center
|
// // Try to place items to the center
|
||||||
Item& itm = *shit;
|
// Item& itm = *shit;
|
||||||
auto ibb = itm.boundingBox();
|
// auto ibb = itm.boundingBox();
|
||||||
auto d = binbb.center() - ibb.center();
|
// auto d = binbb.center() - ibb.center();
|
||||||
itm.translate(d);
|
// itm.translate(d);
|
||||||
if(!arranger.is_colliding(itm)) {
|
// if(!arranger.is_colliding(itm)) {
|
||||||
arranger.preload({{itm}});
|
// arranger.preload({{itm}});
|
||||||
|
|
||||||
auto offset = itm.translation();
|
// auto offset = itm.translation();
|
||||||
Radians rot = itm.rotation();
|
// Radians rot = itm.rotation();
|
||||||
ModelInstance *minst = *shptrit;
|
// ModelInstance *minst = *shptrit;
|
||||||
|
|
||||||
Vec3d foffset(unscaled(offset.X),
|
// Vec3d foffset(unscaled(offset.X),
|
||||||
unscaled(offset.Y),
|
// unscaled(offset.Y),
|
||||||
minst->get_offset()(Z));
|
// minst->get_offset()(Z));
|
||||||
|
|
||||||
// write the transformation data into the model instance
|
// // write the transformation data into the model instance
|
||||||
minst->set_rotation(Z, rot);
|
// minst->set_rotation(Z, rot);
|
||||||
minst->set_offset(foffset);
|
// minst->set_offset(foffset);
|
||||||
|
|
||||||
shit = shapes.erase(shit);
|
// shit = shapes.erase(shit);
|
||||||
shptrit = minstances.erase(shptrit);
|
// shptrit = minstances.erase(shptrit);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return arranger(shapes.begin(), shapes.end());
|
// return arranger(shapes.begin(), shapes.end());
|
||||||
}
|
//}
|
||||||
|
|
||||||
inline SLIC3R_CONSTEXPR libnest2d::Coord stride_padding(Coord w)
|
inline SLIC3R_CONSTEXPR libnest2d::Coord stride_padding(Coord w)
|
||||||
{
|
{
|
||||||
return w + w / 5;
|
return w + w / 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(Model &model, // The model with the geometries
|
//bool arrange(Model &model, // The model with the geometries
|
||||||
WipeTowerInfo& wti, // Wipe tower info
|
// WipeTowerInfo& wti, // Wipe tower info
|
||||||
coord_t min_obj_distance, // Has to be in scaled (clipper) measure
|
// coord_t min_obj_distance, // Has to be in scaled (clipper) measure
|
||||||
const Polyline &bed, // The bed geometry.
|
// const Polyline &bed, // The bed geometry.
|
||||||
BedShapeHint bedhint, // Hint about the bed geometry type.
|
// BedShapeHint bedhint, // Hint about the bed geometry type.
|
||||||
bool first_bin_only, // What to do is not all items fit.
|
// bool first_bin_only, // What to do is not all items fit.
|
||||||
|
|
||||||
// Controlling callbacks.
|
// // Controlling callbacks.
|
||||||
std::function<void (unsigned)> progressind,
|
// std::function<void (unsigned)> progressind,
|
||||||
std::function<bool ()> stopcondition)
|
// std::function<bool ()> stopcondition)
|
||||||
{
|
//{
|
||||||
bool ret = true;
|
// bool ret = true;
|
||||||
|
|
||||||
// Get the 2D projected shapes with their 3D model instance pointers
|
// // Get the 2D projected shapes with their 3D model instance pointers
|
||||||
auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM);
|
// auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM);
|
||||||
|
|
||||||
// Copy the references for the shapes only as the arranger expects a
|
// // Copy the references for the shapes only as the arranger expects a
|
||||||
// sequence of objects convertible to Item or ClipperPolygon
|
// // sequence of objects convertible to Item or ClipperPolygon
|
||||||
std::vector<std::reference_wrapper<Item>> shapes;
|
// std::vector<std::reference_wrapper<Item>> shapes;
|
||||||
shapes.reserve(shapemap.size());
|
// shapes.reserve(shapemap.size());
|
||||||
std::for_each(shapemap.begin(), shapemap.end(),
|
// std::for_each(shapemap.begin(), shapemap.end(),
|
||||||
[&shapes] (ShapeData2D::value_type& it)
|
// [&shapes] (ShapeData2D::value_type& it)
|
||||||
{
|
// {
|
||||||
shapes.push_back(std::ref(it.second));
|
// shapes.push_back(std::ref(it.second));
|
||||||
});
|
// });
|
||||||
|
|
||||||
IndexedPackGroup result;
|
// IndexedPackGroup result;
|
||||||
|
|
||||||
// If there is no hint about the shape, we will try to guess
|
// // If there is no hint about the shape, we will try to guess
|
||||||
if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
|
// if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
|
||||||
|
|
||||||
BoundingBox bbb(bed);
|
// BoundingBox bbb(bed);
|
||||||
|
|
||||||
auto& cfn = stopcondition;
|
// auto& cfn = stopcondition;
|
||||||
|
|
||||||
// Integer ceiling the min distance from the bed perimeters
|
// // Integer ceiling the min distance from the bed perimeters
|
||||||
coord_t md = min_obj_distance - SCALED_EPSILON;
|
// coord_t md = min_obj_distance - SCALED_EPSILON;
|
||||||
md = (md % 2) ? md / 2 + 1 : md / 2;
|
// md = (md % 2) ? md / 2 + 1 : md / 2;
|
||||||
|
|
||||||
auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md,
|
// auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md,
|
||||||
ClipperLib::cInt{bbb.min(1)} - md},
|
// ClipperLib::cInt{bbb.min(1)} - md},
|
||||||
{ClipperLib::cInt{bbb.max(0)} + md,
|
// {ClipperLib::cInt{bbb.max(0)} + md,
|
||||||
ClipperLib::cInt{bbb.max(1)} + md});
|
// ClipperLib::cInt{bbb.max(1)} + md});
|
||||||
|
|
||||||
switch(bedhint.type) {
|
// switch(bedhint.type) {
|
||||||
case BedShapeType::BOX: {
|
// case BedShapeType::BOX: {
|
||||||
// Create the arranger for the box shaped bed
|
// // Create the arranger for the box shaped bed
|
||||||
result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn);
|
// result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case BedShapeType::CIRCLE: {
|
// case BedShapeType::CIRCLE: {
|
||||||
auto c = bedhint.shape.circ;
|
// auto c = bedhint.shape.circ;
|
||||||
auto cc = to_lnCircle(c);
|
// auto cc = to_lnCircle(c);
|
||||||
result = _arrange(shapes, cc, min_obj_distance, progressind, cfn);
|
// result = _arrange(shapes, cc, min_obj_distance, progressind, cfn);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case BedShapeType::IRREGULAR:
|
// case BedShapeType::IRREGULAR:
|
||||||
case BedShapeType::WHO_KNOWS: {
|
// case BedShapeType::WHO_KNOWS: {
|
||||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
// auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||||
ClipperLib::Polygon irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
// ClipperLib::Polygon irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||||
result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn);
|
// result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
if(result.empty() || stopcondition()) return false;
|
// if(result.empty() || stopcondition()) return false;
|
||||||
|
|
||||||
if(first_bin_only) {
|
// if(first_bin_only) {
|
||||||
applyResult(result.front(), 0, shapemap, wti);
|
// applyResult(result.front(), 0, shapemap, wti);
|
||||||
} else {
|
// } else {
|
||||||
|
|
||||||
ClipperLib::cInt stride = stride_padding(binbb.width());
|
// ClipperLib::cInt stride = stride_padding(binbb.width());
|
||||||
ClipperLib::cInt batch_offset = 0;
|
// ClipperLib::cInt batch_offset = 0;
|
||||||
|
|
||||||
for(auto& group : result) {
|
// for(auto& group : result) {
|
||||||
applyResult(group, batch_offset, shapemap, wti);
|
// applyResult(group, batch_offset, shapemap, wti);
|
||||||
|
|
||||||
// Only the first pack group can be placed onto the print bed. The
|
// // Only the first pack group can be placed onto the print bed. The
|
||||||
// other objects which could not fit will be placed next to the
|
// // other objects which could not fit will be placed next to the
|
||||||
// print bed
|
// // print bed
|
||||||
batch_offset += stride;
|
// batch_offset += stride;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
// for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
||||||
|
|
||||||
return ret && result.size() == 1;
|
// return ret && result.size() == 1;
|
||||||
}
|
//}
|
||||||
|
|
||||||
void find_new_position(const Model &model,
|
//void find_new_position(const Model &model,
|
||||||
ModelInstancePtrs toadd,
|
// ModelInstancePtrs toadd,
|
||||||
coord_t min_obj_distance,
|
// coord_t min_obj_distance,
|
||||||
const Polyline &bed,
|
// const Polyline &bed,
|
||||||
WipeTowerInfo& wti)
|
// WipeTowerInfo& wti)
|
||||||
{
|
//{
|
||||||
// Get the 2D projected shapes with their 3D model instance pointers
|
// // Get the 2D projected shapes with their 3D model instance pointers
|
||||||
auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM);
|
// auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM);
|
||||||
|
|
||||||
// Copy the references for the shapes only, as the arranger expects a
|
// // Copy the references for the shapes only, as the arranger expects a
|
||||||
// sequence of objects convertible to Item or ClipperPolygon
|
// // sequence of objects convertible to Item or ClipperPolygon
|
||||||
PackGroup preshapes; preshapes.emplace_back();
|
// PackGroup preshapes; preshapes.emplace_back();
|
||||||
ItemGroup shapes;
|
// ItemGroup shapes;
|
||||||
preshapes.front().reserve(shapemap.size());
|
// preshapes.front().reserve(shapemap.size());
|
||||||
|
|
||||||
std::vector<ModelInstance*> shapes_ptr; shapes_ptr.reserve(toadd.size());
|
// std::vector<ModelInstance*> shapes_ptr; shapes_ptr.reserve(toadd.size());
|
||||||
IndexedPackGroup result;
|
// IndexedPackGroup result;
|
||||||
|
|
||||||
// If there is no hint about the shape, we will try to guess
|
// // If there is no hint about the shape, we will try to guess
|
||||||
BedShapeHint bedhint = bedShape(bed);
|
// BedShapeHint bedhint = bedShape(bed);
|
||||||
|
|
||||||
BoundingBox bbb(bed);
|
// BoundingBox bbb(bed);
|
||||||
|
|
||||||
// Integer ceiling the min distance from the bed perimeters
|
// // Integer ceiling the min distance from the bed perimeters
|
||||||
coord_t md = min_obj_distance - SCALED_EPSILON;
|
// coord_t md = min_obj_distance - SCALED_EPSILON;
|
||||||
md = (md % 2) ? md / 2 + 1 : md / 2;
|
// md = (md % 2) ? md / 2 + 1 : md / 2;
|
||||||
|
|
||||||
auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md,
|
// auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md,
|
||||||
ClipperLib::cInt{bbb.min(1)} - md},
|
// ClipperLib::cInt{bbb.min(1)} - md},
|
||||||
{ClipperLib::cInt{bbb.max(0)} + md,
|
// {ClipperLib::cInt{bbb.max(0)} + md,
|
||||||
ClipperLib::cInt{bbb.max(1)} + md});
|
// ClipperLib::cInt{bbb.max(1)} + md});
|
||||||
|
|
||||||
for(auto it = shapemap.begin(); it != shapemap.end(); ++it) {
|
// for(auto it = shapemap.begin(); it != shapemap.end(); ++it) {
|
||||||
// `toadd` vector contains the instance pointers which have to be
|
// // `toadd` vector contains the instance pointers which have to be
|
||||||
// considered by arrange. If `it` points to an ModelInstance, which
|
// // considered by arrange. If `it` points to an ModelInstance, which
|
||||||
// is NOT in `toadd`, add it to preshapes.
|
// // is NOT in `toadd`, add it to preshapes.
|
||||||
if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) {
|
// if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) {
|
||||||
if(it->second.isInside(binbb)) // just ignore items which are outside
|
// if(it->second.isInside(binbb)) // just ignore items which are outside
|
||||||
preshapes.front().emplace_back(std::ref(it->second));
|
// preshapes.front().emplace_back(std::ref(it->second));
|
||||||
}
|
// }
|
||||||
else {
|
// else {
|
||||||
shapes_ptr.emplace_back(it->first);
|
// shapes_ptr.emplace_back(it->first);
|
||||||
shapes.emplace_back(std::ref(it->second));
|
// shapes.emplace_back(std::ref(it->second));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
switch(bedhint.type) {
|
// switch(bedhint.type) {
|
||||||
case BedShapeType::BOX: {
|
// case BedShapeType::BOX: {
|
||||||
// Create the arranger for the box shaped bed
|
// // Create the arranger for the box shaped bed
|
||||||
result = _arrange(shapes, preshapes, shapes_ptr, binbb, min_obj_distance);
|
// result = _arrange(shapes, preshapes, shapes_ptr, binbb, min_obj_distance);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case BedShapeType::CIRCLE: {
|
// case BedShapeType::CIRCLE: {
|
||||||
auto c = bedhint.shape.circ;
|
// auto c = bedhint.shape.circ;
|
||||||
auto cc = to_lnCircle(c);
|
// auto cc = to_lnCircle(c);
|
||||||
result = _arrange(shapes, preshapes, shapes_ptr, cc, min_obj_distance);
|
// result = _arrange(shapes, preshapes, shapes_ptr, cc, min_obj_distance);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case BedShapeType::IRREGULAR:
|
// case BedShapeType::IRREGULAR:
|
||||||
case BedShapeType::WHO_KNOWS: {
|
// case BedShapeType::WHO_KNOWS: {
|
||||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
// auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||||
ClipperLib::Polygon irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
// ClipperLib::Polygon irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||||
result = _arrange(shapes, preshapes, shapes_ptr, irrbed, min_obj_distance);
|
// result = _arrange(shapes, preshapes, shapes_ptr, irrbed, min_obj_distance);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Now we go through the result which will contain the fixed and the moving
|
// // Now we go through the result which will contain the fixed and the moving
|
||||||
// polygons as well. We will have to search for our item.
|
// // polygons as well. We will have to search for our item.
|
||||||
|
|
||||||
ClipperLib::cInt stride = stride_padding(binbb.width());
|
// ClipperLib::cInt stride = stride_padding(binbb.width());
|
||||||
ClipperLib::cInt batch_offset = 0;
|
// ClipperLib::cInt batch_offset = 0;
|
||||||
|
|
||||||
for(auto& group : result) {
|
// for(auto& group : result) {
|
||||||
for(auto& r : group) if(r.first < shapes.size()) {
|
// for(auto& r : group) if(r.first < shapes.size()) {
|
||||||
Item& resultitem = r.second;
|
// Item& resultitem = r.second;
|
||||||
unsigned idx = r.first;
|
// unsigned idx = r.first;
|
||||||
auto offset = resultitem.translation();
|
// auto offset = resultitem.translation();
|
||||||
Radians rot = resultitem.rotation();
|
// Radians rot = resultitem.rotation();
|
||||||
ModelInstance *minst = shapes_ptr[idx];
|
// ModelInstance *minst = shapes_ptr[idx];
|
||||||
Vec3d foffset(offset.X*SCALING_FACTOR + batch_offset,
|
// Vec3d foffset(unscaled(offset.X + batch_offset),
|
||||||
offset.Y*SCALING_FACTOR,
|
// unscaled(offset.Y),
|
||||||
minst->get_offset()(Z));
|
// minst->get_offset()(Z));
|
||||||
|
|
||||||
// write the transformation data into the model instance
|
// // write the transformation data into the model instance
|
||||||
minst->set_rotation(Z, rot);
|
// minst->set_rotation(Z, rot);
|
||||||
minst->set_offset(foffset);
|
// minst->set_offset(foffset);
|
||||||
}
|
// }
|
||||||
batch_offset += stride;
|
// batch_offset += stride;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#ifndef MODELARRANGE_HPP
|
#ifndef MODELARRANGE_HPP
|
||||||
#define MODELARRANGE_HPP
|
#define MODELARRANGE_HPP
|
||||||
|
|
||||||
#include "Model.hpp"
|
//#include "Model.hpp"
|
||||||
|
#include "Polygon.hpp"
|
||||||
|
#include "BoundingBox.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -40,13 +42,25 @@ struct BedShapeHint {
|
||||||
|
|
||||||
BedShapeHint bedShape(const Polyline& bed);
|
BedShapeHint bedShape(const Polyline& bed);
|
||||||
|
|
||||||
struct WipeTowerInfo {
|
class ArrangeItem {
|
||||||
bool is_wipe_tower = false;
|
public:
|
||||||
Vec2d pos;
|
|
||||||
Vec2d bb_size;
|
virtual ~ArrangeItem() = default;
|
||||||
double rotation;
|
|
||||||
|
virtual void transform(Vec2d offset, double rotation_rads) = 0;
|
||||||
|
|
||||||
|
virtual Polygon silhouette() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ArrangeItems = std::vector<std::reference_wrapper<ArrangeItem>>;
|
||||||
|
|
||||||
|
//struct WipeTowerInfo {
|
||||||
|
// bool is_wipe_tower = false;
|
||||||
|
// Vec2d pos;
|
||||||
|
// Vec2d bb_size;
|
||||||
|
// double rotation;
|
||||||
|
//};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Arranges the model objects on the screen.
|
* \brief Arranges the model objects on the screen.
|
||||||
*
|
*
|
||||||
|
@ -73,22 +87,33 @@ struct WipeTowerInfo {
|
||||||
* packed. The unsigned argument is the number of items remaining to pack.
|
* packed. The unsigned argument is the number of items remaining to pack.
|
||||||
* \param stopcondition A predicate returning true if abort is needed.
|
* \param stopcondition A predicate returning true if abort is needed.
|
||||||
*/
|
*/
|
||||||
bool arrange(Model &model,
|
//bool arrange(Model &model,
|
||||||
WipeTowerInfo& wipe_tower_info,
|
// WipeTowerInfo& wipe_tower_info,
|
||||||
|
// coord_t min_obj_distance,
|
||||||
|
// const Slic3r::Polyline& bed,
|
||||||
|
// BedShapeHint bedhint,
|
||||||
|
// bool first_bin_only,
|
||||||
|
// std::function<void(unsigned)> progressind,
|
||||||
|
// std::function<bool(void)> stopcondition);
|
||||||
|
|
||||||
|
bool arrange(ArrangeItems &items,
|
||||||
coord_t min_obj_distance,
|
coord_t min_obj_distance,
|
||||||
const Slic3r::Polyline& bed,
|
|
||||||
BedShapeHint bedhint,
|
BedShapeHint bedhint,
|
||||||
bool first_bin_only,
|
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned)> progressind,
|
||||||
std::function<bool(void)> stopcondition);
|
std::function<bool(void)> stopcondition);
|
||||||
|
|
||||||
/// This will find a suitable position for a new object instance and leave the
|
/// This will find a suitable position for a new object instance and leave the
|
||||||
/// old items untouched.
|
/// old items untouched.
|
||||||
void find_new_position(const Model& model,
|
//void find_new_position(const Model& model,
|
||||||
ModelInstancePtrs instances_to_add,
|
// ModelInstancePtrs instances_to_add,
|
||||||
|
// coord_t min_obj_distance,
|
||||||
|
// const Slic3r::Polyline& bed,
|
||||||
|
// WipeTowerInfo& wti);
|
||||||
|
void find_new_position(ArrangeItems &items,
|
||||||
|
const ArrangeItems &instances_to_add,
|
||||||
coord_t min_obj_distance,
|
coord_t min_obj_distance,
|
||||||
const Slic3r::Polyline& bed,
|
BedShapeHint bedhint);
|
||||||
WipeTowerInfo& wti);
|
|
||||||
|
|
||||||
} // arr
|
} // arr
|
||||||
} // Slic3r
|
} // Slic3r
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -2400,131 +2401,185 @@ void Plater::priv::sla_optimize_rotation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() {
|
void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() {
|
||||||
|
static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1;
|
||||||
|
|
||||||
|
class ArrItemModelInstance: public arr::ArrangeItem {
|
||||||
|
ModelInstance *m_inst = nullptr;
|
||||||
|
public:
|
||||||
|
|
||||||
|
ArrItemModelInstance() = default;
|
||||||
|
ArrItemModelInstance(ModelInstance *inst) : m_inst(inst) {}
|
||||||
|
|
||||||
|
virtual void transform(Vec2d offs, double rot_rads) override {
|
||||||
|
assert(m_inst);
|
||||||
|
|
||||||
|
// write the transformation data into the model instance
|
||||||
|
m_inst->set_rotation(Z, rot_rads);
|
||||||
|
m_inst->set_offset(offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Polygon silhouette() const override {
|
||||||
|
assert(m_inst);
|
||||||
|
|
||||||
|
Vec3d rotation = m_inst->get_rotation();
|
||||||
|
rotation.z() = 0.;
|
||||||
|
Transform3d trafo_instance = Geometry::assemble_transform(
|
||||||
|
Vec3d::Zero(),
|
||||||
|
rotation,
|
||||||
|
m_inst->get_scaling_factor(),
|
||||||
|
m_inst->get_mirror());
|
||||||
|
|
||||||
|
Polygon p = m_inst->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<double>(SIMPLIFY_TOLERANCE_MM));
|
||||||
|
if (!pp.empty()) p = pp.front();
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Count all the items on the bin (all the object's instances)
|
||||||
|
auto count = std::accumulate(plater().model.objects.begin(),
|
||||||
|
plater().model.objects.end(),
|
||||||
|
size_t(0), [](size_t s, ModelObject* o)
|
||||||
|
{
|
||||||
|
return s + o->instances.size();
|
||||||
|
});
|
||||||
|
|
||||||
|
// std::vector<ArrItemInstance> items(size_t);
|
||||||
|
|
||||||
// TODO: we should decide whether to allow arrange when the search is
|
// TODO: we should decide whether to allow arrange when the search is
|
||||||
// running we should probably disable explicit slicing and background
|
// running we should probably disable explicit slicing and background
|
||||||
// processing
|
// processing
|
||||||
|
|
||||||
static const auto arrangestr = _(L("Arranging"));
|
// static const auto arrangestr = _(L("Arranging"));
|
||||||
|
|
||||||
auto &config = plater().config;
|
// auto &config = plater().config;
|
||||||
auto &view3D = plater().view3D;
|
// auto &view3D = plater().view3D;
|
||||||
auto &model = plater().model;
|
// auto &model = plater().model;
|
||||||
|
|
||||||
// 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);
|
||||||
if (plater().printer_technology == ptFFF) {
|
// if (plater().printer_technology == ptFFF) {
|
||||||
dist = PrintConfig::min_object_distance(config);
|
// dist = PrintConfig::min_object_distance(config);
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto min_obj_distance = coord_t(dist / SCALING_FACTOR);
|
// auto min_obj_distance = coord_t(dist / SCALING_FACTOR);
|
||||||
|
|
||||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>(
|
// const auto *bed_shape_opt = config->opt<ConfigOptionPoints>(
|
||||||
"bed_shape");
|
// "bed_shape");
|
||||||
|
|
||||||
assert(bed_shape_opt);
|
// assert(bed_shape_opt);
|
||||||
auto & bedpoints = bed_shape_opt->values;
|
// auto & bedpoints = bed_shape_opt->values;
|
||||||
Polyline bed;
|
// Polyline bed;
|
||||||
bed.points.reserve(bedpoints.size());
|
// bed.points.reserve(bedpoints.size());
|
||||||
for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
// for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||||
|
|
||||||
update_status(0, arrangestr);
|
// update_status(0, arrangestr);
|
||||||
|
|
||||||
arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info();
|
// arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info();
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
arr::BedShapeHint hint;
|
// arr::BedShapeHint hint;
|
||||||
|
|
||||||
// TODO: from Sasha from GUI or
|
// // TODO: from Sasha from GUI or
|
||||||
hint.type = arr::BedShapeType::WHO_KNOWS;
|
// hint.type = arr::BedShapeType::WHO_KNOWS;
|
||||||
|
|
||||||
arr::arrange(model,
|
// arr::arrange(model,
|
||||||
wti,
|
// wti,
|
||||||
min_obj_distance,
|
// min_obj_distance,
|
||||||
bed,
|
// bed,
|
||||||
hint,
|
// hint,
|
||||||
false, // create many piles not just one pile
|
// false, // create many piles not just one pile
|
||||||
[this](unsigned st) {
|
// [this](unsigned st) {
|
||||||
if (st > 0)
|
// if (st > 0)
|
||||||
update_status(count - int(st), arrangestr);
|
// update_status(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(count,
|
// update_status(count,
|
||||||
was_canceled() ? _(L("Arranging canceled."))
|
// was_canceled() ? _(L("Arranging canceled."))
|
||||||
: _(L("Arranging done.")));
|
// : _(L("Arranging done.")));
|
||||||
|
|
||||||
// it remains to move the wipe tower:
|
// // it remains to move the wipe tower:
|
||||||
view3D->get_canvas3d()->arrange_wipe_tower(wti);
|
// view3D->get_canvas3d()->arrange_wipe_tower(wti);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process()
|
void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process()
|
||||||
{
|
{
|
||||||
int obj_idx = plater().get_selected_object_idx();
|
// int obj_idx = plater().get_selected_object_idx();
|
||||||
if (obj_idx < 0) { return; }
|
// if (obj_idx < 0) { return; }
|
||||||
|
|
||||||
ModelObject *o = plater().model.objects[size_t(obj_idx)];
|
// ModelObject *o = plater().model.objects[size_t(obj_idx)];
|
||||||
|
|
||||||
auto r = sla::find_best_rotation(
|
// auto r = sla::find_best_rotation(
|
||||||
*o,
|
// *o,
|
||||||
.005f,
|
// .005f,
|
||||||
[this](unsigned s) {
|
// [this](unsigned s) {
|
||||||
if (s < 100)
|
// if (s < 100)
|
||||||
update_status(int(s),
|
// update_status(int(s),
|
||||||
_(L("Searching for optimal orientation")));
|
// _(L("Searching for optimal orientation")));
|
||||||
},
|
// },
|
||||||
[this]() { return was_canceled(); });
|
// [this]() { return was_canceled(); });
|
||||||
|
|
||||||
const auto *bed_shape_opt =
|
// const auto *bed_shape_opt =
|
||||||
plater().config->opt<ConfigOptionPoints>("bed_shape");
|
// plater().config->opt<ConfigOptionPoints>("bed_shape");
|
||||||
|
|
||||||
assert(bed_shape_opt);
|
// assert(bed_shape_opt);
|
||||||
|
|
||||||
auto & bedpoints = bed_shape_opt->values;
|
// auto & bedpoints = bed_shape_opt->values;
|
||||||
Polyline bed;
|
// Polyline bed;
|
||||||
bed.points.reserve(bedpoints.size());
|
// bed.points.reserve(bedpoints.size());
|
||||||
for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
// for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||||
|
|
||||||
double mindist = 6.0; // FIXME
|
// double mindist = 6.0; // FIXME
|
||||||
|
|
||||||
if (!was_canceled()) {
|
// if (!was_canceled()) {
|
||||||
for(ModelInstance * oi : o->instances) {
|
// for(ModelInstance * oi : o->instances) {
|
||||||
oi->set_rotation({r[X], r[Y], r[Z]});
|
// oi->set_rotation({r[X], r[Y], r[Z]});
|
||||||
|
|
||||||
auto trmatrix = oi->get_transformation().get_matrix();
|
// auto trmatrix = oi->get_transformation().get_matrix();
|
||||||
Polygon trchull = o->convex_hull_2d(trmatrix);
|
// Polygon trchull = o->convex_hull_2d(trmatrix);
|
||||||
|
|
||||||
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
// MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
||||||
double r = rotbb.angle_to_X();
|
// double r = rotbb.angle_to_X();
|
||||||
|
|
||||||
// The box should be landscape
|
// // The box should be landscape
|
||||||
if(rotbb.width() < rotbb.height()) r += PI / 2;
|
// if(rotbb.width() < rotbb.height()) r += PI / 2;
|
||||||
|
|
||||||
Vec3d rt = oi->get_rotation(); rt(Z) += r;
|
// Vec3d rt = oi->get_rotation(); rt(Z) += r;
|
||||||
|
|
||||||
oi->set_rotation(rt);
|
// oi->set_rotation(rt);
|
||||||
}
|
// }
|
||||||
|
|
||||||
arr::WipeTowerInfo wti; // useless in SLA context
|
// arr::WipeTowerInfo wti; // useless in SLA context
|
||||||
arr::find_new_position(plater().model,
|
// arr::find_new_position(plater().model,
|
||||||
o->instances,
|
// o->instances,
|
||||||
coord_t(mindist / SCALING_FACTOR),
|
// coord_t(mindist / SCALING_FACTOR),
|
||||||
bed,
|
// bed,
|
||||||
wti);
|
// wti);
|
||||||
|
|
||||||
// Correct the z offset of the object which was corrupted be
|
// // Correct the z offset of the object which was corrupted be
|
||||||
// the rotation
|
// // the rotation
|
||||||
o->ensure_on_bed();
|
// o->ensure_on_bed();
|
||||||
}
|
// }
|
||||||
|
|
||||||
update_status(100,
|
// update_status(100,
|
||||||
was_canceled() ? _(L("Orientation search canceled."))
|
// was_canceled() ? _(L("Orientation search canceled."))
|
||||||
: _(L("Orientation found.")));
|
// : _(L("Orientation found.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::split_object()
|
void Plater::priv::split_object()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue