From f4ed0d81378d4272c20f23ca70a98134079b2e2c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 26 Jun 2019 11:16:20 +0200 Subject: [PATCH 01/32] Working on `arrange selection only` feature. revert changes related to scale/unscale --- src/libslic3r/ModelArrange.cpp | 238 ++++++++++++++------------------- 1 file changed, 104 insertions(+), 134 deletions(-) diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index d3586651b..4a3730847 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -667,13 +667,12 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model, return ret; } -// Apply the calculated translations and rotations (currently disabled) to the -// Model object instances. -void applyResult( - IndexedPackGroup::value_type& group, - Coord batch_offset, - ShapeData2D& shapemap, - WipeTowerInfo& wti) +// Apply the calculated translations and rotations (currently disabled) to +// the Model object instances. +void applyResult(IndexedPackGroup::value_type &group, + ClipperLib::cInt batch_offset, + ShapeData2D & shapemap, + WipeTowerInfo & wti) { for(auto& r : group) { auto idx = r.first; // get the original item index @@ -686,15 +685,15 @@ void applyResult( // appropriately auto off = item.translation(); Radians rot = item.rotation(); - - Vec3d foff(off.X*SCALING_FACTOR + batch_offset, - off.Y*SCALING_FACTOR, + + Vec3d foff(unscaled(off.X + batch_offset) , + unscaled(off.Y), inst_ptr ? inst_ptr->get_offset()(Z) : 0.); - if (inst_ptr) { - // write the transformation data into the model instance - inst_ptr->set_rotation(Z, rot); - inst_ptr->set_offset(foff); + if (inst_ptr) { + // write the transformation data into the model instance + inst_ptr->set_rotation(Z, rot); + inst_ptr->set_offset(foff); } 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 @@ -785,6 +784,72 @@ BedShapeHint bedShape(const Polyline &bed) { static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; +template +IndexedPackGroup _arrange(std::vector> &shapes, + const BinT & bin, + coord_t minobjd, + std::function prind, + std::function stopfn) +{ + AutoArranger arranger{bin, minobjd, prind, stopfn}; + return arranger(shapes.begin(), shapes.end()); +} + +template +IndexedPackGroup _arrange(std::vector> &shapes, + const PackGroup & preshapes, + std::vector &minstances, + const BinT & bin, + coord_t minobjd) +{ + + auto binbb = sl::boundingBox(bin); + + AutoArranger arranger{bin, minobjd}; + + if(!preshapes.front().empty()) { // If there is something on the plate + arranger.preload(preshapes); + + // 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}}); + + 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); + + shit = shapes.erase(shit); + shptrit = minstances.erase(shptrit); + break; + } + } + } + + return arranger(shapes.begin(), shapes.end()); +} + +inline SLIC3R_CONSTEXPR libnest2d::Coord stride_padding(Coord w) +{ + return w + w / 5; +} + // 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(Model &model, // The model with the geometries @@ -826,44 +891,28 @@ bool arrange(Model &model, // The model with the geometries coord_t md = min_obj_distance - SCALED_EPSILON; md = (md % 2) ? md / 2 + 1 : md / 2; - auto binbb = Box({libnest2d::Coord{bbb.min(0)} - md, - libnest2d::Coord{bbb.min(1)} - md}, - {libnest2d::Coord{bbb.max(0)} + md, - libnest2d::Coord{bbb.max(1)} + md}); + auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, + ClipperLib::cInt{bbb.min(1)} - md}, + {ClipperLib::cInt{bbb.max(0)} + md, + ClipperLib::cInt{bbb.max(1)} + md}); switch(bedhint.type) { case BedShapeType::BOX: { - // Create the arranger for the box shaped bed - AutoArranger arrange(binbb, min_obj_distance, progressind, cfn); - - // Arrange and return the items with their respective indices within the - // input sequence. - result = arrange(shapes.begin(), shapes.end()); + result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); break; } case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); - - AutoArranger arrange(cc, min_obj_distance, progressind, cfn); - result = arrange(shapes.begin(), shapes.end()); + result = _arrange(shapes, cc, min_obj_distance, progressind, cfn); break; } case BedShapeType::IRREGULAR: case BedShapeType::WHO_KNOWS: { - - using P = libnest2d::PolygonImpl; - auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); - P irrbed = sl::create(std::move(ctour)); - - AutoArranger

arrange(irrbed, min_obj_distance, progressind, cfn); - - // Arrange and return the items with their respective indices within the - // input sequence. - result = arrange(shapes.begin(), shapes.end()); + ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); + result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); break; } }; @@ -873,12 +922,9 @@ bool arrange(Model &model, // The model with the geometries if(first_bin_only) { applyResult(result.front(), 0, shapemap, wti); } else { - - const auto STRIDE_PADDING = 1.2; - - Coord stride = static_cast(STRIDE_PADDING* - binbb.width()*SCALING_FACTOR); - Coord batch_offset = 0; + + ClipperLib::cInt stride = stride_padding(binbb.width()); + ClipperLib::cInt batch_offset = 0; for(auto& group : result) { applyResult(group, batch_offset, shapemap, wti); @@ -904,7 +950,7 @@ void find_new_position(const Model &model, // Get the 2D projected shapes with their 3D model instance pointers 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 PackGroup preshapes; preshapes.emplace_back(); ItemGroup shapes; @@ -922,12 +968,15 @@ void find_new_position(const Model &model, coord_t md = min_obj_distance - SCALED_EPSILON; md = (md % 2) ? md / 2 + 1 : md / 2; - auto binbb = Box({libnest2d::Coord{bbb.min(0)} - md, - libnest2d::Coord{bbb.min(1)} - md}, - {libnest2d::Coord{bbb.max(0)} + md, - libnest2d::Coord{bbb.max(1)} + md}); + auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, + ClipperLib::cInt{bbb.min(1)} - md}, + {ClipperLib::cInt{bbb.max(0)} + md, + ClipperLib::cInt{bbb.max(1)} + md}); for(auto it = shapemap.begin(); it != shapemap.end(); ++it) { + // `toadd` vector contains the instance pointers which have to be + // considered by arrange. If `it` points to an ModelInstance, which + // is NOT in `toadd`, add it to preshapes. if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) { if(it->second.isInside(binbb)) // just ignore items which are outside preshapes.front().emplace_back(std::ref(it->second)); @@ -938,101 +987,23 @@ void find_new_position(const Model &model, } } - auto try_first_to_center = [&shapes, &shapes_ptr, &binbb] - (std::function is_colliding, - std::function preload) - { - // Try to put the first item to the center, as the arranger will not - // do this for us. - auto shptrit = shapes_ptr.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(!is_colliding(itm)) { - preload(itm); - - auto offset = itm.translation(); - Radians rot = itm.rotation(); - ModelInstance *minst = *shptrit; - Vec3d foffset(offset.X*SCALING_FACTOR, - offset.Y*SCALING_FACTOR, - 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); - shptrit = shapes_ptr.erase(shptrit); - break; - } - } - }; - switch(bedhint.type) { case BedShapeType::BOX: { - // Create the arranger for the box shaped bed - AutoArranger arrange(binbb, min_obj_distance); - - if(!preshapes.front().empty()) { // If there is something on the plate - arrange.preload(preshapes); - try_first_to_center( - [&arrange](const Item& itm) {return arrange.is_colliding(itm);}, - [&arrange](Item& itm) { arrange.preload({{itm}}); } - ); - } - - // Arrange and return the items with their respective indices within the - // input sequence. - result = arrange(shapes.begin(), shapes.end()); + result = _arrange(shapes, preshapes, shapes_ptr, binbb, min_obj_distance); break; } case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); - - // Create the arranger for the box shaped bed - AutoArranger arrange(cc, min_obj_distance); - - if(!preshapes.front().empty()) { // If there is something on the plate - arrange.preload(preshapes); - try_first_to_center( - [&arrange](const Item& itm) {return arrange.is_colliding(itm);}, - [&arrange](Item& itm) { arrange.preload({{itm}}); } - ); - } - - // Arrange and return the items with their respective indices within the - // input sequence. - result = arrange(shapes.begin(), shapes.end()); + result = _arrange(shapes, preshapes, shapes_ptr, cc, min_obj_distance); break; } case BedShapeType::IRREGULAR: case BedShapeType::WHO_KNOWS: { - using P = libnest2d::PolygonImpl; - auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); - P irrbed = sl::create(std::move(ctour)); - - AutoArranger

arrange(irrbed, min_obj_distance); - - if(!preshapes.front().empty()) { // If there is something on the plate - arrange.preload(preshapes); - try_first_to_center( - [&arrange](const Item& itm) {return arrange.is_colliding(itm);}, - [&arrange](Item& itm) { arrange.preload({{itm}}); } - ); - } - - // Arrange and return the items with their respective indices within the - // input sequence. - result = arrange(shapes.begin(), shapes.end()); + ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); + result = _arrange(shapes, preshapes, shapes_ptr, irrbed, min_obj_distance); break; } }; @@ -1040,9 +1011,8 @@ void find_new_position(const Model &model, // 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. - const auto STRIDE_PADDING = 1.2; - Coord stride = Coord(STRIDE_PADDING*binbb.width()*SCALING_FACTOR); - Coord batch_offset = 0; + ClipperLib::cInt stride = stride_padding(binbb.width()); + ClipperLib::cInt batch_offset = 0; for(auto& group : result) { for(auto& r : group) if(r.first < shapes.size()) { From e1d612d05fdd98b8489929f564b3a86b6871ac46 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 26 Jun 2019 17:09:26 +0200 Subject: [PATCH 02/32] work in progress on new ModelArrange interface --- src/libslic3r/ModelArrange.cpp | 766 ++++++++++++++++----------------- src/libslic3r/ModelArrange.hpp | 53 ++- src/slic3r/GUI/Plater.cpp | 235 ++++++---- 3 files changed, 567 insertions(+), 487 deletions(-) diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 4a3730847..d50e03bf2 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -1,5 +1,5 @@ #include "ModelArrange.hpp" -#include "Model.hpp" +//#include "Model.hpp" #include "Geometry.hpp" #include "SVG.hpp" #include "MTUtils.hpp" @@ -43,87 +43,87 @@ namespace arr { using namespace libnest2d; // Only for debugging. Prints the model object vertices on stdout. -std::string toString(const Model& model, bool holes = true) { - std::stringstream ss; +//std::string toString(const Model& model, bool holes = true) { +// std::stringstream ss; - ss << "{\n"; +// ss << "{\n"; - for(auto objptr : model.objects) { - if(!objptr) continue; +// for(auto objptr : model.objects) { +// if(!objptr) continue; - auto rmesh = objptr->raw_mesh(); +// auto rmesh = objptr->raw_mesh(); - for(auto objinst : objptr->instances) { - if(!objinst) continue; +// for(auto objinst : objptr->instances) { +// if(!objinst) continue; - Slic3r::TriangleMesh tmpmesh = rmesh; - // CHECK_ME -> Is the following correct ? - tmpmesh.scale(objinst->get_scaling_factor()); - objinst->transform_mesh(&tmpmesh); - ExPolygons expolys = tmpmesh.horizontal_projection(); - for(auto& expoly_complex : expolys) { +// Slic3r::TriangleMesh tmpmesh = rmesh; +// // CHECK_ME -> Is the following correct ? +// tmpmesh.scale(objinst->get_scaling_factor()); +// objinst->transform_mesh(&tmpmesh); +// ExPolygons expolys = tmpmesh.horizontal_projection(); +// for(auto& expoly_complex : expolys) { - ExPolygons tmp = expoly_complex.simplify(scaled(1.)); - if(tmp.empty()) continue; - ExPolygon expoly = tmp.front(); - expoly.contour.make_clockwise(); - for(auto& h : expoly.holes) h.make_counter_clockwise(); +// ExPolygons tmp = expoly_complex.simplify(scaled(1.)); +// if(tmp.empty()) continue; +// ExPolygon expoly = tmp.front(); +// expoly.contour.make_clockwise(); +// for(auto& h : expoly.holes) h.make_counter_clockwise(); - ss << "\t{\n"; - ss << "\t\t{\n"; +// ss << "\t{\n"; +// ss << "\t\t{\n"; - for(auto v : expoly.contour.points) ss << "\t\t\t{" - << v(0) << ", " - << v(1) << "},\n"; - { - auto v = expoly.contour.points.front(); - ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n"; - } - ss << "\t\t},\n"; +// for(auto v : expoly.contour.points) ss << "\t\t\t{" +// << v(0) << ", " +// << v(1) << "},\n"; +// { +// auto v = expoly.contour.points.front(); +// ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n"; +// } +// ss << "\t\t},\n"; - // Holes: - ss << "\t\t{\n"; - if(holes) for(auto h : expoly.holes) { - ss << "\t\t\t{\n"; - for(auto v : h.points) ss << "\t\t\t\t{" - << v(0) << ", " - << v(1) << "},\n"; - { - auto v = h.points.front(); - ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n"; - } - ss << "\t\t\t},\n"; - } - ss << "\t\t},\n"; +// // Holes: +// ss << "\t\t{\n"; +// if(holes) for(auto h : expoly.holes) { +// ss << "\t\t\t{\n"; +// for(auto v : h.points) ss << "\t\t\t\t{" +// << v(0) << ", " +// << v(1) << "},\n"; +// { +// auto v = h.points.front(); +// ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n"; +// } +// ss << "\t\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. -void toSVG(SVG& svg, const Model& model) { - for(auto objptr : model.objects) { - if(!objptr) continue; +//void toSVG(SVG& svg, const Model& model) { +// for(auto objptr : model.objects) { +// if(!objptr) continue; - auto rmesh = objptr->raw_mesh(); +// auto rmesh = objptr->raw_mesh(); - for(auto objinst : objptr->instances) { - if(!objinst) continue; +// for(auto objinst : objptr->instances) { +// if(!objinst) continue; - Slic3r::TriangleMesh tmpmesh = rmesh; - tmpmesh.scale(objinst->get_scaling_factor()); - objinst->transform_mesh(&tmpmesh); - ExPolygons expolys = tmpmesh.horizontal_projection(); - svg.draw(expolys); - } - } -} +// Slic3r::TriangleMesh tmpmesh = rmesh; +// tmpmesh.scale(objinst->get_scaling_factor()); +// objinst->transform_mesh(&tmpmesh); +// ExPolygons expolys = tmpmesh.horizontal_projection(); +// svg.draw(expolys); +// } +// } +//} namespace bgi = boost::geometry::index; @@ -565,143 +565,143 @@ public: // A container which stores a pointer to the 3D object and its projected // 2D shape from top view. -using ShapeData2D = std::vector>; +//using ShapeData2D = std::vector>; -ShapeData2D projectModelFromTop(const Slic3r::Model &model, - const WipeTowerInfo &wti, - double tolerance) -{ - ShapeData2D ret; +//ShapeData2D projectModelFromTop(const Slic3r::Model &model, +// const WipeTowerInfo &wti, +// double tolerance) +//{ +// ShapeData2D ret; - // Count all the items on the bin (all the object's instances) - auto s = std::accumulate(model.objects.begin(), model.objects.end(), - size_t(0), [](size_t s, ModelObject* o) - { - return s + o->instances.size(); - }); +// // Count all the items on the bin (all the object's instances) +// auto s = std::accumulate(model.objects.begin(), model.objects.end(), +// size_t(0), [](size_t s, ModelObject* o) +// { +// 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) { - if (! objptr->instances.empty()) { +// // TODO export the exact 2D projection. Cannot do it as libnest2d +// // does not support concave shapes (yet). +// ClipperLib::Path clpath; - // TODO export the exact 2D projection. Cannot do it as libnest2d - // does not support concave shapes (yet). - ClipperLib::Path clpath; - - // Object instances should carry the same scaling and - // x, y rotation that is why we use the first instance. - { - ModelInstance *finst = objptr->instances.front(); - Vec3d rotation = finst->get_rotation(); - rotation.z() = 0.; - Transform3d trafo_instance = Geometry::assemble_transform( - Vec3d::Zero(), - rotation, - finst->get_scaling_factor(), - finst->get_mirror()); - Polygon p = objptr->convex_hull_2d(trafo_instance); +// // Object instances should carry the same scaling and +// // x, y rotation that is why we use the first instance. +// { +// ModelInstance *finst = objptr->instances.front(); +// Vec3d rotation = finst->get_rotation(); +// rotation.z() = 0.; +// Transform3d trafo_instance = Geometry::assemble_transform( +// Vec3d::Zero(), +// 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: - // https://github.com/prusa3d/PrusaSlicer/issues/2209 - if (p.points.empty()) continue; +// // this may happen for malformed models, see: +// // https://github.com/prusa3d/PrusaSlicer/issues/2209 +// if (p.points.empty()) continue; - if(tolerance > EPSILON) { - Polygons pp { p }; - pp = p.simplify(scaled(tolerance)); - if (!pp.empty()) p = pp.front(); - } +// if(tolerance > EPSILON) { +// Polygons pp { p }; +// pp = p.simplify(scaled(tolerance)); +// if (!pp.empty()) p = pp.front(); +// } - p.reverse(); - assert(!p.is_counter_clockwise()); - clpath = Slic3rMultiPoint_to_ClipperPath(p); - auto firstp = clpath.front(); clpath.emplace_back(firstp); - } +// p.reverse(); +// assert(!p.is_counter_clockwise()); +// clpath = Slic3rMultiPoint_to_ClipperPath(p); +// auto firstp = clpath.front(); clpath.emplace_back(firstp); +// } - Vec3d rotation0 = objptr->instances.front()->get_rotation(); - rotation0(2) = 0.; - for(ModelInstance* objinst : objptr->instances) { - ClipperLib::Polygon pn; - pn.Contour = clpath; +// Vec3d rotation0 = objptr->instances.front()->get_rotation(); +// rotation0(2) = 0.; +// for(ModelInstance* objinst : objptr->instances) { +// ClipperLib::Polygon pn; +// pn.Contour = clpath; - // Efficient conversion to item. - Item item(std::move(pn)); +// // Efficient conversion to item. +// Item item(std::move(pn)); - // Invalid geometries would throw exceptions when arranging - if(item.vertexCount() > 3) { - item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation())); - item.translation({ - scaled(objinst->get_offset(X)), - scaled(objinst->get_offset(Y)) - }); - ret.emplace_back(objinst, item); - } - } - } - } +// // Invalid geometries would throw exceptions when arranging +// if(item.vertexCount() > 3) { +// item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation())); +// item.translation({ +// scaled(objinst->get_offset(X)), +// scaled(objinst->get_offset(Y)) +// }); +// ret.emplace_back(objinst, item); +// } +// } +// } +// } - // The wipe tower is a separate case (in case there is one), let's duplicate the code - if (wti.is_wipe_tower) { - Points pts; - 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_(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.))); - Polygon p(std::move(pts)); - ClipperLib::Path clpath = Slic3rMultiPoint_to_ClipperPath(p); - ClipperLib::Polygon pn; - pn.Contour = clpath; - // Efficient conversion to item. - Item item(std::move(pn)); - item.rotation(wti.rotation), - item.translation({ - scaled(wti.pos(0)), - scaled(wti.pos(1)) - }); - ret.emplace_back(nullptr, item); - } +// // The wipe tower is a separate case (in case there is one), let's duplicate the code +// if (wti.is_wipe_tower) { +// Points pts; +// 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_(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.))); +// Polygon p(std::move(pts)); +// ClipperLib::Path clpath = Slic3rMultiPoint_to_ClipperPath(p); +// ClipperLib::Polygon pn; +// pn.Contour = clpath; +// // Efficient conversion to item. +// Item item(std::move(pn)); +// item.rotation(wti.rotation), +// item.translation({ +// scaled(wti.pos(0)), +// scaled(wti.pos(1)) +// }); +// ret.emplace_back(nullptr, item); +// } - return ret; -} +// return ret; +//} // Apply the calculated translations and rotations (currently disabled) to // the Model object instances. -void applyResult(IndexedPackGroup::value_type &group, - ClipperLib::cInt batch_offset, - ShapeData2D & shapemap, - WipeTowerInfo & wti) -{ - for(auto& r : group) { - auto idx = r.first; // get the original item index - Item& item = r.second; // get the item itself +//void applyResult(IndexedPackGroup::value_type &group, +// ClipperLib::cInt batch_offset, +// ShapeData2D & shapemap, +// WipeTowerInfo & wti) +//{ +// for(auto& r : group) { +// auto idx = r.first; // get the original item index +// Item& item = r.second; // get the item itself - // Get the model instance from the shapemap using the index - ModelInstance *inst_ptr = shapemap[idx].first; +// // Get the model instance from the shapemap using the index +// ModelInstance *inst_ptr = shapemap[idx].first; - // Get the transformation data from the item object and scale it - // appropriately - auto off = item.translation(); - Radians rot = item.rotation(); +// // Get the transformation data from the item object and scale it +// // appropriately +// auto off = item.translation(); +// Radians rot = item.rotation(); - Vec3d foff(unscaled(off.X + batch_offset) , - unscaled(off.Y), - inst_ptr ? inst_ptr->get_offset()(Z) : 0.); +// Vec3d foff(unscaled(off.X + batch_offset), +// unscaled(off.Y), +// inst_ptr ? inst_ptr->get_offset()(Z) : 0.); - if (inst_ptr) { - // write the transformation data into the model instance - inst_ptr->set_rotation(Z, rot); - inst_ptr->set_offset(foff); - } - 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 - wti.pos = Vec2d(foff(0), foff(1)); - wti.rotation = rot; - } - } -} +// if (inst_ptr) { +// // write the transformation data into the model instance +// inst_ptr->set_rotation(Z, rot); +// inst_ptr->set_offset(foff); +// } +// 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 +// wti.pos = Vec2d(foff(0), foff(1)); +// wti.rotation = rot; +// } +// } +//} // Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { @@ -784,254 +784,254 @@ BedShapeHint bedShape(const Polyline &bed) { static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; -template -IndexedPackGroup _arrange(std::vector> &shapes, - const BinT & bin, - coord_t minobjd, - std::function prind, - std::function stopfn) -{ - AutoArranger arranger{bin, minobjd, prind, stopfn}; - return arranger(shapes.begin(), shapes.end()); -} +//template +//IndexedPackGroup _arrange(std::vector> &shapes, +// const BinT & bin, +// coord_t minobjd, +// std::function prind, +// std::function stopfn) +//{ +// AutoArranger arranger{bin, minobjd, prind, stopfn}; +// return arranger(shapes.begin(), shapes.end()); +//} -template -IndexedPackGroup _arrange(std::vector> &shapes, - const PackGroup & preshapes, - std::vector &minstances, - const BinT & bin, - coord_t minobjd) -{ +//template +//IndexedPackGroup _arrange(std::vector> &shapes, +// const PackGroup & preshapes, +// std::vector &minstances, +// const BinT & bin, +// coord_t minobjd) +//{ - auto binbb = sl::boundingBox(bin); +// auto binbb = sl::boundingBox(bin); - AutoArranger arranger{bin, minobjd}; +// AutoArranger arranger{bin, minobjd}; - if(!preshapes.front().empty()) { // If there is something on the plate - arranger.preload(preshapes); +// if(!preshapes.front().empty()) { // If there is something on the plate +// arranger.preload(preshapes); - // 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}}); +// // 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}}); - auto offset = itm.translation(); - Radians rot = itm.rotation(); - ModelInstance *minst = *shptrit; +// auto offset = itm.translation(); +// Radians rot = itm.rotation(); +// ModelInstance *minst = *shptrit; - Vec3d foffset(unscaled(offset.X), - unscaled(offset.Y), - minst->get_offset()(Z)); +// 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 model instance +// minst->set_rotation(Z, rot); +// minst->set_offset(foffset); - shit = shapes.erase(shit); - shptrit = minstances.erase(shptrit); - break; - } - } - } +// shit = shapes.erase(shit); +// shptrit = minstances.erase(shptrit); +// break; +// } +// } +// } - return arranger(shapes.begin(), shapes.end()); -} +// return arranger(shapes.begin(), shapes.end()); +//} inline SLIC3R_CONSTEXPR libnest2d::Coord stride_padding(Coord w) { return w + w / 5; } -// 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(Model &model, // The model with the geometries - WipeTowerInfo& wti, // Wipe tower info - coord_t min_obj_distance, // Has to be in scaled (clipper) measure - const Polyline &bed, // The bed geometry. - BedShapeHint bedhint, // Hint about the bed geometry type. - bool first_bin_only, // What to do is not all items fit. +//// 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(Model &model, // The model with the geometries +// WipeTowerInfo& wti, // Wipe tower info +// coord_t min_obj_distance, // Has to be in scaled (clipper) measure +// const Polyline &bed, // The bed geometry. +// BedShapeHint bedhint, // Hint about the bed geometry type. +// bool first_bin_only, // What to do is not all items fit. - // Controlling callbacks. - std::function progressind, - std::function stopcondition) -{ - bool ret = true; +// // Controlling callbacks. +// std::function progressind, +// std::function stopcondition) +//{ +// bool ret = true; - // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); +// // Get the 2D projected shapes with their 3D model instance pointers +// auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); - // Copy the references for the shapes only as the arranger expects a - // sequence of objects convertible to Item or ClipperPolygon - std::vector> shapes; - shapes.reserve(shapemap.size()); - std::for_each(shapemap.begin(), shapemap.end(), - [&shapes] (ShapeData2D::value_type& it) - { - shapes.push_back(std::ref(it.second)); - }); +// // Copy the references for the shapes only as the arranger expects a +// // sequence of objects convertible to Item or ClipperPolygon +// std::vector> shapes; +// shapes.reserve(shapemap.size()); +// std::for_each(shapemap.begin(), shapemap.end(), +// [&shapes] (ShapeData2D::value_type& it) +// { +// 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(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); +// // If there is no hint about the shape, we will try to guess +// 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 - coord_t md = min_obj_distance - SCALED_EPSILON; - md = (md % 2) ? md / 2 + 1 : md / 2; +// // Integer ceiling the min distance from the bed perimeters +// coord_t md = min_obj_distance - SCALED_EPSILON; +// md = (md % 2) ? md / 2 + 1 : md / 2; - auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, - ClipperLib::cInt{bbb.min(1)} - md}, - {ClipperLib::cInt{bbb.max(0)} + md, - ClipperLib::cInt{bbb.max(1)} + md}); +// auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, +// ClipperLib::cInt{bbb.min(1)} - md}, +// {ClipperLib::cInt{bbb.max(0)} + md, +// ClipperLib::cInt{bbb.max(1)} + md}); - switch(bedhint.type) { - case BedShapeType::BOX: { - // Create the arranger for the box shaped bed - result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; - auto cc = to_lnCircle(c); - result = _arrange(shapes, cc, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::IRREGULAR: - case BedShapeType::WHO_KNOWS: { - auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); - ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); - result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); - break; - } - }; +// switch(bedhint.type) { +// case BedShapeType::BOX: { +// // Create the arranger for the box shaped bed +// result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::CIRCLE: { +// auto c = bedhint.shape.circ; +// auto cc = to_lnCircle(c); +// result = _arrange(shapes, cc, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::IRREGULAR: +// case BedShapeType::WHO_KNOWS: { +// auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); +// ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); +// result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); +// break; +// } +// }; - if(result.empty() || stopcondition()) return false; +// if(result.empty() || stopcondition()) return false; - if(first_bin_only) { - applyResult(result.front(), 0, shapemap, wti); - } else { +// if(first_bin_only) { +// applyResult(result.front(), 0, shapemap, wti); +// } else { - ClipperLib::cInt stride = stride_padding(binbb.width()); - ClipperLib::cInt batch_offset = 0; +// ClipperLib::cInt stride = stride_padding(binbb.width()); +// ClipperLib::cInt batch_offset = 0; - for(auto& group : result) { - applyResult(group, batch_offset, shapemap, wti); +// for(auto& group : result) { +// applyResult(group, batch_offset, shapemap, wti); - // 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 - // print bed - batch_offset += stride; - } - } +// // 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 +// // print bed +// 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, - ModelInstancePtrs toadd, - coord_t min_obj_distance, - const Polyline &bed, - WipeTowerInfo& wti) -{ - // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); +//void find_new_position(const Model &model, +// ModelInstancePtrs toadd, +// coord_t min_obj_distance, +// const Polyline &bed, +// WipeTowerInfo& wti) +//{ +// // Get the 2D projected shapes with their 3D model instance pointers +// auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); - // Copy the references for the shapes only, as the arranger expects a - // sequence of objects convertible to Item or ClipperPolygon - PackGroup preshapes; preshapes.emplace_back(); - ItemGroup shapes; - preshapes.front().reserve(shapemap.size()); +// // Copy the references for the shapes only, as the arranger expects a +// // sequence of objects convertible to Item or ClipperPolygon +// PackGroup preshapes; preshapes.emplace_back(); +// ItemGroup shapes; +// preshapes.front().reserve(shapemap.size()); - std::vector shapes_ptr; shapes_ptr.reserve(toadd.size()); - IndexedPackGroup result; +// std::vector shapes_ptr; shapes_ptr.reserve(toadd.size()); +// IndexedPackGroup result; - // If there is no hint about the shape, we will try to guess - BedShapeHint bedhint = bedShape(bed); +// // If there is no hint about the shape, we will try to guess +// BedShapeHint bedhint = bedShape(bed); - BoundingBox bbb(bed); +// BoundingBox bbb(bed); - // Integer ceiling the min distance from the bed perimeters - coord_t md = min_obj_distance - SCALED_EPSILON; - md = (md % 2) ? md / 2 + 1 : md / 2; +// // Integer ceiling the min distance from the bed perimeters +// coord_t md = min_obj_distance - SCALED_EPSILON; +// md = (md % 2) ? md / 2 + 1 : md / 2; - auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, - ClipperLib::cInt{bbb.min(1)} - md}, - {ClipperLib::cInt{bbb.max(0)} + md, - ClipperLib::cInt{bbb.max(1)} + md}); +// auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, +// ClipperLib::cInt{bbb.min(1)} - md}, +// {ClipperLib::cInt{bbb.max(0)} + md, +// ClipperLib::cInt{bbb.max(1)} + md}); - for(auto it = shapemap.begin(); it != shapemap.end(); ++it) { - // `toadd` vector contains the instance pointers which have to be - // considered by arrange. If `it` points to an ModelInstance, which - // is NOT in `toadd`, add it to preshapes. - if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) { - if(it->second.isInside(binbb)) // just ignore items which are outside - preshapes.front().emplace_back(std::ref(it->second)); - } - else { - shapes_ptr.emplace_back(it->first); - shapes.emplace_back(std::ref(it->second)); - } - } +// for(auto it = shapemap.begin(); it != shapemap.end(); ++it) { +// // `toadd` vector contains the instance pointers which have to be +// // considered by arrange. If `it` points to an ModelInstance, which +// // is NOT in `toadd`, add it to preshapes. +// if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) { +// if(it->second.isInside(binbb)) // just ignore items which are outside +// preshapes.front().emplace_back(std::ref(it->second)); +// } +// else { +// shapes_ptr.emplace_back(it->first); +// shapes.emplace_back(std::ref(it->second)); +// } +// } - switch(bedhint.type) { - case BedShapeType::BOX: { - // Create the arranger for the box shaped bed - result = _arrange(shapes, preshapes, shapes_ptr, binbb, min_obj_distance); - break; - } - case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; - auto cc = to_lnCircle(c); - result = _arrange(shapes, preshapes, shapes_ptr, cc, min_obj_distance); - break; - } - case BedShapeType::IRREGULAR: - case BedShapeType::WHO_KNOWS: { - auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); - ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); - result = _arrange(shapes, preshapes, shapes_ptr, irrbed, min_obj_distance); - break; - } - }; +// switch(bedhint.type) { +// case BedShapeType::BOX: { +// // Create the arranger for the box shaped bed +// result = _arrange(shapes, preshapes, shapes_ptr, binbb, min_obj_distance); +// break; +// } +// case BedShapeType::CIRCLE: { +// auto c = bedhint.shape.circ; +// auto cc = to_lnCircle(c); +// result = _arrange(shapes, preshapes, shapes_ptr, cc, min_obj_distance); +// break; +// } +// case BedShapeType::IRREGULAR: +// case BedShapeType::WHO_KNOWS: { +// auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); +// ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); +// result = _arrange(shapes, preshapes, shapes_ptr, irrbed, min_obj_distance); +// break; +// } +// }; - // 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. +// // 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. - ClipperLib::cInt stride = stride_padding(binbb.width()); - ClipperLib::cInt batch_offset = 0; +// ClipperLib::cInt stride = stride_padding(binbb.width()); +// ClipperLib::cInt batch_offset = 0; - for(auto& group : result) { - for(auto& r : group) if(r.first < shapes.size()) { - Item& resultitem = r.second; - unsigned idx = r.first; - auto offset = resultitem.translation(); - Radians rot = resultitem.rotation(); - ModelInstance *minst = shapes_ptr[idx]; - Vec3d foffset(offset.X*SCALING_FACTOR + batch_offset, - offset.Y*SCALING_FACTOR, - minst->get_offset()(Z)); +// for(auto& group : result) { +// for(auto& r : group) if(r.first < shapes.size()) { +// Item& resultitem = r.second; +// unsigned idx = r.first; +// auto offset = resultitem.translation(); +// Radians rot = resultitem.rotation(); +// ModelInstance *minst = shapes_ptr[idx]; +// Vec3d foffset(unscaled(offset.X + batch_offset), +// unscaled(offset.Y), +// minst->get_offset()(Z)); - // write the transformation data into the model instance - minst->set_rotation(Z, rot); - minst->set_offset(foffset); - } - batch_offset += stride; - } -} +// // write the transformation data into the model instance +// minst->set_rotation(Z, rot); +// minst->set_offset(foffset); +// } +// batch_offset += stride; +// } +//} } diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index b61443da0..cccf1bd57 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -1,7 +1,9 @@ #ifndef MODELARRANGE_HPP #define MODELARRANGE_HPP -#include "Model.hpp" +//#include "Model.hpp" +#include "Polygon.hpp" +#include "BoundingBox.hpp" namespace Slic3r { @@ -40,13 +42,25 @@ struct BedShapeHint { BedShapeHint bedShape(const Polyline& bed); -struct WipeTowerInfo { - bool is_wipe_tower = false; - Vec2d pos; - Vec2d bb_size; - double rotation; +class ArrangeItem { +public: + + virtual ~ArrangeItem() = default; + + virtual void transform(Vec2d offset, double rotation_rads) = 0; + + virtual Polygon silhouette() const = 0; }; +using ArrangeItems = std::vector>; + +//struct WipeTowerInfo { +// bool is_wipe_tower = false; +// Vec2d pos; +// Vec2d bb_size; +// double rotation; +//}; + /** * \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. * \param stopcondition A predicate returning true if abort is needed. */ -bool arrange(Model &model, - WipeTowerInfo& wipe_tower_info, +//bool arrange(Model &model, +// WipeTowerInfo& wipe_tower_info, +// coord_t min_obj_distance, +// const Slic3r::Polyline& bed, +// BedShapeHint bedhint, +// bool first_bin_only, +// std::function progressind, +// std::function stopcondition); + +bool arrange(ArrangeItems &items, coord_t min_obj_distance, - const Slic3r::Polyline& bed, BedShapeHint bedhint, - bool first_bin_only, std::function progressind, std::function stopcondition); /// This will find a suitable position for a new object instance and leave the /// old items untouched. -void find_new_position(const Model& model, - ModelInstancePtrs instances_to_add, +//void find_new_position(const Model& model, +// 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, - const Slic3r::Polyline& bed, - WipeTowerInfo& wti); + BedShapeHint bedhint); + } // arr } // Slic3r diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 472394d43..b4cfa3368 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -2400,131 +2401,185 @@ void Plater::priv::sla_optimize_rotation() { } 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(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 items(size_t); + // TODO: we should decide whether to allow arrange when the search is // running we should probably disable explicit slicing and background // processing - static const auto arrangestr = _(L("Arranging")); +// static const auto arrangestr = _(L("Arranging")); - auto &config = plater().config; - auto &view3D = plater().view3D; - auto &model = plater().model; +// auto &config = plater().config; +// auto &view3D = plater().view3D; +// auto &model = plater().model; - // 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); - if (plater().printer_technology == ptFFF) { - dist = PrintConfig::min_object_distance(config); - } +// // 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); +// if (plater().printer_technology == ptFFF) { +// 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( - "bed_shape"); +// const auto *bed_shape_opt = config->opt( +// "bed_shape"); - assert(bed_shape_opt); - auto & bedpoints = bed_shape_opt->values; - Polyline bed; - bed.points.reserve(bedpoints.size()); - for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); +// assert(bed_shape_opt); +// auto & bedpoints = bed_shape_opt->values; +// Polyline bed; +// bed.points.reserve(bedpoints.size()); +// 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 { - arr::BedShapeHint hint; +// try { +// arr::BedShapeHint hint; - // TODO: from Sasha from GUI or - hint.type = arr::BedShapeType::WHO_KNOWS; +// // TODO: from Sasha from GUI or +// hint.type = arr::BedShapeType::WHO_KNOWS; - arr::arrange(model, - wti, - min_obj_distance, - bed, - hint, - false, // create many piles not just one pile - [this](unsigned st) { - if (st > 0) - update_status(count - int(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.")); - } +// arr::arrange(model, +// wti, +// min_obj_distance, +// bed, +// hint, +// false, // create many piles not just one pile +// [this](unsigned st) { +// if (st > 0) +// update_status(count - int(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(count, - was_canceled() ? _(L("Arranging canceled.")) - : _(L("Arranging done."))); +// update_status(count, +// was_canceled() ? _(L("Arranging canceled.")) +// : _(L("Arranging done."))); - // it remains to move the wipe tower: - view3D->get_canvas3d()->arrange_wipe_tower(wti); +// // it remains to move the wipe tower: +// view3D->get_canvas3d()->arrange_wipe_tower(wti); } void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process() { - int obj_idx = plater().get_selected_object_idx(); - if (obj_idx < 0) { return; } +// int obj_idx = plater().get_selected_object_idx(); +// 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( - *o, - .005f, - [this](unsigned s) { - if (s < 100) - update_status(int(s), - _(L("Searching for optimal orientation"))); - }, - [this]() { return was_canceled(); }); +// auto r = sla::find_best_rotation( +// *o, +// .005f, +// [this](unsigned s) { +// if (s < 100) +// update_status(int(s), +// _(L("Searching for optimal orientation"))); +// }, +// [this]() { return was_canceled(); }); - const auto *bed_shape_opt = - plater().config->opt("bed_shape"); +// const auto *bed_shape_opt = +// plater().config->opt("bed_shape"); - assert(bed_shape_opt); +// assert(bed_shape_opt); - auto & bedpoints = bed_shape_opt->values; - Polyline bed; - bed.points.reserve(bedpoints.size()); - for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); +// auto & bedpoints = bed_shape_opt->values; +// Polyline bed; +// bed.points.reserve(bedpoints.size()); +// 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()) { - for(ModelInstance * oi : o->instances) { - oi->set_rotation({r[X], r[Y], r[Z]}); +// if (!was_canceled()) { +// for(ModelInstance * oi : o->instances) { +// oi->set_rotation({r[X], r[Y], r[Z]}); - auto trmatrix = oi->get_transformation().get_matrix(); - Polygon trchull = o->convex_hull_2d(trmatrix); +// auto trmatrix = oi->get_transformation().get_matrix(); +// Polygon trchull = o->convex_hull_2d(trmatrix); - MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); - double r = rotbb.angle_to_X(); +// MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); +// double r = rotbb.angle_to_X(); - // The box should be landscape - if(rotbb.width() < rotbb.height()) r += PI / 2; +// // The box should be landscape +// 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::find_new_position(plater().model, - o->instances, - coord_t(mindist / SCALING_FACTOR), - bed, - wti); +// arr::WipeTowerInfo wti; // useless in SLA context +// arr::find_new_position(plater().model, +// o->instances, +// coord_t(mindist / SCALING_FACTOR), +// bed, +// wti); - // Correct the z offset of the object which was corrupted be - // the rotation - o->ensure_on_bed(); - } +// // Correct the z offset of the object which was corrupted be +// // the rotation +// o->ensure_on_bed(); +// } - update_status(100, - was_canceled() ? _(L("Orientation search canceled.")) - : _(L("Orientation found."))); +// update_status(100, +// was_canceled() ? _(L("Orientation search canceled.")) +// : _(L("Orientation found."))); } void Plater::priv::split_object() From 19e6bf58dd2a020eca773f38df3ded1550a02890 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 27 Jun 2019 21:13:44 +0200 Subject: [PATCH 03/32] WIP on structuring arrange inputs --- src/libslic3r/MTUtils.hpp | 17 ++-- src/libslic3r/Model.cpp | 30 +++++++ src/libslic3r/Model.hpp | 13 ++- src/libslic3r/ModelArrange.cpp | 160 +++++++++++++++++++++++++-------- src/libslic3r/ModelArrange.hpp | 27 +++--- src/slic3r/GUI/GLCanvas3D.cpp | 37 ++++---- src/slic3r/GUI/GLCanvas3D.hpp | 33 ++++++- src/slic3r/GUI/Plater.cpp | 127 +++++++++++++------------- 8 files changed, 298 insertions(+), 146 deletions(-) diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index ce26887f2..e7f7ed3e6 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -276,8 +276,7 @@ using EigenVec = Eigen::Matrix; // Semantics are the following: // Upscaling (scaled()): only from floating point types (or Vec) to either // floating point or integer 'scaled coord' coordinates. -// Downscaling (unscaled()): from arithmetic types (or Vec) to either -// floating point only +// Downscaling (unscaled()): from arithmetic (or Vec) to floating point only // Conversion definition from unscaled to floating point scaled template> inline SLIC3R_CONSTEXPR Tout scaled(const Tin &v) SLIC3R_NOEXCEPT { - return static_cast(v / static_cast(SCALING_FACTOR)); + return Tout(v / Tin(SCALING_FACTOR)); } // Conversion definition from unscaled to integer 'scaled coord'. -// TODO: is the rounding necessary ? Here it is to show that it can be different -// but it does not have to be. Using std::round means loosing noexcept and -// constexpr modifiers +// TODO: is the rounding necessary? Here it is commented out to show that +// it can be different for integers but it does not have to be. Using +// std::round means loosing noexcept and constexpr modifiers template> inline SLIC3R_CONSTEXPR ScaledCoordOnly scaled(const Tin &v) SLIC3R_NOEXCEPT { //return static_cast(std::round(v / SCALING_FACTOR)); - return static_cast(v / static_cast(SCALING_FACTOR)); + return Tout(v / Tin(SCALING_FACTOR)); } // Conversion for Eigen vectors (N dimensional points) template> inline EigenVec, N> scaled(const EigenVec &v) { - return v.template cast() / SCALING_FACTOR; + return v.template cast() /*/ SCALING_FACTOR*/; } // Conversion from arithmetic scaled type to floating point unscaled @@ -314,7 +313,7 @@ template> inline SLIC3R_CONSTEXPR Tout unscaled(const Tin &v) SLIC3R_NOEXCEPT { - return static_cast(v * static_cast(SCALING_FACTOR)); + return Tout(v * Tout(SCALING_FACTOR)); } // Unscaling for Eigen vectors. Input base type can be arithmetic, output base diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 9d68f7141..6b88987df 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,5 +1,6 @@ #include "Model.hpp" #include "Geometry.hpp" +#include "MTUtils.hpp" #include "Format/AMF.hpp" #include "Format/OBJ.hpp" @@ -1800,6 +1801,35 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin } +Polygon 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()); + + 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 p; +} + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0fd1140f0..85a94b0fd 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -7,6 +7,7 @@ #include "Point.hpp" #include "TriangleMesh.hpp" #include "Slicing.hpp" +#include "ModelArrange.hpp" #include #include @@ -490,7 +491,7 @@ private: // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance : public ModelBase +class ModelInstance : public ModelBase, public arr::Arrangeable { public: enum EPrintVolumeState : unsigned char @@ -552,6 +553,16 @@ public: const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } bool is_printable() const { return print_volume_state == PVS_Inside; } + + virtual void set_arrange_result(Vec2d offs, double rot_rads) final + { + // write the transformation data into the model instance + set_rotation(Z, get_rotation(Z) + rot_rads); + set_offset(X, get_offset(X) + offs(X)); + set_offset(Y, get_offset(Y) + offs(Y)); + } + + virtual Polygon get_arrange_polygon() const final; protected: friend class Print; diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index d50e03bf2..1fe7552b0 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -42,6 +42,8 @@ namespace arr { using namespace libnest2d; +using Shape = ClipperLib::Polygon; + // Only for debugging. Prints the model object vertices on stdout. //std::string toString(const Model& model, bool holes = true) { // std::stringstream ss; @@ -129,9 +131,7 @@ namespace bgi = boost::geometry::index; using SpatElement = std::pair; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; -using ItemGroup = std::vector>; -template -using TPacker = typename placers::_NofitPolyPlacer; +using ItemGroup = std::vector>>; const double BIG_ITEM_TRESHOLD = 0.02; @@ -156,10 +156,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) { // at the same time, it has to provide reasonable results. std::tuple objfunc(const PointImpl& bincenter, - const TMultiShape& merged_pile, + const TMultiShape& merged_pile, const Box& pilebb, const ItemGroup& items, - const Item &item, + const _Item &item, double bin_area, double norm, // A norming factor for physical dimensions // a spatial index to quickly get neighbors of the candidate item @@ -225,7 +225,7 @@ objfunc(const PointImpl& bincenter, mp.emplace_back(item.transformedShape()); auto chull = sl::convexHull(mp); - placers::EdgeCache ec(chull); + placers::EdgeCache ec(chull); double circ = ec.circumference() / norm; double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; @@ -256,7 +256,7 @@ objfunc(const PointImpl& bincenter, for(auto& e : result) { // now get the score for the best alignment auto idx = e.second; - Item& p = items[idx]; + _Item& p = items[idx]; auto parea = p.area(); if(std::abs(1.0 - parea/item.area()) < 1e-6) { auto bb = boundingBox(p.boundingBox(), ibb); @@ -322,12 +322,12 @@ class _ArrBase { public: // Useful type shortcuts... - using Placer = TPacker; - using Selector = FirstFitSelection; + using Placer = typename placers::_NofitPolyPlacer; + using Selector = selections::_FirstFitSelection; using Packer = Nester; using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; - using Pile = TMultiShape; + using Pile = TMultiShape; protected: @@ -373,7 +373,7 @@ public: }; for(unsigned idx = 0; idx < items.size(); ++idx) { - Item& itm = items[idx]; + _Item& itm = items[idx]; if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx}); m_smallsrtree.insert({itm.boundingBox(), idx}); } @@ -382,13 +382,13 @@ public: m_pck.progressIndicator(progressind); m_pck.stopCondition(stopcond); } - - template inline IndexedPackGroup operator()(Args&&...args) { + + template inline _PackGroup operator()(Args&&...args) { m_rtree.clear(); - return m_pck.executeIndexed(std::forward(args)...); + return m_pck.execute(std::forward(args)...); } - - inline void preload(const PackGroup& pg) { + + inline void preload(const _PackGroup& pg) { m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; m_pconf.object_function = nullptr; // drop the special objectfunction m_pck.preload(pg); @@ -396,14 +396,14 @@ public: // Build the rtree for queries to work for(const ItemGroup& grp : pg) for(unsigned idx = 0; idx < grp.size(); ++idx) { - Item& itm = grp[idx]; + _Item& itm = grp[idx]; m_rtree.insert({itm.boundingBox(), idx}); } m_pck.configure(m_pconf); } - bool is_colliding(const Item& item) { + bool is_colliding(const _Item& item) { if(m_rtree.empty()) return false; std::vector result; m_rtree.query(bgi::intersects(item.boundingBox()), @@ -425,7 +425,7 @@ public: // Here we set up the actual object function that calls the common // object function for all bin shapes than does an additional inside // check for the arranged pile. - m_pconf.object_function = [this, bin] (const Item &item) { + m_pconf.object_function = [this, bin] (const _Item &item) { auto result = objfunc(bin.center(), m_merged_pile, @@ -468,7 +468,7 @@ public: _ArrBase(bin, dist, progressind, stopcond) { // As with the box, only the inside check is different. - m_pconf.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const _Item &item) { auto result = objfunc(bin.center(), m_merged_pile, @@ -483,7 +483,7 @@ public: double score = std::get<0>(result); - auto isBig = [this](const Item& itm) { + auto isBig = [this](const _Item& itm) { return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; }; @@ -512,7 +512,7 @@ public: std::function stopcond = [](){return false;}): _ArrBase(bin, dist, progressind, stopcond) { - m_pconf.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const _Item &item) { auto binbb = sl::boundingBox(bin); auto result = objfunc(binbb.center(), @@ -540,11 +540,11 @@ public: template<> class AutoArranger: public _ArrBase { public: - AutoArranger(Distance dist, std::function progressind, + 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) { + this->m_pconf.object_function = [this] (const _Item &item) { auto result = objfunc({0, 0}, m_merged_pile, @@ -782,18 +782,18 @@ BedShapeHint bedShape(const Polyline &bed) { return ret; } -static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; +//static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; -//template -//IndexedPackGroup _arrange(std::vector> &shapes, -// const BinT & bin, -// coord_t minobjd, -// std::function prind, -// std::function stopfn) -//{ -// AutoArranger arranger{bin, minobjd, prind, stopfn}; -// return arranger(shapes.begin(), shapes.end()); -//} +template +_PackGroup _arrange(std::vector &shapes, + const BinT & bin, + coord_t minobjd, + std::function prind, + std::function stopfn) +{ + AutoArranger arranger{bin, minobjd, prind, stopfn}; + return arranger(shapes.begin(), shapes.end()); +} //template //IndexedPackGroup _arrange(std::vector> &shapes, @@ -845,11 +845,99 @@ static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; // return arranger(shapes.begin(), shapes.end()); //} -inline SLIC3R_CONSTEXPR libnest2d::Coord stride_padding(Coord w) +inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) { return w + w / 5; } +bool arrange(ArrangeableRefs & arrangables, + coord_t min_obj_distance, + BedShapeHint bedhint, + std::function progressind, + std::function stopcondition) +{ + bool ret = true; + + std::vector shapes; + shapes.reserve(arrangables.size()); + size_t id = 0; + for (Arrangeable &iref : arrangables) { + Polygon p = iref.get_arrange_polygon(); + + p.reverse(); + assert(!p.is_counter_clockwise()); + + Shape clpath(/*id++,*/ Slic3rMultiPoint_to_ClipperPath(p)); + + auto firstp = clpath.Contour.front(); clpath.Contour.emplace_back(firstp); + shapes.emplace_back(std::move(clpath)); + } + + _PackGroup result; + + auto& cfn = stopcondition; + + // Integer ceiling the min distance from the bed perimeters + coord_t md = min_obj_distance - SCALED_EPSILON; + md = (md % 2) ? md / 2 + 1 : md / 2; + coord_t binwidth = 0; + + switch (bedhint.type) { + case BedShapeType::BOX: { + // Create the arranger for the box shaped bed + BoundingBox bbb = bedhint.shape.box; + + auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, + ClipperLib::cInt{bbb.min(1)} - md}, + {ClipperLib::cInt{bbb.max(0)} + md, + ClipperLib::cInt{bbb.max(1)} + md}); + + result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); + binwidth = coord_t(binbb.width()); + break; + } + case BedShapeType::CIRCLE: { + auto c = bedhint.shape.circ; + auto cc = to_lnCircle(c); + result = _arrange(shapes, cc, min_obj_distance, progressind, cfn); + binwidth = scaled(c.radius()); + break; + } + case BedShapeType::IRREGULAR: { + auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); + ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); + result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); + BoundingBox polybb(bedhint.shape.polygon); + binwidth = (polybb.max(X) - polybb.min(X)); + break; + } + case BedShapeType::WHO_KNOWS: { + result = _arrange(shapes, false, min_obj_distance, progressind, cfn); + break; + } + }; + + if(result.empty() || stopcondition()) return false; + + ClipperLib::cInt stride = stride_padding(binwidth); + ClipperLib::cInt batch_offset = 0; + + for (const auto &group : result) { + for (_Item &itm : group) { + ClipperLib::IntPoint offs = itm.translation(); +// arrangables[itm.id()].get().set_arrange_result({offs.X, offs.Y}, +// itm.rotation()); + } + + // 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 + // print bed + batch_offset += stride; + } + + return ret; +} + //// 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(Model &model, // The model with the geometries diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index cccf1bd57..77b357f4e 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -32,8 +32,8 @@ enum class BedShapeType { }; struct BedShapeHint { - BedShapeType type; - /*union*/ struct { // I know but who cares... + BedShapeType type = BedShapeType::WHO_KNOWS; + /*union*/ struct { // I know but who cares... TODO: use variant from cpp17? Circle circ; BoundingBox box; Polyline polygon; @@ -42,24 +42,17 @@ struct BedShapeHint { BedShapeHint bedShape(const Polyline& bed); -class ArrangeItem { +class Arrangeable { public: - virtual ~ArrangeItem() = default; + virtual ~Arrangeable() = default; - virtual void transform(Vec2d offset, double rotation_rads) = 0; + virtual void set_arrange_result(Vec2d offset, double rotation_rads) = 0; - virtual Polygon silhouette() const = 0; + virtual Polygon get_arrange_polygon() const = 0; }; -using ArrangeItems = std::vector>; - -//struct WipeTowerInfo { -// bool is_wipe_tower = false; -// Vec2d pos; -// Vec2d bb_size; -// double rotation; -//}; +using ArrangeableRefs = std::vector>; /** * \brief Arranges the model objects on the screen. @@ -96,7 +89,7 @@ using ArrangeItems = std::vector>; // std::function progressind, // std::function stopcondition); -bool arrange(ArrangeItems &items, +bool arrange(ArrangeableRefs &items, coord_t min_obj_distance, BedShapeHint bedhint, std::function progressind, @@ -109,8 +102,8 @@ bool arrange(ArrangeItems &items, // coord_t min_obj_distance, // const Slic3r::Polyline& bed, // WipeTowerInfo& wti); -void find_new_position(ArrangeItems &items, - const ArrangeItems &instances_to_add, +void find_new_position(ArrangeableRefs &items, + const ArrangeableRefs &instances_to_add, coord_t min_obj_distance, BedShapeHint bedhint); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index bed3b754b..bf892fe68 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3329,36 +3329,24 @@ void GLCanvas3D::update_ui_from_settings() -arr::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const +GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const { - arr::WipeTowerInfo wti; + WipeTowerInfo wti; + for (const GLVolume* vol : m_volumes.volumes) { if (vol->is_wipe_tower) { - wti.is_wipe_tower = true; - wti.pos = Vec2d(m_config->opt_float("wipe_tower_x"), + wti.m_pos = Vec2d(m_config->opt_float("wipe_tower_x"), m_config->opt_float("wipe_tower_y")); - wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); + wti.m_rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); const BoundingBoxf3& bb = vol->bounding_box; - wti.bb_size = Vec2d(bb.size()(0), bb.size()(1)); + wti.m_bb_size = Vec2d(bb.size().x(), bb.size().y()); break; } } + return wti; } - -void GLCanvas3D::arrange_wipe_tower(const arr::WipeTowerInfo& wti) const -{ - if (wti.is_wipe_tower) { - DynamicPrintConfig cfg; - cfg.opt("wipe_tower_x", true)->value = wti.pos(0); - cfg.opt("wipe_tower_y", true)->value = wti.pos(1); - cfg.opt("wipe_tower_rotation_angle", true)->value = (180./M_PI) * wti.rotation; - wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); - } -} - - Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) { float z0 = 0.0f; @@ -5751,5 +5739,16 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } +void GLCanvas3D::WipeTowerInfo::set_arrange_result(Vec2d offset, double rotation_rads) +{ + m_pos += offset; + m_rotation += rotation_rads; + DynamicPrintConfig cfg; + cfg.opt("wipe_tower_x", true)->value = m_pos(X); + cfg.opt("wipe_tower_y", true)->value = m_pos(Y); + cfg.opt("wipe_tower_rotation_angle", true)->value = (180./M_PI) * m_rotation; + wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 5a4287903..1872d2f37 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -611,9 +611,38 @@ 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 { + Vec2d m_pos = {std::nan(""), std::nan("")}; + Vec2d m_bb_size; + double m_rotation; + friend class GLCanvas3D; + public: + + inline operator bool() const + { + return std::isnan(m_pos.x()) || std::isnan(m_pos.y()); + } - arr::WipeTowerInfo get_wipe_tower_info() const; - void arrange_wipe_tower(const arr::WipeTowerInfo& wti) const; + virtual void set_arrange_result(Vec2d offset, double rotation_rads) final; + + virtual Polygon get_arrange_polygon() const final + { + Polygon p({ + {coord_t(0), coord_t(0)}, + {scaled(m_bb_size(X)), coord_t(0)}, + {scaled(m_bb_size)}, + {coord_t(0), scaled(m_bb_size(Y))}, + {coord_t(0), coord_t(0)}, + }); + + p.rotate(m_rotation); + p.translate(scaled(m_pos)); + return p; + } + }; + + WipeTowerInfo get_wipe_tower_info() const; // Returns the view ray line, in world coordinate, at the given mouse position. Linef3 mouse_ray(const Point& mouse_pos); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b4cfa3368..b4afdfe67 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1424,22 +1424,25 @@ struct Plater::priv priv * m_plater; class ArrangeJob : public Job - { - int count = 0; - + { + int m_count = 0; + GLCanvas3D::WipeTowerInfo m_wti; protected: + void prepare() override { - count = 0; + m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); + m_count = bool(m_wti); + for (auto obj : plater().model.objects) - count += int(obj->instances.size()); + m_count += int(obj->instances.size()); } public: //using Job::Job; ArrangeJob(priv * pltr): Job(pltr) {} - int status_range() const override { return count; } - void set_count(int c) { count = c; } + int status_range() const override { return m_count; } + void set_count(int c) { m_count = c; } void process() override; } arrange_job/*{m_plater}*/; @@ -1525,6 +1528,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; 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); @@ -2171,9 +2175,9 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode auto& bedpoints = bed_shape_opt->values; Polyline bed; bed.points.reserve(bedpoints.size()); for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); - - arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info(); - + + std::pair wti = view3D->get_canvas3d()->get_wipe_tower_info(); + arr::find_new_position(model, new_instances, min_obj_distance, bed, wti); // it remains to move the wipe tower: @@ -2400,61 +2404,60 @@ void Plater::priv::sla_optimize_rotation() { m_ui_jobs.start(Jobs::Rotoptimize); } -void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { - static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; +arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { + arr::BedShapeHint bedshape; - 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); + const auto *bed_shape_opt = config->opt("bed_shape"); + assert(bed_shape_opt); + + if (bed_shape_opt) { + 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); + } + + return bedshape; +} - assert(!p.points.empty()); +void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { + static const auto arrangestr = _(L("Arranging")); + + arr::ArrangeableRefs arrangeinput; arrangeinput.reserve(m_count); + for(ModelObject *mo : plater().model.objects) + for(ModelInstance *minst : mo->instances) + arrangeinput.emplace_back(std::ref(*minst)); + + // 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); + if (plater().printer_technology == ptFFF) { + dist = PrintConfig::min_object_distance(plater().config); + } + + coord_t min_obj_distance = scaled(dist); + + arr::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(); }); + } catch (std::exception & /*e*/) { + GUI::show_error(plater().q, + _(L("Could not arrange model objects! " + "Some geometries may be invalid."))); + } + + update_status(m_count, + was_canceled() ? _(L("Arranging canceled.")) + : _(L("Arranging done."))); - // 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 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 items(size_t); - // TODO: we should decide whether to allow arrange when the search is // running we should probably disable explicit slicing and background // processing From 299e4f74c7af3762be54609b8818c14ca81d29a7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 28 Jun 2019 17:03:50 +0200 Subject: [PATCH 04/32] Arranging with new structure. --- src/libnest2d/include/libnest2d.h | 2 - src/libnest2d/include/libnest2d/libnest2d.hpp | 158 ++----- src/libnest2d/tests/test.cpp | 4 +- src/libslic3r/MTUtils.hpp | 2 +- src/libslic3r/Model.cpp | 6 +- src/libslic3r/Model.hpp | 18 +- src/libslic3r/ModelArrange.cpp | 425 +++++------------- src/libslic3r/ModelArrange.hpp | 21 +- src/slic3r/GUI/GLCanvas3D.cpp | 6 +- src/slic3r/GUI/GLCanvas3D.hpp | 12 +- src/slic3r/GUI/Plater.cpp | 14 +- 11 files changed, 189 insertions(+), 479 deletions(-) diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h index a6eb36a4b..f1d2506f4 100644 --- a/src/libnest2d/include/libnest2d.h +++ b/src/libnest2d/include/libnest2d.h @@ -30,9 +30,7 @@ using Circle = _Circle; using Item = _Item; using Rectangle = _Rectangle; - using PackGroup = _PackGroup; -using IndexedPackGroup = _IndexedPackGroup; using FillerSelection = selections::_FillerSelection; using FirstFitSelection = selections::_FirstFitSelection; diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 5d74aa3d9..b8a542752 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -65,6 +65,8 @@ class _Item { Box bb; bool valid; BBCache(): valid(false) {} } bb_cache_; + + std::function applyfn_; public: @@ -121,8 +123,26 @@ public: inline _Item(TContour&& contour, THolesContainer&& holes): - sh_(sl::create(std::move(contour), - std::move(holes))) {} + sh_(sl::create(std::move(contour), std::move(holes))) {} + + template + _Item(std::function applyfn, Args &&... args): + _Item(std::forward(args)...) + { + applyfn_ = std::move(applyfn); + } + + // Call the apply callback set in constructor. Within the callback, the + // original caller can apply the stored transformation to the original + // objects inteded for nesting. It might not be the shape handed over + // to _Item (e.g. arranging 3D shapes based on 2D silhouette or the + // client uses a simplified or processed polygon for nesting) + // This callback, if present, will be called for each item after the nesting + // is finished. + inline void callApplyFunction(unsigned binidx) const + { + if (applyfn_) applyfn_(*this, binidx); + } /** * @brief Convert the polygon to string representation. The format depends @@ -492,24 +512,6 @@ template using _ItemGroup = std::vector<_ItemRef>; template using _PackGroup = std::vector>>; -/** - * \brief A list of packed (index, item) pair vectors. Each vector represents a - * bin. - * - * The index is points to the position of the item in the original input - * sequence. This way the caller can use the items as a transformation data - * carrier and transform the original objects manually. - */ -template -using _IndexedPackGroup = std::vector< - std::vector< - std::pair< - unsigned, - _ItemRef - > - > - >; - template struct ConstItemRange { Iterator from; @@ -768,13 +770,9 @@ public: using BinType = typename TPlacer::BinType; using PlacementConfig = typename TPlacer::Config; using SelectionConfig = typename TSel::Config; - using Unit = TCoord>; - - using IndexedPackGroup = _IndexedPackGroup; using PackGroup = _PackGroup; using ResultType = PackGroup; - using ResultTypeIndexed = IndexedPackGroup; private: BinType bin_; @@ -786,6 +784,7 @@ private: using TSItem = remove_cvref_t; std::vector item_cache_; + StopCondition stopfn_; public: @@ -814,11 +813,13 @@ public: void configure(const PlacementConfig& pconf) { pconfig_ = pconf; } void configure(const SelectionConfig& sconf) { selector_.configure(sconf); } - void configure(const PlacementConfig& pconf, const SelectionConfig& sconf) { + void configure(const PlacementConfig& pconf, const SelectionConfig& sconf) + { pconfig_ = pconf; selector_.configure(sconf); } - void configure(const SelectionConfig& sconf, const PlacementConfig& pconf) { + void configure(const SelectionConfig& sconf, const PlacementConfig& pconf) + { pconfig_ = pconf; selector_.configure(sconf); } @@ -836,26 +837,6 @@ public: return _execute(from, to); } - /** - * A version of the arrange method returning an IndexedPackGroup with - * the item indexes into the original input sequence. - * - * Takes a little longer to collect the indices. Scales linearly with the - * input sequence size. - */ - template - inline IndexedPackGroup executeIndexed(TIterator from, TIterator to) - { - return _executeIndexed(from, to); - } - - /// Shorthand to normal arrange method. - template - inline PackGroup operator() (TIterator from, TIterator to) - { - return _execute(from, to); - } - /// Set a progress indicator function object for the selector. inline Nester& progressIndicator(ProgressFunction func) { @@ -865,7 +846,7 @@ public: /// Set a predicate to tell when to abort nesting. inline Nester& stopCondition(StopCondition fn) { - selector_.stopCondition(fn); return *this; + stopfn_ = fn; selector_.stopCondition(fn); return *this; } inline const PackGroup& lastResult() const @@ -878,16 +859,6 @@ public: selector_.preload(pgrp); } - inline void preload(const IndexedPackGroup& ipgrp) - { - PackGroup pgrp; pgrp.reserve(ipgrp.size()); - for(auto& ig : ipgrp) { - pgrp.emplace_back(); pgrp.back().reserve(ig.size()); - for(auto& r : ig) pgrp.back().emplace_back(r.second); - } - preload(pgrp); - } - private: template, - - // This function will be used only if the iterators are pointing to - // a type compatible with the libnest2d::_Item template. - // This way we can use references to input elements as they will - // have to exist for the lifetime of this call. - class T = enable_if_t< std::is_convertible::value, IT> - > - inline IndexedPackGroup _executeIndexed(TIterator from, - TIterator to, - bool = false) - { - __execute(from, to); - return createIndexedPackGroup(from, to, selector_); - } - - template, - class T = enable_if_t::value, IT> - > - inline IndexedPackGroup _executeIndexed(TIterator from, - TIterator to, - int = false) - { - item_cache_ = {from, to}; - __execute(item_cache_.begin(), item_cache_.end()); - return createIndexedPackGroup(from, to, selector_); - } - - template - static IndexedPackGroup createIndexedPackGroup(TIterator from, - TIterator to, - TSel& selector) - { - IndexedPackGroup pg; - pg.reserve(selector.getResult().size()); - - const PackGroup& pckgrp = selector.getResult(); - - for(size_t i = 0; i < pckgrp.size(); i++) { - auto items = pckgrp[i]; - pg.emplace_back(); - pg[i].reserve(items.size()); - - for(Item& itemA : items) { - auto it = from; - unsigned idx = 0; - while(it != to) { - Item& itemB = *it; - if(&itemB == &itemA) break; - it++; idx++; - } - pg[i].emplace_back(idx, itemA); - } - } - - return pg; - } - template inline void __execute(TIter from, TIter to) { if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { @@ -985,10 +896,19 @@ private: selector_.template packItems( from, to, bin_, pconfig_); - - if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { + + if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { item.removeOffset(); }); + + if(stopfn_ && !stopfn_()) { // Ignore results if nesting was stopped. + const PackGroup& bins = lastResult(); + unsigned binidx = 0; + for(auto& bin : bins) { + for(const Item& itm : bin) itm.callApplyFunction(binidx); + ++binidx; + } + } } }; diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index 2f2b9beb5..72a600dbb 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -366,7 +366,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) Nester arrange(Box(210, 250)); - auto groups = arrange(rects.begin(), rects.end()); + auto groups = arrange.execute(rects.begin(), rects.end()); ASSERT_EQ(groups.size(), 1u); ASSERT_EQ(groups[0].size(), rects.size()); @@ -420,7 +420,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) Nester arrange(Box(210, 250), min_obj_distance); - auto groups = arrange(rects.begin(), rects.end()); + auto groups = arrange.execute(rects.begin(), rects.end()); ASSERT_EQ(groups.size(), 1u); ASSERT_EQ(groups[0].size(), rects.size()); diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index e7f7ed3e6..01f0095bf 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -303,7 +303,7 @@ inline SLIC3R_CONSTEXPR ScaledCoordOnly scaled(const Tin &v) SLIC3R_NOEXCE template> inline EigenVec, N> scaled(const EigenVec &v) { - return v.template cast() /*/ SCALING_FACTOR*/; + return (v / SCALING_FACTOR).template cast(); } // Conversion from arithmetic scaled type to floating point unscaled diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 6b88987df..18f3f2f5e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1801,7 +1801,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin } -Polygon ModelInstance::get_arrange_polygon() const +std::tuple ModelInstance::get_arrange_polygon() const { static const double SIMPLIFY_TOLERANCE_MM = 0.1; @@ -1827,7 +1827,9 @@ Polygon ModelInstance::get_arrange_polygon() const pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); if (!pp.empty()) p = pp.front(); - return p; + return std::make_tuple(p, Vec2crd{scaled(get_offset(X)), + scaled(get_offset(Y))}, + get_rotation(Z)); } // Test whether the two models contain the same number of ModelObjects with the same set of IDs diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 85a94b0fd..c02280827 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -554,15 +554,21 @@ public: bool is_printable() const { return print_volume_state == PVS_Inside; } - virtual void set_arrange_result(Vec2d offs, double rot_rads) final + // ///////////////////////////////////////////////////////////////////////// + // Implement arr::Arrangeable interface + // ///////////////////////////////////////////////////////////////////////// + + // Getting the input polygon for arrange + virtual std::tuple get_arrange_polygon() const final; + + // Apply the arrange result on the ModelInstance + virtual void apply_arrange_result(Vec2d offs, double rot_rads) final { // write the transformation data into the model instance - set_rotation(Z, get_rotation(Z) + rot_rads); - set_offset(X, get_offset(X) + offs(X)); - set_offset(Y, get_offset(Y) + offs(Y)); + set_rotation(Z, rot_rads); + set_offset(X, offs(X)); + set_offset(Y, offs(Y)); } - - virtual Polygon get_arrange_polygon() const final; protected: friend class Print; diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 1fe7552b0..885979648 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -4,7 +4,10 @@ #include "SVG.hpp" #include "MTUtils.hpp" -#include +#include +#include +#include +#include #include #include @@ -18,14 +21,20 @@ namespace libnest2d { using LargeInt = __int128; #else using LargeInt = boost::multiprecision::int128_t; -template<> struct _NumTag { using Type = ScalarTag; }; +template<> struct _NumTag +{ + using Type = ScalarTag; +}; #endif -template struct _NumTag> { using Type = RationalTag; }; + +template struct _NumTag> +{ + using Type = RationalTag; +}; namespace nfp { -template -struct NfpImpl +template struct NfpImpl { NfpResult operator()(const S &sh, const S &other) { @@ -33,16 +42,22 @@ struct NfpImpl } }; -} -} +} // namespace nfp +} // namespace libnest2d namespace Slic3r { namespace arr { using namespace libnest2d; +namespace clppr = ClipperLib; -using Shape = ClipperLib::Polygon; +using Item = _Item; +using Box = _Box; +using Circle = _Circle; +using Segment = _Segment; +using MultiPolygon = TMultiShape; +using PackGroup = _PackGroup; // Only for debugging. Prints the model object vertices on stdout. //std::string toString(const Model& model, bool holes = true) { @@ -131,7 +146,7 @@ namespace bgi = boost::geometry::index; using SpatElement = std::pair; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; -using ItemGroup = std::vector>>; +using ItemGroup = std::vector>; const double BIG_ITEM_TRESHOLD = 0.02; @@ -156,10 +171,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) { // at the same time, it has to provide reasonable results. std::tuple objfunc(const PointImpl& bincenter, - const TMultiShape& merged_pile, + const MultiPolygon& merged_pile, const Box& pilebb, const ItemGroup& items, - const _Item &item, + const Item &item, double bin_area, double norm, // A norming factor for physical dimensions // a spatial index to quickly get neighbors of the candidate item @@ -224,8 +239,8 @@ objfunc(const PointImpl& bincenter, auto mp = merged_pile; mp.emplace_back(item.transformedShape()); auto chull = sl::convexHull(mp); - - placers::EdgeCache ec(chull); + + placers::EdgeCache ec(chull); double circ = ec.circumference() / norm; double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; @@ -256,7 +271,7 @@ objfunc(const PointImpl& bincenter, for(auto& e : result) { // now get the score for the best alignment auto idx = e.second; - _Item& p = items[idx]; + Item& p = items[idx]; auto parea = p.area(); if(std::abs(1.0 - parea/item.area()) < 1e-6) { auto bb = boundingBox(p.boundingBox(), ibb); @@ -322,12 +337,11 @@ class _ArrBase { public: // Useful type shortcuts... - using Placer = typename placers::_NofitPolyPlacer; - using Selector = selections::_FirstFitSelection; + using Placer = typename placers::_NofitPolyPlacer; + using Selector = selections::_FirstFitSelection; using Packer = Nester; using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; - using Pile = TMultiShape; protected: @@ -337,7 +351,7 @@ protected: SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_smallsrtree; // spatial index for only the smaller items double m_norm; // A coefficient to scale distances - Pile m_merged_pile; // The already merged pile (vector of items) + MultiPolygon m_merged_pile; // The already merged pile (vector of items) Box m_pilebb; // The bounding box of the merged pile. ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_items; // The items to be packed @@ -354,7 +368,7 @@ public: // Set up a callback that is called just before arranging starts // This functionality is provided by the Nester class (m_pack). m_pconf.before_packing = - [this](const Pile& merged_pile, // merged pile + [this](const MultiPolygon& merged_pile, // merged pile const ItemGroup& items, // packed items const ItemGroup& remaining) // future items to be packed { @@ -373,7 +387,7 @@ public: }; for(unsigned idx = 0; idx < items.size(); ++idx) { - _Item& itm = items[idx]; + Item& itm = items[idx]; if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx}); m_smallsrtree.insert({itm.boundingBox(), idx}); } @@ -383,12 +397,12 @@ public: m_pck.stopCondition(stopcond); } - template inline _PackGroup operator()(Args&&...args) { + template inline PackGroup operator()(Args&&...args) { m_rtree.clear(); return m_pck.execute(std::forward(args)...); } - inline void preload(const _PackGroup& pg) { + inline void preload(const PackGroup& pg) { m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; m_pconf.object_function = nullptr; // drop the special objectfunction m_pck.preload(pg); @@ -396,14 +410,14 @@ public: // Build the rtree for queries to work for(const ItemGroup& grp : pg) for(unsigned idx = 0; idx < grp.size(); ++idx) { - _Item& itm = grp[idx]; + Item& itm = grp[idx]; m_rtree.insert({itm.boundingBox(), idx}); } m_pck.configure(m_pconf); } - bool is_colliding(const _Item& item) { + bool is_colliding(const Item& item) { if(m_rtree.empty()) return false; std::vector result; m_rtree.query(bgi::intersects(item.boundingBox()), @@ -425,7 +439,7 @@ public: // Here we set up the actual object function that calls the common // object function for all bin shapes than does an additional inside // check for the arranged pile. - m_pconf.object_function = [this, bin] (const _Item &item) { + m_pconf.object_function = [this, bin] (const Item &item) { auto result = objfunc(bin.center(), m_merged_pile, @@ -452,23 +466,21 @@ public: } }; -using lnCircle = libnest2d::_Circle; - -inline lnCircle to_lnCircle(const Circle& circ) { - return lnCircle({circ.center()(0), circ.center()(1)}, circ.radius()); +inline Circle to_lnCircle(const CircleBed& circ) { + return Circle({circ.center()(0), circ.center()(1)}, circ.radius()); } // Arranger specialization for circle shaped bin. -template<> class AutoArranger: public _ArrBase { +template<> class AutoArranger: public _ArrBase { public: - AutoArranger(const lnCircle& bin, Distance dist, + AutoArranger(const Circle& bin, Distance dist, std::function progressind = [](unsigned){}, std::function stopcond = [](){return false;}): - _ArrBase(bin, dist, progressind, stopcond) { + _ArrBase(bin, dist, progressind, stopcond) { // As with the box, only the inside check is different. - m_pconf.object_function = [this, &bin] (const _Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto result = objfunc(bin.center(), m_merged_pile, @@ -483,7 +495,7 @@ public: double score = std::get<0>(result); - auto isBig = [this](const _Item& itm) { + auto isBig = [this](const Item& itm) { return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; }; @@ -512,7 +524,7 @@ public: std::function stopcond = [](){return false;}): _ArrBase(bin, dist, progressind, stopcond) { - m_pconf.object_function = [this, &bin] (const _Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto binbb = sl::boundingBox(bin); auto result = objfunc(binbb.center(), @@ -544,7 +556,7 @@ public: std::function stopcond): _ArrBase(Box(0, 0), dist, progressind, stopcond) { - this->m_pconf.object_function = [this] (const _Item &item) { + this->m_pconf.object_function = [this] (const Item &item) { auto result = objfunc({0, 0}, m_merged_pile, @@ -563,152 +575,12 @@ public: } }; -// A container which stores a pointer to the 3D object and its projected -// 2D shape from top view. -//using ShapeData2D = std::vector>; - -//ShapeData2D projectModelFromTop(const Slic3r::Model &model, -// const WipeTowerInfo &wti, -// double tolerance) -//{ -// ShapeData2D ret; - -// // Count all the items on the bin (all the object's instances) -// auto s = std::accumulate(model.objects.begin(), model.objects.end(), -// size_t(0), [](size_t s, ModelObject* o) -// { -// return s + o->instances.size(); -// }); - -// ret.reserve(s); - -// for(ModelObject* objptr : model.objects) { -// if (! objptr->instances.empty()) { - -// // TODO export the exact 2D projection. Cannot do it as libnest2d -// // does not support concave shapes (yet). -// ClipperLib::Path clpath; - -// // Object instances should carry the same scaling and -// // x, y rotation that is why we use the first instance. -// { -// ModelInstance *finst = objptr->instances.front(); -// Vec3d rotation = finst->get_rotation(); -// rotation.z() = 0.; -// Transform3d trafo_instance = Geometry::assemble_transform( -// Vec3d::Zero(), -// rotation, -// finst->get_scaling_factor(), -// finst->get_mirror()); -// Polygon p = objptr->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()) continue; - -// if(tolerance > EPSILON) { -// Polygons pp { p }; -// pp = p.simplify(scaled(tolerance)); -// if (!pp.empty()) p = pp.front(); -// } - -// p.reverse(); -// assert(!p.is_counter_clockwise()); -// clpath = Slic3rMultiPoint_to_ClipperPath(p); -// auto firstp = clpath.front(); clpath.emplace_back(firstp); -// } - -// Vec3d rotation0 = objptr->instances.front()->get_rotation(); -// rotation0(2) = 0.; -// for(ModelInstance* objinst : objptr->instances) { -// ClipperLib::Polygon pn; -// pn.Contour = clpath; - -// // Efficient conversion to item. -// Item item(std::move(pn)); - -// // Invalid geometries would throw exceptions when arranging -// if(item.vertexCount() > 3) { -// item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation())); -// item.translation({ -// scaled(objinst->get_offset(X)), -// scaled(objinst->get_offset(Y)) -// }); -// ret.emplace_back(objinst, item); -// } -// } -// } -// } - -// // The wipe tower is a separate case (in case there is one), let's duplicate the code -// if (wti.is_wipe_tower) { -// Points pts; -// 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_(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.))); -// Polygon p(std::move(pts)); -// ClipperLib::Path clpath = Slic3rMultiPoint_to_ClipperPath(p); -// ClipperLib::Polygon pn; -// pn.Contour = clpath; -// // Efficient conversion to item. -// Item item(std::move(pn)); -// item.rotation(wti.rotation), -// item.translation({ -// scaled(wti.pos(0)), -// scaled(wti.pos(1)) -// }); -// ret.emplace_back(nullptr, item); -// } - -// return ret; -//} - -// Apply the calculated translations and rotations (currently disabled) to -// the Model object instances. -//void applyResult(IndexedPackGroup::value_type &group, -// ClipperLib::cInt batch_offset, -// ShapeData2D & shapemap, -// WipeTowerInfo & wti) -//{ -// for(auto& r : group) { -// auto idx = r.first; // get the original item index -// Item& item = r.second; // get the item itself - -// // Get the model instance from the shapemap using the index -// ModelInstance *inst_ptr = shapemap[idx].first; - -// // Get the transformation data from the item object and scale it -// // appropriately -// auto off = item.translation(); -// Radians rot = item.rotation(); - -// Vec3d foff(unscaled(off.X + batch_offset), -// unscaled(off.Y), -// inst_ptr ? inst_ptr->get_offset()(Z) : 0.); - -// if (inst_ptr) { -// // write the transformation data into the model instance -// inst_ptr->set_rotation(Z, rot); -// inst_ptr->set_offset(foff); -// } -// 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 -// wti.pos = Vec2d(foff(0), foff(1)); -// wti.rotation = rot; -// } -// } -//} - // Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { BedShapeHint ret; - auto x = [](const Point& p) { return p(0); }; - auto y = [](const Point& p) { return p(1); }; + auto x = [](const Point& p) { return p(X); }; + auto y = [](const Point& p) { return p(Y); }; auto width = [x](const BoundingBox& box) { return x(box.max) - x(box.min); @@ -721,7 +593,7 @@ BedShapeHint bedShape(const Polyline &bed) { auto area = [&width, &height](const BoundingBox& box) { double w = width(box); double h = height(box); - return w*h; + return w * h; }; auto poly_area = [](Polyline p) { @@ -752,11 +624,11 @@ BedShapeHint bedShape(const Polyline &bed) { avg_dist /= vertex_distances.size(); - Circle ret(center, avg_dist); + CircleBed ret(center, avg_dist); for(auto el : vertex_distances) { if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) { - ret = Circle(); + ret = CircleBed(); break; } } @@ -785,14 +657,14 @@ BedShapeHint bedShape(const Polyline &bed) { //static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; template -_PackGroup _arrange(std::vector &shapes, - const BinT & bin, - coord_t minobjd, - std::function prind, - std::function stopfn) +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(shapes.begin(), shapes.end()); + return arranger(items.begin(), items.end()); } //template @@ -850,185 +722,94 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) return w + w / 5; } -bool arrange(ArrangeableRefs & arrangables, +//// 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, coord_t min_obj_distance, BedShapeHint bedhint, std::function progressind, std::function stopcondition) { bool ret = true; + namespace clppr = ClipperLib; - std::vector shapes; - shapes.reserve(arrangables.size()); - size_t id = 0; - for (Arrangeable &iref : arrangables) { - Polygon p = iref.get_arrange_polygon(); + std::vector items; + items.reserve(arrangables.size()); + coord_t binwidth = 0; + + for (Arrangeable *arrangeable : arrangables) { + assert(arrangeable); - p.reverse(); - assert(!p.is_counter_clockwise()); + auto arrangeitem = arrangeable->get_arrange_polygon(); - Shape clpath(/*id++,*/ Slic3rMultiPoint_to_ClipperPath(p)); + Polygon& p = std::get<0>(arrangeitem); + const Vec2crd& offs = std::get<1>(arrangeitem); + double rotation = std::get<2>(arrangeitem); - auto firstp = clpath.Contour.front(); clpath.Contour.emplace_back(firstp); - shapes.emplace_back(std::move(clpath)); + if (p.is_counter_clockwise()) p.reverse(); + + clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); + + auto firstp = clpath.Contour.front(); + clpath.Contour.emplace_back(firstp); + + items.emplace_back( + // callback called by arrange to apply the result on the arrangeable + [arrangeable, &binwidth](const Item &itm, unsigned binidx) { + clppr::cInt stride = binidx * stride_padding(binwidth); + + clppr::IntPoint offs = itm.translation(); + arrangeable->apply_arrange_result({unscaled(offs.X + stride), + unscaled(offs.Y)}, + itm.rotation()); + }, + std::move(clpath)); + items.front().rotation(rotation); + items.front().translation({offs.x(), offs.y()}); } - _PackGroup result; - - auto& cfn = stopcondition; - // Integer ceiling the min distance from the bed perimeters coord_t md = min_obj_distance - SCALED_EPSILON; md = (md % 2) ? md / 2 + 1 : md / 2; - coord_t binwidth = 0; switch (bedhint.type) { case BedShapeType::BOX: { // Create the arranger for the box shaped bed BoundingBox bbb = bedhint.shape.box; - - auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, - ClipperLib::cInt{bbb.min(1)} - md}, - {ClipperLib::cInt{bbb.max(0)} + md, - ClipperLib::cInt{bbb.max(1)} + md}); - - result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); + 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()); + + _arrange(items, binbb, min_obj_distance, progressind, stopcondition); break; } case BedShapeType::CIRCLE: { auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); - result = _arrange(shapes, cc, min_obj_distance, progressind, cfn); binwidth = scaled(c.radius()); + _arrange(items, cc, min_obj_distance, progressind, stopcondition); break; } case BedShapeType::IRREGULAR: { auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); - ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); - result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); + auto irrbed = sl::create(std::move(ctour)); BoundingBox polybb(bedhint.shape.polygon); binwidth = (polybb.max(X) - polybb.min(X)); + _arrange(items, irrbed, min_obj_distance, progressind, stopcondition); break; } case BedShapeType::WHO_KNOWS: { - result = _arrange(shapes, false, min_obj_distance, progressind, cfn); + _arrange(items, false, min_obj_distance, progressind, stopcondition); break; } }; - if(result.empty() || stopcondition()) return false; - - ClipperLib::cInt stride = stride_padding(binwidth); - ClipperLib::cInt batch_offset = 0; - - for (const auto &group : result) { - for (_Item &itm : group) { - ClipperLib::IntPoint offs = itm.translation(); -// arrangables[itm.id()].get().set_arrange_result({offs.X, offs.Y}, -// itm.rotation()); - } - - // 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 - // print bed - batch_offset += stride; - } + if(stopcondition()) return false; return ret; } -//// 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(Model &model, // The model with the geometries -// WipeTowerInfo& wti, // Wipe tower info -// coord_t min_obj_distance, // Has to be in scaled (clipper) measure -// const Polyline &bed, // The bed geometry. -// BedShapeHint bedhint, // Hint about the bed geometry type. -// bool first_bin_only, // What to do is not all items fit. - -// // Controlling callbacks. -// std::function progressind, -// std::function stopcondition) -//{ -// bool ret = true; - -// // Get the 2D projected shapes with their 3D model instance pointers -// auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); - -// // Copy the references for the shapes only as the arranger expects a -// // sequence of objects convertible to Item or ClipperPolygon -// std::vector> shapes; -// shapes.reserve(shapemap.size()); -// std::for_each(shapemap.begin(), shapemap.end(), -// [&shapes] (ShapeData2D::value_type& it) -// { -// shapes.push_back(std::ref(it.second)); -// }); - -// IndexedPackGroup result; - -// // If there is no hint about the shape, we will try to guess -// if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); - -// BoundingBox bbb(bed); - -// auto& cfn = stopcondition; - -// // Integer ceiling the min distance from the bed perimeters -// coord_t md = min_obj_distance - SCALED_EPSILON; -// md = (md % 2) ? md / 2 + 1 : md / 2; - -// auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, -// ClipperLib::cInt{bbb.min(1)} - md}, -// {ClipperLib::cInt{bbb.max(0)} + md, -// ClipperLib::cInt{bbb.max(1)} + md}); - -// switch(bedhint.type) { -// case BedShapeType::BOX: { -// // Create the arranger for the box shaped bed -// result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); -// break; -// } -// case BedShapeType::CIRCLE: { -// auto c = bedhint.shape.circ; -// auto cc = to_lnCircle(c); -// result = _arrange(shapes, cc, min_obj_distance, progressind, cfn); -// break; -// } -// case BedShapeType::IRREGULAR: -// case BedShapeType::WHO_KNOWS: { -// auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); -// ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); -// result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); -// break; -// } -// }; - -// if(result.empty() || stopcondition()) return false; - -// if(first_bin_only) { -// applyResult(result.front(), 0, shapemap, wti); -// } else { - -// ClipperLib::cInt stride = stride_padding(binbb.width()); -// ClipperLib::cInt batch_offset = 0; - -// for(auto& group : result) { -// applyResult(group, batch_offset, shapemap, wti); - -// // 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 -// // print bed -// batch_offset += stride; -// } -// } - -// for(auto objptr : model.objects) objptr->invalidate_bounding_box(); - -// return ret && result.size() == 1; -//} - //void find_new_position(const Model &model, // ModelInstancePtrs toadd, // coord_t min_obj_distance, diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index 77b357f4e..45dde13d6 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -11,13 +11,13 @@ class Model; namespace arr { -class Circle { +class CircleBed { Point center_; double radius_; public: - inline Circle(): center_(0, 0), radius_(std::nan("")) {} - inline Circle(const Point& c, double r): center_(c), radius_(r) {} + inline CircleBed(): center_(0, 0), radius_(std::nan("")) {} + inline CircleBed(const Point& c, double r): center_(c), radius_(r) {} inline double radius() const { return radius_; } inline const Point& center() const { return center_; } @@ -34,7 +34,7 @@ enum class BedShapeType { struct BedShapeHint { BedShapeType type = BedShapeType::WHO_KNOWS; /*union*/ struct { // I know but who cares... TODO: use variant from cpp17? - Circle circ; + CircleBed circ; BoundingBox box; Polyline polygon; } shape; @@ -47,12 +47,13 @@ public: virtual ~Arrangeable() = default; - virtual void set_arrange_result(Vec2d offset, double rotation_rads) = 0; + virtual void apply_arrange_result(Vec2d offset, double rotation_rads) = 0; - virtual Polygon get_arrange_polygon() const = 0; + /// Get the 2D silhouette to arrange and an initial offset and rotation + virtual std::tuple get_arrange_polygon() const = 0; }; -using ArrangeableRefs = std::vector>; +using Arrangeables = std::vector; /** * \brief Arranges the model objects on the screen. @@ -89,7 +90,7 @@ using ArrangeableRefs = std::vector>; // std::function progressind, // std::function stopcondition); -bool arrange(ArrangeableRefs &items, +bool arrange(Arrangeables &items, coord_t min_obj_distance, BedShapeHint bedhint, std::function progressind, @@ -102,8 +103,8 @@ bool arrange(ArrangeableRefs &items, // coord_t min_obj_distance, // const Slic3r::Polyline& bed, // WipeTowerInfo& wti); -void find_new_position(ArrangeableRefs &items, - const ArrangeableRefs &instances_to_add, +void find_new_position(Arrangeables &items, + const Arrangeables &instances_to_add, coord_t min_obj_distance, BedShapeHint bedhint); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index bf892fe68..4e3093489 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5739,10 +5739,10 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::set_arrange_result(Vec2d offset, double rotation_rads) +void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads) { - m_pos += offset; - m_rotation += rotation_rads; + m_pos = offset; + m_rotation = rotation_rads; DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = m_pos(X); cfg.opt("wipe_tower_y", true)->value = m_pos(Y); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 1872d2f37..2d3c3b27f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -613,7 +613,7 @@ public: int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } class WipeTowerInfo: public arr::Arrangeable { - Vec2d m_pos = {std::nan(""), std::nan("")}; + Vec2d m_pos = {std::nan(""), std::nan("")}; Vec2d m_bb_size; double m_rotation; friend class GLCanvas3D; @@ -621,12 +621,12 @@ public: inline operator bool() const { - return std::isnan(m_pos.x()) || std::isnan(m_pos.y()); + return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } - virtual void set_arrange_result(Vec2d offset, double rotation_rads) final; + virtual void apply_arrange_result(Vec2d offset, double rotation_rads) final; - virtual Polygon get_arrange_polygon() const final + virtual std::tuple get_arrange_polygon() const final { Polygon p({ {coord_t(0), coord_t(0)}, @@ -636,9 +636,7 @@ public: {coord_t(0), coord_t(0)}, }); - p.rotate(m_rotation); - p.translate(scaled(m_pos)); - return p; + return std::make_tuple(p, scaled(m_pos), m_rotation); } }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b4afdfe67..193390f85 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2357,7 +2357,7 @@ void Plater::priv::remove(size_t obj_idx) void Plater::priv::delete_object_from_model(size_t obj_idx) -{ +{ model.delete_object(obj_idx); update(); object_list_changed(); @@ -2422,11 +2422,15 @@ arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { static const auto arrangestr = _(L("Arranging")); - - arr::ArrangeableRefs arrangeinput; arrangeinput.reserve(m_count); + + // 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(std::ref(*minst)); + 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. @@ -3456,7 +3460,7 @@ void Plater::priv::set_bed_shape(const Pointfs& shape) bool Plater::priv::can_delete() const { - return !get_selection().is_empty() && !get_selection().is_wipe_tower(); + return !get_selection().is_empty() && !get_selection().is_wipe_tower() && !m_ui_jobs.is_any_running(); } bool Plater::priv::can_delete_all() const From cb3a586debed37fdc338e4afdd6673a2bb377dfd Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 28 Jun 2019 18:27:15 +0200 Subject: [PATCH 05/32] Adapt find_new_position is WIP. Cleaning up comments. --- src/libnest2d/include/libnest2d/libnest2d.hpp | 4 + .../include/libnest2d/selections/firstfit.hpp | 12 +- src/libslic3r/ModelArrange.cpp | 222 +++++++----------- src/libslic3r/ModelArrange.hpp | 88 ++++--- src/slic3r/GUI/Plater.cpp | 156 ++++-------- 5 files changed, 181 insertions(+), 301 deletions(-) diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index b8a542752..4831c1fb6 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -67,6 +67,7 @@ class _Item { } bb_cache_; std::function applyfn_; + bool fixed_{false}; public: @@ -143,6 +144,9 @@ public: { if (applyfn_) applyfn_(*this, binidx); } + + inline bool isFixed() const noexcept { return fixed_; } + inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } /** * @brief Convert the polygon to string representation. The format depends diff --git a/src/libnest2d/include/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp index d521673b4..287204c08 100644 --- a/src/libnest2d/include/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -39,6 +39,15 @@ public: std::vector placers; placers.reserve(last-first); + + std::for_each(first, last, [this](Item& itm) { + if(itm.isFixed()) { + if(packed_bins_.empty()) packed_bins_.emplace_back(); + packed_bins_.front().emplace_back(itm); + } else { + store_.emplace_back(itm); + } + }); // If the packed_items array is not empty we have to create as many // placers as there are elements in packed bins and preload each item @@ -49,8 +58,6 @@ public: placers.back().preload(ig); } - std::copy(first, last, std::back_inserter(store_)); - auto sortfunc = [](Item& i1, Item& i2) { return i1.area() > i2.area(); }; @@ -76,7 +83,6 @@ public: } } - auto it = store_.begin(); while(it != store_.end() && !cancelled()) { diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 885979648..6d9c6007f 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -1,5 +1,4 @@ #include "ModelArrange.hpp" -//#include "Model.hpp" #include "Geometry.hpp" #include "SVG.hpp" #include "MTUtils.hpp" @@ -656,34 +655,35 @@ BedShapeHint bedShape(const Polyline &bed) { //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 & items, +PackGroup _arrange(std::vector & shapes, + const PackGroup & preshapes, 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 -//IndexedPackGroup _arrange(std::vector> &shapes, -// const PackGroup & preshapes, -// std::vector &minstances, -// const BinT & bin, -// coord_t minobjd) -//{ // auto binbb = sl::boundingBox(bin); -// AutoArranger arranger{bin, minobjd}; + AutoArranger arranger{bin, minobjd, prind, stopfn}; -// if(!preshapes.front().empty()) { // If there is something on the plate -// arranger.preload(preshapes); + if(!preshapes.front().empty()) { // If there is something on the plate + arranger.preload(preshapes); -// // Try to put the first item to the center, as the arranger will not -// // do this for us. + // 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) // { @@ -712,10 +712,10 @@ PackGroup _arrange(std::vector & items, // break; // } // } -// } + } -// return arranger(shapes.begin(), shapes.end()); -//} + return arranger(shapes.begin(), shapes.end()); +} inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) { @@ -725,53 +725,73 @@ 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, coord_t min_obj_distance, - BedShapeHint bedhint, + const BedShapeHint & bedhint, std::function progressind, std::function stopcondition) { bool ret = true; namespace clppr = ClipperLib; - std::vector items; + std::vector items, excluded_items; items.reserve(arrangables.size()); coord_t binwidth = 0; - for (Arrangeable *arrangeable : arrangables) { - assert(arrangeable); - - 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); - - if (p.is_counter_clockwise()) p.reverse(); - - clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); - - auto firstp = clpath.Contour.front(); - clpath.Contour.emplace_back(firstp); + PackGroup preshapes{ {} }; // pack group with one initial bin for preloading - items.emplace_back( + auto process_arrangeable = + [](const Arrangeable * arrangeable, + std::vector & outp, + std::function applyfn) + { + assert(arrangeable); + + 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); + + if (p.is_counter_clockwise()) p.reverse(); + + clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); + + 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()}); + }; + + for (Arrangeable *arrangeable : arrangables) { + process_arrangeable( + arrangeable, + items, // callback called by arrange to apply the result on the arrangeable [arrangeable, &binwidth](const Item &itm, unsigned binidx) { 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()); - }, - std::move(clpath)); - items.front().rotation(rotation); - items.front().translation({offs.x(), offs.y()}); + }); } + for (const Arrangeable * fixed: excludes) + process_arrangeable(fixed, excluded_items, nullptr); + + for(Item& excl : excluded_items) preshapes.front().emplace_back(excl); + // Integer ceiling the min distance from the bed perimeters coord_t md = min_obj_distance - SCALED_EPSILON; md = (md % 2) ? md / 2 + 1 : md / 2; - + + auto& cfn = stopcondition; + switch (bedhint.type) { case BedShapeType::BOX: { // Create the arranger for the box shaped bed @@ -781,14 +801,14 @@ bool arrange(Arrangeables & arrangables, Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; binwidth = coord_t(binbb.width()); - _arrange(items, binbb, min_obj_distance, progressind, stopcondition); + _arrange(items, preshapes, binbb, min_obj_distance, progressind, cfn); break; } case BedShapeType::CIRCLE: { auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); binwidth = scaled(c.radius()); - _arrange(items, cc, min_obj_distance, progressind, stopcondition); + _arrange(items, preshapes, cc, min_obj_distance, progressind, cfn); break; } case BedShapeType::IRREGULAR: { @@ -796,11 +816,11 @@ 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, irrbed, min_obj_distance, progressind, stopcondition); + _arrange(items, preshapes, irrbed, min_obj_distance, progressind, cfn); break; } case BedShapeType::WHO_KNOWS: { - _arrange(items, false, min_obj_distance, progressind, stopcondition); + _arrange(items, preshapes, false, min_obj_distance, progressind, cfn); break; } }; @@ -810,99 +830,15 @@ bool arrange(Arrangeables & arrangables, return ret; } -//void find_new_position(const Model &model, -// ModelInstancePtrs toadd, -// coord_t min_obj_distance, -// const Polyline &bed, -// WipeTowerInfo& wti) -//{ -// // Get the 2D projected shapes with their 3D model instance pointers -// auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); - -// // Copy the references for the shapes only, as the arranger expects a -// // sequence of objects convertible to Item or ClipperPolygon -// PackGroup preshapes; preshapes.emplace_back(); -// ItemGroup shapes; -// preshapes.front().reserve(shapemap.size()); - -// std::vector shapes_ptr; shapes_ptr.reserve(toadd.size()); -// IndexedPackGroup result; - -// // If there is no hint about the shape, we will try to guess -// BedShapeHint bedhint = bedShape(bed); - -// BoundingBox bbb(bed); - -// // Integer ceiling the min distance from the bed perimeters -// coord_t md = min_obj_distance - SCALED_EPSILON; -// md = (md % 2) ? md / 2 + 1 : md / 2; - -// auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, -// ClipperLib::cInt{bbb.min(1)} - md}, -// {ClipperLib::cInt{bbb.max(0)} + md, -// ClipperLib::cInt{bbb.max(1)} + md}); - -// for(auto it = shapemap.begin(); it != shapemap.end(); ++it) { -// // `toadd` vector contains the instance pointers which have to be -// // considered by arrange. If `it` points to an ModelInstance, which -// // is NOT in `toadd`, add it to preshapes. -// if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) { -// if(it->second.isInside(binbb)) // just ignore items which are outside -// preshapes.front().emplace_back(std::ref(it->second)); -// } -// else { -// shapes_ptr.emplace_back(it->first); -// shapes.emplace_back(std::ref(it->second)); -// } -// } - -// switch(bedhint.type) { -// case BedShapeType::BOX: { -// // Create the arranger for the box shaped bed -// result = _arrange(shapes, preshapes, shapes_ptr, binbb, min_obj_distance); -// break; -// } -// case BedShapeType::CIRCLE: { -// auto c = bedhint.shape.circ; -// auto cc = to_lnCircle(c); -// result = _arrange(shapes, preshapes, shapes_ptr, cc, min_obj_distance); -// break; -// } -// case BedShapeType::IRREGULAR: -// case BedShapeType::WHO_KNOWS: { -// auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); -// ClipperLib::Polygon irrbed = sl::create(std::move(ctour)); -// result = _arrange(shapes, preshapes, shapes_ptr, irrbed, min_obj_distance); -// break; -// } -// }; - -// // 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. - -// ClipperLib::cInt stride = stride_padding(binbb.width()); -// ClipperLib::cInt batch_offset = 0; - -// for(auto& group : result) { -// for(auto& r : group) if(r.first < shapes.size()) { -// Item& resultitem = r.second; -// unsigned idx = r.first; -// auto offset = resultitem.translation(); -// Radians rot = resultitem.rotation(); -// ModelInstance *minst = shapes_ptr[idx]; -// Vec3d foffset(unscaled(offset.X + batch_offset), -// unscaled(offset.Y), -// minst->get_offset()(Z)); - -// // write the transformation data into the model instance -// minst->set_rotation(Z, rot); -// minst->set_offset(foffset); -// } -// batch_offset += stride; -// } -//} - +/// Arrange, without the fixed items (excludes) +bool arrange(Arrangeables & inp, + coord_t min_d, + const BedShapeHint & bedhint, + std::function prfn, + std::function stopfn) +{ + return arrange(inp, {}, min_d, bedhint, prfn, stopfn); } - -} +} // namespace arr +} // namespace Slic3r diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index 45dde13d6..306081eb8 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -1,16 +1,14 @@ #ifndef MODELARRANGE_HPP #define MODELARRANGE_HPP -//#include "Model.hpp" #include "Polygon.hpp" #include "BoundingBox.hpp" namespace Slic3r { -class Model; - namespace arr { +/// A geometry abstraction for a circular print bed. Similarly to BoundingBox. class CircleBed { Point center_; double radius_; @@ -24,6 +22,7 @@ public: inline operator bool() { return !std::isnan(radius_); } }; +/// Types of print bed shapes. enum class BedShapeType { BOX, CIRCLE, @@ -31,6 +30,7 @@ enum class BedShapeType { WHO_KNOWS }; +/// Info about the print bed for the arrange() function. struct BedShapeHint { BedShapeType type = BedShapeType::WHO_KNOWS; /*union*/ struct { // I know but who cares... TODO: use variant from cpp17? @@ -40,13 +40,19 @@ struct BedShapeHint { } shape; }; +/// Get a bed shape hint for arrange() from a naked Polyline. BedShapeHint bedShape(const Polyline& bed); +/** + * @brief Classes implementing the Arrangeable interface can be used as input + * to the arrange function. + */ class Arrangeable { public: virtual ~Arrangeable() = default; + /// Apply the result transformation calculated by the arrangement. virtual void apply_arrange_result(Vec2d offset, double rotation_rads) = 0; /// Get the 2D silhouette to arrange and an initial offset and rotation @@ -58,56 +64,48 @@ using Arrangeables = std::vector; /** * \brief Arranges the model objects on the screen. * - * The arrangement considers multiple bins (aka. print beds) for placing all - * the items provided in the model argument. If the items don't fit on one - * print bed, the remaining will be placed onto newly created print beds. - * The first_bin_only parameter, if set to true, disables this behavior and - * makes sure that only one print bed is filled and the remaining items will be - * untouched. When set to false, the items which could not fit onto the - * print bed will be placed next to the print bed so the user should see a - * pile of items on the print bed and some other piles outside the print - * area that can be dragged later onto the print bed as a group. + * The arrangement considers multiple bins (aka. print beds) for placing + * all the items provided in the model argument. If the items don't fit on + * one print bed, the remaining will be placed onto newly created print + * beds. The first_bin_only parameter, if set to true, disables this + * behavior and makes sure that only one print bed is filled and the + * remaining items will be untouched. When set to false, the items which + * could not fit onto the print bed will be placed next to the print bed so + * the user should see a pile of items on the print bed and some other + * piles outside the print area that can be dragged later onto the print + * bed as a group. + * + * \param items Input which are object pointers implementing the + * Arrangeable interface. + * + * \param min_obj_distance The minimum distance which is allowed for any + * pair of items on the print bed in any direction. + * + * \param bedhint Info about the shape and type of the + * bed. remaining items which do not fit onto the print area next to the + * print bed or leave them untouched (let the user arrange them by hand or + * remove them). + * + * \param progressind Progress indicator callback called when + * an object gets packed. The unsigned argument is the number of items + * remaining to pack. * - * \param model The model object with the 3D content. - * \param dist The minimum distance which is allowed for any pair of items - * on the print bed in any direction. - * \param bb The bounding box of the print bed. It corresponds to the 'bin' - * for bin packing. - * \param first_bin_only This parameter controls whether to place the - * remaining items which do not fit onto the print area next to the print - * bed or leave them untouched (let the user arrange them by hand or remove - * them). - * \param progressind Progress indicator callback called when an object gets - * packed. The unsigned argument is the number of items remaining to pack. * \param stopcondition A predicate returning true if abort is needed. */ -//bool arrange(Model &model, -// WipeTowerInfo& wipe_tower_info, -// coord_t min_obj_distance, -// const Slic3r::Polyline& bed, -// BedShapeHint bedhint, -// bool first_bin_only, -// std::function progressind, -// std::function stopcondition); - bool arrange(Arrangeables &items, coord_t min_obj_distance, - BedShapeHint bedhint, + const BedShapeHint& bedhint, std::function progressind, std::function stopcondition); -/// This will find a suitable position for a new object instance and leave the -/// old items untouched. -//void find_new_position(const Model& model, -// ModelInstancePtrs instances_to_add, -// coord_t min_obj_distance, -// const Slic3r::Polyline& bed, -// WipeTowerInfo& wti); -void find_new_position(Arrangeables &items, - const Arrangeables &instances_to_add, - coord_t min_obj_distance, - BedShapeHint bedhint); - +/// Same as the previous, only that it takes unmovable items as an +/// additional argument. +bool arrange(Arrangeables &items, + const Arrangeables &excludes, + coord_t min_obj_distance, + const BedShapeHint& bedhint, + std::function progressind, + std::function stopcondition); } // arr } // Slic3r diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 193390f85..aba8ae71d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2461,132 +2461,68 @@ void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { update_status(m_count, was_canceled() ? _(L("Arranging canceled.")) : _(L("Arranging done."))); +} - // TODO: we should decide whether to allow arrange when the search is - // running we should probably disable explicit slicing and background - // processing - -// static const auto arrangestr = _(L("Arranging")); - -// auto &config = plater().config; -// auto &view3D = plater().view3D; -// auto &model = plater().model; - -// // 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); -// if (plater().printer_technology == ptFFF) { -// dist = PrintConfig::min_object_distance(config); -// } - -// auto min_obj_distance = coord_t(dist / SCALING_FACTOR); - -// const auto *bed_shape_opt = config->opt( -// "bed_shape"); - -// assert(bed_shape_opt); -// auto & bedpoints = bed_shape_opt->values; -// Polyline bed; -// bed.points.reserve(bedpoints.size()); -// for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); - -// update_status(0, arrangestr); - -// arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info(); - -// try { -// arr::BedShapeHint hint; - -// // TODO: from Sasha from GUI or -// hint.type = arr::BedShapeType::WHO_KNOWS; - -// arr::arrange(model, -// wti, -// min_obj_distance, -// bed, -// hint, -// false, // create many piles not just one pile -// [this](unsigned st) { -// if (st > 0) -// update_status(count - int(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(count, -// was_canceled() ? _(L("Arranging canceled.")) -// : _(L("Arranging done."))); - -// // it remains to move the wipe tower: -// view3D->get_canvas3d()->arrange_wipe_tower(wti); +void find_new_position(const Model & model, + ModelInstancePtrs instances, + coord_t min_d, + const arr::BedShapeHint &bedhint) +{ + + // TODO } void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process() { -// int obj_idx = plater().get_selected_object_idx(); -// if (obj_idx < 0) { return; } + int obj_idx = plater().get_selected_object_idx(); + 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( -// *o, -// .005f, -// [this](unsigned s) { -// if (s < 100) -// update_status(int(s), -// _(L("Searching for optimal orientation"))); -// }, -// [this]() { return was_canceled(); }); + auto r = sla::find_best_rotation( + *o, + .005f, + [this](unsigned s) { + if (s < 100) + update_status(int(s), + _(L("Searching for optimal orientation"))); + }, + [this]() { return was_canceled(); }); -// const auto *bed_shape_opt = -// plater().config->opt("bed_shape"); + + double mindist = 6.0; // FIXME -// assert(bed_shape_opt); - -// auto & bedpoints = bed_shape_opt->values; -// Polyline bed; -// bed.points.reserve(bedpoints.size()); -// for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); - -// double mindist = 6.0; // FIXME + if (!was_canceled()) { + for(ModelInstance * oi : o->instances) { + oi->set_rotation({r[X], r[Y], r[Z]}); -// if (!was_canceled()) { -// for(ModelInstance * oi : o->instances) { -// oi->set_rotation({r[X], r[Y], r[Z]}); - -// auto trmatrix = oi->get_transformation().get_matrix(); -// Polygon trchull = o->convex_hull_2d(trmatrix); + auto trmatrix = oi->get_transformation().get_matrix(); + Polygon trchull = o->convex_hull_2d(trmatrix); -// MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); -// double r = rotbb.angle_to_X(); + MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); + double r = rotbb.angle_to_X(); -// // The box should be landscape -// if(rotbb.width() < rotbb.height()) r += PI / 2; + // The box should be landscape + 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::find_new_position(plater().model, -// o->instances, -// coord_t(mindist / SCALING_FACTOR), -// bed, -// wti); - -// // Correct the z offset of the object which was corrupted be -// // the rotation -// o->ensure_on_bed(); -// } + find_new_position(plater().model, + o->instances, + scaled(mindist), + plater().get_bed_shape_hint()); -// update_status(100, -// was_canceled() ? _(L("Orientation search canceled.")) -// : _(L("Orientation found."))); + // Correct the z offset of the object which was corrupted be + // the rotation + o->ensure_on_bed(); + } + + update_status(100, + was_canceled() ? _(L("Orientation search canceled.")) + : _(L("Orientation found."))); } void Plater::priv::split_object() From 253ec07cb28ccefd2bd304ca515d9001b28d95c3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 1 Jul 2019 18:22:07 +0200 Subject: [PATCH 06/32] Still WIP --- .clang-format | 2 +- src/libnest2d/include/libnest2d/libnest2d.hpp | 2 +- .../{ModelArrange.cpp => Arrange.cpp} | 171 +++++++----------- .../{ModelArrange.hpp => Arrange.hpp} | 31 ++-- src/libslic3r/CMakeLists.txt | 4 +- src/libslic3r/Model.cpp | 89 +++++---- src/libslic3r/Model.hpp | 4 +- src/slic3r/GUI/GLCanvas3D.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 145 ++++++++++----- 9 files changed, 242 insertions(+), 210 deletions(-) rename src/libslic3r/{ModelArrange.cpp => Arrange.cpp} (85%) rename src/libslic3r/{ModelArrange.hpp => Arrange.hpp} (82%) 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 From ba82cbe007caf8b17b0507b5681c8b3c0f8e7a77 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jul 2019 10:32:01 +0200 Subject: [PATCH 07/32] Fix broken partial arrange --- src/libslic3r/Arrange.cpp | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 3abe49531..87289d968 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -340,28 +340,26 @@ class AutoArranger {}; template class _ArrBase { public: - // Useful type shortcuts... using Placer = typename placers::_NofitPolyPlacer; using Selector = selections::_FirstFitSelection; - using Packer = Nester; - using PConfig = typename Packer::PlacementConfig; + using Packer = Nester; + using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; - + protected: - - Packer m_pck; - PConfig m_pconf; // Placement configuration - double m_bin_area; - SpatIndex m_rtree; // spatial index for the normal (bigger) objects + Packer m_pck; + PConfig m_pconf; // Placement configuration + double m_bin_area; + SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_smallsrtree; // spatial index for only the smaller items - double m_norm; // A coefficient to scale distances + double m_norm; // A coefficient to scale distances MultiPolygon m_merged_pile; // The already merged pile (vector of items) - Box m_pilebb; // The bounding box of the merged pile. - ItemGroup m_remaining; // Remaining items (m_items at the beginning) - ItemGroup m_items; // The items to be packed + Box m_pilebb; // The bounding box of the merged pile. + ItemGroup m_remaining; // Remaining items (m_items at the beginning) + ItemGroup m_items; // The items to be packed + public: - _ArrBase(const TBin& bin, Distance dist, std::function progressind, std::function stopcond): @@ -715,8 +713,8 @@ bool arrange(ArrangeablePtrs & arrangables, clpath.Contour.emplace_back(firstp); outp.emplace_back(applyfn, std::move(clpath)); - outp.front().rotation(rotation); - outp.front().translation({offs.x(), offs.y()}); + outp.back().rotation(rotation); + outp.back().translation({offs.x(), offs.y()}); }; for (Arrangeable *arrangeable : arrangables) { From 914bf632288f036b2b7cb509eb5ba843cdc3b7ed Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jul 2019 12:15:53 +0200 Subject: [PATCH 08/32] Unify AutoArranger subclasses --- src/libnest2d/include/libnest2d/libnest2d.hpp | 8 +- src/libslic3r/Arrange.cpp | 561 +++++++++--------- 2 files changed, 301 insertions(+), 268 deletions(-) diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 8d3a2272d..c7d515d49 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -774,14 +774,14 @@ public: using BinType = typename TPlacer::BinType; using PlacementConfig = typename TPlacer::Config; using SelectionConfig = typename TSel::Config; - using Unit = TCoord>; + using Coord = TCoord>; using PackGroup = _PackGroup; using ResultType = PackGroup; private: BinType bin_; PlacementConfig pconfig_; - Unit min_obj_distance_; + Coord min_obj_distance_; using SItem = typename SelectionStrategy::Item; using TPItem = remove_cvref_t; @@ -802,7 +802,7 @@ public: class PConf = PlacementConfig, class SConf = SelectionConfig> Nester( TBinType&& bin, - Unit min_obj_distance = 0, + Coord min_obj_distance = 0, const PConf& pconfig = PConf(), const SConf& sconfig = SConf()): bin_(std::forward(bin)), @@ -895,7 +895,7 @@ private: template inline void __execute(TIter from, TIter to) { if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { - item.addOffset(static_cast(std::ceil(min_obj_distance_/2.0))); + item.addOffset(static_cast(std::ceil(min_obj_distance_/2.0))); }); selector_.template packItems( diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 87289d968..debd29024 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -171,142 +171,6 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) { return Box(minc, maxc); } -// This is "the" object function which is evaluated many times for each vertex -// (decimated with the accuracy parameter) of each object. Therefore it is -// upmost crucial for this function to be as efficient as it possibly can be but -// at the same time, it has to provide reasonable results. -std::tuple -objfunc(const PointImpl& bincenter, - const MultiPolygon& merged_pile, - const Box& pilebb, - const ItemGroup& items, - const Item &item, - double bin_area, - double norm, // A norming factor for physical dimensions - // a spatial index to quickly get neighbors of the candidate item - const SpatIndex& spatindex, - const SpatIndex& smalls_spatindex, - const ItemGroup& remaining - ) -{ - // We will treat big items (compared to the print bed) differently - auto isBig = [bin_area](double a) { - return a/bin_area > BIG_ITEM_TRESHOLD ; - }; - - // Candidate item bounding box - auto ibb = sl::boundingBox(item.transformedShape()); - - // Calculate the full bounding box of the pile with the candidate item - auto fullbb = boundingBox(pilebb, ibb); - - // The bounding box of the big items (they will accumulate in the center - // of the pile - Box bigbb; - if(spatindex.empty()) bigbb = fullbb; - else { - auto boostbb = spatindex.bounds(); - boost::geometry::convert(boostbb, bigbb); - } - - // Will hold the resulting score - double score = 0; - - if(isBig(item.area()) || spatindex.empty()) { - // This branch is for the bigger items.. - - auto minc = ibb.minCorner(); // bottom left corner - auto maxc = ibb.maxCorner(); // top right corner - - // top left and bottom right corners - auto top_left = PointImpl{getX(minc), getY(maxc)}; - auto bottom_right = PointImpl{getX(maxc), getY(minc)}; - - // Now the distance of the gravity center will be calculated to the - // five anchor points and the smallest will be chosen. - std::array dists; - auto cc = fullbb.center(); // The gravity center - dists[0] = pl::distance(minc, cc); - dists[1] = pl::distance(maxc, cc); - dists[2] = pl::distance(ibb.center(), cc); - dists[3] = pl::distance(top_left, cc); - dists[4] = pl::distance(bottom_right, cc); - - // The smalles distance from the arranged pile center: - auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; - auto bindist = pl::distance(ibb.center(), bincenter) / norm; - dist = 0.8*dist + 0.2*bindist; - - // Density is the pack density: how big is the arranged pile - double density = 0; - - if(remaining.empty()) { - - auto mp = merged_pile; - mp.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(mp); - - placers::EdgeCache ec(chull); - - double circ = ec.circumference() / norm; - double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; - score = 0.5*circ + 0.5*bcirc; - - } else { - // Prepare a variable for the alignment score. - // This will indicate: how well is the candidate item aligned with - // its neighbors. We will check the alignment with all neighbors and - // return the score for the best alignment. So it is enough for the - // candidate to be aligned with only one item. - auto alignment_score = 1.0; - - density = std::sqrt((fullbb.width() / norm )* - (fullbb.height() / norm)); - auto querybb = item.boundingBox(); - - // Query the spatial index for the neighbors - std::vector result; - result.reserve(spatindex.size()); - if(isBig(item.area())) { - spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); - } else { - smalls_spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); - } - - for(auto& e : result) { // now get the score for the best alignment - auto idx = e.second; - Item& p = items[idx]; - auto parea = p.area(); - if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = boundingBox(p.boundingBox(), ibb); - auto bbarea = bb.area(); - auto ascore = 1.0 - (item.area() + parea)/bbarea; - - if(ascore < alignment_score) alignment_score = ascore; - } - } - - // The final mix of the score is the balance between the distance - // from the full pile center, the pack density and the - // alignment with the neighbors - if(result.empty()) - score = 0.5 * dist + 0.5 * density; - else - score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; - } - } else { - // Here there are the small items that should be placed around the - // already processed bigger items. - // No need to play around with the anchor points, the center will be - // just fine for small items - score = pl::distance(ibb.center(), bigbb.center()) / norm; - } - - return std::make_tuple(score, fullbb); -} - // Fill in the placer algorithm configuration with values carefully chosen for // Slic3r. template @@ -332,13 +196,16 @@ void fillConfig(PConf& pcfg) { // Type trait for an arranger class for different bin types (box, circle, // polygon, etc...) -template -class AutoArranger {}; +//template +//class AutoArranger {}; + +template clppr::IntPoint center(const Bin& bin) { return bin.center(); } +template<> clppr::IntPoint center(const clppr::Polygon &bin) { return sl::boundingBox(bin).center(); } // A class encapsulating the libnest2d Nester class and extending it with other // management and spatial index structures for acceleration. template -class _ArrBase { +class AutoArranger { public: // Useful type shortcuts... using Placer = typename placers::_NofitPolyPlacer; @@ -350,7 +217,9 @@ public: protected: Packer m_pck; PConfig m_pconf; // Placement configuration - double m_bin_area; + TBin m_bin; + double m_bin_area; // caching + PointImpl m_bincenter; // caching SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_smallsrtree; // spatial index for only the smaller items double m_norm; // A coefficient to scale distances @@ -358,13 +227,152 @@ protected: Box m_pilebb; // The bounding box of the merged pile. ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_items; // The items to be packed + + // This is "the" object function which is evaluated many times for each + // vertex (decimated with the accuracy parameter) of each object. + // Therefore it is upmost crucial for this function to be as efficient + // as it possibly can be but at the same time, it has to provide + // reasonable results. + std::tuple + objfunc(const Item &item ) + { + const double bin_area = m_bin_area; + const SpatIndex& spatindex = m_rtree; + const SpatIndex& smalls_spatindex = m_smallsrtree; + const ItemGroup& remaining = m_remaining; + + // We will treat big items (compared to the print bed) differently + auto isBig = [bin_area](double a) { + return a/bin_area > BIG_ITEM_TRESHOLD ; + }; + + // Candidate item bounding box + auto ibb = sl::boundingBox(item.transformedShape()); + + // Calculate the full bounding box of the pile with the candidate item + auto fullbb = boundingBox(m_pilebb, ibb); + + // The bounding box of the big items (they will accumulate in the center + // of the pile + Box bigbb; + if(spatindex.empty()) bigbb = fullbb; + else { + auto boostbb = spatindex.bounds(); + boost::geometry::convert(boostbb, bigbb); + } + + // Will hold the resulting score + double score = 0; + + if(isBig(item.area()) || spatindex.empty()) { + // This branch is for the bigger items.. + + auto minc = ibb.minCorner(); // bottom left corner + auto maxc = ibb.maxCorner(); // top right corner + + // top left and bottom right corners + auto top_left = PointImpl{getX(minc), getY(maxc)}; + auto bottom_right = PointImpl{getX(maxc), getY(minc)}; + + // Now the distance of the gravity center will be calculated to the + // five anchor points and the smallest will be chosen. + std::array dists; + auto cc = fullbb.center(); // The gravity center + dists[0] = pl::distance(minc, cc); + dists[1] = pl::distance(maxc, cc); + dists[2] = pl::distance(ibb.center(), cc); + dists[3] = pl::distance(top_left, cc); + dists[4] = pl::distance(bottom_right, cc); + + // The smalles distance from the arranged pile center: + double dist = *(std::min_element(dists.begin(), dists.end())) / m_norm; + double bindist = pl::distance(ibb.center(), m_bincenter) / m_norm; + dist = 0.8*dist + 0.2*bindist; + + // Density is the pack density: how big is the arranged pile + double density = 0; + + if(remaining.empty()) { + + auto mp = m_merged_pile; + mp.emplace_back(item.transformedShape()); + auto chull = sl::convexHull(mp); + + placers::EdgeCache ec(chull); + + double circ = ec.circumference() / m_norm; + double bcirc = 2.0*(fullbb.width() + fullbb.height()) / m_norm; + score = 0.5*circ + 0.5*bcirc; + + } else { + // Prepare a variable for the alignment score. + // This will indicate: how well is the candidate item + // aligned with its neighbors. We will check the alignment + // with all neighbors and return the score for the best + // alignment. So it is enough for the candidate to be + // aligned with only one item. + auto alignment_score = 1.0; + + auto querybb = item.boundingBox(); + density = std::sqrt((fullbb.width() / m_norm )* + (fullbb.height() / m_norm)); + + // Query the spatial index for the neighbors + std::vector result; + result.reserve(spatindex.size()); + if(isBig(item.area())) { + spatindex.query(bgi::intersects(querybb), + std::back_inserter(result)); + } else { + smalls_spatindex.query(bgi::intersects(querybb), + std::back_inserter(result)); + } + + // now get the score for the best alignment + for(auto& e : result) { + auto idx = e.second; + Item& p = m_items[idx]; + auto parea = p.area(); + if(std::abs(1.0 - parea/item.area()) < 1e-6) { + auto bb = boundingBox(p.boundingBox(), ibb); + auto bbarea = bb.area(); + auto ascore = 1.0 - (item.area() + parea)/bbarea; + + if(ascore < alignment_score) alignment_score = ascore; + } + } + + // The final mix of the score is the balance between the + // distance from the full pile center, the pack density and + // the alignment with the neighbors + if (result.empty()) + score = 0.5 * dist + 0.5 * density; + else + score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; + } + } else { + // Here there are the small items that should be placed around the + // already processed bigger items. + // No need to play around with the anchor points, the center will be + // just fine for small items + score = pl::distance(ibb.center(), bigbb.center()) / m_norm; + } + + return std::make_tuple(score, fullbb); + } + + std::function get_objfn(); public: - _ArrBase(const TBin& bin, Distance dist, - std::function progressind, - std::function stopcond): - m_pck(bin, dist), m_bin_area(sl::area(bin)), - m_norm(std::sqrt(m_bin_area)) + AutoArranger(const TBin & bin, + Distance dist, + std::function progressind, + std::function stopcond) + : m_pck(bin, dist) + , m_bin(bin) + , m_bin_area(sl::area(bin)) + , m_bincenter(center(bin)) + , m_norm(std::sqrt(m_bin_area)) { fillConfig(m_pconf); @@ -396,8 +404,12 @@ public: } }; + m_pconf.object_function = get_objfn(); + if (progressind) m_pck.progressIndicator(progressind); if (stopcond) m_pck.stopCondition(stopcond); + + m_pck.configure(m_pconf); } template inline PackGroup operator()(Args&&...args) { @@ -405,15 +417,16 @@ public: return m_pck.execute(std::forward(args)...); } - inline void preload(const PackGroup& pg) { + inline void preload(std::vector& fixeditems) { m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; - m_pconf.object_function = nullptr; // drop the special objectfunction - m_pck.preload(pg); +// m_pconf.object_function = nullptr; // drop the special objectfunction +// m_pck.preload(pg); // Build the rtree for queries to work - for(const ItemGroup& grp : pg) - for(unsigned idx = 0; idx < grp.size(); ++idx) { - Item& itm = grp[idx]; + + for(unsigned idx = 0; idx < fixeditems.size(); ++idx) { + Item& itm = fixeditems[idx]; + itm.markAsFixed(); m_rtree.insert({itm.boundingBox(), idx}); } @@ -429,125 +442,144 @@ public: } }; -// Arranger specialization for a Box shaped bin. -template<> class AutoArranger: public _ArrBase { -public: +template<> std::function AutoArranger::get_objfn() +{ + return [this](const Item &itm) { + auto result = objfunc(itm); + + double score = std::get<0>(result); + auto& fullbb = std::get<1>(result); - AutoArranger(const Box& bin, Distance dist, - std::function progressind = [](unsigned){}, - std::function stopcond = [](){return false;}): - _ArrBase(bin, dist, progressind, stopcond) - { + double miss = Placer::overfit(fullbb, m_bin); + miss = miss > 0? miss : 0; + score += miss*miss; - // Here we set up the actual object function that calls the common - // object function for all bin shapes than does an additional inside - // check for the arranged pile. - m_pconf.object_function = [this, bin] (const Item &item) { + return score; + }; +} - auto result = objfunc(bin.center(), - m_merged_pile, - m_pilebb, - m_items, - item, - m_bin_area, - m_norm, - m_rtree, - m_smallsrtree, - m_remaining); +template<> std::function AutoArranger::get_objfn() +{ + return [this](const Item &item) { + + auto result = objfunc(item); - double score = std::get<0>(result); - auto& fullbb = std::get<1>(result); + double score = std::get<0>(result); - double miss = Placer::overfit(fullbb, bin); - miss = miss > 0? miss : 0; - score += miss*miss; - - return score; + auto isBig = [this](const Item& itm) { + return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; }; - m_pck.configure(m_pconf); - } -}; + if(isBig(item)) { + auto mp = m_merged_pile; + mp.push_back(item.transformedShape()); + auto chull = sl::convexHull(mp); + double miss = Placer::overfit(chull, m_bin); + if(miss < 0) miss = 0; + score += miss*miss; + } + + return score; + }; +} + +template<> std::function AutoArranger::get_objfn() +{ + return [this] (const Item &item) { return std::get<0>(objfunc(item)); }; +} + +// Arranger specialization for a Box shaped bin. +//template<> class AutoArranger: public _ArrBase { +//public: + +// AutoArranger(const Box& bin, Distance dist, +// std::function progressind = [](unsigned){}, +// std::function stopcond = [](){return false;}): +// _ArrBase(bin, dist, progressind, stopcond) +// { + +// // Here we set up the actual object function that calls the common +// // object function for all bin shapes than does an additional inside +// // check for the arranged pile. +// m_pconf.object_function = [this, bin](const Item &item) { + +// auto result = objfunc(bin.center(), item); + +// double score = std::get<0>(result); +// auto& fullbb = std::get<1>(result); + +// double miss = Placer::overfit(fullbb, bin); +// miss = miss > 0? miss : 0; +// score += miss*miss; + +// return score; +// }; + +// m_pck.configure(m_pconf); +// } +//}; inline Circle to_lnCircle(const CircleBed& circ) { return Circle({circ.center()(0), circ.center()(1)}, circ.radius()); } -// Arranger specialization for circle shaped bin. -template<> class AutoArranger: public _ArrBase { -public: +//// Arranger specialization for circle shaped bin. +//template<> class AutoArranger: public _ArrBase { +//public: - AutoArranger(const Circle& bin, Distance dist, - std::function progressind = [](unsigned){}, - std::function stopcond = [](){return false;}): - _ArrBase(bin, dist, progressind, stopcond) { +// AutoArranger(const Circle& bin, Distance dist, +// std::function progressind = [](unsigned){}, +// std::function stopcond = [](){return false;}): +// _ArrBase(bin, dist, progressind, stopcond) { - // As with the box, only the inside check is different. - m_pconf.object_function = [this, &bin] (const Item &item) { +// // As with the box, only the inside check is different. +// m_pconf.object_function = [this, &bin](const Item &item) { + +// auto result = objfunc(bin.center(), item); - auto result = objfunc(bin.center(), - m_merged_pile, - m_pilebb, - m_items, - item, - m_bin_area, - m_norm, - m_rtree, - m_smallsrtree, - m_remaining); +// double score = std::get<0>(result); - double score = std::get<0>(result); +// auto isBig = [this](const Item& itm) { +// return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; +// }; - auto isBig = [this](const Item& itm) { - return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; - }; +// if(isBig(item)) { +// auto mp = m_merged_pile; +// mp.push_back(item.transformedShape()); +// auto chull = sl::convexHull(mp); +// double miss = Placer::overfit(chull, bin); +// if(miss < 0) miss = 0; +// score += miss*miss; +// } - if(isBig(item)) { - auto mp = m_merged_pile; - mp.push_back(item.transformedShape()); - auto chull = sl::convexHull(mp); - double miss = Placer::overfit(chull, bin); - if(miss < 0) miss = 0; - score += miss*miss; - } +// return score; +// }; - return score; - }; - - m_pck.configure(m_pconf); - } -}; +// m_pck.configure(m_pconf); +// } +//}; // Arranger specialization for a generalized polygon. // Warning: this is unfinished business. It may or may not work. -template<> class AutoArranger: public _ArrBase { -public: - AutoArranger(const PolygonImpl& bin, Distance dist, - std::function progressind = [](unsigned){}, - std::function stopcond = [](){return false;}): - _ArrBase(bin, dist, progressind, stopcond) - { - m_pconf.object_function = [this, &bin] (const Item &item) { +//template<> class AutoArranger: public _ArrBase { +//public: +// AutoArranger(const PolygonImpl& bin, Distance dist, +// std::function progressind = [](unsigned){}, +// std::function stopcond = [](){return false;}): +// _ArrBase(bin, dist, progressind, stopcond) +// { +// m_pconf.object_function = [this, &bin] (const Item &item) { - auto binbb = sl::boundingBox(bin); - auto result = objfunc(binbb.center(), - m_merged_pile, - m_pilebb, - m_items, - item, - m_bin_area, - m_norm, - m_rtree, - m_smallsrtree, - m_remaining); - double score = std::get<0>(result); +// auto binbb = sl::boundingBox(bin); +// auto result = objfunc(binbb.center(), item); +// double score = std::get<0>(result); - return score; - }; +// return score; +// }; - m_pck.configure(m_pconf); - } -}; +// m_pck.configure(m_pconf); +// } +//}; // Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { @@ -628,9 +660,9 @@ BedShapeHint bedShape(const Polyline &bed) { return ret; } -template +template // Arrange for arbitrary bin type PackGroup _arrange(std::vector & shapes, - const PackGroup & preshapes, + std::vector & excludes, const BinT & bin, coord_t minobjd, std::function prind, @@ -638,9 +670,13 @@ PackGroup _arrange(std::vector & shapes, { AutoArranger arranger{bin, minobjd, prind, stopfn}; + for(auto it = excludes.begin(); it != excludes.end(); ++it) + if (!sl::isInside(it->transformedShape(), bin)) + it = excludes.erase(it); + // If there is something on the plate - if(!preshapes.empty() && !preshapes.front().empty()) { - arranger.preload(preshapes); + if(!excludes.empty()) { +// arranger.preload(preshapes); auto binbb = sl::boundingBox(bin); // Try to put the first item to the center, as the arranger will not @@ -652,7 +688,8 @@ PackGroup _arrange(std::vector & shapes, itm.translate(d); if (!arranger.is_colliding(itm)) { - arranger.preload({{itm}}); + itm.markAsFixed(); +// arranger.preload({{itm}}); // Write the transformation data into the item. The callback // was set on the instantiation of Item and calls the @@ -674,8 +711,8 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) return w + w / 5; } -//// The final client function to arrange the Model. A progress indicator and -//// a stop predicate can be also be passed to control the process. +// The final client function for arrangement. A progress indicator and +// a stop predicate can be also be passed to control the process. bool arrange(ArrangeablePtrs & arrangables, const ArrangeablePtrs & excludes, coord_t min_obj_distance, @@ -686,11 +723,9 @@ bool arrange(ArrangeablePtrs & arrangables, bool ret = true; namespace clppr = ClipperLib; - std::vector items, excluded_items; + std::vector items, fixeditems; items.reserve(arrangables.size()); coord_t binwidth = 0; - - PackGroup preshapes{ {} }; // pack group with one initial bin for preloading auto process_arrangeable = [](const Arrangeable * arrangeable, @@ -733,9 +768,7 @@ bool arrange(ArrangeablePtrs & arrangables, } for (const Arrangeable * fixed: excludes) - process_arrangeable(fixed, excluded_items, nullptr); - - for(Item& excl : excluded_items) preshapes.front().emplace_back(excl); + process_arrangeable(fixed, fixeditems, nullptr); // Integer ceiling the min distance from the bed perimeters coord_t md = min_obj_distance - SCALED_EPSILON; @@ -751,7 +784,7 @@ bool arrange(ArrangeablePtrs & arrangables, Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; binwidth = coord_t(binbb.width()); - _arrange(items, preshapes, binbb, min_obj_distance, progressind, cfn); + _arrange(items, fixeditems, binbb, min_obj_distance, progressind, cfn); break; } case BedShapeType::CIRCLE: { @@ -759,7 +792,7 @@ bool arrange(ArrangeablePtrs & arrangables, auto cc = to_lnCircle(c); binwidth = scaled(c.radius()); - _arrange(items, preshapes, cc, min_obj_distance, progressind, cfn); + _arrange(items, fixeditems, cc, min_obj_distance, progressind, cfn); break; } case BedShapeType::IRREGULAR: { @@ -768,7 +801,7 @@ bool arrange(ArrangeablePtrs & arrangables, BoundingBox polybb(bedhint.shape.polygon); binwidth = (polybb.max(X) - polybb.min(X)); - _arrange(items, preshapes, irrbed, min_obj_distance, progressind, cfn); + _arrange(items, fixeditems, irrbed, min_obj_distance, progressind, cfn); break; } case BedShapeType::INFINITE: { @@ -776,12 +809,12 @@ bool arrange(ArrangeablePtrs & arrangables, //Box infbb{{nobin.center.x(), nobin.center.y()}}; Box infbb; - _arrange(items, preshapes, infbb, min_obj_distance, progressind, cfn); + _arrange(items, fixeditems, 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); + _arrange(items, fixeditems, Box{}, min_obj_distance, progressind, cfn); break; } }; @@ -791,7 +824,7 @@ bool arrange(ArrangeablePtrs & arrangables, return ret; } -/// Arrange, without the fixed items (excludes) +// Arrange, without the fixed items (excludes) bool arrange(ArrangeablePtrs & inp, coord_t min_d, const BedShapeHint & bedhint, From 87c5e9bbaa264db564bfd8bd6a2f9238791ee2e8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jul 2019 15:24:40 +0200 Subject: [PATCH 09/32] Partial arrange starts to work again. --- .../include/libnest2d/geometry_traits.hpp | 26 +- src/libnest2d/include/libnest2d/libnest2d.hpp | 5 +- .../include/libnest2d/placers/nfpplacer.hpp | 31 +- src/libslic3r/Arrange.cpp | 339 +++++------------- 4 files changed, 123 insertions(+), 278 deletions(-) diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 6c55d0e3f..345252f12 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -875,6 +875,28 @@ inline _Box> boundingBox(const S& sh) return boundingBox(sh, Tag() ); } +template _Box

boundingBox(const _Box

& bb1, const _Box

& bb2 ) +{ + auto& pminc = bb1.minCorner(); + auto& pmaxc = bb1.maxCorner(); + auto& iminc = bb2.minCorner(); + auto& imaxc = bb2.maxCorner(); + P minc, maxc; + + setX(minc, std::min(getX(pminc), getX(iminc))); + setY(minc, std::min(getY(pminc), getY(iminc))); + + setX(maxc, std::max(getX(pmaxc), getX(imaxc))); + setY(maxc, std::max(getY(pmaxc), getY(imaxc))); + return _Box

(minc, maxc); +} + +template +_Box> boundingBox(const S1 &s1, const S2 &s2) +{ + return boundingBox(boundingBox(s1), boundingBox(s2)); +} + template inline double area(const Box& box, const BoxTag& ) { @@ -1060,8 +1082,8 @@ template inline bool isInside(const TB& box, const TC& circ, const BoxTag&, const CircleTag&) { - return isInside(box.minCorner(), circ, BoxTag(), CircleTag()) && - isInside(box.maxCorner(), circ, BoxTag(), CircleTag()); + return isInside(box.minCorner(), circ, PointTag(), CircleTag()) && + isInside(box.maxCorner(), circ, PointTag(), CircleTag()); } template diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index c7d515d49..ab018f3f8 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -895,7 +895,10 @@ private: template inline void __execute(TIter from, TIter to) { if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { - item.addOffset(static_cast(std::ceil(min_obj_distance_/2.0))); + auto offs = min_obj_distance_; + if (item.isFixed()) offs *= 0.99; + + item.addOffset(static_cast(std::ceil(offs/2.0))); }); selector_.template packItems( diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index c1f15fe61..b94443bff 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -801,7 +801,6 @@ private: // optimize config_.object_function = prev_func; } - } struct Optimum { @@ -816,29 +815,14 @@ private: class Optimizer: public opt::TOptimizer { public: - Optimizer() { + Optimizer(float accuracy = 1.f) { opt::StopCriteria stopcr; - stopcr.max_iterations = 200; + stopcr.max_iterations = unsigned(std::floor(1000 * accuracy)); stopcr.relative_score_difference = 1e-20; this->stopcr_ = stopcr; } }; - static Box boundingBox(const Box& pilebb, const Box& ibb ) { - auto& pminc = pilebb.minCorner(); - auto& pmaxc = pilebb.maxCorner(); - auto& iminc = ibb.minCorner(); - auto& imaxc = ibb.maxCorner(); - Vertex minc, maxc; - - setX(minc, std::min(getX(pminc), getX(iminc))); - setY(minc, std::min(getY(pminc), getY(iminc))); - - setX(maxc, std::max(getX(pmaxc), getX(imaxc))); - setY(maxc, std::max(getY(pmaxc), getY(imaxc))); - return Box(minc, maxc); - } - using Edges = EdgeCache; template> @@ -935,7 +919,7 @@ private: _objfunc = [norm, binbb, pbb, ins_check](const Item& item) { auto ibb = item.boundingBox(); - auto fullbb = boundingBox(pbb, ibb); + auto fullbb = sl::boundingBox(pbb, ibb); double score = pl::distance(ibb.center(), binbb.center()); @@ -1005,14 +989,15 @@ private: auto& rofn = rawobjfunc; auto& nfpoint = getNfpPoint; + float accuracy = config_.accuracy; __parallel::enumerate( cache.corners().begin(), cache.corners().end(), - [&results, &item, &rofn, &nfpoint, ch] + [&results, &item, &rofn, &nfpoint, ch, accuracy] (double pos, size_t n) { - Optimizer solver; + Optimizer solver(accuracy); Item itemcpy = item; auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy] @@ -1059,10 +1044,10 @@ private: __parallel::enumerate(cache.corners(hidx).begin(), cache.corners(hidx).end(), [&results, &item, &nfpoint, - &rofn, ch, hidx] + &rofn, ch, hidx, accuracy] (double pos, size_t n) { - Optimizer solver; + Optimizer solver(accuracy); Item itmcpy = item; auto hole_ofn = diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index debd29024..2c4417b4d 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -65,89 +65,6 @@ using Segment = _Segment; using MultiPolygon = TMultiShape; using PackGroup = _PackGroup; -// Only for debugging. Prints the model object vertices on stdout. -//std::string toString(const Model& model, bool holes = true) { -// std::stringstream ss; - -// ss << "{\n"; - -// for(auto objptr : model.objects) { -// if(!objptr) continue; - -// auto rmesh = objptr->raw_mesh(); - -// for(auto objinst : objptr->instances) { -// if(!objinst) continue; - -// Slic3r::TriangleMesh tmpmesh = rmesh; -// // CHECK_ME -> Is the following correct ? -// tmpmesh.scale(objinst->get_scaling_factor()); -// objinst->transform_mesh(&tmpmesh); -// ExPolygons expolys = tmpmesh.horizontal_projection(); -// for(auto& expoly_complex : expolys) { - -// ExPolygons tmp = expoly_complex.simplify(scaled(1.)); -// if(tmp.empty()) continue; -// ExPolygon expoly = tmp.front(); -// expoly.contour.make_clockwise(); -// for(auto& h : expoly.holes) h.make_counter_clockwise(); - -// ss << "\t{\n"; -// ss << "\t\t{\n"; - -// for(auto v : expoly.contour.points) ss << "\t\t\t{" -// << v(0) << ", " -// << v(1) << "},\n"; -// { -// auto v = expoly.contour.points.front(); -// ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n"; -// } -// ss << "\t\t},\n"; - -// // Holes: -// ss << "\t\t{\n"; -// if(holes) for(auto h : expoly.holes) { -// ss << "\t\t\t{\n"; -// for(auto v : h.points) ss << "\t\t\t\t{" -// << v(0) << ", " -// << v(1) << "},\n"; -// { -// auto v = h.points.front(); -// ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n"; -// } -// ss << "\t\t\t},\n"; -// } -// ss << "\t\t},\n"; - -// ss << "\t},\n"; -// } -// } -// } - -// ss << "}\n"; - -// return ss.str(); -//} - -// Debugging: Save model to svg file. -//void toSVG(SVG& svg, const Model& model) { -// for(auto objptr : model.objects) { -// if(!objptr) continue; - -// auto rmesh = objptr->raw_mesh(); - -// for(auto objinst : objptr->instances) { -// if(!objinst) continue; - -// Slic3r::TriangleMesh tmpmesh = rmesh; -// tmpmesh.scale(objinst->get_scaling_factor()); -// objinst->transform_mesh(&tmpmesh); -// ExPolygons expolys = tmpmesh.horizontal_projection(); -// svg.draw(expolys); -// } -// } -//} - namespace bgi = boost::geometry::index; using SpatElement = std::pair; @@ -156,21 +73,6 @@ using ItemGroup = std::vector>; const double BIG_ITEM_TRESHOLD = 0.02; -Box boundingBox(const Box& pilebb, const Box& ibb ) { - auto& pminc = pilebb.minCorner(); - auto& pmaxc = pilebb.maxCorner(); - auto& iminc = ibb.minCorner(); - auto& imaxc = ibb.maxCorner(); - PointImpl minc, maxc; - - setX(minc, std::min(getX(pminc), getX(iminc))); - setY(minc, std::min(getY(pminc), getY(iminc))); - - setX(maxc, std::max(getX(pmaxc), getX(imaxc))); - setY(maxc, std::max(getY(pmaxc), getY(imaxc))); - return Box(minc, maxc); -} - // Fill in the placer algorithm configuration with values carefully chosen for // Slic3r. template @@ -194,13 +96,18 @@ void fillConfig(PConf& pcfg) { pcfg.parallel = true; } -// Type trait for an arranger class for different bin types (box, circle, -// polygon, etc...) -//template -//class AutoArranger {}; - -template clppr::IntPoint center(const Bin& bin) { return bin.center(); } -template<> clppr::IntPoint center(const clppr::Polygon &bin) { return sl::boundingBox(bin).center(); } +// Apply penality to object function result. This is used only when alignment +// after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN) +double fixed_overfit(const std::tuple& result, const Box &binbb) +{ + double score = std::get<0>(result); + Box pilebb = std::get<1>(result); + Box fullbb = sl::boundingBox(pilebb, binbb); + double diff = fullbb.area() - binbb.area(); + if(diff > 0) score += diff; + + return score; +} // A class encapsulating the libnest2d Nester class and extending it with other // management and spatial index structures for acceleration. @@ -218,8 +125,7 @@ protected: Packer m_pck; PConfig m_pconf; // Placement configuration TBin m_bin; - double m_bin_area; // caching - PointImpl m_bincenter; // caching + double m_bin_area; SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_smallsrtree; // spatial index for only the smaller items double m_norm; // A coefficient to scale distances @@ -234,7 +140,7 @@ protected: // as it possibly can be but at the same time, it has to provide // reasonable results. std::tuple - objfunc(const Item &item ) + objfunc(const Item &item, const clppr::IntPoint &bincenter) { const double bin_area = m_bin_area; const SpatIndex& spatindex = m_rtree; @@ -250,7 +156,7 @@ protected: auto ibb = sl::boundingBox(item.transformedShape()); // Calculate the full bounding box of the pile with the candidate item - auto fullbb = boundingBox(m_pilebb, ibb); + auto fullbb = sl::boundingBox(m_pilebb, ibb); // The bounding box of the big items (they will accumulate in the center // of the pile @@ -286,7 +192,7 @@ protected: // The smalles distance from the arranged pile center: double dist = *(std::min_element(dists.begin(), dists.end())) / m_norm; - double bindist = pl::distance(ibb.center(), m_bincenter) / m_norm; + double bindist = pl::distance(ibb.center(), bincenter) / m_norm; dist = 0.8*dist + 0.2*bindist; // Density is the pack density: how big is the arranged pile @@ -334,7 +240,7 @@ protected: Item& p = m_items[idx]; auto parea = p.area(); if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = boundingBox(p.boundingBox(), ibb); + auto bb = sl::boundingBox(p.boundingBox(), ibb); auto bbarea = bb.area(); auto ascore = 1.0 - (item.area() + parea)/bbarea; @@ -370,9 +276,7 @@ public: std::function stopcond) : m_pck(bin, dist) , m_bin(bin) - , m_bin_area(sl::area(bin)) - , m_bincenter(center(bin)) - , m_norm(std::sqrt(m_bin_area)) + , m_norm(std::sqrt(sl::area(bin))) { fillConfig(m_pconf); @@ -391,10 +295,10 @@ public: m_rtree.clear(); m_smallsrtree.clear(); - + // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { - return a/m_bin_area > BIG_ITEM_TRESHOLD ; + return a / m_bin_area > BIG_ITEM_TRESHOLD ; }; for(unsigned idx = 0; idx < items.size(); ++idx) { @@ -419,8 +323,11 @@ public: inline void preload(std::vector& fixeditems) { m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; -// m_pconf.object_function = nullptr; // drop the special objectfunction -// m_pck.preload(pg); + auto bb = sl::boundingBox(m_bin); + auto bbcenter = bb.center(); + m_pconf.object_function = [this, bb, bbcenter](const Item &item) { + return fixed_overfit(objfunc(item, bbcenter), bb); + }; // Build the rtree for queries to work @@ -442,34 +349,39 @@ public: } }; + + template<> std::function AutoArranger::get_objfn() { - return [this](const Item &itm) { - auto result = objfunc(itm); + auto bincenter = m_bin.center(); + + return [this, bincenter](const Item &itm) { + auto result = objfunc(itm, bincenter); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); - + double miss = Placer::overfit(fullbb, m_bin); miss = miss > 0? miss : 0; score += miss*miss; - + return score; }; } template<> std::function AutoArranger::get_objfn() { - return [this](const Item &item) { + auto bincenter = m_bin.center(); + return [this, bincenter](const Item &item) { + + auto result = objfunc(item, bincenter); - auto result = objfunc(item); - double score = std::get<0>(result); - + auto isBig = [this](const Item& itm) { - return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; + return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ; }; - + if(isBig(item)) { auto mp = m_merged_pile; mp.push_back(item.transformedShape()); @@ -478,109 +390,26 @@ template<> std::function AutoArranger::get_objfn() if(miss < 0) miss = 0; score += miss*miss; } - + return score; }; } -template<> std::function AutoArranger::get_objfn() +// Specialization for a generalized polygon. +// Warning: this is unfinished business. It may or may not work. +template<> +std::function AutoArranger::get_objfn() { - return [this] (const Item &item) { return std::get<0>(objfunc(item)); }; + auto bincenter = sl::boundingBox(m_bin).center(); + return [this, bincenter](const Item &item) { + return std::get<0>(objfunc(item, bincenter)); + }; } -// Arranger specialization for a Box shaped bin. -//template<> class AutoArranger: public _ArrBase { -//public: - -// AutoArranger(const Box& bin, Distance dist, -// std::function progressind = [](unsigned){}, -// std::function stopcond = [](){return false;}): -// _ArrBase(bin, dist, progressind, stopcond) -// { - -// // Here we set up the actual object function that calls the common -// // object function for all bin shapes than does an additional inside -// // check for the arranged pile. -// m_pconf.object_function = [this, bin](const Item &item) { - -// auto result = objfunc(bin.center(), item); - -// double score = std::get<0>(result); -// auto& fullbb = std::get<1>(result); - -// double miss = Placer::overfit(fullbb, bin); -// miss = miss > 0? miss : 0; -// score += miss*miss; - -// return score; -// }; - -// m_pck.configure(m_pconf); -// } -//}; - inline Circle to_lnCircle(const CircleBed& circ) { return Circle({circ.center()(0), circ.center()(1)}, circ.radius()); } -//// Arranger specialization for circle shaped bin. -//template<> class AutoArranger: public _ArrBase { -//public: - -// AutoArranger(const Circle& bin, Distance dist, -// std::function progressind = [](unsigned){}, -// std::function stopcond = [](){return false;}): -// _ArrBase(bin, dist, progressind, stopcond) { - -// // As with the box, only the inside check is different. -// m_pconf.object_function = [this, &bin](const Item &item) { - -// auto result = objfunc(bin.center(), item); - -// double score = std::get<0>(result); - -// auto isBig = [this](const Item& itm) { -// return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; -// }; - -// if(isBig(item)) { -// auto mp = m_merged_pile; -// mp.push_back(item.transformedShape()); -// auto chull = sl::convexHull(mp); -// double miss = Placer::overfit(chull, bin); -// if(miss < 0) miss = 0; -// score += miss*miss; -// } - -// return score; -// }; - -// m_pck.configure(m_pconf); -// } -//}; - -// Arranger specialization for a generalized polygon. -// Warning: this is unfinished business. It may or may not work. -//template<> class AutoArranger: public _ArrBase { -//public: -// AutoArranger(const PolygonImpl& bin, Distance dist, -// std::function progressind = [](unsigned){}, -// std::function stopcond = [](){return false;}): -// _ArrBase(bin, dist, progressind, stopcond) -// { -// m_pconf.object_function = [this, &bin] (const Item &item) { - -// auto binbb = sl::boundingBox(bin); -// auto result = objfunc(binbb.center(), item); -// double score = std::get<0>(result); - -// return score; -// }; - -// m_pck.configure(m_pconf); -// } -//}; - // Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { BedShapeHint ret; @@ -670,40 +499,46 @@ PackGroup _arrange(std::vector & shapes, { AutoArranger arranger{bin, minobjd, prind, stopfn}; - for(auto it = excludes.begin(); it != excludes.end(); ++it) - if (!sl::isInside(it->transformedShape(), bin)) - it = excludes.erase(it); - - // If there is something on the plate - if(!excludes.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. - 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); + auto it = excludes.begin(); + while (it != excludes.end()) + sl::isInside(it->transformedShape(), bin) ? + ++it : it = excludes.erase(it); - if (!arranger.is_colliding(itm)) { - itm.markAsFixed(); -// arranger.preload({{itm}}); - - // Write the transformation data into the item. The callback - // was set on the instantiation of Item and calls the - // Arrangeable interface. - it->callApplyFunction(0); - - // Remove this item, as it is arranged now - it = shapes.erase(it); - break; + // If there is something on the plate + if(!excludes.empty()) + { + arranger.preload(excludes); + auto binbb = sl::boundingBox(bin); + + // Try to put the first item to the center, as the arranger + // will not do this for us. + 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)) { + itm.markAsFixed(); + + // Write the transformation data into the item. The + // callback was set on the instantiation of Item and + // calls the Arrangeable interface. + it->callApplyFunction(0); + + // Remove this item, as it is arranged now + it = shapes.erase(it); + break; + } } } - } + + std::vector> inp; + inp.reserve(shapes.size() + excludes.size()); + for (auto &itm : shapes ) inp.emplace_back(itm); + for (auto &itm : excludes) inp.emplace_back(itm); - return arranger(shapes.begin(), shapes.end()); + return arranger(inp.begin(), inp.end()); } inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) @@ -713,8 +548,8 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) // The final client function for arrangement. A progress indicator and // a stop predicate can be also be passed to control the process. -bool arrange(ArrangeablePtrs & arrangables, - const ArrangeablePtrs & excludes, +bool arrange(ArrangeablePtrs & arrangables, + const ArrangeablePtrs & excludes, coord_t min_obj_distance, const BedShapeHint & bedhint, std::function progressind, From 320f2ecefd953cdab7089c0037bc29d0471fed35 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jul 2019 16:08:13 +0200 Subject: [PATCH 10/32] Try to deal with infinite bin. --- .../include/libnest2d/geometry_traits.hpp | 532 +++++++++--------- 1 file changed, 280 insertions(+), 252 deletions(-) diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 345252f12..8b87229c0 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -30,11 +30,11 @@ template struct ShapeTag { using Type = typename Shape::Tag; }; template using Tag = typename ShapeTag>::Type; /// Meta function to derive the contour type for a polygon which could be itself -template struct ContourType { using Type = RawShape; }; +template struct ContourType { using Type = S; }; -/// TContour instead of `typename ContourType::type` -template -using TContour = typename ContourType>::Type; +/// TContour instead of `typename ContourType::type` +template +using TContour = typename ContourType>::Type; /// Getting the type of point structure used by a shape. template struct PointType { @@ -83,12 +83,12 @@ template struct ComputeType { template using TCompute = typename ComputeType>::Type; /// A meta function to derive a container type for holes in a polygon -template -struct HolesContainer { using Type = std::vector>; }; +template +struct HolesContainer { using Type = std::vector>; }; -/// Shorthand for `typename HolesContainer::Type` -template -using THolesContainer = typename HolesContainer>::Type; +/// Shorthand for `typename HolesContainer::Type` +template +using THolesContainer = typename HolesContainer>::Type; /* * TContour, TPoint, TCoord and TCompute should be usable for any type for which @@ -132,7 +132,7 @@ enum class Orientation { COUNTER_CLOCKWISE }; -template +template struct OrientationType { // Default Polygon orientation that the library expects @@ -146,45 +146,46 @@ template inline /*constexpr*/ bool is_clockwise() { /** * \brief A point pair base class for other point pairs (segment, box, ...). - * \tparam RawPoint The actual point type to use. + * \tparam P The actual point type to use. */ -template +template struct PointPair { - RawPoint p1; - RawPoint p2; + P p1; + P p2; }; /** * \brief An abstraction of a box; */ -template -class _Box: PointPair { - using PointPair::p1; - using PointPair::p2; +template +class _Box: PointPair

{ + using PointPair

::p1; + using PointPair

::p2; public: using Tag = BoxTag; - using PointType = RawPoint; + using PointType = P; - inline _Box() = default; - inline _Box(const RawPoint& p, const RawPoint& pp): - PointPair({p, pp}) {} + inline _Box(const P& p = {TCoord

(0), TCoord

(0)}); + inline _Box(const P& p, const P& pp): + PointPair

({p, pp}) {} + + inline _Box(TCoord

width, TCoord

height, + const P& p = {TCoord

(0), TCoord

(0)});/*: + _Box(p, P{width, height}) {}*/ - inline _Box(TCoord width, TCoord height): - _Box(RawPoint{0, 0}, RawPoint{width, height}) {} + inline const P& minCorner() const BP2D_NOEXCEPT { return p1; } + inline const P& maxCorner() const BP2D_NOEXCEPT { return p2; } - inline const RawPoint& minCorner() const BP2D_NOEXCEPT { return p1; } - inline const RawPoint& maxCorner() const BP2D_NOEXCEPT { return p2; } + inline P& minCorner() BP2D_NOEXCEPT { return p1; } + inline P& maxCorner() BP2D_NOEXCEPT { return p2; } - inline RawPoint& minCorner() BP2D_NOEXCEPT { return p1; } - inline RawPoint& maxCorner() BP2D_NOEXCEPT { return p2; } + inline TCoord

width() const BP2D_NOEXCEPT; + inline TCoord

height() const BP2D_NOEXCEPT; - inline TCoord width() const BP2D_NOEXCEPT; - inline TCoord height() const BP2D_NOEXCEPT; + inline P center() const BP2D_NOEXCEPT; - inline RawPoint center() const BP2D_NOEXCEPT; - - template> + template> inline Unit area() const BP2D_NOEXCEPT { return Unit(width())*height(); } @@ -194,20 +195,20 @@ template struct PointType<_Box> { using Type = typename _Box::PointType; }; -template +template class _Circle { - RawPoint center_; + P center_; double radius_ = 0; public: using Tag = CircleTag; - using PointType = RawPoint; + using PointType = P; _Circle() = default; - _Circle(const RawPoint& center, double r): center_(center), radius_(r) {} + _Circle(const P& center, double r): center_(center), radius_(r) {} - inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; } - inline void center(const RawPoint& c) { center_ = c; } + inline const P& center() const BP2D_NOEXCEPT { return center_; } + inline void center(const P& c) { center_ = c; } inline double radius() const BP2D_NOEXCEPT { return radius_; } inline void radius(double r) { radius_ = r; } @@ -224,38 +225,38 @@ template struct PointType<_Circle> { /** * \brief An abstraction of a directed line segment with two points. */ -template -class _Segment: PointPair { - using PointPair::p1; - using PointPair::p2; +template +class _Segment: PointPair

{ + using PointPair

::p1; + using PointPair

::p2; mutable Radians angletox_ = std::nan(""); public: - using PointType = RawPoint; + using PointType = P; inline _Segment() = default; - inline _Segment(const RawPoint& p, const RawPoint& pp): - PointPair({p, pp}) {} + inline _Segment(const P& p, const P& pp): + PointPair

({p, pp}) {} /** * @brief Get the first point. * @return Returns the starting point. */ - inline const RawPoint& first() const BP2D_NOEXCEPT { return p1; } + inline const P& first() const BP2D_NOEXCEPT { return p1; } /** * @brief The end point. * @return Returns the end point of the segment. */ - inline const RawPoint& second() const BP2D_NOEXCEPT { return p2; } + inline const P& second() const BP2D_NOEXCEPT { return p2; } - inline void first(const RawPoint& p) BP2D_NOEXCEPT + inline void first(const P& p) BP2D_NOEXCEPT { angletox_ = std::nan(""); p1 = p; } - inline void second(const RawPoint& p) BP2D_NOEXCEPT { + inline void second(const P& p) BP2D_NOEXCEPT { angletox_ = std::nan(""); p2 = p; } @@ -263,7 +264,7 @@ public: inline Radians angleToXaxis() const; /// The length of the segment in the measure of the coordinate system. - template> inline Unit sqlength() const; + template> inline Unit sqlength() const; }; @@ -275,42 +276,42 @@ template struct PointType<_Segment> { // used in friend declarations. namespace pointlike { -template -inline TCoord x(const RawPoint& p) +template +inline TCoord

x(const P& p) { return p.x(); } -template -inline TCoord y(const RawPoint& p) +template +inline TCoord

y(const P& p) { return p.y(); } -template -inline TCoord& x(RawPoint& p) +template +inline TCoord

& x(P& p) { return p.x(); } -template -inline TCoord& y(RawPoint& p) +template +inline TCoord

& y(P& p) { return p.y(); } -template> -inline Unit squaredDistance(const RawPoint& p1, const RawPoint& p2) +template> +inline Unit squaredDistance(const P& p1, const P& p2) { auto x1 = Unit(x(p1)), y1 = Unit(y(p1)), x2 = Unit(x(p2)), y2 = Unit(y(p2)); Unit a = (x2 - x1), b = (y2 - y1); return a * a + b * b; } -template -inline double distance(const RawPoint& p1, const RawPoint& p2) +template +inline double distance(const P& p1, const P& p2) { - return std::sqrt(squaredDistance(p1, p2)); + return std::sqrt(squaredDistance(p1, p2)); } // create perpendicular vector @@ -339,9 +340,9 @@ inline Unit magnsq(const Pt& p) return Unit(x(p)) * x(p) + Unit(y(p)) * y(p); } -template> +template> inline std::pair horizontalDistance( - const RawPoint& p, const _Segment& s) + const P& p, const _Segment

& s) { namespace pl = pointlike; auto x = Unit(pl::x(p)), y = Unit(pl::y(p)); @@ -364,9 +365,9 @@ inline std::pair horizontalDistance( return {ret, true}; } -template> +template> inline std::pair verticalDistance( - const RawPoint& p, const _Segment& s) + const P& p, const _Segment

& s) { namespace pl = pointlike; auto x = Unit(pl::x(p)), y = Unit(pl::y(p)); @@ -390,42 +391,42 @@ inline std::pair verticalDistance( } } -template -TCoord _Box::width() const BP2D_NOEXCEPT +template +TCoord

_Box

::width() const BP2D_NOEXCEPT { return pointlike::x(maxCorner()) - pointlike::x(minCorner()); } -template -TCoord _Box::height() const BP2D_NOEXCEPT +template +TCoord

_Box

::height() const BP2D_NOEXCEPT { return pointlike::y(maxCorner()) - pointlike::y(minCorner()); } -template -TCoord getX(const RawPoint& p) { return pointlike::x(p); } +template +TCoord

getX(const P& p) { return pointlike::x

(p); } -template -TCoord getY(const RawPoint& p) { return pointlike::y(p); } +template +TCoord

getY(const P& p) { return pointlike::y

(p); } -template -void setX(RawPoint& p, const TCoord& val) +template +void setX(P& p, const TCoord

& val) { - pointlike::x(p) = val; + pointlike::x

(p) = val; } -template -void setY(RawPoint& p, const TCoord& val) +template +void setY(P& p, const TCoord

& val) { - pointlike::y(p) = val; + pointlike::y

(p) = val; } -template -inline Radians _Segment::angleToXaxis() const +template +inline Radians _Segment

::angleToXaxis() const { if(std::isnan(static_cast(angletox_))) { - TCoord dx = getX(second()) - getX(first()); - TCoord dy = getY(second()) - getY(first()); + TCoord

dx = getX(second()) - getX(first()); + TCoord

dy = getY(second()) - getY(first()); double a = std::atan2(dy, dx); auto s = std::signbit(a); @@ -436,21 +437,48 @@ inline Radians _Segment::angleToXaxis() const return angletox_; } -template +template template -inline Unit _Segment::sqlength() const +inline Unit _Segment

::sqlength() const { - return pointlike::squaredDistance(first(), second()); + return pointlike::squaredDistance(first(), second()); } -template -inline RawPoint _Box::center() const BP2D_NOEXCEPT { +template +enable_if_t::value, T> modulo(const T &v, const T &m) +{ + return 0; +} +template +enable_if_t::value, T> modulo(const T &v, const T &m) +{ + return v % m; +} + +template +inline _Box

::_Box(TCoord

width, TCoord

height, const P & center) : + PointPair

({center - P{width / 2, height / 2}, + center + P{width / 2, height / 2} + + P{modulo(width, TCoord

(2)), + modulo(height, TCoord

(2))}}) {} + +template +inline _Box

::_Box(const P& center) { + using C = TCoord

; + TCoord

M = std::max(getX(center), getY(center)) - + std::numeric_limits::lowest(); + maxCorner() = center + P{M, M}; + minCorner() = center - P{M, M}; +} + +template +inline P _Box

::center() const BP2D_NOEXCEPT { auto& minc = minCorner(); auto& maxc = maxCorner(); - using Coord = TCoord; + using Coord = TCoord

; - RawPoint ret = { // No rounding here, we dont know if these are int coords + P ret = { // No rounding here, we dont know if these are int coords Coord( (getX(minc) + getX(maxc)) / Coord(2) ), Coord( (getY(minc) + getY(maxc)) / Coord(2) ) }; @@ -467,76 +495,76 @@ enum class Formats { // used in friend declarations and can be aliased at class scope. namespace shapelike { -template -inline RawShape create(const TContour& contour, - const THolesContainer& holes) +template +inline S create(const TContour& contour, + const THolesContainer& holes) { - return RawShape(contour, holes); + return S(contour, holes); } -template -inline RawShape create(TContour&& contour, - THolesContainer&& holes) +template +inline S create(TContour&& contour, + THolesContainer&& holes) { - return RawShape(contour, holes); + return S(contour, holes); } -template -inline RawShape create(const TContour& contour) +template +inline S create(const TContour& contour) { - return create(contour, {}); + return create(contour, {}); } -template -inline RawShape create(TContour&& contour) +template +inline S create(TContour&& contour) { - return create(contour, {}); + return create(contour, {}); } -template -inline THolesContainer& holes(RawShape& /*sh*/) +template +inline THolesContainer& holes(S& /*sh*/) { - static THolesContainer empty; + static THolesContainer empty; return empty; } -template -inline const THolesContainer& holes(const RawShape& /*sh*/) +template +inline const THolesContainer& holes(const S& /*sh*/) { - static THolesContainer empty; + static THolesContainer empty; return empty; } -template -inline TContour& hole(RawShape& sh, unsigned long idx) +template +inline TContour& hole(S& sh, unsigned long idx) { return holes(sh)[idx]; } -template -inline const TContour& hole(const RawShape& sh, unsigned long idx) +template +inline const TContour& hole(const S& sh, unsigned long idx) { return holes(sh)[idx]; } -template -inline size_t holeCount(const RawShape& sh) +template +inline size_t holeCount(const S& sh) { return holes(sh).size(); } -template -inline TContour& contour(RawShape& sh) +template +inline TContour& contour(S& sh) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::contour() unimplemented!"); return sh; } -template -inline const TContour& contour(const RawShape& sh) +template +inline const TContour& contour(const S& sh) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::contour() unimplemented!"); return sh; } @@ -548,71 +576,71 @@ inline void reserve(RawPath& p, size_t vertex_capacity, const PathTag&) p.reserve(vertex_capacity); } -template -inline void addVertex(RawShape& sh, const PathTag&, Args...args) +template +inline void addVertex(S& sh, const PathTag&, Args...args) { sh.emplace_back(std::forward(args)...); } -template -inline void foreachVertex(RawShape& sh, Fn fn, const PathTag&) { +template +inline void foreachVertex(S& sh, Fn fn, const PathTag&) { std::for_each(sh.begin(), sh.end(), fn); } -template -inline typename RawShape::iterator begin(RawShape& sh, const PathTag&) +template +inline typename S::iterator begin(S& sh, const PathTag&) { return sh.begin(); } -template -inline typename RawShape::iterator end(RawShape& sh, const PathTag&) +template +inline typename S::iterator end(S& sh, const PathTag&) { return sh.end(); } -template -inline typename RawShape::const_iterator -cbegin(const RawShape& sh, const PathTag&) +template +inline typename S::const_iterator +cbegin(const S& sh, const PathTag&) { return sh.cbegin(); } -template -inline typename RawShape::const_iterator -cend(const RawShape& sh, const PathTag&) +template +inline typename S::const_iterator +cend(const S& sh, const PathTag&) { return sh.cend(); } -template -inline std::string toString(const RawShape& /*sh*/) +template +inline std::string toString(const S& /*sh*/) { return ""; } -template -inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) +template +inline std::string serialize(const S& /*sh*/, double /*scale*/=1) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::serialize() unimplemented!"); return ""; } -template -inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/) +template +inline void unserialize(S& /*sh*/, const std::string& /*str*/) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::unserialize() unimplemented!"); } template inline Unit area(const Cntr& poly, const PathTag& ); -template -inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) +template +inline bool intersects(const S& /*sh*/, const S& /*sh*/) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::intersects() unimplemented!"); return false; } @@ -633,29 +661,29 @@ inline bool isInside(const TGuest&, const THost&, return false; } -template -inline bool touches( const RawShape& /*shape*/, - const RawShape& /*shape*/) +template +inline bool touches( const S& /*shape*/, + const S& /*shape*/) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::touches(shape, shape) unimplemented!"); return false; } -template -inline bool touches( const TPoint& /*point*/, - const RawShape& /*shape*/) +template +inline bool touches( const TPoint& /*point*/, + const S& /*shape*/) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::touches(point, shape) unimplemented!"); return false; } -template -inline _Box> boundingBox(const RawShape& /*sh*/, +template +inline _Box> boundingBox(const S& /*sh*/, const PathTag&) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::boundingBox(shape) unimplemented!"); } @@ -667,34 +695,34 @@ boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) "shapelike::boundingBox(shapes) unimplemented!"); } -template -inline RawShape convexHull(const RawShape& sh, const PathTag&); +template +inline S convexHull(const S& sh, const PathTag&); template inline S convexHull(const RawShapes& sh, const MultiPolygonTag&); -template -inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/) +template +inline void rotate(S& /*sh*/, const Radians& /*rads*/) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::rotate() unimplemented!"); } -template -inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) +template +inline void translate(S& /*sh*/, const P& /*offs*/) { - static_assert(always_false::value, + static_assert(always_false::value, "shapelike::translate() unimplemented!"); } -template -inline void offset(RawShape& /*sh*/, TCoord> /*distance*/) +template +inline void offset(S& /*sh*/, TCoord> /*distance*/) { dout() << "The current geometry backend does not support offsetting!\n"; } -template -inline std::pair isValid(const RawShape& /*sh*/) +template +inline std::pair isValid(const S& /*sh*/) { return {false, "shapelike::isValid() unimplemented!"}; } @@ -735,56 +763,56 @@ template inline bool isConvex(const RawPath& sh, const PathTag&) // No need to implement these // ***************************************************************************** -template -inline typename TContour::iterator -begin(RawShape& sh, const PolygonTag&) +template +inline typename TContour::iterator +begin(S& sh, const PolygonTag&) { return begin(contour(sh), PathTag()); } -template // Tag dispatcher -inline auto begin(RawShape& sh) -> decltype(begin(sh, Tag())) +template // Tag dispatcher +inline auto begin(S& sh) -> decltype(begin(sh, Tag())) { - return begin(sh, Tag()); + return begin(sh, Tag()); } -template -inline typename TContour::const_iterator -cbegin(const RawShape& sh, const PolygonTag&) +template +inline typename TContour::const_iterator +cbegin(const S& sh, const PolygonTag&) { return cbegin(contour(sh), PathTag()); } -template // Tag dispatcher -inline auto cbegin(const RawShape& sh) -> decltype(cbegin(sh, Tag())) +template // Tag dispatcher +inline auto cbegin(const S& sh) -> decltype(cbegin(sh, Tag())) { - return cbegin(sh, Tag()); + return cbegin(sh, Tag()); } -template -inline typename TContour::iterator -end(RawShape& sh, const PolygonTag&) +template +inline typename TContour::iterator +end(S& sh, const PolygonTag&) { return end(contour(sh), PathTag()); } -template // Tag dispatcher -inline auto end(RawShape& sh) -> decltype(begin(sh, Tag())) +template // Tag dispatcher +inline auto end(S& sh) -> decltype(begin(sh, Tag())) { - return end(sh, Tag()); + return end(sh, Tag()); } -template -inline typename TContour::const_iterator -cend(const RawShape& sh, const PolygonTag&) +template +inline typename TContour::const_iterator +cend(const S& sh, const PolygonTag&) { return cend(contour(sh), PathTag()); } -template // Tag dispatcher -inline auto cend(const RawShape& sh) -> decltype(cend(sh, Tag())) +template // Tag dispatcher +inline auto cend(const S& sh) -> decltype(cend(sh, Tag())) { - return cend(sh, Tag()); + return cend(sh, Tag()); } template std::reverse_iterator _backward(It iter) { @@ -817,8 +845,8 @@ template TPoint

back (const P& p) { } // Optional, does nothing by default -template -inline void reserve(RawShape& sh, size_t vertex_capacity, const PolygonTag&) +template +inline void reserve(S& sh, size_t vertex_capacity, const PolygonTag&) { reserve(contour(sh), vertex_capacity, PathTag()); } @@ -828,20 +856,20 @@ inline void reserve(T& sh, size_t vertex_capacity) { reserve(sh, vertex_capacity, Tag()); } -template -inline void addVertex(RawShape& sh, const PolygonTag&, Args...args) +template +inline void addVertex(S& sh, const PolygonTag&, Args...args) { addVertex(contour(sh), PathTag(), std::forward(args)...); } -template // Tag dispatcher -inline void addVertex(RawShape& sh, Args...args) +template // Tag dispatcher +inline void addVertex(S& sh, Args...args) { - addVertex(sh, Tag(), std::forward(args)...); + addVertex(sh, Tag(), std::forward(args)...); } -template -inline _Box> boundingBox(const RawShape& poly, const PolygonTag&) +template +inline _Box> boundingBox(const S& poly, const PolygonTag&) { return boundingBox(contour(poly), PathTag()); } @@ -938,40 +966,40 @@ template inline double area(const S& poly, const PolygonTag& ) }); } -template // Dispatching function -inline double area(const RawShape& sh) +template // Dispatching function +inline double area(const S& sh) { - return area(sh, Tag()); + return area(sh, Tag()); } template inline double area(const RawShapes& shapes, const MultiPolygonTag&) { - using RawShape = typename RawShapes::value_type; + using S = typename RawShapes::value_type; return std::accumulate(shapes.begin(), shapes.end(), 0.0, - [](double a, const RawShape& b) { + [](double a, const S& b) { return a += area(b); }); } -template -inline RawShape convexHull(const RawShape& sh, const PolygonTag&) +template +inline S convexHull(const S& sh, const PolygonTag&) { - return create(convexHull(contour(sh), PathTag())); + return create(convexHull(contour(sh), PathTag())); } -template -inline auto convexHull(const RawShape& sh) - -> decltype(convexHull(sh, Tag())) // TODO: C++14 could deduce +template +inline auto convexHull(const S& sh) + -> decltype(convexHull(sh, Tag())) // TODO: C++14 could deduce { - return convexHull(sh, Tag()); + return convexHull(sh, Tag()); } -template -inline RawShape convexHull(const RawShape& sh, const PathTag&) +template +inline S convexHull(const S& sh, const PathTag&) { - using Unit = TCompute; - using Point = TPoint; + using Unit = TCompute; + using Point = TPoint; namespace sl = shapelike; size_t edges = sl::cend(sh) - sl::cbegin(sh); @@ -1016,8 +1044,8 @@ inline RawShape convexHull(const RawShape& sh, const PathTag&) ++ik; } - RawShape ret; reserve(ret, U.size() + L.size()); - if(is_clockwise()) { + S ret; reserve(ret, U.size() + L.size()); + if(is_clockwise()) { for(auto it = U.begin(); it != std::prev(U.end()); ++it) addVertex(ret, *it); for(auto it = L.rbegin(); it != std::prev(L.rend()); ++it) @@ -1068,11 +1096,11 @@ inline bool isInside(const TP& point, const TB& box, return px > minx && px < maxx && py > miny && py < maxy; } -template -inline bool isInside(const RawShape& sh, const TC& circ, +template +inline bool isInside(const S& sh, const TC& circ, const PolygonTag&, const CircleTag&) { - return std::all_of(cbegin(sh), cend(sh), [&circ](const TPoint& p) + return std::all_of(cbegin(sh), cend(sh), [&circ](const TPoint& p) { return isInside(p, circ, PointTag(), CircleTag()); }); @@ -1103,8 +1131,8 @@ inline bool isInside(const TBGuest& ibb, const TBHost& box, return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY; } -template -inline bool isInside(const RawShape& poly, const TB& box, +template +inline bool isInside(const S& poly, const TB& box, const PolygonTag&, const BoxTag&) { return isInside(boundingBox(poly), box, BoxTag(), BoxTag()); @@ -1115,36 +1143,36 @@ inline bool isInside(const TGuest& guest, const THost& host) { return isInside(guest, host, Tag(), Tag()); } -template // Potential O(1) implementation may exist -inline TPoint& vertex(RawShape& sh, unsigned long idx, +template // Potential O(1) implementation may exist +inline TPoint& vertex(S& sh, unsigned long idx, const PolygonTag&) { return *(shapelike::begin(contour(sh)) + idx); } -template // Potential O(1) implementation may exist -inline TPoint& vertex(RawShape& sh, unsigned long idx, +template // Potential O(1) implementation may exist +inline TPoint& vertex(S& sh, unsigned long idx, const PathTag&) { return *(shapelike::begin(sh) + idx); } -template // Potential O(1) implementation may exist -inline TPoint& vertex(RawShape& sh, unsigned long idx) +template // Potential O(1) implementation may exist +inline TPoint& vertex(S& sh, unsigned long idx) { - return vertex(sh, idx, Tag()); + return vertex(sh, idx, Tag()); } -template // Potential O(1) implementation may exist -inline const TPoint& vertex(const RawShape& sh, +template // Potential O(1) implementation may exist +inline const TPoint& vertex(const S& sh, unsigned long idx, const PolygonTag&) { return *(shapelike::cbegin(contour(sh)) + idx); } -template // Potential O(1) implementation may exist -inline const TPoint& vertex(const RawShape& sh, +template // Potential O(1) implementation may exist +inline const TPoint& vertex(const S& sh, unsigned long idx, const PathTag&) { @@ -1152,28 +1180,28 @@ inline const TPoint& vertex(const RawShape& sh, } -template // Potential O(1) implementation may exist -inline const TPoint& vertex(const RawShape& sh, +template // Potential O(1) implementation may exist +inline const TPoint& vertex(const S& sh, unsigned long idx) { - return vertex(sh, idx, Tag()); + return vertex(sh, idx, Tag()); } -template -inline size_t contourVertexCount(const RawShape& sh) +template +inline size_t contourVertexCount(const S& sh) { return shapelike::cend(sh) - shapelike::cbegin(sh); } -template -inline void foreachVertex(RawShape& sh, Fn fn, const PolygonTag&) { +template +inline void foreachVertex(S& sh, Fn fn, const PolygonTag&) { foreachVertex(contour(sh), fn, PathTag()); for(auto& h : holes(sh)) foreachVertex(h, fn, PathTag()); } -template -inline void foreachVertex(RawShape& sh, Fn fn) { - foreachVertex(sh, fn, Tag()); +template +inline void foreachVertex(S& sh, Fn fn) { + foreachVertex(sh, fn, Tag()); } template inline bool isConvex(const Poly& sh, const PolygonTag&) @@ -1184,9 +1212,9 @@ template inline bool isConvex(const Poly& sh, const PolygonTag&) return convex; } -template inline bool isConvex(const RawShape& sh) // dispatch +template inline bool isConvex(const S& sh) // dispatch { - return isConvex(sh, Tag()); + return isConvex(sh, Tag()); } } From bc315f4c2c87bc6a1262696be96df29af4147d6c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Jul 2019 15:06:10 +0200 Subject: [PATCH 11/32] Deal with infinite box. --- .../libnest2d/backends/clipper/geometries.hpp | 8 +- .../include/libnest2d/geometry_traits.hpp | 25 +- .../include/libnest2d/placers/nfpplacer.hpp | 8 +- src/libnest2d/tests/test.cpp | 1 + src/libslic3r/Arrange.cpp | 258 ++++++++++-------- src/libslic3r/Model.cpp | 3 + 6 files changed, 175 insertions(+), 128 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index e9fad405b..c51e0f5c9 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -41,25 +41,25 @@ template<> struct HolesContainer { using Type = ClipperLib::Paths; namespace pointlike { // Tell libnest2d how to extract the X coord from a ClipperPoint object -template<> inline TCoord x(const PointImpl& p) +template<> inline ClipperLib::cInt x(const PointImpl& p) { return p.X; } // Tell libnest2d how to extract the Y coord from a ClipperPoint object -template<> inline TCoord y(const PointImpl& p) +template<> inline ClipperLib::cInt y(const PointImpl& p) { return p.Y; } // Tell libnest2d how to extract the X coord from a ClipperPoint object -template<> inline TCoord& x(PointImpl& p) +template<> inline ClipperLib::cInt& x(PointImpl& p) { return p.X; } // Tell libnest2d how to extract the Y coord from a ClipperPoint object -template<> inline TCoord& y(PointImpl& p) +template<> inline ClipperLib::cInt& y(PointImpl& p) { return p.Y; } diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 8b87229c0..5c213c110 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -166,7 +166,9 @@ public: using Tag = BoxTag; using PointType = P; - inline _Box(const P& p = {TCoord

(0), TCoord

(0)}); + inline _Box(const P& center = {TCoord

(0), TCoord

(0)}): + _Box(TCoord

(0), TCoord

(0), center) {} + inline _Box(const P& p, const P& pp): PointPair

({p, pp}) {} @@ -189,6 +191,8 @@ public: inline Unit area() const BP2D_NOEXCEPT { return Unit(width())*height(); } + + static inline _Box infinite(const P ¢er); }; template struct PointType<_Box> { @@ -463,12 +467,19 @@ inline _Box

::_Box(TCoord

width, TCoord

height, const P & center) : modulo(height, TCoord

(2))}}) {} template -inline _Box

::_Box(const P& center) { +inline _Box

_Box

::infinite(const P& center) { using C = TCoord

; - TCoord

M = std::max(getX(center), getY(center)) - - std::numeric_limits::lowest(); - maxCorner() = center + P{M, M}; - minCorner() = center - P{M, M}; + _Box

ret; + + // It is important for Mx and My to be strictly less than half of the + // range of type C. width(), height() and area() will not overflow this way. + C Mx = C((std::numeric_limits::lowest() + 2 * getX(center)) / 2.01); + C My = C((std::numeric_limits::lowest() + 2 * getY(center)) / 2.01); + + ret.maxCorner() = center - P{Mx, My}; + ret.minCorner() = center + P{Mx, My}; + + return ret; } template @@ -478,7 +489,7 @@ inline P _Box

::center() const BP2D_NOEXCEPT { using Coord = TCoord

; - P ret = { // No rounding here, we dont know if these are int coords + P ret = { // No rounding here, we dont know if these are int coords Coord( (getX(minc) + getX(maxc)) / Coord(2) ), Coord( (getY(minc) + getY(maxc)) / Coord(2) ) }; diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index b94443bff..4ef1fe71d 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -581,8 +581,12 @@ public: static inline double overfit(const Box& bb, const Box& bin) { - auto wdiff = double(bb.width() - bin.width()); - auto hdiff = double(bb.height() - bin.height()); + auto Bw = bin.width(); + auto Bh = bin.height(); + auto mBw = -Bw; + auto mBh = -Bh; + auto wdiff = double(bb.width()) + mBw; + auto hdiff = double(bb.height()) + mBh; double diff = 0; if(wdiff > 0) diff += wdiff; if(hdiff > 0) diff += hdiff; diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index 72a600dbb..d0c660e49 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -379,6 +379,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) for(Item& r2 : result) { if(&r1 != &r2 ) { valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); + ASSERT_TRUE(valid); valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); ASSERT_TRUE(valid); } diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 2c4417b4d..84a406fd9 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -58,19 +58,24 @@ namespace arrangement { using namespace libnest2d; namespace clppr = ClipperLib; +// Get the libnest2d types for clipper backend using Item = _Item; using Box = _Box; using Circle = _Circle; using Segment = _Segment; using MultiPolygon = TMultiShape; + +// The return value of nesting, a vector (for each logical bed) of Item +// reference vectors. using PackGroup = _PackGroup; +// Summon the spatial indexing facilities from boost namespace bgi = boost::geometry::index; - using SpatElement = std::pair; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; using ItemGroup = std::vector>; +// A coefficient used in separating bigger items and smaller items. const double BIG_ITEM_TRESHOLD = 0.02; // Fill in the placer algorithm configuration with values carefully chosen for @@ -85,14 +90,14 @@ void fillConfig(PConf& pcfg) { pcfg.starting_point = PConf::Alignment::CENTER; // TODO cannot use rotations until multiple objects of same geometry can - // handle different rotations - // arranger.useMinimumBoundigBoxRotation(); + // handle different rotations. pcfg.rotations = { 0.0 }; // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well pcfg.accuracy = 0.65f; - + + // Allow parallel execution. pcfg.parallel = true; } @@ -153,7 +158,7 @@ protected: }; // Candidate item bounding box - auto ibb = sl::boundingBox(item.transformedShape()); + auto ibb = item.boundingBox(); // Calculate the full bounding box of the pile with the candidate item auto fullbb = sl::boundingBox(m_pilebb, ibb); @@ -170,16 +175,39 @@ protected: // Will hold the resulting score double score = 0; - if(isBig(item.area()) || spatindex.empty()) { - // This branch is for the bigger items.. + // Density is the pack density: how big is the arranged pile + double density = 0; + + const double N = m_norm; + auto norm = [N](double val) { return val / N; }; + + // Distinction of cases for the arrangement scene + enum e_cases { + // This branch is for big items in a mixed (big and small) scene + // OR for all items in a small-only scene. + BIG_ITEM, - auto minc = ibb.minCorner(); // bottom left corner - auto maxc = ibb.maxCorner(); // top right corner + // This branch is for the last big item in a mixed scene + LAST_BIG_ITEM, + // For small items in a mixed scene. + SMALL_ITEM + } compute_case; + + bool bigitems = isBig(item.area()) || spatindex.empty(); + if(bigitems && !remaining.empty()) compute_case = BIG_ITEM; + else if (bigitems && remaining.empty()) compute_case = LAST_BIG_ITEM; + else compute_case = SMALL_ITEM; + + switch (compute_case) { + case BIG_ITEM: { + const clppr::IntPoint& minc = ibb.minCorner(); // bottom left corner + const clppr::IntPoint& maxc = ibb.maxCorner(); // top right corner + // top left and bottom right corners - auto top_left = PointImpl{getX(minc), getY(maxc)}; - auto bottom_right = PointImpl{getX(maxc), getY(minc)}; - + clppr::IntPoint top_left{getX(minc), getY(maxc)}; + clppr::IntPoint bottom_right{getX(maxc), getY(minc)}; + // Now the distance of the gravity center will be calculated to the // five anchor points and the smallest will be chosen. std::array dists; @@ -189,79 +217,75 @@ protected: dists[2] = pl::distance(ibb.center(), cc); dists[3] = pl::distance(top_left, cc); dists[4] = pl::distance(bottom_right, cc); - - // The smalles distance from the arranged pile center: - double dist = *(std::min_element(dists.begin(), dists.end())) / m_norm; - double bindist = pl::distance(ibb.center(), bincenter) / m_norm; - dist = 0.8*dist + 0.2*bindist; - - // Density is the pack density: how big is the arranged pile - double density = 0; - - if(remaining.empty()) { - - auto mp = m_merged_pile; - mp.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(mp); - - placers::EdgeCache ec(chull); - - double circ = ec.circumference() / m_norm; - double bcirc = 2.0*(fullbb.width() + fullbb.height()) / m_norm; - score = 0.5*circ + 0.5*bcirc; - - } else { - // Prepare a variable for the alignment score. - // This will indicate: how well is the candidate item - // aligned with its neighbors. We will check the alignment - // with all neighbors and return the score for the best - // alignment. So it is enough for the candidate to be - // aligned with only one item. - auto alignment_score = 1.0; - - auto querybb = item.boundingBox(); - density = std::sqrt((fullbb.width() / m_norm )* - (fullbb.height() / m_norm)); - - // Query the spatial index for the neighbors - std::vector result; - result.reserve(spatindex.size()); - if(isBig(item.area())) { - spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); - } else { - smalls_spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); - } - - // now get the score for the best alignment - for(auto& e : result) { - auto idx = e.second; - Item& p = m_items[idx]; - auto parea = p.area(); - if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = sl::boundingBox(p.boundingBox(), ibb); - auto bbarea = bb.area(); - auto ascore = 1.0 - (item.area() + parea)/bbarea; - - if(ascore < alignment_score) alignment_score = ascore; - } - } - // The final mix of the score is the balance between the - // distance from the full pile center, the pack density and - // the alignment with the neighbors - if (result.empty()) - score = 0.5 * dist + 0.5 * density; - else - score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; + // The smalles distance from the arranged pile center: + double dist = norm(*(std::min_element(dists.begin(), dists.end()))); + double bindist = norm(pl::distance(ibb.center(), bincenter)); + dist = 0.8 * dist + 0.2*bindist; + + // Prepare a variable for the alignment score. + // This will indicate: how well is the candidate item + // aligned with its neighbors. We will check the alignment + // with all neighbors and return the score for the best + // alignment. So it is enough for the candidate to be + // aligned with only one item. + auto alignment_score = 1.0; + + auto query = bgi::intersects(ibb); + auto& index = isBig(item.area()) ? spatindex : smalls_spatindex; + + // Query the spatial index for the neighbors + std::vector result; + result.reserve(index.size()); + + index.query(query, std::back_inserter(result)); + + // now get the score for the best alignment + for(auto& e : result) { + auto idx = e.second; + Item& p = m_items[idx]; + auto parea = p.area(); + if(std::abs(1.0 - parea/item.area()) < 1e-6) { + auto bb = sl::boundingBox(p.boundingBox(), ibb); + auto bbarea = bb.area(); + auto ascore = 1.0 - (item.area() + parea)/bbarea; + + if(ascore < alignment_score) alignment_score = ascore; + } } - } else { + + density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height())); + + // The final mix of the score is the balance between the + // distance from the full pile center, the pack density and + // the alignment with the neighbors + if (result.empty()) + score = 0.5 * dist + 0.5 * density; + else + score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; + + break; + } + case LAST_BIG_ITEM: { + auto mp = m_merged_pile; + mp.emplace_back(item.transformedShape()); + auto chull = sl::convexHull(mp); + + placers::EdgeCache ec(chull); + + double circ = norm(ec.circumference()); + double bcirc = 2.0 * norm(fullbb.width() + fullbb.height()); + score = 0.5 * circ + 0.5 * bcirc; + break; + } + case SMALL_ITEM: { // Here there are the small items that should be placed around the // already processed bigger items. // No need to play around with the anchor points, the center will be // just fine for small items - score = pl::distance(ibb.center(), bigbb.center()) / m_norm; + score = norm(pl::distance(ibb.center(), bigbb.center())); + break; + } } return std::make_tuple(score, fullbb); @@ -276,7 +300,8 @@ public: std::function stopcond) : m_pck(bin, dist) , m_bin(bin) - , m_norm(std::sqrt(sl::area(bin))) + , m_bin_area(sl::area(bin)) + , m_norm(std::sqrt(m_bin_area)) { fillConfig(m_pconf); @@ -349,8 +374,6 @@ public: } }; - - template<> std::function AutoArranger::get_objfn() { auto bincenter = m_bin.center(); @@ -612,46 +635,51 @@ bool arrange(ArrangeablePtrs & arrangables, auto& cfn = stopcondition; switch (bedhint.type) { - case BedShapeType::BOX: { - // 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()); +// case BedShapeType::BOX: { +// // 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()); - _arrange(items, fixeditems, binbb, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; - auto cc = to_lnCircle(c); - binwidth = scaled(c.radius()); +// _arrange(items, fixeditems, binbb, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::CIRCLE: { +// auto c = bedhint.shape.circ; +// auto cc = to_lnCircle(c); +// binwidth = scaled(c.radius()); - _arrange(items, fixeditems, cc, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::IRREGULAR: { - auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); - auto irrbed = sl::create(std::move(ctour)); - BoundingBox polybb(bedhint.shape.polygon); - binwidth = (polybb.max(X) - polybb.min(X)); +// _arrange(items, fixeditems, cc, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::IRREGULAR: { +// auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); +// auto irrbed = sl::create(std::move(ctour)); +// BoundingBox polybb(bedhint.shape.polygon); +// binwidth = (polybb.max(X) - polybb.min(X)); - _arrange(items, fixeditems, irrbed, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::INFINITE: { - // const InfiniteBed& nobin = bedhint.shape.infinite; - //Box infbb{{nobin.center.x(), nobin.center.y()}}; - Box infbb; +// _arrange(items, fixeditems, irrbed, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::INFINITE: { +// const InfiniteBed& nobin = bedhint.shape.infinite; +// Box infbb{{nobin.center.x(), nobin.center.y()}}; +// _arrange(items, fixeditems, 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, fixeditems, Box{}, min_obj_distance, progressind, cfn); +// break; +// } + default: { + Box infbb = Box::infinite({bedhint.shape.box.center().x(), bedhint.shape.box.center().y()}); + _arrange(items, fixeditems, 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, fixeditems, Box{}, min_obj_distance, progressind, cfn); - break; - } }; if(stopcondition()) return false; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index fd48dcec4..2af4e4c27 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -398,6 +398,9 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) // } // o->invalidate_bounding_box(); // } + +// return true; + size_t count = 0; for (auto obj : objects) count += obj->instances.size(); From b5215dae1be313c8bd2d62f89594e72853ebea82 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Jul 2019 17:05:08 +0200 Subject: [PATCH 12/32] Fix libnest2d tests --- .../libnest2d/placers/bottomleftplacer.hpp | 10 +++++----- .../libnest2d/placers/placer_boilerplate.hpp | 2 -- src/libnest2d/tests/test.cpp | 18 +++++++++++++++--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp b/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp index 28aaad5ce..e1a4ffbd9 100644 --- a/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp @@ -68,11 +68,11 @@ public: return toWallPoly(item, Dir::DOWN); } - inline Unit availableSpaceLeft(const Item& item) { + inline Coord availableSpaceLeft(const Item& item) { return availableSpace(item, Dir::LEFT); } - inline Unit availableSpaceDown(const Item& item) { + inline Coord availableSpaceDown(const Item& item) { return availableSpace(item, Dir::DOWN); } @@ -83,7 +83,7 @@ protected: // Get initial position for item in the top right corner setInitialPosition(item); - Unit d = availableSpaceDown(item); + Coord d = availableSpaceDown(item); auto eps = config_.epsilon; bool can_move = d > eps; bool can_be_packed = can_move; @@ -179,7 +179,7 @@ protected: return ret; } - Unit availableSpace(const Item& _item, const Dir dir) { + Coord availableSpace(const Item& _item, const Dir dir) { Item item (_item.transformedShape()); @@ -223,7 +223,7 @@ protected: cmp); // Get the initial distance in floating point - Unit m = getCoord(*minvertex_it); + Coord m = getCoord(*minvertex_it); // Check available distance for every vertex of item to the objects // in the way for the nearest intersection diff --git a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp index abcd86183..26681aeec 100644 --- a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp @@ -18,7 +18,6 @@ public: using Segment = _Segment; using BinType = TBin; using Coord = TCoord; - using Unit = Coord; using Config = Cfg; using ItemGroup = _ItemGroup; using DefaultIter = typename ItemGroup::const_iterator; @@ -131,7 +130,6 @@ using typename Base::Vertex; \ using typename Base::Segment; \ using typename Base::PackResult; \ using typename Base::Coord; \ -using typename Base::Unit; \ private: } diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index d0c660e49..e5bd87182 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -363,8 +363,14 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) {5, 5}, {20, 20} }; + Box bin(210, 250, {105, 125}); - Nester arrange(Box(210, 250)); + ASSERT_EQ(bin.width(), 210); + ASSERT_EQ(bin.height(), 250); + ASSERT_EQ(getX(bin.center()), 105); + ASSERT_EQ(getY(bin.center()), 125); + + Nester arrange(bin); auto groups = arrange.execute(rects.begin(), rects.end()); @@ -416,10 +422,16 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) {5, 5}, {20, 20} }; + Box bin(210, 250, {105, 125}); + + ASSERT_EQ(bin.width(), 210); + ASSERT_EQ(bin.height(), 250); + ASSERT_EQ(getX(bin.center()), 105); + ASSERT_EQ(getY(bin.center()), 125); + Coord min_obj_distance = 5; - Nester arrange(Box(210, 250), - min_obj_distance); + Nester arrange(bin, min_obj_distance); auto groups = arrange.execute(rects.begin(), rects.end()); From e81f8a5fd929f7fb2eaab80b0534ee3b49ecfa67 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Jul 2019 19:24:41 +0200 Subject: [PATCH 13/32] WIP still with arrange return value. --- .../include/libnest2d/geometry_traits.hpp | 2 +- src/libnest2d/include/libnest2d/libnest2d.hpp | 3 +- src/libslic3r/Arrange.cpp | 88 +++++++++---------- src/libslic3r/Arrange.hpp | 2 +- src/libslic3r/Model.hpp | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 52 +++++------ 8 files changed, 78 insertions(+), 79 deletions(-) diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 5c213c110..c4f2fcaca 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -192,7 +192,7 @@ public: return Unit(width())*height(); } - static inline _Box infinite(const P ¢er); + static inline _Box infinite(const P ¢er = {TCoord

(0), TCoord

(0)}); }; template struct PointType<_Box> { diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index ab018f3f8..f106ad3d8 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -908,7 +908,8 @@ private: item.removeOffset(); }); - if(stopfn_ && !stopfn_()) { // Ignore results if nesting was stopped. + if(!stopfn_ || (stopfn_ && !stopfn_())) { + // Ignore results if nesting was stopped. const PackGroup& bins = lastResult(); unsigned binidx = 0; for(auto& bin : bins) { diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 84a406fd9..f778a7f75 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -573,7 +573,7 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) // a stop predicate can be also be passed to control the process. bool arrange(ArrangeablePtrs & arrangables, const ArrangeablePtrs & excludes, - coord_t min_obj_distance, + coord_t min_obj_dist, const BedShapeHint & bedhint, std::function progressind, std::function stopcondition) @@ -615,13 +615,14 @@ bool arrange(ArrangeablePtrs & arrangables, arrangeable, items, // callback called by arrange to apply the result on the arrangeable - [arrangeable, &binwidth](const Item &itm, unsigned binidx) { + [arrangeable, &binwidth, &ret](const Item &itm, unsigned binidx) { + ret = !binidx; // Return value false more bed is required clppr::cInt stride = binidx * stride_padding(binwidth); clppr::IntPoint offs = itm.translation(); arrangeable->apply_arrange_result({unscaled(offs.X + stride), unscaled(offs.Y)}, - itm.rotation()); + itm.rotation(), binidx); }); } @@ -629,66 +630,61 @@ bool arrange(ArrangeablePtrs & arrangables, process_arrangeable(fixed, fixeditems, nullptr); // Integer ceiling the min distance from the bed perimeters - coord_t md = min_obj_distance - SCALED_EPSILON; + coord_t md = min_obj_dist - SCALED_EPSILON; md = (md % 2) ? md / 2 + 1 : md / 2; - auto& cfn = stopcondition; + auto &cfn = stopcondition; + auto &pri = progressind; switch (bedhint.type) { -// case BedShapeType::BOX: { -// // 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()); + case BedShapeType::BOX: { + // 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()); -// _arrange(items, fixeditems, binbb, min_obj_distance, progressind, cfn); -// break; -// } -// case BedShapeType::CIRCLE: { -// auto c = bedhint.shape.circ; -// auto cc = to_lnCircle(c); -// binwidth = scaled(c.radius()); + _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn); + break; + } + case BedShapeType::CIRCLE: { + auto c = bedhint.shape.circ; + auto cc = to_lnCircle(c); + binwidth = scaled(c.radius()); -// _arrange(items, fixeditems, cc, min_obj_distance, progressind, cfn); -// break; -// } -// case BedShapeType::IRREGULAR: { -// auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); -// auto irrbed = sl::create(std::move(ctour)); -// BoundingBox polybb(bedhint.shape.polygon); -// binwidth = (polybb.max(X) - polybb.min(X)); + _arrange(items, fixeditems, cc, min_obj_dist, pri, cfn); + break; + } + case BedShapeType::IRREGULAR: { + auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); + auto irrbed = sl::create(std::move(ctour)); + BoundingBox polybb(bedhint.shape.polygon); + binwidth = (polybb.max(X) - polybb.min(X)); -// _arrange(items, fixeditems, irrbed, min_obj_distance, progressind, cfn); -// break; -// } -// case BedShapeType::INFINITE: { -// const InfiniteBed& nobin = bedhint.shape.infinite; -// Box infbb{{nobin.center.x(), nobin.center.y()}}; + _arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn); + break; + } + case BedShapeType::INFINITE: { + const InfiniteBed& nobin = bedhint.shape.infinite; + auto infbb = Box::infinite({nobin.center.x(), nobin.center.y()}); -// _arrange(items, fixeditems, 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, fixeditems, Box{}, min_obj_distance, progressind, cfn); -// break; -// } - default: { - Box infbb = Box::infinite({bedhint.shape.box.center().x(), bedhint.shape.box.center().y()}); - - _arrange(items, fixeditems, infbb, min_obj_distance, progressind, cfn); + _arrange(items, fixeditems, infbb, min_obj_dist, pri, cfn); + break; + } + case BedShapeType::UNKNOWN: { + // We know nothing about the bed, let it be infinite and zero centered + _arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn); break; } }; - if(stopcondition()) return false; + if(stopcondition && stopcondition()) return false; return ret; } // Arrange, without the fixed items (excludes) -bool arrange(ArrangeablePtrs & inp, +bool arrange(ArrangeablePtrs & inp, coord_t min_d, const BedShapeHint & bedhint, std::function prfn, diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 87514a600..337a7d959 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -58,7 +58,7 @@ public: virtual ~Arrangeable() = default; /// Apply the result transformation calculated by the arrangement. - virtual void apply_arrange_result(Vec2d offset, double rotation_rads) = 0; + virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned bed_num) = 0; /// Get the 2D silhouette to arrange and an initial offset and rotation virtual std::tuple get_arrange_polygon() const = 0; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 51759640c..d0ed0bc88 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -559,10 +559,10 @@ public: // ///////////////////////////////////////////////////////////////////////// // Getting the input polygon for arrange - virtual std::tuple get_arrange_polygon() const final; + virtual std::tuple get_arrange_polygon() const override; // Apply the arrange result on the ModelInstance - virtual void apply_arrange_result(Vec2d offs, double rot_rads) final + virtual void apply_arrange_result(Vec2d offs, double rot_rads, unsigned /*bed_num*/) override { // write the transformation data into the model instance set_rotation(Z, rot_rads); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4e3093489..e5b2b38f8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5739,7 +5739,7 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads) +void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) { m_pos = offset; m_rotation = rotation_rads; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 524e0c883..8f419a16d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -624,9 +624,9 @@ public: return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } - virtual void apply_arrange_result(Vec2d offset, double rotation_rads) final; + virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) override; - virtual std::tuple get_arrange_polygon() const final + virtual std::tuple get_arrange_polygon() const override { Polygon p({ {coord_t(0), coord_t(0)}, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 08c70dbe0..a49b541b5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2482,33 +2482,35 @@ arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const { return bedshape; } -void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { - static const auto arrangestr = _(L("Arranging")); - - // 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); - if (plater().printer_technology == ptFFF) { - dist = PrintConfig::min_object_distance(plater().config); - } - - coord_t min_obj_distance = scaled(dist); +void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { auto count = unsigned(m_selected.size()); - arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); + plater().model.arrange_objects(6.f, nullptr); +// static const auto arrangestr = _(L("Arranging")); - try { - 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."))); - } +// // 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); +// if (plater().printer_technology == ptFFF) { +// dist = PrintConfig::min_object_distance(plater().config); +// } + +// coord_t min_obj_distance = scaled(dist); +// auto count = unsigned(m_selected.size()); +// arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); + +// try { +// 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."))); +// } // finalize just here. update_status(int(count), From 9372f1c6ad15d6d4f938f167625ff9e8e5905cb1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 4 Jul 2019 13:58:18 +0200 Subject: [PATCH 14/32] Wip in Nester interface --- src/libnest2d/include/libnest2d/libnest2d.hpp | 216 +++++++++++------- src/libslic3r/Arrange.cpp | 49 ++-- 2 files changed, 141 insertions(+), 124 deletions(-) diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index f106ad3d8..99c8c90c1 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -36,20 +36,20 @@ class _Item { // Transformation data Vertex translation_; Radians rotation_; - Coord offset_distance_; + Coord inflation_; // Info about whether the transformations will have to take place // This is needed because if floating point is used, it is hard to say // that a zero angle is not a rotation because of testing for equality. - bool has_rotation_ = false, has_translation_ = false, has_offset_ = false; + bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false; // For caching the calculations as they can get pretty expensive. mutable RawShape tr_cache_; mutable bool tr_cache_valid_ = false; mutable double area_cache_ = 0; mutable bool area_cache_valid_ = false; - mutable RawShape offset_cache_; - mutable bool offset_cache_valid_ = false; + mutable RawShape inflate_cache_; + mutable bool inflate_cache_valid_ = false; enum class Convexity: char { UNCHECKED, @@ -66,7 +66,9 @@ class _Item { BBCache(): valid(false) {} } bb_cache_; - std::function applyfn_; + static const size_t ID_UNSET = size_t(-1); + + size_t id_{ID_UNSET}; bool fixed_{false}; public: @@ -126,12 +128,12 @@ public: THolesContainer&& holes): sh_(sl::create(std::move(contour), std::move(holes))) {} - template - _Item(std::function applyfn, Args &&... args): - _Item(std::forward(args)...) - { - applyfn_ = std::move(applyfn); - } +// template +// _Item(std::function applyfn, Args &&... args): +// _Item(std::forward(args)...) +// { +// applyfn_ = std::move(applyfn); +// } // Call the apply callback set in constructor. Within the callback, the // original caller can apply the stored transformation to the original @@ -140,13 +142,15 @@ public: // client uses a simplified or processed polygon for nesting) // This callback, if present, will be called for each item after the nesting // is finished. - inline void callApplyFunction(unsigned binidx) const - { - if (applyfn_) applyfn_(*this, binidx); - } +// inline void callApplyFunction(unsigned binidx) const +// { +// if (applyfn_) applyfn_(*this, binidx); +// } inline bool isFixed() const noexcept { return fixed_; } inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } + inline void id(size_t idx) { id_ = idx; } + inline long id() const noexcept { return id_; } /** * @brief Convert the polygon to string representation. The format depends @@ -224,7 +228,7 @@ public: double ret ; if(area_cache_valid_) ret = area_cache_; else { - ret = sl::area(offsettedShape()); + ret = sl::area(infaltedShape()); area_cache_ = ret; area_cache_valid_ = true; } @@ -295,17 +299,21 @@ public: { rotation(rotation() + rads); } - - inline void addOffset(Coord distance) BP2D_NOEXCEPT + + inline void inflation(Coord distance) BP2D_NOEXCEPT { - offset_distance_ = distance; - has_offset_ = true; + inflation_ = distance; + has_inflation_ = true; invalidateCache(); } - - inline void removeOffset() BP2D_NOEXCEPT { - has_offset_ = false; - invalidateCache(); + + inline Coord inflation() const BP2D_NOEXCEPT { + return inflation_; + } + + inline void inflate(Coord distance) BP2D_NOEXCEPT + { + inflation(inflation() + distance); } inline Radians rotation() const BP2D_NOEXCEPT @@ -339,7 +347,7 @@ public: { if(tr_cache_valid_) return tr_cache_; - RawShape cpy = offsettedShape(); + RawShape cpy = infaltedShape(); if(has_rotation_) sl::rotate(cpy, rotation_); if(has_translation_) sl::translate(cpy, translation_); tr_cache_ = cpy; tr_cache_valid_ = true; @@ -360,17 +368,17 @@ public: inline void resetTransformation() BP2D_NOEXCEPT { - has_translation_ = false; has_rotation_ = false; has_offset_ = false; + has_translation_ = false; has_rotation_ = false; has_inflation_ = false; invalidateCache(); } inline Box boundingBox() const { if(!bb_cache_.valid) { if(!has_rotation_) - bb_cache_.bb = sl::boundingBox(offsettedShape()); + bb_cache_.bb = sl::boundingBox(infaltedShape()); else { // TODO make sure this works - auto rotsh = offsettedShape(); + auto rotsh = infaltedShape(); sl::rotate(rotsh, rotation_); bb_cache_.bb = sl::boundingBox(rotsh); } @@ -419,14 +427,14 @@ public: private: - inline const RawShape& offsettedShape() const { - if(has_offset_ ) { - if(offset_cache_valid_) return offset_cache_; + inline const RawShape& infaltedShape() const { + if(has_inflation_ ) { + if(inflate_cache_valid_) return inflate_cache_; - offset_cache_ = sh_; - sl::offset(offset_cache_, offset_distance_); - offset_cache_valid_ = true; - return offset_cache_; + inflate_cache_ = sh_; + sl::offset(inflate_cache_, inflation_); + inflate_cache_valid_ = true; + return inflate_cache_; } return sh_; } @@ -436,7 +444,7 @@ private: tr_cache_valid_ = false; lmb_valid_ = false; rmt_valid_ = false; area_cache_valid_ = false; - offset_cache_valid_ = false; + inflate_cache_valid_ = false; bb_cache_.valid = false; convexity_ = Convexity::UNCHECKED; } @@ -758,6 +766,25 @@ public: void clear() { impl_.clear(); } }; +using BinIdx = unsigned; +template using _NestResult = + std::vector< + std::tuple, // Translation calculated by nesting + Radians, // Rotation calculated by nesting + BinIdx> // Logical bin index, first is zero + >; + +template struct Indexed { + using ShapeType = T; + static T& get(T& obj) { return obj; } +}; + +template struct Indexed> { + using ShapeType = S; + static S& get(std::pair& obj) { return obj.second; } +}; + /** * The Arranger is the front-end class for the libnest2d library. It takes the * input items and outputs the items with the proper transformations to be @@ -769,6 +796,7 @@ class Nester { TSel selector_; public: using Item = typename PlacementStrategy::Item; + using ShapeType = typename Item::ShapeType; using ItemRef = std::reference_wrapper; using TPlacer = PlacementStrategyLike; using BinType = typename TPlacer::BinType; @@ -777,6 +805,7 @@ public: using Coord = TCoord>; using PackGroup = _PackGroup; using ResultType = PackGroup; + template using NestResult = _NestResult; private: BinType bin_; @@ -835,10 +864,12 @@ public: * The number of groups in the pack group is the number of bins opened by * the selection algorithm. */ - template - inline PackGroup execute(TIterator from, TIterator to) + template + inline const NestResult execute(It from, It to, + std::function keyfn = nullptr) { - return _execute(from, to); + if (!keyfn) keyfn = [to](It it) { return to - it; }; + return _execute(from, to, keyfn); } /// Set a progress indicator function object for the selector. @@ -858,65 +889,74 @@ public: return selector_.getResult(); } - inline void preload(const PackGroup& pgrp) - { - selector_.preload(pgrp); - } - private: - - template, - - // This function will be used only if the iterators are pointing to - // a type compatible with the libnets2d::_Item template. - // This way we can use references to input elements as they will - // have to exist for the lifetime of this call. - class T = enable_if_t< std::is_convertible::value, IT> - > - inline const PackGroup& _execute(TIterator from, TIterator to, bool = false) + + template using TVal = remove_cvref_t; + + template + using ConvertibleOnly = + enable_if_t< std::is_convertible, TPItem>::value, void>; + + template + using NotConvertibleOnly = + enable_if_t< ! std::is_convertible, TPItem>::value, void>; + + // This function will be used only if the iterators are pointing to + // a type compatible with the libnets2d::_Item template. + // This way we can use references to input elements as they will + // have to exist for the lifetime of this call. + template + inline ConvertibleOnly> _execute( + It from, It to, std::function keyfn) { - __execute(from, to); - return lastResult(); - } - - template, - class T = enable_if_t::value, IT> - > - inline const PackGroup& _execute(TIterator from, TIterator to, int = false) - { - item_cache_ = {from, to}; - - __execute(item_cache_.begin(), item_cache_.end()); - return lastResult(); - } - - template inline void __execute(TIter from, TIter to) - { - if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { - auto offs = min_obj_distance_; - if (item.isFixed()) offs *= 0.99; + { + auto it = from; size_t id = 0; + while(it != to) + if (it->id() == Item::ID_UNSET) (it++)->id(id++); + else { id = it->id() + 1; ++it; } + } + + NestResult result(to - from); + + __execute(from, to, keyfn); + + BinIdx binidx = 0; + for(auto &itmgrp : lastResult()) { + for(const Item& itm : itmgrp) + result[itm.id()] = + std::make_tuple(keyfn(from + itm.id()), itm.translation(), + itm.rotation(), binidx); - item.addOffset(static_cast(std::ceil(offs/2.0))); + ++binidx; + } + + return result; + } + + template + inline NotConvertibleOnly> _execute( + It from, It to, std::function keyfn) + { + item_cache_.reserve(to - from); + for(auto it = from; it != to; ++it) + item_cache_.emplace_back(Indexed::get(*it)); + + return _execute(item_cache_.begin(), item_cache_.end(), keyfn); + } + + template inline void __execute(It from, It to) + { + auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); + if(infl > 0) std::for_each(from, to, [this](Item& item) { + item.inflate(infl); }); selector_.template packItems( from, to, bin_, pconfig_); if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { - item.removeOffset(); + item.inflate(-infl); }); - - if(!stopfn_ || (stopfn_ && !stopfn_())) { - // Ignore results if nesting was stopped. - const PackGroup& bins = lastResult(); - unsigned binidx = 0; - for(auto& bin : bins) { - for(const Item& itm : bin) itm.callApplyFunction(binidx); - ++binidx; - } - } } }; diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index f778a7f75..6bbcc9577 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -513,12 +513,13 @@ BedShapeHint bedShape(const Polyline &bed) { } template // Arrange for arbitrary bin type -PackGroup _arrange(std::vector & shapes, - std::vector & excludes, - const BinT & bin, - coord_t minobjd, - std::function prind, - std::function stopfn) +_NestResult _arrange( + std::vector & shapes, + std::vector & excludes, + const BinT & bin, + coord_t minobjd, + std::function prind, + std::function stopfn) { AutoArranger arranger{bin, minobjd, prind, stopfn}; @@ -535,22 +536,13 @@ PackGroup _arrange(std::vector & shapes, // Try to put the first item to the center, as the arranger // will not do this for us. - for (auto it = shapes.begin(); it != shapes.end(); ++it) { - Item &itm = *it; + for (Item &itm : shapes) { auto ibb = itm.boundingBox(); auto d = binbb.center() - ibb.center(); itm.translate(d); if (!arranger.is_colliding(itm)) { itm.markAsFixed(); - - // Write the transformation data into the item. The - // callback was set on the instantiation of Item and - // calls the Arrangeable interface. - it->callApplyFunction(0); - - // Remove this item, as it is arranged now - it = shapes.erase(it); break; } } @@ -586,9 +578,7 @@ bool arrange(ArrangeablePtrs & arrangables, coord_t binwidth = 0; auto process_arrangeable = - [](const Arrangeable * arrangeable, - std::vector & outp, - std::function applyfn) + [](const Arrangeable *arrangeable, std::vector &outp) { assert(arrangeable); @@ -605,29 +595,16 @@ bool arrange(ArrangeablePtrs & arrangables, auto firstp = clpath.Contour.front(); clpath.Contour.emplace_back(firstp); - outp.emplace_back(applyfn, std::move(clpath)); + outp.emplace_back(std::move(clpath)); outp.back().rotation(rotation); outp.back().translation({offs.x(), offs.y()}); }; - for (Arrangeable *arrangeable : arrangables) { - process_arrangeable( - arrangeable, - items, - // callback called by arrange to apply the result on the arrangeable - [arrangeable, &binwidth, &ret](const Item &itm, unsigned binidx) { - ret = !binidx; // Return value false more bed is required - clppr::cInt stride = binidx * stride_padding(binwidth); - - clppr::IntPoint offs = itm.translation(); - arrangeable->apply_arrange_result({unscaled(offs.X + stride), - unscaled(offs.Y)}, - itm.rotation(), binidx); - }); - } + for (Arrangeable *arrangeable : arrangables) + process_arrangeable(arrangeable, items); for (const Arrangeable * fixed: excludes) - process_arrangeable(fixed, fixeditems, nullptr); + process_arrangeable(fixed, fixeditems); // Integer ceiling the min distance from the bed perimeters coord_t md = min_obj_dist - SCALED_EPSILON; From df7bb94dafea6a64922184fe65db3c61f7c85da0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 12 Jul 2019 21:03:49 +0200 Subject: [PATCH 15/32] Not handling logical beds in arrange() --- src/libnest2d/include/libnest2d.h | 23 ++- src/libnest2d/include/libnest2d/libnest2d.hpp | 141 ++++++------------ .../libnest2d/selections/djd_heuristic.hpp | 7 +- .../include/libnest2d/selections/firstfit.hpp | 6 +- src/libnest2d/tests/test.cpp | 97 ++++++------ src/libslic3r/Arrange.cpp | 62 ++++---- src/libslic3r/Arrange.hpp | 79 ++++++---- src/libslic3r/Model.cpp | 40 +++-- src/libslic3r/Model.hpp | 12 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.hpp | 13 +- src/slic3r/GUI/Plater.cpp | 46 ++---- 12 files changed, 256 insertions(+), 272 deletions(-) diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h index f1d2506f4..5f7a29dfb 100644 --- a/src/libnest2d/include/libnest2d.h +++ b/src/libnest2d/include/libnest2d.h @@ -59,20 +59,20 @@ extern template PackGroup Nester::execute( template::iterator> -PackGroup nest(Iterator from, Iterator to, +void nest(Iterator from, Iterator to, const typename Placer::BinType& bin, Coord dist = 0, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { Nester nester(bin, dist, pconf, sconf); - return nester.execute(from, to); + nester.execute(from, to); } template::iterator> -PackGroup nest(Iterator from, Iterator to, +void nest(Iterator from, Iterator to, const typename Placer::BinType& bin, ProgressFunction prg, StopCondition scond = []() { return false; }, @@ -83,7 +83,7 @@ PackGroup nest(Iterator from, Iterator to, Nester nester(bin, dist, pconf, sconf); if(prg) nester.progressIndicator(prg); if(scond) nester.stopCondition(scond); - return nester.execute(from, to); + nester.execute(from, to); } #ifdef LIBNEST2D_STATIC @@ -91,14 +91,14 @@ PackGroup nest(Iterator from, Iterator to, extern template class Nester; extern template class Nester; -extern template PackGroup nest(std::vector::iterator from, +extern template void nest(std::vector::iterator from, std::vector::iterator to, const Box& bin, Coord dist = 0, const NfpPlacer::Config& pconf, const FirstFitSelection::Config& sconf); -extern template PackGroup nest(std::vector::iterator from, +extern template void nest(std::vector::iterator from, std::vector::iterator to, const Box& bin, ProgressFunction prg, @@ -112,20 +112,19 @@ extern template PackGroup nest(std::vector::iterator from, template> -PackGroup nest(Container&& cont, +void nest(Container&& cont, const typename Placer::BinType& bin, Coord dist = 0, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { - return nest(cont.begin(), cont.end(), - bin, dist, pconf, sconf); + nest(cont.begin(), cont.end(), bin, dist, pconf, sconf); } template> -PackGroup nest(Container&& cont, +void nest(Container&& cont, const typename Placer::BinType& bin, ProgressFunction prg, StopCondition scond = []() { return false; }, @@ -133,8 +132,8 @@ PackGroup nest(Container&& cont, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { - return nest(cont.begin(), cont.end(), - bin, prg, scond, dist, pconf, sconf); + nest(cont.begin(), cont.end(), bin, prg, scond, dist, + pconf, sconf); } } diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 99c8c90c1..a83a16ecf 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -12,6 +12,8 @@ namespace libnest2d { +static const constexpr int BIN_ID_UNSET = -1; + /** * \brief An item to be placed on a bin. * @@ -34,9 +36,9 @@ class _Item { RawShape sh_; // Transformation data - Vertex translation_; - Radians rotation_; - Coord inflation_; + Vertex translation_{0, 0}; + Radians rotation_{0.0}; + Coord inflation_{0}; // Info about whether the transformations will have to take place // This is needed because if floating point is used, it is hard to say @@ -66,9 +68,7 @@ class _Item { BBCache(): valid(false) {} } bb_cache_; - static const size_t ID_UNSET = size_t(-1); - - size_t id_{ID_UNSET}; + int binid_{BIN_ID_UNSET}; bool fixed_{false}; public: @@ -149,8 +149,8 @@ public: inline bool isFixed() const noexcept { return fixed_; } inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } - inline void id(size_t idx) { id_ = idx; } - inline long id() const noexcept { return id_; } + inline void binId(int idx) { binid_ = idx; } + inline int binId() const noexcept { return binid_; } /** * @brief Convert the polygon to string representation. The format depends @@ -766,25 +766,6 @@ public: void clear() { impl_.clear(); } }; -using BinIdx = unsigned; -template using _NestResult = - std::vector< - std::tuple, // Translation calculated by nesting - Radians, // Rotation calculated by nesting - BinIdx> // Logical bin index, first is zero - >; - -template struct Indexed { - using ShapeType = T; - static T& get(T& obj) { return obj; } -}; - -template struct Indexed> { - using ShapeType = S; - static S& get(std::pair& obj) { return obj.second; } -}; - /** * The Arranger is the front-end class for the libnest2d library. It takes the * input items and outputs the items with the proper transformations to be @@ -805,7 +786,6 @@ public: using Coord = TCoord>; using PackGroup = _PackGroup; using ResultType = PackGroup; - template using NestResult = _NestResult; private: BinType bin_; @@ -816,8 +796,13 @@ private: using TPItem = remove_cvref_t; using TSItem = remove_cvref_t; - std::vector item_cache_; StopCondition stopfn_; + + template using TVal = remove_cvref_t; + + template + using ConvertibleOnly = + enable_if_t< std::is_convertible, TPItem>::value, void>; public: @@ -864,12 +849,20 @@ public: * The number of groups in the pack group is the number of bins opened by * the selection algorithm. */ - template - inline const NestResult execute(It from, It to, - std::function keyfn = nullptr) + template + inline ConvertibleOnly execute(It from, It to) { - if (!keyfn) keyfn = [to](It it) { return to - it; }; - return _execute(from, to, keyfn); + auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); + if(infl > 0) std::for_each(from, to, [this, infl](Item& item) { + item.inflate(infl); + }); + + selector_.template packItems( + from, to, bin_, pconfig_); + + if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) { + item.inflate(-infl); + }); } /// Set a progress indicator function object for the selector. @@ -890,74 +883,32 @@ public: } private: - - template using TVal = remove_cvref_t; - - template - using ConvertibleOnly = - enable_if_t< std::is_convertible, TPItem>::value, void>; - - template - using NotConvertibleOnly = - enable_if_t< ! std::is_convertible, TPItem>::value, void>; - + + // This function will be used only if the iterators are pointing to // a type compatible with the libnets2d::_Item template. // This way we can use references to input elements as they will // have to exist for the lifetime of this call. - template - inline ConvertibleOnly> _execute( - It from, It to, std::function keyfn) - { - { - auto it = from; size_t id = 0; - while(it != to) - if (it->id() == Item::ID_UNSET) (it++)->id(id++); - else { id = it->id() + 1; ++it; } - } - - NestResult result(to - from); - - __execute(from, to, keyfn); - - BinIdx binidx = 0; - for(auto &itmgrp : lastResult()) { - for(const Item& itm : itmgrp) - result[itm.id()] = - std::make_tuple(keyfn(from + itm.id()), itm.translation(), - itm.rotation(), binidx); - - ++binidx; - } - - return result; - } +// template +// inline ConvertibleOnly _execute(It from, It to) +// { +// __execute(from, to); +// } - template - inline NotConvertibleOnly> _execute( - It from, It to, std::function keyfn) - { - item_cache_.reserve(to - from); - for(auto it = from; it != to; ++it) - item_cache_.emplace_back(Indexed::get(*it)); +// template inline void _execute(It from, It to) +// { +// auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); +// if(infl > 0) std::for_each(from, to, [this](Item& item) { +// item.inflate(infl); +// }); - return _execute(item_cache_.begin(), item_cache_.end(), keyfn); - } - - template inline void __execute(It from, It to) - { - auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); - if(infl > 0) std::for_each(from, to, [this](Item& item) { - item.inflate(infl); - }); - - selector_.template packItems( - from, to, bin_, pconfig_); +// selector_.template packItems( +// from, to, bin_, pconfig_); - if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { - item.inflate(-infl); - }); - } +// if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { +// item.inflate(-infl); +// }); +// } }; } diff --git a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp index 25007e580..f904210aa 100644 --- a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp +++ b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp @@ -711,7 +711,12 @@ public: addBin(); packjob(placers[idx], remaining, idx); idx++; } - + + int binid = 0; + for(auto &bin : packed_bins_) { + for(Item& itm : bin) itm.binId(binid); + binid++; + } } }; diff --git a/src/libnest2d/include/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp index 287204c08..6a3e2e61b 100644 --- a/src/libnest2d/include/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -90,8 +90,10 @@ public: size_t j = 0; while(!was_packed && !cancelled()) { for(; j < placers.size() && !was_packed && !cancelled(); j++) { - if((was_packed = placers[j].pack(*it, rem(it, store_) ))) - makeProgress(placers[j], j); + if((was_packed = placers[j].pack(*it, rem(it, store_) ))) { + it->get().binId(int(j)); + makeProgress(placers[j], j); + } } if(!was_packed) { diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index e5bd87182..6891b16e1 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -372,27 +372,34 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) Nester arrange(bin); - auto groups = arrange.execute(rects.begin(), rects.end()); - - ASSERT_EQ(groups.size(), 1u); - ASSERT_EQ(groups[0].size(), rects.size()); - + arrange.execute(rects.begin(), rects.end()); + + auto max_group = std::max_element(rects.begin(), rects.end(), + [](const Item &i1, const Item &i2) { + return i1.binId() < i2.binId(); + }); + + int groups = max_group == rects.end() ? 0 : max_group->binId() + 1; + + ASSERT_EQ(groups, 1u); + ASSERT_TRUE( + std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) { + return itm.binId() != BIN_ID_UNSET; + })); + // check for no intersections, no containment: - for(auto result : groups) { - bool valid = true; - for(Item& r1 : result) { - for(Item& r2 : result) { - if(&r1 != &r2 ) { - valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); - ASSERT_TRUE(valid); - valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); - ASSERT_TRUE(valid); - } + bool valid = true; + for(Item& r1 : rects) { + for(Item& r2 : rects) { + if(&r1 != &r2 ) { + valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); + ASSERT_TRUE(valid); + valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); + ASSERT_TRUE(valid); } } } - } TEST(GeometryAlgorithms, ArrangeRectanglesLoose) @@ -433,16 +440,25 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) Nester arrange(bin, min_obj_distance); - auto groups = arrange.execute(rects.begin(), rects.end()); + arrange.execute(rects.begin(), rects.end()); - ASSERT_EQ(groups.size(), 1u); - ASSERT_EQ(groups[0].size(), rects.size()); + auto max_group = std::max_element(rects.begin(), rects.end(), + [](const Item &i1, const Item &i2) { + return i1.binId() < i2.binId(); + }); + + size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1; + + ASSERT_EQ(groups, 1u); + ASSERT_TRUE( + std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) { + return itm.binId() != BIN_ID_UNSET; + })); // check for no intersections, no containment: - auto result = groups[0]; bool valid = true; - for(Item& r1 : result) { - for(Item& r2 : result) { + for(Item& r1 : rects) { + for(Item& r2 : rects) { if(&r1 != &r2 ) { valid = !Item::intersects(r1, r2); valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); @@ -554,27 +570,24 @@ TEST(GeometryAlgorithms, convexHull) { TEST(GeometryAlgorithms, NestTest) { std::vector input = prusaParts(); + + libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) { + std::cout << "parts left: " << cnt << std::endl; + }); + + auto max_binid_it = std::max_element(input.begin(), input.end(), + [](const Item &i1, const Item &i2) { + return i1.binId() < i2.binId(); + }); + + size_t bins = max_binid_it == input.end() ? 0 : max_binid_it->binId() + 1; - PackGroup result = libnest2d::nest(input, - Box(250000000, 210000000), - [](unsigned cnt) { - std::cout - << "parts left: " << cnt - << std::endl; - }); - - ASSERT_LE(result.size(), 2); - - size_t partsum = std::accumulate(result.begin(), - result.end(), - size_t(0), - [](size_t s, - const decltype( - result)::value_type &bin) { - return s += bin.size(); - }); - - ASSERT_EQ(input.size(), partsum); + ASSERT_EQ(bins, 2u); + + ASSERT_TRUE( + std::all_of(input.begin(), input.end(), [](const Item &itm) { + return itm.binId() != BIN_ID_UNSET; + })); } namespace { diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 6bbcc9577..c49c9fb97 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -341,9 +341,9 @@ public: m_pck.configure(m_pconf); } - template inline PackGroup operator()(Args&&...args) { + template inline void operator()(Args&&...args) { m_rtree.clear(); - return m_pck.execute(std::forward(args)...); + m_pck.execute(std::forward(args)...); } inline void preload(std::vector& fixeditems) { @@ -513,7 +513,7 @@ BedShapeHint bedShape(const Polyline &bed) { } template // Arrange for arbitrary bin type -_NestResult _arrange( +void _arrange( std::vector & shapes, std::vector & excludes, const BinT & bin, @@ -553,40 +553,30 @@ _NestResult _arrange( for (auto &itm : shapes ) inp.emplace_back(itm); for (auto &itm : excludes) inp.emplace_back(itm); - return arranger(inp.begin(), inp.end()); -} - -inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) -{ - return w + w / 5; + arranger(inp.begin(), inp.end()); } // The final client function for arrangement. A progress indicator and // a stop predicate can be also be passed to control the process. -bool arrange(ArrangeablePtrs & arrangables, - const ArrangeablePtrs & excludes, +void arrange(ArrangePolygons & arrangables, + const ArrangePolygons & excludes, coord_t min_obj_dist, const BedShapeHint & bedhint, std::function progressind, std::function stopcondition) { - bool ret = true; namespace clppr = ClipperLib; std::vector items, fixeditems; items.reserve(arrangables.size()); - coord_t binwidth = 0; - + + // Create Item from Arrangeable auto process_arrangeable = - [](const Arrangeable *arrangeable, std::vector &outp) + [](const ArrangePolygon &arrpoly, std::vector &outp) { - assert(arrangeable); - - 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 = arrpoly.poly.contour; + const Vec2crd & offs = arrpoly.translation; + double rotation = arrpoly.rotation; if (p.is_counter_clockwise()) p.reverse(); @@ -600,10 +590,10 @@ bool arrange(ArrangeablePtrs & arrangables, outp.back().translation({offs.x(), offs.y()}); }; - for (Arrangeable *arrangeable : arrangables) + for (ArrangePolygon &arrangeable : arrangables) process_arrangeable(arrangeable, items); - for (const Arrangeable * fixed: excludes) + for (const ArrangePolygon &fixed: excludes) process_arrangeable(fixed, fixeditems); // Integer ceiling the min distance from the bed perimeters @@ -619,7 +609,6 @@ bool arrange(ArrangeablePtrs & arrangables, 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()); _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn); break; @@ -627,7 +616,6 @@ bool arrange(ArrangeablePtrs & arrangables, case BedShapeType::CIRCLE: { auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); - binwidth = scaled(c.radius()); _arrange(items, fixeditems, cc, min_obj_dist, pri, cfn); break; @@ -636,7 +624,6 @@ bool arrange(ArrangeablePtrs & arrangables, auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); auto irrbed = sl::create(std::move(ctour)); BoundingBox polybb(bedhint.shape.polygon); - binwidth = (polybb.max(X) - polybb.min(X)); _arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn); break; @@ -655,19 +642,22 @@ bool arrange(ArrangeablePtrs & arrangables, } }; - if(stopcondition && stopcondition()) return false; - - return ret; + for(size_t i = 0; i < items.size(); ++i) { + clppr::IntPoint tr = items[i].translation(); + arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)}; + arrangables[i].rotation = items[i].rotation(); + arrangables[i].bed_idx = items[i].binId(); + } } // Arrange, without the fixed items (excludes) -bool arrange(ArrangeablePtrs & inp, - coord_t min_d, - const BedShapeHint & bedhint, - std::function prfn, - std::function stopfn) +void arrange(ArrangePolygons & inp, + coord_t min_d, + const BedShapeHint & bedhint, + std::function prfn, + std::function stopfn) { - return arrange(inp, {}, min_d, bedhint, prfn, stopfn); + arrange(inp, {}, min_d, bedhint, prfn, stopfn); } } // namespace arr diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 337a7d959..bc23108cd 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -1,7 +1,7 @@ #ifndef MODELARRANGE_HPP #define MODELARRANGE_HPP -#include "Polygon.hpp" +#include "ExPolygon.hpp" #include "BoundingBox.hpp" namespace Slic3r { @@ -37,34 +37,57 @@ enum class BedShapeType { /// Info about the print bed for the arrange() function. struct BedShapeHint { BedShapeType type = BedShapeType::INFINITE; - /*union*/ struct { // I know but who cares... TODO: use variant from cpp17? + union BedShape_u { // I know but who cares... TODO: use variant from cpp17? CircleBed circ; BoundingBox box; Polyline polygon; - InfiniteBed infinite; + InfiniteBed infinite{}; + ~BedShape_u() {} + BedShape_u() {}; } shape; + + BedShapeHint() {}; + + ~BedShapeHint() { + if (type == BedShapeType::IRREGULAR) + shape.polygon.Slic3r::Polyline::~Polyline(); + }; + + BedShapeHint(const BedShapeHint &cpy) { + *this = cpy; + } + + BedShapeHint& operator=(const BedShapeHint &cpy) { + type = cpy.type; + switch(type) { + case BedShapeType::BOX: shape.box = cpy.shape.box; break; + case BedShapeType::CIRCLE: shape.circ = cpy.shape.circ; break; + case BedShapeType::IRREGULAR: shape.polygon = cpy.shape.polygon; break; + case BedShapeType::INFINITE: shape.infinite = cpy.shape.infinite; break; + case BedShapeType::UNKNOWN: break; + } + + return *this; + } }; /// Get a bed shape hint for arrange() from a naked Polyline. BedShapeHint bedShape(const Polyline& bed); -/** - * @brief Classes implementing the Arrangeable interface can be used as input - * to the arrange function. - */ -class Arrangeable { -public: +static const constexpr long UNARRANGED = -1; + +struct ArrangePolygon { + const ExPolygon poly; + Vec2crd translation{0, 0}; + double rotation{0.0}; + long bed_idx{UNARRANGED}; - virtual ~Arrangeable() = default; - - /// Apply the result transformation calculated by the arrangement. - virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned bed_num) = 0; - - /// Get the 2D silhouette to arrange and an initial offset and rotation - virtual std::tuple get_arrange_polygon() const = 0; + ArrangePolygon(const ExPolygon &p, const Vec2crd &tr = {}, double rot = 0.0) + : poly{p}, translation{tr}, rotation{rot} + {} }; -using ArrangeablePtrs = std::vector; +using ArrangePolygons = std::vector; /** * \brief Arranges the model objects on the screen. @@ -97,20 +120,20 @@ using ArrangeablePtrs = std::vector; * * \param stopcondition A predicate returning true if abort is needed. */ -bool arrange(ArrangeablePtrs &items, - coord_t min_obj_distance, - const BedShapeHint& bedhint, - std::function progressind = nullptr, - std::function stopcondition = nullptr); +void arrange(ArrangePolygons & items, + coord_t min_obj_distance, + const BedShapeHint & bedhint, + std::function progressind = nullptr, + std::function stopcondition = nullptr); /// Same as the previous, only that it takes unmovable items as an /// additional argument. -bool arrange(ArrangeablePtrs &items, - const ArrangeablePtrs &excludes, - coord_t min_obj_distance, - const BedShapeHint& bedhint, - std::function progressind = nullptr, - std::function stopcondition = nullptr); +void arrange(ArrangePolygons & items, + const ArrangePolygons & excludes, + coord_t min_obj_distance, + const BedShapeHint & bedhint, + std::function progressind = nullptr, + std::function stopcondition = nullptr); } // arr } // Slic3r diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2af4e4c27..966be4c88 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -404,11 +404,16 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) size_t count = 0; for (auto obj : objects) count += obj->instances.size(); - arrangement::ArrangeablePtrs input; + arrangement::ArrangePolygons input; + ModelInstancePtrs instances; input.reserve(count); + instances.reserve(count); for (ModelObject *mo : objects) - for (ModelInstance *minst : mo->instances) - input.emplace_back(minst); + for (ModelInstance *minst : mo->instances) { + input.emplace_back(minst->get_arrange_polygon()); + instances.emplace_back(minst); + } + arrangement::BedShapeHint bedhint; @@ -417,7 +422,22 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max)); } - return arrangement::arrange(input, scaled(dist), bedhint); + arrangement::arrange(input, scaled(dist), bedhint); + + bool ret = true; + + for(size_t i = 0; i < input.size(); ++i) { + auto inst = instances[i]; + inst->set_rotation(Z, input[i].rotation); + auto tr = unscaled(input[i].translation); + inst->set_offset(X, tr.x()); + inst->set_offset(Y, tr.y()); + + if (input[i].bed_idx != 0) ret = false; // no logical beds are allowed + } + + + return ret; } // Duplicate the entire model preserving instance relative positions. @@ -1819,7 +1839,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin } -std::tuple ModelInstance::get_arrange_polygon() const +arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const { static const double SIMPLIFY_TOLERANCE_MM = 0.1; @@ -1835,15 +1855,15 @@ std::tuple ModelInstance::get_arrange_polygon() const // this may happen for malformed models, see: // https://github.com/prusa3d/PrusaSlicer/issues/2209 - if (p.points.empty()) return {}; + 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))}, - get_rotation(Z)); + + ExPolygon ep; ep.contour = std::move(p); + + return {ep, Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, get_rotation(Z)}; } // Test whether the two models contain the same number of ModelObjects with the same set of IDs diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index d0ed0bc88..2fd696d14 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -491,7 +491,7 @@ private: // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance : public ModelBase, public arrangement::Arrangeable +class ModelInstance : public ModelBase { public: enum EPrintVolumeState : unsigned char @@ -555,19 +555,19 @@ public: bool is_printable() const { return print_volume_state == PVS_Inside; } // ///////////////////////////////////////////////////////////////////////// - // Implement arr::Arrangeable interface + // Implement arrangement::Arrangeable interface // ///////////////////////////////////////////////////////////////////////// // Getting the input polygon for arrange - virtual std::tuple get_arrange_polygon() const override; + arrangement::ArrangePolygon get_arrange_polygon() const; // Apply the arrange result on the ModelInstance - virtual void apply_arrange_result(Vec2d offs, double rot_rads, unsigned /*bed_num*/) override + void apply_arrange_result(Vec2crd offs, double rot_rads) { // write the transformation data into the model instance set_rotation(Z, rot_rads); - set_offset(X, offs(X)); - set_offset(Y, offs(Y)); + set_offset(X, unscale(offs(X))); + set_offset(Y, unscale(offs(Y))); } protected: diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e5b2b38f8..4e3093489 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5739,7 +5739,7 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) +void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads) { m_pos = offset; m_rotation = rotation_rads; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8f419a16d..2316637d8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.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 arrangement::Arrangeable { + class WipeTowerInfo { Vec2d m_pos = {std::nan(""), std::nan("")}; Vec2d m_bb_size; double m_rotation; @@ -624,9 +624,9 @@ public: return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } - virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) override; - - virtual std::tuple get_arrange_polygon() const override + void apply_arrange_result(Vec2d offset, double rotation_rads); + + arrangement::ArrangePolygon get_arrange_polygon() const { Polygon p({ {coord_t(0), coord_t(0)}, @@ -635,8 +635,9 @@ public: {coord_t(0), scaled(m_bb_size(Y))}, {coord_t(0), coord_t(0)}, }); - - return std::make_tuple(p, scaled(m_pos), m_rotation); + + ExPolygon ep; ep.contour = std::move(p); + return {ep, scaled(m_pos), m_rotation}; } }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a49b541b5..3adfc0f05 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1217,28 +1217,6 @@ 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 { @@ -1447,17 +1425,18 @@ struct Plater::priv class ArrangeJob : public Job { GLCanvas3D::WipeTowerInfo m_wti; - arrangement::ArrangeablePtrs m_selected, m_unselected; - - static std::array collect( + arrangement::ArrangePolygons m_selected, m_unselected; + + static std::array collect( Model &model, const Selection &sel) { - auto selmap = sel.get_content(); - + const Selection::ObjectIdxsToInstanceIdxsMap &selmap = + sel.get_content(); + size_t count = 0; for (auto obj : model.objects) count += obj->instances.size(); - arrangement::ArrangeablePtrs selected, unselected; + arrangement::ArrangePolygons selected, unselected; selected.reserve(count + 1 /* for optional wti */); unselected.reserve(count + 1 /* for optional wti */); @@ -1475,12 +1454,12 @@ struct Plater::priv ModelInstance *inst = model.objects[oidx] ->instances[iidx]; instit == iids.end() ? - unselected.emplace_back(inst) : - selected.emplace_back(inst); + unselected.emplace_back(inst->get_arrange_polygon()) : + selected.emplace_back(inst->get_arrange_polygon()); } } else // object not selected, all instances are unselected for (auto inst : model.objects[oidx]->instances) - unselected.emplace_back(inst); + unselected.emplace_back(inst->get_arrange_polygon()); } if (selected.empty()) selected.swap(unselected); @@ -1495,14 +1474,15 @@ struct Plater::priv m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); const Selection& sel = plater().get_selection(); + BoundingBoxf bedbb(plater().bed.get_shape()); 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); + m_selected.emplace_back(m_wti.get_arrange_polygon()) : + m_unselected.emplace_back(m_wti.get_arrange_polygon()); } public: From 1b0e192046e358ba34941d1895f86c94ad6c6ac0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 15 Jul 2019 17:30:44 +0200 Subject: [PATCH 16/32] Arrange cache in ModeInstance and logical bed remembered. --- src/libnest2d/include/libnest2d/libnest2d.hpp | 47 -- .../include/libnest2d/selections/firstfit.hpp | 9 +- src/libslic3r/Arrange.cpp | 45 +- src/libslic3r/Arrange.hpp | 169 ++++--- src/libslic3r/Model.cpp | 103 ++-- src/libslic3r/Model.hpp | 40 +- src/slic3r/GUI/GLCanvas3D.cpp | 7 +- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 478 ++++++++++-------- 9 files changed, 488 insertions(+), 412 deletions(-) diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index a83a16ecf..11c032fae 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -127,25 +127,6 @@ public: inline _Item(TContour&& contour, THolesContainer&& holes): sh_(sl::create(std::move(contour), std::move(holes))) {} - -// template -// _Item(std::function applyfn, Args &&... args): -// _Item(std::forward(args)...) -// { -// applyfn_ = std::move(applyfn); -// } - - // Call the apply callback set in constructor. Within the callback, the - // original caller can apply the stored transformation to the original - // objects inteded for nesting. It might not be the shape handed over - // to _Item (e.g. arranging 3D shapes based on 2D silhouette or the - // client uses a simplified or processed polygon for nesting) - // This callback, if present, will be called for each item after the nesting - // is finished. -// inline void callApplyFunction(unsigned binidx) const -// { -// if (applyfn_) applyfn_(*this, binidx); -// } inline bool isFixed() const noexcept { return fixed_; } inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } @@ -881,34 +862,6 @@ public: { return selector_.getResult(); } - -private: - - - // This function will be used only if the iterators are pointing to - // a type compatible with the libnets2d::_Item template. - // This way we can use references to input elements as they will - // have to exist for the lifetime of this call. -// template -// inline ConvertibleOnly _execute(It from, It to) -// { -// __execute(from, to); -// } - -// template inline void _execute(It from, It to) -// { -// auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); -// if(infl > 0) std::for_each(from, to, [this](Item& item) { -// item.inflate(infl); -// }); - -// selector_.template packItems( -// from, to, bin_, pconfig_); - -// if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { -// item.inflate(-infl); -// }); -// } }; } diff --git a/src/libnest2d/include/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp index 6a3e2e61b..74207f8cf 100644 --- a/src/libnest2d/include/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -42,8 +42,13 @@ public: std::for_each(first, last, [this](Item& itm) { if(itm.isFixed()) { - if(packed_bins_.empty()) packed_bins_.emplace_back(); - packed_bins_.front().emplace_back(itm); + if (itm.binId() < 0) itm.binId(0); + auto binidx = size_t(itm.binId()); + + while(packed_bins_.size() <= binidx) + packed_bins_.emplace_back(); + + packed_bins_[binidx].emplace_back(itm); } else { store_.emplace_back(itm); } diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index c49c9fb97..fd3573699 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -434,9 +434,7 @@ inline Circle to_lnCircle(const CircleBed& circ) { } // Get the type of bed geometry from a simple vector of points. -BedShapeHint bedShape(const Polyline &bed) { - BedShapeHint ret; - +BedShapeHint::BedShapeHint(const Polyline &bed) { auto x = [](const Point& p) { return p(X); }; auto y = [](const Point& p) { return p(Y); }; @@ -497,19 +495,16 @@ BedShapeHint bedShape(const Polyline &bed) { auto parea = poly_area(bed); if( (1.0 - parea/area(bb)) < 1e-3 ) { - ret.type = BedShapeType::BOX; - ret.shape.box = bb; + m_type = BedShapes::bsBox; + m_bed.box = bb; } else if(auto c = isCircle(bed)) { - ret.type = BedShapeType::CIRCLE; - ret.shape.circ = c; + m_type = BedShapes::bsCircle; + m_bed.circ = c; } else { - ret.type = BedShapeType::IRREGULAR; - ret.shape.polygon = bed; + m_type = BedShapes::bsIrregular; + m_bed.polygon = bed; } - - // Determine the bed shape by hand - return ret; } template // Arrange for arbitrary bin type @@ -588,6 +583,7 @@ void arrange(ArrangePolygons & arrangables, outp.emplace_back(std::move(clpath)); outp.back().rotation(rotation); outp.back().translation({offs.x(), offs.y()}); + outp.back().binId(arrpoly.bed_idx); }; for (ArrangePolygon &arrangeable : arrangables) @@ -596,6 +592,8 @@ void arrange(ArrangePolygons & arrangables, for (const ArrangePolygon &fixed: excludes) process_arrangeable(fixed, fixeditems); + for (Item &itm : fixeditems) itm.inflate(-2 * SCALED_EPSILON); + // Integer ceiling the min distance from the bed perimeters coord_t md = min_obj_dist - SCALED_EPSILON; md = (md % 2) ? md / 2 + 1 : md / 2; @@ -603,39 +601,38 @@ void arrange(ArrangePolygons & arrangables, auto &cfn = stopcondition; auto &pri = progressind; - switch (bedhint.type) { - case BedShapeType::BOX: { + switch (bedhint.get_type()) { + case bsBox: { // Create the arranger for the box shaped bed - BoundingBox bbb = bedhint.shape.box; + BoundingBox bbb = bedhint.get_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)}}; _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn); break; } - case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; - auto cc = to_lnCircle(c); + case bsCircle: { + auto cc = to_lnCircle(bedhint.get_circle()); _arrange(items, fixeditems, cc, min_obj_dist, pri, cfn); break; } - case BedShapeType::IRREGULAR: { - auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); + case bsIrregular: { + auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.get_irregular()); auto irrbed = sl::create(std::move(ctour)); - BoundingBox polybb(bedhint.shape.polygon); + BoundingBox polybb(bedhint.get_irregular()); _arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn); break; } - case BedShapeType::INFINITE: { - const InfiniteBed& nobin = bedhint.shape.infinite; + case bsInfinite: { + const InfiniteBed& nobin = bedhint.get_infinite(); auto infbb = Box::infinite({nobin.center.x(), nobin.center.y()}); _arrange(items, fixeditems, infbb, min_obj_dist, pri, cfn); break; } - case BedShapeType::UNKNOWN: { + case bsUnknown: { // We know nothing about the bed, let it be infinite and zero centered _arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn); break; diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index bc23108cd..c705b612b 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -22,97 +22,152 @@ public: inline operator bool() { return !std::isnan(radius_); } }; -/// Representing an unbounded bin +/// Representing an unbounded bed. struct InfiniteBed { Point center; }; /// Types of print bed shapes. -enum class BedShapeType { - BOX, - CIRCLE, - IRREGULAR, - INFINITE, - UNKNOWN +enum BedShapes { + bsBox, + bsCircle, + bsIrregular, + bsInfinite, + bsUnknown }; -/// Info about the print bed for the arrange() function. -struct BedShapeHint { - BedShapeType type = BedShapeType::INFINITE; - union BedShape_u { // I know but who cares... TODO: use variant from cpp17? +/// Info about the print bed for the arrange() function. This is a variant +/// holding one of the four shapes a bed can be. +class BedShapeHint { + BedShapes m_type = BedShapes::bsInfinite; + + union BedShape_u { // TODO: use variant from cpp17? CircleBed circ; BoundingBox box; Polyline polygon; - InfiniteBed infinite{}; + InfiniteBed infbed{}; ~BedShape_u() {} BedShape_u() {}; - } shape; + } m_bed; - BedShapeHint() {}; +public: + + BedShapeHint(){}; - ~BedShapeHint() { - if (type == BedShapeType::IRREGULAR) - shape.polygon.Slic3r::Polyline::~Polyline(); - }; - - BedShapeHint(const BedShapeHint &cpy) { - *this = cpy; + /// Get a bed shape hint for arrange() from a naked Polyline. + explicit BedShapeHint(const Polyline &polyl); + explicit BedShapeHint(const BoundingBox &bb) + { + m_type = bsBox; m_bed.box = bb; } - BedShapeHint& operator=(const BedShapeHint &cpy) { - type = cpy.type; - switch(type) { - case BedShapeType::BOX: shape.box = cpy.shape.box; break; - case BedShapeType::CIRCLE: shape.circ = cpy.shape.circ; break; - case BedShapeType::IRREGULAR: shape.polygon = cpy.shape.polygon; break; - case BedShapeType::INFINITE: shape.infinite = cpy.shape.infinite; break; - case BedShapeType::UNKNOWN: break; + explicit BedShapeHint(const CircleBed &c) + { + m_type = bsCircle; m_bed.circ = c; + } + + explicit BedShapeHint(const InfiniteBed &ibed) + { + m_type = bsInfinite; m_bed.infbed = ibed; + } + + ~BedShapeHint() + { + if (m_type == BedShapes::bsIrregular) + m_bed.polygon.Slic3r::Polyline::~Polyline(); + }; + + BedShapeHint(const BedShapeHint &cpy) { *this = cpy; } + BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); } + + BedShapeHint &operator=(const BedShapeHint &cpy) + { + m_type = cpy.m_type; + switch(m_type) { + case bsBox: m_bed.box = cpy.m_bed.box; break; + case bsCircle: m_bed.circ = cpy.m_bed.circ; break; + case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break; + case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break; + case bsUnknown: break; } return *this; } + + BedShapeHint& operator=(BedShapeHint &&cpy) + { + m_type = cpy.m_type; + switch(m_type) { + case bsBox: m_bed.box = std::move(cpy.m_bed.box); break; + case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break; + case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break; + case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break; + case bsUnknown: break; + } + + return *this; + } + + BedShapes get_type() const { return m_type; } + + const BoundingBox &get_box() const + { + assert(m_type == bsBox); return m_bed.box; + } + const CircleBed &get_circle() const + { + assert(m_type == bsCircle); return m_bed.circ; + } + const Polyline &get_irregular() const + { + assert(m_type == bsIrregular); return m_bed.polygon; + } + const InfiniteBed &get_infinite() const + { + assert(m_type == bsInfinite); return m_bed.infbed; + } }; -/// Get a bed shape hint for arrange() from a naked Polyline. -BedShapeHint bedShape(const Polyline& bed); - -static const constexpr long UNARRANGED = -1; +/// A logical bed representing an object not being arranged. Either the arrange +/// has not yet succesfully run on this ArrangePolygon or it could not fit the +/// object due to overly large size or invalid geometry. +static const constexpr int UNARRANGED = -1; +/// Input/Output structure for the arrange() function. The poly field will not +/// be modified during arrangement. Instead, the translation and rotation fields +/// will mark the needed transformation for the polygon to be in the arranged +/// position. These can also be set to an initial offset and rotation. +/// +/// The bed_idx field will indicate the logical bed into which the +/// polygon belongs: UNARRANGED means no place for the polygon +/// (also the initial state before arrange), 0..N means the index of the bed. +/// Zero is the physical bed, larger than zero means a virtual bed. struct ArrangePolygon { - const ExPolygon poly; - Vec2crd translation{0, 0}; - double rotation{0.0}; - long bed_idx{UNARRANGED}; + const ExPolygon poly; /// The 2D silhouette to be arranged + Vec2crd translation{0, 0}; /// The translation of the poly + double rotation{0.0}; /// The rotation of the poly in radians + int bed_idx{UNARRANGED}; /// To which logical bed does poly belong... - ArrangePolygon(const ExPolygon &p, const Vec2crd &tr = {}, double rot = 0.0) - : poly{p}, translation{tr}, rotation{rot} + ArrangePolygon(ExPolygon p, const Vec2crd &tr = {}, double rot = 0.0) + : poly{std::move(p)}, translation{tr}, rotation{rot} {} }; using ArrangePolygons = std::vector; /** - * \brief Arranges the model objects on the screen. + * \brief Arranges the input polygons. * - * The arrangement considers multiple bins (aka. print beds) for placing - * all the items provided in the model argument. If the items don't fit on - * one print bed, the remaining will be placed onto newly created print - * beds. The first_bin_only parameter, if set to true, disables this - * behavior and makes sure that only one print bed is filled and the - * remaining items will be untouched. When set to false, the items which - * could not fit onto the print bed will be placed next to the print bed so - * the user should see a pile of items on the print bed and some other - * piles outside the print area that can be dragged later onto the print - * bed as a group. + * WARNING: Currently, only convex polygons are supported by the libnest2d + * library which is used to do the arrangement. This might change in the future + * this is why the interface contains a general polygon capable to have holes. * - * \param items Input which are object pointers implementing the - * Arrangeable interface. + * \param items Input vector of ArrangePolygons. The transformation, rotation + * and bin_idx fields will be changed after the call finished and can be used + * to apply the result on the input polygon. * * \param min_obj_distance The minimum distance which is allowed for any * pair of items on the print bed in any direction. * - * \param bedhint Info about the shape and type of the - * bed. remaining items which do not fit onto the print area next to the - * print bed or leave them untouched (let the user arrange them by hand or - * remove them). + * \param bedhint Info about the shape and type of the bed. * * \param progressind Progress indicator callback called when * an object gets packed. The unsigned argument is the number of items @@ -127,7 +182,7 @@ void arrange(ArrangePolygons & items, std::function stopcondition = nullptr); /// Same as the previous, only that it takes unmovable items as an -/// additional argument. +/// additional argument. Those will be considered as already arranged objects. void arrange(ArrangePolygons & items, const ArrangePolygons & excludes, coord_t min_obj_distance, diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 966be4c88..5f296cc5f 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -372,35 +372,7 @@ static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb /* arrange objects preserving their instance count but altering their instance positions */ 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 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(); -// } - -// return true; - +{ size_t count = 0; for (auto obj : objects) count += obj->instances.size(); @@ -414,29 +386,23 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) instances.emplace_back(minst); } - arrangement::BedShapeHint bedhint; - - if (bb) { - bedhint.type = arrangement::BedShapeType::BOX; - bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max)); - } - + + if (bb) + bedhint = arrangement::BedShapeHint( + BoundingBox(scaled(bb->min), scaled(bb->max))); + arrangement::arrange(input, scaled(dist), bedhint); bool ret = true; for(size_t i = 0; i < input.size(); ++i) { - auto inst = instances[i]; - inst->set_rotation(Z, input[i].rotation); - auto tr = unscaled(input[i].translation); - inst->set_offset(X, tr.x()); - inst->set_offset(Y, tr.y()); - - if (input[i].bed_idx != 0) ret = false; // no logical beds are allowed + if (input[i].bed_idx == 0) { // no logical beds are allowed + instances[i]->apply_arrange_result(input[i].translation, + input[i].rotation); + } else ret = false; } - return ret; } @@ -1842,28 +1808,37 @@ void ModelInstance::transform_polygon(Polygon* polygon) const arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const { static const double SIMPLIFY_TOLERANCE_MM = 0.1; - - 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(); - ExPolygon ep; ep.contour = std::move(p); + if (!m_arrange_cache.valid) { + Vec3d rotation = get_rotation(); + rotation.z() = 0.; + Transform3d trafo_instance = + Geometry::assemble_transform(Vec3d::Zero(), rotation, + get_scaling_factor(), get_mirror()); - return {ep, Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, get_rotation(Z)}; + 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(); + m_arrange_cache.poly.contour = std::move(p); + m_arrange_cache.valid = true; + } + + arrangement::ArrangePolygon ret{m_arrange_cache.poly, + Vec2crd{scaled(get_offset(X)), + scaled(get_offset(Y))}, + get_rotation(Z)}; + + ret.bed_idx = m_arrange_cache.bed_idx; + + return ret; } // Test whether the two models contain the same number of ModelObjects with the same set of IDs diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2fd696d14..7ce790c7c 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -512,7 +512,7 @@ public: ModelObject* get_object() const { return this->object; } const Geometry::Transformation& get_transformation() const { return m_transformation; } - void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } + void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; m_arrange_cache.valid = false; } const Vec3d& get_offset() const { return m_transformation.get_offset(); } double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } @@ -523,21 +523,21 @@ public: const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } - void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); } - void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); } + void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); m_arrange_cache.valid = false; } + void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); if (axis != Z) m_arrange_cache.valid = false; } const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); } double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } - void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); } - void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); } + void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); m_arrange_cache.valid = false; } + void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); m_arrange_cache.valid = false; } const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } bool is_left_handed() const { return m_transformation.is_left_handed(); } - - void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } - void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } + + void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); m_arrange_cache.valid = false; } + void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); m_arrange_cache.valid = false; } // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; @@ -554,20 +554,17 @@ public: bool is_printable() const { return print_volume_state == PVS_Inside; } - // ///////////////////////////////////////////////////////////////////////// - // Implement arrangement::Arrangeable interface - // ///////////////////////////////////////////////////////////////////////// - // Getting the input polygon for arrange arrangement::ArrangePolygon get_arrange_polygon() const; // Apply the arrange result on the ModelInstance - void apply_arrange_result(Vec2crd offs, double rot_rads) + void apply_arrange_result(Vec2crd offs, double rot_rads, int bed_idx = 0) { // write the transformation data into the model instance set_rotation(Z, rot_rads); set_offset(X, unscale(offs(X))); set_offset(Y, unscale(offs(Y))); + m_arrange_cache.bed_idx = bed_idx; } protected: @@ -583,15 +580,28 @@ private: ModelObject* object; // Constructor, which assigns a new unique ID. - explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} + explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) + { + get_arrange_polygon(); // initialize the arrange cache + } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} + m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) + { + get_arrange_polygon(); // initialize the arrange cache + } ModelInstance() = delete; explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; ModelInstance& operator=(ModelInstance &&rhs) = delete; + + // Warning! This object is not guarded against concurrency. + mutable struct ArrangeCache { + bool valid = false; + int bed_idx { arrangement::UNARRANGED }; + ExPolygon poly; + } m_arrange_cache; }; // The print bed content. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4e3093489..771d092a7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5739,10 +5739,11 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads) +void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2crd off, double rotation_rads) { - m_pos = offset; - m_rotation = rotation_rads; + Vec2d offset = unscaled(off); + m_pos = offset; + m_rotation = rotation_rads; DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = m_pos(X); cfg.opt("wipe_tower_y", true)->value = m_pos(Y); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2316637d8..a28791ed0 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -624,7 +624,7 @@ public: return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } - void apply_arrange_result(Vec2d offset, double rotation_rads); + void apply_arrange_result(Vec2crd offset, double rotation_rads); arrangement::ArrangePolygon get_arrange_polygon() const { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3adfc0f05..f615c66d1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1272,147 +1272,290 @@ struct Plater::priv // objects would be frozen for the user. In case of arrange, an animation // could be shown, or with the optimize orientations, partial results // could be displayed. - class Job: public wxEvtHandler { - int m_range = 100; + class Job : public wxEvtHandler + { + int m_range = 100; std::future m_ftr; - priv *m_plater = nullptr; - std::atomic m_running {false}, m_canceled {false}; - bool m_finalized = false; - - void run() { - m_running.store(true); process(); m_running.store(false); - + priv * m_plater = nullptr; + std::atomic m_running{false}, m_canceled{false}; + bool m_finalized = false; + + void run() + { + m_running.store(true); + process(); + m_running.store(false); + // ensure to call the last status to finalize the job update_status(status_range(), ""); } - + protected: - // status range for a particular job virtual int status_range() const { return 100; } - + // status update, to be used from the work thread (process() method) - void update_status(int st, const wxString& msg = "") { - auto evt = new wxThreadEvent(); evt->SetInt(st); evt->SetString(msg); - wxQueueEvent(this, evt); + void update_status(int st, const wxString &msg = "") + { + auto evt = new wxThreadEvent(); + evt->SetInt(st); + evt->SetString(msg); + wxQueueEvent(this, evt); } - - priv& plater() { return *m_plater; } - bool was_canceled() const { return m_canceled.load(); } - + + priv &plater() { return *m_plater; } + bool was_canceled() const { return m_canceled.load(); } + // Launched just before start(), a job can use it to prepare internals virtual void prepare() {} - - // Launched when the job is finished. It refreshes the 3dscene by def. - virtual void finalize() { + + // Launched when the job is finished. It refreshes the 3Dscene by def. + virtual void finalize() + { // Do a full refresh of scene tree, including regenerating // all the GLVolumes. FIXME The update function shall just // reload the modified matrices. - if(! was_canceled()) - plater().update(true); + if (!was_canceled()) plater().update(true); } - + public: - - Job(priv *_plater): m_plater(_plater) + Job(priv *_plater) : m_plater(_plater) { - Bind(wxEVT_THREAD, [this](const wxThreadEvent& evt){ + Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { auto msg = evt.GetString(); - if(! msg.empty()) plater().statusbar()->set_status_text(msg); - - if(m_finalized) return; - + if (!msg.empty()) + plater().statusbar()->set_status_text(msg); + + if (m_finalized) return; + plater().statusbar()->set_progress(evt.GetInt()); - if(evt.GetInt() == status_range()) { - + if (evt.GetInt() == status_range()) { // set back the original range and cancel callback plater().statusbar()->set_range(m_range); plater().statusbar()->set_cancel_callback(); wxEndBusyCursor(); - + finalize(); - + // dont do finalization again for the same process m_finalized = true; } }); } - - // TODO: use this when we all migrated to VS2019 - // Job(const Job&) = delete; - // Job(Job&&) = default; - // Job& operator=(const Job&) = delete; - // Job& operator=(Job&&) = default; - Job(const Job&) = delete; - Job& operator=(const Job&) = delete; - Job(Job &&o) : - m_range(o.m_range), - m_ftr(std::move(o.m_ftr)), - m_plater(o.m_plater), - m_finalized(o.m_finalized) - { - m_running.store(o.m_running.load()); - m_canceled.store(o.m_canceled.load()); - } - + + Job(const Job &) = delete; + Job(Job &&) = default; + Job &operator=(const Job &) = delete; + Job &operator=(Job &&) = default; + virtual void process() = 0; - - void start() { // Start the job. No effect if the job is already running - if(! m_running.load()) { - - prepare(); - + + void start() + { // Start the job. No effect if the job is already running + if (!m_running.load()) { + prepare(); + // Save the current status indicatior range and push the new one m_range = plater().statusbar()->get_range(); plater().statusbar()->set_range(status_range()); - + // init cancellation flag and set the cancel callback m_canceled.store(false); - plater().statusbar()->set_cancel_callback( [this](){ - m_canceled.store(true); - }); - + plater().statusbar()->set_cancel_callback( + [this]() { m_canceled.store(true); }); + m_finalized = false; - + // Changing cursor to busy wxBeginBusyCursor(); - - try { // Execute the job + + try { // Execute the job m_ftr = std::async(std::launch::async, &Job::run, this); - } catch(std::exception& ) { - update_status(status_range(), - _(L("ERROR: not enough resources to execute a new job."))); + } catch (std::exception &) { + update_status(status_range(), + _(L("ERROR: not enough resources to " + "execute a new job."))); } - + // The state changes will be undone when the process hits the // last status value, in the status update handler (see ctor) } } - - // To wait for the running job and join the threads. False is returned - // if the timeout has been reached and the job is still running. Call - // cancel() before this fn if you want to explicitly end the job. - bool join(int timeout_ms = 0) const { - if(!m_ftr.valid()) return true; - - if(timeout_ms <= 0) + + // To wait for the running job and join the threads. False is + // returned if the timeout has been reached and the job is still + // running. Call cancel() before this fn if you want to explicitly + // end the job. + bool join(int timeout_ms = 0) const + { + if (!m_ftr.valid()) return true; + + if (timeout_ms <= 0) m_ftr.wait(); - else if(m_ftr.wait_for(std::chrono::milliseconds(timeout_ms)) == - std::future_status::timeout) + else if (m_ftr.wait_for(std::chrono::milliseconds( + timeout_ms)) == std::future_status::timeout) return false; - + return true; } - + bool is_running() const { return m_running.load(); } void cancel() { m_canceled.store(true); } }; - + enum class Jobs : size_t { Arrange, Rotoptimize }; + class ArrangeJob : public Job + { + // The gap between logical beds in the x axis expressed in ratio of + // the current bed width. + static const constexpr double LOGICAL_BED_GAP = 1. / 5.; + + // Cache the wti info + GLCanvas3D::WipeTowerInfo m_wti; + + // Cache the selected instances needed to write back the arrange + // result. The order of instances is the same as the arrange polys + struct IndexedArrangePolys { + ModelInstancePtrs insts; + arrangement::ArrangePolygons polys; + + void reserve(size_t cap) { insts.reserve(cap); polys.reserve(cap); } + void clear() { insts.clear(); polys.clear(); } + + void emplace_back(ModelInstance *inst) { + insts.emplace_back(inst); + polys.emplace_back(inst->get_arrange_polygon()); + } + + void swap(IndexedArrangePolys &pp) { + insts.swap(pp.insts); polys.swap(pp.polys); + } + }; + + IndexedArrangePolys m_selected, m_unselected; + + protected: + + void prepare() override + { + m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); + + // Get the selection map + Selection& sel = plater().get_selection(); + const Selection::ObjectIdxsToInstanceIdxsMap &selmap = + sel.get_content(); + + Model &model = plater().model; + + size_t count = 0; // To know how much space to reserve + for (auto obj : model.objects) count += obj->instances.size(); + + m_selected.clear(), m_unselected.clear(); + m_selected.reserve(count + 1 /* for optional wti */); + m_unselected.reserve(count + 1 /* for optional wti */); + + // Go through the objects and check if inside the selection + for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { + auto oit = selmap.find(int(oidx)); + + if (oit != selmap.end()) { // Object is selected + auto &iids = oit->second; + + // Go through instances and check if inside selection + size_t instcnt = model.objects[oidx]->instances.size(); + for (size_t iidx = 0; iidx < instcnt; ++iidx) { + auto instit = iids.find(iidx); + ModelInstance *oi = model.objects[oidx] + ->instances[iidx]; + + // Instance is selected + instit != iids.end() ? + m_selected.emplace_back(oi) : + m_unselected.emplace_back(oi); + } + } else // object not selected, all instances are unselected + for (ModelInstance *oi : model.objects[oidx]->instances) + m_unselected.emplace_back(oi); + } + + // If the selection is completely empty, consider all items as the + // selection + if (m_selected.insts.empty() && m_selected.polys.empty()) + m_selected.swap(m_unselected); + + if (m_wti) + sel.is_wipe_tower() ? + m_selected.polys.emplace_back(m_wti.get_arrange_polygon()) : + m_unselected.polys.emplace_back(m_wti.get_arrange_polygon()); + + // Stride between logical beds + double bedwidth = plater().bed_shape_bb().size().x(); + coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth); + + for (arrangement::ArrangePolygon &ap : m_selected.polys) + if (ap.bed_idx > 0) ap.translation.x() -= ap.bed_idx * stride; + + for (arrangement::ArrangePolygon &ap : m_unselected.polys) + if (ap.bed_idx > 0) ap.translation.x() -= ap.bed_idx * stride; + } + + public: + using Job::Job; + + + int status_range() const override + { + return int(m_selected.polys.size()); + } + + void process() override; + + void finalize() override { + + if (was_canceled()) { // Ignore the arrange result if aborted. + Job::finalize(); + return; + } + + // Stride between logical beds + double bedwidth = plater().bed_shape_bb().size().x(); + coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth); + + for(size_t i = 0; i < m_selected.insts.size(); ++i) { + if (m_selected.polys[i].bed_idx != arrangement::UNARRANGED) { + Vec2crd offs = m_selected.polys[i].translation; + double rot = m_selected.polys[i].rotation; + int bdidx = m_selected.polys[i].bed_idx; + offs.x() += bdidx * stride; + m_selected.insts[i]->apply_arrange_result(offs, rot, bdidx); + } + } + + // Handle the wipe tower + const arrangement::ArrangePolygon &wtipoly = m_selected.polys.back(); + if (m_wti && wtipoly.bed_idx != arrangement::UNARRANGED) { + Vec2crd o = wtipoly.translation; + double r = wtipoly.rotation; + o.x() += wtipoly.bed_idx * stride; + m_wti.apply_arrange_result(o, r); + } + + // Call original finalize (will update the scene) + Job::finalize(); + } + }; + + class RotoptimizeJob : public Job + { + public: + using Job::Job; + void process() override; + }; + // Jobs defined inside the group class will be managed so that only one can // run at a time. Also, the background process will be stopped if a job is // started. @@ -1422,84 +1565,8 @@ struct Plater::priv priv * m_plater; - class ArrangeJob : public Job - { - GLCanvas3D::WipeTowerInfo m_wti; - arrangement::ArrangePolygons m_selected, m_unselected; - - static std::array collect( - Model &model, const Selection &sel) - { - const Selection::ObjectIdxsToInstanceIdxsMap &selmap = - sel.get_content(); - - size_t count = 0; - for (auto obj : model.objects) count += obj->instances.size(); - - arrangement::ArrangePolygons 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->get_arrange_polygon()) : - selected.emplace_back(inst->get_arrange_polygon()); - } - } else // object not selected, all instances are unselected - for (auto inst : model.objects[oidx]->instances) - unselected.emplace_back(inst->get_arrange_polygon()); - } - - if (selected.empty()) selected.swap(unselected); - - return {selected, unselected}; - } - - protected: - - void prepare() override - { - m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); - - const Selection& sel = plater().get_selection(); - BoundingBoxf bedbb(plater().bed.get_shape()); - 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.get_arrange_polygon()) : - m_unselected.emplace_back(m_wti.get_arrange_polygon()); - } - - public: - using Job::Job; - int status_range() const override - { - return int(m_selected.size()); - } - void process() override; - } arrange_job{m_plater}; - - class RotoptimizeJob : public Job - { - public: - using Job::Job; - void process() override; - } rotoptimize_job{m_plater}; + ArrangeJob arrange_job{m_plater}; + RotoptimizeJob 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 @@ -2447,50 +2514,47 @@ void Plater::priv::sla_optimize_rotation() { } 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); - if (bed_shape_opt) { - auto &bedpoints = bed_shape_opt->values; - Polyline bedpoly; bedpoly.points.reserve(bedpoints.size()); - for (auto &v : bedpoints) bedpoly.append(scaled(v)); - bedshape = arrangement::bedShape(bedpoly); - } + if (!bed_shape_opt) return {}; - return bedshape; + auto &bedpoints = bed_shape_opt->values; + Polyline bedpoly; bedpoly.points.reserve(bedpoints.size()); + for (auto &v : bedpoints) bedpoly.append(scaled(v)); + + return arrangement::BedShapeHint(bedpoly); } -void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { - auto count = unsigned(m_selected.size()); - plater().model.arrange_objects(6.f, nullptr); -// static const auto arrangestr = _(L("Arranging")); +void Plater::priv::ArrangeJob::process() { + static const auto arrangestr = _(L("Arranging")); -// // 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); -// if (plater().printer_technology == ptFFF) { -// dist = PrintConfig::min_object_distance(plater().config); -// } + // 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); + if (plater().printer_technology == ptFFF) { + dist = PrintConfig::min_object_distance(plater().config); + } -// coord_t min_obj_distance = scaled(dist); -// auto count = unsigned(m_selected.size()); -// arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); + coord_t min_obj_distance = scaled(dist); + auto count = unsigned(m_selected.polys.size()); + arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); -// try { -// 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."))); -// } + try { + arrangement::arrange(m_selected.polys, m_unselected.polys, + 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."))); + } // finalize just here. update_status(int(count), @@ -2503,11 +2567,27 @@ void find_new_position(const Model & model, coord_t min_d, const arrangement::BedShapeHint &bedhint) { + arrangement::ArrangePolygons movable, fixed; - // TODO + for (const ModelObject *mo : model.objects) + for (const ModelInstance *inst : mo->instances) { + auto it = std::find(instances.begin(), instances.end(), inst); + if (it != instances.end()) + fixed.emplace_back(inst->get_arrange_polygon()); + } + + for (ModelInstance *inst : instances) + movable.emplace_back(inst->get_arrange_polygon()); + + arrangement::arrange(movable, fixed, min_d, bedhint); + + for (size_t i = 0; i < instances.size(); ++i) + if (movable[i].bed_idx == 0) + instances[i]->apply_arrange_result(movable[i].translation, + movable[i].rotation); } -void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process() +void Plater::priv::RotoptimizeJob::process() { int obj_idx = plater().get_selected_object_idx(); if (obj_idx < 0) { return; } From 5446167c1112fda5c2baefd10715ad0af48416d8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 15 Jul 2019 18:18:34 +0200 Subject: [PATCH 17/32] Fixes for arranging wipe tower --- src/slic3r/GUI/Plater.cpp | 90 ++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f615c66d1..3ae5c3005 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1481,16 +1481,16 @@ struct Plater::priv for (ModelInstance *oi : model.objects[oidx]->instances) m_unselected.emplace_back(oi); } - - // If the selection is completely empty, consider all items as the - // selection - if (m_selected.insts.empty() && m_selected.polys.empty()) - m_selected.swap(m_unselected); - + if (m_wti) sel.is_wipe_tower() ? m_selected.polys.emplace_back(m_wti.get_arrange_polygon()) : m_unselected.polys.emplace_back(m_wti.get_arrange_polygon()); + + // If the selection is completely empty, consider all items as the + // selection + if (m_selected.insts.empty() && m_selected.polys.empty()) + m_selected.swap(m_unselected); // Stride between logical beds double bedwidth = plater().bed_shape_bb().size().x(); @@ -1534,16 +1534,18 @@ struct Plater::priv m_selected.insts[i]->apply_arrange_result(offs, rot, bdidx); } } - + // Handle the wipe tower - const arrangement::ArrangePolygon &wtipoly = m_selected.polys.back(); - if (m_wti && wtipoly.bed_idx != arrangement::UNARRANGED) { - Vec2crd o = wtipoly.translation; - double r = wtipoly.rotation; - o.x() += wtipoly.bed_idx * stride; - m_wti.apply_arrange_result(o, r); + if (m_wti && m_selected.polys.size() > m_selected.insts.size()) { + auto &wtipoly = m_selected.polys.back(); + if (wtipoly.bed_idx != arrangement::UNARRANGED) { + Vec2crd o = wtipoly.translation; + double r = wtipoly.rotation; + o.x() += wtipoly.bed_idx * stride; + m_wti.apply_arrange_result(o, r); + } } - + // Call original finalize (will update the scene) Job::finalize(); } @@ -1638,6 +1640,8 @@ struct Plater::priv BoundingBoxf bed_shape_bb() const; BoundingBox scaled_bed_shape_bb() const; arrangement::BedShapeHint get_bed_shape_hint() const; + + void find_new_position(const ModelInstancePtrs &instances, coord_t min_d); 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); @@ -2527,6 +2531,32 @@ arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const { return arrangement::BedShapeHint(bedpoly); } +void Plater::priv::find_new_position(const ModelInstancePtrs &instances, coord_t min_d) +{ + arrangement::ArrangePolygons movable, fixed; + + for (const ModelObject *mo : model.objects) + for (const ModelInstance *inst : mo->instances) { + auto it = std::find(instances.begin(), instances.end(), inst); + auto arrpoly = inst->get_arrange_polygon(); + + if (it == instances.end()) + fixed.emplace_back(std::move(arrpoly)); + else + movable.emplace_back(std::move(arrpoly)); + } + + auto wti = view3D->get_canvas3d()->get_wipe_tower_info(); + if (wti) fixed.emplace_back(wti.get_arrange_polygon()); + + arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint()); + + for (size_t i = 0; i < instances.size(); ++i) + if (movable[i].bed_idx == 0) + instances[i]->apply_arrange_result(movable[i].translation, + movable[i].rotation); +} + void Plater::priv::ArrangeJob::process() { static const auto arrangestr = _(L("Arranging")); @@ -2562,31 +2592,6 @@ void Plater::priv::ArrangeJob::process() { : _(L("Arranging done."))); } -void find_new_position(const Model & model, - ModelInstancePtrs instances, - coord_t min_d, - const arrangement::BedShapeHint &bedhint) -{ - arrangement::ArrangePolygons movable, fixed; - - for (const ModelObject *mo : model.objects) - for (const ModelInstance *inst : mo->instances) { - auto it = std::find(instances.begin(), instances.end(), inst); - if (it != instances.end()) - fixed.emplace_back(inst->get_arrange_polygon()); - } - - for (ModelInstance *inst : instances) - movable.emplace_back(inst->get_arrange_polygon()); - - arrangement::arrange(movable, fixed, min_d, bedhint); - - for (size_t i = 0; i < instances.size(); ++i) - if (movable[i].bed_idx == 0) - instances[i]->apply_arrange_result(movable[i].translation, - movable[i].rotation); -} - void Plater::priv::RotoptimizeJob::process() { int obj_idx = plater().get_selected_object_idx(); @@ -2624,11 +2629,8 @@ void Plater::priv::RotoptimizeJob::process() oi->set_rotation(rt); } - - find_new_position(plater().model, - o->instances, - scaled(mindist), - plater().get_bed_shape_hint()); + + plater().find_new_position(o->instances, scaled(mindist)); // Correct the z offset of the object which was corrupted be // the rotation From 44801f442932dfc3c61c5bbe6aa2eff597e448b5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 16 Jul 2019 18:33:42 +0200 Subject: [PATCH 18/32] Priority for wipe tower, Increased safety distance from bed edges. * WipeTowerInfo class extended in plater (WipeTower) instead of GLCanvas3D * Bed origin support in ModelInstance and WipeTower --- src/libnest2d/include/libnest2d/libnest2d.hpp | 6 +- .../include/libnest2d/selections/firstfit.hpp | 5 +- src/libslic3r/Arrange.cpp | 3 +- src/libslic3r/Arrange.hpp | 13 +- src/libslic3r/Model.cpp | 34 +-- src/libslic3r/Model.hpp | 15 +- src/slic3r/GUI/GLCanvas3D.cpp | 5 +- src/slic3r/GUI/GLCanvas3D.hpp | 25 +-- src/slic3r/GUI/Plater.cpp | 211 +++++++++--------- 9 files changed, 164 insertions(+), 153 deletions(-) diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 11c032fae..d91b3c8f9 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -68,7 +68,7 @@ class _Item { BBCache(): valid(false) {} } bb_cache_; - int binid_{BIN_ID_UNSET}; + int binid_{BIN_ID_UNSET}, priority_{0}; bool fixed_{false}; public: @@ -130,8 +130,12 @@ public: inline bool isFixed() const noexcept { return fixed_; } inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } + inline void binId(int idx) { binid_ = idx; } inline int binId() const noexcept { return binid_; } + + inline void priority(int p) { priority_ = p; } + inline int priority() const noexcept { return priority_; } /** * @brief Convert the polygon to string representation. The format depends diff --git a/src/libnest2d/include/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp index 74207f8cf..5585e565d 100644 --- a/src/libnest2d/include/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -62,9 +62,10 @@ public: placers.back().configure(pconfig); placers.back().preload(ig); } - + auto sortfunc = [](Item& i1, Item& i2) { - return i1.area() > i2.area(); + int p1 = i1.priority(), p2 = i2.priority(); + return p1 == p2 ? i1.area() > i2.area() : p1 > p2; }; std::sort(store_.begin(), store_.end(), sortfunc); diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index fd3573699..8086dc3fd 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -584,6 +584,7 @@ void arrange(ArrangePolygons & arrangables, outp.back().rotation(rotation); outp.back().translation({offs.x(), offs.y()}); outp.back().binId(arrpoly.bed_idx); + outp.back().priority(arrpoly.priority); }; for (ArrangePolygon &arrangeable : arrangables) @@ -595,7 +596,7 @@ void arrange(ArrangePolygons & arrangables, for (Item &itm : fixeditems) itm.inflate(-2 * SCALED_EPSILON); // Integer ceiling the min distance from the bed perimeters - coord_t md = min_obj_dist - SCALED_EPSILON; + coord_t md = min_obj_dist - 2 * scaled(0.1 + EPSILON); md = (md % 2) ? md / 2 + 1 : md / 2; auto &cfn = stopcondition; diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index c705b612b..3391eb0d7 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -140,15 +140,18 @@ static const constexpr int UNARRANGED = -1; /// polygon belongs: UNARRANGED means no place for the polygon /// (also the initial state before arrange), 0..N means the index of the bed. /// Zero is the physical bed, larger than zero means a virtual bed. -struct ArrangePolygon { - const ExPolygon poly; /// The 2D silhouette to be arranged +struct ArrangePolygon { + ExPolygon poly; /// The 2D silhouette to be arranged Vec2crd translation{0, 0}; /// The translation of the poly double rotation{0.0}; /// The rotation of the poly in radians int bed_idx{UNARRANGED}; /// To which logical bed does poly belong... + int priority{0}; - ArrangePolygon(ExPolygon p, const Vec2crd &tr = {}, double rot = 0.0) - : poly{std::move(p)}, translation{tr}, rotation{rot} - {} + /// Optional setter function which can store arbitrary data in its closure + std::function setter = nullptr; + + /// Helper function to call the setter with the arrange data arguments + void apply() const { if (setter) setter(*this); } }; using ArrangePolygons = std::vector; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5f296cc5f..cf0f7c855 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -387,20 +387,24 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) } arrangement::BedShapeHint bedhint; - - if (bb) + coord_t bedwidth = 0; + + if (bb) { + bedwidth = scaled(bb->size().x()); bedhint = arrangement::BedShapeHint( BoundingBox(scaled(bb->min), scaled(bb->max))); + } arrangement::arrange(input, scaled(dist), bedhint); bool ret = true; + coord_t stride = bedwidth + bedwidth / 5; for(size_t i = 0; i < input.size(); ++i) { - if (input[i].bed_idx == 0) { // no logical beds are allowed - instances[i]->apply_arrange_result(input[i].translation, - input[i].rotation); - } else ret = false; + if (input[i].bed_idx != 0) ret = false; + if (input[i].bed_idx >= 0) + instances[i]->apply_arrange_result(input[i], + {input[i].bed_idx * stride, 0}); } return ret; @@ -1822,22 +1826,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const // this may happen for malformed models, see: // https://github.com/prusa3d/PrusaSlicer/issues/2209 - if (p.points.empty()) return {{}}; + if (p.points.empty()) return {}; Polygons pp{p}; pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); if (!pp.empty()) p = pp.front(); m_arrange_cache.poly.contour = std::move(p); + m_arrange_cache.bed_origin = {0, 0}; + m_arrange_cache.bed_idx = arrangement::UNARRANGED; m_arrange_cache.valid = true; } - arrangement::ArrangePolygon ret{m_arrange_cache.poly, - Vec2crd{scaled(get_offset(X)), - scaled(get_offset(Y))}, - get_rotation(Z)}; - - ret.bed_idx = m_arrange_cache.bed_idx; - + arrangement::ArrangePolygon ret; + ret.poly = m_arrange_cache.poly; + ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))} - + m_arrange_cache.bed_origin; + ret.rotation = get_rotation(Z); + ret.bed_idx = m_arrange_cache.bed_idx; + return ret; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 7ce790c7c..548e0f015 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -558,13 +558,15 @@ public: arrangement::ArrangePolygon get_arrange_polygon() const; // Apply the arrange result on the ModelInstance - void apply_arrange_result(Vec2crd offs, double rot_rads, int bed_idx = 0) + void apply_arrange_result(const arrangement::ArrangePolygon& ap, + const Vec2crd& bed_origin = {0, 0}) { // write the transformation data into the model instance - set_rotation(Z, rot_rads); - set_offset(X, unscale(offs(X))); - set_offset(Y, unscale(offs(Y))); - m_arrange_cache.bed_idx = bed_idx; + set_rotation(Z, ap.rotation); + set_offset(X, unscale(ap.translation(X) + bed_origin.x())); + set_offset(Y, unscale(ap.translation(Y) + bed_origin.y())); + m_arrange_cache.bed_origin = bed_origin; + m_arrange_cache.bed_idx = ap.bed_idx; } protected: @@ -599,8 +601,9 @@ private: // Warning! This object is not guarded against concurrency. mutable struct ArrangeCache { bool valid = false; - int bed_idx { arrangement::UNARRANGED }; + Vec2crd bed_origin {0, 0}; ExPolygon poly; + int bed_idx = arrangement::UNARRANGED; } m_arrange_cache; }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 771d092a7..c674172e3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5739,11 +5739,8 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2crd off, double rotation_rads) +void GLCanvas3D::WipeTowerInfo::apply_wipe_tower() const { - Vec2d offset = unscaled(off); - m_pos = offset; - m_rotation = rotation_rads; DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = m_pos(X); cfg.opt("wipe_tower_y", true)->value = m_pos(Y); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a28791ed0..6c5e6475c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -613,9 +613,10 @@ public: int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } class WipeTowerInfo { + protected: Vec2d m_pos = {std::nan(""), std::nan("")}; - Vec2d m_bb_size; - double m_rotation; + Vec2d m_bb_size = {0., 0.}; + double m_rotation = 0.; friend class GLCanvas3D; public: @@ -623,22 +624,12 @@ public: { return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } - - void apply_arrange_result(Vec2crd offset, double rotation_rads); - arrangement::ArrangePolygon get_arrange_polygon() const - { - Polygon p({ - {coord_t(0), coord_t(0)}, - {scaled(m_bb_size(X)), coord_t(0)}, - {scaled(m_bb_size)}, - {coord_t(0), scaled(m_bb_size(Y))}, - {coord_t(0), coord_t(0)}, - }); - - ExPolygon ep; ep.contour = std::move(p); - return {ep, scaled(m_pos), m_rotation}; - } + inline const Vec2d& pos() const { return m_pos; } + inline double rotation() const { return m_rotation; } + inline const Vec2d bb_size() const { return m_bb_size; } + + void apply_wipe_tower() const; }; WipeTowerInfo get_wipe_tower_info() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3ae5c3005..a6360a020 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1262,6 +1262,56 @@ struct Plater::priv BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; + // Cache the wti info + class WipeTower: public GLCanvas3D::WipeTowerInfo { + Vec2d m_bed_origin = {0., 0.}; + int m_bed_idx = arrangement::UNARRANGED; + friend priv; + public: + + void apply_arrange_result(const arrangement::ArrangePolygon& ap, + const Vec2crd& bedc) { + m_bed_origin = unscaled(bedc); + m_pos = unscaled(ap.translation) + m_bed_origin; + m_rotation = ap.rotation; + m_bed_idx = ap.bed_idx; + apply_wipe_tower(); + } + + arrangement::ArrangePolygon get_arrange_polygon() const + { + Polygon p({ + {coord_t(0), coord_t(0)}, + {scaled(m_bb_size(X)), coord_t(0)}, + {scaled(m_bb_size)}, + {coord_t(0), scaled(m_bb_size(Y))}, + {coord_t(0), coord_t(0)}, + }); + + arrangement::ArrangePolygon ret; + ret.poly.contour = std::move(p); + ret.translation = scaled(m_pos) - scaled(m_bed_origin); + ret.rotation = m_rotation; + ret.bed_idx = m_bed_idx; + return ret; + } + + // For future use + int bed_index() const { return m_bed_idx; } + }; + +private: + WipeTower m_wipetower; + +public: + WipeTower& wipe_tower() { + auto wti = view3D->get_canvas3d()->get_wipe_tower_info(); + m_wipetower.m_pos = wti.pos(); + m_wipetower.m_rotation = wti.rotation(); + m_wipetower.m_bb_size = wti.bb_size(); + return m_wipetower; + } + // A class to handle UI jobs like arranging and optimizing rotation. // These are not instant jobs, the user has to be informed about their // state in the status progress indicator. On the other hand they are @@ -1410,40 +1460,20 @@ struct Plater::priv class ArrangeJob : public Job { + using ArrangePolygon = arrangement::ArrangePolygon; + using ArrangePolygons = arrangement::ArrangePolygons; + // The gap between logical beds in the x axis expressed in ratio of // the current bed width. static const constexpr double LOGICAL_BED_GAP = 1. / 5.; + static const constexpr int UNARRANGED = arrangement::UNARRANGED; - // Cache the wti info - GLCanvas3D::WipeTowerInfo m_wti; - - // Cache the selected instances needed to write back the arrange - // result. The order of instances is the same as the arrange polys - struct IndexedArrangePolys { - ModelInstancePtrs insts; - arrangement::ArrangePolygons polys; - - void reserve(size_t cap) { insts.reserve(cap); polys.reserve(cap); } - void clear() { insts.clear(); polys.clear(); } - - void emplace_back(ModelInstance *inst) { - insts.emplace_back(inst); - polys.emplace_back(inst->get_arrange_polygon()); - } - - void swap(IndexedArrangePolys &pp) { - insts.swap(pp.insts); polys.swap(pp.polys); - } - }; - - IndexedArrangePolys m_selected, m_unselected; + ArrangePolygons m_selected, m_unselected; protected: void prepare() override { - m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); - // Get the selection map Selection& sel = plater().get_selection(); const Selection::ObjectIdxsToInstanceIdxsMap &selmap = @@ -1458,59 +1488,57 @@ struct Plater::priv m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); - // Go through the objects and check if inside the selection - for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { - auto oit = selmap.find(int(oidx)); - - if (oit != selmap.end()) { // Object is selected - auto &iids = oit->second; - - // Go through instances and check if inside selection - size_t instcnt = model.objects[oidx]->instances.size(); - for (size_t iidx = 0; iidx < instcnt; ++iidx) { - auto instit = iids.find(iidx); - ModelInstance *oi = model.objects[oidx] - ->instances[iidx]; - - // Instance is selected - instit != iids.end() ? - m_selected.emplace_back(oi) : - m_unselected.emplace_back(oi); - } - } else // object not selected, all instances are unselected - for (ModelInstance *oi : model.objects[oidx]->instances) - m_unselected.emplace_back(oi); - } - - if (m_wti) - sel.is_wipe_tower() ? - m_selected.polys.emplace_back(m_wti.get_arrange_polygon()) : - m_unselected.polys.emplace_back(m_wti.get_arrange_polygon()); - - // If the selection is completely empty, consider all items as the - // selection - if (m_selected.insts.empty() && m_selected.polys.empty()) - m_selected.swap(m_unselected); - // Stride between logical beds double bedwidth = plater().bed_shape_bb().size().x(); coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth); - for (arrangement::ArrangePolygon &ap : m_selected.polys) - if (ap.bed_idx > 0) ap.translation.x() -= ap.bed_idx * stride; + // Go through the objects and check if inside the selection + for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { + auto oit = selmap.find(int(oidx)); + ModelObject *mo = model.objects[oidx]; + + std::vector inst_sel(mo->instances.size(), false); + + if (oit != selmap.end()) + for (auto inst_id : oit->second) inst_sel[inst_id] = true; + + for (size_t i = 0; i < inst_sel.size(); ++i) { + ModelInstance *mi = mo->instances[i]; + ArrangePolygon ap = mi->get_arrange_polygon(); + ap.priority = 0; + ap.setter = [mi, stride](const ArrangePolygon &p) { + if (p.bed_idx != UNARRANGED) + mi->apply_arrange_result(p, {p.bed_idx * stride, 0}); + }; + + inst_sel[i] ? + m_selected.emplace_back(std::move(ap)) : + m_unselected.emplace_back(std::move(ap)); + } + } - for (arrangement::ArrangePolygon &ap : m_unselected.polys) - if (ap.bed_idx > 0) ap.translation.x() -= ap.bed_idx * stride; + auto& wti = plater().wipe_tower(); + if (wti) { + ArrangePolygon ap = wti.get_arrange_polygon(); + ap.setter = [&wti, stride](const ArrangePolygon &p) { + if (p.bed_idx != UNARRANGED) + wti.apply_arrange_result(p, {p.bed_idx * stride, 0}); + }; + + ap.priority = 1; + sel.is_wipe_tower() ? + m_selected.emplace_back(std::move(ap)) : + m_unselected.emplace_back(std::move(ap)); + } + + // If the selection was empty arrange everything + if (m_selected.empty()) m_selected.swap(m_unselected); } public: using Job::Job; - - int status_range() const override - { - return int(m_selected.polys.size()); - } + int status_range() const override { return int(m_selected.size()); } void process() override; @@ -1521,30 +1549,8 @@ struct Plater::priv return; } - // Stride between logical beds - double bedwidth = plater().bed_shape_bb().size().x(); - coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth); - - for(size_t i = 0; i < m_selected.insts.size(); ++i) { - if (m_selected.polys[i].bed_idx != arrangement::UNARRANGED) { - Vec2crd offs = m_selected.polys[i].translation; - double rot = m_selected.polys[i].rotation; - int bdidx = m_selected.polys[i].bed_idx; - offs.x() += bdidx * stride; - m_selected.insts[i]->apply_arrange_result(offs, rot, bdidx); - } - } - - // Handle the wipe tower - if (m_wti && m_selected.polys.size() > m_selected.insts.size()) { - auto &wtipoly = m_selected.polys.back(); - if (wtipoly.bed_idx != arrangement::UNARRANGED) { - Vec2crd o = wtipoly.translation; - double r = wtipoly.rotation; - o.x() += wtipoly.bed_idx * stride; - m_wti.apply_arrange_result(o, r); - } - } + // Apply the arrange result to all selected objects + for (ArrangePolygon &ap : m_selected) ap.apply(); // Call original finalize (will update the scene) Job::finalize(); @@ -2531,7 +2537,8 @@ arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const { return arrangement::BedShapeHint(bedpoly); } -void Plater::priv::find_new_position(const ModelInstancePtrs &instances, coord_t min_d) +void Plater::priv::find_new_position(const ModelInstancePtrs &instances, + coord_t min_d) { arrangement::ArrangePolygons movable, fixed; @@ -2546,15 +2553,14 @@ void Plater::priv::find_new_position(const ModelInstancePtrs &instances, coord_t movable.emplace_back(std::move(arrpoly)); } - auto wti = view3D->get_canvas3d()->get_wipe_tower_info(); - if (wti) fixed.emplace_back(wti.get_arrange_polygon()); + if (wipe_tower()) + fixed.emplace_back(m_wipetower.get_arrange_polygon()); arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint()); for (size_t i = 0; i < instances.size(); ++i) if (movable[i].bed_idx == 0) - instances[i]->apply_arrange_result(movable[i].translation, - movable[i].rotation); + instances[i]->apply_arrange_result(movable[i]); } void Plater::priv::ArrangeJob::process() { @@ -2567,16 +2573,15 @@ void Plater::priv::ArrangeJob::process() { dist = PrintConfig::min_object_distance(plater().config); } - coord_t min_obj_distance = scaled(dist); - auto count = unsigned(m_selected.polys.size()); + coord_t min_d = scaled(dist); + auto count = unsigned(m_selected.size()); arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); try { - arrangement::arrange(m_selected.polys, m_unselected.polys, - min_obj_distance, - bedshape, + arrangement::arrange(m_selected, m_unselected, min_d, bedshape, [this, count](unsigned st) { - if (st > 0) // will not finalize after last one + if (st > + 0) // will not finalize after last one update_status(count - st, arrangestr); }, [this]() { return was_canceled(); }); From 2728f411238049668938e5529e36fc03c81eb203 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Jul 2019 13:32:31 +0200 Subject: [PATCH 19/32] Solved issue with virtual bed management. libnest2d: Fix for unclosed polygons after merge. --- .../libnest2d/backends/clipper/geometries.hpp | 18 +++-- src/libslic3r/Model.cpp | 25 ++++--- src/libslic3r/Model.hpp | 15 ++-- src/slic3r/GUI/GLCanvas3D.hpp | 1 - src/slic3r/GUI/Plater.cpp | 72 +++++++++---------- 5 files changed, 64 insertions(+), 67 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index c51e0f5c9..2ca4f5d50 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -259,10 +259,12 @@ inline TMultiShape clipper_execute( poly.Contour.swap(pptr->Contour); assert(!pptr->IsHole()); - - if(pptr->IsOpen()) { + + if(!poly.Contour.empty() ) { auto front_p = poly.Contour.front(); - poly.Contour.emplace_back(front_p); + auto &back_p = poly.Contour.back(); + if(front_p.X != back_p.X || front_p.Y != back_p.X) + poly.Contour.emplace_back(front_p); } for(auto h : pptr->Childs) { processHole(h, poly); } @@ -274,10 +276,12 @@ inline TMultiShape clipper_execute( poly.Holes.emplace_back(std::move(pptr->Contour)); assert(pptr->IsHole()); - - if(pptr->IsOpen()) { - auto front_p = poly.Holes.back().front(); - poly.Holes.back().emplace_back(front_p); + + if(!poly.Contour.empty() ) { + auto front_p = poly.Contour.front(); + auto &back_p = poly.Contour.back(); + if(front_p.X != back_p.X || front_p.Y != back_p.X) + poly.Contour.emplace_back(front_p); } for(auto c : pptr->Childs) processPoly(c); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index cf0f7c855..abe8af16a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -402,9 +402,11 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) for(size_t i = 0; i < input.size(); ++i) { if (input[i].bed_idx != 0) ret = false; - if (input[i].bed_idx >= 0) - instances[i]->apply_arrange_result(input[i], - {input[i].bed_idx * stride, 0}); + if (input[i].bed_idx >= 0) { + input[i].translation += Vec2crd{input[i].bed_idx * stride, 0}; + instances[i]->apply_arrange_result(input[i].translation, + input[i].rotation); + } } return ret; @@ -1826,23 +1828,20 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const // 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(); + if (!p.points.empty()) { + Polygons pp{p}; + pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); + if (!pp.empty()) p = pp.front(); + } + m_arrange_cache.poly.contour = std::move(p); - m_arrange_cache.bed_origin = {0, 0}; - m_arrange_cache.bed_idx = arrangement::UNARRANGED; m_arrange_cache.valid = true; } arrangement::ArrangePolygon ret; ret.poly = m_arrange_cache.poly; - ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))} - - m_arrange_cache.bed_origin; + ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; ret.rotation = get_rotation(Z); - ret.bed_idx = m_arrange_cache.bed_idx; return ret; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 548e0f015..5a0dc9863 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -516,7 +516,7 @@ public: const Vec3d& get_offset() const { return m_transformation.get_offset(); } double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } - + void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); } void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); } @@ -558,15 +558,12 @@ public: arrangement::ArrangePolygon get_arrange_polygon() const; // Apply the arrange result on the ModelInstance - void apply_arrange_result(const arrangement::ArrangePolygon& ap, - const Vec2crd& bed_origin = {0, 0}) + void apply_arrange_result(const Vec2crd& offs, double rotation) { // write the transformation data into the model instance - set_rotation(Z, ap.rotation); - set_offset(X, unscale(ap.translation(X) + bed_origin.x())); - set_offset(Y, unscale(ap.translation(Y) + bed_origin.y())); - m_arrange_cache.bed_origin = bed_origin; - m_arrange_cache.bed_idx = ap.bed_idx; + set_rotation(Z, rotation); + set_offset(X, unscale(offs(X))); + set_offset(Y, unscale(offs(Y))); } protected: @@ -601,9 +598,7 @@ private: // Warning! This object is not guarded against concurrency. mutable struct ArrangeCache { bool valid = false; - Vec2crd bed_origin {0, 0}; ExPolygon poly; - int bed_idx = arrangement::UNARRANGED; } m_arrange_cache; }; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6c5e6475c..36d16035e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -4,7 +4,6 @@ #include #include -#include "libslic3r/Arrange.hpp" #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a6360a020..151642613 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1264,21 +1264,18 @@ struct Plater::priv // Cache the wti info class WipeTower: public GLCanvas3D::WipeTowerInfo { - Vec2d m_bed_origin = {0., 0.}; - int m_bed_idx = arrangement::UNARRANGED; + using ArrangePolygon = arrangement::ArrangePolygon; + static const constexpr auto UNARRANGED = arrangement::UNARRANGED; friend priv; public: - void apply_arrange_result(const arrangement::ArrangePolygon& ap, - const Vec2crd& bedc) { - m_bed_origin = unscaled(bedc); - m_pos = unscaled(ap.translation) + m_bed_origin; - m_rotation = ap.rotation; - m_bed_idx = ap.bed_idx; + void apply_arrange_result(const Vec2crd& tr, double rotation) + { + m_pos = unscaled(tr); m_rotation = rotation; apply_wipe_tower(); } - arrangement::ArrangePolygon get_arrange_polygon() const + ArrangePolygon get_arrange_polygon() const { Polygon p({ {coord_t(0), coord_t(0)}, @@ -1288,28 +1285,20 @@ struct Plater::priv {coord_t(0), coord_t(0)}, }); - arrangement::ArrangePolygon ret; + ArrangePolygon ret; ret.poly.contour = std::move(p); - ret.translation = scaled(m_pos) - scaled(m_bed_origin); + ret.translation = scaled(m_pos); ret.rotation = m_rotation; - ret.bed_idx = m_bed_idx; return ret; } - - // For future use - int bed_index() const { return m_bed_idx; } - }; + } wipetower; -private: - WipeTower m_wipetower; - -public: - WipeTower& wipe_tower() { + WipeTower& updated_wipe_tower() { auto wti = view3D->get_canvas3d()->get_wipe_tower_info(); - m_wipetower.m_pos = wti.pos(); - m_wipetower.m_rotation = wti.rotation(); - m_wipetower.m_bb_size = wti.bb_size(); - return m_wipetower; + wipetower.m_pos = wti.pos(); + wipetower.m_rotation = wti.rotation(); + wipetower.m_bb_size = wti.bb_size(); + return wipetower; } // A class to handle UI jobs like arranging and optimizing rotation. @@ -1506,26 +1495,31 @@ public: ModelInstance *mi = mo->instances[i]; ArrangePolygon ap = mi->get_arrange_polygon(); ap.priority = 0; - ap.setter = [mi, stride](const ArrangePolygon &p) { - if (p.bed_idx != UNARRANGED) - mi->apply_arrange_result(p, {p.bed_idx * stride, 0}); - }; + ap.bed_idx = ap.translation.x() / stride; + ap.setter = [mi, stride](const ArrangePolygon &p) { + if (p.bed_idx != UNARRANGED) { + auto t = p.translation; t.x() += p.bed_idx * stride; + mi->apply_arrange_result(t, p.rotation); + } + }; + inst_sel[i] ? m_selected.emplace_back(std::move(ap)) : m_unselected.emplace_back(std::move(ap)); } } - auto& wti = plater().wipe_tower(); + auto& wti = plater().updated_wipe_tower(); if (wti) { ArrangePolygon ap = wti.get_arrange_polygon(); + ap.bed_idx = ap.translation.x() / stride; + ap.priority = 1; // Wipe tower should be on physical bed ap.setter = [&wti, stride](const ArrangePolygon &p) { - if (p.bed_idx != UNARRANGED) - wti.apply_arrange_result(p, {p.bed_idx * stride, 0}); + auto t = p.translation; t.x() += p.bed_idx * stride; + wti.apply_arrange_result(t, p.rotation); }; - ap.priority = 1; sel.is_wipe_tower() ? m_selected.emplace_back(std::move(ap)) : m_unselected.emplace_back(std::move(ap)); @@ -1533,6 +1527,11 @@ public: // If the selection was empty arrange everything if (m_selected.empty()) m_selected.swap(m_unselected); + + // The strides have to be removed from the fixed items. For the + // arrangeable (selected) items it bed_idx is ignored and the + // translation is irrelevant. + for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; } public: @@ -2553,14 +2552,15 @@ void Plater::priv::find_new_position(const ModelInstancePtrs &instances, movable.emplace_back(std::move(arrpoly)); } - if (wipe_tower()) - fixed.emplace_back(m_wipetower.get_arrange_polygon()); + if (updated_wipe_tower()) + fixed.emplace_back(wipetower.get_arrange_polygon()); arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint()); for (size_t i = 0; i < instances.size(); ++i) if (movable[i].bed_idx == 0) - instances[i]->apply_arrange_result(movable[i]); + instances[i]->apply_arrange_result(movable[i].translation, + movable[i].rotation); } void Plater::priv::ArrangeJob::process() { From c74e6513d9358796f98a4d2137c8b665f167e2df Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Jul 2019 14:34:28 +0200 Subject: [PATCH 20/32] Fix for msvc build. --- src/libslic3r/Arrange.cpp | 12 +++++++----- src/libslic3r/MTUtils.hpp | 5 ++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 8086dc3fd..63398fd0f 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -108,7 +108,7 @@ double fixed_overfit(const std::tuple& result, const Box &binbb) double score = std::get<0>(result); Box pilebb = std::get<1>(result); Box fullbb = sl::boundingBox(pilebb, binbb); - double diff = fullbb.area() - binbb.area(); + auto diff = double(fullbb.area()) - binbb.area(); if(diff > 0) score += diff; return score; @@ -138,6 +138,11 @@ protected: Box m_pilebb; // The bounding box of the merged pile. ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_items; // The items to be packed + + template> double norm(T val) + { + return double(val) / m_norm; + } // This is "the" object function which is evaluated many times for each // vertex (decimated with the accuracy parameter) of each object. @@ -178,9 +183,6 @@ protected: // Density is the pack density: how big is the arranged pile double density = 0; - const double N = m_norm; - auto norm = [N](double val) { return val / N; }; - // Distinction of cases for the arrangement scene enum e_cases { // This branch is for big items in a mixed (big and small) scene @@ -593,7 +595,7 @@ void arrange(ArrangePolygons & arrangables, for (const ArrangePolygon &fixed: excludes) process_arrangeable(fixed, fixeditems); - for (Item &itm : fixeditems) itm.inflate(-2 * SCALED_EPSILON); + for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON)); // Integer ceiling the min distance from the bed perimeters coord_t md = min_obj_dist - 2 * scaled(0.1 + EPSILON); diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 01f0095bf..24542558c 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -281,9 +281,8 @@ using EigenVec = Eigen::Matrix; // Conversion definition from unscaled to floating point scaled template, - class = FloatingOnly> -inline SLIC3R_CONSTEXPR Tout scaled(const Tin &v) SLIC3R_NOEXCEPT + class = FloatingOnly> +inline SLIC3R_CONSTEXPR FloatingOnly scaled(const Tin &v) SLIC3R_NOEXCEPT { return Tout(v / Tin(SCALING_FACTOR)); } From aff1863aed6450c05712b59847571052cdff8c1e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Jul 2019 16:47:09 +0200 Subject: [PATCH 21/32] Really fix build for msvc --- src/libslic3r/Arrange.cpp | 9 +++++---- src/libslic3r/MTUtils.hpp | 38 ++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 63398fd0f..bc3eedb63 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -46,11 +46,12 @@ template struct NfpImpl namespace Slic3r { -template> -inline SLIC3R_CONSTEXPR EigenVec unscaled( +template, int...EigenArgs> +inline SLIC3R_CONSTEXPR Eigen::Matrix unscaled( const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT { - return EigenVec{unscaled(v.X), unscaled(v.Y)}; + return Eigen::Matrix{unscaled(v.X), + unscaled(v.Y)}; } namespace arrangement { @@ -139,7 +140,7 @@ protected: ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_items; // The items to be packed - template> double norm(T val) + template ArithmeticOnly norm(T val) { return double(val) / m_norm; } diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 24542558c..a9f2c0274 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -260,18 +260,14 @@ template struct is_scaled_coord }; // Meta predicates for floating, 'scaled coord' and generic arithmetic types -template -using FloatingOnly = enable_if_t::value, T>; +template +using FloatingOnly = enable_if_t::value, O>; -template -using ScaledCoordOnly = enable_if_t::value, T>; +template +using ScaledCoordOnly = enable_if_t::value, O>; -template -using ArithmeticOnly = enable_if_t::value, T>; - -// A shorter form for a generic Eigen vector which is widely used in PrusaSlicer -template -using EigenVec = Eigen::Matrix; +template +using ArithmeticOnly = enable_if_t::value, O>; // Semantics are the following: // Upscaling (scaled()): only from floating point types (or Vec) to either @@ -282,7 +278,7 @@ using EigenVec = Eigen::Matrix; template> -inline SLIC3R_CONSTEXPR FloatingOnly scaled(const Tin &v) SLIC3R_NOEXCEPT +inline constexpr FloatingOnly scaled(const Tin &v) noexcept { return Tout(v / Tin(SCALING_FACTOR)); } @@ -292,15 +288,20 @@ inline SLIC3R_CONSTEXPR FloatingOnly scaled(const Tin &v) SLIC3R_NOEXCEPT // it can be different for integers but it does not have to be. Using // std::round means loosing noexcept and constexpr modifiers template> -inline SLIC3R_CONSTEXPR ScaledCoordOnly scaled(const Tin &v) SLIC3R_NOEXCEPT +inline constexpr ScaledCoordOnly scaled(const Tin &v) noexcept { //return static_cast(std::round(v / SCALING_FACTOR)); return Tout(v / Tin(SCALING_FACTOR)); } // Conversion for Eigen vectors (N dimensional points) -template> -inline EigenVec, N> scaled(const EigenVec &v) +template, + int...EigenArgs> +inline Eigen::Matrix, N, EigenArgs...> +scaled(const Eigen::Matrix &v) { return (v / SCALING_FACTOR).template cast(); } @@ -310,7 +311,7 @@ template, class = FloatingOnly> -inline SLIC3R_CONSTEXPR Tout unscaled(const Tin &v) SLIC3R_NOEXCEPT +inline constexpr Tout unscaled(const Tin &v) noexcept { return Tout(v * Tout(SCALING_FACTOR)); } @@ -321,9 +322,10 @@ template, - class = FloatingOnly> -inline SLIC3R_CONSTEXPR EigenVec unscaled( - const EigenVec &v) SLIC3R_NOEXCEPT + class = FloatingOnly, + int...EigenArgs> +inline constexpr Eigen::Matrix +unscaled(const Eigen::Matrix &v) noexcept { return v.template cast() * SCALING_FACTOR; } From c82fb9c84fa9a72b41e8cad8445f65b4b4038b06 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Jul 2019 17:19:42 +0200 Subject: [PATCH 22/32] libnest2d: Remove old preload method from selector interface --- src/libnest2d/include/libnest2d/libnest2d.hpp | 11 ----------- .../libnest2d/selections/selection_boilerplate.hpp | 2 -- 2 files changed, 13 deletions(-) diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index d91b3c8f9..d0d91838f 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -737,17 +737,6 @@ public: return impl_.getResult(); } - /** - * @brief Loading a group of already packed bins. It is best to use a result - * from a previous packing. The algorithm will consider this input as if the - * objects are already packed and not move them. If any of these items are - * outside the bin, it is up to the placer algorithm what will happen. - * Packing additional items can fail for the bottom-left and nfp placers. - * @param pckgrp A packgroup which is a vector of item vectors. Each item - * vector corresponds to a packed bin. - */ - inline void preload(const PackGroup& pckgrp) { impl_.preload(pckgrp); } - void clear() { impl_.clear(); } }; diff --git a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp index fd6577d97..9ae92fe29 100644 --- a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp @@ -22,8 +22,6 @@ public: inline void stopCondition(StopCondition cond) { stopcond_ = cond; } - inline void preload(const PackGroup& pckgrp) { packed_bins_ = pckgrp; } - inline void clear() { packed_bins_.clear(); } protected: From 6949543912ab3974bc65f3ece28f173f33052dd2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Jul 2019 18:10:08 +0200 Subject: [PATCH 23/32] Fixes after merge with master. --- src/libslic3r/Model.cpp | 2 +- src/libslic3r/Model.hpp | 20 ++++++++++++++------ src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ed1c1bc00..ed8be8ce5 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1992,4 +1992,4 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model) -#endif \ No newline at end of file +#endif diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 463d7b58e..f5786812f 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -623,10 +623,18 @@ private: ModelObject* object; // Constructor, which assigns a new unique ID. - explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) { assert(this->id().valid()); } + explicit ModelInstance(ModelObject *object) : print_volume_state(PVS_Inside), object(object) + { + assert(this->id().valid()); + get_arrange_polygon(); // initialize the cache + } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) { assert(this->id().valid() && this->id() != other.id()); } + m_transformation(other.m_transformation), print_volume_state(PVS_Inside), object(object) + { + assert(this->id().valid() && this->id() != other.id()); + get_arrange_polygon(); // initialize the cache + } explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; @@ -641,10 +649,10 @@ private: } // Warning! This object is not guarded against concurrency. - // mutable struct ArrangeCache { - // bool valid = false; - // ExPolygon poly; - // } m_arrange_cache; + mutable struct ArrangeCache { + bool valid = false; + ExPolygon poly; + } m_arrange_cache; }; // The print bed content. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 77a727108..3823f944a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3410,9 +3410,9 @@ GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const if (vol->is_wipe_tower) { wti.m_pos = Vec2d(m_config->opt_float("wipe_tower_x"), m_config->opt_float("wipe_tower_y")); - wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); + wti.m_rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); const BoundingBoxf3& bb = vol->bounding_box(); - wti.bb_size = Vec2d(bb.size()(0), bb.size()(1)); + wti.m_bb_size = Vec2d(bb.size().x(), bb.size().y()); break; } } From 99b8e08e674711b0d8a63919c0ffac2b34f5755f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 18 Jul 2019 12:58:28 +0200 Subject: [PATCH 24/32] Remove arrange cache. --- src/libslic3r/Model.cpp | 45 ++++++++++++++++++----------------------- src/libslic3r/Model.hpp | 32 +++++++++-------------------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ed8be8ce5..cdea83fb7 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1829,33 +1829,28 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const { static const double SIMPLIFY_TOLERANCE_MM = 0.1; - if (!m_arrange_cache.valid) { - 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()) { - Polygons pp{p}; - pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); - if (!pp.empty()) p = pp.front(); - } - - m_arrange_cache.poly.contour = std::move(p); - m_arrange_cache.valid = true; - } + 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()) { + Polygons pp{p}; + pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); + if (!pp.empty()) p = pp.front(); + } + arrangement::ArrangePolygon ret; - ret.poly = m_arrange_cache.poly; - ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; - ret.rotation = get_rotation(Z); + ret.poly.contour = std::move(p); + ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; + ret.rotation = get_rotation(Z); return ret; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index f5786812f..008699818 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -555,7 +555,7 @@ public: ModelObject* get_object() const { return this->object; } const Geometry::Transformation& get_transformation() const { return m_transformation; } - void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; m_arrange_cache.valid = false; } + void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } const Vec3d& get_offset() const { return m_transformation.get_offset(); } double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } @@ -566,21 +566,21 @@ public: const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } - void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); m_arrange_cache.valid = false; } - void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); if (axis != Z) m_arrange_cache.valid = false; } + void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); } + void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); } const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); } double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } - void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); m_arrange_cache.valid = false; } - void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); m_arrange_cache.valid = false; } + void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); } + void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); } const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } bool is_left_handed() const { return m_transformation.is_left_handed(); } - void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); m_arrange_cache.valid = false; } - void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); m_arrange_cache.valid = false; } + void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } + void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; @@ -623,18 +623,10 @@ private: ModelObject* object; // Constructor, which assigns a new unique ID. - explicit ModelInstance(ModelObject *object) : print_volume_state(PVS_Inside), object(object) - { - assert(this->id().valid()); - get_arrange_polygon(); // initialize the cache - } + explicit ModelInstance(ModelObject *object) : print_volume_state(PVS_Inside), object(object) { assert(this->id().valid()); } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_transformation(other.m_transformation), print_volume_state(PVS_Inside), object(object) - { - assert(this->id().valid() && this->id() != other.id()); - get_arrange_polygon(); // initialize the cache - } + m_transformation(other.m_transformation), print_volume_state(PVS_Inside), object(object) { assert(this->id().valid() && this->id() != other.id()); } explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; @@ -647,12 +639,6 @@ private: template void serialize(Archive &ar) { ar(m_transformation, print_volume_state); } - - // Warning! This object is not guarded against concurrency. - mutable struct ArrangeCache { - bool valid = false; - ExPolygon poly; - } m_arrange_cache; }; // The print bed content. From e3ca95152c7754f6700d06eee2100bf71a432d29 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 18 Jul 2019 16:30:32 +0200 Subject: [PATCH 25/32] Minor refactor --- src/libslic3r/Arrange.hpp | 5 ++++- src/slic3r/GUI/Plater.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 3391eb0d7..c02393dd9 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -127,7 +127,7 @@ public: }; /// A logical bed representing an object not being arranged. Either the arrange -/// has not yet succesfully run on this ArrangePolygon or it could not fit the +/// has not yet successfully run on this ArrangePolygon or it could not fit the /// object due to overly large size or invalid geometry. static const constexpr int UNARRANGED = -1; @@ -152,6 +152,9 @@ struct ArrangePolygon { /// Helper function to call the setter with the arrange data arguments void apply() const { if (setter) setter(*this); } + + /// Test if arrange() was called previously and gave a successful result. + bool is_arranged() const { return bed_idx != UNARRANGED; } }; using ArrangePolygons = std::vector; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0c7a8a3ee..d4697e7b3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1345,7 +1345,6 @@ struct Plater::priv // Cache the wti info class WipeTower: public GLCanvas3D::WipeTowerInfo { using ArrangePolygon = arrangement::ArrangePolygon; - static const constexpr auto UNARRANGED = arrangement::UNARRANGED; friend priv; public: @@ -1535,7 +1534,6 @@ struct Plater::priv // The gap between logical beds in the x axis expressed in ratio of // the current bed width. static const constexpr double LOGICAL_BED_GAP = 1. / 5.; - static const constexpr int UNARRANGED = arrangement::UNARRANGED; ArrangePolygons m_selected, m_unselected; @@ -1578,7 +1576,7 @@ struct Plater::priv ap.bed_idx = ap.translation.x() / stride; ap.setter = [mi, stride](const ArrangePolygon &p) { - if (p.bed_idx != UNARRANGED) { + if (p.is_arranged()) { auto t = p.translation; t.x() += p.bed_idx * stride; mi->apply_arrange_result(t, p.rotation); } @@ -1596,8 +1594,10 @@ struct Plater::priv ap.bed_idx = ap.translation.x() / stride; ap.priority = 1; // Wipe tower should be on physical bed ap.setter = [&wti, stride](const ArrangePolygon &p) { - auto t = p.translation; t.x() += p.bed_idx * stride; - wti.apply_arrange_result(t, p.rotation); + if (p.is_arranged()) { + auto t = p.translation; t.x() += p.bed_idx * stride; + wti.apply_arrange_result(t, p.rotation); + } }; sel.is_wipe_tower() ? From 6ae50a710a97e8379cb2f85392b0a6c90abd7d40 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 18 Jul 2019 17:31:11 +0200 Subject: [PATCH 26/32] Further refactoring --- src/libnest2d/include/libnest2d.h | 4 +-- src/libnest2d/include/libnest2d/libnest2d.hpp | 34 +++++++++---------- src/libnest2d/tests/test.cpp | 4 +-- src/libslic3r/Arrange.cpp | 8 ++--- src/slic3r/GUI/Plater.cpp | 2 +- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h index 5f7a29dfb..4661b4574 100644 --- a/src/libnest2d/include/libnest2d.h +++ b/src/libnest2d/include/libnest2d.h @@ -65,7 +65,7 @@ void nest(Iterator from, Iterator to, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { - Nester nester(bin, dist, pconf, sconf); + _Nester nester(bin, dist, pconf, sconf); nester.execute(from, to); } @@ -80,7 +80,7 @@ void nest(Iterator from, Iterator to, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { - Nester nester(bin, dist, pconf, sconf); + _Nester nester(bin, dist, pconf, sconf); if(prg) nester.progressIndicator(prg); if(scond) nester.stopCondition(scond); nester.execute(from, to); diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index d0d91838f..29d52c10f 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -741,12 +741,11 @@ public: }; /** - * The Arranger is the front-end class for the libnest2d library. It takes the - * input items and outputs the items with the proper transformations to be - * inside the provided bin. + * The _Nester is the front-end class for the libnest2d library. It takes the + * input items and changes their transformations to be inside the provided bin. */ template -class Nester { +class _Nester { using TSel = SelectionStrategyLike; TSel selector_; public: @@ -771,12 +770,12 @@ private: using TSItem = remove_cvref_t; StopCondition stopfn_; - - template using TVal = remove_cvref_t; + + template using TVal = remove_ref_t; template - using ConvertibleOnly = - enable_if_t< std::is_convertible, TPItem>::value, void>; + using ItemIteratorOnly = + enable_if_t&, TPItem&>::value, Out>; public: @@ -789,10 +788,8 @@ public: template - Nester( TBinType&& bin, - Coord min_obj_distance = 0, - const PConf& pconfig = PConf(), - const SConf& sconfig = SConf()): + _Nester(TBinType&& bin, Coord min_obj_distance = 0, + const PConf& pconfig = PConf(), const SConf& sconfig = SConf()): bin_(std::forward(bin)), pconfig_(pconfig), min_obj_distance_(min_obj_distance) @@ -817,14 +814,17 @@ public: } /** - * \brief Arrange an input sequence and return a PackGroup object with - * the packed groups corresponding to the bins. + * \brief Arrange an input sequence of _Item-s. + * + * To get the result, call the translation(), rotation() and binId() + * methods of each item. If only the transformed polygon is needed, call + * transformedShape() to get the properly transformed shapes. * * The number of groups in the pack group is the number of bins opened by * the selection algorithm. */ template - inline ConvertibleOnly execute(It from, It to) + inline ItemIteratorOnly execute(It from, It to) { auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); if(infl > 0) std::for_each(from, to, [this, infl](Item& item) { @@ -840,13 +840,13 @@ public: } /// Set a progress indicator function object for the selector. - inline Nester& progressIndicator(ProgressFunction func) + inline _Nester& progressIndicator(ProgressFunction func) { selector_.progressIndicator(func); return *this; } /// Set a predicate to tell when to abort nesting. - inline Nester& stopCondition(StopCondition fn) + inline _Nester& stopCondition(StopCondition fn) { stopfn_ = fn; selector_.stopCondition(fn); return *this; } diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index 6891b16e1..29577344d 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -370,7 +370,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) ASSERT_EQ(getX(bin.center()), 105); ASSERT_EQ(getY(bin.center()), 125); - Nester arrange(bin); + _Nester arrange(bin); arrange.execute(rects.begin(), rects.end()); @@ -438,7 +438,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) Coord min_obj_distance = 5; - Nester arrange(bin, min_obj_distance); + _Nester arrange(bin, min_obj_distance); arrange.execute(rects.begin(), rects.end()); diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index bc3eedb63..b02efa55c 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -66,10 +66,6 @@ using Circle = _Circle; using Segment = _Segment; using MultiPolygon = TMultiShape; -// The return value of nesting, a vector (for each logical bed) of Item -// reference vectors. -using PackGroup = _PackGroup; - // Summon the spatial indexing facilities from boost namespace bgi = boost::geometry::index; using SpatElement = std::pair; @@ -102,7 +98,7 @@ void fillConfig(PConf& pcfg) { pcfg.parallel = true; } -// Apply penality to object function result. This is used only when alignment +// Apply penalty to object function result. This is used only when alignment // after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN) double fixed_overfit(const std::tuple& result, const Box &binbb) { @@ -123,7 +119,7 @@ public: // Useful type shortcuts... using Placer = typename placers::_NofitPolyPlacer; using Selector = selections::_FirstFitSelection; - using Packer = Nester; + using Packer = _Nester; using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d4697e7b3..58f0db292 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1609,7 +1609,7 @@ struct Plater::priv if (m_selected.empty()) m_selected.swap(m_unselected); // The strides have to be removed from the fixed items. For the - // arrangeable (selected) items it bed_idx is ignored and the + // arrangeable (selected) items bed_idx is ignored and the // translation is irrelevant. for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; } From 72ed8c034e17c89d37f8ab0eaba309da669621ad Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Jul 2019 12:34:27 +0200 Subject: [PATCH 27/32] Fix for incorrect inside check of fixed items. libnest2d: Add dispatched overloads for offsetting different shapes. --- .../libnest2d/backends/clipper/geometries.hpp | 11 ++++++- .../include/libnest2d/geometry_traits.hpp | 32 ++++++++++++++++--- src/libslic3r/Arrange.cpp | 25 +++++++++------ 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 2ca4f5d50..06afbd187 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -71,7 +71,8 @@ template<> inline ClipperLib::cInt& y(PointImpl& p) namespace shapelike { -template<> inline void offset(PolygonImpl& sh, TCoord distance) +template<> +inline void offset(PolygonImpl& sh, TCoord distance, const PolygonTag&) { #define DISABLE_BOOST_OFFSET @@ -123,6 +124,14 @@ template<> inline void offset(PolygonImpl& sh, TCoord distance) } } +template<> +inline void offset(PathImpl& sh, TCoord distance, const PathTag&) +{ + PolygonImpl p(std::move(sh)); + offset(p, distance, PolygonTag()); + sh = p.Contour; +} + // Tell libnest2d how to make string out of a ClipperPolygon object template<> inline std::string toString(const PolygonImpl& sh) { diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index c4f2fcaca..827e2d8ba 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -507,15 +507,13 @@ enum class Formats { namespace shapelike { template -inline S create(const TContour& contour, - const THolesContainer& holes) +inline S create(const TContour& contour, const THolesContainer& holes) { return S(contour, holes); } template -inline S create(TContour&& contour, - THolesContainer&& holes) +inline S create(TContour&& contour, THolesContainer&& holes) { return S(contour, holes); } @@ -727,11 +725,18 @@ inline void translate(S& /*sh*/, const P& /*offs*/) } template -inline void offset(S& /*sh*/, TCoord> /*distance*/) +inline void offset(S& /*sh*/, TCoord /*distance*/, const PathTag&) { dout() << "The current geometry backend does not support offsetting!\n"; } +template +inline void offset(S& sh, TCoord distance, const PolygonTag&) +{ + offset(contour(sh), distance); + for(auto &h : holes(sh)) offset(h, -distance); +} + template inline std::pair isValid(const S& /*sh*/) { @@ -1228,6 +1233,23 @@ template inline bool isConvex(const S& sh) // dispatch return isConvex(sh, Tag()); } +template inline void offset(Box& bb, TCoord d, const BoxTag&) +{ + TPoint md{d, d}; + bb.minCorner() -= md; + bb.maxCorner() += md; +} + +template inline void offset(C& circ, TCoord d, const CircleTag&) +{ + circ.radius(circ.radius() + double(d)); +} + +// Dispatch function +template inline void offset(S& sh, TCoord d) { + offset(sh, d, Tag()); +} + } #define DECLARE_MAIN_TYPES(T) \ diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index b02efa55c..f692f91c7 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -514,19 +514,30 @@ void _arrange( coord_t minobjd, std::function prind, std::function stopfn) -{ - AutoArranger arranger{bin, minobjd, prind, stopfn}; +{ + // Integer ceiling the min distance from the bed perimeters + coord_t md = minobjd - 2 * scaled(0.1 + EPSILON); + md = (md % 2) ? md / 2 + 1 : md / 2; + + auto corrected_bin = bin; + sl::offset(corrected_bin, md); + + AutoArranger arranger{corrected_bin, 0, prind, stopfn}; + + auto infl = coord_t(std::ceil(minobjd / 2.0)); + for (Item& itm : shapes) itm.inflate(infl); + for (Item& itm : excludes) itm.inflate(infl); auto it = excludes.begin(); while (it != excludes.end()) - sl::isInside(it->transformedShape(), bin) ? + sl::isInside(it->transformedShape(), corrected_bin) ? ++it : it = excludes.erase(it); // If there is something on the plate if(!excludes.empty()) { arranger.preload(excludes); - auto binbb = sl::boundingBox(bin); + auto binbb = sl::boundingBox(corrected_bin); // Try to put the first item to the center, as the arranger // will not do this for us. @@ -548,6 +559,7 @@ void _arrange( for (auto &itm : excludes) inp.emplace_back(itm); arranger(inp.begin(), inp.end()); + for (Item &itm : inp) itm.inflate(-infl); } // The final client function for arrangement. A progress indicator and @@ -594,10 +606,6 @@ void arrange(ArrangePolygons & arrangables, for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON)); - // Integer ceiling the min distance from the bed perimeters - coord_t md = min_obj_dist - 2 * scaled(0.1 + EPSILON); - md = (md % 2) ? md / 2 + 1 : md / 2; - auto &cfn = stopcondition; auto &pri = progressind; @@ -605,7 +613,6 @@ void arrange(ArrangePolygons & arrangables, case bsBox: { // Create the arranger for the box shaped bed BoundingBox bbb = bedhint.get_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)}}; _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn); From 63fada9469a4285212f70782863f713fbf45bd02 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Jul 2019 18:10:10 +0200 Subject: [PATCH 28/32] Arrange selection if shift is pressed. Remove first item centering --- src/libslic3r/Arrange.cpp | 57 ++++++++++++--------- src/slic3r/GUI/Plater.cpp | 105 +++++++++++++++++++++++--------------- 2 files changed, 97 insertions(+), 65 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index f692f91c7..77f4f55db 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -136,6 +136,9 @@ protected: ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_items; // The items to be packed + // Used only for preloading objects before arrange + // std::vector m_preload_idx; // spatial index for preloaded beds + template ArithmeticOnly norm(T val) { return double(val) / m_norm; @@ -318,6 +321,7 @@ public: m_pilebb = sl::boundingBox(merged_pile); m_rtree.clear(); +// m_preload_idx.clear(); m_smallsrtree.clear(); // We will treat big items (compared to the print bed) differently @@ -341,7 +345,7 @@ public: } template inline void operator()(Args&&...args) { - m_rtree.clear(); + m_rtree.clear(); /*m_preload_idx.clear();*/ m_pck.execute(std::forward(args)...); } @@ -358,19 +362,25 @@ public: for(unsigned idx = 0; idx < fixeditems.size(); ++idx) { Item& itm = fixeditems[idx]; itm.markAsFixed(); - m_rtree.insert({itm.boundingBox(), idx}); +// size_t bedidx = itm.binId() < 0 ? 0u : size_t(itm.binId()); + +// while (m_preload_idx.size() <= bedidx) m_preload_idx.emplace_back(); +// m_preload_idx[bedidx].insert({itm.boundingBox(), idx}); } m_pck.configure(m_pconf); } - bool is_colliding(const Item& item) { - if(m_rtree.empty()) return false; - std::vector result; - m_rtree.query(bgi::intersects(item.boundingBox()), - std::back_inserter(result)); - return !result.empty(); - } +// int is_colliding(const Item& item) { +// size_t bedidx = item.binId() < 0 ? 0u : size_t(item.binId()); +// if (m_preload_idx.size() <= bedidx || m_preload_idx[bedidx].empty()) +// return false; + +// std::vector result; +// m_preload_idx[bedidx].query(bgi::intersects(item.boundingBox()), +// std::back_inserter(result)); +// return !result.empty(); +// } }; template<> std::function AutoArranger::get_objfn() @@ -534,24 +544,21 @@ void _arrange( ++it : it = excludes.erase(it); // If there is something on the plate - if(!excludes.empty()) - { - arranger.preload(excludes); - auto binbb = sl::boundingBox(corrected_bin); + if (!excludes.empty()) { + arranger.preload(excludes); +// auto binbb = sl::boundingBox(corrected_bin); - // Try to put the first item to the center, as the arranger - // will not do this for us. - for (Item &itm : shapes) { - auto ibb = itm.boundingBox(); - auto d = binbb.center() - ibb.center(); - itm.translate(d); +// // Try to put the first item to the center, as the arranger +// // will not do this for us. +// for (Item &itm : shapes) { +// auto ibb = itm.boundingBox(); +// auto d = binbb.center() - ibb.center(); +// itm.translate(d); +// itm.binId(UNARRANGED); - if (!arranger.is_colliding(itm)) { - itm.markAsFixed(); - break; - } - } - } +// if (!arranger.is_colliding(itm)) { itm.markAsFixed(); break; } +// } + } std::vector> inp; inp.reserve(shapes.size() + excludes.size()); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 58f0db292..48a05110e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1421,8 +1421,9 @@ struct Plater::priv wxQueueEvent(this, evt); } - priv &plater() { return *m_plater; } - bool was_canceled() const { return m_canceled.load(); } + priv & plater() { return *m_plater; } + const priv &plater() const { return *m_plater; } + bool was_canceled() const { return m_canceled.load(); } // Launched just before start(), a job can use it to prepare internals virtual void prepare() {} @@ -1537,51 +1538,76 @@ struct Plater::priv ArrangePolygons m_selected, m_unselected; - protected: - - void prepare() override - { - // Get the selection map - Selection& sel = plater().get_selection(); - const Selection::ObjectIdxsToInstanceIdxsMap &selmap = - sel.get_content(); - - Model &model = plater().model; + // clear m_selected and m_unselected, reserve space for next usage + void clear_input() { + const Model &model = plater().model; size_t count = 0; // To know how much space to reserve for (auto obj : model.objects) count += obj->instances.size(); - m_selected.clear(), m_unselected.clear(); m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); - - // Stride between logical beds + } + + // Stride between logical beds + coord_t bed_stride() const { double bedwidth = plater().bed_shape_bb().size().x(); - coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth); + return scaled((1. + LOGICAL_BED_GAP) * bedwidth); + } + + // Set up arrange polygon for a ModelInstance and Wipe tower + template ArrangePolygon get_arrange_poly(T *obj) const { + ArrangePolygon ap = obj->get_arrange_polygon(); + ap.priority = 0; + ap.bed_idx = ap.translation.x() / bed_stride(); + ap.setter = [obj, this](const ArrangePolygon &p) { + if (p.is_arranged()) { + auto t = p.translation; t.x() += p.bed_idx * bed_stride(); + obj->apply_arrange_result(t, p.rotation); + } + }; + return ap; + } + + // Prepare all objects on the bed regardless of the selection + void prepare_all() { + clear_input(); + + for (ModelObject *obj: plater().model.objects) + for (ModelInstance *mi : obj->instances) + m_selected.emplace_back(get_arrange_poly(mi)); + + auto& wti = plater().updated_wipe_tower(); + if (wti) m_selected.emplace_back(get_arrange_poly(&wti)); + } + + // Prepare the selected and unselected items separately. If nothing is + // selected, behaves as if everything would be selected. + void prepare_selected() { + clear_input(); + + Model &model = plater().model; + coord_t stride = bed_stride(); + + std::vector + obj_sel(model.objects.size(), nullptr); + + for (auto &s : plater().get_selection().get_content()) + if (s.first < int(obj_sel.size())) obj_sel[s.first] = &s.second; // Go through the objects and check if inside the selection for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { - auto oit = selmap.find(int(oidx)); + const Selection::InstanceIdxsList * instlist = obj_sel[oidx]; ModelObject *mo = model.objects[oidx]; std::vector inst_sel(mo->instances.size(), false); - if (oit != selmap.end()) - for (auto inst_id : oit->second) inst_sel[inst_id] = true; + if (instlist) + for (auto inst_id : *instlist) inst_sel[inst_id] = true; for (size_t i = 0; i < inst_sel.size(); ++i) { - ModelInstance *mi = mo->instances[i]; - ArrangePolygon ap = mi->get_arrange_polygon(); - ap.priority = 0; - ap.bed_idx = ap.translation.x() / stride; + ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]); - ap.setter = [mi, stride](const ArrangePolygon &p) { - if (p.is_arranged()) { - auto t = p.translation; t.x() += p.bed_idx * stride; - mi->apply_arrange_result(t, p.rotation); - } - }; - inst_sel[i] ? m_selected.emplace_back(std::move(ap)) : m_unselected.emplace_back(std::move(ap)); @@ -1590,17 +1616,9 @@ struct Plater::priv auto& wti = plater().updated_wipe_tower(); if (wti) { - ArrangePolygon ap = wti.get_arrange_polygon(); - ap.bed_idx = ap.translation.x() / stride; - ap.priority = 1; // Wipe tower should be on physical bed - ap.setter = [&wti, stride](const ArrangePolygon &p) { - if (p.is_arranged()) { - auto t = p.translation; t.x() += p.bed_idx * stride; - wti.apply_arrange_result(t, p.rotation); - } - }; + ArrangePolygon &&ap = get_arrange_poly(&wti); - sel.is_wipe_tower() ? + plater().get_selection().is_wipe_tower() ? m_selected.emplace_back(std::move(ap)) : m_unselected.emplace_back(std::move(ap)); } @@ -1614,6 +1632,13 @@ struct Plater::priv for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; } + protected: + + void prepare() override + { + wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); + } + public: using Job::Job; From 10e86a06cc5b9f3e8c9755cdd5181da8493b89f5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Jul 2019 18:16:36 +0200 Subject: [PATCH 29/32] Dont do force_full_scene_refresh after arrange. --- src/slic3r/GUI/Plater.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 48a05110e..52795fa41 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1647,17 +1647,13 @@ struct Plater::priv void process() override; void finalize() override { - - if (was_canceled()) { // Ignore the arrange result if aborted. - Job::finalize(); - return; - } + // Ignore the arrange result if aborted. + if (was_canceled()) return; // Apply the arrange result to all selected objects for (ArrangePolygon &ap : m_selected) ap.apply(); - // Call original finalize (will update the scene) - Job::finalize(); + plater().update(false /*dont force_full_scene_refresh*/); } }; From f6633df57b2cd6e7fc7ec62f44dc8713a854db4b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 22 Jul 2019 09:41:34 +0200 Subject: [PATCH 30/32] Fix of SPE-987 (Slicer crash when layers are selected in right panel and settings is added) --- src/slic3r/GUI/GUI_ObjectList.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f7abb4128..429cc18ee 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -359,9 +359,7 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons assert(item); const ItemType type = m_objects_model->GetItemType(item); - const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : - m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); - + const int obj_idx = m_objects_model->GetObjectIdByItem(item); const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1; assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0)); @@ -1037,12 +1035,17 @@ void ObjectList::get_settings_choice(const wxString& category_name) { wxArrayString names; wxArrayInt selections; + wxDataViewItem item = GetSelection(); settings_menu_hierarchy settings_menu; - const bool is_part = m_objects_model->GetParent(GetSelection()) != wxDataViewItem(0); + const bool is_part = m_objects_model->GetItemType(item) & (itVolume | itLayer); get_options_menu(settings_menu, is_part); std::vector< std::pair > *settings_list = nullptr; + if (!m_config) + m_config = &get_item_config(item); + + assert(m_config); auto opt_keys = m_config->keys(); for (auto& cat : settings_menu) @@ -1144,27 +1147,33 @@ void ObjectList::get_settings_choice(const wxString& category_name) // Add settings item for object/sub-object and show them - show_settings(add_settings_item(GetSelection(), m_config)); + if (!(m_objects_model->GetItemType(item) & (itObject | itVolume | itLayer))) + item = m_objects_model->GetTopParent(item); + show_settings(add_settings_item(item, m_config)); } void ObjectList::get_freq_settings_choice(const wxString& bundle_name) { std::vector options = get_options_for_bundle(bundle_name); + wxDataViewItem item = GetSelection(); /* Because of we couldn't edited layer_height for ItVolume from settings list, * correct options according to the selected item type : * remove "layer_height" option */ - if ((m_objects_model->GetItemType(GetSelection()) & itVolume) && bundle_name == _("Layers and Perimeters")) { + if ((m_objects_model->GetItemType(item) & itVolume) && bundle_name == _("Layers and Perimeters")) { const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height"); if (layer_height_it != options.end()) options.erase(layer_height_it); } + if (!m_config) + m_config = &get_item_config(item); + assert(m_config); auto opt_keys = m_config->keys(); - take_snapshot(wxString::Format(_(L("Add Settings Bundle for %s")), m_objects_model->GetItemType(GetSelection()) & itObject ? _(L("Object")) : _(L("Sub-object")))); + take_snapshot(wxString::Format(_(L("Add Settings Bundle for %s")), m_objects_model->GetItemType(item) & (itVolume|itLayer) ? _(L("Sub-object")) : _(L("Object")))); const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; for (auto& opt_key : options) @@ -1181,7 +1190,9 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) } // Add settings item for object/sub-object and show them - show_settings(add_settings_item(GetSelection(), m_config)); + if (!(m_objects_model->GetItemType(item) & (itObject | itVolume | itLayer))) + item = m_objects_model->GetTopParent(item); + show_settings(add_settings_item(item, m_config)); } void ObjectList::show_settings(const wxDataViewItem settings_item) From fb39516c650254e5132c87e9018d9af9870968b0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 22 Jul 2019 10:28:25 +0200 Subject: [PATCH 31/32] Added checking for a extruder value in ObjectList. Set value to "default" if "0" is selected. --- src/slic3r/GUI/wxExtensions.cpp | 23 +++++++++++++++++++++++ src/slic3r/GUI/wxExtensions.hpp | 22 +--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 7e8a2d92d..6300ada31 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -552,6 +552,29 @@ void ObjectDataViewModelNode::msw_rescale() update_settings_digest_bitmaps(); } +bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) +{ + switch (col) + { + case 0: { + DataViewBitmapText data; + data << variant; + m_bmp = data.GetBitmap(); + m_name = data.GetText(); + return true; } + case 1: { + const wxString & val = variant.GetString(); + m_extruder = val == "0" ? _(L("default")) : val; + return true; } + case 2: + m_action_icon << variant; + return true; + default: + printf("MyObjectTreeModel::SetValue: wrong column"); + } + return false; +} + void ObjectDataViewModelNode::SetIdx(const int& idx) { m_idx = idx; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index d0edf9760..9a6460dcb 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -301,27 +301,7 @@ public: return m_children.GetCount(); } - bool SetValue(const wxVariant &variant, unsigned int col) - { - switch (col) - { - case 0:{ - DataViewBitmapText data; - data << variant; - m_bmp = data.GetBitmap(); - m_name = data.GetText(); - return true;} - case 1: - m_extruder = variant.GetString(); - return true; - case 2: - m_action_icon << variant; - return true; - default: - printf("MyObjectTreeModel::SetValue: wrong column"); - } - return false; - } + bool SetValue(const wxVariant &variant, unsigned int col); void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } const wxBitmap& GetBitmap() const { return m_bmp; } From 764efb1385a2ec644c1ec1b29a7e124cf256a8dc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 22 Jul 2019 11:18:10 +0200 Subject: [PATCH 32/32] Remove disabled code. --- src/libslic3r/Arrange.cpp | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 77f4f55db..b4cfac954 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -136,9 +136,6 @@ protected: ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_items; // The items to be packed - // Used only for preloading objects before arrange - // std::vector m_preload_idx; // spatial index for preloaded beds - template ArithmeticOnly norm(T val) { return double(val) / m_norm; @@ -321,7 +318,6 @@ public: m_pilebb = sl::boundingBox(merged_pile); m_rtree.clear(); -// m_preload_idx.clear(); m_smallsrtree.clear(); // We will treat big items (compared to the print bed) differently @@ -362,25 +358,10 @@ public: for(unsigned idx = 0; idx < fixeditems.size(); ++idx) { Item& itm = fixeditems[idx]; itm.markAsFixed(); -// size_t bedidx = itm.binId() < 0 ? 0u : size_t(itm.binId()); - -// while (m_preload_idx.size() <= bedidx) m_preload_idx.emplace_back(); -// m_preload_idx[bedidx].insert({itm.boundingBox(), idx}); } m_pck.configure(m_pconf); } - -// int is_colliding(const Item& item) { -// size_t bedidx = item.binId() < 0 ? 0u : size_t(item.binId()); -// if (m_preload_idx.size() <= bedidx || m_preload_idx[bedidx].empty()) -// return false; - -// std::vector result; -// m_preload_idx[bedidx].query(bgi::intersects(item.boundingBox()), -// std::back_inserter(result)); -// return !result.empty(); -// } }; template<> std::function AutoArranger::get_objfn() @@ -544,21 +525,7 @@ void _arrange( ++it : it = excludes.erase(it); // If there is something on the plate - if (!excludes.empty()) { - arranger.preload(excludes); -// auto binbb = sl::boundingBox(corrected_bin); - -// // Try to put the first item to the center, as the arranger -// // will not do this for us. -// for (Item &itm : shapes) { -// auto ibb = itm.boundingBox(); -// auto d = binbb.center() - ibb.center(); -// itm.translate(d); -// itm.binId(UNARRANGED); - -// if (!arranger.is_colliding(itm)) { itm.markAsFixed(); break; } -// } - } + if (!excludes.empty()) arranger.preload(excludes); std::vector> inp; inp.reserve(shapes.size() + excludes.size());