From 8da8ecb4157aefa24e3ff4d13367bf94d0f1952e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 3 Aug 2018 14:49:26 +0200 Subject: [PATCH 01/50] Bed shape detection in progress --- xs/src/libslic3r/ModelArrange.hpp | 164 ++++++++++++++++++++++++++++-- xs/src/slic3r/AppController.cpp | 7 +- 2 files changed, 161 insertions(+), 10 deletions(-) diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index f2d399ac66..5b7a2a3258 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -292,6 +292,7 @@ public: template inline IndexedPackGroup operator()(Args&&...args) { areacache_.clear(); + rtree_.clear(); return pck_.arrangeIndexed(std::forward(args)...); } }; @@ -440,16 +441,157 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { return ret; } -enum BedShapeHint { +class Circle { + 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 double radius() const { return radius_; } + inline const Point& center() const { return center_; } + inline operator bool() { return std::isnan(radius_); } +}; + + +Circle circle(std::array P) { + + using Coord = Point::coord_type; + using std::pow; + using std::abs; + using std::round; + using std::nan; + + auto getX = [](const Point& p) { return p.x; }; + auto getY = [](const Point& p) { return p.y; }; + + auto distance = [](const Point& p1, const Point& p2) { + return abs(p1.distance_to(p2)); + }; + + static const auto E = 10.0/SCALING_FACTOR; + + auto x1 = getX(P[0]), y1 = getY(P[0]); + auto x2 = getX(P[1]), y2 = getY(P[1]); + auto x3 = getX(P[2]), y3 = getY(P[2]); + + + auto A_div = (x2 - x1); + auto B_div = (x3 - x2); + if(A_div == 0 || B_div == 0) return Circle(); + + auto A = (y2 - y1)/A_div; + auto B = (y2 - y3)/B_div; + auto C = (-pow(x1, 2) - pow(y1, 2) + pow(x2, 2) + pow(y2, 2))/(2*(x2 - x1)); + auto D = (pow(x2, 2) + pow(y1, 2) - pow(x3, 2) - pow(y3, 2))/(2*(x3 - x2)); + + auto cy = (C + D)/(A + B); + auto cx = B*cy - D; + + Point cc = {Coord(round(cx)), Coord(round(cy))}; + auto d = distance(cc, P[0]); + auto d2 = distance(cc, P[1]); + auto d3 = distance(cc, P[2]); + + auto e1 = abs(d - d2); + auto e2 = abs(d - d3); + + if(e1 > E || e2 > E) return Circle(); + + return { cc, d }; +} + +Circle isCircle(const Polyline& p) { + + using std::abs; + + auto& pp = p.points; + static const double E = 10/SCALING_FACTOR; + double radius = 0; + bool ret = true; + Circle c; + for(auto i = 0; i < pp.size() - 3 && ret; i += 3) { + c = circle({pp[i], pp[i+1], pp[i+2]}); + if(c || abs(radius - c.radius()) >= E) ret = false; + else radius = c.radius(); + } + +// auto rem = pp.size() % 3; + +// if(ret && rem > 0) { +// std::array remarr; + +// auto i = 0; +// for(i = 0; i < rem; i++) remarr[i] = *(pp.rbegin() - i); +// while(i < 3) remarr[i] = pp[i++]; +// c = circle(remarr); +// if(c || abs(radius - c.radius()) >= E) ret = false; +// } + + if(!ret) c = Circle(); + + return c; +} + +enum class BedShapeType { BOX, CIRCLE, IRREGULAR, WHO_KNOWS }; -BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) { +struct BedShapeHint { + BedShapeType type; + /*union*/ struct { // I know but who cares... + Circle circ; + BoundingBox box; + Polyline polygon; + } shape; +}; + +BedShapeHint bedShape(const Polyline& bed) { + static const double E = 10/SCALING_FACTOR; + + BedShapeHint ret; + + auto width = [](const BoundingBox& box) { + return box.max.x - box.min.x; + }; + + auto height = [](const BoundingBox& box) { + return box.max.y - box.min.y; + }; + + auto area = [&width, &height](const BoundingBox& box) { + return width(box) * height(box); + }; + + auto poly_area = [](Polyline p) { + Polygon pp; pp.points.reserve(p.points.size() + 1); + pp.points = std::move(p.points); + pp.points.emplace_back(pp.points.front()); + return std::abs(pp.area()); + }; + + auto bb = bed.bounding_box(); + if(std::abs(area(bb) - poly_area(bed)) < E) { + ret.type = BedShapeType::BOX; + ret.shape.box = bb; + std::cout << "BOX" << std::endl; + } + else if(auto c = isCircle(bed)) { + ret.type = BedShapeType::CIRCLE; + ret.shape.circ = c; + std::cout << "Circle" << std::endl; + } else { + std::cout << "Polygon" << std::endl; + ret.type = BedShapeType::IRREGULAR; + ret.shape.polygon = bed; + } + // Determine the bed shape by hand - return BOX; + return ret; } void applyResult( @@ -525,7 +667,10 @@ bool arrange(Model &model, coordf_t min_obj_distance, }); IndexedPackGroup result; - BoundingBox bbb(bed.points); + + if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); + + BoundingBox bbb(bed); auto binbb = Box({ static_cast(bbb.min.x), @@ -536,8 +681,8 @@ bool arrange(Model &model, coordf_t min_obj_distance, static_cast(bbb.max.y) }); - switch(bedhint) { - case BOX: { + switch(bedhint.type) { + case BedShapeType::BOX: { // Create the arranger for the box shaped bed AutoArranger arrange(binbb, min_obj_distance, progressind); @@ -547,10 +692,11 @@ bool arrange(Model &model, coordf_t min_obj_distance, result = arrange(shapes.begin(), shapes.end()); break; } - case CIRCLE: + case BedShapeType::CIRCLE: break; - case IRREGULAR: - case WHO_KNOWS: { + case BedShapeType::IRREGULAR: + case BedShapeType::WHO_KNOWS: { + using P = libnest2d::PolygonImpl; auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 58858f5fc4..9394df3637 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -288,6 +288,7 @@ const PrintConfig &PrintController::config() const return print_->config; } + void AppController::arrange_model() { auto ftr = std::async( @@ -324,10 +325,14 @@ void AppController::arrange_model() if(pind) pind->update(0, _(L("Arranging objects..."))); try { + arr::BedShapeHint hint; + // TODO: from Sasha from GUI + hint.type = arr::BedShapeType::WHO_KNOWS; + arr::arrange(*model_, min_obj_distance, bed, - arr::BOX, + hint, false, // create many piles not just one pile [pind, count](unsigned rem) { if(pind) From db8762a93cefaa7790198c6ef8d2781abcb4f5e8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 6 Aug 2018 11:30:10 +0200 Subject: [PATCH 02/50] bed shape detection works and circle shaped bed now supported with limited arrange quality. --- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 7 +- xs/src/libslic3r/ModelArrange.hpp | 178 ++++++++---------- 2 files changed, 86 insertions(+), 99 deletions(-) diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index 5d09a61fcb..a3429bf484 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -505,13 +505,18 @@ public: bool static inline wouldFit(const Box& bb, const _Circle& bin) { + return sl::isInside(bb, bin); } bool static inline wouldFit(const RawShape& chull, const _Circle& bin) { - return sl::isInside(chull, bin); + auto bb = sl::boundingBox(chull); + auto d = bin.center() - bb.center(); + auto chullcpy = chull; + sl::translate(chullcpy, d); + return sl::isInside(chullcpy, bin); } PackResult trypack(Item& item) { diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 5b7a2a3258..53b99b7817 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -329,6 +329,45 @@ public: } }; +using lnCircle = libnest2d::_Circle; + +template<> +class AutoArranger: public _ArrBase { +public: + + AutoArranger(const lnCircle& bin, Distance dist, + std::function progressind): + _ArrBase(bin, dist, progressind) { + + pconf_.object_function = [this, &bin] ( + Pile& pile, + const Item &item, + double pile_area, + double norm, + double /*penality*/) { + + auto result = objfunc(bin.center(), bin_area_, pile, + pile_area, item, norm, areacache_, rtree_); + double score = std::get<0>(result); + + // Circle fitting detection is very rough at the moment but + // we still need something that tells how badly the arrangement + // misses the print bed. + auto& fullbb = std::get<1>(result); + auto bbr = 0.5*PointLike::distance(fullbb.minCorner(), + fullbb.maxCorner()); + auto diff = bbr - bin.radius(); + + if(diff > 0) score += std::pow(diff, 2) / norm; + + + return score; + }; + + pck_.configure(pconf_); + } +}; + template<> class AutoArranger: public _ArrBase { public: @@ -348,15 +387,6 @@ public: pile_area, item, norm, areacache_, rtree_); double score = std::get<0>(result); - pile.emplace_back(item.transformedShape()); - auto chull = ShapeLike::convexHull(pile); - pile.pop_back(); - - // If it does not fit into the print bed we will beat it with a - // large penality. If we would not do this, there would be only one - // big pile that doesn't care whether it fits onto the print bed. - if(!Placer::wouldFit(chull, bin)) score += norm; - return score; }; @@ -451,89 +481,9 @@ public: inline double radius() const { return radius_; } inline const Point& center() const { return center_; } - inline operator bool() { return std::isnan(radius_); } + inline operator bool() { return !std::isnan(radius_); } }; - -Circle circle(std::array P) { - - using Coord = Point::coord_type; - using std::pow; - using std::abs; - using std::round; - using std::nan; - - auto getX = [](const Point& p) { return p.x; }; - auto getY = [](const Point& p) { return p.y; }; - - auto distance = [](const Point& p1, const Point& p2) { - return abs(p1.distance_to(p2)); - }; - - static const auto E = 10.0/SCALING_FACTOR; - - auto x1 = getX(P[0]), y1 = getY(P[0]); - auto x2 = getX(P[1]), y2 = getY(P[1]); - auto x3 = getX(P[2]), y3 = getY(P[2]); - - - auto A_div = (x2 - x1); - auto B_div = (x3 - x2); - if(A_div == 0 || B_div == 0) return Circle(); - - auto A = (y2 - y1)/A_div; - auto B = (y2 - y3)/B_div; - auto C = (-pow(x1, 2) - pow(y1, 2) + pow(x2, 2) + pow(y2, 2))/(2*(x2 - x1)); - auto D = (pow(x2, 2) + pow(y1, 2) - pow(x3, 2) - pow(y3, 2))/(2*(x3 - x2)); - - auto cy = (C + D)/(A + B); - auto cx = B*cy - D; - - Point cc = {Coord(round(cx)), Coord(round(cy))}; - auto d = distance(cc, P[0]); - auto d2 = distance(cc, P[1]); - auto d3 = distance(cc, P[2]); - - auto e1 = abs(d - d2); - auto e2 = abs(d - d3); - - if(e1 > E || e2 > E) return Circle(); - - return { cc, d }; -} - -Circle isCircle(const Polyline& p) { - - using std::abs; - - auto& pp = p.points; - static const double E = 10/SCALING_FACTOR; - double radius = 0; - bool ret = true; - Circle c; - for(auto i = 0; i < pp.size() - 3 && ret; i += 3) { - c = circle({pp[i], pp[i+1], pp[i+2]}); - if(c || abs(radius - c.radius()) >= E) ret = false; - else radius = c.radius(); - } - -// auto rem = pp.size() % 3; - -// if(ret && rem > 0) { -// std::array remarr; - -// auto i = 0; -// for(i = 0; i < rem; i++) remarr[i] = *(pp.rbegin() - i); -// while(i < 3) remarr[i] = pp[i++]; -// c = circle(remarr); -// if(c || abs(radius - c.radius()) >= E) ret = false; -// } - - if(!ret) c = Circle(); - - return c; -} - enum class BedShapeType { BOX, CIRCLE, @@ -564,7 +514,9 @@ BedShapeHint bedShape(const Polyline& bed) { }; auto area = [&width, &height](const BoundingBox& box) { - return width(box) * height(box); + double w = width(box); + double h = height(box); + return w*h; }; auto poly_area = [](Polyline p) { @@ -574,18 +526,43 @@ BedShapeHint bedShape(const Polyline& bed) { return std::abs(pp.area()); }; + auto bb = bed.bounding_box(); - if(std::abs(area(bb) - poly_area(bed)) < E) { + + auto isCircle = [bb](const Polyline& polygon) { + auto center = bb.center(); + std::vector vertex_distances; + double avg_dist = 0; + for (auto pt: polygon.points) + { + double distance = center.distance_to(pt); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + + Circle ret(center, avg_dist); + for (auto el: vertex_distances) + { + if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + ret = Circle(); + break; + } + + return ret; + }; + + auto parea = poly_area(bed); + + if( (1.0 - parea/area(bb)) < 1e-3 ) { ret.type = BedShapeType::BOX; ret.shape.box = bb; - std::cout << "BOX" << std::endl; } else if(auto c = isCircle(bed)) { ret.type = BedShapeType::CIRCLE; ret.shape.circ = c; - std::cout << "Circle" << std::endl; } else { - std::cout << "Polygon" << std::endl; ret.type = BedShapeType::IRREGULAR; ret.shape.polygon = bed; } @@ -692,8 +669,15 @@ bool arrange(Model &model, coordf_t min_obj_distance, result = arrange(shapes.begin(), shapes.end()); break; } - case BedShapeType::CIRCLE: + case BedShapeType::CIRCLE: { + + auto c = bedhint.shape.circ; + auto cc = lnCircle({c.center().x, c.center().y} , c.radius()); + + AutoArranger arrange(cc, min_obj_distance, progressind); + result = arrange(shapes.begin(), shapes.end()); break; + } case BedShapeType::IRREGULAR: case BedShapeType::WHO_KNOWS: { @@ -702,8 +686,6 @@ bool arrange(Model &model, coordf_t min_obj_distance, auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); P irrbed = ShapeLike::create(std::move(ctour)); -// std::cout << ShapeLike::toString(irrbed) << std::endl; - AutoArranger

arrange(irrbed, min_obj_distance, progressind); // Arrange and return the items with their respective indices within the From e1edb05bbbd95bf0f15d3540545d9bf1cf63356e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 6 Aug 2018 20:13:04 +0200 Subject: [PATCH 03/50] Better support for circular bed. --- xs/src/libnest2d/examples/main.cpp | 501 +----------------- .../libnest2d/libnest2d/geometry_traits.hpp | 2 +- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 110 +++- xs/src/libnest2d/tests/test.cpp | 37 ++ xs/src/libslic3r/ModelArrange.hpp | 52 +- 5 files changed, 172 insertions(+), 530 deletions(-) diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp index d6b2ccc348..02be465a88 100644 --- a/xs/src/libnest2d/examples/main.cpp +++ b/xs/src/libnest2d/examples/main.cpp @@ -50,492 +50,12 @@ void arrangeRectangles() { using namespace libnest2d; const int SCALE = 1000000; -// const int SCALE = 1; + std::vector rects = { - {80*SCALE, 80*SCALE}, - {60*SCALE, 90*SCALE}, - {70*SCALE, 30*SCALE}, - {80*SCALE, 60*SCALE}, - {60*SCALE, 60*SCALE}, - {60*SCALE, 40*SCALE}, - {40*SCALE, 40*SCALE}, - {10*SCALE, 10*SCALE}, - {10*SCALE, 10*SCALE}, - {10*SCALE, 10*SCALE}, - {10*SCALE, 10*SCALE}, - {10*SCALE, 10*SCALE}, - {5*SCALE, 5*SCALE}, - {5*SCALE, 5*SCALE}, - {5*SCALE, 5*SCALE}, - {5*SCALE, 5*SCALE}, - {5*SCALE, 5*SCALE}, - {5*SCALE, 5*SCALE}, - {5*SCALE, 5*SCALE}, - {20*SCALE, 20*SCALE} - }; - -// std::vector rects = { -// {20*SCALE, 10*SCALE}, -// {20*SCALE, 10*SCALE}, -// {20*SCALE, 20*SCALE}, -// }; - -// std::vector input { -// {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}} -// }; - - std::vector crasher = - { - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568549}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568549}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568549}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568549}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568549}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568549}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568549}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568549}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568549}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568549}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568549}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568549}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190020}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802200}, - {6691309, -11542300}, - {5877850, -12201100}, - {5000000, -12771100}, - {4067369, -13246399}, - {3090169, -13621500}, - {2079119, -13892399}, - {1045279, -14056099}, - {0, -14110899}, - {-1045279, -14056099}, - {-2079119, -13892399}, - {-3090169, -13621500}, - {-4067369, -13246399}, - {-5000000, -12771100}, - {-5877850, -12201100}, - {-6691309, -11542300}, - {-7431449, -10802200}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190020}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-18000000, -1000000}, - {-15000000, 22000000}, - {-11000000, 26000000}, - {11000000, 26000000}, - {15000000, 22000000}, - {18000000, -1000000}, - {18000000, -26000000}, - {-18000000, -26000000}, - {-18000000, -1000000}, - }, + {60*SCALE, 200*SCALE}, + {60*SCALE, 200*SCALE} }; - std::vector proba = { - { - Rectangle(100, 2) - }, - { - Rectangle(100, 2) - }, - { - Rectangle(100, 2) - }, - { - Rectangle(10, 10) - }, - }; - - proba[0].rotate(Pi/3); - proba[1].rotate(Pi-Pi/3); - -// std::vector input(25, Rectangle(70*SCALE, 10*SCALE)); std::vector input; input.insert(input.end(), prusaParts().begin(), prusaParts().end()); // input.insert(input.end(), prusaExParts().begin(), prusaExParts().end()); @@ -544,7 +64,7 @@ void arrangeRectangles() { // input.insert(input.end(), proba.begin(), proba.end()); // input.insert(input.end(), crasher.begin(), crasher.end()); - Box bin(250*SCALE, 210*SCALE); +// Box bin(250*SCALE, 210*SCALE); // PolygonImpl bin = { // { // {25*SCALE, 0}, @@ -560,9 +80,11 @@ void arrangeRectangles() { // {} // }; + _Circle bin({0, 0}, 125*SCALE); + auto min_obj_distance = static_cast(0*SCALE); - using Placer = strategies::_NofitPolyPlacer; + using Placer = strategies::_NofitPolyPlacer; using Packer = Arranger; Packer arrange(bin, min_obj_distance); @@ -571,9 +93,9 @@ void arrangeRectangles() { pconf.alignment = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; - pconf.accuracy = 0.5f; + pconf.accuracy = 1.0f; -// auto bincenter = ShapeLike::boundingBox(bin).center(); +// auto bincenter = ShapeLike::boundingBox(bin).center(); // pconf.object_function = [&bin, bincenter]( // Placer::Pile pile, const Item& item, // double /*area*/, double norm, double penality) { @@ -660,10 +182,7 @@ void arrangeRectangles() { // score = pl::distance(ibb.center(), bigbb.center()) / norm; // } -// // If it does not fit into the print bed we will beat it -// // with a large penality. If we would not do this, there would be only -// // one big pile that doesn't care whether it fits onto the print bed. -// if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score; +// if(!Placer::wouldFit(fullbb, bin)) score += norm; // return score; // }; diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp index 058c47cd43..8306431300 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp @@ -109,7 +109,7 @@ public: inline void radius(double r) { radius_ = r; } inline double area() const BP2D_NOEXCEPT { - return 2.0*Pi*radius_; + return 2.0*Pi*radius_*radius_; } }; diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index a3429bf484..c506a5d5a4 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -1,16 +1,19 @@ #ifndef NOFITPOLY_HPP #define NOFITPOLY_HPP +#include +#include + #ifndef NDEBUG #include #endif #include "placer_boilerplate.hpp" #include "../geometry_traits_nfp.hpp" #include "libnest2d/optimizer.hpp" -#include #include "tools/svgtools.hpp" + namespace libnest2d { namespace strategies { template @@ -161,12 +164,11 @@ template class EdgeCache { } size_t stride(const size_t N) const { - using std::ceil; using std::round; using std::pow; return static_cast( - std::round(N/std::pow(N, std::pow(accuracy_, 1.0/3.0))) + round(N/pow(N, pow(accuracy_, 1.0/3.0))) ); } @@ -177,6 +179,7 @@ template class EdgeCache { const auto S = stride(N); contour_.corners.reserve(N / S + 1); + contour_.corners.emplace_back(0.0); auto N_1 = N-1; contour_.corners.emplace_back(0.0); for(size_t i = 0; i < N_1; i += S) { @@ -190,8 +193,8 @@ template class EdgeCache { if(!hc.corners.empty()) return; const auto N = hc.distances.size(); - const auto S = stride(N); auto N_1 = N-1; + const auto S = stride(N); hc.corners.reserve(N / S + 1); hc.corners.emplace_back(0.0); for(size_t i = 0; i < N_1; i += S) { @@ -339,7 +342,7 @@ Nfp::Shapes nfp( const Container& polygons, Nfp::Shapes nfps; - //int pi = 0; +// int pi = 0; for(Item& sh : polygons) { auto subnfp_r = Nfp::noFitPolygon( sh.transformedShape(), trsh.transformedShape()); @@ -441,6 +444,63 @@ Nfp::Shapes nfp( const Container& polygons, // return nfps; } +template +_Circle> minimizeCircle(const RawShape& sh) { + using sl = ShapeLike; using pl = PointLike; + using Point = TPoint; + using Coord = TCoord; + + auto bb = sl::boundingBox(sh); + auto capprx = bb.center(); + auto rapprx = pl::distance(bb.minCorner(), bb.maxCorner()); + + auto& ctr = sl::getContour(sh); + + opt::StopCriteria stopcr; + stopcr.max_iterations = 100; + stopcr.relative_score_difference = 1e-3; + opt::TOptimizer solver(stopcr); + + std::vector dists(ctr.size(), 0); + + auto result = solver.optimize_min( + [capprx, rapprx, &ctr, &dists](double xf, double yf) { + auto xt = Coord( std::round(getX(capprx) + rapprx*xf) ); + auto yt = Coord( std::round(getY(capprx) + rapprx*yf) ); + + Point centr(xt, yt); + + unsigned i = 0; + for(auto v : ctr) { + dists[i++] = pl::distance(v, centr); + } + + auto mit = std::max_element(dists.begin(), dists.end()); + + assert(mit != dists.end()); + + return *mit; + }, + opt::initvals(0.0, 0.0), + opt::bound(-1.0, 1.0), opt::bound(-1.0, 1.0) + ); + + double oxf = std::get<0>(result.optimum); + double oyf = std::get<1>(result.optimum); + auto xt = Coord( std::round(getX(capprx) + rapprx*oxf) ); + auto yt = Coord( std::round(getY(capprx) + rapprx*oyf) ); + + Point cc(xt, yt); + auto r = result.score; + + return {cc, r}; +} + +template +_Circle> boundingCircle(const RawShape& sh) { + return minimizeCircle(sh); +} + template>> class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer, RawShape, TBin, NfpPConfig> { @@ -512,11 +572,7 @@ public: bool static inline wouldFit(const RawShape& chull, const _Circle& bin) { - auto bb = sl::boundingBox(chull); - auto d = bin.center() - bb.center(); - auto chullcpy = chull; - sl::translate(chullcpy, d); - return sl::isInside(chullcpy, bin); + return boundingCircle(chull).radius() < bin.radius(); } PackResult trypack(Item& item) { @@ -574,8 +630,9 @@ public: auto getNfpPoint = [&ecache](const Optimum& opt) { - return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) : + auto ret = opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) : ecache[opt.nfpidx].coords(opt.hidx, opt.relpos); + return ret; }; Nfp::Shapes pile; @@ -595,7 +652,7 @@ public: [this, &merged_pile]( Nfp::Shapes& /*pile*/, const Item& item, - double occupied_area, + double occupied_area, double norm, double /*penality*/) { @@ -751,14 +808,37 @@ public: } inline void clearItems() { + finalAlign(bin_); + Base::clearItems(); + } + +private: + + inline void finalAlign(const RawShape& pbin) { + auto bbin = sl::boundingBox(pbin); + finalAlign(bbin); + } + + inline void finalAlign(_Circle> cbin) { + if(items_.empty()) return; + Nfp::Shapes m; m.reserve(items_.size()); + for(Item& item : items_) m.emplace_back(item.transformedShape()); + auto c = boundingCircle(sl::convexHull(m)); + + auto d = cbin.center() - c.center(); + for(Item& item : items_) item.translate(d); + } + + inline void finalAlign(Box bbin) { + Nfp::Shapes m; + m.reserve(items_.size()); for(Item& item : items_) m.emplace_back(item.transformedShape()); auto&& bb = sl::boundingBox(m); Vertex ci, cb; - auto bbin = sl::boundingBox(bin_); switch(config_.alignment) { case Config::Alignment::CENTER: { @@ -790,12 +870,8 @@ public: auto d = cb - ci; for(Item& item : items_) item.translate(d); - - Base::clearItems(); } -private: - void setInitialPosition(Item& item) { Box&& bb = item.boundingBox(); Vertex ci, cb; diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp index 39315ff1ab..1e030c0566 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/xs/src/libnest2d/tests/test.cpp @@ -99,6 +99,43 @@ TEST(BasicFunctionality, creationAndDestruction) } +TEST(GeometryAlgorithms, boundingCircle) { + using namespace libnest2d; + using strategies::boundingCircle; + + PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}}; + _Circle c = boundingCircle(p); + + ASSERT_EQ(c.center().X, 0); + ASSERT_EQ(c.center().Y, 0); + ASSERT_DOUBLE_EQ(c.radius(), 10); + + ShapeLike::translate(p, PointImpl{10, 10}); + c = boundingCircle(p); + + ASSERT_EQ(c.center().X, 10); + ASSERT_EQ(c.center().Y, 10); + ASSERT_DOUBLE_EQ(c.radius(), 10); + + auto parts = prusaParts(); + + int i = 0; + for(auto& part : parts) { + c = boundingCircle(part.transformedShape()); + if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl; + + else for(auto v : ShapeLike::getContour(part.transformedShape()) ) { + auto d = PointLike::distance(v, c.center()); + if(d > c.radius() ) { + auto e = std::abs( 1.0 - d/c.radius()); + ASSERT_LE(e, 1e-3); + } + } + i++; + } + +} + TEST(GeometryAlgorithms, Distance) { using namespace libnest2d; diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 53b99b7817..57a1692059 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -102,9 +102,9 @@ using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; std::tuple objfunc(const PointImpl& bincenter, - double /*bin_area*/, + double bin_area, ShapeLike::Shapes& pile, // The currently arranged pile - double /*pile_area*/, + double pile_area, const Item &item, double norm, // A norming factor for physical dimensions std::vector& areacache, // pile item areas will be cached @@ -115,12 +115,16 @@ objfunc(const PointImpl& bincenter, using pl = PointLike; using sl = ShapeLike; - static const double BIG_ITEM_TRESHOLD = 0.2; + static const double BIG_ITEM_TRESHOLD = 0.04; static const double ROUNDNESS_RATIO = 0.5; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; // We will treat big items (compared to the print bed) differently - auto normarea = [norm](double area) { return std::sqrt(area)/norm; }; + + auto isBig = [&areacache, bin_area](double a) { + bool t = areacache.empty() ? true : a > 0.5*areacache.front(); + return a/bin_area > BIG_ITEM_TRESHOLD || t; + }; // If a new bin has been created: if(pile.size() < areacache.size()) { @@ -133,7 +137,7 @@ objfunc(const PointImpl& bincenter, for(auto& p : pile) { if(idx == areacache.size()) { areacache.emplace_back(sl::area(p)); - if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD) + if(isBig(areacache[idx])) spatindex.insert({sl::boundingBox(p), idx}); } @@ -157,14 +161,10 @@ objfunc(const PointImpl& bincenter, boost::geometry::convert(boostbb, bigbb); } - // The size indicator of the candidate item. This is not the area, - // but almost... - double item_normarea = normarea(item.area()); - // Will hold the resulting score double score = 0; - if(item_normarea > BIG_ITEM_TRESHOLD) { + if(isBig(item.area())) { // This branch is for the bigger items.. // Here we will use the closest point of the item bounding box to // the already arranged pile. So not the bb center nor the a choosen @@ -223,10 +223,9 @@ objfunc(const PointImpl& bincenter, // 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 neigbours - auto C = 0.33; - score = C * dist + C * density + C * alignment_score; + score = 0.4 * dist + 0.4 * density + 0.2 * alignment_score; - } else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) { + } else if( !isBig(item.area()) && spatindex.empty()) { // If there are no big items, only small, we should consider the // density here as well to not get silly results auto bindist = pl::distance(ibb.center(), bincenter) / norm; @@ -349,17 +348,26 @@ public: auto result = objfunc(bin.center(), bin_area_, pile, pile_area, item, norm, areacache_, rtree_); double score = std::get<0>(result); - - // Circle fitting detection is very rough at the moment but - // we still need something that tells how badly the arrangement - // misses the print bed. auto& fullbb = std::get<1>(result); - auto bbr = 0.5*PointLike::distance(fullbb.minCorner(), - fullbb.maxCorner()); - auto diff = bbr - bin.radius(); - if(diff > 0) score += std::pow(diff, 2) / norm; + auto d = PointLike::distance(fullbb.minCorner(), + fullbb.maxCorner()); + auto diff = d - 2*bin.radius(); + if(diff > 0) { + if( item.area() > 0.01*bin_area_ && item.vertexCount() < 20) { + pile.emplace_back(item.transformedShape()); + auto chull = ShapeLike::convexHull(pile); + pile.pop_back(); + + auto C = strategies::boundingCircle(chull); + auto rdiff = C.radius() - bin.radius(); + + if(rdiff > 0) { + score += std::pow(rdiff, 3) / norm; + } + } + } return score; }; @@ -695,6 +703,8 @@ bool arrange(Model &model, coordf_t min_obj_distance, } }; + if(result.empty()) return false; + if(first_bin_only) { applyResult(result.front(), 0, shapemap); } else { From 224c0e74eab48b9bc117d1af60ddcd7f1362e837 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 7 Aug 2018 10:57:22 +0200 Subject: [PATCH 04/50] Precision raised and big item classification improved --- xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp | 2 +- xs/src/libslic3r/ModelArrange.hpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index c506a5d5a4..110c05f0ce 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -706,7 +706,7 @@ public: opt::StopCriteria stopcr; stopcr.max_iterations = 100; - stopcr.relative_score_difference = 1e-6; + stopcr.relative_score_difference = 1e-12; opt::TOptimizer solver(stopcr); Optimum optimum(0, 0); diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 57a1692059..118bff17f2 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -115,15 +115,18 @@ objfunc(const PointImpl& bincenter, using pl = PointLike; using sl = ShapeLike; - static const double BIG_ITEM_TRESHOLD = 0.04; + static const double BIG_ITEM_TRESHOLD = 0.02; static const double ROUNDNESS_RATIO = 0.5; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; // We will treat big items (compared to the print bed) differently auto isBig = [&areacache, bin_area](double a) { - bool t = areacache.empty() ? true : a > 0.5*areacache.front(); - return a/bin_area > BIG_ITEM_TRESHOLD || t; + double farea = areacache.empty() ? 0 : areacache.front(); + bool fbig = farea / bin_area > BIG_ITEM_TRESHOLD; + bool abig = a/bin_area > BIG_ITEM_TRESHOLD; + bool rbig = fbig && a > 0.5*farea; + return abig || rbig; }; // If a new bin has been created: @@ -258,7 +261,7 @@ void fillConfig(PConf& pcfg) { // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well - pcfg.accuracy = 0.6f; + pcfg.accuracy = 1.0f; } template @@ -355,7 +358,7 @@ public: auto diff = d - 2*bin.radius(); if(diff > 0) { - if( item.area() > 0.01*bin_area_ && item.vertexCount() < 20) { + if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) { pile.emplace_back(item.transformedShape()); auto chull = ShapeLike::convexHull(pile); pile.pop_back(); From 08fb677583518382f3ff4a42fadc431d0a9870f0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 7 Aug 2018 14:23:57 +0200 Subject: [PATCH 05/50] Fine tuning of precision. --- xs/src/libslic3r/ModelArrange.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 118bff17f2..f4ce0daca7 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -226,7 +226,7 @@ objfunc(const PointImpl& bincenter, // 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 neigbours - score = 0.4 * dist + 0.4 * density + 0.2 * alignment_score; + score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score; } else if( !isBig(item.area()) && spatindex.empty()) { // If there are no big items, only small, we should consider the @@ -261,7 +261,7 @@ void fillConfig(PConf& pcfg) { // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well - pcfg.accuracy = 1.0f; + pcfg.accuracy = 0.6f; } template From 20b7aad6d1cbb4b282586a3844fb66843c81f10d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 7 Aug 2018 19:48:00 +0200 Subject: [PATCH 06/50] Bug fixes for the neighborhood detection --- xs/src/libnest2d/README.md | 18 ++- xs/src/libnest2d/examples/main.cpp | 92 -------------- .../libnest2d/libnest2d/geometry_traits.hpp | 6 +- xs/src/libnest2d/libnest2d/libnest2d.hpp | 36 +++--- .../libnest2d/placers/bottomleftplacer.hpp | 7 +- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 65 +++++----- .../libnest2d/placers/placer_boilerplate.hpp | 7 +- .../libnest2d/selections/djd_heuristic.hpp | 32 ++--- .../libnest2d/libnest2d/selections/filler.hpp | 2 +- .../libnest2d/selections/firstfit.hpp | 9 +- xs/src/libnest2d/tests/test.cpp | 4 +- xs/src/libslic3r/ModelArrange.hpp | 118 ++++++++++++------ 12 files changed, 190 insertions(+), 206 deletions(-) diff --git a/xs/src/libnest2d/README.md b/xs/src/libnest2d/README.md index 3508801a83..61a7ac7d05 100644 --- a/xs/src/libnest2d/README.md +++ b/xs/src/libnest2d/README.md @@ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already existing implementation to avoid copying or having unnecessary dependencies. A default backend is provided if the user of the library just wants to use it -out of the box without additional integration. The default backend is reasonably +out of the box without additional integration. This backend is reasonably fast and robust, being built on top of boost geometry and the [polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of -this default backend implies the dependency on these packages as well as the -compilation of the backend itself (The default backend is not yet header only). +this default backend implies the dependency on these packages but its header +only as well. This software is currently under construction and lacks a throughout documentation and some essential algorithms as well. At this stage it works well for rectangles and convex closed polygons without considering holes and concavities. -Holes and non-convex polygons will be usable in the near future as well. +Holes and non-convex polygons will be usable in the near future as well. The +no fit polygon based placer module combined with the first fit selection +strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r) +application's arrangement feature. It uses local optimization techniques to find +the best placement of each new item based on some features of the arrangement. + +In the near future I would like to use machine learning to evaluate the +placements and (or) the order if items in which they are placed and see what +results can be obtained. This is a different approach than that of SVGnest which +uses genetic algorithms to find better and better selection orders. Maybe the +two approaches can be combined as well. # References - [SVGNest](https://github.com/Jack000/SVGnest) diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp index 02be465a88..11f8f50cf7 100644 --- a/xs/src/libnest2d/examples/main.cpp +++ b/xs/src/libnest2d/examples/main.cpp @@ -95,98 +95,6 @@ void arrangeRectangles() { pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; pconf.accuracy = 1.0f; -// auto bincenter = ShapeLike::boundingBox(bin).center(); -// pconf.object_function = [&bin, bincenter]( -// Placer::Pile pile, const Item& item, -// double /*area*/, double norm, double penality) { - -// using pl = PointLike; - -// static const double BIG_ITEM_TRESHOLD = 0.2; -// static const double GRAVITY_RATIO = 0.5; -// static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO; - -// // We will treat big items (compared to the print bed) differently -// NfpPlacer::Pile bigs; -// bigs.reserve(pile.size()); -// for(auto& p : pile) { -// auto pbb = ShapeLike::boundingBox(p); -// auto na = std::sqrt(pbb.width()*pbb.height())/norm; -// if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p); -// } - -// // Candidate item bounding box -// auto ibb = item.boundingBox(); - -// // Calculate the full bounding box of the pile with the candidate item -// pile.emplace_back(item.transformedShape()); -// auto fullbb = ShapeLike::boundingBox(pile); -// pile.pop_back(); - -// // The bounding box of the big items (they will accumulate in the center -// // of the pile -// auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs); - -// // The size indicator of the candidate item. This is not the area, -// // but almost... -// auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm; - -// // Will hold the resulting score -// double score = 0; - -// if(itemnormarea > BIG_ITEM_TRESHOLD) { -// // This branch is for the bigger items.. -// // Here we will use the closest point of the item bounding box to -// // the already arranged pile. So not the bb center nor the a choosen -// // corner but whichever is the closest to the center. This will -// // prevent unwanted strange arrangements. - -// 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)}; - -// auto cc = fullbb.center(); // The gravity center - -// // Now the distnce of the gravity center will be calculated to the -// // five anchor points and the smallest will be chosen. -// std::array dists; -// 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); - -// auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; - -// // Density is the pack density: how big is the arranged pile -// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; - -// // The score is a weighted sum of the distance from pile center -// // and the pile size -// score = GRAVITY_RATIO * dist + DENSITY_RATIO * density; - -// } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) { -// // If there are no big items, only small, we should consider the -// // density here as well to not get silly results -// auto bindist = pl::distance(ibb.center(), bincenter) / norm; -// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; -// score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density; -// } 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; -// } - -// if(!Placer::wouldFit(fullbb, bin)) score += norm; - -// return score; -// }; - Packer::SelectionConfig sconf; // sconf.allow_parallel = false; // sconf.force_parallel = false; diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp index 8306431300..1c0d44c9fd 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp @@ -313,9 +313,9 @@ inline RawPoint _Box::center() const BP2D_NOEXCEPT { using Coord = TCoord; - RawPoint ret = { - static_cast( std::round((getX(minc) + getX(maxc))/2.0) ), - static_cast( std::round((getY(minc) + getY(maxc))/2.0) ) + RawPoint ret = { // No rounding here, we dont know if these are int coords + static_cast( (getX(minc) + getX(maxc))/2.0 ), + static_cast( (getY(minc) + getY(maxc))/2.0 ) }; return ret; diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index 7f23de3583..eadd1e110b 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -541,21 +541,20 @@ public: inline void configure(const Config& config) { impl_.configure(config); } /** - * @brief A method that tries to pack an item and returns an object - * describing the pack result. + * Try to pack an item with a result object that contains the packing + * information for later accepting it. * - * The result can be casted to bool and used as an argument to the accept - * method to accept a succesfully packed item. This way the next packing - * will consider the accepted item as well. The PackResult should carry the - * transformation info so that if the tried item is later modified or tried - * multiple times, the result object should set it to the originally - * determied position. An implementation can be found in the - * strategies::PlacerBoilerplate::PackResult class. - * - * @param item Ithe item to be packed. - * @return The PackResult object that can be implicitly casted to bool. + * \param item_store A container of items */ - inline PackResult trypack(Item& item) { return impl_.trypack(item); } + template + inline PackResult trypack(Container& item_store, + typename Container::iterator from, + unsigned count = 1) { + using V = typename Container::value_type; + static_assert(std::is_convertible::value, + "Invalid Item container!"); + return impl_.trypack(item_store, from, count); + } /** * @brief A method to accept a previously tried item. @@ -578,7 +577,16 @@ public: * @return Returns true if the item was packed or false if it could not be * packed. */ - inline bool pack(Item& item) { return impl_.pack(item); } + template + inline bool pack(Container& item_store, + typename Container::iterator from, + unsigned count = 1) + { + using V = typename Container::value_type; + static_assert(std::is_convertible::value, + "Invalid Item container!"); + return impl_.pack(item_store, from, count); + } /// Unpack the last element (remove it from the list of packed items). inline void unpackLast() { impl_.unpackLast(); } diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp index 775e44e090..71573e34dd 100644 --- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp @@ -27,9 +27,14 @@ public: explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {} - PackResult trypack(Item& item) { + template + PackResult trypack(Store& /*s*/, typename Store::iterator from, + unsigned /*count*/ = 1) + { + Item& item = *from; auto r = _trypack(item); if(!r && Base::config_.allow_rotations) { + item.rotate(Degrees(90)); r =_trypack(item); } diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index 110c05f0ce..d74fe2b1d3 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -19,6 +19,8 @@ namespace libnest2d { namespace strategies { template struct NfpPConfig { + using ItemGroup = std::vector>>; + enum class Alignment { CENTER, BOTTOM_LEFT, @@ -57,8 +59,8 @@ struct NfpPConfig { * \param item The second parameter is the candidate item. * * \param occupied_area The third parameter is the sum of areas of the - * items in the first parameter so you don't have to iterate through them - * if you only need their area. + * items in the first parameter (no candidate item there) so you don't have + * to iterate through them if you only need their accumulated area. * * \param norm A norming factor for physical dimensions. E.g. if your score * is the distance between the item and the bin center, you should divide @@ -66,21 +68,21 @@ struct NfpPConfig { * divide it with the square of the norming factor. Imagine it as a unit of * distance. * - * \param penality The fifth parameter is the amount of minimum penality if - * the arranged pile would't fit into the bin. You can use the wouldFit() - * function to check this. Note that the pile can be outside the bin's - * boundaries while the placement algorithm is running. Your job is only to - * check if the pile could be translated into a position in the bin where - * all the items would be inside. For a box shaped bin you can use the - * pile's bounding box to check whether it's width and height is small - * enough. If the pile would not fit, you have to make sure that the - * resulting score will be higher then the penality value. A good solution - * would be to set score = 2*penality-score in case the pile wouldn't fit - * into the bin. + * \param remaining A container with the remaining items waiting to be + * placed. You can use some features about the remaining items to alter to + * score of the current placement. If you know that you have to leave place + * for other items as well, that might influence your decision about where + * the current candidate should be placed. E.g. imagine three big circles + * which you want to place into a box: you might place them in a triangle + * shape which has the maximum pack density. But if there is a 4th big + * circle than you won't be able to pack it. If you knew apriori that + * there four circles are to be placed, you would have placed the first 3 + * into an L shape. This parameter can be used to make these kind of + * decisions (for you or a more intelligent AI). * */ std::function&, const _Item&, - double, double, double)> + double, double, const ItemGroup&)> object_function; /** @@ -450,11 +452,13 @@ _Circle> minimizeCircle(const RawShape& sh) { using Point = TPoint; using Coord = TCoord; + auto& ctr = sl::getContour(sh); + if(ctr.empty()) return {{0, 0}, 0}; + auto bb = sl::boundingBox(sh); auto capprx = bb.center(); auto rapprx = pl::distance(bb.minCorner(), bb.maxCorner()); - auto& ctr = sl::getContour(sh); opt::StopCriteria stopcr; stopcr.max_iterations = 100; @@ -513,7 +517,6 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer>; const double norm_; - const double penality_; using MaxNfpLevel = Nfp::MaxNfpLevel; using sl = ShapeLike; @@ -524,8 +527,7 @@ public: inline explicit _NofitPolyPlacer(const BinType& bin): Base(bin), - norm_(std::sqrt(sl::area(bin))), - penality_(1e6*norm_) {} + norm_(std::sqrt(sl::area(bin))) {} _NofitPolyPlacer(const _NofitPolyPlacer&) = default; _NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default; @@ -575,7 +577,15 @@ public: return boundingCircle(chull).radius() < bin.radius(); } - PackResult trypack(Item& item) { + template + PackResult trypack(Container& items, + typename Container::iterator from, + unsigned /*count*/ = 1) + { + return trypack(*from, {std::next(from), items.end()}); + } + + PackResult trypack(Item& item, ItemGroup remaining) { PackResult ret; @@ -586,7 +596,7 @@ public: can_pack = item.isInside(bin_); } else { - double global_score = penality_; + double global_score = std::numeric_limits::max(); auto initial_tr = item.translation(); auto initial_rot = item.rotation(); @@ -630,9 +640,8 @@ public: auto getNfpPoint = [&ecache](const Optimum& opt) { - auto ret = opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) : + return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) : ecache[opt.nfpidx].coords(opt.hidx, opt.relpos); - return ret; }; Nfp::Shapes pile; @@ -654,7 +663,7 @@ public: const Item& item, double occupied_area, double norm, - double /*penality*/) + const ItemGroup& /*remaining*/) { merged_pile.emplace_back(item.transformedShape()); auto ch = sl::convexHull(merged_pile); @@ -686,7 +695,7 @@ public: double occupied_area = pile_area + item.area(); double score = _objfunc(pile, item, occupied_area, - norm_, penality_); + norm_, remaining); return score; }; @@ -705,12 +714,12 @@ public: }; opt::StopCriteria stopcr; - stopcr.max_iterations = 100; - stopcr.relative_score_difference = 1e-12; + stopcr.max_iterations = 200; + stopcr.relative_score_difference = 1e-20; opt::TOptimizer solver(stopcr); Optimum optimum(0, 0); - double best_score = penality_; + double best_score = std::numeric_limits::max(); // Local optimization with the four polygon corners as // starting points @@ -821,7 +830,6 @@ private: inline void finalAlign(_Circle> cbin) { if(items_.empty()) return; - Nfp::Shapes m; m.reserve(items_.size()); for(Item& item : items_) m.emplace_back(item.transformedShape()); @@ -833,6 +841,7 @@ private: } inline void finalAlign(Box bbin) { + if(items_.empty()) return; Nfp::Shapes m; m.reserve(items_.size()); for(Item& item : items_) m.emplace_back(item.transformedShape()); diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp index 9d2cb626be..f31a9343c1 100644 --- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp @@ -56,8 +56,11 @@ public: config_ = config; } - bool pack(Item& item) { - auto&& r = static_cast(this)->trypack(item); + template + bool pack(Container& items, + typename Container::iterator from, + unsigned count = 1) { + auto&& r = static_cast(this)->trypack(items, from, count); if(r) { items_.push_back(*(r.item_ptr_)); farea_valid_ = false; diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index e3ad97c100..34d6d05c5c 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -230,7 +230,7 @@ public: while(it != not_packed.end() && !ret && free_area - (item_area = it->get().area()) <= waste) { - if(item_area <= free_area && placer.pack(*it) ) { + if(item_area <= free_area && placer.pack(not_packed, it) ) { free_area -= item_area; filled_area = bin_area - free_area; ret = true; @@ -278,7 +278,7 @@ public: if(item_area + smallestPiece(it, not_packed)->get().area() > free_area ) { it++; continue; } - auto pr = placer.trypack(*it); + auto pr = placer.trypack(not_packed, it); // First would fit it2 = not_packed.begin(); @@ -294,14 +294,14 @@ public: } placer.accept(pr); - auto pr2 = placer.trypack(*it2); + auto pr2 = placer.trypack(not_packed, it2); if(!pr2) { placer.unpackLast(); // remove first if(try_reverse) { - pr2 = placer.trypack(*it2); + pr2 = placer.trypack(not_packed, it2); if(pr2) { placer.accept(pr2); - auto pr12 = placer.trypack(*it); + auto pr12 = placer.trypack(not_packed, it); if(pr12) { placer.accept(pr12); ret = true; @@ -394,7 +394,7 @@ public: it++; continue; } - auto pr = placer.trypack(*it); + auto pr = placer.trypack(not_packed, it); // Check for free area and try to pack the 1st item... if(!pr) { it++; continue; } @@ -420,15 +420,15 @@ public: bool can_pack2 = false; placer.accept(pr); - auto pr2 = placer.trypack(*it2); + auto pr2 = placer.trypack(not_packed, it2); auto pr12 = pr; if(!pr2) { placer.unpackLast(); // remove first if(try_reverse) { - pr2 = placer.trypack(*it2); + pr2 = placer.trypack(not_packed, it2); if(pr2) { placer.accept(pr2); - pr12 = placer.trypack(*it); + pr12 = placer.trypack(not_packed, it); if(pr12) can_pack2 = true; placer.unpackLast(); } @@ -463,7 +463,7 @@ public: if(a3_sum > free_area) { it3++; continue; } placer.accept(pr12); placer.accept(pr2); - bool can_pack3 = placer.pack(*it3); + bool can_pack3 = placer.pack(not_packed, it3); if(!can_pack3) { placer.unpackLast(); @@ -473,16 +473,16 @@ public: if(!can_pack3 && try_reverse) { std::array indices = {0, 1, 2}; - std::array - candidates = {*it, *it2, *it3}; + std::array + candidates = {it, it2, it3}; - auto tryPack = [&placer, &candidates]( + auto tryPack = [&placer, &candidates, ¬_packed]( const decltype(indices)& idx) { std::array packed = {false}; for(auto id : idx) packed.at(id) = - placer.pack(candidates[id]); + placer.pack(not_packed, candidates[id]); bool check = std::all_of(packed.begin(), @@ -536,7 +536,7 @@ public: { auto it = store_.begin(); while (it != store_.end()) { Placer p(bin); p.configure(pconfig); - if(!p.pack(*it)) { + if(!p.pack(store_, it)) { it = store_.erase(it); } else it++; } @@ -601,7 +601,7 @@ public: while(it != not_packed.end() && filled_area < INITIAL_FILL_AREA) { - if(placer.pack(*it)) { + if(placer.pack(not_packed, it)) { filled_area += it->get().area(); free_area = bin_area - filled_area; it = not_packed.erase(it); diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp index d0018dc73d..ca1281fe6f 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp @@ -65,7 +65,7 @@ public: auto it = store_.begin(); while(it != store_.end()) { - if(!placer.pack(*it)) { + if(!placer.pack(store_, it)) { if(packed_bins_.back().empty()) ++it; // makeProgress(placer); placer.clearItems(); diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index 665b9da9f2..93ca02b1e6 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -40,6 +40,7 @@ public: packed_bins_.clear(); std::vector placers; + placers.reserve(last-first); std::copy(first, last, std::back_inserter(store_)); @@ -60,18 +61,19 @@ public: { auto it = store_.begin(); while (it != store_.end()) { Placer p(bin); p.configure(pconfig); - if(!p.pack(*it)) { + if(!p.pack(store_, it)) { it = store_.erase(it); } else it++; } } - for(auto& item : store_ ) { + auto it = store_.begin(); + while(it != store_.end()) { bool was_packed = false; while(!was_packed) { for(size_t j = 0; j < placers.size() && !was_packed; j++) { - if((was_packed = placers[j].pack(item))) + if((was_packed = placers[j].pack(store_, it))) makeProgress(placers[j], j); } @@ -81,6 +83,7 @@ public: packed_bins_.emplace_back(); } } + ++it; } } diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp index 1e030c0566..79832b6830 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/xs/src/libnest2d/tests/test.cpp @@ -471,8 +471,8 @@ TEST(GeometryAlgorithms, BottomLeftStressTest) { auto next = it; int i = 0; while(it != input.end() && ++next != input.end()) { - placer.pack(*it); - placer.pack(*next); + placer.pack(input, it); + placer.pack(input, next); auto result = placer.getItems(); bool valid = true; diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index f4ce0daca7..952a9e3a64 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -99,6 +99,7 @@ namespace bgi = boost::geometry::index; using SpatElement = std::pair; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; +using ItemGroup = std::vector>; std::tuple objfunc(const PointImpl& bincenter, @@ -109,24 +110,21 @@ objfunc(const PointImpl& bincenter, double norm, // A norming factor for physical dimensions std::vector& areacache, // pile item areas will be cached // a spatial index to quickly get neighbors of the candidate item - SpatIndex& spatindex + SpatIndex& spatindex, + const ItemGroup& remaining ) { using pl = PointLike; using sl = ShapeLike; + using Coord = TCoord; static const double BIG_ITEM_TRESHOLD = 0.02; static const double ROUNDNESS_RATIO = 0.5; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; // We will treat big items (compared to the print bed) differently - auto isBig = [&areacache, bin_area](double a) { - double farea = areacache.empty() ? 0 : areacache.front(); - bool fbig = farea / bin_area > BIG_ITEM_TRESHOLD; - bool abig = a/bin_area > BIG_ITEM_TRESHOLD; - bool rbig = fbig && a > 0.5*farea; - return abig || rbig; + return a/bin_area > BIG_ITEM_TRESHOLD ; }; // If a new bin has been created: @@ -195,39 +193,74 @@ objfunc(const PointImpl& bincenter, auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; // Density is the pack density: how big is the arranged pile - auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; + double density = 0; - // Prepare a variable for the alignment score. - // This will indicate: how well is the candidate item aligned with - // its neighbors. We will check the aligment 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 = std::numeric_limits::max(); + if(remaining.empty()) { + pile.emplace_back(item.transformedShape()); + auto chull = sl::convexHull(pile); + pile.pop_back(); + strategies::EdgeCache ec(chull); - auto& trsh = item.transformedShape(); + double circ = ec.circumference() / norm; + double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; + score = 0.5*circ + 0.5*bcirc; - auto querybb = item.boundingBox(); + } 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 aligment 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 = std::numeric_limits::max(); - // Query the spatial index for the neigbours - std::vector result; - spatindex.query(bgi::intersects(querybb), std::back_inserter(result)); + density = (fullbb.width()*fullbb.height()) / (norm*norm); + auto& trsh = item.transformedShape(); + auto querybb = item.boundingBox(); + auto wp = querybb.width()*0.2; + auto hp = querybb.height()*0.2; + auto pad = PointImpl( Coord(wp), Coord(hp)); + querybb = Box({ querybb.minCorner() - pad, + querybb.maxCorner() + pad + }); - for(auto& e : result) { // now get the score for the best alignment - auto idx = e.second; - auto& p = pile[idx]; - auto parea = areacache[idx]; - auto bb = sl::boundingBox(sl::Shapes{p, trsh}); - auto bbarea = bb.area(); - auto ascore = 1.0 - (item.area() + parea)/bbarea; + // Query the spatial index for the neigbours + std::vector result; + result.reserve(spatindex.size()); + spatindex.query(bgi::intersects(querybb), + std::back_inserter(result)); + +// if(result.empty()) { +// std::cout << "Error while arranging!" << std::endl; +// std::cout << spatindex.size() << " " << pile.size() << std::endl; + +// auto ib = spatindex.bounds(); +// Box ibb; +// boost::geometry::convert(ib, ibb); +// std::cout << "Inside: " << (sl::isInside(querybb, ibb) || +// boost::geometry::intersects(querybb, ibb)) << std::endl; +// } + + for(auto& e : result) { // now get the score for the best alignment + auto idx = e.second; + auto& p = pile[idx]; + auto parea = areacache[idx]; + auto bb = sl::boundingBox(sl::Shapes{p, trsh}); + 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 neigbours + if(result.empty()) + score = 0.5 * dist + 0.5 * density; + else + score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score; - 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 neigbours - score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score; - } else if( !isBig(item.area()) && spatindex.empty()) { // If there are no big items, only small, we should consider the // density here as well to not get silly results @@ -312,10 +345,12 @@ public: const Item &item, double pile_area, double norm, - double /*penality*/) { + const ItemGroup& rem) { auto result = objfunc(bin.center(), bin_area_, pile, - pile_area, item, norm, areacache_, rtree_); + pile_area, item, norm, areacache_, + rtree_, + rem); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); @@ -346,10 +381,11 @@ public: const Item &item, double pile_area, double norm, - double /*penality*/) { + const ItemGroup& rem) { auto result = objfunc(bin.center(), bin_area_, pile, - pile_area, item, norm, areacache_, rtree_); + pile_area, item, norm, areacache_, + rtree_, rem); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); @@ -391,11 +427,12 @@ public: const Item &item, double pile_area, double norm, - double /*penality*/) { + const ItemGroup& rem) { auto binbb = ShapeLike::boundingBox(bin); auto result = objfunc(binbb.center(), bin_area_, pile, - pile_area, item, norm, areacache_, rtree_); + pile_area, item, norm, areacache_, + rtree_, rem); double score = std::get<0>(result); return score; @@ -417,10 +454,11 @@ public: const Item &item, double pile_area, double norm, - double /*penality*/) { + const ItemGroup& rem) { auto result = objfunc({0, 0}, 0, pile, pile_area, - item, norm, areacache_, rtree_); + item, norm, areacache_, + rtree_, rem); return std::get<0>(result); }; From ad92aa74865fcaf522f936b5962988753b7bdac3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Aug 2018 12:51:17 +0200 Subject: [PATCH 07/50] Solution for stupid arrangement of rotated items and some fine tuning. --- xs/src/libslic3r/ModelArrange.hpp | 44 ++++++++----------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 952a9e3a64..79371cdb27 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -167,10 +167,6 @@ objfunc(const PointImpl& bincenter, if(isBig(item.area())) { // This branch is for the bigger items.. - // Here we will use the closest point of the item bounding box to - // the already arranged pile. So not the bb center nor the a choosen - // corner but whichever is the closest to the center. This will - // prevent some unwanted strange arrangements. auto minc = ibb.minCorner(); // bottom left corner auto maxc = ibb.maxCorner(); // top right corner @@ -211,17 +207,11 @@ objfunc(const PointImpl& bincenter, // its neighbors. We will check the aligment 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 = std::numeric_limits::max(); + auto alignment_score = 1.0; density = (fullbb.width()*fullbb.height()) / (norm*norm); auto& trsh = item.transformedShape(); auto querybb = item.boundingBox(); - auto wp = querybb.width()*0.2; - auto hp = querybb.height()*0.2; - auto pad = PointImpl( Coord(wp), Coord(hp)); - querybb = Box({ querybb.minCorner() - pad, - querybb.maxCorner() + pad - }); // Query the spatial index for the neigbours std::vector result; @@ -229,26 +219,17 @@ objfunc(const PointImpl& bincenter, spatindex.query(bgi::intersects(querybb), std::back_inserter(result)); -// if(result.empty()) { -// std::cout << "Error while arranging!" << std::endl; -// std::cout << spatindex.size() << " " << pile.size() << std::endl; - -// auto ib = spatindex.bounds(); -// Box ibb; -// boost::geometry::convert(ib, ibb); -// std::cout << "Inside: " << (sl::isInside(querybb, ibb) || -// boost::geometry::intersects(querybb, ibb)) << std::endl; -// } - for(auto& e : result) { // now get the score for the best alignment auto idx = e.second; auto& p = pile[idx]; auto parea = areacache[idx]; - auto bb = sl::boundingBox(sl::Shapes{p, trsh}); - auto bbarea = bb.area(); - auto ascore = 1.0 - (item.area() + parea)/bbarea; + if(std::abs(1.0 - parea/item.area()) < 1e-6) { + auto bb = sl::boundingBox(sl::Shapes{p, trsh}); + auto bbarea = bb.area(); + auto ascore = 1.0 - (item.area() + parea)/bbarea; - if(ascore < alignment_score) alignment_score = ascore; + if(ascore < alignment_score) alignment_score = ascore; + } } // The final mix of the score is the balance between the distance @@ -258,15 +239,12 @@ objfunc(const PointImpl& bincenter, score = 0.5 * dist + 0.5 * density; else score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score; - } - } else if( !isBig(item.area()) && spatindex.empty()) { - // If there are no big items, only small, we should consider the - // density here as well to not get silly results auto bindist = pl::distance(ibb.center(), bincenter) / norm; - auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; - score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density; + + // Bindist is surprisingly enough... + score = bindist; } else { // Here there are the small items that should be placed around the // already processed bigger items. @@ -294,7 +272,7 @@ void fillConfig(PConf& pcfg) { // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well - pcfg.accuracy = 0.6f; + pcfg.accuracy = 0.65f; } template From e678368b233670ab6a707f87b42306964f12d463 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Aug 2018 09:59:05 +0200 Subject: [PATCH 08/50] fix compilation on linux and mac --- xs/src/libnest2d/CMakeLists.txt | 1 + xs/src/libnest2d/examples/main.cpp | 20 +- xs/src/libnest2d/libnest2d.h | 4 +- xs/src/libnest2d/libnest2d/boost_alg.hpp | 98 ++++----- .../clipper_backend/clipper_backend.hpp | 105 +++++---- .../libnest2d/libnest2d/geometry_traits.hpp | 208 ++++++++++-------- .../libnest2d/geometry_traits_nfp.hpp | 113 +++++----- xs/src/libnest2d/libnest2d/libnest2d.hpp | 143 ++++++------ .../libnest2d/placers/bottomleftplacer.hpp | 63 +++--- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 125 +++++------ .../libnest2d/placers/placer_boilerplate.hpp | 25 +-- xs/src/libnest2d/libnest2d/rotfinder.hpp | 41 ++++ .../libnest2d/selections/djd_heuristic.hpp | 48 ++-- .../libnest2d/libnest2d/selections/filler.hpp | 10 +- .../libnest2d/selections/firstfit.hpp | 7 +- xs/src/libnest2d/tests/test.cpp | 58 ++--- xs/src/libnest2d/tools/svgtools.hpp | 6 +- xs/src/libslic3r/ModelArrange.hpp | 58 ++--- 18 files changed, 590 insertions(+), 543 deletions(-) create mode 100644 xs/src/libnest2d/libnest2d/rotfinder.hpp diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt index 835e8311d3..0a181f4ab8 100644 --- a/xs/src/libnest2d/CMakeLists.txt +++ b/xs/src/libnest2d/CMakeLists.txt @@ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/rotfinder.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp index 11f8f50cf7..37096019d3 100644 --- a/xs/src/libnest2d/examples/main.cpp +++ b/xs/src/libnest2d/examples/main.cpp @@ -9,6 +9,8 @@ #include "tests/printer_parts.h" #include "tools/benchmark.h" #include "tools/svgtools.hpp" +#include "libnest2d/rotfinder.hpp" + //#include "tools/libnfpglue.hpp" using namespace libnest2d; @@ -64,7 +66,7 @@ void arrangeRectangles() { // input.insert(input.end(), proba.begin(), proba.end()); // input.insert(input.end(), crasher.begin(), crasher.end()); -// Box bin(250*SCALE, 210*SCALE); + Box bin(250*SCALE, 210*SCALE); // PolygonImpl bin = { // { // {25*SCALE, 0}, @@ -80,12 +82,12 @@ void arrangeRectangles() { // {} // }; - _Circle bin({0, 0}, 125*SCALE); +// _Circle bin({0, 0}, 125*SCALE); auto min_obj_distance = static_cast(0*SCALE); using Placer = strategies::_NofitPolyPlacer; - using Packer = Arranger; + using Packer = Nester; Packer arrange(bin, min_obj_distance); @@ -112,7 +114,9 @@ void arrangeRectangles() { // svgw.writePackGroup(arrange.lastResult()); // svgw.save("debout"); std::cout << "Remaining items: " << r << std::endl; - })/*.useMinimumBoundigBoxRotation()*/; + }); + +// findMinimumBoundingBoxRotations(input.begin(), input.end()); Benchmark bench; @@ -120,7 +124,7 @@ void arrangeRectangles() { Packer::ResultType result; try { - result = arrange.arrange(input.begin(), input.end()); + result = arrange.execute(input.begin(), input.end()); } catch(GeometryException& ge) { std::cerr << "Geometry error: " << ge.what() << std::endl; return ; @@ -134,7 +138,7 @@ void arrangeRectangles() { std::vector eff; eff.reserve(result.size()); - auto bin_area = ShapeLike::area(bin); + auto bin_area = sl::area(bin); for(auto& r : result) { double a = 0; std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); }); @@ -156,7 +160,7 @@ void arrangeRectangles() { std::cout << ") Total: " << total << std::endl; for(auto& it : input) { - auto ret = ShapeLike::isValid(it.transformedShape()); + auto ret = sl::isValid(it.transformedShape()); std::cout << ret.second << std::endl; } @@ -177,7 +181,7 @@ void arrangeRectangles() { int main(void /*int argc, char **argv*/) { arrangeRectangles(); -// findDegenerateCase(); +//// findDegenerateCase(); return EXIT_SUCCESS; } diff --git a/xs/src/libnest2d/libnest2d.h b/xs/src/libnest2d/libnest2d.h index c9e21ecfb3..05677afd7c 100644 --- a/xs/src/libnest2d/libnest2d.h +++ b/xs/src/libnest2d/libnest2d.h @@ -22,6 +22,7 @@ using Point = PointImpl; using Coord = TCoord; using Box = _Box; using Segment = _Segment; +using Circle = _Circle; using Item = _Item; using Rectangle = _Rectangle; @@ -36,9 +37,6 @@ using DJDHeuristic = strategies::_DJDHeuristic; using NfpPlacer = strategies::_NofitPolyPlacer; using BottomLeftPlacer = strategies::_BottomLeftPlacer; -//template -//using NofitPolyPlacer = strategies::_NofitPolyPlacer; - } #endif // LIBNEST2D_H diff --git a/xs/src/libnest2d/libnest2d/boost_alg.hpp b/xs/src/libnest2d/libnest2d/boost_alg.hpp index 67e19fcbdd..7da1036f01 100644 --- a/xs/src/libnest2d/libnest2d/boost_alg.hpp +++ b/xs/src/libnest2d/libnest2d/boost_alg.hpp @@ -36,7 +36,7 @@ using libnest2d::setX; using libnest2d::setY; using Box = libnest2d::_Box; using Segment = libnest2d::_Segment; -using Shapes = libnest2d::Nfp::Shapes; +using Shapes = libnest2d::nfp::Shapes; } @@ -241,11 +241,11 @@ template<> struct tag { template<> struct exterior_ring { static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) { - return libnest2d::ShapeLike::getContour(p); + return libnest2d::shapelike::getContour(p); } static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) { - return libnest2d::ShapeLike::getContour(p); + return libnest2d::shapelike::getContour(p); } }; @@ -271,13 +271,13 @@ struct interior_rings { static inline libnest2d::THolesContainer& get( bp2d::PolygonImpl& p) { - return libnest2d::ShapeLike::holes(p); + return libnest2d::shapelike::holes(p); } static inline const libnest2d::THolesContainer& get( bp2d::PolygonImpl const& p) { - return libnest2d::ShapeLike::holes(p); + return libnest2d::shapelike::holes(p); } }; @@ -311,83 +311,78 @@ struct range_value { namespace libnest2d { // Now the algorithms that boost can provide... +namespace pointlike { template<> -inline double PointLike::distance(const PointImpl& p1, - const PointImpl& p2 ) +inline double distance(const PointImpl& p1, const PointImpl& p2 ) { return boost::geometry::distance(p1, p2); } template<> -inline double PointLike::distance(const PointImpl& p, - const bp2d::Segment& seg ) +inline double distance(const PointImpl& p, const bp2d::Segment& seg ) { return boost::geometry::distance(p, seg); } +} +namespace shapelike { // Tell libnest2d how to make string out of a ClipperPolygon object template<> -inline bool ShapeLike::intersects(const PathImpl& sh1, - const PathImpl& sh2) +inline bool intersects(const PathImpl& sh1, const PathImpl& sh2) { return boost::geometry::intersects(sh1, sh2); } // Tell libnest2d how to make string out of a ClipperPolygon object template<> -inline bool ShapeLike::intersects(const PolygonImpl& sh1, - const PolygonImpl& sh2) +inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2) { return boost::geometry::intersects(sh1, sh2); } // Tell libnest2d how to make string out of a ClipperPolygon object template<> -inline bool ShapeLike::intersects(const bp2d::Segment& s1, - const bp2d::Segment& s2) +inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2) { return boost::geometry::intersects(s1, s2); } #ifndef DISABLE_BOOST_AREA template<> -inline double ShapeLike::area(const PolygonImpl& shape) +inline double area(const PolygonImpl& shape, const PolygonTag&) { return boost::geometry::area(shape); } #endif template<> -inline bool ShapeLike::isInside(const PointImpl& point, - const PolygonImpl& shape) +inline bool isInside(const PointImpl& point, + const PolygonImpl& shape) { return boost::geometry::within(point, shape); } template<> -inline bool ShapeLike::isInside(const PolygonImpl& sh1, - const PolygonImpl& sh2) +inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2) { return boost::geometry::within(sh1, sh2); } template<> -inline bool ShapeLike::touches( const PolygonImpl& sh1, - const PolygonImpl& sh2) +inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2) { return boost::geometry::touches(sh1, sh2); } template<> -inline bool ShapeLike::touches( const PointImpl& point, - const PolygonImpl& shape) +inline bool touches( const PointImpl& point, const PolygonImpl& shape) { return boost::geometry::touches(point, shape); } #ifndef DISABLE_BOOST_BOUNDING_BOX template<> -inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh) +inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&) { bp2d::Box b; boost::geometry::envelope(sh, b); @@ -395,7 +390,7 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh) } template<> -inline bp2d::Box ShapeLike::boundingBox(const bp2d::Shapes& shapes) +inline bp2d::Box boundingBox(const bp2d::Shapes& shapes) { bp2d::Box b; boost::geometry::envelope(shapes, b); @@ -405,7 +400,7 @@ inline bp2d::Box ShapeLike::boundingBox(const bp2d::Shapes& shapes) #ifndef DISABLE_BOOST_CONVEX_HULL template<> -inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh) +inline PolygonImpl convexHull(const PolygonImpl& sh) { PolygonImpl ret; boost::geometry::convex_hull(sh, ret); @@ -413,7 +408,7 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh) } template<> -inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes) +inline PolygonImpl convexHull(const bp2d::Shapes& shapes) { PolygonImpl ret; boost::geometry::convex_hull(shapes, ret); @@ -423,7 +418,7 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes) #ifndef DISABLE_BOOST_ROTATE template<> -inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) +inline void rotate(PolygonImpl& sh, const Radians& rads) { namespace trans = boost::geometry::strategy::transform; @@ -437,7 +432,7 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) #ifndef DISABLE_BOOST_TRANSLATE template<> -inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) +inline void translate(PolygonImpl& sh, const PointImpl& offs) { namespace trans = boost::geometry::strategy::transform; @@ -451,26 +446,15 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) #ifndef DISABLE_BOOST_OFFSET template<> -inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance) +inline void offset(PolygonImpl& sh, bp2d::Coord distance) { PolygonImpl cpy = sh; boost::geometry::buffer(cpy, sh, distance); } #endif -#ifndef DISABLE_BOOST_NFP_MERGE -template<> -inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes, - const PolygonImpl& sh) -{ - bp2d::Shapes retv; - boost::geometry::union_(shapes, sh, retv); - return retv; -} -#endif - #ifndef DISABLE_BOOST_SERIALIZE -template<> inline std::string ShapeLike::serialize( +template<> inline std::string serialize( const PolygonImpl& sh, double scale) { std::stringstream ss; @@ -482,14 +466,14 @@ template<> inline std::string ShapeLike::serialize( Polygonf::ring_type ring; Polygonf::inner_container_type holes; - ring.reserve(ShapeLike::contourVertexCount(sh)); + ring.reserve(shapelike::contourVertexCount(sh)); - for(auto it = ShapeLike::cbegin(sh); it != ShapeLike::cend(sh); it++) { + for(auto it = shapelike::cbegin(sh); it != shapelike::cend(sh); it++) { auto& v = *it; ring.emplace_back(getX(v)*scale, getY(v)*scale); }; - auto H = ShapeLike::holes(sh); + auto H = shapelike::holes(sh); for(PathImpl& h : H ) { Polygonf::ring_type hf; for(auto it = h.begin(); it != h.end(); it++) { @@ -512,21 +496,37 @@ template<> inline std::string ShapeLike::serialize( #ifndef DISABLE_BOOST_UNSERIALIZE template<> -inline void ShapeLike::unserialize( +inline void unserialize( PolygonImpl& sh, const std::string& str) { } #endif -template<> inline std::pair -ShapeLike::isValid(const PolygonImpl& sh) +template<> inline std::pair isValid(const PolygonImpl& sh) { std::string message; bool ret = boost::geometry::is_valid(sh, message); return {ret, message}; } +} + +namespace nfp { + +#ifndef DISABLE_BOOST_NFP_MERGE +template<> +inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes, + const PolygonImpl& sh) +{ + bp2d::Shapes retv; + boost::geometry::union_(shapes, sh, retv); + return retv; +} +#endif + +} + } diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp index 15ceb15767..4238212ad2 100644 --- a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp +++ b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp @@ -21,6 +21,9 @@ struct PolygonImpl { PathImpl Contour; HoleStore Holes; + using Tag = libnest2d::PolygonTag; + using PointType = PointImpl; + inline PolygonImpl() = default; inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {} @@ -113,35 +116,32 @@ template<> struct CountourType { using Type = PathImpl; }; +namespace pointlike { + // Tell binpack2d how to extract the X coord from a ClipperPoint object -template<> inline TCoord PointLike::x(const PointImpl& p) +template<> inline TCoord x(const PointImpl& p) { return p.X; } // Tell binpack2d how to extract the Y coord from a ClipperPoint object -template<> inline TCoord PointLike::y(const PointImpl& p) +template<> inline TCoord y(const PointImpl& p) { return p.Y; } // Tell binpack2d how to extract the X coord from a ClipperPoint object -template<> inline TCoord& PointLike::x(PointImpl& p) +template<> inline TCoord& x(PointImpl& p) { return p.X; } // Tell binpack2d how to extract the Y coord from a ClipperPoint object -template<> -inline TCoord& PointLike::y(PointImpl& p) +template<> inline TCoord& y(PointImpl& p) { return p.Y; } -template<> -inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity) -{ - return sh.Contour.reserve(vertex_capacity); } #define DISABLE_BOOST_AREA @@ -175,16 +175,28 @@ inline double area(const PolygonImpl& sh) { return ClipperLib::Area(sh.Contour) + a; } + +} + +template<> struct HolesContainer { + using Type = ClipperLib::Paths; +}; + +namespace shapelike { + +template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity) +{ + return sh.Contour.reserve(vertex_capacity); } // Tell binpack2d how to make string out of a ClipperPolygon object -template<> -inline double ShapeLike::area(const PolygonImpl& sh) { +template<> inline double area(const PolygonImpl& sh, const PolygonTag&) +{ return _smartarea::area::Value>(sh); } -template<> -inline void ShapeLike::offset(PolygonImpl& sh, TCoord distance) { +template<> inline void offset(PolygonImpl& sh, TCoord distance) +{ #define DISABLE_BOOST_OFFSET using ClipperLib::ClipperOffset; @@ -234,7 +246,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord distance) { } // Tell libnest2d how to make string out of a ClipperPolygon object -template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) { +template<> inline std::string toString(const PolygonImpl& sh) +{ std::stringstream ss; ss << "Contour {\n"; @@ -256,38 +269,31 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) { return ss.str(); } -template<> -inline TVertexIterator ShapeLike::begin(PolygonImpl& sh) +template<> inline TVertexIterator begin(PolygonImpl& sh) { return sh.Contour.begin(); } -template<> -inline TVertexIterator ShapeLike::end(PolygonImpl& sh) +template<> inline TVertexIterator end(PolygonImpl& sh) { return sh.Contour.end(); } template<> -inline TVertexConstIterator ShapeLike::cbegin( - const PolygonImpl& sh) +inline TVertexConstIterator cbegin(const PolygonImpl& sh) { return sh.Contour.cbegin(); } -template<> -inline TVertexConstIterator ShapeLike::cend( +template<> inline TVertexConstIterator cend( const PolygonImpl& sh) { return sh.Contour.cend(); } -template<> struct HolesContainer { - using Type = ClipperLib::Paths; -}; - -template<> inline PolygonImpl ShapeLike::create(const PathImpl& path, - const HoleStore& holes) { +template<> +inline PolygonImpl create(const PathImpl& path, const HoleStore& holes) +{ PolygonImpl p; p.Contour = path; @@ -308,8 +314,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path, return p; } -template<> inline PolygonImpl ShapeLike::create( PathImpl&& path, - HoleStore&& holes) { +template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) { PolygonImpl p; p.Contour.swap(path); @@ -331,49 +336,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path, return p; } -template<> inline const THolesContainer& -ShapeLike::holes(const PolygonImpl& sh) +template<> +inline const THolesContainer& holes(const PolygonImpl& sh) { return sh.Holes; } -template<> inline THolesContainer& -ShapeLike::holes(PolygonImpl& sh) +template<> inline THolesContainer& holes(PolygonImpl& sh) { return sh.Holes; } -template<> inline TContour& -ShapeLike::getHole(PolygonImpl& sh, unsigned long idx) +template<> +inline TContour& getHole(PolygonImpl& sh, unsigned long idx) { return sh.Holes[idx]; } -template<> inline const TContour& -ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx) +template<> +inline const TContour& getHole(const PolygonImpl& sh, + unsigned long idx) { return sh.Holes[idx]; } -template<> inline size_t ShapeLike::holeCount(const PolygonImpl& sh) +template<> inline size_t holeCount(const PolygonImpl& sh) { return sh.Holes.size(); } -template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh) +template<> inline PathImpl& getContour(PolygonImpl& sh) { return sh.Contour; } template<> -inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh) +inline const PathImpl& getContour(const PolygonImpl& sh) { return sh.Contour; } #define DISABLE_BOOST_TRANSLATE template<> -inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) +inline void translate(PolygonImpl& sh, const PointImpl& offs) { for(auto& p : sh.Contour) { p += offs; } for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; } @@ -381,7 +386,7 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) #define DISABLE_BOOST_ROTATE template<> -inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) +inline void rotate(PolygonImpl& sh, const Radians& rads) { using Coord = TCoord; @@ -402,9 +407,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) } } +} // namespace shapelike + #define DISABLE_BOOST_NFP_MERGE -inline Nfp::Shapes _merge(ClipperLib::Clipper& clipper) { - Nfp::Shapes retv; +inline nfp::Shapes _merge(ClipperLib::Clipper& clipper) { + nfp::Shapes retv; ClipperLib::PolyTree result; clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative); @@ -438,8 +445,10 @@ inline Nfp::Shapes _merge(ClipperLib::Clipper& clipper) { return retv; } -template<> inline Nfp::Shapes -Nfp::merge(const Nfp::Shapes& shapes) +namespace nfp { + +template<> inline nfp::Shapes +merge(const nfp::Shapes& shapes) { ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution); @@ -461,6 +470,8 @@ Nfp::merge(const Nfp::Shapes& shapes) } +} + //#define DISABLE_BOOST_SERIALIZE //#define DISABLE_BOOST_UNSERIALIZE diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp index 1c0d44c9fd..0826cbd4bc 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp @@ -60,6 +60,10 @@ struct PointPair { RawPoint p2; }; +struct PolygonTag {}; +struct BoxTag {}; +struct CircleTag {}; + /** * \brief An abstraction of a box; */ @@ -69,6 +73,9 @@ class _Box: PointPair { using PointPair::p2; public: + using Tag = BoxTag; + using PointType = RawPoint; + inline _Box() = default; inline _Box(const RawPoint& p, const RawPoint& pp): PointPair({p, pp}) {} @@ -98,6 +105,9 @@ class _Circle { double radius_ = 0; public: + using Tag = CircleTag; + using PointType = RawPoint; + _Circle() = default; _Circle(const RawPoint& center, double r): center_(center), radius_(r) {} @@ -123,6 +133,8 @@ class _Segment: PointPair { mutable Radians angletox_ = std::nan(""); public: + using PointType = RawPoint; + inline _Segment() = default; inline _Segment(const RawPoint& p, const RawPoint& pp): @@ -156,36 +168,36 @@ public: inline double length(); }; -// This struct serves as a namespace. The only difference is that is can be +// This struct serves almost as a namespace. The only difference is that is can // used in friend declarations. -struct PointLike { +namespace pointlike { template - static TCoord x(const RawPoint& p) + inline TCoord x(const RawPoint& p) { return p.x(); } template - static TCoord y(const RawPoint& p) + inline TCoord y(const RawPoint& p) { return p.y(); } template - static TCoord& x(RawPoint& p) + inline TCoord& x(RawPoint& p) { return p.x(); } template - static TCoord& y(RawPoint& p) + inline TCoord& y(RawPoint& p) { return p.y(); } template - static double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) + inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) { static_assert(always_false::value, "PointLike::distance(point, point) unimplemented!"); @@ -193,7 +205,7 @@ struct PointLike { } template - static double distance(const RawPoint& /*p1*/, + inline double distance(const RawPoint& /*p1*/, const _Segment& /*s*/) { static_assert(always_false::value, @@ -202,13 +214,13 @@ struct PointLike { } template - static std::pair, bool> horizontalDistance( + inline std::pair, bool> horizontalDistance( const RawPoint& p, const _Segment& s) { using Unit = TCoord; - auto x = PointLike::x(p), y = PointLike::y(p); - auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first()); - auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second()); + auto x = pointlike::x(p), y = pointlike::y(p); + auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); + auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); TCoord ret; @@ -228,13 +240,13 @@ struct PointLike { } template - static std::pair, bool> verticalDistance( + inline std::pair, bool> verticalDistance( const RawPoint& p, const _Segment& s) { using Unit = TCoord; - auto x = PointLike::x(p), y = PointLike::y(p); - auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first()); - auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second()); + auto x = pointlike::x(p), y = pointlike::y(p); + auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); + auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); TCoord ret; @@ -252,36 +264,36 @@ struct PointLike { return {ret, true}; } -}; +} template TCoord _Box::width() const BP2D_NOEXCEPT { - return PointLike::x(maxCorner()) - PointLike::x(minCorner()); + return pointlike::x(maxCorner()) - pointlike::x(minCorner()); } template TCoord _Box::height() const BP2D_NOEXCEPT { - return PointLike::y(maxCorner()) - PointLike::y(minCorner()); + return pointlike::y(maxCorner()) - pointlike::y(minCorner()); } template -TCoord getX(const RawPoint& p) { return PointLike::x(p); } +TCoord getX(const RawPoint& p) { return pointlike::x(p); } template -TCoord getY(const RawPoint& p) { return PointLike::y(p); } +TCoord getY(const RawPoint& p) { return pointlike::y(p); } template void setX(RawPoint& p, const TCoord& val) { - PointLike::x(p) = val; + pointlike::x(p) = val; } template void setY(RawPoint& p, const TCoord& val) { - PointLike::y(p) = val; + pointlike::y(p) = val; } template @@ -303,7 +315,7 @@ inline Radians _Segment::angleToXaxis() const template inline double _Segment::length() { - return PointLike::distance(first(), second()); + return pointlike::distance(first(), second()); } template @@ -356,124 +368,124 @@ enum class Formats { // This struct serves as a namespace. The only difference is that it can be // used in friend declarations and can be aliased at class scope. -struct ShapeLike { +namespace shapelike { template using Shapes = std::vector; template - static RawShape create(const TContour& contour, + inline RawShape create(const TContour& contour, const THolesContainer& holes) { return RawShape(contour, holes); } template - static RawShape create(TContour&& contour, + inline RawShape create(TContour&& contour, THolesContainer&& holes) { return RawShape(contour, holes); } template - static RawShape create(const TContour& contour) + inline RawShape create(const TContour& contour) { return create(contour, {}); } template - static RawShape create(TContour&& contour) + inline RawShape create(TContour&& contour) { return create(contour, {}); } template - static THolesContainer& holes(RawShape& /*sh*/) + inline THolesContainer& holes(RawShape& /*sh*/) { static THolesContainer empty; return empty; } template - static const THolesContainer& holes(const RawShape& /*sh*/) + inline const THolesContainer& holes(const RawShape& /*sh*/) { static THolesContainer empty; return empty; } template - static TContour& getHole(RawShape& sh, unsigned long idx) + inline TContour& getHole(RawShape& sh, unsigned long idx) { return holes(sh)[idx]; } template - static const TContour& getHole(const RawShape& sh, + inline const TContour& getHole(const RawShape& sh, unsigned long idx) { return holes(sh)[idx]; } template - static size_t holeCount(const RawShape& sh) + inline size_t holeCount(const RawShape& sh) { return holes(sh).size(); } template - static TContour& getContour(RawShape& sh) + inline TContour& getContour(RawShape& sh) { return sh; } template - static const TContour& getContour(const RawShape& sh) + inline const TContour& getContour(const RawShape& sh) { return sh; } // Optional, does nothing by default template - static void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {} + inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {} template - static void addVertex(RawShape& sh, Args...args) + inline void addVertex(RawShape& sh, Args...args) { return getContour(sh).emplace_back(std::forward(args)...); } template - static TVertexIterator begin(RawShape& sh) + inline TVertexIterator begin(RawShape& sh) { return sh.begin(); } template - static TVertexIterator end(RawShape& sh) + inline TVertexIterator end(RawShape& sh) { return sh.end(); } template - static TVertexConstIterator cbegin(const RawShape& sh) + inline TVertexConstIterator cbegin(const RawShape& sh) { return sh.cbegin(); } template - static TVertexConstIterator cend(const RawShape& sh) + inline TVertexConstIterator cend(const RawShape& sh) { return sh.cend(); } template - static std::string toString(const RawShape& /*sh*/) + inline std::string toString(const RawShape& /*sh*/) { return ""; } template - static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) + inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) { static_assert(always_false::value, "ShapeLike::serialize() unimplemented!"); @@ -481,14 +493,14 @@ struct ShapeLike { } template - static void unserialize(RawShape& /*sh*/, const std::string& /*str*/) + inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/) { static_assert(always_false::value, "ShapeLike::unserialize() unimplemented!"); } template - static double area(const RawShape& /*sh*/) + inline double area(const RawShape& /*sh*/, const PolygonTag&) { static_assert(always_false::value, "ShapeLike::area() unimplemented!"); @@ -496,7 +508,7 @@ struct ShapeLike { } template - static bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) + inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) { static_assert(always_false::value, "ShapeLike::intersects() unimplemented!"); @@ -504,7 +516,7 @@ struct ShapeLike { } template - static bool isInside(const TPoint& /*point*/, + inline bool isInside(const TPoint& /*point*/, const RawShape& /*shape*/) { static_assert(always_false::value, @@ -513,7 +525,7 @@ struct ShapeLike { } template - static bool isInside(const RawShape& /*shape*/, + inline bool isInside(const RawShape& /*shape*/, const RawShape& /*shape*/) { static_assert(always_false::value, @@ -522,7 +534,7 @@ struct ShapeLike { } template - static bool touches( const RawShape& /*shape*/, + inline bool touches( const RawShape& /*shape*/, const RawShape& /*shape*/) { static_assert(always_false::value, @@ -531,7 +543,7 @@ struct ShapeLike { } template - static bool touches( const TPoint& /*point*/, + inline bool touches( const TPoint& /*point*/, const RawShape& /*shape*/) { static_assert(always_false::value, @@ -540,21 +552,22 @@ struct ShapeLike { } template - static _Box> boundingBox(const RawShape& /*sh*/) + inline _Box> boundingBox(const RawShape& /*sh*/, + const PolygonTag&) { static_assert(always_false::value, "ShapeLike::boundingBox(shape) unimplemented!"); } template - static _Box> boundingBox(const Shapes& /*sh*/) + inline _Box> boundingBox(const Shapes& /*sh*/) { static_assert(always_false::value, "ShapeLike::boundingBox(shapes) unimplemented!"); } template - static RawShape convexHull(const RawShape& /*sh*/) + inline RawShape convexHull(const RawShape& /*sh*/) { static_assert(always_false::value, "ShapeLike::convexHull(shape) unimplemented!"); @@ -562,7 +575,7 @@ struct ShapeLike { } template - static RawShape convexHull(const Shapes& /*sh*/) + inline RawShape convexHull(const Shapes& /*sh*/) { static_assert(always_false::value, "ShapeLike::convexHull(shapes) unimplemented!"); @@ -570,34 +583,34 @@ struct ShapeLike { } template - static void rotate(RawShape& /*sh*/, const Radians& /*rads*/) + inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/) { static_assert(always_false::value, "ShapeLike::rotate() unimplemented!"); } template - static void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) + inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) { static_assert(always_false::value, "ShapeLike::translate() unimplemented!"); } template - static void offset(RawShape& /*sh*/, TCoord> /*distance*/) + inline void offset(RawShape& /*sh*/, TCoord> /*distance*/) { static_assert(always_false::value, "ShapeLike::offset() unimplemented!"); } template - static std::pair isValid(const RawShape& /*sh*/) + inline std::pair isValid(const RawShape& /*sh*/) { return {false, "ShapeLike::isValid() unimplemented!"}; } template - static inline bool isConvex(const TContour& sh) + inline bool isConvex(const TContour& sh) { using Vertex = TPoint; auto first = sh.begin(); @@ -633,43 +646,55 @@ struct ShapeLike { // No need to implement these // ************************************************************************* - template - static inline _Box> boundingBox( - const _Box>& box) + template + inline Box boundingBox(const Box& box, const BoxTag& ) { return box; } - template - static inline _Box> boundingBox( - const _Circle>& circ) + template + inline _Box boundingBox( + const Circle& circ, const CircleTag&) { - using Coord = TCoord>; - TPoint pmin = { + using Point = typename Circle::PointType; + using Coord = TCoord; + Point pmin = { static_cast(getX(circ.center()) - circ.radius()), static_cast(getY(circ.center()) - circ.radius()) }; - TPoint pmax = { + Point pmax = { static_cast(getX(circ.center()) + circ.radius()), static_cast(getY(circ.center()) + circ.radius()) }; return {pmin, pmax}; } - template - static inline double area(const _Box>& box) + template // Dispatch function + inline _Box boundingBox(const S& sh) { - return static_cast(box.width() * box.height()); + return boundingBox(sh, typename S::Tag()); } - template - static inline double area(const _Circle>& circ) + template + inline double area(const Box& box, const BoxTag& ) + { + return box.area(); + } + + template + inline double area(const Circle& circ, const CircleTag& ) { return circ.area(); } + template // Dispatching function + inline double area(const RawShape& sh) + { + return area(sh, typename RawShape::Tag()); + } + template - static inline double area(const Shapes& shapes) + inline double area(const Shapes& shapes) { return std::accumulate(shapes.begin(), shapes.end(), 0.0, [](double a, const RawShape& b) { @@ -678,14 +703,14 @@ struct ShapeLike { } template - static bool isInside(const TPoint& point, + inline bool isInside(const TPoint& point, const _Circle>& circ) { - return PointLike::distance(point, circ.center()) < circ.radius(); + return pointlike::distance(point, circ.center()) < circ.radius(); } template - static bool isInside(const TPoint& point, + inline bool isInside(const TPoint& point, const _Box>& box) { auto px = getX(point); @@ -699,7 +724,7 @@ struct ShapeLike { } template - static bool isInside(const RawShape& sh, + inline bool isInside(const RawShape& sh, const _Circle>& circ) { return std::all_of(cbegin(sh), cend(sh), @@ -709,7 +734,7 @@ struct ShapeLike { } template - static bool isInside(const _Box>& box, + inline bool isInside(const _Box>& box, const _Circle>& circ) { return isInside(box.minCorner(), circ) && @@ -717,7 +742,7 @@ struct ShapeLike { } template - static bool isInside(const _Box>& ibb, + inline bool isInside(const _Box>& ibb, const _Box>& box) { auto iminX = getX(ibb.minCorner()); @@ -734,31 +759,31 @@ struct ShapeLike { } template // Potential O(1) implementation may exist - static inline TPoint& vertex(RawShape& sh, unsigned long idx) + inline TPoint& vertex(RawShape& sh, unsigned long idx) { return *(begin(sh) + idx); } template // Potential O(1) implementation may exist - static inline const TPoint& vertex(const RawShape& sh, + inline const TPoint& vertex(const RawShape& sh, unsigned long idx) { return *(cbegin(sh) + idx); } template - static inline size_t contourVertexCount(const RawShape& sh) + inline size_t contourVertexCount(const RawShape& sh) { return cend(sh) - cbegin(sh); } template - static inline void foreachContourVertex(RawShape& sh, Fn fn) { + inline void foreachContourVertex(RawShape& sh, Fn fn) { for(auto it = begin(sh); it != end(sh); ++it) fn(*it); } template - static inline void foreachHoleVertex(RawShape& sh, Fn fn) { + inline void foreachHoleVertex(RawShape& sh, Fn fn) { for(int i = 0; i < holeCount(sh); ++i) { auto& h = getHole(sh, i); for(auto it = begin(h); it != end(h); ++it) fn(*it); @@ -766,12 +791,12 @@ struct ShapeLike { } template - static inline void foreachContourVertex(const RawShape& sh, Fn fn) { + inline void foreachContourVertex(const RawShape& sh, Fn fn) { for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it); } template - static inline void foreachHoleVertex(const RawShape& sh, Fn fn) { + inline void foreachHoleVertex(const RawShape& sh, Fn fn) { for(int i = 0; i < holeCount(sh); ++i) { auto& h = getHole(sh, i); for(auto it = cbegin(h); it != cend(h); ++it) fn(*it); @@ -779,18 +804,17 @@ struct ShapeLike { } template - static inline void foreachVertex(RawShape& sh, Fn fn) { + inline void foreachVertex(RawShape& sh, Fn fn) { foreachContourVertex(sh, fn); foreachHoleVertex(sh, fn); } template - static inline void foreachVertex(const RawShape& sh, Fn fn) { + inline void foreachVertex(const RawShape& sh, Fn fn) { foreachContourVertex(sh, fn); foreachHoleVertex(sh, fn); } - -}; +} } diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp index 90cf21be5b..6cac374aef 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp @@ -9,6 +9,27 @@ namespace libnest2d { +namespace __nfp { +// Do not specialize this... +template +inline bool _vsort(const TPoint& v1, const TPoint& v2) +{ + using Coord = TCoord>; + Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); + auto diff = y1 - y2; + if(std::abs(diff) <= std::numeric_limits::epsilon()) + return x1 < x2; + + return diff < 0; +} +} + +/// A collection of static methods for handling the no fit polygon creation. +namespace nfp { + +namespace sl = shapelike; +namespace pl = pointlike; + /// The complexity level of a polygon that an NFP implementation can handle. enum class NfpLevel: unsigned { CONVEX_ONLY, @@ -18,12 +39,17 @@ enum class NfpLevel: unsigned { BOTH_CONCAVE_WITH_HOLES }; -/// A collection of static methods for handling the no fit polygon creation. -struct Nfp { +template +using NfpResult = std::pair>; + +template struct MaxNfpLevel { + static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; +}; + // Shorthand for a pile of polygons template -using Shapes = typename ShapeLike::Shapes; +using Shapes = typename shapelike::Shapes; /** * Merge a bunch of polygons with the specified additional polygon. @@ -37,7 +63,7 @@ using Shapes = typename ShapeLike::Shapes; * polygons are disjuct than the resulting set will contain more polygons. */ template -static Shapes merge(const Shapes& /*shc*/) +inline Shapes merge(const Shapes& /*shc*/) { static_assert(always_false::value, "Nfp::merge(shapes, shape) unimplemented!"); @@ -55,7 +81,7 @@ static Shapes merge(const Shapes& /*shc*/) * polygons are disjuct than the resulting set will contain more polygons. */ template -static Shapes merge(const Shapes& shc, +inline Shapes merge(const Shapes& shc, const RawShape& sh) { auto m = merge(shc); @@ -63,31 +89,18 @@ static Shapes merge(const Shapes& shc, return merge(m); } -/** - * A method to get a vertex from a polygon that always maintains a relative - * position to the coordinate system: It is always the rightmost top vertex. - * - * This way it does not matter in what order the vertices are stored, the - * reference will be always the same for the same polygon. - */ -template -inline static TPoint referenceVertex(const RawShape& sh) -{ - return rightmostUpVertex(sh); -} - /** * Get the vertex of the polygon that is at the lowest values (bottom) in the Y * axis and if there are more than one vertices on the same Y coordinate than * the result will be the leftmost (with the highest X coordinate). */ template -static TPoint leftmostDownVertex(const RawShape& sh) +inline TPoint leftmostDownVertex(const RawShape& sh) { // find min x and min y vertex - auto it = std::min_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh), - _vsort); + auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh), + __nfp::_vsort); return *it; } @@ -98,26 +111,27 @@ static TPoint leftmostDownVertex(const RawShape& sh) * the result will be the rightmost (with the lowest X coordinate). */ template -static TPoint rightmostUpVertex(const RawShape& sh) +TPoint rightmostUpVertex(const RawShape& sh) { // find max x and max y vertex - auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh), - _vsort); + auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh), + __nfp::_vsort); return *it; } +/** + * A method to get a vertex from a polygon that always maintains a relative + * position to the coordinate system: It is always the rightmost top vertex. + * + * This way it does not matter in what order the vertices are stored, the + * reference will be always the same for the same polygon. + */ template -using NfpResult = std::pair>; - -/// Helper function to get the NFP -template -static NfpResult noFitPolygon(const RawShape& sh, - const RawShape& other) +inline TPoint referenceVertex(const RawShape& sh) { - NfpImpl nfp; - return nfp(sh, other); + return rightmostUpVertex(sh); } /** @@ -139,11 +153,11 @@ static NfpResult noFitPolygon(const RawShape& sh, * */ template -static NfpResult nfpConvexOnly(const RawShape& sh, +inline NfpResult nfpConvexOnly(const RawShape& sh, const RawShape& other) { using Vertex = TPoint; using Edge = _Segment; - using sl = ShapeLike; + namespace sl = shapelike; RawShape rsh; // Final nfp placeholder Vertex top_nfp; @@ -187,7 +201,7 @@ static NfpResult nfpConvexOnly(const RawShape& sh, sl::addVertex(rsh, edgelist.front().second()); // Sorting function for the nfp reference vertex search - auto& cmp = _vsort; + auto& cmp = __nfp::_vsort; // the reference (rightmost top) vertex so far top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp ); @@ -214,7 +228,7 @@ static NfpResult nfpConvexOnly(const RawShape& sh, } template -static NfpResult nfpSimpleSimple(const RawShape& cstationary, +NfpResult nfpSimpleSimple(const RawShape& cstationary, const RawShape& cother) { @@ -233,7 +247,7 @@ static NfpResult nfpSimpleSimple(const RawShape& cstationary, using Vertex = TPoint; using Coord = TCoord; using Edge = _Segment; - using sl = ShapeLike; + namespace sl = shapelike; using std::signbit; using std::sort; using std::vector; @@ -528,27 +542,16 @@ struct NfpImpl { } }; -template struct MaxNfpLevel { - static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; -}; - -private: - -// Do not specialize this... -template -static inline bool _vsort(const TPoint& v1, - const TPoint& v2) +/// Helper function to get the NFP +template +inline NfpResult noFitPolygon(const RawShape& sh, + const RawShape& other) { - using Coord = TCoord>; - Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); - auto diff = y1 - y2; - if(std::abs(diff) <= std::numeric_limits::epsilon()) - return x1 < x2; - - return diff < 0; + NfpImpl nfps; + return nfps(sh, other); } -}; +} } diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index eadd1e110b..d2850d4eda 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -9,10 +9,12 @@ #include #include "geometry_traits.hpp" -#include "optimizer.hpp" namespace libnest2d { +namespace sl = shapelike; +namespace pl = pointlike; + /** * \brief An item to be placed on a bin. * @@ -28,7 +30,6 @@ class _Item { using Coord = TCoord>; using Vertex = TPoint; using Box = _Box; - using sl = ShapeLike; // The original shape that gets encapsulated. RawShape sh_; @@ -438,7 +439,7 @@ public: inline _Rectangle(Unit width, Unit height, // disable this ctor if o != CLOCKWISE enable_if_t< o == TO::CLOCKWISE, int> = 0 ): - _Item( ShapeLike::create( { + _Item( sl::create( { {0, 0}, {0, height}, {width, height}, @@ -452,7 +453,7 @@ public: inline _Rectangle(Unit width, Unit height, // disable this ctor if o != COUNTER_CLOCKWISE enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ): - _Item( ShapeLike::create( { + _Item( sl::create( { {0, 0}, {width, 0}, {width, height}, @@ -473,12 +474,32 @@ public: template inline bool _Item::isInside(const _Box>& box) const { - return ShapeLike::isInside(boundingBox(), box); + return sl::isInside(boundingBox(), box); } template inline bool _Item::isInside(const _Circle>& circ) const { - return ShapeLike::isInside(transformedShape(), circ); + return sl::isInside(transformedShape(), circ); +} + + +template using _ItemRef = std::reference_wrapper; +template using _ItemGroup = std::vector<_ItemRef>; + +template +struct ConstItemRange { + Iterator from; + Iterator to; + bool valid = false; + + ConstItemRange() = default; + ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {} +}; + +template +inline ConstItemRange +rem(typename Container::const_iterator it, const Container& cont) { + return {std::next(it), cont.end()}; } /** @@ -515,8 +536,9 @@ public: */ using PackResult = typename PlacementStrategy::PackResult; - using ItemRef = std::reference_wrapper; - using ItemGroup = std::vector; + using ItemRef = _ItemRef; + using ItemGroup = _ItemGroup; + using DefaultIterator = typename ItemGroup::const_iterator; /** * @brief Constructor taking the bin and an optional configuration. @@ -544,20 +566,24 @@ public: * Try to pack an item with a result object that contains the packing * information for later accepting it. * - * \param item_store A container of items + * \param item_store A container of items that are intended to be packed + * later. Can be used by the placer to switch tactics. When it's knows that + * many items will come a greedy startegy may not be the best. + * \param from The iterator to the item from which the packing should start, + * including the pointed item + * \param count How many items should be packed. If the value is 1, than + * just the item pointed to by "from" argument should be packed. */ - template - inline PackResult trypack(Container& item_store, - typename Container::iterator from, - unsigned count = 1) { - using V = typename Container::value_type; - static_assert(std::is_convertible::value, - "Invalid Item container!"); - return impl_.trypack(item_store, from, count); + template + inline PackResult trypack( + Item& item, + const ConstItemRange& remaining = ConstItemRange()) + { + return impl_.trypack(item, remaining); } /** - * @brief A method to accept a previously tried item. + * @brief A method to accept a previously tried item (or items). * * If the pack result is a failure the method should ignore it. * @param r The result of a previous trypack call. @@ -565,10 +591,10 @@ public: inline void accept(PackResult& r) { impl_.accept(r); } /** - * @brief pack Try to pack an item and immediately accept it on success. + * @brief pack Try to pack and immediately accept it on success. * * A default implementation would be to call - * { auto&& r = trypack(item); accept(r); return r; } but we should let the + * { auto&& r = trypack(...); accept(r); return r; } but we should let the * implementor of the placement strategy to harvest any optimizations from * the absence of an intermadiate step. The above version can still be used * in the implementation. @@ -577,15 +603,12 @@ public: * @return Returns true if the item was packed or false if it could not be * packed. */ - template - inline bool pack(Container& item_store, - typename Container::iterator from, - unsigned count = 1) + template> + inline bool pack( + Item& item, + const Range& remaining = Range()) { - using V = typename Container::value_type; - static_assert(std::is_convertible::value, - "Invalid Item container!"); - return impl_.pack(item_store, from, count); + return impl_.pack(item, remaining); } /// Unpack the last element (remove it from the list of packed items). @@ -736,10 +759,9 @@ using _IndexedPackGroup = std::vector< * inside the provided bin. */ template -class Arranger { +class Nester { using TSel = SelectionStrategyLike; TSel selector_; - bool use_min_bb_rotation_ = false; public: using Item = typename PlacementStrategy::Item; using ItemRef = std::reference_wrapper; @@ -777,7 +799,7 @@ public: template - Arranger( TBinType&& bin, + Nester( TBinType&& bin, Unit min_obj_distance = 0, PConf&& pconfig = PConf(), SConf&& sconfig = SConf()): @@ -810,9 +832,9 @@ public: * the selection algorithm. */ template - inline PackGroup arrange(TIterator from, TIterator to) + inline PackGroup execute(TIterator from, TIterator to) { - return _arrange(from, to); + return _execute(from, to); } /** @@ -823,20 +845,20 @@ public: * input sequence size. */ template - inline IndexedPackGroup arrangeIndexed(TIterator from, TIterator to) + inline IndexedPackGroup executeIndexed(TIterator from, TIterator to) { - return _arrangeIndexed(from, to); + return _executeIndexed(from, to); } /// Shorthand to normal arrange method. template inline PackGroup operator() (TIterator from, TIterator to) { - return _arrange(from, to); + return _execute(from, to); } /// Set a progress indicatior function object for the selector. - inline Arranger& progressIndicator(ProgressFunction func) + inline Nester& progressIndicator(ProgressFunction func) { selector_.progressIndicator(func); return *this; } @@ -850,10 +872,6 @@ public: return ret; } - inline Arranger& useMinimumBoundigBoxRotation(bool s = true) { - use_min_bb_rotation_ = s; return *this; - } - private: template::value, IT> > - inline PackGroup _arrange(TIterator from, TIterator to, bool = false) + inline PackGroup _execute(TIterator from, TIterator to, bool = false) { - __arrange(from, to); + __execute(from, to); return lastResult(); } @@ -875,11 +893,11 @@ private: class IT = remove_cvref_t, class T = enable_if_t::value, IT> > - inline PackGroup _arrange(TIterator from, TIterator to, int = false) + inline PackGroup _execute(TIterator from, TIterator to, int = false) { item_cache_ = {from, to}; - __arrange(item_cache_.begin(), item_cache_.end()); + __execute(item_cache_.begin(), item_cache_.end()); return lastResult(); } @@ -892,11 +910,11 @@ private: // have to exist for the lifetime of this call. class T = enable_if_t< std::is_convertible::value, IT> > - inline IndexedPackGroup _arrangeIndexed(TIterator from, + inline IndexedPackGroup _executeIndexed(TIterator from, TIterator to, bool = false) { - __arrange(from, to); + __execute(from, to); return createIndexedPackGroup(from, to, selector_); } @@ -904,12 +922,12 @@ private: class IT = remove_cvref_t, class T = enable_if_t::value, IT> > - inline IndexedPackGroup _arrangeIndexed(TIterator from, + inline IndexedPackGroup _executeIndexed(TIterator from, TIterator to, int = false) { item_cache_ = {from, to}; - __arrange(item_cache_.begin(), item_cache_.end()); + __execute(item_cache_.begin(), item_cache_.end()); return createIndexedPackGroup(from, to, selector_); } @@ -941,37 +959,12 @@ private: return pg; } - Radians findBestRotation(Item& item) { - opt::StopCriteria stopcr; - stopcr.absolute_score_difference = 0.01; - stopcr.max_iterations = 10000; - opt::TOptimizer solver(stopcr); - - auto orig_rot = item.rotation(); - - auto result = solver.optimize_min([&item, &orig_rot](Radians rot){ - item.rotation(orig_rot + rot); - auto bb = item.boundingBox(); - return std::sqrt(bb.height()*bb.width()); - }, opt::initvals(Radians(0)), opt::bound(-Pi/2, Pi/2)); - - item.rotation(orig_rot); - - return std::get<0>(result.optimum); - } - - template inline void __arrange(TIter from, TIter to) + 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))); }); - if(use_min_bb_rotation_) - std::for_each(from, to, [this](Item& item){ - Radians rot = findBestRotation(item); - item.rotate(rot); - }); - selector_.template packItems( from, to, bin_, pconfig_); diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp index 71573e34dd..af16783721 100644 --- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp @@ -27,11 +27,10 @@ public: explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {} - template - PackResult trypack(Store& /*s*/, typename Store::iterator from, - unsigned /*count*/ = 1) + template> + PackResult trypack(Item& item, + const Range& = Range()) { - Item& item = *from; auto r = _trypack(item); if(!r && Base::config_.allow_rotations) { @@ -117,10 +116,10 @@ protected: const RawShape& scanpoly) { auto tsh = other.transformedShape(); - return ( ShapeLike::intersects(tsh, scanpoly) || - ShapeLike::isInside(tsh, scanpoly) ) && - ( !ShapeLike::intersects(tsh, item.rawShape()) && - !ShapeLike::isInside(tsh, item.rawShape()) ); + return ( sl::intersects(tsh, scanpoly) || + sl::isInside(tsh, scanpoly) ) && + ( !sl::intersects(tsh, item.rawShape()) && + !sl::isInside(tsh, item.rawShape()) ); } template @@ -131,25 +130,25 @@ protected: { auto tsh = other.transformedShape(); - bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) && - !ShapeLike::touches(tsh, scanpoly); - bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) && - !ShapeLike::touches(tsh, item.rawShape()); + bool inters_scanpoly = sl::intersects(tsh, scanpoly) && + !sl::touches(tsh, scanpoly); + bool inters_item = sl::intersects(tsh, item.rawShape()) && + !sl::touches(tsh, item.rawShape()); return ( inters_scanpoly || - ShapeLike::isInside(tsh, scanpoly)) && + sl::isInside(tsh, scanpoly)) && ( !inters_item && - !ShapeLike::isInside(tsh, item.rawShape()) + !sl::isInside(tsh, item.rawShape()) ); } - Container itemsInTheWayOf(const Item& item, const Dir dir) { + ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) { // Get the left or down polygon, that has the same area as the shadow // of input item reflected to the left or downwards auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) : downPoly(item); - Container ret; // packed items 'in the way' of item + ItemGroup ret; // packed items 'in the way' of item ret.reserve(items_.size()); // Predicate to find items that are 'in the way' for left (down) move @@ -178,18 +177,18 @@ protected: if(dir == Dir::LEFT) { getCoord = [](const Vertex& v) { return getX(v); }; - availableDistance = PointLike::horizontalDistance; + availableDistance = pointlike::horizontalDistance; availableDistanceSV = [](const Segment& s, const Vertex& v) { - auto ret = PointLike::horizontalDistance(v, s); + auto ret = pointlike::horizontalDistance(v, s); if(ret.second) ret.first = -ret.first; return ret; }; } else { getCoord = [](const Vertex& v) { return getY(v); }; - availableDistance = PointLike::verticalDistance; + availableDistance = pointlike::verticalDistance; availableDistanceSV = [](const Segment& s, const Vertex& v) { - auto ret = PointLike::verticalDistance(v, s); + auto ret = pointlike::verticalDistance(v, s); if(ret.second) ret.first = -ret.first; return ret; }; @@ -219,9 +218,9 @@ protected: assert(pleft.vertexCount() > 0); auto trpleft = pleft.transformedShape(); - auto first = ShapeLike::begin(trpleft); + auto first = sl::begin(trpleft); auto next = first + 1; - auto endit = ShapeLike::end(trpleft); + auto endit = sl::end(trpleft); while(next != endit) { Segment seg(*(first++), *(next++)); @@ -345,16 +344,16 @@ protected: // reserve for all vertices plus 2 for the left horizontal wall, 2 for // the additional vertices for maintaning min object distance - ShapeLike::reserve(rsh, finish-start+4); + sl::reserve(rsh, finish-start+4); /*auto addOthers = [&rsh, finish, start, &item](){ for(size_t i = start+1; i < finish; i++) - ShapeLike::addVertex(rsh, item.vertex(i)); + sl::addVertex(rsh, item.vertex(i)); };*/ auto reverseAddOthers = [&rsh, finish, start, &item](){ for(auto i = finish-1; i > start; i--) - ShapeLike::addVertex(rsh, item.vertex( + sl::addVertex(rsh, item.vertex( static_cast(i))); }; @@ -366,25 +365,25 @@ protected: // Clockwise polygon construction - ShapeLike::addVertex(rsh, topleft_vertex); + sl::addVertex(rsh, topleft_vertex); if(dir == Dir::LEFT) reverseAddOthers(); else { - ShapeLike::addVertex(rsh, getX(topleft_vertex), 0); - ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0); + sl::addVertex(rsh, getX(topleft_vertex), 0); + sl::addVertex(rsh, getX(bottomleft_vertex), 0); } - ShapeLike::addVertex(rsh, bottomleft_vertex); + sl::addVertex(rsh, bottomleft_vertex); if(dir == Dir::LEFT) { - ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex)); - ShapeLike::addVertex(rsh, 0, getY(topleft_vertex)); + sl::addVertex(rsh, 0, getY(bottomleft_vertex)); + sl::addVertex(rsh, 0, getY(topleft_vertex)); } else reverseAddOthers(); // Close the polygon - ShapeLike::addVertex(rsh, topleft_vertex); + sl::addVertex(rsh, topleft_vertex); return rsh; } diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index d74fe2b1d3..638d606e0f 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -19,7 +19,7 @@ namespace libnest2d { namespace strategies { template struct NfpPConfig { - using ItemGroup = std::vector>>; + using ItemGroup = _ItemGroup<_Item>; enum class Alignment { CENTER, @@ -58,16 +58,6 @@ struct NfpPConfig { * * \param item The second parameter is the candidate item. * - * \param occupied_area The third parameter is the sum of areas of the - * items in the first parameter (no candidate item there) so you don't have - * to iterate through them if you only need their accumulated area. - * - * \param norm A norming factor for physical dimensions. E.g. if your score - * is the distance between the item and the bin center, you should divide - * that distance with the norming factor. If the score is an area than - * divide it with the square of the norming factor. Imagine it as a unit of - * distance. - * * \param remaining A container with the remaining items waiting to be * placed. You can use some features about the remaining items to alter to * score of the current placement. If you know that you have to leave place @@ -81,8 +71,8 @@ struct NfpPConfig { * decisions (for you or a more intelligent AI). * */ - std::function&, const _Item&, - double, double, const ItemGroup&)> + std::function&, const _Item&, + const ItemGroup&)> object_function; /** @@ -134,11 +124,11 @@ template class EdgeCache { void createCache(const RawShape& sh) { { // For the contour - auto first = ShapeLike::cbegin(sh); + auto first = shapelike::cbegin(sh); auto next = std::next(first); - auto endit = ShapeLike::cend(sh); + auto endit = shapelike::cend(sh); - contour_.distances.reserve(ShapeLike::contourVertexCount(sh)); + contour_.distances.reserve(shapelike::contourVertexCount(sh)); while(next != endit) { contour_.emap.emplace_back(*(first++), *(next++)); @@ -147,7 +137,7 @@ template class EdgeCache { } } - for(auto& h : ShapeLike::holes(sh)) { // For the holes + for(auto& h : shapelike::holes(sh)) { // For the holes auto first = h.begin(); auto next = std::next(first); auto endit = h.end(); @@ -295,11 +285,11 @@ public: }; -template -struct Lvl { static const NfpLevel value = lvl; }; +template +struct Lvl { static const nfp::NfpLevel value = lvl; }; template -inline void correctNfpPosition(Nfp::NfpResult& nfp, +inline void correctNfpPosition(nfp::NfpResult& nfp, const _Item& stationary, const _Item& orbiter) { @@ -319,46 +309,47 @@ inline void correctNfpPosition(Nfp::NfpResult& nfp, auto dtouch = touch_sh - touch_other; auto top_other = orbiter.rightmostTopVertex() + dtouch; auto dnfp = top_other - nfp.second; // nfp.second is the nfp reference point - ShapeLike::translate(nfp.first, dnfp); + shapelike::translate(nfp.first, dnfp); } template -inline void correctNfpPosition(Nfp::NfpResult& nfp, +inline void correctNfpPosition(nfp::NfpResult& nfp, const RawShape& stationary, const _Item& orbiter) { - auto touch_sh = Nfp::rightmostUpVertex(stationary); + auto touch_sh = nfp::rightmostUpVertex(stationary); auto touch_other = orbiter.leftmostBottomVertex(); auto dtouch = touch_sh - touch_other; auto top_other = orbiter.rightmostTopVertex() + dtouch; auto dnfp = top_other - nfp.second; - ShapeLike::translate(nfp.first, dnfp); + shapelike::translate(nfp.first, dnfp); } template -Nfp::Shapes nfp( const Container& polygons, +nfp::Shapes calcnfp( const Container& polygons, const _Item& trsh, - Lvl) + Lvl) { using Item = _Item; + using namespace nfp; - Nfp::Shapes nfps; + nfp::Shapes nfps; // int pi = 0; for(Item& sh : polygons) { - auto subnfp_r = Nfp::noFitPolygon( + auto subnfp_r = noFitPolygon( sh.transformedShape(), trsh.transformedShape()); #ifndef NDEBUG - auto vv = ShapeLike::isValid(sh.transformedShape()); + auto vv = sl::isValid(sh.transformedShape()); assert(vv.first); - auto vnfp = ShapeLike::isValid(subnfp_r.first); + auto vnfp = sl::isValid(subnfp_r.first); assert(vnfp.first); #endif correctNfpPosition(subnfp_r, sh, trsh); - nfps = Nfp::merge(nfps, subnfp_r.first); + nfps = nfp::merge(nfps, subnfp_r.first); // double SCALE = 1000000; // using SVGWriter = svg::SVGWriter; @@ -379,31 +370,32 @@ Nfp::Shapes nfp( const Container& polygons, } template -Nfp::Shapes nfp( const Container& polygons, +nfp::Shapes calcnfp( const Container& polygons, const _Item& trsh, Level) { + using namespace nfp; using Item = _Item; - Nfp::Shapes nfps; + Shapes nfps; auto& orb = trsh.transformedShape(); bool orbconvex = trsh.isContourConvex(); for(Item& sh : polygons) { - Nfp::NfpResult subnfp; + nfp::NfpResult subnfp; auto& stat = sh.transformedShape(); if(sh.isContourConvex() && orbconvex) - subnfp = Nfp::noFitPolygon(stat, orb); + subnfp = nfp::noFitPolygon(stat, orb); else if(orbconvex) - subnfp = Nfp::noFitPolygon(stat, orb); + subnfp = nfp::noFitPolygon(stat, orb); else - subnfp = Nfp::noFitPolygon(stat, orb); + subnfp = nfp::noFitPolygon(stat, orb); correctNfpPosition(subnfp, sh, trsh); - nfps = Nfp::merge(nfps, subnfp.first); + nfps = nfp::merge(nfps, subnfp.first); } return nfps; @@ -448,7 +440,6 @@ Nfp::Shapes nfp( const Container& polygons, template _Circle> minimizeCircle(const RawShape& sh) { - using sl = ShapeLike; using pl = PointLike; using Point = TPoint; using Coord = TCoord; @@ -518,16 +509,14 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer; - using sl = ShapeLike; - + using MaxNfpLevel = nfp::MaxNfpLevel; public: - using Pile = Nfp::Shapes; + using Pile = nfp::Shapes; inline explicit _NofitPolyPlacer(const BinType& bin): Base(bin), - norm_(std::sqrt(sl::area(bin))) {} + norm_(std::sqrt(sl::area(bin))) {} _NofitPolyPlacer(const _NofitPolyPlacer&) = default; _NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default; @@ -577,20 +566,17 @@ public: return boundingCircle(chull).radius() < bin.radius(); } - template - PackResult trypack(Container& items, - typename Container::iterator from, - unsigned /*count*/ = 1) - { - return trypack(*from, {std::next(from), items.end()}); - } - - PackResult trypack(Item& item, ItemGroup remaining) { + template> + PackResult trypack( + Item& item, + const Range& remaining = Range()) { PackResult ret; bool can_pack = false; + auto remlist = ItemGroup(remaining.from, remaining.to); + if(items_.empty()) { setInitialPosition(item); can_pack = item.isInside(bin_); @@ -602,7 +588,7 @@ public: auto initial_rot = item.rotation(); Vertex final_tr = {0, 0}; Radians final_rot = initial_rot; - Nfp::Shapes nfps; + nfp::Shapes nfps; for(auto rot : config_.rotations) { @@ -615,8 +601,8 @@ public: auto trsh = item.transformedShape(); - nfps = nfp(items_, item, Lvl()); - auto iv = Nfp::referenceVertex(trsh); + nfps = calcnfp(items_, item, Lvl()); + auto iv = nfp::referenceVertex(trsh); auto startpos = item.translation(); @@ -644,7 +630,7 @@ public: ecache[opt.nfpidx].coords(opt.hidx, opt.relpos); }; - Nfp::Shapes pile; + nfp::Shapes pile; pile.reserve(items_.size()+1); double pile_area = 0; for(Item& mitem : items_) { @@ -652,17 +638,15 @@ public: pile_area += mitem.area(); } - auto merged_pile = Nfp::merge(pile); + auto merged_pile = nfp::merge(pile); // This is the kernel part of the object function that is // customizable by the library client auto _objfunc = config_.object_function? config_.object_function : - [this, &merged_pile]( - Nfp::Shapes& /*pile*/, + [this, &merged_pile, &pile_area]( + nfp::Shapes& /*pile*/, const Item& item, - double occupied_area, - double norm, const ItemGroup& /*remaining*/) { merged_pile.emplace_back(item.transformedShape()); @@ -670,7 +654,7 @@ public: merged_pile.pop_back(); // The pack ratio -- how much is the convex hull occupied - double pack_rate = occupied_area/sl::area(ch); + double pack_rate = (pile_area + item.area())/sl::area(ch); // ratio of waste double waste = 1.0 - pack_rate; @@ -680,7 +664,7 @@ public: // (larger) values. auto score = std::sqrt(waste); - if(!wouldFit(ch, bin_)) score += norm; + if(!wouldFit(ch, bin_)) score += norm_; return score; }; @@ -692,10 +676,7 @@ public: d += startpos; item.translation(d); - double occupied_area = pile_area + item.area(); - - double score = _objfunc(pile, item, occupied_area, - norm_, remaining); + double score = _objfunc(pile, item, remlist); return score; }; @@ -830,7 +811,7 @@ private: inline void finalAlign(_Circle> cbin) { if(items_.empty()) return; - Nfp::Shapes m; + nfp::Shapes m; m.reserve(items_.size()); for(Item& item : items_) m.emplace_back(item.transformedShape()); @@ -842,7 +823,7 @@ private: inline void finalAlign(Box bbin) { if(items_.empty()) return; - Nfp::Shapes m; + nfp::Shapes m; m.reserve(items_.size()); for(Item& item : items_) m.emplace_back(item.transformedShape()); auto&& bb = sl::boundingBox(m); @@ -884,7 +865,7 @@ private: void setInitialPosition(Item& item) { Box&& bb = item.boundingBox(); Vertex ci, cb; - auto bbin = sl::boundingBox(bin_); + auto bbin = sl::boundingBox(bin_); switch(config_.starting_point) { case Config::Alignment::CENTER: { @@ -920,7 +901,7 @@ private: void placeOutsideOfBin(Item& item) { auto&& bb = item.boundingBox(); - Box binbb = sl::boundingBox(bin_); + Box binbb = sl::boundingBox(bin_); Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) }; diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp index f31a9343c1..1a0730d880 100644 --- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp @@ -7,10 +7,7 @@ namespace libnest2d { namespace strategies { struct EmptyConfig {}; -template>> - > +template class PlacerBoilerplate { mutable bool farea_valid_ = false; mutable double farea_ = 0.0; @@ -22,7 +19,8 @@ public: using Coord = TCoord; using Unit = Coord; using Config = Cfg; - using Container = Store; + using ItemGroup = _ItemGroup; + using DefaultIter = typename ItemGroup::const_iterator; class PackResult { Item *item_ptr_; @@ -39,8 +37,6 @@ public: operator bool() { return item_ptr_ != nullptr; } }; - using ItemGroup = const Container&; - inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin) { items_.reserve(cap); @@ -56,11 +52,10 @@ public: config_ = config; } - template - bool pack(Container& items, - typename Container::iterator from, - unsigned count = 1) { - auto&& r = static_cast(this)->trypack(items, from, count); + template> + bool pack(Item& item, + const Range& rem = Range()) { + auto&& r = static_cast(this)->trypack(item, rem); if(r) { items_.push_back(*(r.item_ptr_)); farea_valid_ = false; @@ -82,7 +77,7 @@ public: farea_valid_ = false; } - inline ItemGroup getItems() const { return items_; } + inline const ItemGroup& getItems() const { return items_; } inline void clearItems() { items_.clear(); @@ -113,7 +108,7 @@ public: protected: BinType bin_; - Container items_; + ItemGroup items_; Cfg config_; }; @@ -124,6 +119,7 @@ using Base::items_; \ using Base::config_; \ public: \ using typename Base::Item; \ +using typename Base::ItemGroup; \ using typename Base::BinType; \ using typename Base::Config; \ using typename Base::Vertex; \ @@ -131,7 +127,6 @@ using typename Base::Segment; \ using typename Base::PackResult; \ using typename Base::Coord; \ using typename Base::Unit; \ -using typename Base::Container; \ private: } diff --git a/xs/src/libnest2d/libnest2d/rotfinder.hpp b/xs/src/libnest2d/libnest2d/rotfinder.hpp new file mode 100644 index 0000000000..525fd87595 --- /dev/null +++ b/xs/src/libnest2d/libnest2d/rotfinder.hpp @@ -0,0 +1,41 @@ +#ifndef ROTFINDER_HPP +#define ROTFINDER_HPP + +#include +#include +#include + +namespace libnest2d { + +template +Radians findBestRotation(_Item& item) { + opt::StopCriteria stopcr; + stopcr.absolute_score_difference = 0.01; + stopcr.max_iterations = 10000; + opt::TOptimizer solver(stopcr); + + auto orig_rot = item.rotation(); + + auto result = solver.optimize_min([&item, &orig_rot](Radians rot){ + item.rotation(orig_rot + rot); + auto bb = item.boundingBox(); + return std::sqrt(bb.height()*bb.width()); + }, opt::initvals(Radians(0)), opt::bound(-Pi/2, Pi/2)); + + item.rotation(orig_rot); + + return std::get<0>(result.optimum); +} + +template +void findMinimumBoundingBoxRotations(Iterator from, Iterator to) { + using V = typename std::iterator_traits::value_type; + std::for_each(from, to, [](V& item){ + Radians rot = findBestRotation(item); + item.rotate(rot); + }); +} + +} + +#endif // ROTFINDER_HPP diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index 34d6d05c5c..8c02dc3733 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -118,7 +118,7 @@ public: using Placer = PlacementStrategyLike; using ItemList = std::list; - const double bin_area = ShapeLike::area(bin); + const double bin_area = sl::area(bin); const double w = bin_area * config_.waste_increment; const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion; @@ -227,10 +227,14 @@ public: bool ret = false; auto it = not_packed.begin(); + auto pack = [&placer, ¬_packed](ItemListIt it) { + return placer.pack(*it, rem(it, not_packed)); + }; + while(it != not_packed.end() && !ret && free_area - (item_area = it->get().area()) <= waste) { - if(item_area <= free_area && placer.pack(not_packed, it) ) { + if(item_area <= free_area && pack(it) ) { free_area -= item_area; filled_area = bin_area - free_area; ret = true; @@ -270,6 +274,11 @@ public: auto it2 = it; std::vector wrong_pairs; + using std::placeholders::_1; + + auto trypack = [&placer, ¬_packed](ItemListIt it) { + return placer.trypack(*it, rem(it, not_packed)); + }; while(it != endit && !ret && free_area - (item_area = it->get().area()) - @@ -278,7 +287,7 @@ public: if(item_area + smallestPiece(it, not_packed)->get().area() > free_area ) { it++; continue; } - auto pr = placer.trypack(not_packed, it); + auto pr = trypack(it); // First would fit it2 = not_packed.begin(); @@ -294,14 +303,14 @@ public: } placer.accept(pr); - auto pr2 = placer.trypack(not_packed, it2); + auto pr2 = trypack(it2); if(!pr2) { placer.unpackLast(); // remove first if(try_reverse) { - pr2 = placer.trypack(not_packed, it2); + pr2 = trypack(it2); if(pr2) { placer.accept(pr2); - auto pr12 = placer.trypack(not_packed, it); + auto pr12 = trypack(it); if(pr12) { placer.accept(pr12); ret = true; @@ -365,6 +374,14 @@ public: return it->get().area(); }; + auto trypack = [&placer, ¬_packed](ItemListIt it) { + return placer.trypack(*it, rem(it, not_packed)); + }; + + auto pack = [&placer, ¬_packed](ItemListIt it) { + return placer.pack(*it, rem(it, not_packed)); + }; + while (it != endit && !ret) { // drill down 1st level // We need to determine in each iteration the largest, second @@ -394,7 +411,7 @@ public: it++; continue; } - auto pr = placer.trypack(not_packed, it); + auto pr = trypack(it); // Check for free area and try to pack the 1st item... if(!pr) { it++; continue; } @@ -420,15 +437,15 @@ public: bool can_pack2 = false; placer.accept(pr); - auto pr2 = placer.trypack(not_packed, it2); + auto pr2 = trypack(it2); auto pr12 = pr; if(!pr2) { placer.unpackLast(); // remove first if(try_reverse) { - pr2 = placer.trypack(not_packed, it2); + pr2 = trypack(it2); if(pr2) { placer.accept(pr2); - pr12 = placer.trypack(not_packed, it); + pr12 = trypack(it); if(pr12) can_pack2 = true; placer.unpackLast(); } @@ -463,7 +480,7 @@ public: if(a3_sum > free_area) { it3++; continue; } placer.accept(pr12); placer.accept(pr2); - bool can_pack3 = placer.pack(not_packed, it3); + bool can_pack3 = pack(it3); if(!can_pack3) { placer.unpackLast(); @@ -476,13 +493,14 @@ public: std::array candidates = {it, it2, it3}; - auto tryPack = [&placer, &candidates, ¬_packed]( + auto tryPack = [&placer, &candidates, ¬_packed, + &pack]( const decltype(indices)& idx) { std::array packed = {false}; for(auto id : idx) packed.at(id) = - placer.pack(not_packed, candidates[id]); + pack(candidates[id]); bool check = std::all_of(packed.begin(), @@ -536,7 +554,7 @@ public: { auto it = store_.begin(); while (it != store_.end()) { Placer p(bin); p.configure(pconfig); - if(!p.pack(store_, it)) { + if(!p.pack(*it, rem(it, store_))) { it = store_.erase(it); } else it++; } @@ -601,7 +619,7 @@ public: while(it != not_packed.end() && filled_area < INITIAL_FILL_AREA) { - if(placer.pack(not_packed, it)) { + if(placer.pack(*it, rem(it, not_packed))) { filled_area += it->get().area(); free_area = bin_area - filled_area; it = not_packed.erase(it); diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp index ca1281fe6f..b20455b0e4 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp @@ -56,18 +56,13 @@ public: std::sort(store_.begin(), store_.end(), sortfunc); -// Container a = {store_[0], store_[1], store_[4], store_[5] }; -//// a.insert(a.end(), store_.end()-10, store_.end()); -// store_ = a; - PlacementStrategyLike placer(bin); placer.configure(pconfig); auto it = store_.begin(); while(it != store_.end()) { - if(!placer.pack(store_, it)) { + if(!placer.pack(*it, {std::next(it), store_.end()})) { if(packed_bins_.back().empty()) ++it; -// makeProgress(placer); placer.clearItems(); packed_bins_.emplace_back(); } else { @@ -76,9 +71,6 @@ public: } } -// if(was_packed) { -// packed_bins_.push_back(placer.getItems()); -// } } }; diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index 93ca02b1e6..1312f9874d 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -61,7 +61,7 @@ public: { auto it = store_.begin(); while (it != store_.end()) { Placer p(bin); p.configure(pconfig); - if(!p.pack(store_, it)) { + if(!p.pack(*it)) { it = store_.erase(it); } else it++; } @@ -73,8 +73,9 @@ public: while(!was_packed) { for(size_t j = 0; j < placers.size() && !was_packed; j++) { - if((was_packed = placers[j].pack(store_, it))) - makeProgress(placers[j], j); + if((was_packed = + placers[j].pack(*it, rem(it, store_) ))) + makeProgress(placers[j], j); } if(!was_packed) { diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp index 79832b6830..197ff65981 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/xs/src/libnest2d/tests/test.cpp @@ -110,7 +110,7 @@ TEST(GeometryAlgorithms, boundingCircle) { ASSERT_EQ(c.center().Y, 0); ASSERT_DOUBLE_EQ(c.radius(), 10); - ShapeLike::translate(p, PointImpl{10, 10}); + shapelike::translate(p, PointImpl{10, 10}); c = boundingCircle(p); ASSERT_EQ(c.center().X, 10); @@ -124,8 +124,8 @@ TEST(GeometryAlgorithms, boundingCircle) { c = boundingCircle(part.transformedShape()); if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl; - else for(auto v : ShapeLike::getContour(part.transformedShape()) ) { - auto d = PointLike::distance(v, c.center()); + else for(auto v : shapelike::getContour(part.transformedShape()) ) { + auto d = pointlike::distance(v, c.center()); if(d > c.radius() ) { auto e = std::abs( 1.0 - d/c.radius()); ASSERT_LE(e, 1e-3); @@ -144,14 +144,14 @@ TEST(GeometryAlgorithms, Distance) { Point p2 = {10, 0}; Point p3 = {10, 10}; - ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10); - ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200)); + ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10); + ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200)); Segment seg(p1, p3); - ASSERT_DOUBLE_EQ(PointLike::distance(p2, seg), 7.0710678118654755); + ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755); - auto result = PointLike::horizontalDistance(p2, seg); + auto result = pointlike::horizontalDistance(p2, seg); auto check = [](Coord val, Coord expected) { if(std::is_floating_point::value) @@ -164,11 +164,11 @@ TEST(GeometryAlgorithms, Distance) { ASSERT_TRUE(result.second); check(result.first, 10); - result = PointLike::verticalDistance(p2, seg); + result = pointlike::verticalDistance(p2, seg); ASSERT_TRUE(result.second); check(result.first, -10); - result = PointLike::verticalDistance(Point{10, 20}, seg); + result = pointlike::verticalDistance(Point{10, 20}, seg); ASSERT_TRUE(result.second); check(result.first, 10); @@ -176,12 +176,12 @@ TEST(GeometryAlgorithms, Distance) { Point p4 = {80, 0}; Segment seg2 = { {0, 0}, {0, 40} }; - result = PointLike::horizontalDistance(p4, seg2); + result = pointlike::horizontalDistance(p4, seg2); ASSERT_TRUE(result.second); check(result.first, 80); - result = PointLike::verticalDistance(p4, seg2); + result = pointlike::verticalDistance(p4, seg2); // Point should not be related to the segment ASSERT_FALSE(result.second); @@ -209,7 +209,7 @@ TEST(GeometryAlgorithms, Area) { {61, 97} }; - ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 ); + ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 ); } TEST(GeometryAlgorithms, IsPointInsidePolygon) { @@ -287,7 +287,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon) Item leftp(placer.leftPoly(item)); - ASSERT_TRUE(ShapeLike::isValid(leftp.rawShape()).first); + ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first); ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount()); for(unsigned long i = 0; i < leftControl.vertexCount(); i++) { @@ -297,7 +297,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon) Item downp(placer.downPoly(item)); - ASSERT_TRUE(ShapeLike::isValid(downp.rawShape()).first); + ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first); ASSERT_EQ(downp.vertexCount(), downControl.vertexCount()); for(unsigned long i = 0; i < downControl.vertexCount(); i++) { @@ -334,7 +334,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) {20, 20} }; - Arranger arrange(Box(210, 250)); + Nester arrange(Box(210, 250)); auto groups = arrange(rects.begin(), rects.end()); @@ -387,7 +387,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) Coord min_obj_distance = 5; - Arranger arrange(Box(210, 250), + Nester arrange(Box(210, 250), min_obj_distance); auto groups = arrange(rects.begin(), rects.end()); @@ -438,7 +438,7 @@ R"raw( setX(v, getX(v)/SCALE); rbin.setVertex(i, v); } - out << ShapeLike::serialize(rbin.rawShape()) << std::endl; + out << shapelike::serialize(rbin.rawShape()) << std::endl; for(Item& sh : r) { Item tsh(sh.transformedShape()); for(unsigned i = 0; i < tsh.vertexCount(); i++) { @@ -447,7 +447,7 @@ R"raw( setX(v, getX(v)/SCALE); tsh.setVertex(i, v); } - out << ShapeLike::serialize(tsh.rawShape()) << std::endl; + out << shapelike::serialize(tsh.rawShape()) << std::endl; } out << "\n" << std::endl; } @@ -471,8 +471,8 @@ TEST(GeometryAlgorithms, BottomLeftStressTest) { auto next = it; int i = 0; while(it != input.end() && ++next != input.end()) { - placer.pack(input, it); - placer.pack(input, next); + placer.pack(*it); + placer.pack(*next); auto result = placer.getItems(); bool valid = true; @@ -701,7 +701,7 @@ std::vector nfp_concave_testdata = { } }; -template +template void testNfp(const std::vector& testdata) { using namespace libnest2d; @@ -716,12 +716,12 @@ void testNfp(const std::vector& testdata) { orbiter.translate({210*SCALE, 0}); - auto&& nfp = Nfp::noFitPolygon(stationary.rawShape(), + auto&& nfp = nfp::noFitPolygon(stationary.rawShape(), orbiter.transformedShape()); strategies::correctNfpPosition(nfp, stationary, orbiter); - auto v = ShapeLike::isValid(nfp.first); + auto v = shapelike::isValid(nfp.first); if(!v.first) { std::cout << v.second << std::endl; @@ -733,7 +733,7 @@ void testNfp(const std::vector& testdata) { int i = 0; auto rorbiter = orbiter.transformedShape(); - auto vo = Nfp::referenceVertex(rorbiter); + auto vo = nfp::referenceVertex(rorbiter); ASSERT_TRUE(stationary.isInside(infp)); @@ -774,7 +774,7 @@ void testNfp(const std::vector& testdata) { } TEST(GeometryAlgorithms, nfpConvexConvex) { - testNfp(nfp_testdata); + testNfp(nfp_testdata); } //TEST(GeometryAlgorithms, nfpConcaveConcave) { @@ -807,7 +807,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) { for(int i = 0; i <= 100; i++) { auto v = ecache.coords(i*(0.01)); - ASSERT_TRUE(ShapeLike::touches(v, input.transformedShape())); + ASSERT_TRUE(shapelike::touches(v, input.transformedShape())); } } @@ -821,17 +821,17 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) { rect2.translate({10, 0}); rect3.translate({25, 0}); - ShapeLike::Shapes pile; + shapelike::Shapes pile; pile.push_back(rect1.transformedShape()); pile.push_back(rect2.transformedShape()); - auto result = Nfp::merge(pile, rect3.transformedShape()); + auto result = nfp::merge(pile, rect3.transformedShape()); ASSERT_EQ(result.size(), 1); Rectangle ref(45, 15); - ASSERT_EQ(ShapeLike::area(result.front()), ref.area()); + ASSERT_EQ(shapelike::area(result.front()), ref.area()); } int main(int argc, char **argv) { diff --git a/xs/src/libnest2d/tools/svgtools.hpp b/xs/src/libnest2d/tools/svgtools.hpp index 3a83caa707..776dd5a1a8 100644 --- a/xs/src/libnest2d/tools/svgtools.hpp +++ b/xs/src/libnest2d/tools/svgtools.hpp @@ -56,14 +56,14 @@ public: auto d = static_cast( std::round(conf_.height*conf_.mm_in_coord_units) ); - auto& contour = ShapeLike::getContour(tsh); + auto& contour = shapelike::getContour(tsh); for(auto& v : contour) setY(v, -getY(v) + d); - auto& holes = ShapeLike::holes(tsh); + auto& holes = shapelike::holes(tsh); for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d); } - currentLayer() += ShapeLike::serialize(tsh, + currentLayer() += shapelike::serialize(tsh, 1.0/conf_.mm_in_coord_units) + "\n"; } diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 79371cdb27..cc4bfff0f6 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -104,8 +104,7 @@ using ItemGroup = std::vector>; std::tuple objfunc(const PointImpl& bincenter, double bin_area, - ShapeLike::Shapes& pile, // The currently arranged pile - double pile_area, + sl::Shapes& pile, // The currently arranged pile const Item &item, double norm, // A norming factor for physical dimensions std::vector& areacache, // pile item areas will be cached @@ -114,8 +113,6 @@ objfunc(const PointImpl& bincenter, const ItemGroup& remaining ) { - using pl = PointLike; - using sl = ShapeLike; using Coord = TCoord; static const double BIG_ITEM_TRESHOLD = 0.02; @@ -150,7 +147,7 @@ objfunc(const PointImpl& bincenter, // Calculate the full bounding box of the pile with the candidate item pile.emplace_back(item.transformedShape()); - auto fullbb = ShapeLike::boundingBox(pile); + auto fullbb = sl::boundingBox(pile); pile.pop_back(); // The bounding box of the big items (they will accumulate in the center @@ -283,21 +280,23 @@ class _ArrBase { protected: using Placer = strategies::_NofitPolyPlacer; using Selector = FirstFitSelection; - using Packer = Arranger; + using Packer = Nester; using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; - using Pile = ShapeLike::Shapes; + using Pile = sl::Shapes; Packer pck_; PConfig pconf_; // Placement configuration double bin_area_; std::vector areacache_; SpatIndex rtree_; + double norm_; public: _ArrBase(const TBin& bin, Distance dist, std::function progressind): - pck_(bin, dist), bin_area_(ShapeLike::area(bin)) + pck_(bin, dist), bin_area_(sl::area(bin)), + norm_(std::sqrt(sl::area(bin))) { fillConfig(pconf_); pck_.progressIndicator(progressind); @@ -306,7 +305,7 @@ public: template inline IndexedPackGroup operator()(Args&&...args) { areacache_.clear(); rtree_.clear(); - return pck_.arrangeIndexed(std::forward(args)...); + return pck_.executeIndexed(std::forward(args)...); } }; @@ -321,21 +320,17 @@ public: pconf_.object_function = [this, bin] ( Pile& pile, const Item &item, - double pile_area, - double norm, const ItemGroup& rem) { auto result = objfunc(bin.center(), bin_area_, pile, - pile_area, item, norm, areacache_, - rtree_, - rem); + item, norm_, areacache_, rtree_, rem); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); auto wdiff = fullbb.width() - bin.width(); auto hdiff = fullbb.height() - bin.height(); - if(wdiff > 0) score += std::pow(wdiff, 2) / norm; - if(hdiff > 0) score += std::pow(hdiff, 2) / norm; + if(wdiff > 0) score += std::pow(wdiff, 2) / norm_; + if(hdiff > 0) score += std::pow(hdiff, 2) / norm_; return score; }; @@ -357,31 +352,28 @@ public: pconf_.object_function = [this, &bin] ( Pile& pile, const Item &item, - double pile_area, - double norm, const ItemGroup& rem) { - auto result = objfunc(bin.center(), bin_area_, pile, - pile_area, item, norm, areacache_, - rtree_, rem); + auto result = objfunc(bin.center(), bin_area_, pile, item, norm_, + areacache_, rtree_, rem); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); - auto d = PointLike::distance(fullbb.minCorner(), + auto d = pl::distance(fullbb.minCorner(), fullbb.maxCorner()); auto diff = d - 2*bin.radius(); if(diff > 0) { if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) { pile.emplace_back(item.transformedShape()); - auto chull = ShapeLike::convexHull(pile); + auto chull = sl::convexHull(pile); pile.pop_back(); auto C = strategies::boundingCircle(chull); auto rdiff = C.radius() - bin.radius(); if(rdiff > 0) { - score += std::pow(rdiff, 3) / norm; + score += std::pow(rdiff, 3) / norm_; } } } @@ -403,14 +395,11 @@ public: pconf_.object_function = [this, &bin] ( Pile& pile, const Item &item, - double pile_area, - double norm, const ItemGroup& rem) { - auto binbb = ShapeLike::boundingBox(bin); - auto result = objfunc(binbb.center(), bin_area_, pile, - pile_area, item, norm, areacache_, - rtree_, rem); + auto binbb = sl::boundingBox(bin); + auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_, + areacache_, rtree_, rem); double score = std::get<0>(result); return score; @@ -430,13 +419,10 @@ public: this->pconf_.object_function = [this] ( Pile& pile, const Item &item, - double pile_area, - double norm, const ItemGroup& rem) { - auto result = objfunc({0, 0}, 0, pile, pile_area, - item, norm, areacache_, - rtree_, rem); + auto result = objfunc({0, 0}, 0, pile, item, norm_, + areacache_, rtree_, rem); return std::get<0>(result); }; @@ -711,7 +697,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, using P = libnest2d::PolygonImpl; auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); - P irrbed = ShapeLike::create(std::move(ctour)); + P irrbed = sl::create(std::move(ctour)); AutoArranger

arrange(irrbed, min_obj_distance, progressind); From 8617b0a409ba0cbb682c5cd0078fa23be66ab440 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Aug 2018 16:34:35 +0200 Subject: [PATCH 09/50] parallel nesting can be enabled but fails with the current objectfunction. --- xs/src/libnest2d/CMakeLists.txt | 2 + xs/src/libnest2d/examples/main.cpp | 59 +- xs/src/libnest2d/libnest2d/boost_alg.hpp | 51 +- .../clipper_backend/clipper_backend.hpp | 71 +- .../libnest2d/libnest2d/geometry_traits.hpp | 94 +- .../libnest2d/geometry_traits_nfp.hpp | 20 +- xs/src/libnest2d/libnest2d/libnest2d.hpp | 53 +- xs/src/libnest2d/libnest2d/metaloop.hpp | 8 +- xs/src/libnest2d/libnest2d/optimizer.hpp | 3 + .../optimizers/nlopt_boilerplate.hpp | 6 +- .../libnest2d/placers/bottomleftplacer.hpp | 28 +- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 730 +++++++----- .../libnest2d/placers/placer_boilerplate.hpp | 15 +- .../libnest2d/selections/djd_heuristic.hpp | 9 +- .../libnest2d/selections/firstfit.hpp | 9 +- xs/src/libnest2d/tests/test.cpp | 9 +- xs/src/libnest2d/tools/libnfpglue.cpp | 16 +- xs/src/libnest2d/tools/libnfpglue.hpp | 10 +- xs/src/libnest2d/tools/nfp_svgnest.hpp | 1004 +++++++++++++++++ xs/src/libnest2d/tools/nfp_svgnest_glue.hpp | 77 ++ xs/src/libslic3r/ModelArrange.hpp | 49 +- 21 files changed, 1833 insertions(+), 490 deletions(-) create mode 100644 xs/src/libnest2d/tools/nfp_svgnest.hpp create mode 100644 xs/src/libnest2d/tools/nfp_svgnest_glue.hpp diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt index 0a181f4ab8..cd3e35b978 100644 --- a/xs/src/libnest2d/CMakeLists.txt +++ b/xs/src/libnest2d/CMakeLists.txt @@ -93,6 +93,8 @@ if(LIBNEST2D_BUILD_EXAMPLES) add_executable(example examples/main.cpp # tools/libnfpglue.hpp # tools/libnfpglue.cpp + tools/nfp_svgnest.hpp + tools/nfp_svgnest_glue.hpp tools/svgtools.hpp tests/printer_parts.cpp tests/printer_parts.h diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp index 37096019d3..57be7a2080 100644 --- a/xs/src/libnest2d/examples/main.cpp +++ b/xs/src/libnest2d/examples/main.cpp @@ -1,7 +1,6 @@ #include #include #include - //#define DEBUG_EXPORT_NFP #include @@ -12,6 +11,8 @@ #include "libnest2d/rotfinder.hpp" //#include "tools/libnfpglue.hpp" +//#include "tools/nfp_svgnest_glue.hpp" + using namespace libnest2d; using ItemGroup = std::vector>; @@ -53,10 +54,50 @@ void arrangeRectangles() { const int SCALE = 1000000; - std::vector rects = { - {60*SCALE, 200*SCALE}, - {60*SCALE, 200*SCALE} - }; + std::vector rects(100, { + {-9945219, -3065619}, + {-9781479, -2031780}, + {-9510560, -1020730}, + {-9135450, -43529}, + {-2099999, 14110899}, + {2099999, 14110899}, + {9135450, -43529}, + {9510560, -1020730}, + {9781479, -2031780}, + {9945219, -3065619}, + {10000000, -4110899}, + {9945219, -5156179}, + {9781479, -6190019}, + {9510560, -7201069}, + {9135450, -8178270}, + {8660249, -9110899}, + {8090169, -9988750}, + {7431449, -10802209}, + {6691309, -11542349}, + {5877850, -12201069}, + {5000000, -12771149}, + {4067369, -13246350}, + {3090169, -13621459}, + {2079119, -13892379}, + {1045279, -14056119}, + {0, -14110899}, + {-1045279, -14056119}, + {-2079119, -13892379}, + {-3090169, -13621459}, + {-4067369, -13246350}, + {-5000000, -12771149}, + {-5877850, -12201069}, + {-6691309, -11542349}, + {-7431449, -10802209}, + {-8090169, -9988750}, + {-8660249, -9110899}, + {-9135450, -8178270}, + {-9510560, -7201069}, + {-9781479, -6190019}, + {-9945219, -5156179}, + {-10000000, -4110899}, + {-9945219, -3065619}, + }); std::vector input; input.insert(input.end(), prusaParts().begin(), prusaParts().end()); @@ -84,7 +125,7 @@ void arrangeRectangles() { // _Circle bin({0, 0}, 125*SCALE); - auto min_obj_distance = static_cast(0*SCALE); + auto min_obj_distance = static_cast(1.5*SCALE); using Placer = strategies::_NofitPolyPlacer; using Packer = Nester; @@ -95,14 +136,15 @@ void arrangeRectangles() { pconf.alignment = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; - pconf.accuracy = 1.0f; + pconf.accuracy = 0.5f; + pconf.parallel = true; Packer::SelectionConfig sconf; // sconf.allow_parallel = false; // sconf.force_parallel = false; // sconf.try_triplets = true; // sconf.try_reverse_order = true; -// sconf.waste_increment = 0.005; +// sconf.waste_increment = 0.01; arrange.configure(pconf, sconf); @@ -175,7 +217,6 @@ void arrangeRectangles() { SVGWriter svgw(conf); svgw.setSize(Box(250*SCALE, 210*SCALE)); svgw.writePackGroup(result); -// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);}); svgw.save("out"); } diff --git a/xs/src/libnest2d/libnest2d/boost_alg.hpp b/xs/src/libnest2d/libnest2d/boost_alg.hpp index 7da1036f01..bb0403b066 100644 --- a/xs/src/libnest2d/libnest2d/boost_alg.hpp +++ b/xs/src/libnest2d/libnest2d/boost_alg.hpp @@ -356,8 +356,7 @@ inline double area(const PolygonImpl& shape, const PolygonTag&) #endif template<> -inline bool isInside(const PointImpl& point, - const PolygonImpl& shape) +inline bool isInside(const PointImpl& point, const PolygonImpl& shape) { return boost::geometry::within(point, shape); } @@ -390,7 +389,8 @@ inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&) } template<> -inline bp2d::Box boundingBox(const bp2d::Shapes& shapes) +inline bp2d::Box boundingBox(const bp2d::Shapes& shapes, + const MultiPolygonTag&) { bp2d::Box b; boost::geometry::envelope(shapes, b); @@ -400,7 +400,7 @@ inline bp2d::Box boundingBox(const bp2d::Shapes& shapes) #ifndef DISABLE_BOOST_CONVEX_HULL template<> -inline PolygonImpl convexHull(const PolygonImpl& sh) +inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&) { PolygonImpl ret; boost::geometry::convex_hull(sh, ret); @@ -408,7 +408,8 @@ inline PolygonImpl convexHull(const PolygonImpl& sh) } template<> -inline PolygonImpl convexHull(const bp2d::Shapes& shapes) +inline PolygonImpl convexHull(const TMultiShape& shapes, + const MultiPolygonTag&) { PolygonImpl ret; boost::geometry::convex_hull(shapes, ret); @@ -416,34 +417,6 @@ inline PolygonImpl convexHull(const bp2d::Shapes& shapes) } #endif -#ifndef DISABLE_BOOST_ROTATE -template<> -inline void rotate(PolygonImpl& sh, const Radians& rads) -{ - namespace trans = boost::geometry::strategy::transform; - - PolygonImpl cpy = sh; - trans::rotate_transformer - rotate(rads); - - boost::geometry::transform(cpy, sh, rotate); -} -#endif - -#ifndef DISABLE_BOOST_TRANSLATE -template<> -inline void translate(PolygonImpl& sh, const PointImpl& offs) -{ - namespace trans = boost::geometry::strategy::transform; - - PolygonImpl cpy = sh; - trans::translate_transformer translate( - bp2d::getX(offs), bp2d::getY(offs)); - - boost::geometry::transform(cpy, sh, translate); -} -#endif - #ifndef DISABLE_BOOST_OFFSET template<> inline void offset(PolygonImpl& sh, bp2d::Coord distance) @@ -515,14 +488,24 @@ template<> inline std::pair isValid(const PolygonImpl& sh) namespace nfp { #ifndef DISABLE_BOOST_NFP_MERGE + +// Warning: I could not get boost union_ to work. Geometries will overlap. template<> -inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes, +inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes, const PolygonImpl& sh) { bp2d::Shapes retv; boost::geometry::union_(shapes, sh, retv); return retv; } + +template<> +inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes) +{ + bp2d::Shapes retv; + boost::geometry::union_(shapes, shapes.back(), retv); + return retv; +} #endif } diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp index 4238212ad2..745fd2108d 100644 --- a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp +++ b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp @@ -21,9 +21,6 @@ struct PolygonImpl { PathImpl Contour; HoleStore Holes; - using Tag = libnest2d::PolygonTag; - using PointType = PointImpl; - inline PolygonImpl() = default; inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {} @@ -102,41 +99,49 @@ template<> struct PointType { using Type = PointImpl; }; -// Type of vertex iterator used by Clipper -template<> struct VertexIteratorType { - using Type = ClipperLib::Path::iterator; -}; - -// Type of vertex iterator used by Clipper -template<> struct VertexConstIteratorType { - using Type = ClipperLib::Path::const_iterator; +template<> struct PointType { + using Type = PointImpl; }; template<> struct CountourType { using Type = PathImpl; }; +template<> struct ShapeTag { using Type = PolygonTag; }; + +template<> struct ShapeTag> { + using Type = MultiPolygonTag; +}; + +template<> struct PointType> { + using Type = PointImpl; +}; + +template<> struct HolesContainer { + using Type = ClipperLib::Paths; +}; + namespace pointlike { -// Tell binpack2d how to extract the X coord from a ClipperPoint object +// Tell libnest2d how to extract the X coord from a ClipperPoint object template<> inline TCoord x(const PointImpl& p) { return p.X; } -// Tell binpack2d how to extract the Y coord from a ClipperPoint object +// Tell libnest2d how to extract the Y coord from a ClipperPoint object template<> inline TCoord y(const PointImpl& p) { return p.Y; } -// Tell binpack2d how to extract the X coord from a ClipperPoint object +// Tell libnest2d how to extract the X coord from a ClipperPoint object template<> inline TCoord& x(PointImpl& p) { return p.X; } -// Tell binpack2d how to extract the Y coord from a ClipperPoint object +// Tell libnest2d how to extract the Y coord from a ClipperPoint object template<> inline TCoord& y(PointImpl& p) { return p.Y; @@ -178,10 +183,6 @@ inline double area(const PolygonImpl& sh) { } -template<> struct HolesContainer { - using Type = ClipperLib::Paths; -}; - namespace shapelike { template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity) @@ -189,7 +190,7 @@ template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity) return sh.Contour.reserve(vertex_capacity); } -// Tell binpack2d how to make string out of a ClipperPolygon object +// Tell libnest2d how to make string out of a ClipperPolygon object template<> inline double area(const PolygonImpl& sh, const PolygonTag&) { return _smartarea::area::Value>(sh); @@ -269,28 +270,6 @@ template<> inline std::string toString(const PolygonImpl& sh) return ss.str(); } -template<> inline TVertexIterator begin(PolygonImpl& sh) -{ - return sh.Contour.begin(); -} - -template<> inline TVertexIterator end(PolygonImpl& sh) -{ - return sh.Contour.end(); -} - -template<> -inline TVertexConstIterator cbegin(const PolygonImpl& sh) -{ - return sh.Contour.cbegin(); -} - -template<> inline TVertexConstIterator cend( - const PolygonImpl& sh) -{ - return sh.Contour.cend(); -} - template<> inline PolygonImpl create(const PathImpl& path, const HoleStore& holes) { @@ -410,8 +389,8 @@ inline void rotate(PolygonImpl& sh, const Radians& rads) } // namespace shapelike #define DISABLE_BOOST_NFP_MERGE -inline nfp::Shapes _merge(ClipperLib::Clipper& clipper) { - nfp::Shapes retv; +inline std::vector _merge(ClipperLib::Clipper& clipper) { + shapelike::Shapes retv; ClipperLib::PolyTree result; clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative); @@ -447,8 +426,8 @@ inline nfp::Shapes _merge(ClipperLib::Clipper& clipper) { namespace nfp { -template<> inline nfp::Shapes -merge(const nfp::Shapes& shapes) +template<> inline std::vector +merge(const std::vector& shapes) { ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution); diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp index 0826cbd4bc..d162577315 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp @@ -22,34 +22,12 @@ template using TCoord = typename CoordType>::Type; /// Getting the type of point structure used by a shape. -template struct PointType { /*using Type = void;*/ }; +template struct PointType { using Type = typename Sh::PointType; }; /// TPoint as shorthand for `typename PointType::Type`. template using TPoint = typename PointType>::Type; -/// Getting the VertexIterator type of a shape class. -template struct VertexIteratorType { /*using Type = void;*/ }; - -/// Getting the const vertex iterator for a shape class. -template struct VertexConstIteratorType {/* using Type = void;*/ }; - -/** - * TVertexIterator as shorthand for - * `typename VertexIteratorType::Type` - */ -template -using TVertexIterator = -typename VertexIteratorType>::Type; - -/** - * \brief TVertexConstIterator as shorthand for - * `typename VertexConstIteratorType::Type` - */ -template -using TVertexConstIterator = -typename VertexConstIteratorType>::Type; - /** * \brief A point pair base class for other point pairs (segment, box, ...). * \tparam RawPoint The actual point type to use. @@ -61,9 +39,16 @@ struct PointPair { }; struct PolygonTag {}; +struct MultiPolygonTag {}; struct BoxTag {}; struct CircleTag {}; +template struct ShapeTag { using Type = typename Shape::Tag; }; +template using Tag = typename ShapeTag::Type; + +template struct MultiShape { using Type = std::vector; }; +template using TMultiShape = typename MultiShape::Type; + /** * \brief An abstraction of a box; */ @@ -371,7 +356,7 @@ enum class Formats { namespace shapelike { template - using Shapes = std::vector; + using Shapes = TMultiShape; template inline RawShape create(const TContour& contour, @@ -455,27 +440,28 @@ namespace shapelike { } template - inline TVertexIterator begin(RawShape& sh) + inline typename TContour::iterator begin(RawShape& sh) { - return sh.begin(); + return getContour(sh).begin(); } template - inline TVertexIterator end(RawShape& sh) + inline typename TContour::iterator end(RawShape& sh) { - return sh.end(); + return getContour(sh).end(); } template - inline TVertexConstIterator cbegin(const RawShape& sh) + inline typename TContour::const_iterator + cbegin(const RawShape& sh) { - return sh.cbegin(); + return getContour(sh).cbegin(); } template - inline TVertexConstIterator cend(const RawShape& sh) + inline typename TContour::const_iterator cend(const RawShape& sh) { - return sh.cend(); + return getContour(sh).cend(); } template @@ -559,27 +545,29 @@ namespace shapelike { "ShapeLike::boundingBox(shape) unimplemented!"); } - template - inline _Box> boundingBox(const Shapes& /*sh*/) + template + inline _Box> + boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) { - static_assert(always_false::value, + static_assert(always_false::value, "ShapeLike::boundingBox(shapes) unimplemented!"); } template - inline RawShape convexHull(const RawShape& /*sh*/) + inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&) { static_assert(always_false::value, "ShapeLike::convexHull(shape) unimplemented!"); return RawShape(); } - template - inline RawShape convexHull(const Shapes& /*sh*/) + template + inline typename RawShapes::value_type + convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&) { - static_assert(always_false::value, + static_assert(always_false::value, "ShapeLike::convexHull(shapes) unimplemented!"); - return RawShape(); + return typename RawShapes::value_type(); } template @@ -599,8 +587,7 @@ namespace shapelike { template inline void offset(RawShape& /*sh*/, TCoord> /*distance*/) { - static_assert(always_false::value, - "ShapeLike::offset() unimplemented!"); + dout() << "The current geometry backend does not support offsetting!\n"; } template @@ -670,9 +657,9 @@ namespace shapelike { } template // Dispatch function - inline _Box boundingBox(const S& sh) + inline _Box> boundingBox(const S& sh) { - return boundingBox(sh, typename S::Tag()); + return boundingBox(sh, Tag() ); } template @@ -690,7 +677,7 @@ namespace shapelike { template // Dispatching function inline double area(const RawShape& sh) { - return area(sh, typename RawShape::Tag()); + return area(sh, Tag()); } template @@ -702,6 +689,13 @@ namespace shapelike { }); } + template + inline auto convexHull(const RawShape& sh) + -> decltype(convexHull(sh, Tag())) // TODO: C++14 could deduce + { + return convexHull(sh, Tag()); + } + template inline bool isInside(const TPoint& point, const _Circle>& circ) @@ -816,6 +810,16 @@ namespace shapelike { } } +#define DECLARE_MAIN_TYPES(T) \ + using Polygon = T; \ + using Point = TPoint; \ + using Coord = TCoord; \ + using Contour = TContour; \ + using Box = _Box; \ + using Circle = _Circle; \ + using Segment = _Segment; \ + using Polygons = TMultiShape + } #endif // GEOMETRY_TRAITS_HPP diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp index 6cac374aef..b9dfd21854 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp @@ -27,8 +27,8 @@ inline bool _vsort(const TPoint& v1, const TPoint& v2) /// A collection of static methods for handling the no fit polygon creation. namespace nfp { -namespace sl = shapelike; -namespace pl = pointlike; +//namespace sl = shapelike; +//namespace pl = pointlike; /// The complexity level of a polygon that an NFP implementation can handle. enum class NfpLevel: unsigned { @@ -49,7 +49,7 @@ template struct MaxNfpLevel { // Shorthand for a pile of polygons template -using Shapes = typename shapelike::Shapes; +using Shapes = TMultiShape; /** * Merge a bunch of polygons with the specified additional polygon. @@ -62,10 +62,10 @@ using Shapes = typename shapelike::Shapes; * mostly it will be a set containing only one big polygon but if the input * polygons are disjuct than the resulting set will contain more polygons. */ -template -inline Shapes merge(const Shapes& /*shc*/) +template +inline RawShapes merge(const RawShapes& /*shc*/) { - static_assert(always_false::value, + static_assert(always_false::value, "Nfp::merge(shapes, shape) unimplemented!"); } @@ -81,12 +81,12 @@ inline Shapes merge(const Shapes& /*shc*/) * polygons are disjuct than the resulting set will contain more polygons. */ template -inline Shapes merge(const Shapes& shc, - const RawShape& sh) +inline TMultiShape merge(const TMultiShape& shc, + const RawShape& sh) { - auto m = merge(shc); + auto m = nfp::merge(shc); m.push_back(sh); - return merge(m); + return nfp::merge(m); } /** diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index d2850d4eda..42255cbb48 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -31,6 +31,8 @@ class _Item { using Vertex = TPoint; using Box = _Box; + using VertexConstIterator = typename TContour::const_iterator; + // The original shape that gets encapsulated. RawShape sh_; @@ -39,7 +41,7 @@ class _Item { Radians rotation_; Coord offset_distance_; - // Info about whether the tranformations will have to take place + // 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; @@ -59,8 +61,8 @@ class _Item { }; mutable Convexity convexity_ = Convexity::UNCHECKED; - mutable TVertexConstIterator rmt_; // rightmost top vertex - mutable TVertexConstIterator lmb_; // leftmost bottom vertex + mutable VertexConstIterator rmt_; // rightmost top vertex + mutable VertexConstIterator lmb_; // leftmost bottom vertex mutable bool rmt_valid_ = false, lmb_valid_ = false; mutable struct BBCache { Box bb; bool valid; Vertex tr; @@ -81,7 +83,7 @@ public: * supports. Giving out a non const iterator would make it impossible to * perform correct cache invalidation. */ - using Iterator = TVertexConstIterator; + using Iterator = VertexConstIterator; /** * @brief Get the orientation of the polygon. @@ -110,7 +112,7 @@ public: explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {} /** - * @brief Create an item from an initilizer list. + * @brief Create an item from an initializer list. * @param il The initializer list of vertices. */ inline _Item(const std::initializer_list< Vertex >& il): @@ -160,7 +162,7 @@ public: } /** - * @brief Get a copy of an outer vertex whithin the carried shape. + * @brief Get a copy of an outer vertex within the carried shape. * * Note that the vertex considered here is taken from the original shape * that this item is constructed from. This means that no transformation is @@ -245,7 +247,7 @@ public: * @param p * @return */ - inline bool isPointInside(const Vertex& p) const + inline bool isInside(const Vertex& p) const { return sl::isInside(p, transformedShape()); } @@ -505,7 +507,7 @@ rem(typename Container::const_iterator it, const Container& cont) { /** * \brief A wrapper interface (trait) class for any placement strategy provider. * - * If a client want's to use its own placement algorithm, all it has to do is to + * If a client wants to use its own placement algorithm, all it has to do is to * specialize this class template and define all the ten methods it has. It can * use the strategies::PlacerBoilerplace class for creating a new placement * strategy where only the constructor and the trypack method has to be provided @@ -558,7 +560,7 @@ public: * Note that it depends on the particular placer implementation how it * reacts to config changes in the middle of a calculation. * - * @param config The configuration object defined by the placement startegy. + * @param config The configuration object defined by the placement strategy. */ inline void configure(const Config& config) { impl_.configure(config); } @@ -568,7 +570,7 @@ public: * * \param item_store A container of items that are intended to be packed * later. Can be used by the placer to switch tactics. When it's knows that - * many items will come a greedy startegy may not be the best. + * many items will come a greedy strategy may not be the best. * \param from The iterator to the item from which the packing should start, * including the pointed item * \param count How many items should be packed. If the value is 1, than @@ -596,7 +598,7 @@ public: * A default implementation would be to call * { auto&& r = trypack(...); accept(r); return r; } but we should let the * implementor of the placement strategy to harvest any optimizations from - * the absence of an intermadiate step. The above version can still be used + * the absence of an intermediate step. The above version can still be used * in the implementation. * * @param item The item to pack. @@ -628,13 +630,6 @@ public: inline double filledArea() const { return impl_.filledArea(); } -#ifndef NDEBUG - inline auto getDebugItems() -> decltype(impl_.debug_items_)& - { - return impl_.debug_items_; - } -#endif - }; // The progress function will be called with the number of placed items @@ -659,15 +654,15 @@ public: * Note that it depends on the particular placer implementation how it * reacts to config changes in the middle of a calculation. * - * @param config The configuration object defined by the selection startegy. + * @param config The configuration object defined by the selection strategy. */ inline void configure(const Config& config) { impl_.configure(config); } /** - * @brief A function callback which should be called whenewer an item or - * a group of items where succesfully packed. + * @brief A function callback which should be called whenever an item or + * a group of items where successfully packed. * @param fn A function callback object taking one unsigned integer as the * number of the remaining items to pack. */ @@ -680,7 +675,7 @@ public: * placer compatible with the PlacementStrategyLike interface. * * \param first, last The first and last iterator if the input sequence. It - * can be only an iterator of a type converitible to Item. + * can be only an iterator of a type convertible to Item. * \param bin. The shape of the bin. It has to be supported by the placement * strategy. * \param An optional config object for the placer. @@ -712,7 +707,7 @@ public: /** * @brief Get the items for a particular bin. * @param binIndex The index of the requested bin. - * @return Returns a list of allitems packed into the requested bin. + * @return Returns a list of all items packed into the requested bin. */ inline ItemGroup itemsForBin(size_t binIndex) { return impl_.itemsForBin(binIndex); @@ -754,7 +749,7 @@ using _IndexedPackGroup = std::vector< >; /** - * The Arranger is the frontend class for the binpack2d library. It takes the + * 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. */ @@ -857,7 +852,7 @@ public: return _execute(from, to); } - /// Set a progress indicatior function object for the selector. + /// Set a progress indicator function object for the selector. inline Nester& progressIndicator(ProgressFunction func) { selector_.progressIndicator(func); return *this; @@ -877,8 +872,8 @@ private: template, - // This funtion will be used only if the iterators are pointing to - // a type compatible with the binpack2d::_Item 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> @@ -904,8 +899,8 @@ private: template, - // This funtion will be used only if the iterators are pointing to - // a type compatible with the binpack2d::_Item 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> diff --git a/xs/src/libnest2d/libnest2d/metaloop.hpp b/xs/src/libnest2d/libnest2d/metaloop.hpp index 18755525c3..d88988ba15 100644 --- a/xs/src/libnest2d/libnest2d/metaloop.hpp +++ b/xs/src/libnest2d/libnest2d/metaloop.hpp @@ -67,11 +67,11 @@ class metaloop { // need to wrap that in a type (see metaloop::Int). /* - * A helper alias to create integer values wrapped as a type. It is nessecary + * A helper alias to create integer values wrapped as a type. It is necessary * because a non type template parameter (such as int) would be prohibited in * a partial specialization. Also for the same reason we have to use a class * _Metaloop instead of a simple function as a functor. A function cannot be - * partially specialized in a way that is neccesary for this trick. + * partially specialized in a way that is necessary for this trick. */ template using Int = std::integral_constant; @@ -88,7 +88,7 @@ public: // It takes the real functor that can be specified in-place but only // with C++14 because the second parameter's type will depend on the // type of the parameter pack element that is processed. In C++14 we can - // specify this second parameter type as auto in the lamda parameter list. + // specify this second parameter type as auto in the lambda parameter list. inline MapFn(Fn&& fn): fn_(forward(fn)) {} template void operator ()(T&& pack_element) { @@ -146,7 +146,7 @@ public: * version of run is called which does not call itself anymore. * * If you are utterly annoyed, at least you have learned a super crazy - * functional metaprogramming pattern. + * functional meta-programming pattern. */ template using MetaLoop = _MetaLoop, Args...>; diff --git a/xs/src/libnest2d/libnest2d/optimizer.hpp b/xs/src/libnest2d/libnest2d/optimizer.hpp index c8ed2e378a..90d2f2ff99 100644 --- a/xs/src/libnest2d/libnest2d/optimizer.hpp +++ b/xs/src/libnest2d/libnest2d/optimizer.hpp @@ -102,6 +102,9 @@ struct StopCriteria { /// If the relative value difference between two scores. double relative_score_difference = std::nan(""); + /// Stop if this value or better is found. + double stop_score = std::nan(""); + unsigned max_iterations = 0; }; diff --git a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp b/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp index 737ca6e3c0..1a0f06e028 100644 --- a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp @@ -142,10 +142,12 @@ protected: default: ; } - auto abs_diff = stopcr_.absolute_score_difference; - auto rel_diff = stopcr_.relative_score_difference; + double abs_diff = stopcr_.absolute_score_difference; + double rel_diff = stopcr_.relative_score_difference; + double stopval = stopcr_.stop_score; if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff); if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff); + if(!std::isnan(stopval)) opt_.set_stopval(stopval); if(this->stopcr_.max_iterations > 0) opt_.set_maxeval(this->stopcr_.max_iterations ); diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp index af16783721..0ba9eb3c06 100644 --- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp @@ -7,9 +7,24 @@ namespace libnest2d { namespace strategies { +template struct Epsilon {}; + +template +struct Epsilon::value, T> > { + static const T Value = 1; +}; + +template +struct Epsilon::value, T> > { + static const T Value = 1e-3; +}; + template struct BLConfig { - TCoord> min_obj_distance = 0; + DECLARE_MAIN_TYPES(RawShape); + + Coord min_obj_distance = 0; + Coord epsilon = Epsilon::Value; bool allow_rotations = false; }; @@ -69,20 +84,21 @@ protected: setInitialPosition(item); Unit d = availableSpaceDown(item); - bool can_move = d > 1 /*std::numeric_limits::epsilon()*/; + auto eps = config_.epsilon; + bool can_move = d > eps; bool can_be_packed = can_move; bool left = true; while(can_move) { if(left) { // write previous down move and go down - item.translate({0, -d+1}); + item.translate({0, -d+eps}); d = availableSpaceLeft(item); - can_move = d > 1/*std::numeric_limits::epsilon()*/; + can_move = d > eps; left = false; } else { // write previous left move and go down - item.translate({-d+1, 0}); + item.translate({-d+eps, 0}); d = availableSpaceDown(item); - can_move = d > 1/*std::numeric_limits::epsilon()*/; + can_move = d > eps; left = true; } } diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index 638d606e0f..b9e0ba8f16 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -2,7 +2,15 @@ #define NOFITPOLY_HPP #include -#include + +// For caching nfps +#include + +// For parallel for +#include +#include +#include +#include #ifndef NDEBUG #include @@ -13,8 +21,84 @@ #include "tools/svgtools.hpp" +namespace libnest2d { -namespace libnest2d { namespace strategies { +namespace __parallel { + +using std::function; +using std::iterator_traits; +template +using TIteratorValue = typename iterator_traits::value_type; + +template +inline void enumerate( + Iterator from, Iterator to, + function, unsigned)> fn, + std::launch policy = std::launch::deferred | std::launch::async) +{ + auto N = to-from; + std::vector> rets(N); + + auto it = from; + for(unsigned b = 0; b < N; b++) { + rets[b] = std::async(policy, fn, *it++, b); + } + + for(unsigned fi = 0; fi < rets.size(); ++fi) rets[fi].wait(); +} + +} + +namespace __itemhash { + +using Key = size_t; + +template +Key hash(const _Item& item) { + using Point = TPoint; + using Segment = _Segment; + + static const int N = 26; + static const int M = N*N - 1; + + std::string ret; + auto& rhs = item.rawShape(); + auto& ctr = sl::getContour(rhs); + auto it = ctr.begin(); + auto nx = std::next(it); + + double circ = 0; + while(nx != ctr.end()) { + Segment seg(*it++, *nx++); + Radians a = seg.angleToXaxis(); + double deg = Degrees(a); + int ms = 'A', ls = 'A'; + while(deg > N) { ms++; deg -= N; } + ls += int(deg); + ret.push_back(char(ms)); ret.push_back(char(ls)); + circ += seg.length(); + } + + it = ctr.begin(); nx = std::next(it); + + while(nx != ctr.end()) { + Segment seg(*it++, *nx++); + auto l = int(M * seg.length() / circ); + int ms = 'A', ls = 'A'; + while(l > N) { ms++; l -= N; } + ls += l; + ret.push_back(char(ms)); ret.push_back(char(ls)); + } + + return std::hash()(ret); +} + +template +using Hash = std::unordered_map>; + +} + +namespace strategies { template struct NfpPConfig { @@ -71,7 +155,7 @@ struct NfpPConfig { * decisions (for you or a more intelligent AI). * */ - std::function&, const _Item&, + std::function&, const _Item&, const ItemGroup&)> object_function; @@ -80,7 +164,7 @@ struct NfpPConfig { * This is a compromise slider between quality and speed. Zero is the * fast and poor solution while 1.0 is the slowest but most accurate. */ - float accuracy = 1.0; + float accuracy = 0.65f; /** * @brief If you want to see items inside other item's holes, you have to @@ -91,6 +175,11 @@ struct NfpPConfig { */ bool explore_holes = false; + /** + * @brief If true, use all CPUs available. Run on a single core otherwise. + */ + bool parallel = true; + NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}), alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {} }; @@ -325,121 +414,8 @@ inline void correctNfpPosition(nfp::NfpResult& nfp, shapelike::translate(nfp.first, dnfp); } -template -nfp::Shapes calcnfp( const Container& polygons, - const _Item& trsh, - Lvl) -{ - using Item = _Item; - using namespace nfp; - - nfp::Shapes nfps; - -// int pi = 0; - for(Item& sh : polygons) { - auto subnfp_r = noFitPolygon( - sh.transformedShape(), trsh.transformedShape()); - #ifndef NDEBUG - auto vv = sl::isValid(sh.transformedShape()); - assert(vv.first); - - auto vnfp = sl::isValid(subnfp_r.first); - assert(vnfp.first); - #endif - - correctNfpPosition(subnfp_r, sh, trsh); - - nfps = nfp::merge(nfps, subnfp_r.first); - -// double SCALE = 1000000; -// using SVGWriter = svg::SVGWriter; -// SVGWriter::Config conf; -// conf.mm_in_coord_units = SCALE; -// SVGWriter svgw(conf); -// Box bin(250*SCALE, 210*SCALE); -// svgw.setSize(bin); -// for(int i = 0; i <= pi; i++) svgw.writeItem(polygons[i]); -// svgw.writeItem(trsh); -//// svgw.writeItem(Item(subnfp_r.first)); -// for(auto& n : nfps) svgw.writeItem(Item(n)); -// svgw.save("nfpout"); -// pi++; - } - - return nfps; -} - -template -nfp::Shapes calcnfp( const Container& polygons, - const _Item& trsh, - Level) -{ - using namespace nfp; - using Item = _Item; - - Shapes nfps; - - auto& orb = trsh.transformedShape(); - bool orbconvex = trsh.isContourConvex(); - - for(Item& sh : polygons) { - nfp::NfpResult subnfp; - auto& stat = sh.transformedShape(); - - if(sh.isContourConvex() && orbconvex) - subnfp = nfp::noFitPolygon(stat, orb); - else if(orbconvex) - subnfp = nfp::noFitPolygon(stat, orb); - else - subnfp = nfp::noFitPolygon(stat, orb); - - correctNfpPosition(subnfp, sh, trsh); - - nfps = nfp::merge(nfps, subnfp.first); - } - - return nfps; - - -// using Item = _Item; -// using sl = ShapeLike; - -// Nfp::Shapes nfps, stationary; - -// for(Item& sh : polygons) { -// stationary = Nfp::merge(stationary, sh.transformedShape()); -// } - -// for(RawShape& sh : stationary) { - -//// auto vv = sl::isValid(sh); -//// std::cout << vv.second << std::endl; - - -// Nfp::NfpResult subnfp; -// bool shconvex = sl::isConvex(sl::getContour(sh)); -// if(shconvex && trsh.isContourConvex()) { -// subnfp = Nfp::noFitPolygon( -// sh, trsh.transformedShape()); -// } else if(trsh.isContourConvex()) { -// subnfp = Nfp::noFitPolygon( -// sh, trsh.transformedShape()); -// } -// else { -// subnfp = Nfp::noFitPolygon( sh, -// trsh.transformedShape()); -// } - -// correctNfpPosition(subnfp, sh, trsh); - -// nfps = Nfp::merge(nfps, subnfp.first); -// } - -// return nfps; -} - -template -_Circle> minimizeCircle(const RawShape& sh) { +template> > +Circle minimizeCircle(const RawShape& sh) { using Point = TPoint; using Coord = TCoord; @@ -507,9 +483,19 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer>; + using MaxNfpLevel = nfp::MaxNfpLevel; + + using ItemKeys = std::vector<__itemhash::Key>; + + // Norming factor for the optimization function const double norm_; - using MaxNfpLevel = nfp::MaxNfpLevel; + // Caching calculated nfps + __itemhash::Hash nfpcache_; + + // Storing item hash keys + ItemKeys item_keys_; + public: using Pile = nfp::Shapes; @@ -526,60 +512,290 @@ public: _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default; #endif - bool static inline wouldFit(const Box& bb, const RawShape& bin) { - auto bbin = sl::boundingBox(bin); + static inline double overfit(const Box& bb, const RawShape& bin) { + auto bbin = sl::boundingBox(bin); auto d = bbin.center() - bb.center(); _Rectangle rect(bb.width(), bb.height()); rect.translate(bb.minCorner() + d); - return sl::isInside(rect.transformedShape(), bin); + return sl::isInside(rect.transformedShape(), bin) ? -1.0 : 1; } - bool static inline wouldFit(const RawShape& chull, const RawShape& bin) { - auto bbch = sl::boundingBox(chull); - auto bbin = sl::boundingBox(bin); + static inline double overfit(const RawShape& chull, const RawShape& bin) { + auto bbch = sl::boundingBox(chull); + auto bbin = sl::boundingBox(bin); auto d = bbch.center() - bbin.center(); auto chullcpy = chull; sl::translate(chullcpy, d); - return sl::isInside(chullcpy, bin); + return sl::isInside(chullcpy, bin) ? -1.0 : 1.0; } - bool static inline wouldFit(const RawShape& chull, const Box& bin) + static inline double overfit(const RawShape& chull, const Box& bin) { - auto bbch = sl::boundingBox(chull); - return wouldFit(bbch, bin); + auto bbch = sl::boundingBox(chull); + return overfit(bbch, bin); } - bool static inline wouldFit(const Box& bb, const Box& bin) + static inline double overfit(const Box& bb, const Box& bin) { - return bb.width() <= bin.width() && bb.height() <= bin.height(); + auto wdiff = double(bb.width() - bin.width()); + auto hdiff = double(bb.height() - bin.height()); + double diff = 0; + if(wdiff > 0) diff += wdiff; + if(hdiff > 0) diff += hdiff; + return diff; } - bool static inline wouldFit(const Box& bb, const _Circle& bin) + static inline double overfit(const Box& bb, const _Circle& bin) { - - return sl::isInside(bb, bin); + double boxr = 0.5*pl::distance(bb.minCorner(), bb.maxCorner()); + double diff = boxr - bin.radius(); + return diff; } - bool static inline wouldFit(const RawShape& chull, + static inline double overfit(const RawShape& chull, const _Circle& bin) { - return boundingCircle(chull).radius() < bin.radius(); + double r = boundingCircle(chull).radius(); + double diff = r - bin.radius(); + return diff; } template> - PackResult trypack( + PackResult trypack(Item& item, + const Range& remaining = Range()) { + auto result = _trypack(item, remaining); + + // Experimental + // if(!result) repack(item, result); + + return result; + } + + ~_NofitPolyPlacer() { + clearItems(); + } + + inline void clearItems() { + finalAlign(bin_); + Base::clearItems(); + } + +private: + + using Shapes = TMultiShape; + using ItemRef = std::reference_wrapper; + using ItemWithHash = const std::pair; + + Shapes calcnfp(const ItemWithHash itsh, Lvl) + { + using namespace nfp; + + Shapes nfps; + const Item& trsh = itsh.first; + // nfps.reserve(polygons.size()); + +// unsigned idx = 0; + for(Item& sh : items_) { + +// auto ik = item_keys_[idx++] + itsh.second; +// auto fnd = nfpcache_.find(ik); + +// nfp::NfpResult subnfp_r; +// if(fnd == nfpcache_.end()) { + + auto subnfp_r = noFitPolygon( + sh.transformedShape(), trsh.transformedShape()); +// nfpcache_[ik] = subnfp_r; +// } else { +// subnfp_r = fnd->second; +// } + + correctNfpPosition(subnfp_r, sh, trsh); + + // nfps.emplace_back(subnfp_r.first); + nfps = nfp::merge(nfps, subnfp_r.first); + } + + // nfps = nfp::merge(nfps); + + return nfps; + } + + template + Shapes calcnfp( const ItemWithHash itsh, Level) + { // Function for arbitrary level of nfp implementation + using namespace nfp; + + Shapes nfps; + const Item& trsh = itsh.first; + + auto& orb = trsh.transformedShape(); + bool orbconvex = trsh.isContourConvex(); + + for(Item& sh : items_) { + nfp::NfpResult subnfp; + auto& stat = sh.transformedShape(); + + if(sh.isContourConvex() && orbconvex) + subnfp = nfp::noFitPolygon(stat, orb); + else if(orbconvex) + subnfp = nfp::noFitPolygon(stat, orb); + else + subnfp = nfp::noFitPolygon(stat, orb); + + correctNfpPosition(subnfp, sh, trsh); + + nfps = nfp::merge(nfps, subnfp.first); + } + + return nfps; + } + + // Very much experimental + void repack(Item& item, PackResult& result) { + + if((sl::area(bin_) - this->filledArea()) >= item.area()) { + auto prev_func = config_.object_function; + + unsigned iter = 0; + ItemGroup backup_rf = items_; + std::vector backup_cpy; + for(Item& itm : items_) backup_cpy.emplace_back(itm); + + auto ofn = [this, &item, &result, &iter, &backup_cpy, &backup_rf] + (double ratio) + { + auto& bin = bin_; + iter++; + config_.object_function = [bin, ratio]( + nfp::Shapes& pile, + const Item& item, + const ItemGroup& /*remaining*/) + { + pile.emplace_back(item.transformedShape()); + auto ch = sl::convexHull(pile); + auto pbb = sl::boundingBox(pile); + pile.pop_back(); + + double parea = 0.5*(sl::area(ch) + sl::area(pbb)); + + double pile_area = std::accumulate( + pile.begin(), pile.end(), item.area(), + [](double sum, const RawShape& sh){ + return sum + sl::area(sh); + }); + + // The pack ratio -- how much is the convex hull occupied + double pack_rate = (pile_area)/parea; + + // ratio of waste + double waste = 1.0 - pack_rate; + + // Score is the square root of waste. This will extend the + // range of good (lower) values and shrink the range of bad + // (larger) values. + auto wscore = std::sqrt(waste); + + + auto ibb = item.boundingBox(); + auto bbb = sl::boundingBox(bin); + auto c = ibb.center(); + double norm = 0.5*pl::distance(bbb.minCorner(), + bbb.maxCorner()); + + double dscore = pl::distance(c, pbb.center()) / norm; + + return ratio*wscore + (1.0 - ratio) * dscore; + }; + + auto bb = sl::boundingBox(bin); + double norm = bb.width() + bb.height(); + + auto items = items_; + clearItems(); + auto it = items.begin(); + while(auto pr = _trypack(*it++)) { + this->accept(pr); if(it == items.end()) break; + } + + auto count_diff = items.size() - items_.size(); + double score = count_diff; + + if(count_diff == 0) { + result = _trypack(item); + + if(result) { + std::cout << "Success" << std::endl; + score = 0.0; + } else { + score += result.overfit() / norm; + } + } else { + result = PackResult(); + items_ = backup_rf; + for(unsigned i = 0; i < items_.size(); i++) { + items_[i].get() = backup_cpy[i]; + } + } + + std::cout << iter << " repack result: " << score << " " + << ratio << " " << count_diff << std::endl; + + return score; + }; + + opt::StopCriteria stopcr; + stopcr.max_iterations = 30; + stopcr.stop_score = 1e-20; + opt::TOptimizer solver(stopcr); + solver.optimize_min(ofn, opt::initvals(0.5), + opt::bound(0.0, 1.0)); + + // optimize + config_.object_function = prev_func; + } + + } + + struct Optimum { + double relpos; + unsigned nfpidx; + int hidx; + Optimum(double pos, unsigned nidx): + relpos(pos), nfpidx(nidx), hidx(-1) {} + Optimum(double pos, unsigned nidx, int holeidx): + relpos(pos), nfpidx(nidx), hidx(holeidx) {} + }; + + class Optimizer: public opt::TOptimizer { + public: + Optimizer() { + opt::StopCriteria stopcr; + stopcr.max_iterations = 200; + stopcr.relative_score_difference = 1e-20; + this->stopcr_ = stopcr; + } + }; + + using Edges = EdgeCache; + + template> + PackResult _trypack( Item& item, const Range& remaining = Range()) { PackResult ret; bool can_pack = false; + double best_overfit = std::numeric_limits::max(); auto remlist = ItemGroup(remaining.from, remaining.to); + size_t itemhash = __itemhash::hash(item); if(items_.empty()) { setInitialPosition(item); - can_pack = item.isInside(bin_); + best_overfit = overfit(item.transformedShape(), bin_); + can_pack = best_overfit <= 0; } else { double global_score = std::numeric_limits::max(); @@ -588,7 +804,7 @@ public: auto initial_rot = item.rotation(); Vertex final_tr = {0, 0}; Radians final_rot = initial_rot; - nfp::Shapes nfps; + Shapes nfps; for(auto rot : config_.rotations) { @@ -596,17 +812,16 @@ public: item.rotation(initial_rot + rot); // place the new item outside of the print bed to make sure - // it is disjuct from the current merged pile + // it is disjunct from the current merged pile placeOutsideOfBin(item); - auto trsh = item.transformedShape(); + nfps = calcnfp({item, itemhash}, Lvl()); - nfps = calcnfp(items_, item, Lvl()); - auto iv = nfp::referenceVertex(trsh); + auto iv = item.referenceVertex(); auto startpos = item.translation(); - std::vector> ecache; + std::vector ecache; ecache.reserve(nfps.size()); for(auto& nfp : nfps ) { @@ -614,14 +829,54 @@ public: ecache.back().accuracy(config_.accuracy); } - struct Optimum { - double relpos; - unsigned nfpidx; - int hidx; - Optimum(double pos, unsigned nidx): - relpos(pos), nfpidx(nidx), hidx(-1) {} - Optimum(double pos, unsigned nidx, int holeidx): - relpos(pos), nfpidx(nidx), hidx(holeidx) {} + Shapes pile; + pile.reserve(items_.size()+1); + // double pile_area = 0; + for(Item& mitem : items_) { + pile.emplace_back(mitem.transformedShape()); + // pile_area += mitem.area(); + } + + auto merged_pile = nfp::merge(pile); + auto& bin = bin_; + double norm = norm_; + + // This is the kernel part of the object function that is + // customizable by the library client + auto _objfunc = config_.object_function? + config_.object_function : + [norm, /*pile_area,*/ bin, merged_pile]( + const Pile& /*pile*/, + const Item& item, + const ItemGroup& /*remaining*/) + { + auto ibb = item.boundingBox(); + auto binbb = sl::boundingBox(bin); + auto mp = merged_pile; + mp.emplace_back(item.transformedShape()); + auto fullbb = sl::boundingBox(mp); + + double score = pl::distance(ibb.center(), binbb.center()); + score /= norm; + + double miss = overfit(fullbb, bin); + miss = miss > 0? miss : 0; + score += std::pow(miss, 2); + + return score; + }; + + // Our object function for placement + auto rawobjfunc = + [item, _objfunc, iv, + startpos, remlist, pile] (Vertex v) + { + auto d = v - iv; + d += startpos; + Item itm = item; + itm.translation(d); + + return _objfunc(pile, itm, remlist); }; auto getNfpPoint = [&ecache](const Optimum& opt) @@ -630,58 +885,10 @@ public: ecache[opt.nfpidx].coords(opt.hidx, opt.relpos); }; - nfp::Shapes pile; - pile.reserve(items_.size()+1); - double pile_area = 0; - for(Item& mitem : items_) { - pile.emplace_back(mitem.transformedShape()); - pile_area += mitem.area(); - } - - auto merged_pile = nfp::merge(pile); - - // This is the kernel part of the object function that is - // customizable by the library client - auto _objfunc = config_.object_function? - config_.object_function : - [this, &merged_pile, &pile_area]( - nfp::Shapes& /*pile*/, - const Item& item, - const ItemGroup& /*remaining*/) + auto boundaryCheck = + [&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos] + (const Optimum& o) { - merged_pile.emplace_back(item.transformedShape()); - auto ch = sl::convexHull(merged_pile); - merged_pile.pop_back(); - - // The pack ratio -- how much is the convex hull occupied - double pack_rate = (pile_area + item.area())/sl::area(ch); - - // ratio of waste - double waste = 1.0 - pack_rate; - - // Score is the square root of waste. This will extend the - // range of good (lower) values and shring the range of bad - // (larger) values. - auto score = std::sqrt(waste); - - if(!wouldFit(ch, bin_)) score += norm_; - - return score; - }; - - // Our object function for placement - auto rawobjfunc = [&] (Vertex v) - { - auto d = v - iv; - d += startpos; - item.translation(d); - - double score = _objfunc(pile, item, remlist); - - return score; - }; - - auto boundaryCheck = [&](const Optimum& o) { auto v = getNfpPoint(o); auto d = v - iv; d += startpos; @@ -691,84 +898,111 @@ public: auto chull = sl::convexHull(merged_pile); merged_pile.pop_back(); - return wouldFit(chull, bin_); + return overfit(chull, bin); }; - opt::StopCriteria stopcr; - stopcr.max_iterations = 200; - stopcr.relative_score_difference = 1e-20; - opt::TOptimizer solver(stopcr); - Optimum optimum(0, 0); double best_score = std::numeric_limits::max(); + std::launch policy = std::launch::deferred; + if(config_.parallel) policy |= std::launch::async; + + using OptResult = opt::Result; + using OptResults = std::vector; // Local optimization with the four polygon corners as // starting points for(unsigned ch = 0; ch < ecache.size(); ch++) { auto& cache = ecache[ch]; - auto contour_ofn = [&rawobjfunc, &getNfpPoint, ch] + auto contour_ofn = [rawobjfunc, getNfpPoint, ch] (double relpos) { return rawobjfunc(getNfpPoint(Optimum(relpos, ch))); }; - std::for_each(cache.corners().begin(), - cache.corners().end(), - [ch, &contour_ofn, &solver, &best_score, - &optimum, &boundaryCheck] (double pos) + OptResults results(cache.corners().size()); + + __parallel::enumerate( + cache.corners().begin(), + cache.corners().end(), + [&contour_ofn, &results] + (double pos, unsigned n) { + Optimizer solver; try { - auto result = solver.optimize_min(contour_ofn, + results[n] = solver.optimize_min(contour_ofn, opt::initvals(pos), opt::bound(0, 1.0) ); - - if(result.score < best_score) { - Optimum o(std::get<0>(result.optimum), ch, -1); - if(boundaryCheck(o)) { - best_score = result.score; - optimum = o; - } - } } catch(std::exception& e) { derr() << "ERROR: " << e.what() << "\n"; } - }); + }, policy); + + auto resultcomp = + []( const OptResult& r1, const OptResult& r2 ) { + return r1.score < r2.score; + }; + + auto mr = *std::min_element(results.begin(), results.end(), + resultcomp); + + if(mr.score < best_score) { + Optimum o(std::get<0>(mr.optimum), ch, -1); + double miss = boundaryCheck(o); + if(miss <= 0) { + best_score = mr.score; + optimum = o; + } else { + best_overfit = std::min(miss, best_overfit); + } + } for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) { auto hole_ofn = - [&rawobjfunc, &getNfpPoint, ch, hidx] + [rawobjfunc, getNfpPoint, ch, hidx] (double pos) { Optimum opt(pos, ch, hidx); return rawobjfunc(getNfpPoint(opt)); }; - std::for_each(cache.corners(hidx).begin(), + results.clear(); + results.resize(cache.corners(hidx).size()); + + // TODO : use parallel for + __parallel::enumerate(cache.corners(hidx).begin(), cache.corners(hidx).end(), - [&hole_ofn, &solver, &best_score, - &optimum, ch, hidx, &boundaryCheck] - (double pos) + [&hole_ofn, &results] + (double pos, unsigned n) { + Optimizer solver; try { - auto result = solver.optimize_min(hole_ofn, + results[n] = solver.optimize_min(hole_ofn, opt::initvals(pos), opt::bound(0, 1.0) ); - if(result.score < best_score) { - Optimum o(std::get<0>(result.optimum), - ch, hidx); - if(boundaryCheck(o)) { - best_score = result.score; - optimum = o; - } - } } catch(std::exception& e) { derr() << "ERROR: " << e.what() << "\n"; } - }); + }, policy); + + auto hmr = *std::min_element(results.begin(), + results.end(), + resultcomp); + + if(hmr.score < best_score) { + Optimum o(std::get<0>(hmr.optimum), + ch, hidx); + double miss = boundaryCheck(o); + if(miss <= 0.0) { + best_score = hmr.score; + optimum = o; + } else { + best_overfit = std::min(miss, best_overfit); + } + } } } @@ -788,22 +1022,14 @@ public: if(can_pack) { ret = PackResult(item); + item_keys_.emplace_back(itemhash); + } else { + ret = PackResult(best_overfit); } return ret; } - ~_NofitPolyPlacer() { - clearItems(); - } - - inline void clearItems() { - finalAlign(bin_); - Base::clearItems(); - } - -private: - inline void finalAlign(const RawShape& pbin) { auto bbin = sl::boundingBox(pbin); finalAlign(bbin); @@ -826,7 +1052,7 @@ private: nfp::Shapes m; m.reserve(items_.size()); for(Item& item : items_) m.emplace_back(item.transformedShape()); - auto&& bb = sl::boundingBox(m); + auto&& bb = sl::boundingBox(m); Vertex ci, cb; diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp index 1a0730d880..44e2bc1b00 100644 --- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp @@ -26,15 +26,21 @@ public: Item *item_ptr_; Vertex move_; Radians rot_; + double overfit_; friend class PlacerBoilerplate; friend Subclass; + PackResult(Item& item): item_ptr_(&item), move_(item.translation()), rot_(item.rotation()) {} - PackResult(): item_ptr_(nullptr) {} + + PackResult(double overfit = 1.0): + item_ptr_(nullptr), overfit_(overfit) {} + public: operator bool() { return item_ptr_ != nullptr; } + double overfit() const { return overfit_; } }; inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin) @@ -82,9 +88,6 @@ public: inline void clearItems() { items_.clear(); farea_valid_ = false; -#ifndef NDEBUG - debug_items_.clear(); -#endif } inline double filledArea() const { @@ -101,10 +104,6 @@ public: return farea_; } -#ifndef NDEBUG - std::vector debug_items_; -#endif - protected: BinType bin_; diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index 8c02dc3733..846b00badf 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -493,8 +493,7 @@ public: std::array candidates = {it, it2, it3}; - auto tryPack = [&placer, &candidates, ¬_packed, - &pack]( + auto tryPack = [&placer, &candidates, &pack]( const decltype(indices)& idx) { std::array packed = {false}; @@ -569,11 +568,7 @@ public: { packed_bins_[idx] = placer.getItems(); -#ifndef NDEBUG - packed_bins_[idx].insert(packed_bins_[idx].end(), - placer.getDebugItems().begin(), - placer.getDebugItems().end()); -#endif + // TODO here should be a spinlock slock.lock(); acounter -= packednum; diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index 1312f9874d..eb820a5184 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -68,13 +68,13 @@ public: } auto it = store_.begin(); + while(it != store_.end()) { bool was_packed = false; + size_t j = 0; while(!was_packed) { - - for(size_t j = 0; j < placers.size() && !was_packed; j++) { - if((was_packed = - placers[j].pack(*it, rem(it, store_) ))) + for(; j < placers.size() && !was_packed; j++) { + if((was_packed = placers[j].pack(*it, rem(it, store_) ))) makeProgress(placers[j], j); } @@ -82,6 +82,7 @@ public: placers.emplace_back(bin); placers.back().configure(pconfig); packed_bins_.emplace_back(); + j = placers.size() - 1; } } ++it; diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp index 197ff65981..b85bbc1112 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/xs/src/libnest2d/tests/test.cpp @@ -5,6 +5,7 @@ #include "printer_parts.h" #include //#include "../tools/libnfpglue.hpp" +//#include "../tools/nfp_svgnest_glue.hpp" std::vector& prusaParts() { static std::vector ret; @@ -219,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) { Point p = {1, 1}; - ASSERT_TRUE(rect.isPointInside(p)); + ASSERT_TRUE(rect.isInside(p)); p = {11, 11}; - ASSERT_FALSE(rect.isPointInside(p)); + ASSERT_FALSE(rect.isInside(p)); p = {11, 12}; - ASSERT_FALSE(rect.isPointInside(p)); + ASSERT_FALSE(rect.isInside(p)); p = {3, 3}; - ASSERT_TRUE(rect.isPointInside(p)); + ASSERT_TRUE(rect.isInside(p)); } diff --git a/xs/src/libnest2d/tools/libnfpglue.cpp b/xs/src/libnest2d/tools/libnfpglue.cpp index 18656fd402..31733acf97 100644 --- a/xs/src/libnest2d/tools/libnfpglue.cpp +++ b/xs/src/libnest2d/tools/libnfpglue.cpp @@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) { NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) { - using Vertex = PointImpl; + namespace sl = shapelike; NfpR ret; @@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) // this can throw auto nfp = libnfporb::generateNFP(pstat, porb, true); - auto &ct = ShapeLike::getContour(ret.first); + auto &ct = sl::getContour(ret.first); ct.reserve(nfp.front().size()+1); for(auto v : nfp.front()) { v = scale(v, refactor); @@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) ct.push_back(ct.front()); std::reverse(ct.begin(), ct.end()); - auto &rholes = ShapeLike::holes(ret.first); + auto &rholes = sl::holes(ret.first); for(size_t hidx = 1; hidx < nfp.size(); ++hidx) { if(nfp[hidx].size() >= 3) { rholes.emplace_back(); @@ -110,31 +110,31 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) } } - ret.second = Nfp::referenceVertex(ret.first); + ret.second = nfp::referenceVertex(ret.first); } catch(std::exception& e) { std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl; // auto ch_stat = ShapeLike::convexHull(sh); // auto ch_orb = ShapeLike::convexHull(cother); - ret = Nfp::nfpConvexOnly(sh, cother); + ret = nfp::nfpConvexOnly(sh, cother); } return ret; } -NfpR Nfp::NfpImpl::operator()( +NfpR nfp::NfpImpl::operator()( const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) { return _nfp(sh, cother);//nfpConvexOnly(sh, cother); } -NfpR Nfp::NfpImpl::operator()( +NfpR nfp::NfpImpl::operator()( const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) { return _nfp(sh, cother); } -NfpR Nfp::NfpImpl::operator()( +NfpR nfp::NfpImpl::operator()( const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) { return _nfp(sh, cother); diff --git a/xs/src/libnest2d/tools/libnfpglue.hpp b/xs/src/libnest2d/tools/libnfpglue.hpp index 75f6394451..1ff033cb92 100644 --- a/xs/src/libnest2d/tools/libnfpglue.hpp +++ b/xs/src/libnest2d/tools/libnfpglue.hpp @@ -5,22 +5,22 @@ namespace libnest2d { -using NfpR = Nfp::NfpResult; +using NfpR = nfp::NfpResult; NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother); template<> -struct Nfp::NfpImpl { +struct nfp::NfpImpl { NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); }; template<> -struct Nfp::NfpImpl { +struct nfp::NfpImpl { NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); }; template<> -struct Nfp::NfpImpl { +struct nfp::NfpImpl { NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); }; @@ -34,7 +34,7 @@ struct Nfp::NfpImpl { // NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother); //}; -template<> struct Nfp::MaxNfpLevel { +template<> struct nfp::MaxNfpLevel { static const BP2D_CONSTEXPR NfpLevel value = // NfpLevel::CONVEX_ONLY; NfpLevel::BOTH_CONCAVE; diff --git a/xs/src/libnest2d/tools/nfp_svgnest.hpp b/xs/src/libnest2d/tools/nfp_svgnest.hpp new file mode 100644 index 0000000000..8ab571c003 --- /dev/null +++ b/xs/src/libnest2d/tools/nfp_svgnest.hpp @@ -0,0 +1,1004 @@ +#ifndef NFP_SVGNEST_HPP +#define NFP_SVGNEST_HPP + +#include +#include + +#include + +namespace libnest2d { + +namespace __svgnest { + +using std::sqrt; +using std::min; +using std::max; +using std::abs; +using std::isnan; + +//template struct _Scale { +// static const BP2D_CONSTEXPR long long Value = 1000000; +//}; + +template struct _alg { + using Contour = TContour; + using Point = TPoint; + using iCoord = TCoord; + using Coord = double; + using Shapes = nfp::Shapes; + + static const Coord TOL; + +#define dNAN std::nan("") + + struct Vector { + Coord x, y; + bool marked = false; + Vector() = default; + Vector(Coord X, Coord Y): x(X), y(Y) {} + Vector(const Point& p): x(getX(p)), y(getY(p)) {} + operator Point() const { return {iCoord(x), iCoord(y)}; } + Vector& operator=(const Point& p) { + x = getX(p), y = getY(p); return *this; + } + Vector(std::initializer_list il): + x(*il.begin()), y(*std::next(il.begin())) {} + }; + + static inline Coord x(const Point& p) { return Coord(getX(p)); } + static inline Coord y(const Point& p) { return Coord(getY(p)); } + + static inline Coord x(const Vector& p) { return p.x; } + static inline Coord y(const Vector& p) { return p.y; } + + class Cntr { + std::vector v_; + public: + Cntr(const Contour& c) { + v_.reserve(c.size()); + std::transform(c.begin(), c.end(), std::back_inserter(v_), + [](const Point& p) { + return Vector(double(x(p))/1e6, double(y(p))/1e6); + }); + } + Cntr() = default; + + Coord offsetx = 0; + Coord offsety = 0; + size_t size() const { return v_.size(); } + bool empty() const { return v_.empty(); } + typename Contour::const_iterator begin() const { return v_.cbegin(); } + typename Contour::const_iterator end() const { return v_.cend(); } + Vector& operator[](size_t idx) { return v_[idx]; } + const Vector& operator[](size_t idx) const { return v_[idx]; } + template + void emplace_back(Args&&...args) { + v_.emplace_back(std::forward(args)...); + } + template + void push(Args&&...args) { + v_.emplace_back(std::forward(args)...); + } + void clear() { v_.clear(); } + + operator Contour() const { + Contour cnt; + cnt.reserve(v_.size()); + std::transform(v_.begin(), v_.end(), std::back_inserter(cnt), + [](const Vector& vertex) { + return Point(iCoord(vertex.x*1e6), iCoord(vertex.y*1e6)); + }); + return cnt; + } + }; + + inline static bool _almostEqual(Coord a, Coord b, + Coord tolerance = TOL) + { + return std::abs(a - b) < tolerance; + } + + // returns true if p lies on the line segment defined by AB, + // but not at any endpoints may need work! + static bool _onSegment(const Vector& A, const Vector& B, const Vector& p) { + + // vertical line + if(_almostEqual(A.x, B.x) && _almostEqual(p.x, A.x)) { + if(!_almostEqual(p.y, B.y) && !_almostEqual(p.y, A.y) && + p.y < max(B.y, A.y) && p.y > min(B.y, A.y)){ + return true; + } + else{ + return false; + } + } + + // horizontal line + if(_almostEqual(A.y, B.y) && _almostEqual(p.y, A.y)){ + if(!_almostEqual(p.x, B.x) && !_almostEqual(p.x, A.x) && + p.x < max(B.x, A.x) && p.x > min(B.x, A.x)){ + return true; + } + else{ + return false; + } + } + + //range check + if((p.x < A.x && p.x < B.x) || (p.x > A.x && p.x > B.x) || + (p.y < A.y && p.y < B.y) || (p.y > A.y && p.y > B.y)) + return false; + + // exclude end points + if((_almostEqual(p.x, A.x) && _almostEqual(p.y, A.y)) || + (_almostEqual(p.x, B.x) && _almostEqual(p.y, B.y))) + return false; + + + double cross = (p.y - A.y) * (B.x - A.x) - (p.x - A.x) * (B.y - A.y); + + if(abs(cross) > TOL) return false; + + double dot = (p.x - A.x) * (B.x - A.x) + (p.y - A.y)*(B.y - A.y); + + if(dot < 0 || _almostEqual(dot, 0)) return false; + + double len2 = (B.x - A.x)*(B.x - A.x) + (B.y - A.y)*(B.y - A.y); + + if(dot > len2 || _almostEqual(dot, len2)) return false; + + return true; + } + + // return true if point is in the polygon, false if outside, and null if exactly on a point or edge + static int pointInPolygon(const Vector& point, const Cntr& polygon) { + if(polygon.size() < 3){ + return 0; + } + + bool inside = false; + Coord offsetx = polygon.offsetx; + Coord offsety = polygon.offsety; + + for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j=i++) { + auto xi = polygon[i].x + offsetx; + auto yi = polygon[i].y + offsety; + auto xj = polygon[j].x + offsetx; + auto yj = polygon[j].y + offsety; + + if(_almostEqual(xi, point.x) && _almostEqual(yi, point.y)){ + return 0; // no result + } + + if(_onSegment({xi, yi}, {xj, yj}, point)){ + return 0; // exactly on the segment + } + + if(_almostEqual(xi, xj) && _almostEqual(yi, yj)){ // ignore very small lines + continue; + } + + bool intersect = ((yi > point.y) != (yj > point.y)) && + (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi); + if (intersect) inside = !inside; + } + + return inside? 1 : -1; + } + + static bool intersect(const Cntr& A, const Cntr& B){ + Contour a = A, b = B; + return shapelike::intersects(shapelike::create(a), shapelike::create(b)); + } + + static Vector _normalizeVector(const Vector& v) { + if(_almostEqual(v.x*v.x + v.y*v.y, Coord(1))){ + return Point(v); // given vector was already a unit vector + } + auto len = sqrt(v.x*v.x + v.y*v.y); + auto inverse = 1/len; + + return { Coord(v.x*inverse), Coord(v.y*inverse) }; + } + + static double pointDistance( const Vector& p, + const Vector& s1, + const Vector& s2, + Vector normal, + bool infinite = false) + { + normal = _normalizeVector(normal); + + Vector dir = { + normal.y, + -normal.x + }; + + auto pdot = p.x*dir.x + p.y*dir.y; + auto s1dot = s1.x*dir.x + s1.y*dir.y; + auto s2dot = s2.x*dir.x + s2.y*dir.y; + + auto pdotnorm = p.x*normal.x + p.y*normal.y; + auto s1dotnorm = s1.x*normal.x + s1.y*normal.y; + auto s2dotnorm = s2.x*normal.x + s2.y*normal.y; + + if(!infinite){ + if (((pdots1dot || _almostEqual(pdot, s1dot)) && + (pdot>s2dot || _almostEqual(pdot, s2dot)))) + { + // dot doesn't collide with segment, + // or lies directly on the vertex + return dNAN; + } + if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && + (pdotnorm>s1dotnorm && pdotnorm>s2dotnorm)) + { + return double(min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm)); + } + if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && + (pdotnorm EFmax){ + return dNAN; + } + + double overlap = 0; + + if((ABmax > EFmax && ABmin < EFmin) || (EFmax > ABmax && EFmin < ABmin)) + { + overlap = 1; + } + else{ + auto minMax = min(ABmax, EFmax); + auto maxMin = max(ABmin, EFmin); + + auto maxMax = max(ABmax, EFmax); + auto minMin = min(ABmin, EFmin); + + overlap = (minMax-maxMin)/(maxMax-minMin); + } + + auto crossABE = (E.y - A.y) * (B.x - A.x) - (E.x - A.x) * (B.y - A.y); + auto crossABF = (F.y - A.y) * (B.x - A.x) - (F.x - A.x) * (B.y - A.y); + + // lines are colinear + if(_almostEqual(crossABE,0) && _almostEqual(crossABF,0)){ + + Vector ABnorm = {B.y-A.y, A.x-B.x}; + Vector EFnorm = {F.y-E.y, E.x-F.x}; + + auto ABnormlength = sqrt(ABnorm.x*ABnorm.x + ABnorm.y*ABnorm.y); + ABnorm.x /= ABnormlength; + ABnorm.y /= ABnormlength; + + auto EFnormlength = sqrt(EFnorm.x*EFnorm.x + EFnorm.y*EFnorm.y); + EFnorm.x /= EFnormlength; + EFnorm.y /= EFnormlength; + + // segment normals must point in opposite directions + if(abs(ABnorm.y * EFnorm.x - ABnorm.x * EFnorm.y) < TOL && + ABnorm.y * EFnorm.y + ABnorm.x * EFnorm.x < 0){ + // normal of AB segment must point in same direction as + // given direction vector + auto normdot = ABnorm.y * direction.y + ABnorm.x * direction.x; + // the segments merely slide along eachother + if(_almostEqual(normdot,0, TOL)){ + return dNAN; + } + if(normdot < 0){ + return 0.0; + } + } + return dNAN; + } + + std::vector distances; distances.reserve(10); + + // coincident points + if(_almostEqual(dotA, dotE)){ + distances.emplace_back(crossA-crossE); + } + else if(_almostEqual(dotA, dotF)){ + distances.emplace_back(crossA-crossF); + } + else if(dotA > EFmin && dotA < EFmax){ + auto d = pointDistance(A,E,F,reverse); + if(!isnan(d) && _almostEqual(d, 0)) + { // A currently touches EF, but AB is moving away from EF + auto dB = pointDistance(B,E,F,reverse,true); + if(dB < 0 || _almostEqual(dB*overlap,0)){ + d = dNAN; + } + } + if(isnan(d)){ + distances.emplace_back(d); + } + } + + if(_almostEqual(dotB, dotE)){ + distances.emplace_back(crossB-crossE); + } + else if(_almostEqual(dotB, dotF)){ + distances.emplace_back(crossB-crossF); + } + else if(dotB > EFmin && dotB < EFmax){ + auto d = pointDistance(B,E,F,reverse); + + if(!isnan(d) && _almostEqual(d, 0)) + { // crossA>crossB A currently touches EF, but AB is moving away from EF + double dA = pointDistance(A,E,F,reverse,true); + if(dA < 0 || _almostEqual(dA*overlap,0)){ + d = dNAN; + } + } + if(!isnan(d)){ + distances.emplace_back(d); + } + } + + if(dotE > ABmin && dotE < ABmax){ + auto d = pointDistance(E,A,B,direction); + if(!isnan(d) && _almostEqual(d, 0)) + { // crossF ABmin && dotF < ABmax){ + auto d = pointDistance(F,A,B,direction); + if(!isnan(d) && _almostEqual(d, 0)) + { // && crossE 0 || _almostEqual(d, 0)){ + distance = d; + } + } + } + } + return distance; + } + + static double polygonProjectionDistance(const Cntr& A, + const Cntr& B, + Vector direction) + { + auto Boffsetx = B.offsetx; + auto Boffsety = B.offsety; + auto Aoffsetx = A.offsetx; + auto Aoffsety = A.offsety; + + // close the loop for polygons + /*if(A[0] != A[A.length-1]){ + A.push(A[0]); + } + + if(B[0] != B[B.length-1]){ + B.push(B[0]); + }*/ + + auto& edgeA = A; + auto& edgeB = B; + + double distance = dNAN, d; +// Vector p, s1, s2; + + for(size_t i = 0; i < edgeB.size(); i++) { + // the shortest/most negative projection of B onto A + double minprojection = dNAN; + Vector minp; + for(size_t j = 0; j < edgeA.size() - 1; j++){ + Vector p = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety }; + Vector s1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety }; + Vector s2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety }; + + if(abs((s2.y-s1.y) * direction.x - + (s2.x-s1.x) * direction.y) < TOL) continue; + + // project point, ignore edge boundaries + d = pointDistance(p, s1, s2, direction); + + if(!isnan(d) && (isnan(minprojection) || d < minprojection)) { + minprojection = d; + minp = p; + } + } + + if(!isnan(minprojection) && (isnan(distance) || + minprojection > distance)){ + distance = minprojection; + } + } + + return distance; + } + + static std::pair searchStartPoint( + const Cntr& AA, const Cntr& BB, bool inside, const std::vector& NFP = {}) + { + // clone arrays + auto A = AA; + auto B = BB; + +// // close the loop for polygons +// if(A[0] != A[A.size()-1]){ +// A.push(A[0]); +// } + +// if(B[0] != B[B.size()-1]){ +// B.push(B[0]); +// } + + // returns true if point already exists in the given nfp + auto inNfp = [](const Vector& p, const std::vector& nfp){ + if(nfp.empty()){ + return false; + } + + for(size_t i=0; i < nfp.size(); i++){ + for(size_t j = 0; j< nfp[i].size(); j++){ + if(_almostEqual(p.x, nfp[i][j].x) && + _almostEqual(p.y, nfp[i][j].y)){ + return true; + } + } + } + + return false; + }; + + for(size_t i = 0; i < A.size() - 1; i++){ + if(!A[i].marked) { + A[i].marked = true; + for(size_t j = 0; j < B.size(); j++){ + B.offsetx = A[i].x - B[j].x; + B.offsety = A[i].y - B[j].y; + + int Binside = 0; + for(size_t k = 0; k < B.size(); k++){ + int inpoly = pointInPolygon({B[k].x + B.offsetx, B[k].y + B.offsety}, A); + if(inpoly != 0){ + Binside = inpoly; + break; + } + } + + if(Binside == 0){ // A and B are the same + return {false, {}}; + } + + auto startPoint = std::make_pair(true, Vector(B.offsetx, B.offsety)); + if(((Binside && inside) || (!Binside && !inside)) && + !intersect(A,B) && !inNfp(startPoint.second, NFP)){ + return startPoint; + } + + // slide B along vector + auto vx = A[i+1].x - A[i].x; + auto vy = A[i+1].y - A[i].y; + + double d1 = polygonProjectionDistance(A,B,{vx, vy}); + double d2 = polygonProjectionDistance(B,A,{-vx, -vy}); + + double d = dNAN; + + // todo: clean this up + if(isnan(d1) && isnan(d2)){ + // nothin + } + else if(isnan(d1)){ + d = d2; + } + else if(isnan(d2)){ + d = d1; + } + else{ + d = min(d1,d2); + } + + // only slide until no longer negative + // todo: clean this up + if(!isnan(d) && !_almostEqual(d,0) && d > 0){ + + } + else{ + continue; + } + + auto vd2 = vx*vx + vy*vy; + + if(d*d < vd2 && !_almostEqual(d*d, vd2)){ + auto vd = sqrt(vx*vx + vy*vy); + vx *= d/vd; + vy *= d/vd; + } + + B.offsetx += vx; + B.offsety += vy; + + for(size_t k = 0; k < B.size(); k++){ + int inpoly = pointInPolygon({B[k].x + B.offsetx, B[k].y + B.offsety}, A); + if(inpoly != 0){ + Binside = inpoly; + break; + } + } + startPoint = std::make_pair(true, Vector{B.offsetx, B.offsety}); + if(((Binside && inside) || (!Binside && !inside)) && + !intersect(A,B) && !inNfp(startPoint.second, NFP)){ + return startPoint; + } + } + } + } + + return {false, Vector(0, 0)}; + } + + static std::vector noFitPolygon(Cntr A, + Cntr B, + bool inside, + bool searchEdges) + { + if(A.size() < 3 || B.size() < 3) { + throw GeometryException(GeomErr::NFP); + return {}; + } + + A.offsetx = 0; + A.offsety = 0; + + unsigned i = 0, j = 0; + + auto minA = y(A[0]); + long minAindex = 0; + + auto maxB = y(B[0]); + long maxBindex = 0; + + for(i = 1; i < A.size(); i++){ + A[i].marked = false; + if(y(A[i]) < minA){ + minA = y(A[i]); + minAindex = i; + } + } + + for(i = 1; i < B.size(); i++){ + B[i].marked = false; + if(y(B[i]) > maxB){ + maxB = y(B[i]); + maxBindex = i; + } + } + + std::pair startpoint; + + if(!inside){ + // shift B such that the bottom-most point of B is at the top-most + // point of A. This guarantees an initial placement with no + // intersections + startpoint = { true, + { x(A[minAindex]) - x(B[maxBindex]), + y(A[minAindex]) - y(B[maxBindex]) } + }; + } + else { + // no reliable heuristic for inside + startpoint = searchStartPoint(A, B, true); + } + + std::vector NFPlist; + + struct Touch { + int type; + long A, B; + Touch(int t, long a, long b): type(t), A(a), B(b) {} + }; + + while(startpoint.first) { + + B.offsetx = startpoint.second.x; + B.offsety = startpoint.second.y; + + // maintain a list of touching points/edges + std::vector touching; + + Cntr NFP; + NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety); + + auto referencex = x(B[0]) + B.offsetx; + auto referencey = y(B[0]) + B.offsety; + auto startx = referencex; + auto starty = referencey; + unsigned counter = 0; + + // sanity check, prevent infinite loop + while(counter < 10*(A.size() + B.size())){ + touching.clear(); + + // find touching vertices/edges + for(i = 0; i < A.size(); i++){ + auto nexti = (i == A.size() - 1) ? 0 : i + 1; + for(j = 0; j < B.size(); j++){ + + auto nextj = (j == B.size() - 1) ? 0 : j + 1; + + if( _almostEqual(A[i].x, B[j].x+B.offsetx) && + _almostEqual(A[i].y, B[j].y+B.offsety)) + { + touching.emplace_back(0, i, j); + } + else if( _onSegment( + A[i], A[nexti], + { B[j].x+B.offsetx, B[j].y + B.offsety}) ) + { + touching.emplace_back(1, nexti, j); + } + else if( _onSegment( + {B[j].x+B.offsetx, B[j].y + B.offsety}, + {B[nextj].x+B.offsetx, B[nextj].y + B.offsety}, + A[i]) ) + { + touching.emplace_back(2, i, nextj); + } + } + } + + struct V { + Coord x, y; + Vector *start, *end; + operator bool() { + return start != nullptr && end != nullptr; + } + operator Vector() const { return {x, y}; } + }; + + // generate translation vectors from touching vertices/edges + std::vector vectors; + for(i=0; i < touching.size(); i++){ + auto& vertexA = A[touching[i].A]; + vertexA.marked = true; + + // adjacent A vertices + auto prevAindex = touching[i].A - 1; + auto nextAindex = touching[i].A + 1; + + prevAindex = (prevAindex < 0) ? A.size() - 1 : prevAindex; // loop + nextAindex = (nextAindex >= A.size()) ? 0 : nextAindex; // loop + + auto& prevA = A[prevAindex]; + auto& nextA = A[nextAindex]; + + // adjacent B vertices + auto& vertexB = B[touching[i].B]; + + auto prevBindex = touching[i].B-1; + auto nextBindex = touching[i].B+1; + + prevBindex = (prevBindex < 0) ? B.size() - 1 : prevBindex; // loop + nextBindex = (nextBindex >= B.size()) ? 0 : nextBindex; // loop + + auto& prevB = B[prevBindex]; + auto& nextB = B[nextBindex]; + + if(touching[i].type == 0){ + + V vA1 = { + prevA.x - vertexA.x, + prevA.y - vertexA.y, + &vertexA, + &prevA + }; + + V vA2 = { + nextA.x - vertexA.x, + nextA.y - vertexA.y, + &vertexA, + &nextA + }; + + // B vectors need to be inverted + V vB1 = { + vertexB.x - prevB.x, + vertexB.y - prevB.y, + &prevB, + &vertexB + }; + + V vB2 = { + vertexB.x - nextB.x, + vertexB.y - nextB.y, + &nextB, + &vertexB + }; + + vectors.emplace_back(vA1); + vectors.emplace_back(vA2); + vectors.emplace_back(vB1); + vectors.emplace_back(vB2); + } + else if(touching[i].type == 1){ + vectors.emplace_back(V{ + vertexA.x-(vertexB.x+B.offsetx), + vertexA.y-(vertexB.y+B.offsety), + &prevA, + &vertexA + }); + + vectors.emplace_back(V{ + prevA.x-(vertexB.x+B.offsetx), + prevA.y-(vertexB.y+B.offsety), + &vertexA, + &prevA + }); + } + else if(touching[i].type == 2){ + vectors.emplace_back(V{ + vertexA.x-(vertexB.x+B.offsetx), + vertexA.y-(vertexB.y+B.offsety), + &prevB, + &vertexB + }); + + vectors.emplace_back(V{ + vertexA.x-(prevB.x+B.offsetx), + vertexA.y-(prevB.y+B.offsety), + &vertexB, + &prevB + }); + } + } + + // TODO: there should be a faster way to reject vectors that + // will cause immediate intersection. For now just check them all + + V translate = {0, 0, nullptr, nullptr}; + V prevvector = {0, 0, nullptr, nullptr}; + double maxd = 0; + + for(i = 0; i < vectors.size(); i++) { + if(vectors[i].x == 0 && vectors[i].y == 0){ + continue; + } + + // if this vector points us back to where we came from, ignore it. + // ie cross product = 0, dot product < 0 + if(prevvector && vectors[i].y * prevvector.y + vectors[i].x * prevvector.x < 0){ + + // compare magnitude with unit vectors + double vectorlength = sqrt(vectors[i].x*vectors[i].x+vectors[i].y*vectors[i].y); + Vector unitv = {Coord(vectors[i].x/vectorlength), + Coord(vectors[i].y/vectorlength)}; + + double prevlength = sqrt(prevvector.x*prevvector.x+prevvector.y*prevvector.y); + Vector prevunit = { prevvector.x/prevlength, prevvector.y/prevlength}; + + // we need to scale down to unit vectors to normalize vector length. Could also just do a tan here + if(abs(unitv.y * prevunit.x - unitv.x * prevunit.y) < 0.0001){ + continue; + } + } + + double d = polygonSlideDistance(A, B, vectors[i], true); + double vecd2 = vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y; + + if(isnan(d) || d*d > vecd2){ + double vecd = sqrt(vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y); + d = vecd; + } + + if(!isnan(d) && d > maxd){ + maxd = d; + translate = vectors[i]; + } + } + + if(!translate || _almostEqual(maxd, 0)) + { + // didn't close the loop, something went wrong here + NFP.clear(); + break; + } + + translate.start->marked = true; + translate.end->marked = true; + + prevvector = translate; + + // trim + double vlength2 = translate.x*translate.x + translate.y*translate.y; + if(maxd*maxd < vlength2 && !_almostEqual(maxd*maxd, vlength2)){ + double scale = sqrt((maxd*maxd)/vlength2); + translate.x *= scale; + translate.y *= scale; + } + + referencex += translate.x; + referencey += translate.y; + + if(_almostEqual(referencex, startx) && + _almostEqual(referencey, starty)) { + // we've made a full loop + break; + } + + // if A and B start on a touching horizontal line, + // the end point may not be the start point + bool looped = false; + if(NFP.size() > 0) { + for(i = 0; i < NFP.size() - 1; i++) { + if(_almostEqual(referencex, NFP[i].x) && + _almostEqual(referencey, NFP[i].y)){ + looped = true; + } + } + } + + if(looped){ + // we've made a full loop + break; + } + + NFP.emplace_back(referencex, referencey); + + B.offsetx += translate.x; + B.offsety += translate.y; + + counter++; + } + + if(NFP.size() > 0){ + NFPlist.emplace_back(NFP); + } + + if(!searchEdges){ + // only get outer NFP or first inner NFP + break; + } + + startpoint = + searchStartPoint(A, B, inside, NFPlist); + + } + + return NFPlist; + } +}; + +template const double _alg::TOL = std::pow(10, -9); + +//template +//nfp::NfpResult nfpSimpleSimple(const S& stat, const S& orb) { +//// using Cntr = TContour; +// using Point = TPoint; +//// using Coord = TCoord; +//// using Shapes = nfp::Shapes; + +// namespace sl = shapelike; + +// noFitPolygon(sl::getContour(stat), sl::getContour(orb), true, true); +// return {S(), Point()}; +//} + +} +} + +#endif // NFP_SVGNEST_HPP diff --git a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp new file mode 100644 index 0000000000..7ceb2d24dc --- /dev/null +++ b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp @@ -0,0 +1,77 @@ +#ifndef NFP_SVGNEST_GLUE_HPP +#define NFP_SVGNEST_GLUE_HPP + +#include "nfp_svgnest.hpp" + +#include + +namespace libnest2d { + +namespace __svgnest { + +//template<> struct _Tol { +// static const BP2D_CONSTEXPR TCoord Value = 1000000; +//}; + +} + +namespace nfp { + +using NfpR = NfpResult; + +template<> struct NfpImpl { + NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { +// return nfpConvexOnly(sh, cother); + namespace sl = shapelike; + using alg = __svgnest::_alg; + + std::cout << "Itt vagyok" << std::endl; + auto nfp_p = alg::noFitPolygon(sl::getContour(sh), + sl::getContour(cother), false, false); + + PolygonImpl nfp_cntr; + nfp_cntr.Contour = nfp_p.front(); + std::cout << "Contour size: " << nfp_cntr.Contour.size() << std::endl; + return {nfp_cntr, referenceVertex(nfp_cntr)}; + } +}; + +template<> struct NfpImpl { + NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { +// return nfpConvexOnly(sh, cother); + namespace sl = shapelike; + using alg = __svgnest::_alg; + + std::cout << "Itt vagyok" << std::endl; + auto nfp_p = alg::noFitPolygon(sl::getContour(sh), + sl::getContour(cother), false, false); + + PolygonImpl nfp_cntr; + nfp_cntr.Contour = nfp_p.front(); + return {nfp_cntr, referenceVertex(nfp_cntr)}; + } +}; + +template<> +struct NfpImpl { + NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { + namespace sl = shapelike; + using alg = __svgnest::_alg; + + auto nfp_p = alg::noFitPolygon(sl::getContour(sh), + sl::getContour(cother), true, false); + + PolygonImpl nfp_cntr; + nfp_cntr.Contour = nfp_p.front(); + return {nfp_cntr, referenceVertex(nfp_cntr)}; + } +}; + +template<> struct MaxNfpLevel { +// static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE; + static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; +}; + +}} + +#endif // NFP_SVGNEST_GLUE_HPP diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index cc4bfff0f6..dcb0da9e5d 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -270,6 +270,8 @@ void fillConfig(PConf& pcfg) { // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well pcfg.accuracy = 0.65f; + + pcfg.parallel = false; } template @@ -291,6 +293,7 @@ protected: std::vector areacache_; SpatIndex rtree_; double norm_; + Pile pile_cache_; public: _ArrBase(const TBin& bin, Distance dist, @@ -317,23 +320,26 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { - pconf_.object_function = [this, bin] ( - Pile& pile, - const Item &item, - const ItemGroup& rem) { +// pconf_.object_function = [this, bin] ( +// const Pile& pile_c, +// const Item &item, +// const ItemGroup& rem) { - auto result = objfunc(bin.center(), bin_area_, pile, - item, norm_, areacache_, rtree_, rem); - double score = std::get<0>(result); - auto& fullbb = std::get<1>(result); +// auto& pile = pile_cache_; +// if(pile.size() != pile_c.size()) pile = pile_c; - auto wdiff = fullbb.width() - bin.width(); - auto hdiff = fullbb.height() - bin.height(); - if(wdiff > 0) score += std::pow(wdiff, 2) / norm_; - if(hdiff > 0) score += std::pow(hdiff, 2) / norm_; +// auto result = objfunc(bin.center(), bin_area_, pile, +// item, norm_, areacache_, rtree_, rem); +// double score = std::get<0>(result); +// auto& fullbb = std::get<1>(result); - return score; - }; +// auto wdiff = fullbb.width() - bin.width(); +// auto hdiff = fullbb.height() - bin.height(); +// if(wdiff > 0) score += std::pow(wdiff, 2) / norm_; +// if(hdiff > 0) score += std::pow(hdiff, 2) / norm_; + +// return score; +// }; pck_.configure(pconf_); } @@ -350,10 +356,13 @@ public: _ArrBase(bin, dist, progressind) { pconf_.object_function = [this, &bin] ( - Pile& pile, + const Pile& pile_c, const Item &item, const ItemGroup& rem) { + auto& pile = pile_cache_; + if(pile.size() != pile_c.size()) pile = pile_c; + auto result = objfunc(bin.center(), bin_area_, pile, item, norm_, areacache_, rtree_, rem); double score = std::get<0>(result); @@ -393,10 +402,13 @@ public: _ArrBase(bin, dist, progressind) { pconf_.object_function = [this, &bin] ( - Pile& pile, + const Pile& pile_c, const Item &item, const ItemGroup& rem) { + auto& pile = pile_cache_; + if(pile.size() != pile_c.size()) pile = pile_c; + auto binbb = sl::boundingBox(bin); auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_, areacache_, rtree_, rem); @@ -417,10 +429,13 @@ public: _ArrBase(Box(0, 0), dist, progressind) { this->pconf_.object_function = [this] ( - Pile& pile, + const Pile& pile_c, const Item &item, const ItemGroup& rem) { + auto& pile = pile_cache_; + if(pile.size() != pile_c.size()) pile = pile_c; + auto result = objfunc({0, 0}, 0, pile, item, norm_, areacache_, rtree_, rem); return std::get<0>(result); From e522ad1a00cfb85b6cd21c664b213d5d35dca2bd Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 22 Aug 2018 13:52:41 +0200 Subject: [PATCH 10/50] Parallel placer now works with the custom Slic3r object function. Works an order of magnitude faster. --- xs/CMakeLists.txt | 1 + xs/src/libnest2d/CMakeLists.txt | 25 +- xs/src/libnest2d/cmake_modules/FindTBB.cmake | 322 ++++++++++++++++++ xs/src/libnest2d/examples/main.cpp | 28 +- xs/src/libnest2d/libnest2d.h | 10 +- .../libnest2d/geometry_traits_nfp.hpp | 4 +- xs/src/libnest2d/libnest2d/libnest2d.hpp | 20 +- .../libnest2d/placers/bottomleftplacer.hpp | 2 +- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 227 +++++++----- .../libnest2d/placers/placer_boilerplate.hpp | 2 +- .../libnest2d/selections/djd_heuristic.hpp | 2 +- .../libnest2d/libnest2d/selections/filler.hpp | 2 +- .../libnest2d/selections/firstfit.hpp | 2 +- .../selections/selection_boilerplate.hpp | 3 +- xs/src/libnest2d/tests/test.cpp | 34 +- xs/src/libnest2d/tools/nfp_svgnest.hpp | 114 ++++--- xs/src/libnest2d/tools/nfp_svgnest_glue.hpp | 4 +- xs/src/libslic3r/ModelArrange.hpp | 233 +++++++------ 18 files changed, 739 insertions(+), 296 deletions(-) create mode 100644 xs/src/libnest2d/cmake_modules/FindTBB.cmake diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index d41b4c13a9..70e9330580 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -729,6 +729,7 @@ add_custom_target(pot set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") add_subdirectory(${LIBDIR}/libnest2d) +target_compile_definitions(libslic3r PUBLIC -DUSE_TBB) target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt index cd3e35b978..f813550123 100644 --- a/xs/src/libnest2d/CMakeLists.txt +++ b/xs/src/libnest2d/CMakeLists.txt @@ -90,6 +90,7 @@ if(LIBNEST2D_UNITTESTS) endif() if(LIBNEST2D_BUILD_EXAMPLES) + add_executable(example examples/main.cpp # tools/libnfpglue.hpp # tools/libnfpglue.cpp @@ -98,8 +99,30 @@ if(LIBNEST2D_BUILD_EXAMPLES) tools/svgtools.hpp tests/printer_parts.cpp tests/printer_parts.h - ${LIBNEST2D_SRCFILES}) + ${LIBNEST2D_SRCFILES} + ) + set(TBB_STATIC ON) + find_package(TBB QUIET) + if(TBB_FOUND) + message(STATUS "Parallelization with Intel TBB") + target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS}) + target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB) + if(MSVC) + # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. + target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE) + endif() + # The Intel TBB library will use the std::exception_ptr feature of C++11. + target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1) + target_link_libraries(example ${TBB_LIBRARIES}) + else() + find_package(OpenMP QUIET) + if(OpenMP_CXX_FOUND) + message(STATUS "Parallelization with OpenMP") + target_include_directories(example PUBLIC OpenMP::OpenMP_CXX) + target_link_libraries(example OpenMP::OpenMP_CXX) + endif() + endif() target_link_libraries(example ${LIBNEST2D_LIBRARIES}) target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS}) diff --git a/xs/src/libnest2d/cmake_modules/FindTBB.cmake b/xs/src/libnest2d/cmake_modules/FindTBB.cmake new file mode 100644 index 0000000000..8b498d3ab1 --- /dev/null +++ b/xs/src/libnest2d/cmake_modules/FindTBB.cmake @@ -0,0 +1,322 @@ +# The MIT License (MIT) +# +# Copyright (c) 2015 Justus Calvin +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# +# FindTBB +# ------- +# +# Find TBB include directories and libraries. +# +# Usage: +# +# find_package(TBB [major[.minor]] [EXACT] +# [QUIET] [REQUIRED] +# [[COMPONENTS] [components...]] +# [OPTIONAL_COMPONENTS components...]) +# +# where the allowed components are tbbmalloc and tbb_preview. Users may modify +# the behavior of this module with the following variables: +# +# * TBB_ROOT_DIR - The base directory the of TBB installation. +# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. +# * TBB_LIBRARY - The directory that contains the TBB library files. +# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. +# These libraries, if specified, override the +# corresponding library search results, where +# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, +# tbb_preview, or tbb_preview_debug. +# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will +# be used instead of the release version. +# * TBB_STATIC - Static linking of libraries with a _static suffix. +# For example, on Windows a tbb_static.lib will be searched for +# instead of tbb.lib. +# +# Users may modify the behavior of this module with the following environment +# variables: +# +# * TBB_INSTALL_DIR +# * TBBROOT +# * LIBRARY_PATH +# +# This module will set the following variables: +# +# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or +# don’t want to use TBB. +# * TBB__FOUND - If False, optional part of TBB sytem is +# not available. +# * TBB_VERSION - The full version string +# * TBB_VERSION_MAJOR - The major version +# * TBB_VERSION_MINOR - The minor version +# * TBB_INTERFACE_VERSION - The interface version number defined in +# tbb/tbb_stddef.h. +# * TBB__LIBRARY_RELEASE - The path of the TBB release version of +# , where may be tbb, tbb_debug, +# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. +# * TBB__LIBRARY_DEGUG - The path of the TBB release version of +# , where may be tbb, tbb_debug, +# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. +# +# The following varibles should be used to build and link with TBB: +# +# * TBB_INCLUDE_DIRS - The include directory for TBB. +# * TBB_LIBRARIES - The libraries to link against to use TBB. +# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB. +# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB. +# * TBB_DEFINITIONS - Definitions to use when compiling code that uses +# TBB. +# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that +# uses TBB. +# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that +# uses TBB. +# +# This module will also create the "tbb" target that may be used when building +# executables and libraries. + +include(FindPackageHandleStandardArgs) + +if(NOT TBB_FOUND) + + ################################## + # Check the build type + ################################## + + if(NOT DEFINED TBB_USE_DEBUG_BUILD) + if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)") + set(TBB_BUILD_TYPE DEBUG) + else() + set(TBB_BUILD_TYPE RELEASE) + endif() + elseif(TBB_USE_DEBUG_BUILD) + set(TBB_BUILD_TYPE DEBUG) + else() + set(TBB_BUILD_TYPE RELEASE) + endif() + + ################################## + # Set the TBB search directories + ################################## + + # Define search paths based on user input and environment variables + set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) + + # Define the search directories based on the current platform + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" + "C:/Program Files (x86)/Intel/TBB") + + # Set the target architecture + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(TBB_ARCHITECTURE "intel64") + else() + set(TBB_ARCHITECTURE "ia32") + endif() + + # Set the TBB search library path search suffix based on the version of VC + if(WINDOWS_STORE) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") + elseif(MSVC14) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14") + elseif(MSVC12) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12") + elseif(MSVC11) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") + elseif(MSVC10) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") + endif() + + # Add the library path search suffix for the VC independent version of TBB + list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") + + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # OS X + set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") + + # TODO: Check to see which C++ library is being used by the compiler. + if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) + # The default C++ library on OS X 10.9 and later is libc++ + set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") + else() + set(TBB_LIB_PATH_SUFFIX "lib") + endif() + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Linux + set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") + + # TODO: Check compiler version to see the suffix should be /gcc4.1 or + # /gcc4.1. For now, assume that the compiler is more recent than + # gcc 4.4.x or later. + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") + set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") + endif() + endif() + + ################################## + # Find the TBB include dir + ################################## + + find_path(TBB_INCLUDE_DIRS tbb/tbb.h + HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} + PATHS ${TBB_DEFAULT_SEARCH_DIR} + PATH_SUFFIXES include) + + ################################## + # Set version strings + ################################## + + if(TBB_INCLUDE_DIRS) + file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) + string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" + TBB_VERSION_MAJOR "${_tbb_version_file}") + string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" + TBB_VERSION_MINOR "${_tbb_version_file}") + string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" + TBB_INTERFACE_VERSION "${_tbb_version_file}") + set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") + endif() + + ################################## + # Find TBB components + ################################## + + if(TBB_VERSION VERSION_LESS 4.3) + set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) + else() + set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) + endif() + + if(TBB_STATIC) + set(TBB_STATIC_SUFFIX "_static") + endif() + + # Find each component + foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) + if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") + + # Search for the libraries + find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} + HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} + PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH + PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) + + find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug + HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} + PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH + PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) + + if(TBB_${_comp}_LIBRARY_DEBUG) + list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") + endif() + if(TBB_${_comp}_LIBRARY_RELEASE) + list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") + endif() + if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) + set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") + endif() + + if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") + set(TBB_${_comp}_FOUND TRUE) + else() + set(TBB_${_comp}_FOUND FALSE) + endif() + + # Mark internal variables as advanced + mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) + mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) + mark_as_advanced(TBB_${_comp}_LIBRARY) + + endif() + endforeach() + + unset(TBB_STATIC_SUFFIX) + + ################################## + # Set compile flags and libraries + ################################## + + set(TBB_DEFINITIONS_RELEASE "") + set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1") + + if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) + set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") + set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") + elseif(TBB_LIBRARIES_RELEASE) + set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}") + set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}") + elseif(TBB_LIBRARIES_DEBUG) + set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}") + set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") + endif() + + find_package_handle_standard_args(TBB + REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES + HANDLE_COMPONENTS + VERSION_VAR TBB_VERSION) + + ################################## + # Create targets + ################################## + + if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) + add_library(tbb SHARED IMPORTED) + set_target_properties(tbb PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} + IMPORTED_LOCATION ${TBB_LIBRARIES}) + if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) + set_target_properties(tbb PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "$<$,$>:TBB_USE_DEBUG=1>" + IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} + IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG} + IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} + IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} + ) + elseif(TBB_LIBRARIES_RELEASE) + set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE}) + else() + set_target_properties(tbb PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}" + IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG} + ) + endif() + endif() + + mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) + + unset(TBB_ARCHITECTURE) + unset(TBB_BUILD_TYPE) + unset(TBB_LIB_PATH_SUFFIX) + unset(TBB_DEFAULT_SEARCH_DIR) + + if(TBB_DEBUG) + message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") + message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}") + message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") + message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}") + message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}") + message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") + message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}") + endif() + +endif() diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp index 57be7a2080..ebc3fb15c5 100644 --- a/xs/src/libnest2d/examples/main.cpp +++ b/xs/src/libnest2d/examples/main.cpp @@ -54,7 +54,7 @@ void arrangeRectangles() { const int SCALE = 1000000; - std::vector rects(100, { + std::vector rects(202, { {-9945219, -3065619}, {-9781479, -2031780}, {-9510560, -1020730}, @@ -104,8 +104,6 @@ void arrangeRectangles() { // input.insert(input.end(), prusaExParts().begin(), prusaExParts().end()); // input.insert(input.end(), stegoParts().begin(), stegoParts().end()); // input.insert(input.end(), rects.begin(), rects.end()); -// input.insert(input.end(), proba.begin(), proba.end()); -// input.insert(input.end(), crasher.begin(), crasher.end()); Box bin(250*SCALE, 210*SCALE); // PolygonImpl bin = { @@ -123,11 +121,11 @@ void arrangeRectangles() { // {} // }; -// _Circle bin({0, 0}, 125*SCALE); +// Circle bin({0, 0}, 125*SCALE); - auto min_obj_distance = static_cast(1.5*SCALE); + auto min_obj_distance = static_cast(6*SCALE); - using Placer = strategies::_NofitPolyPlacer; + using Placer = placers::_NofitPolyPlacer; using Packer = Nester; Packer arrange(bin, min_obj_distance); @@ -136,7 +134,7 @@ void arrangeRectangles() { pconf.alignment = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; - pconf.accuracy = 0.5f; + pconf.accuracy = 0.65f; pconf.parallel = true; Packer::SelectionConfig sconf; @@ -149,12 +147,6 @@ void arrangeRectangles() { arrange.configure(pconf, sconf); arrange.progressIndicator([&](unsigned r){ -// svg::SVGWriter::Config conf; -// conf.mm_in_coord_units = SCALE; -// svg::SVGWriter svgw(conf); -// svgw.setSize(bin); -// svgw.writePackGroup(arrange.lastResult()); -// svgw.save("debout"); std::cout << "Remaining items: " << r << std::endl; }); @@ -201,10 +193,10 @@ void arrangeRectangles() { for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); } std::cout << ") Total: " << total << std::endl; - for(auto& it : input) { - auto ret = sl::isValid(it.transformedShape()); - std::cout << ret.second << std::endl; - } +// for(auto& it : input) { +// auto ret = sl::isValid(it.transformedShape()); +// std::cout << ret.second << std::endl; +// } if(total != input.size()) std::cout << "ERROR " << "could not pack " << input.size() - total << " elements!" @@ -222,7 +214,5 @@ void arrangeRectangles() { int main(void /*int argc, char **argv*/) { arrangeRectangles(); -//// findDegenerateCase(); - return EXIT_SUCCESS; } diff --git a/xs/src/libnest2d/libnest2d.h b/xs/src/libnest2d/libnest2d.h index 05677afd7c..bfd88f4f5d 100644 --- a/xs/src/libnest2d/libnest2d.h +++ b/xs/src/libnest2d/libnest2d.h @@ -30,12 +30,12 @@ using Rectangle = _Rectangle; using PackGroup = _PackGroup; using IndexedPackGroup = _IndexedPackGroup; -using FillerSelection = strategies::_FillerSelection; -using FirstFitSelection = strategies::_FirstFitSelection; -using DJDHeuristic = strategies::_DJDHeuristic; +using FillerSelection = selections::_FillerSelection; +using FirstFitSelection = selections::_FirstFitSelection; +using DJDHeuristic = selections::_DJDHeuristic; -using NfpPlacer = strategies::_NofitPolyPlacer; -using BottomLeftPlacer = strategies::_BottomLeftPlacer; +using NfpPlacer = placers::_NofitPolyPlacer; +using BottomLeftPlacer = placers::_BottomLeftPlacer; } diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp index b9dfd21854..2982454cdc 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp @@ -102,7 +102,7 @@ inline TPoint leftmostDownVertex(const RawShape& sh) auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh), __nfp::_vsort); - return *it; + return it == shapelike::cend(sh) ? TPoint() : *it;; } /** @@ -118,7 +118,7 @@ TPoint rightmostUpVertex(const RawShape& sh) auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh), __nfp::_vsort); - return *it; + return it == shapelike::cend(sh) ? TPoint() : *it; } /** diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index 42255cbb48..4d1e62f991 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -65,8 +65,8 @@ class _Item { mutable VertexConstIterator lmb_; // leftmost bottom vertex mutable bool rmt_valid_ = false, lmb_valid_ = false; mutable struct BBCache { - Box bb; bool valid; Vertex tr; - BBCache(): valid(false), tr(0, 0) {} + Box bb; bool valid; + BBCache(): valid(false) {} } bb_cache_; public: @@ -310,7 +310,7 @@ public: { if(translation_ != tr) { translation_ = tr; has_translation_ = true; tr_cache_valid_ = false; - bb_cache_.valid = false; + //bb_cache_.valid = false; } } @@ -345,13 +345,19 @@ public: inline Box boundingBox() const { if(!bb_cache_.valid) { - bb_cache_.bb = sl::boundingBox(transformedShape()); - bb_cache_.tr = {0, 0}; + if(!has_rotation_) + bb_cache_.bb = sl::boundingBox(offsettedShape()); + else { + // TODO make sure this works + auto rotsh = offsettedShape(); + sl::rotate(rotsh, rotation_); + bb_cache_.bb = sl::boundingBox(rotsh); + } bb_cache_.valid = true; } - auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr; - return {bb.minCorner() + tr, bb.maxCorner() + tr}; + auto &bb = bb_cache_.bb; auto &tr = translation_; + return {bb.minCorner() + tr, bb.maxCorner() + tr }; } inline Vertex referenceVertex() const { diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp index 0ba9eb3c06..18c27c40cd 100644 --- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp @@ -5,7 +5,7 @@ #include "placer_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace placers { template struct Epsilon {}; diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index b9e0ba8f16..c86fb507e3 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -21,6 +21,12 @@ #include "tools/svgtools.hpp" +#ifdef USE_TBB +#include +#elif defined(_OPENMP) +#include +#endif + namespace libnest2d { namespace __parallel { @@ -33,20 +39,52 @@ using TIteratorValue = typename iterator_traits::value_type; template inline void enumerate( Iterator from, Iterator to, - function, unsigned)> fn, + function, size_t)> fn, std::launch policy = std::launch::deferred | std::launch::async) { - auto N = to-from; + using TN = size_t; + auto iN = to-from; + TN N = iN < 0? 0 : TN(iN); + +#ifdef USE_TBB + if((policy & std::launch::async) == std::launch::async) { + tbb::parallel_for(0, N, [from, fn] (TN n) { fn(*(from + n), n); } ); + } else { + for(TN n = 0; n < N; n++) fn(*(from + n), n); + } +#elif defined(_OPENMP) + if((policy & std::launch::async) == std::launch::async) { + #pragma omp parallel for + for(TN n = 0; n < N; n++) fn(*(from + n), n); + } + else { + for(TN n = 0; n < N; n++) fn(*(from + n), n); + } +#else std::vector> rets(N); auto it = from; - for(unsigned b = 0; b < N; b++) { - rets[b] = std::async(policy, fn, *it++, b); + for(TN b = 0; b < N; b++) { + rets[b] = std::async(policy, fn, *it++, unsigned(b)); } - for(unsigned fi = 0; fi < rets.size(); ++fi) rets[fi].wait(); + for(TN fi = 0; fi < N; ++fi) rets[fi].wait(); +#endif } +class SpinLock { + static std::atomic_flag locked; +public: + void lock() { + while (locked.test_and_set(std::memory_order_acquire)) { ; } + } + void unlock() { + locked.clear(std::memory_order_release); + } +}; + +std::atomic_flag SpinLock::locked = ATOMIC_FLAG_INIT ; + } namespace __itemhash { @@ -98,7 +136,7 @@ using Hash = std::unordered_map>; } -namespace strategies { +namespace placers { template struct NfpPConfig { @@ -134,30 +172,12 @@ struct NfpPConfig { * that will optimize for the best pack efficiency. With a custom fitting * function you can e.g. influence the shape of the arranged pile. * - * \param shapes The first parameter is a container with all the placed - * polygons excluding the current candidate. You can calculate a bounding - * box or convex hull on this pile of polygons without the candidate item - * or push back the candidate item into the container and then calculate - * some features. - * - * \param item The second parameter is the candidate item. - * - * \param remaining A container with the remaining items waiting to be - * placed. You can use some features about the remaining items to alter to - * score of the current placement. If you know that you have to leave place - * for other items as well, that might influence your decision about where - * the current candidate should be placed. E.g. imagine three big circles - * which you want to place into a box: you might place them in a triangle - * shape which has the maximum pack density. But if there is a 4th big - * circle than you won't be able to pack it. If you knew apriori that - * there four circles are to be placed, you would have placed the first 3 - * into an L shape. This parameter can be used to make these kind of - * decisions (for you or a more intelligent AI). + * \param item The only parameter is the candidate item which has info + * about its current position. Your job is to rate this position compared to + * the already packed items. * */ - std::function&, const _Item&, - const ItemGroup&)> - object_function; + std::function&)> object_function; /** * @brief The quality of search for an optimal placement. @@ -180,6 +200,34 @@ struct NfpPConfig { */ bool parallel = true; + /** + * @brief before_packing Callback that is called just before a search for + * a new item's position is started. You can use this to create various + * cache structures and update them between subsequent packings. + * + * \param merged pile A polygon that is the union of all items in the bin. + * + * \param pile The items parameter is a container with all the placed + * polygons excluding the current candidate. You can for instance check the + * alignment with the candidate item or do anything else. + * + * \param remaining A container with the remaining items waiting to be + * placed. You can use some features about the remaining items to alter to + * score of the current placement. If you know that you have to leave place + * for other items as well, that might influence your decision about where + * the current candidate should be placed. E.g. imagine three big circles + * which you want to place into a box: you might place them in a triangle + * shape which has the maximum pack density. But if there is a 4th big + * circle than you won't be able to pack it. If you knew apriori that + * there four circles are to be placed, you would have placed the first 3 + * into an L shape. This parameter can be used to make these kind of + * decisions (for you or a more intelligent AI). + */ + std::function&, // merged pile + const ItemGroup&, // packed items + const ItemGroup& // remaining items + )> before_packing; + NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}), alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {} }; @@ -428,7 +476,7 @@ Circle minimizeCircle(const RawShape& sh) { opt::StopCriteria stopcr; - stopcr.max_iterations = 100; + stopcr.max_iterations = 30; stopcr.relative_score_difference = 1e-3; opt::TOptimizer solver(stopcr); @@ -590,35 +638,25 @@ private: { using namespace nfp; - Shapes nfps; + Shapes nfps(items_.size()); const Item& trsh = itsh.first; - // nfps.reserve(polygons.size()); - -// unsigned idx = 0; - for(Item& sh : items_) { - -// auto ik = item_keys_[idx++] + itsh.second; -// auto fnd = nfpcache_.find(ik); - -// nfp::NfpResult subnfp_r; -// if(fnd == nfpcache_.end()) { - - auto subnfp_r = noFitPolygon( - sh.transformedShape(), trsh.transformedShape()); -// nfpcache_[ik] = subnfp_r; -// } else { -// subnfp_r = fnd->second; -// } + __parallel::enumerate(items_.begin(), items_.end(), + [&nfps, &trsh](const Item& sh, size_t n) + { + auto& fixedp = sh.transformedShape(); + auto& orbp = trsh.transformedShape(); + auto subnfp_r = noFitPolygon(fixedp, orbp); correctNfpPosition(subnfp_r, sh, trsh); + nfps[n] = subnfp_r.first; + }); - // nfps.emplace_back(subnfp_r.first); - nfps = nfp::merge(nfps, subnfp_r.first); - } +// for(auto& n : nfps) { +// auto valid = sl::isValid(n); +// if(!valid.first) std::cout << "Warning: " << valid.second << std::endl; +// } - // nfps = nfp::merge(nfps); - - return nfps; + return nfp::merge(nfps); } template @@ -777,6 +815,21 @@ private: } }; + 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> @@ -810,6 +863,7 @@ private: item.translation(initial_tr); item.rotation(initial_rot + rot); + item.boundingBox(); // fill the bb cache // place the new item outside of the print bed to make sure // it is disjunct from the current merged pile @@ -840,21 +894,17 @@ private: auto merged_pile = nfp::merge(pile); auto& bin = bin_; double norm = norm_; + auto pbb = sl::boundingBox(merged_pile); + auto binbb = sl::boundingBox(bin); // This is the kernel part of the object function that is // customizable by the library client auto _objfunc = config_.object_function? config_.object_function : - [norm, /*pile_area,*/ bin, merged_pile]( - const Pile& /*pile*/, - const Item& item, - const ItemGroup& /*remaining*/) + [norm, bin, binbb, pbb](const Item& item) { auto ibb = item.boundingBox(); - auto binbb = sl::boundingBox(bin); - auto mp = merged_pile; - mp.emplace_back(item.transformedShape()); - auto fullbb = sl::boundingBox(mp); + auto fullbb = boundingBox(pbb, ibb); double score = pl::distance(ibb.center(), binbb.center()); score /= norm; @@ -868,15 +918,12 @@ private: // Our object function for placement auto rawobjfunc = - [item, _objfunc, iv, - startpos, remlist, pile] (Vertex v) + [_objfunc, iv, startpos] (Vertex v, Item& itm) { auto d = v - iv; d += startpos; - Item itm = item; itm.translation(d); - - return _objfunc(pile, itm, remlist); + return _objfunc(itm); }; auto getNfpPoint = [&ecache](const Optimum& opt) @@ -906,6 +953,9 @@ private: std::launch policy = std::launch::deferred; if(config_.parallel) policy |= std::launch::async; + if(config_.before_packing) + config_.before_packing(merged_pile, items_, remlist); + using OptResult = opt::Result; using OptResults = std::vector; @@ -914,21 +964,27 @@ private: for(unsigned ch = 0; ch < ecache.size(); ch++) { auto& cache = ecache[ch]; - auto contour_ofn = [rawobjfunc, getNfpPoint, ch] - (double relpos) - { - return rawobjfunc(getNfpPoint(Optimum(relpos, ch))); - }; - OptResults results(cache.corners().size()); + auto& rofn = rawobjfunc; + auto& nfpoint = getNfpPoint; + __parallel::enumerate( cache.corners().begin(), cache.corners().end(), - [&contour_ofn, &results] - (double pos, unsigned n) + [&results, &item, &rofn, &nfpoint, ch] + (double pos, size_t n) { Optimizer solver; + + Item itemcpy = item; + auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy] + (double relpos) + { + Optimum op(relpos, ch); + return rofn(nfpoint(op), itemcpy); + }; + try { results[n] = solver.optimize_min(contour_ofn, opt::initvals(pos), @@ -959,24 +1015,27 @@ private: } for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) { - auto hole_ofn = - [rawobjfunc, getNfpPoint, ch, hidx] - (double pos) - { - Optimum opt(pos, ch, hidx); - return rawobjfunc(getNfpPoint(opt)); - }; - results.clear(); results.resize(cache.corners(hidx).size()); // TODO : use parallel for __parallel::enumerate(cache.corners(hidx).begin(), cache.corners(hidx).end(), - [&hole_ofn, &results] - (double pos, unsigned n) + [&results, &item, &nfpoint, + &rofn, ch, hidx] + (double pos, size_t n) { Optimizer solver; + + Item itmcpy = item; + auto hole_ofn = + [&rofn, &nfpoint, ch, hidx, &itmcpy] + (double pos) + { + Optimum opt(pos, ch, hidx); + return rofn(nfpoint(opt), itmcpy); + }; + try { results[n] = solver.optimize_min(hole_ofn, opt::initvals(pos), diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp index 44e2bc1b00..0df1b8c913 100644 --- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp @@ -3,7 +3,7 @@ #include "../libnest2d.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace placers { struct EmptyConfig {}; diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index 846b00badf..ee93d05928 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -8,7 +8,7 @@ #include "selection_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace selections { /** * Selection heuristic based on [López-Camacho]\ diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp index b20455b0e4..0da7220a19 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp @@ -3,7 +3,7 @@ #include "selection_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace selections { template class _FillerSelection: public SelectionBoilerplate { diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index eb820a5184..bca7497db8 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -4,7 +4,7 @@ #include "../libnest2d.hpp" #include "selection_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace selections { template class _FirstFitSelection: public SelectionBoilerplate { diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp index 59ef5cb238..05bbae658e 100644 --- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp @@ -3,8 +3,7 @@ #include "../libnest2d.hpp" -namespace libnest2d { -namespace strategies { +namespace libnest2d { namespace selections { template class SelectionBoilerplate { diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp index b85bbc1112..323fb8d31e 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/xs/src/libnest2d/tests/test.cpp @@ -102,17 +102,17 @@ TEST(BasicFunctionality, creationAndDestruction) TEST(GeometryAlgorithms, boundingCircle) { using namespace libnest2d; - using strategies::boundingCircle; + using placers::boundingCircle; PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}}; - _Circle c = boundingCircle(p); + Circle c = boundingCircle(p); ASSERT_EQ(c.center().X, 0); ASSERT_EQ(c.center().Y, 0); ASSERT_DOUBLE_EQ(c.radius(), 10); shapelike::translate(p, PointImpl{10, 10}); - c = boundingCircle(p); + c = boundingCircle(p); ASSERT_EQ(c.center().X, 10); ASSERT_EQ(c.center().Y, 10); @@ -712,7 +712,7 @@ void testNfp(const std::vector& testdata) { auto& exportfun = exportSVG; - auto onetest = [&](Item& orbiter, Item& stationary){ + auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){ testcase++; orbiter.translate({210*SCALE, 0}); @@ -720,15 +720,19 @@ void testNfp(const std::vector& testdata) { auto&& nfp = nfp::noFitPolygon(stationary.rawShape(), orbiter.transformedShape()); - strategies::correctNfpPosition(nfp, stationary, orbiter); + placers::correctNfpPosition(nfp, stationary, orbiter); - auto v = shapelike::isValid(nfp.first); + auto valid = shapelike::isValid(nfp.first); - if(!v.first) { - std::cout << v.second << std::endl; - } + /*Item infp(nfp.first); + if(!valid.first) { + std::cout << "test instance: " << testidx << " " + << valid.second << std::endl; + std::vector> inp = {std::ref(infp)}; + exportfun(inp, bin, testidx); + }*/ - ASSERT_TRUE(v.first); + ASSERT_TRUE(valid.first); Item infp(nfp.first); @@ -748,7 +752,7 @@ void testNfp(const std::vector& testdata) { bool touching = Item::touches(tmp, stationary); - if(!touching) { + if(!touching || !valid.first) { std::vector> inp = { std::ref(stationary), std::ref(tmp), std::ref(infp) }; @@ -760,16 +764,18 @@ void testNfp(const std::vector& testdata) { } }; + unsigned tidx = 0; for(auto& td : testdata) { auto orbiter = td.orbiter; auto stationary = td.stationary; - onetest(orbiter, stationary); + onetest(orbiter, stationary, tidx++); } + tidx = 0; for(auto& td : testdata) { auto orbiter = td.stationary; auto stationary = td.orbiter; - onetest(orbiter, stationary); + onetest(orbiter, stationary, tidx++); } } } @@ -796,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) { Rectangle input(10, 10); - strategies::EdgeCache ecache(input); + placers::EdgeCache ecache(input); auto first = *input.begin(); ASSERT_TRUE(getX(first) == getX(ecache.coords(0))); diff --git a/xs/src/libnest2d/tools/nfp_svgnest.hpp b/xs/src/libnest2d/tools/nfp_svgnest.hpp index 8ab571c003..ac5700c101 100644 --- a/xs/src/libnest2d/tools/nfp_svgnest.hpp +++ b/xs/src/libnest2d/tools/nfp_svgnest.hpp @@ -32,15 +32,18 @@ template struct _alg { #define dNAN std::nan("") struct Vector { - Coord x, y; + Coord x = 0.0, y = 0.0; bool marked = false; Vector() = default; Vector(Coord X, Coord Y): x(X), y(Y) {} - Vector(const Point& p): x(getX(p)), y(getY(p)) {} + Vector(const Point& p): x(Coord(getX(p))), y(Coord(getY(p))) {} operator Point() const { return {iCoord(x), iCoord(y)}; } Vector& operator=(const Point& p) { x = getX(p), y = getY(p); return *this; } + bool operator!=(const Vector& v) const { + return v.x != x || v.y != y; + } Vector(std::initializer_list il): x(*il.begin()), y(*std::next(il.begin())) {} }; @@ -58,8 +61,10 @@ template struct _alg { v_.reserve(c.size()); std::transform(c.begin(), c.end(), std::back_inserter(v_), [](const Point& p) { - return Vector(double(x(p))/1e6, double(y(p))/1e6); + return Vector(double(x(p)) / 1e6, double(y(p)) / 1e6); }); + std::reverse(v_.begin(), v_.end()); + v_.pop_back(); } Cntr() = default; @@ -67,8 +72,10 @@ template struct _alg { Coord offsety = 0; size_t size() const { return v_.size(); } bool empty() const { return v_.empty(); } - typename Contour::const_iterator begin() const { return v_.cbegin(); } - typename Contour::const_iterator end() const { return v_.cend(); } + typename std::vector::const_iterator cbegin() const { return v_.cbegin(); } + typename std::vector::const_iterator cend() const { return v_.cend(); } + typename std::vector::iterator begin() { return v_.begin(); } + typename std::vector::iterator end() { return v_.end(); } Vector& operator[](size_t idx) { return v_[idx]; } const Vector& operator[](size_t idx) const { return v_[idx]; } template @@ -83,12 +90,16 @@ template struct _alg { operator Contour() const { Contour cnt; - cnt.reserve(v_.size()); + cnt.reserve(v_.size() + 1); std::transform(v_.begin(), v_.end(), std::back_inserter(cnt), [](const Vector& vertex) { - return Point(iCoord(vertex.x*1e6), iCoord(vertex.y*1e6)); + return Point(iCoord(vertex.x) * 1000000, iCoord(vertex.y) * 1000000); }); - return cnt; + if(!cnt.empty()) cnt.emplace_back(cnt.front()); + S sh = shapelike::create(cnt); + +// std::reverse(cnt.begin(), cnt.end()); + return shapelike::getContour(sh); } }; @@ -235,11 +246,11 @@ template struct _alg { if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && (pdotnorm>s1dotnorm && pdotnorm>s2dotnorm)) { - return double(min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm)); + return min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm); } if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && (pdotnorm struct _alg { auto EFmin = min(dotE, dotF); // segments that will merely touch at one point - if(_almostEqual(ABmax, EFmin,TOL) || _almostEqual(ABmin, EFmax,TOL)){ + if(_almostEqual(ABmax, EFmin, TOL) || _almostEqual(ABmin, EFmax,TOL)) { return dNAN; } // segments miss eachother completely @@ -362,7 +373,7 @@ template struct _alg { d = dNAN; } } - if(isnan(d)){ + if(!isnan(d)){ distances.emplace_back(d); } } @@ -392,7 +403,7 @@ template struct _alg { auto d = pointDistance(E,A,B,direction); if(!isnan(d) && _almostEqual(d, 0)) { // crossF struct _alg { if(!isnan(d) && _almostEqual(d, 0)) { // && crossE struct _alg { return *std::min_element(distances.begin(), distances.end()); } - static double polygonSlideDistance( const Cntr& A, - const Cntr& B, + static double polygonSlideDistance( const Cntr& AA, + const Cntr& BB, Vector direction, bool ignoreNegative) { // Vector A1, A2, B1, B2; + Cntr A = AA; + Cntr B = BB; + Coord Aoffsetx = A.offsetx; Coord Boffsetx = B.offsetx; Coord Aoffsety = A.offsety; Coord Boffsety = B.offsety; + // close the loop for polygons + if(A[0] != A[A.size()-1]){ + A.emplace_back(AA[0]); + } + + if(B[0] != B[B.size()-1]){ + B.emplace_back(BB[0]); + } + auto& edgeA = A; auto& edgeB = B; @@ -457,7 +480,7 @@ template struct _alg { Vector A1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety }; Vector A2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety}; Vector B1 = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety }; - Vector B2 = {x(edgeB[i+1]) + Boffsety, y(edgeB[i+1]) + Boffsety}; + Vector B2 = {x(edgeB[i+1]) + Boffsetx, y(edgeB[i+1]) + Boffsety}; if((_almostEqual(A1.x, A2.x) && _almostEqual(A1.y, A2.y)) || (_almostEqual(B1.x, B2.x) && _almostEqual(B1.y, B2.y))){ @@ -476,23 +499,26 @@ template struct _alg { return distance; } - static double polygonProjectionDistance(const Cntr& A, - const Cntr& B, + static double polygonProjectionDistance(const Cntr& AA, + const Cntr& BB, Vector direction) { + Cntr A = AA; + Cntr B = BB; + auto Boffsetx = B.offsetx; auto Boffsety = B.offsety; auto Aoffsetx = A.offsetx; auto Aoffsety = A.offsety; // close the loop for polygons - /*if(A[0] != A[A.length-1]){ + if(A[0] != A[A.size()-1]){ A.push(A[0]); } - if(B[0] != B[B.length-1]){ + if(B[0] != B[B.size()-1]){ B.push(B[0]); - }*/ + } auto& edgeA = A; auto& edgeB = B; @@ -665,7 +691,7 @@ template struct _alg { A.offsetx = 0; A.offsety = 0; - unsigned i = 0, j = 0; + long i = 0, j = 0; auto minA = y(A[0]); long minAindex = 0; @@ -709,7 +735,8 @@ template struct _alg { struct Touch { int type; - long A, B; + long A; + long B; Touch(int t, long a, long b): type(t), A(a), B(b) {} }; @@ -721,6 +748,15 @@ template struct _alg { // maintain a list of touching points/edges std::vector touching; + struct V { + Coord x, y; + Vector *start, *end; + operator bool() { + return start != nullptr && end != nullptr; + } + operator Vector() const { return {x, y}; } + } prevvector = {0, 0, nullptr, nullptr}; + Cntr NFP; NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety); @@ -736,10 +772,10 @@ template struct _alg { // find touching vertices/edges for(i = 0; i < A.size(); i++){ - auto nexti = (i == A.size() - 1) ? 0 : i + 1; + long nexti = (i == A.size() - 1) ? 0 : i + 1; for(j = 0; j < B.size(); j++){ - auto nextj = (j == B.size() - 1) ? 0 : j + 1; + long nextj = (j == B.size() - 1) ? 0 : j + 1; if( _almostEqual(A[i].x, B[j].x+B.offsetx) && _almostEqual(A[i].y, B[j].y+B.offsety)) @@ -762,15 +798,6 @@ template struct _alg { } } - struct V { - Coord x, y; - Vector *start, *end; - operator bool() { - return start != nullptr && end != nullptr; - } - operator Vector() const { return {x, y}; } - }; - // generate translation vectors from touching vertices/edges std::vector vectors; for(i=0; i < touching.size(); i++){ @@ -871,7 +898,6 @@ template struct _alg { // will cause immediate intersection. For now just check them all V translate = {0, 0, nullptr, nullptr}; - V prevvector = {0, 0, nullptr, nullptr}; double maxd = 0; for(i = 0; i < vectors.size(); i++) { @@ -897,7 +923,8 @@ template struct _alg { } } - double d = polygonSlideDistance(A, B, vectors[i], true); + V vi = vectors[i]; + double d = polygonSlideDistance(A, B, vi, true); double vecd2 = vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y; if(isnan(d) || d*d > vecd2){ @@ -985,19 +1012,6 @@ template struct _alg { template const double _alg::TOL = std::pow(10, -9); -//template -//nfp::NfpResult nfpSimpleSimple(const S& stat, const S& orb) { -//// using Cntr = TContour; -// using Point = TPoint; -//// using Coord = TCoord; -//// using Shapes = nfp::Shapes; - -// namespace sl = shapelike; - -// noFitPolygon(sl::getContour(stat), sl::getContour(orb), true, true); -// return {S(), Point()}; -//} - } } diff --git a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp index 7ceb2d24dc..ea1fb4d076 100644 --- a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp +++ b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp @@ -25,13 +25,11 @@ template<> struct NfpImpl { namespace sl = shapelike; using alg = __svgnest::_alg; - std::cout << "Itt vagyok" << std::endl; auto nfp_p = alg::noFitPolygon(sl::getContour(sh), sl::getContour(cother), false, false); PolygonImpl nfp_cntr; - nfp_cntr.Contour = nfp_p.front(); - std::cout << "Contour size: " << nfp_cntr.Contour.size() << std::endl; + if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front(); return {nfp_cntr, referenceVertex(nfp_cntr)}; } }; diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index dcb0da9e5d..618230cb7e 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -100,55 +100,54 @@ 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; + +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); +} std::tuple objfunc(const PointImpl& bincenter, - double bin_area, - sl::Shapes& pile, // The currently arranged pile + const shapelike::Shapes& merged_pile, + const Box& pilebb, + const ItemGroup& items, const Item &item, + double bin_area, double norm, // A norming factor for physical dimensions - std::vector& areacache, // pile item areas will be cached // a spatial index to quickly get neighbors of the candidate item - SpatIndex& spatindex, + const SpatIndex& spatindex, const ItemGroup& remaining ) { using Coord = TCoord; - static const double BIG_ITEM_TRESHOLD = 0.02; static const double ROUNDNESS_RATIO = 0.5; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; // We will treat big items (compared to the print bed) differently - auto isBig = [&areacache, bin_area](double a) { + auto isBig = [bin_area](double a) { return a/bin_area > BIG_ITEM_TRESHOLD ; }; - // If a new bin has been created: - if(pile.size() < areacache.size()) { - areacache.clear(); - spatindex.clear(); - } - - // We must fill the caches: - int idx = 0; - for(auto& p : pile) { - if(idx == areacache.size()) { - areacache.emplace_back(sl::area(p)); - if(isBig(areacache[idx])) - spatindex.insert({sl::boundingBox(p), idx}); - } - - idx++; - } - // Candidate item bounding box - auto ibb = item.boundingBox(); + auto ibb = sl::boundingBox(item.transformedShape()); // Calculate the full bounding box of the pile with the candidate item - pile.emplace_back(item.transformedShape()); - auto fullbb = sl::boundingBox(pile); - pile.pop_back(); + auto fullbb = boundingBox(pilebb, ibb); // The bounding box of the big items (they will accumulate in the center // of the pile @@ -189,10 +188,12 @@ objfunc(const PointImpl& bincenter, double density = 0; if(remaining.empty()) { - pile.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(pile); - pile.pop_back(); - strategies::EdgeCache ec(chull); + + 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; @@ -201,16 +202,15 @@ objfunc(const PointImpl& bincenter, } 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 aligment with all neighbors and + // 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 = (fullbb.width()*fullbb.height()) / (norm*norm); - auto& trsh = item.transformedShape(); auto querybb = item.boundingBox(); - // Query the spatial index for the neigbours + // Query the spatial index for the neighbors std::vector result; result.reserve(spatindex.size()); spatindex.query(bgi::intersects(querybb), @@ -218,10 +218,10 @@ objfunc(const PointImpl& bincenter, for(auto& e : result) { // now get the score for the best alignment auto idx = e.second; - auto& p = pile[idx]; - auto parea = areacache[idx]; + Item& p = items[idx]; + auto parea = p.area(); if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = sl::boundingBox(sl::Shapes{p, trsh}); + auto bb = boundingBox(p.boundingBox(), ibb); auto bbarea = bb.area(); auto ascore = 1.0 - (item.area() + parea)/bbarea; @@ -231,7 +231,7 @@ objfunc(const PointImpl& bincenter, // 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 neigbours + // alignment with the neighbors if(result.empty()) score = 0.5 * dist + 0.5 * density; else @@ -239,7 +239,6 @@ objfunc(const PointImpl& bincenter, } } else if( !isBig(item.area()) && spatindex.empty()) { auto bindist = pl::distance(ibb.center(), bincenter) / norm; - // Bindist is surprisingly enough... score = bindist; } else { @@ -271,7 +270,7 @@ void fillConfig(PConf& pcfg) { // Goes from 0.0 to 1.0 and scales performance as well pcfg.accuracy = 0.65f; - pcfg.parallel = false; + pcfg.parallel = true; } template @@ -280,7 +279,8 @@ class AutoArranger {}; template class _ArrBase { protected: - using Placer = strategies::_NofitPolyPlacer; + + using Placer = TPacker; using Selector = FirstFitSelection; using Packer = Nester; using PConfig = typename Packer::PlacementConfig; @@ -290,10 +290,12 @@ protected: Packer pck_; PConfig pconf_; // Placement configuration double bin_area_; - std::vector areacache_; SpatIndex rtree_; double norm_; - Pile pile_cache_; + Pile merged_pile_; + Box pilebb_; + ItemGroup remaining_; + ItemGroup items_; public: _ArrBase(const TBin& bin, Distance dist, @@ -302,11 +304,35 @@ public: norm_(std::sqrt(sl::area(bin))) { fillConfig(pconf_); + + pconf_.before_packing = + [this](const Pile& merged_pile, // merged pile + const ItemGroup& items, // packed items + const ItemGroup& remaining) // future items to be packed + { + items_ = items; + merged_pile_ = merged_pile; + remaining_ = remaining; + + pilebb_ = sl::boundingBox(merged_pile); + + rtree_.clear(); + + // We will treat big items (compared to the print bed) differently + auto isBig = [this](double a) { + return a/bin_area_ > BIG_ITEM_TRESHOLD ; + }; + + for(unsigned idx = 0; idx < items.size(); ++idx) { + Item& itm = items[idx]; + if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx}); + } + }; + pck_.progressIndicator(progressind); } template inline IndexedPackGroup operator()(Args&&...args) { - areacache_.clear(); rtree_.clear(); return pck_.executeIndexed(std::forward(args)...); } @@ -320,26 +346,28 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { -// pconf_.object_function = [this, bin] ( -// const Pile& pile_c, -// const Item &item, -// const ItemGroup& rem) { -// auto& pile = pile_cache_; -// if(pile.size() != pile_c.size()) pile = pile_c; + pconf_.object_function = [this, bin] (const Item &item) { -// auto result = objfunc(bin.center(), bin_area_, pile, -// item, norm_, areacache_, rtree_, rem); -// double score = std::get<0>(result); -// auto& fullbb = std::get<1>(result); + auto result = objfunc(bin.center(), + merged_pile_, + pilebb_, + items_, + item, + bin_area_, + norm_, + rtree_, + remaining_); -// auto wdiff = fullbb.width() - bin.width(); -// auto hdiff = fullbb.height() - bin.height(); -// if(wdiff > 0) score += std::pow(wdiff, 2) / norm_; -// if(hdiff > 0) score += std::pow(hdiff, 2) / norm_; + double score = std::get<0>(result); + auto& fullbb = std::get<1>(result); -// return score; -// }; + double miss = Placer::overfit(fullbb, bin); + miss = miss > 0? miss : 0; + score += miss*miss; + + return score; + }; pck_.configure(pconf_); } @@ -355,36 +383,31 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { - pconf_.object_function = [this, &bin] ( - const Pile& pile_c, - const Item &item, - const ItemGroup& rem) { + pconf_.object_function = [this, &bin] (const Item &item) { - auto& pile = pile_cache_; - if(pile.size() != pile_c.size()) pile = pile_c; + auto result = objfunc(bin.center(), + merged_pile_, + pilebb_, + items_, + item, + bin_area_, + norm_, + rtree_, + remaining_); - auto result = objfunc(bin.center(), bin_area_, pile, item, norm_, - areacache_, rtree_, rem); double score = std::get<0>(result); - auto& fullbb = std::get<1>(result); - auto d = pl::distance(fullbb.minCorner(), - fullbb.maxCorner()); - auto diff = d - 2*bin.radius(); + auto isBig = [this](const Item& itm) { + return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ; + }; - if(diff > 0) { - if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) { - pile.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(pile); - pile.pop_back(); - - auto C = strategies::boundingCircle(chull); - auto rdiff = C.radius() - bin.radius(); - - if(rdiff > 0) { - score += std::pow(rdiff, 3) / norm_; - } - } + if(isBig(item)) { + auto mp = 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; @@ -401,17 +424,18 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { - pconf_.object_function = [this, &bin] ( - const Pile& pile_c, - const Item &item, - const ItemGroup& rem) { - - auto& pile = pile_cache_; - if(pile.size() != pile_c.size()) pile = pile_c; + pconf_.object_function = [this, &bin] (const Item &item) { auto binbb = sl::boundingBox(bin); - auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_, - areacache_, rtree_, rem); + auto result = objfunc(binbb.center(), + merged_pile_, + pilebb_, + items_, + item, + bin_area_, + norm_, + rtree_, + remaining_); double score = std::get<0>(result); return score; @@ -428,16 +452,17 @@ public: AutoArranger(Distance dist, std::function progressind): _ArrBase(Box(0, 0), dist, progressind) { - this->pconf_.object_function = [this] ( - const Pile& pile_c, - const Item &item, - const ItemGroup& rem) { + this->pconf_.object_function = [this] (const Item &item) { - auto& pile = pile_cache_; - if(pile.size() != pile_c.size()) pile = pile_c; - - auto result = objfunc({0, 0}, 0, pile, item, norm_, - areacache_, rtree_, rem); + auto result = objfunc({0, 0}, + merged_pile_, + pilebb_, + items_, + item, + 0, + norm_, + rtree_, + remaining_); return std::get<0>(result); }; From 5ee106fbf9baf114619f2622ef9e991c73a09667 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 29 Aug 2018 08:42:42 +0200 Subject: [PATCH 11/50] Fixed transformation center for objects imported from 3mf files --- xs/src/libslic3r/Format/3mf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 5de1d26c5f..945bb1f868 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -603,6 +603,8 @@ namespace Slic3r { if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr)) return false; + + object.second->center_around_origin(); } // fixes the min z of the model if negative From 3750bd781eb2b48bc9ac6ac5acf8ce1bf99da0be Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 29 Aug 2018 13:36:03 +0200 Subject: [PATCH 12/50] Enhanced gizmo scale 3D --- xs/src/slic3r/GUI/GLGizmo.cpp | 86 ++++++++++++++++++++++++----------- xs/src/slic3r/GUI/GLGizmo.hpp | 6 ++- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 1dcf35de05..be085e913a 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -617,7 +617,7 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent) : GLGizmoBase(parent) , m_scale(Vec3d::Ones()) , m_starting_scale(Vec3d::Ones()) - , m_starting_center(Vec3d::Zero()) + , m_show_starting_box(false) { } @@ -660,7 +660,8 @@ void GLGizmoScale3D::on_start_dragging() if (m_hover_id != -1) { m_starting_drag_position = m_grabbers[m_hover_id].center; - m_starting_center = m_box.center(); + m_show_starting_box = true; + m_starting_box = m_box; } } @@ -731,16 +732,25 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const if (m_hover_id == -1) { - ::glColor3fv(m_base_color); // draw box - render_box(); + ::glColor3fv(m_base_color); + render_box(m_box); // draw grabbers render_grabbers(); } else if ((m_hover_id == 0) || (m_hover_id == 1)) { - ::glColor3fv(m_grabbers[0].color); + // draw starting box + if (m_show_starting_box) + { + ::glColor3fv(m_base_color); + render_box(m_starting_box); + } + // draw current box + ::glColor3fv(m_drag_color); + render_box(m_box); // draw connection + ::glColor3fv(m_grabbers[0].color); render_grabbers_connection(0, 1); // draw grabbers m_grabbers[0].render(true); @@ -748,8 +758,17 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const } else if ((m_hover_id == 2) || (m_hover_id == 3)) { - ::glColor3fv(m_grabbers[2].color); + // draw starting box + if (m_show_starting_box) + { + ::glColor3fv(m_base_color); + render_box(m_starting_box); + } + // draw current box + ::glColor3fv(m_drag_color); + render_box(m_box); // draw connection + ::glColor3fv(m_grabbers[2].color); render_grabbers_connection(2, 3); // draw grabbers m_grabbers[2].render(true); @@ -757,8 +776,17 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const } else if ((m_hover_id == 4) || (m_hover_id == 5)) { - ::glColor3fv(m_grabbers[4].color); + // draw starting box + if (m_show_starting_box) + { + ::glColor3fv(m_base_color); + render_box(m_starting_box); + } + // draw current box + ::glColor3fv(m_drag_color); + render_box(m_box); // draw connection + ::glColor3fv(m_grabbers[4].color); render_grabbers_connection(4, 5); // draw grabbers m_grabbers[4].render(true); @@ -766,9 +794,15 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const } else if (m_hover_id >= 6) { + // draw starting box + if (m_show_starting_box) + { + ::glColor3fv(m_base_color); + render_box(m_starting_box); + } + // draw current box ::glColor3fv(m_drag_color); - // draw box - render_box(); + render_box(m_box); // draw grabbers for (int i = 6; i < 10; ++i) { @@ -791,30 +825,30 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const render_grabbers_for_picking(); } -void GLGizmoScale3D::render_box() const +void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const { // bottom face ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2)); - ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2)); - ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2)); - ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2)); + ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); + ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); + ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); + ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glEnd(); // top face ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2)); - ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2)); - ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2)); - ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2)); + ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); + ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); + ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); + ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); ::glEnd(); // vertical edges ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2)); - ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2)); - ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2)); - ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2)); + ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); + ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); + ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); + ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); ::glEnd(); } @@ -832,7 +866,7 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray) { - double ratio = calc_ratio(1, mouse_ray, m_starting_center); + double ratio = calc_ratio(1, mouse_ray, m_starting_box.center()); if (ratio > 0.0) m_scale(0) = m_starting_scale(0) * ratio; @@ -840,7 +874,7 @@ void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray) void GLGizmoScale3D::do_scale_y(const Linef3& mouse_ray) { - double ratio = calc_ratio(2, mouse_ray, m_starting_center); + double ratio = calc_ratio(2, mouse_ray, m_starting_box.center()); if (ratio > 0.0) m_scale(0) = m_starting_scale(1) * ratio; // << this is temporary @@ -849,7 +883,7 @@ void GLGizmoScale3D::do_scale_y(const Linef3& mouse_ray) void GLGizmoScale3D::do_scale_z(const Linef3& mouse_ray) { - double ratio = calc_ratio(1, mouse_ray, m_starting_center); + double ratio = calc_ratio(1, mouse_ray, m_starting_box.center()); if (ratio > 0.0) m_scale(0) = m_starting_scale(2) * ratio; // << this is temporary @@ -858,7 +892,7 @@ void GLGizmoScale3D::do_scale_z(const Linef3& mouse_ray) void GLGizmoScale3D::do_scale_uniform(const Linef3& mouse_ray) { - Vec3d center = m_starting_center; + Vec3d center = m_starting_box.center(); center(2) = m_box.min(2); double ratio = calc_ratio(0, mouse_ray, center); diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 35335c1a87..3041f2adf4 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -230,7 +230,8 @@ class GLGizmoScale3D : public GLGizmoBase Vec3d m_starting_scale; Vec3d m_starting_drag_position; - Vec3d m_starting_center; + bool m_show_starting_box; + BoundingBoxf3 m_starting_box; public: explicit GLGizmoScale3D(GLCanvas3D& parent); @@ -249,12 +250,13 @@ public: protected: virtual bool on_init(); virtual void on_start_dragging(); + virtual void on_stop_dragging() { m_show_starting_box = false; } virtual void on_update(const Linef3& mouse_ray); virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; private: - void render_box() const; + void render_box(const BoundingBoxf3& box) const; void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; void do_scale_x(const Linef3& mouse_ray); From 9e2d48ff3b5ad09a65b8039ed9cd6bd9d40db488 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Aug 2018 18:02:10 +0200 Subject: [PATCH 13/50] Almost working c++ status bar Signed-off-by: tamasmeszaros --- lib/Slic3r/GUI/MainFrame.pm | 9 +- lib/Slic3r/GUI/ProgressStatusBar.pm | 232 +++++++++--------- xs/CMakeLists.txt | 5 +- xs/src/perlglue.cpp | 1 + xs/src/slic3r/AppController.cpp | 64 +++-- xs/src/slic3r/AppController.hpp | 4 +- xs/src/slic3r/AppControllerWx.cpp | 14 +- xs/src/slic3r/GUI/ProgressStatusBar.cpp | 156 ++++++++++++ xs/src/slic3r/GUI/ProgressStatusBar.hpp | 61 +++++ ...essIndicator.hpp => ProgressIndicator.hpp} | 4 +- xs/xsp/ProgressStatusBar.xsp | 41 ++++ xs/xsp/my.map | 6 +- xs/xsp/typemap.xspt | 1 + 13 files changed, 442 insertions(+), 156 deletions(-) create mode 100644 xs/src/slic3r/GUI/ProgressStatusBar.cpp create mode 100644 xs/src/slic3r/GUI/ProgressStatusBar.hpp rename xs/src/slic3r/{IProgressIndicator.hpp => ProgressIndicator.hpp} (96%) create mode 100644 xs/xsp/ProgressStatusBar.xsp diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 6baefa545a..6b932f92f2 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -62,13 +62,14 @@ sub new { eval { Wx::ToolTip::SetAutoPop(32767) }; # initialize status bar - $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, Wx::NewId); + $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new(); + # $self->{statusbar}->SetParent($self, Wx::NewId); + $self->{statusbar}->Embed; $self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")); - $self->SetStatusBar($self->{statusbar}); - + # Make the global status bar and its progress indicator available in C++ $appController->set_global_progress_indicator( - $self->{statusbar}->{prog}->GetId(), + $self->{statusbar}->GetProgId(), $self->{statusbar}->GetId(), ); diff --git a/lib/Slic3r/GUI/ProgressStatusBar.pm b/lib/Slic3r/GUI/ProgressStatusBar.pm index 32fd526800..a696feb7f9 100644 --- a/lib/Slic3r/GUI/ProgressStatusBar.pm +++ b/lib/Slic3r/GUI/ProgressStatusBar.pm @@ -4,141 +4,143 @@ package Slic3r::GUI::ProgressStatusBar; use strict; use warnings; -use Wx qw(:gauge :misc); -use base 'Wx::StatusBar'; +# use Wx qw(:gauge :misc); +# use base 'Wx::StatusBar'; -sub new { - my $class = shift; - my $self = $class->SUPER::new(@_); - - $self->{busy} = 0; - $self->{timer} = Wx::Timer->new($self); - $self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize); - $self->{prog}->Hide; - $self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize); - $self->{cancelbutton}->Hide; - - $self->SetFieldsCount(3); - $self->SetStatusWidths(-1, 150, 155); - - Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer}); - Wx::Event::EVT_SIZE($self, \&OnSize); - Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub { - $self->{cancel_cb}->(); - $self->{cancelbutton}->Hide; - }); - - return $self; -} +our $cancel_cb; -sub DESTROY { - my $self = shift; - $self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning; -} - -sub OnSize { - my ($self, $event) = @_; +# sub new { +# my $class = shift; +# my $self = $class->SUPER::new(@_); - my %fields = ( - # 0 is reserved for status text - 1 => $self->{cancelbutton}, - 2 => $self->{prog}, - ); - - foreach (keys %fields) { - my $rect = $self->GetFieldRect($_); - my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK - my $pos = [$rect->GetX + $offset, $rect->GetY + $offset]; - $fields{$_}->Move($pos); - $fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight); - } - - $event->Skip; -} - -sub OnTimer { - my ($self, $event) = @_; +# $self->{busy} = 0; +# $self->{timer} = Wx::Timer->new($self); +# $self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize); +# $self->{prog}->Hide; +# $self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize); +# $self->{cancelbutton}->Hide; - if ($self->{prog}->IsShown) { - $self->{timer}->Stop; - } - $self->{prog}->Pulse if $self->{_busy}; -} +# $self->SetFieldsCount(3); +# $self->SetStatusWidths(-1, 150, 155); + +# Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer}); +# Wx::Event::EVT_SIZE($self, \&OnSize); +# Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub { +# $self->{cancel_cb}->(); +# $self->{cancelbutton}->Hide; +# }); + +# return $self; +# } + +# sub DESTROY { +# my $self = shift; +# $self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning; +# } + +# sub OnSize { +# my ($self, $event) = @_; + +# my %fields = ( +# # 0 is reserved for status text +# 1 => $self->{cancelbutton}, +# 2 => $self->{prog}, +# ); + +# foreach (keys %fields) { +# my $rect = $self->GetFieldRect($_); +# my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK +# my $pos = [$rect->GetX + $offset, $rect->GetY + $offset]; +# $fields{$_}->Move($pos); +# $fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight); +# } + +# $event->Skip; +# } + +# sub OnTimer { +# my ($self, $event) = @_; + +# if ($self->{prog}->IsShown) { +# $self->{timer}->Stop; +# } +# $self->{prog}->Pulse if $self->{_busy}; +# } sub SetCancelCallback { my $self = shift; my ($cb) = @_; - $self->{cancel_cb} = $cb; - $cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide; + $cancel_cb = $cb; + # $cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide; } -sub Run { - my $self = shift; - my $rate = shift || 100; - if (!$self->{timer}->IsRunning) { - $self->{timer}->Start($rate); - } -} +# sub Run { +# my $self = shift; +# my $rate = shift || 100; +# if (!$self->{timer}->IsRunning) { +# $self->{timer}->Start($rate); +# } +# } -sub GetProgress { - my $self = shift; - return $self->{prog}->GetValue; -} +# sub GetProgress { +# my $self = shift; +# return $self->{prog}->GetValue; +# } -sub SetProgress { - my $self = shift; - my ($val) = @_; - if (!$self->{prog}->IsShown) { - $self->ShowProgress(1); - } - if ($val == $self->{prog}->GetRange) { - $self->{prog}->SetValue(0); - $self->ShowProgress(0); - } else { - $self->{prog}->SetValue($val); - } -} +# sub SetProgress { +# my $self = shift; +# my ($val) = @_; +# if (!$self->{prog}->IsShown) { +# $self->ShowProgress(1); +# } +# if ($val == $self->{prog}->GetRange) { +# $self->{prog}->SetValue(0); +# $self->ShowProgress(0); +# } else { +# $self->{prog}->SetValue($val); +# } +# } -sub SetRange { - my $self = shift; - my ($val) = @_; +# sub SetRange { +# my $self = shift; +# my ($val) = @_; - if ($val != $self->{prog}->GetRange) { - $self->{prog}->SetRange($val); - } -} +# if ($val != $self->{prog}->GetRange) { +# $self->{prog}->SetRange($val); +# } +# } -sub ShowProgress { - my $self = shift; - my ($show) = @_; +# sub ShowProgress { +# my $self = shift; +# my ($show) = @_; - $self->{prog}->Show($show); - $self->{prog}->Pulse; -} +# $self->{prog}->Show($show); +# $self->{prog}->Pulse; +# } -sub StartBusy { - my $self = shift; - my $rate = shift || 100; +# sub StartBusy { +# my $self = shift; +# my $rate = shift || 100; - $self->{_busy} = 1; - $self->ShowProgress(1); - if (!$self->{timer}->IsRunning) { - $self->{timer}->Start($rate); - } -} +# $self->{_busy} = 1; +# $self->ShowProgress(1); +# if (!$self->{timer}->IsRunning) { +# $self->{timer}->Start($rate); +# } +# } -sub StopBusy { - my $self = shift; +# sub StopBusy { +# my $self = shift; - $self->{timer}->Stop; - $self->ShowProgress(0); - $self->{prog}->SetValue(0); - $self->{_busy} = 0; -} +# $self->{timer}->Stop; +# $self->ShowProgress(0); +# $self->{prog}->SetValue(0); +# $self->{_busy} = 0; +# } -sub IsBusy { - my $self = shift; - return $self->{_busy}; -} +# sub IsBusy { +# my $self = shift; +# return $self->{_busy}; +# } 1; diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 0ab4648eb9..2ebcb2d456 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -247,6 +247,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp ${LIBDIR}/slic3r/GUI/FirmwareDialog.cpp ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp + ${LIBDIR}/slic3r/GUI/ProgressStatusBar.hpp + ${LIBDIR}/slic3r/GUI/ProgressStatusBar.cpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp @@ -267,7 +269,7 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/Time.hpp ${LIBDIR}/slic3r/Utils/HexFile.cpp ${LIBDIR}/slic3r/Utils/HexFile.hpp - ${LIBDIR}/slic3r/IProgressIndicator.hpp + ${LIBDIR}/slic3r/ProgressIndicator.hpp ${LIBDIR}/slic3r/AppController.hpp ${LIBDIR}/slic3r/AppController.cpp ${LIBDIR}/slic3r/AppControllerWx.cpp @@ -420,6 +422,7 @@ set(XS_XSP_FILES ${XSP_DIR}/Utils_PrintHost.xsp ${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/AppController.xsp + ${XSP_DIR}/ProgressStatusBar.xsp ${XSP_DIR}/XS.xsp ) foreach (file ${XS_XSP_FILES}) diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d6bd0e94c3..f0c0f4f7e0 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -63,6 +63,7 @@ REGISTER_CLASS(Preset, "GUI::Preset"); REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(TabIface, "GUI::Tab"); +REGISTER_CLASS(ProgressStatusBar, "GUI::ProgressStatusBar"); REGISTER_CLASS(PresetUpdater, "PresetUpdater"); REGISTER_CLASS(AppController, "AppController"); REGISTER_CLASS(PrintController, "PrintController"); diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 58858f5fc4..2f1e9a148a 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -290,26 +290,22 @@ const PrintConfig &PrintController::config() const void AppController::arrange_model() { - auto ftr = std::async( - supports_asynch()? std::launch::async : std::launch::deferred, - [this]() - { - using Coord = libnest2d::TCoord; + using Coord = libnest2d::TCoord; - unsigned count = 0; - for(auto obj : model_->objects) count += obj->instances.size(); + unsigned count = 0; + for(auto obj : model_->objects) count += obj->instances.size(); - auto pind = global_progress_indicator(); + auto pind = global_progress_indicator(); - float pmax = 1.0; + float pmax = 1.0; - if(pind) { - pmax = pind->max(); + if(pind) { + pmax = pind->max(); - // Set the range of the progress to the object count - pind->max(count); + // Set the range of the progress to the object count + pind->max(count); - } + } auto dist = print_ctl()->config().min_object_distance(); @@ -341,16 +337,38 @@ void AppController::arrange_model() _(L("Exception occurred"))); } - // Restore previous max value - if(pind) { - pind->max(pmax); - pind->update(0, _(L("Arranging done."))); - } - }); + // Create the arranger config + auto min_obj_distance = static_cast(dist/SCALING_FACTOR); - while( ftr.wait_for(std::chrono::milliseconds(10)) - != std::future_status::ready) { - process_events(); + auto& bedpoints = print_ctl()->config().bed_shape.values; + Polyline bed; bed.points.reserve(bedpoints.size()); + for(auto& v : bedpoints) + bed.append(Point::new_scale(v(0), v(1))); + + if(pind) pind->update(0, _(L("Arranging objects..."))); + + try { + arr::arrange(*model_, + min_obj_distance, + bed, + arr::BOX, + false, // create many piles not just one pile + [pind, count](unsigned rem) { + if(pind) + pind->update(count - rem, _(L("Arranging objects..."))); + }); + } catch(std::exception& e) { + std::cerr << e.what() << std::endl; + report_issue(IssueType::ERR, + _(L("Could not arrange model objects! " + "Some geometries may be invalid.")), + _(L("Exception occurred"))); + } + + // Restore previous max value + if(pind) { + pind->max(pmax); + pind->update(0, _(L("Arranging done."))); } } diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index e9252b50cb..58681a7de5 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -7,7 +7,7 @@ #include #include -#include "IProgressIndicator.hpp" +#include "ProgressIndicator.hpp" namespace Slic3r { @@ -33,7 +33,7 @@ class AppControllerBoilerplate { public: /// A Progress indicator object smart pointer - using ProgresIndicatorPtr = std::shared_ptr; + using ProgresIndicatorPtr = std::shared_ptr; private: class PriData; // Some structure to store progress indication data diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 4e116c7b91..64d783356d 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -104,10 +104,10 @@ namespace { * the main thread as well. */ class GuiProgressIndicator: - public IProgressIndicator, public wxEvtHandler { + public ProgressIndicator, public wxEvtHandler { wxProgressDialog gauge_; - using Base = IProgressIndicator; + using Base = ProgressIndicator; wxString message_; int range_; wxString title_; bool is_asynch_ = false; @@ -153,7 +153,7 @@ public: virtual void cancel() override { update(max(), "Abort"); - IProgressIndicator::cancel(); + ProgressIndicator::cancel(); } virtual void state(float val) override { @@ -211,10 +211,10 @@ AppControllerBoilerplate::create_progress_indicator(unsigned statenum, namespace { // A wrapper progress indicator class around the statusbar created in perl. -class Wrapper: public IProgressIndicator, public wxEvtHandler { +class Wrapper: public ProgressIndicator, public wxEvtHandler { wxGauge *gauge_; wxStatusBar *stbar_; - using Base = IProgressIndicator; + using Base = ProgressIndicator; std::string message_; AppControllerBoilerplate& ctl_; @@ -223,7 +223,7 @@ class Wrapper: public IProgressIndicator, public wxEvtHandler { } void _state(unsigned st) { - if( st <= IProgressIndicator::max() ) { + if( st <= ProgressIndicator::max() ) { Base::state(st); if(!gauge_->IsShown()) showProgress(true); @@ -266,7 +266,7 @@ public: virtual void max(float val) override { if(val > 1.0) { gauge_->SetRange(static_cast(val)); - IProgressIndicator::max(val); + ProgressIndicator::max(val); } } diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp new file mode 100644 index 0000000000..13ca8cd812 --- /dev/null +++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp @@ -0,0 +1,156 @@ +#include "ProgressStatusBar.hpp" + +#include +#include +#include +#include +#include + +#include "GUI.hpp" + +#include + +namespace Slic3r { + +ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): + self(new wxStatusBar(parent ? parent : GUI::get_main_frame(), + id == -1? wxID_ANY : id)), + timer_(new wxTimer(self)), + prog_ (new wxGauge(self, + wxGA_HORIZONTAL, + 100, + wxDefaultPosition, + wxDefaultSize)), + cancelbutton_(new wxButton(self, + -1, + "Cancel", + wxDefaultPosition, + wxDefaultSize)) +{ + prog_->Hide(); + cancelbutton_->Hide(); + + + self->SetFieldsCount(3); + int w[] = {-1, 150, 155}; + self->SetStatusWidths(3, w); + + self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) { + if (prog_->IsShown()) timer_->Stop(); + if(is_busy()) prog_->Pulse(); + }); + + self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){ + wxRect rect; + self->GetFieldRect(1, rect); + auto offset = 0; + cancelbutton_->Move(rect.GetX() + offset, rect.GetY() + offset); + cancelbutton_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + + self->GetFieldRect(2, rect); + prog_->Move(rect.GetX() + offset, rect.GetY() + offset); + prog_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + + event.Skip(); + }); + + cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { + if(cancel_cb_) cancel_cb_(); + cancelbutton_->Hide(); + }); +} + +//ProgressStatusBar::ProgressStatusBar(): ProgressStatusBar(nullptr, wxID_ANY) +//{ + +//} + +ProgressStatusBar::~ProgressStatusBar() { + if(timer_->IsRunning()) timer_->Stop(); +} + +int ProgressStatusBar::get_progress() const +{ + return prog_->GetValue(); +} + +void ProgressStatusBar::set_progress(int val) +{ + if(!prog_->IsShown()) show_progress(true); + + if(val == prog_->GetRange()) { + prog_->SetValue(0); + show_progress(false); + } else { + prog_->SetValue(val); + } +} + +void ProgressStatusBar::set_range(int val) +{ + if(val != prog_->GetRange()) { + prog_->SetRange(val); + } +} + +void ProgressStatusBar::show_progress(bool show) +{ + prog_->Show(show); + prog_->Pulse(); +} + +void ProgressStatusBar::start_busy(int rate) +{ + busy_ = true; + show_progress(true); + if (!timer_->IsRunning()) { + timer_->Start(rate); + } +} + +void ProgressStatusBar::stop_busy() +{ + timer_->Stop(); + show_progress(false); + prog_->SetValue(0); + busy_ = false; +} + +void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) { + cancel_cb_ = ccb; + if(ccb) cancelbutton_->Show(); + else cancelbutton_->Hide(); +} + +void ProgressStatusBar::run(int rate) +{ + if(!timer_->IsRunning()) { + timer_->Start(rate); + } +} + +void ProgressStatusBar::Embed() +{ + std::cout << "Embedding" << std::endl; + wxFrame* mf = GUI::get_main_frame(); + std::cout << mf->GetName() << std::endl; + std::cout << self->GetName() << std::endl; + mf->SetStatusBar(self); +} + +void ProgressStatusBar::SetStatusText(std::string txt) +{ + self->SetStatusText(txt); +} + +int ProgressStatusBar::GetId() +{ + return self->GetId(); +} + +int ProgressStatusBar::GetProgId() +{ + return prog_->GetId(); +} + +} diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp new file mode 100644 index 0000000000..53366aace8 --- /dev/null +++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp @@ -0,0 +1,61 @@ +#ifndef PROGRESSSTATUSBAR_HPP +#define PROGRESSSTATUSBAR_HPP + +#include +#include + +class wxTimer; +class wxGauge; +class wxButton; +class wxTimerEvent; +class wxStatusBar; +class wxWindow; + +namespace Slic3r { + +/** + * @brief The ProgressStatusBar class is the widgets occupying the lower area + * of the Slicer main window. It consists of a message area to the left and a + * progress indication area to the right with an optional cancel button. + */ +class ProgressStatusBar { + wxStatusBar *self; // we cheat! It should be the base class but: perl! + wxTimer *timer_; + wxGauge *prog_; + wxButton *cancelbutton_; +public: + + /// Cancel callback function type + using CancelFn = std::function; + + ProgressStatusBar(wxWindow *parent = nullptr, int id = -1); + ~ProgressStatusBar(); + + int get_progress() const; + void set_progress(int); + void set_range(int = 100); + void show_progress(bool); + void start_busy(int = 100); + void stop_busy(); + inline bool is_busy() const { return busy_; } + void set_cancel_callback(CancelFn); + void run(int rate); + + // Temporary methods to satisfy Perl side + void Embed(); + void SetStatusText(std::string txt); + int GetId(); + int GetProgId(); + +private: + bool busy_ = false; + CancelFn cancel_cb_; +}; + +namespace GUI { + using Slic3r::ProgressStatusBar; +} + +} + +#endif // PROGRESSSTATUSBAR_HPP diff --git a/xs/src/slic3r/IProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp similarity index 96% rename from xs/src/slic3r/IProgressIndicator.hpp rename to xs/src/slic3r/ProgressIndicator.hpp index 7049315741..49d0c251ce 100644 --- a/xs/src/slic3r/IProgressIndicator.hpp +++ b/xs/src/slic3r/ProgressIndicator.hpp @@ -10,7 +10,7 @@ namespace Slic3r { /** * @brief Generic progress indication interface. */ -class IProgressIndicator { +class ProgressIndicator { public: using CancelFn = std::function; // Cancel functio signature. @@ -20,7 +20,7 @@ private: public: - inline virtual ~IProgressIndicator() {} + inline virtual ~ProgressIndicator() {} /// Get the maximum of the progress range. float max() const { return max_; } diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp new file mode 100644 index 0000000000..3b9be3ba8f --- /dev/null +++ b/xs/xsp/ProgressStatusBar.xsp @@ -0,0 +1,41 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/ProgressStatusBar.hpp" +%} + +%name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar { + ProgressStatusBar(); + ~ProgressStatusBar(); + + int GetProgress() const + %code%{ RETVAL=THIS->get_progress(); %}; + + void SetProgress(int val) + %code%{ THIS->set_progress(val); %}; + + void SetRange(int val = 100) + %code%{ THIS->set_range(val); %}; + + void ShowProgress(bool show) + %code%{ THIS->show_progress(show); %}; + + void StartBusy(int val = 100) + %code%{ THIS->start_busy(val); %}; + + void StopBusy() + %code%{ THIS->stop_busy(); %}; + + bool IsBusy() const + %code%{ RETVAL=THIS->is_busy(); %}; + + void Run(int rate) + %code%{ THIS->run(rate); %}; + + void Embed(); + void SetStatusText(std::string txt); + int GetId(); + int GetProgId(); + +}; \ No newline at end of file diff --git a/xs/xsp/my.map b/xs/xsp/my.map index ba20ee2362..e92db33463 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -233,8 +233,10 @@ PresetCollection* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T PresetBundle* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T -TabIface* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T +TabIface* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +ProgressStatusBar* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T PresetUpdater* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index cee75fe261..1654bf3b07 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -213,6 +213,7 @@ %typemap{PresetHints*}; %typemap{Ref}{simple}; %typemap{TabIface*}; +%typemap{ProgressStatusBar*}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*}; From 15ad0ef2f2b3f93d1e0e2d0c375bcebc20ad4b85 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 30 Aug 2018 11:40:06 +0200 Subject: [PATCH 14/50] working C++ status bar. Signed-off-by: tamasmeszaros --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/MainFrame.pm | 6 +- lib/Slic3r/GUI/Plater.pm | 4 +- lib/Slic3r/GUI/ProgressStatusBar.pm | 136 +----------------------- xs/src/slic3r/AppController.cpp | 7 +- xs/src/slic3r/AppController.hpp | 4 +- xs/src/slic3r/AppControllerWx.cpp | 45 ++++---- xs/src/slic3r/GUI/ProgressStatusBar.cpp | 28 +++-- xs/src/slic3r/GUI/ProgressStatusBar.hpp | 13 ++- xs/src/slic3r/ProgressIndicator.hpp | 14 +-- xs/xsp/AppController.xsp | 3 +- xs/xsp/ProgressStatusBar.xsp | 15 ++- 12 files changed, 75 insertions(+), 201 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 46627311ff..4a178682c7 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -166,6 +166,7 @@ sub thread_cleanup { *Slic3r::GUI::Tab::DESTROY = sub {}; *Slic3r::GUI::PresetHints::DESTROY = sub {}; *Slic3r::GUI::TabIface::DESTROY = sub {}; + *Slic3r::GUI::ProgressStatusBar::DESTROY= sub {}; *Slic3r::OctoPrint::DESTROY = sub {}; *Slic3r::Duet::DESTROY = sub {}; *Slic3r::PresetUpdater::DESTROY = sub {}; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 6b932f92f2..50aa3e51d7 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -63,15 +63,11 @@ sub new { # initialize status bar $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new(); - # $self->{statusbar}->SetParent($self, Wx::NewId); $self->{statusbar}->Embed; $self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")); # Make the global status bar and its progress indicator available in C++ - $appController->set_global_progress_indicator( - $self->{statusbar}->GetProgId(), - $self->{statusbar}->GetId(), - ); + $appController->set_global_progress_indicator($self->{statusbar}); $appController->set_model($self->{plater}->{model}); $appController->set_print($self->{plater}->{print}); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ee2689d164..ffc3b9640a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1569,7 +1569,9 @@ sub on_progress_event { my ($self, $percent, $message) = @_; $self->statusbar->SetProgress($percent); - $self->statusbar->SetStatusText("$message…"); + # TODO: three dot character is not properly translated into C++ + # $self->statusbar->SetStatusText("$message…"); + $self->statusbar->SetStatusText("$message..."); } # Called when the G-code export finishes, either successfully or with an error. diff --git a/lib/Slic3r/GUI/ProgressStatusBar.pm b/lib/Slic3r/GUI/ProgressStatusBar.pm index a696feb7f9..edc0c0ce33 100644 --- a/lib/Slic3r/GUI/ProgressStatusBar.pm +++ b/lib/Slic3r/GUI/ProgressStatusBar.pm @@ -1,146 +1,18 @@ # Status bar at the bottom of the main screen. +# Now it just implements cancel cb on perl side, every other functionality is +# in C++ -package Slic3r::GUI::ProgressStatusBar; +package Slic3r::GUI::ProgressStatusBar; use strict; use warnings; -# use Wx qw(:gauge :misc); -# use base 'Wx::StatusBar'; - our $cancel_cb; -# sub new { -# my $class = shift; -# my $self = $class->SUPER::new(@_); - -# $self->{busy} = 0; -# $self->{timer} = Wx::Timer->new($self); -# $self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize); -# $self->{prog}->Hide; -# $self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize); -# $self->{cancelbutton}->Hide; - -# $self->SetFieldsCount(3); -# $self->SetStatusWidths(-1, 150, 155); - -# Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer}); -# Wx::Event::EVT_SIZE($self, \&OnSize); -# Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub { -# $self->{cancel_cb}->(); -# $self->{cancelbutton}->Hide; -# }); - -# return $self; -# } - -# sub DESTROY { -# my $self = shift; -# $self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning; -# } - -# sub OnSize { -# my ($self, $event) = @_; - -# my %fields = ( -# # 0 is reserved for status text -# 1 => $self->{cancelbutton}, -# 2 => $self->{prog}, -# ); - -# foreach (keys %fields) { -# my $rect = $self->GetFieldRect($_); -# my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK -# my $pos = [$rect->GetX + $offset, $rect->GetY + $offset]; -# $fields{$_}->Move($pos); -# $fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight); -# } - -# $event->Skip; -# } - -# sub OnTimer { -# my ($self, $event) = @_; - -# if ($self->{prog}->IsShown) { -# $self->{timer}->Stop; -# } -# $self->{prog}->Pulse if $self->{_busy}; -# } - sub SetCancelCallback { my $self = shift; my ($cb) = @_; $cancel_cb = $cb; - # $cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide; + $cb ? $self->ShowCancelButton : $self->HideCancelButton; } -# sub Run { -# my $self = shift; -# my $rate = shift || 100; -# if (!$self->{timer}->IsRunning) { -# $self->{timer}->Start($rate); -# } -# } - -# sub GetProgress { -# my $self = shift; -# return $self->{prog}->GetValue; -# } - -# sub SetProgress { -# my $self = shift; -# my ($val) = @_; -# if (!$self->{prog}->IsShown) { -# $self->ShowProgress(1); -# } -# if ($val == $self->{prog}->GetRange) { -# $self->{prog}->SetValue(0); -# $self->ShowProgress(0); -# } else { -# $self->{prog}->SetValue($val); -# } -# } - -# sub SetRange { -# my $self = shift; -# my ($val) = @_; - -# if ($val != $self->{prog}->GetRange) { -# $self->{prog}->SetRange($val); -# } -# } - -# sub ShowProgress { -# my $self = shift; -# my ($show) = @_; - -# $self->{prog}->Show($show); -# $self->{prog}->Pulse; -# } - -# sub StartBusy { -# my $self = shift; -# my $rate = shift || 100; - -# $self->{_busy} = 1; -# $self->ShowProgress(1); -# if (!$self->{timer}->IsRunning) { -# $self->{timer}->Start($rate); -# } -# } - -# sub StopBusy { -# my $self = shift; - -# $self->{timer}->Stop; -# $self->ShowProgress(0); -# $self->{prog}->SetValue(0); -# $self->{_busy} = 0; -# } - -# sub IsBusy { -# my $self = shift; -# return $self->{_busy}; -# } - 1; diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 2f1e9a148a..cee6d3394e 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -305,6 +305,10 @@ void AppController::arrange_model() // Set the range of the progress to the object count pind->max(count); + pind->on_cancel([](){ + std::cout << "Cannot be cancelled!" << std::endl; + }); + } auto dist = print_ctl()->config().min_object_distance(); @@ -353,7 +357,7 @@ void AppController::arrange_model() bed, arr::BOX, false, // create many piles not just one pile - [pind, count](unsigned rem) { + [this, pind, count](unsigned rem) { if(pind) pind->update(count - rem, _(L("Arranging objects..."))); }); @@ -369,6 +373,7 @@ void AppController::arrange_model() if(pind) { pind->max(pmax); pind->update(0, _(L("Arranging done."))); + pind->on_cancel(/*remove cancel function*/); } } diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index 58681a7de5..1106a0558c 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -15,6 +15,7 @@ class Model; class Print; class PrintObject; class PrintConfig; +class ProgressStatusBar; /** @@ -255,8 +256,7 @@ public: * @param gauge_id The ID of the gague widget of the status bar. * @param statusbar_id The ID of the status bar. */ - void set_global_progress_indicator(unsigned gauge_id, - unsigned statusbar_id); + void set_global_progress_indicator(ProgressStatusBar *prs); void arrange_model(); }; diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 64d783356d..3aeb4f3e78 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -210,31 +211,21 @@ AppControllerBoilerplate::create_progress_indicator(unsigned statenum, namespace { -// A wrapper progress indicator class around the statusbar created in perl. class Wrapper: public ProgressIndicator, public wxEvtHandler { - wxGauge *gauge_; - wxStatusBar *stbar_; + ProgressStatusBar *sbar_; using Base = ProgressIndicator; std::string message_; AppControllerBoilerplate& ctl_; void showProgress(bool show = true) { - gauge_->Show(show); + sbar_->show_progress(show); } void _state(unsigned st) { if( st <= ProgressIndicator::max() ) { Base::state(st); - - if(!gauge_->IsShown()) showProgress(true); - - stbar_->SetStatusText(message_); - if(static_cast(st) == gauge_->GetRange()) { - gauge_->SetValue(0); - showProgress(false); - } else { - gauge_->SetValue(static_cast(st)); - } + sbar_->set_status_text(message_); + sbar_->set_progress(st); } } @@ -247,12 +238,12 @@ class Wrapper: public ProgressIndicator, public wxEvtHandler { public: - inline Wrapper(wxGauge *gauge, wxStatusBar *stbar, + inline Wrapper(ProgressStatusBar *sbar, AppControllerBoilerplate& ctl): - gauge_(gauge), stbar_(stbar), ctl_(ctl) + sbar_(sbar), ctl_(ctl) { - Base::max(static_cast(gauge->GetRange())); - Base::states(static_cast(gauge->GetRange())); + Base::max(static_cast(sbar_->get_range())); + Base::states(static_cast(sbar_->get_range())); Bind(PROGRESS_STATUS_UPDATE_EVENT, &Wrapper::_state, @@ -265,7 +256,7 @@ public: virtual void max(float val) override { if(val > 1.0) { - gauge_->SetRange(static_cast(val)); + sbar_->set_range(static_cast(val)); ProgressIndicator::max(val); } } @@ -293,18 +284,18 @@ public: virtual void title(const string & /*title*/) override {} + virtual void on_cancel(CancelFn fn) override { + sbar_->set_cancel_callback(fn); + Base::on_cancel(fn); + } + }; } -void AppController::set_global_progress_indicator( - unsigned gid, - unsigned sid) +void AppController::set_global_progress_indicator(ProgressStatusBar *prsb) { - wxGauge* gauge = dynamic_cast(wxWindow::FindWindowById(gid)); - wxStatusBar* sb = dynamic_cast(wxWindow::FindWindowById(sid)); - - if(gauge && sb) { - global_progressind_ = std::make_shared(gauge, sb, *this); + if(prsb) { + global_progress_indicator(std::make_shared(prsb, *this)); } } } diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp index 13ca8cd812..dab1a88fac 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp @@ -30,7 +30,6 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): prog_->Hide(); cancelbutton_->Hide(); - self->SetFieldsCount(3); int w[] = {-1, 150, 155}; self->SetStatusWidths(3, w); @@ -60,11 +59,6 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): }); } -//ProgressStatusBar::ProgressStatusBar(): ProgressStatusBar(nullptr, wxID_ANY) -//{ - -//} - ProgressStatusBar::~ProgressStatusBar() { if(timer_->IsRunning()) timer_->Stop(); } @@ -86,6 +80,11 @@ void ProgressStatusBar::set_progress(int val) } } +int ProgressStatusBar::get_range() const +{ + return prog_->GetRange(); +} + void ProgressStatusBar::set_range(int val) { if(val != prog_->GetRange()) { @@ -129,28 +128,25 @@ void ProgressStatusBar::run(int rate) } } -void ProgressStatusBar::Embed() +void ProgressStatusBar::embed(wxFrame *frame) { - std::cout << "Embedding" << std::endl; - wxFrame* mf = GUI::get_main_frame(); - std::cout << mf->GetName() << std::endl; - std::cout << self->GetName() << std::endl; + wxFrame* mf = frame? frame : GUI::get_main_frame(); mf->SetStatusBar(self); } -void ProgressStatusBar::SetStatusText(std::string txt) +void ProgressStatusBar::set_status_text(const std::string& txt) { self->SetStatusText(txt); } -int ProgressStatusBar::GetId() +void ProgressStatusBar::show_cancel_button() { - return self->GetId(); + cancelbutton_->Show(); } -int ProgressStatusBar::GetProgId() +void ProgressStatusBar::hide_cancel_button() { - return prog_->GetId(); + cancelbutton_->Hide(); } } diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp index 53366aace8..a117cacfbf 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp @@ -10,6 +10,7 @@ class wxButton; class wxTimerEvent; class wxStatusBar; class wxWindow; +class wxFrame; namespace Slic3r { @@ -33,19 +34,21 @@ public: int get_progress() const; void set_progress(int); + int get_range() const; void set_range(int = 100); void show_progress(bool); void start_busy(int = 100); void stop_busy(); inline bool is_busy() const { return busy_; } - void set_cancel_callback(CancelFn); + void set_cancel_callback(CancelFn = CancelFn()); + inline void remove_cancel_callback() { set_cancel_callback(); } void run(int rate); + void embed(wxFrame *frame = nullptr); + void set_status_text(const std::string& txt); // Temporary methods to satisfy Perl side - void Embed(); - void SetStatusText(std::string txt); - int GetId(); - int GetProgId(); + void show_cancel_button(); + void hide_cancel_button(); private: bool busy_ = false; diff --git a/xs/src/slic3r/ProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp index 49d0c251ce..4a39e84abc 100644 --- a/xs/src/slic3r/ProgressIndicator.hpp +++ b/xs/src/slic3r/ProgressIndicator.hpp @@ -12,7 +12,7 @@ namespace Slic3r { */ class ProgressIndicator { public: - using CancelFn = std::function; // Cancel functio signature. + using CancelFn = std::function; // Cancel function signature. private: float state_ = .0f, max_ = 1.f, step_; @@ -28,14 +28,14 @@ public: /// Get the current progress state float state() const { return state_; } - /// Set the maximum of hte progress range + /// Set the maximum of the progress range virtual void max(float maxval) { max_ = maxval; } /// Set the current state of the progress. virtual void state(float val) { state_ = val; } /** - * @brief Number of states int the progress. Can be used insted of giving a + * @brief Number of states int the progress. Can be used instead of giving a * maximum value. */ virtual void states(unsigned statenum) { @@ -45,14 +45,14 @@ public: /// Message shown on the next status update. virtual void message(const string&) = 0; - /// Title of the operaton. + /// Title of the operation. virtual void title(const string&) = 0; - /// Formatted message for the next status update. Works just like sprinf. + /// Formatted message for the next status update. Works just like sprintf. virtual void message_fmt(const string& fmt, ...); /// Set up a cancel callback for the operation if feasible. - inline void on_cancel(CancelFn func) { cancelfunc_ = func; } + virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } /** * Explicitly shut down the progress indicator and call the associated @@ -60,7 +60,7 @@ public: */ virtual void cancel() { cancelfunc_(); } - /// Convinience function to call message and status update in one function. + /// Convenience function to call message and status update in one function. void update(float st, const string& msg) { message(msg); state(st); } diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp index 1b653081df..1355a7d6e5 100644 --- a/xs/xsp/AppController.xsp +++ b/xs/xsp/AppController.xsp @@ -5,6 +5,7 @@ #include "slic3r/AppController.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" +#include "slic3r/GUI/ProgressStatusBar.hpp" %} %name{Slic3r::PrintController} class PrintController { @@ -21,7 +22,7 @@ PrintController *print_ctl(); void set_model(Model *model); void set_print(Print *print); - void set_global_progress_indicator(unsigned gauge_id, unsigned statusbar_id); + void set_global_progress_indicator(ProgressStatusBar *prs); void arrange_model(); }; \ No newline at end of file diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp index 3b9be3ba8f..f59895581f 100644 --- a/xs/xsp/ProgressStatusBar.xsp +++ b/xs/xsp/ProgressStatusBar.xsp @@ -33,9 +33,16 @@ void Run(int rate) %code%{ THIS->run(rate); %}; - void Embed(); - void SetStatusText(std::string txt); - int GetId(); - int GetProgId(); + void Embed() + %code%{ THIS->embed(); %}; + + void SetStatusText(std::string txt) + %code%{ THIS->set_status_text(txt); %}; + + void ShowCancelButton() + %code%{ THIS->show_cancel_button(); %}; + + void HideCancelButton() + %code%{ THIS->hide_cancel_button(); %}; }; \ No newline at end of file From f5b1729af3e09ffc5ac64d2540227a0286cdb465 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 30 Aug 2018 12:11:52 +0200 Subject: [PATCH 15/50] fixing build issues --- xs/src/slic3r/AppController.cpp | 36 ++++----------------------------- xs/src/slic3r/GUI/GUI.cpp | 2 ++ xs/src/slic3r/GUI/GUI.hpp | 1 + 3 files changed, 7 insertions(+), 32 deletions(-) diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index cee6d3394e..a93ee63f05 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -256,7 +256,7 @@ void PrintController::slice() slice(pri); } -void IProgressIndicator::message_fmt( +void ProgressIndicator::message_fmt( const string &fmtstr, ...) { std::stringstream ss; va_list args; @@ -311,35 +311,7 @@ void AppController::arrange_model() } - auto dist = print_ctl()->config().min_object_distance(); - - // Create the arranger config - auto min_obj_distance = static_cast(dist/SCALING_FACTOR); - - auto& bedpoints = print_ctl()->config().bed_shape.values; - Polyline bed; bed.points.reserve(bedpoints.size()); - for(auto& v : bedpoints) - bed.append(Point::new_scale(v.x, v.y)); - - if(pind) pind->update(0, _(L("Arranging objects..."))); - - try { - arr::arrange(*model_, - min_obj_distance, - bed, - arr::BOX, - false, // create many piles not just one pile - [pind, count](unsigned rem) { - if(pind) - pind->update(count - rem, _(L("Arranging objects..."))); - }); - } catch(std::exception& e) { - std::cerr << e.what() << std::endl; - report_issue(IssueType::ERR, - _(L("Could not arrange model objects! " - "Some geometries may be invalid.")), - _(L("Exception occurred"))); - } + auto dist = print_ctl()->config().min_object_distance(); // Create the arranger config auto min_obj_distance = static_cast(dist/SCALING_FACTOR); @@ -347,7 +319,7 @@ void AppController::arrange_model() auto& bedpoints = print_ctl()->config().bed_shape.values; Polyline bed; bed.points.reserve(bedpoints.size()); for(auto& v : bedpoints) - bed.append(Point::new_scale(v(0), v(1))); + bed.append(Point::new_scale(v.x, v.y)); if(pind) pind->update(0, _(L("Arranging objects..."))); @@ -357,7 +329,7 @@ void AppController::arrange_model() bed, arr::BOX, false, // create many piles not just one pile - [this, pind, count](unsigned rem) { + [pind, count](unsigned rem) { if(pind) pind->update(count - rem, _(L("Arranging objects..."))); }); diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 8555f0b921..a96ad2da3d 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -181,6 +181,8 @@ void set_main_frame(wxFrame *main_frame) g_wxMainFrame = main_frame; } +wxFrame* get_main_frame() { return g_wxMainFrame; } + void set_tab_panel(wxNotebook *tab_panel) { g_wxTabPanel = tab_panel; diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 165288819b..20e6de667e 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -97,6 +97,7 @@ void set_3DScene(_3DScene *scene); AppConfig* get_app_config(); wxApp* get_app(); PresetBundle* get_preset_bundle(); +wxFrame* get_main_frame(); const wxColour& get_label_clr_modified(); const wxColour& get_label_clr_sys(); From d57725ee183e3730a7bcc3778fd880cc31771a39 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Aug 2018 16:08:25 +0200 Subject: [PATCH 16/50] Bumped up the version number to 1.41.0 release, bundled updated PrusaResearch configs. --- resources/profiles/PrusaResearch.idx | 10 ++- resources/profiles/PrusaResearch.ini | 117 ++++++++++++++++++++------- xs/src/libslic3r/libslic3r.h | 2 +- 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index ba4123588c..7a0c58aa6e 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,8 +1,12 @@ -min_slic3r_version = 1.41.0-alpha +0.2.1 Added PET and BVOH settings for MMU2 +0.2.0-beta5 Fixed MMU1 ramming parameters +0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower +0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 +0.2.0-beta2 Edited first layer speed and wipe tower position 0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles 0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. -0.2.0-alpha7 Fixed the *MK3* references -0.2.0-alpha6 +0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references +0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version 0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. 0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 32ec800e7f..ab86af976e 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the Slic3r configuration to be downgraded. -config_version = 0.2.0-beta +config_version = 0.2.1 # Where to get the updates from? config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/ @@ -67,7 +67,7 @@ fill_pattern = cubic first_layer_acceleration = 1000 first_layer_extrusion_width = 0.42 first_layer_height = 0.2 -first_layer_speed = 30 +first_layer_speed = 20 gap_fill_speed = 40 gcode_comments = 0 infill_every_layers = 1 @@ -113,7 +113,7 @@ support_material_interface_extruder = 0 support_material_angle = 0 support_material_buildplate_only = 0 support_material_enforce_layers = 0 -support_material_contact_distance = 0.15 +support_material_contact_distance = 0.1 support_material_interface_contact_loops = 0 support_material_interface_layers = 2 support_material_interface_spacing = 0.2 @@ -122,9 +122,9 @@ support_material_pattern = rectilinear support_material_spacing = 2 support_material_speed = 50 support_material_synchronize_layers = 0 -support_material_threshold = 45 +support_material_threshold = 55 support_material_with_sheath = 0 -support_material_xy_spacing = 60% +support_material_xy_spacing = 50% thin_walls = 0 top_infill_extrusion_width = 0.45 top_solid_infill_speed = 40 @@ -133,13 +133,15 @@ wipe_tower = 1 wipe_tower_bridging = 10 wipe_tower_rotation_angle = 0 wipe_tower_width = 60 -wipe_tower_x = 180 -wipe_tower_y = 135 +wipe_tower_x = 170 +wipe_tower_y = 140 xy_size_compensation = 0 [print:*MK3*] fill_pattern = grid single_extruder_multi_material_priming = 0 +wipe_tower_x = 170 +wipe_tower_y = 125 # Print parameters common to a 0.25mm diameter nozzle. [print:*0.25nozzle*] @@ -557,13 +559,17 @@ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ a end_filament_gcode = "; Filament-specific end gcode" extrusion_multiplier = 1 filament_loading_speed = 28 +filament_loading_speed_start = 3 filament_unloading_speed = 90 +filament_unloading_speed_start = 100 filament_toolchange_delay = 0 filament_cooling_moves = 4 filament_cooling_initial_speed = 2.2 filament_cooling_final_speed = 3.4 +filament_load_time = 0 +filament_unload_time = 0 filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" -filament_minimal_purge_on_wipe_tower = 5 +filament_minimal_purge_on_wipe_tower = 15 filament_cost = 0 filament_density = 0 filament_diameter = 1.75 @@ -664,6 +670,8 @@ cooling = 1 disable_fan_first_layers = 3 fan_always_on = 0 fan_below_layer_time = 10 +filament_cost = 58.66 +filament_density = 1.18 first_layer_bed_temperature = 105 first_layer_temperature = 270 max_fan_speed = 20 @@ -741,7 +749,7 @@ temperature = 260 [filament:E3D Edge] inherits = *PET* -filament_cost = 0 +filament_cost = 56.9 filament_density = 1.26 filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG" @@ -754,14 +762,14 @@ temperature = 270 [filament:Fillamentum ABS] inherits = *ABS* -filament_cost = 0 +filament_cost = 32.4 filament_density = 1.04 first_layer_temperature = 240 temperature = 240 [filament:Fillamentum ASA] inherits = *ABS* -filament_cost = 0 +filament_cost = 38.7 filament_density = 1.04 fan_always_on = 1 first_layer_temperature = 265 @@ -769,7 +777,7 @@ temperature = 265 [filament:Fillamentum CPE HG100 HM100] inherits = *PET* -filament_cost = 0 +filament_cost = 54.1 filament_density = 1.25 filament_notes = "CPE HG100 , CPE HM100" first_layer_bed_temperature = 90 @@ -783,7 +791,7 @@ inherits = *PLA* # For now, all but selected filaments are disabled for the MMU 2.0 compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) extrusion_multiplier = 1.2 -filament_cost = 0 +filament_cost = 68 filament_density = 1.15 filament_colour = #804040 filament_max_volumetric_speed = 10 @@ -793,24 +801,26 @@ temperature = 190 [filament:Generic ABS] inherits = *ABS* -filament_cost = 0 +filament_cost = 27.82 filament_density = 1.04 filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS" [filament:Generic PET] inherits = *PET* -filament_cost = 0 +filament_cost = 27.82 filament_density = 1.24 filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG" [filament:Generic PLA] inherits = *PLA* -filament_cost = 0 +filament_cost = 25.4 filament_density = 1.27 filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" [filament:Polymaker PC-Max] inherits = *ABS* +filament_cost = 77.3 +filament_density = 1.20 bed_temperature = 115 filament_colour = #3A80CA first_layer_bed_temperature = 100 @@ -819,6 +829,8 @@ temperature = 270 [filament:Primavalue PVA] inherits = *PLA* +filament_cost = 108 +filament_density = 1.23 cooling = 0 fan_always_on = 0 filament_colour = #FFFFD7 @@ -843,10 +855,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and filament_cooling_final_speed = 50 filament_cooling_initial_speed = 10 filament_cooling_moves = 5 -filament_loading_speed = 14 filament_ramming_parameters = "120 110 5.32258 5.45161 5.67742 6 6.48387 7.12903 7.90323 8.70968 9.3871 9.83871 10.0968 10.2258| 0.05 5.30967 0.45 5.50967 0.95 6.1871 1.45 7.39677 1.95 9.05484 2.45 10 2.95 10.3098 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6"; -filament_load_time = 12 -filament_unload_time = 11 [filament:Generic ABS MMU2] inherits = *ABS MMU2* @@ -856,6 +865,8 @@ inherits = *ABS MMU2* [filament:Prusa HIPS] inherits = *ABS* +filament_cost = 27.3 +filament_density = 1.04 bridge_fan_speed = 50 cooling = 1 extrusion_multiplier = 0.9 @@ -876,6 +887,28 @@ filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG" +[filament:*PET MMU2*] +inherits = Prusa PET +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material +temperature = 230 +first_layer_temperature = 230 +filament_cooling_final_speed = 1 +filament_cooling_initial_speed = 2 +filament_cooling_moves = 1 +filament_load_time = 12 +filament_loading_speed = 14 +filament_notes = PET +filament_ramming_parameters = "120 140 4.70968 4.74194 4.77419 4.80645 4.83871 4.87097 4.90323 5 5.25806 5.67742 6.29032 7.06452 7.83871 8.3871| 0.05 4.72901 0.45 4.73545 0.95 4.83226 1.45 4.88067 1.95 5.05483 2.45 5.93553 2.95 7.53556 3.45 8.6323 3.95 7.6 4.45 7.6 4.95 7.6" +filament_unload_time = 11 +filament_unloading_speed = 20 +filament_unloading_speed_start = 120 + +[filament:Generic PET MMU2] +inherits = *PET MMU2* + +[filament:Prusa PET MMU2] +inherits = *PET MMU2* + [filament:Prusa PLA] inherits = *PLA* filament_cost = 25.4 @@ -885,13 +918,15 @@ filament_notes = "List of materials tested with standart PLA print settings for [filament:*PLA MMU2*] inherits = Prusa PLA compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material -filament_cooling_final_speed = 50 -filament_cooling_initial_speed = 10 -filament_cooling_moves = 7 -filament_loading_speed = 14 -filament_ramming_parameters = "120 110 4.03226 4.12903 4.25806 4.41935 4.58065 4.80645 5.35484 6.29032 7.58065 9.09677 10.5806 11.8387 12.6452 12.9677| 0.05 4.01935 0.45 4.15483 0.95 4.50968 1.45 4.94516 1.95 6.79677 2.45 9.87102 2.95 12.4388 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6" +temperature = 205 +filament_cooling_final_speed = 1 +filament_cooling_initial_speed = 2 +filament_cooling_moves = 1 filament_load_time = 12 +filament_loading_speed = 14 +filament_ramming_parameters = "120 110 2.70968 2.93548 3.32258 3.83871 4.58065 5.54839 6.51613 7.35484 7.93548 8.16129| 0.05 2.66451 0.45 3.05805 0.95 4.05807 1.45 5.97742 1.95 7.69999 2.45 8.1936 2.95 11.342 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6" filament_unload_time = 11 +filament_unloading_speed = 20 [filament:Generic PLA MMU2] inherits = *PLA MMU2* @@ -901,11 +936,13 @@ inherits = *PLA MMU2* [filament:SemiFlex or Flexfill 98A] inherits = *FLEX* -filament_cost = 0 +filament_cost = 82 filament_density = 1.22 [filament:Taulman Bridge] inherits = *common* +filament_cost = 40 +filament_density = 1.13 bed_temperature = 90 bridge_fan_speed = 40 cooling = 0 @@ -925,6 +962,8 @@ temperature = 250 [filament:Taulman T-Glase] inherits = *PET* +filament_cost = 40 +filament_density = 1.27 bridge_fan_speed = 40 cooling = 0 fan_always_on = 0 @@ -936,6 +975,8 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el [filament:Verbatim BVOH] inherits = *common* +filament_cost = 218 +filament_density = 1.23 bed_temperature = 60 bridge_fan_speed = 100 cooling = 0 @@ -944,7 +985,7 @@ extrusion_multiplier = 1 fan_always_on = 0 fan_below_layer_time = 100 filament_colour = #FFFFD7 -filament_max_volumetric_speed = 10 +filament_max_volumetric_speed = 4 filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" filament_soluble = 1 filament_type = PLA @@ -955,8 +996,29 @@ min_fan_speed = 100 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 210 +[filament:Verbatim BVOH MMU2] +inherits = Verbatim BVOH +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material +temperature = 195 +filament_notes = BVOH +fan_always_on = 1 +first_layer_temperature = 200 +filament_cooling_final_speed = 1 +filament_cooling_initial_speed = 2 +filament_max_volumetric_speed = 4 +filament_type = PVA +filament_cooling_moves = 1 +filament_load_time = 12 +filament_loading_speed = 14 +filament_ramming_parameters = "120 110 1.74194 1.90323 2.16129 2.48387 2.83871 3.25806 3.83871 4.6129 5.41935 5.96774| 0.05 1.69677 0.45 1.96128 0.95 2.63872 1.45 3.46129 1.95 4.99031 2.45 6.12908 2.95 8.30974 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6" +filament_unload_time = 11 +filament_unloading_speed = 20 +filament_unloading_speed_start = 100 + [filament:Verbatim PP] inherits = *common* +filament_cost = 72 +filament_density = 0.89 bed_temperature = 100 bridge_fan_speed = 100 cooling = 1 @@ -1215,6 +1277,7 @@ inherits = *mm2* # The 5x nozzle diameter defines the number of extruders. Other extruder parameters # (for example the retract values) are duplicaed from the first value, so they do not need # to be defined explicitely. +machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#0080FF;#00FFFF;#FF4F4F;#9FFF9F start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\n;M221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n @@ -1223,4 +1286,4 @@ end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG # The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer. [obsolete_presets] print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2" -filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" +filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" \ No newline at end of file diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 6db60440b3..ddd056bc74 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -14,7 +14,7 @@ #include #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.41.0-beta2" +#define SLIC3R_VERSION "1.41.0" #define SLIC3R_BUILD "UNKNOWN" typedef int32_t coord_t; From 453e7e37d3335c6fc1050d23f2e4cd7d11adc01c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 1 Sep 2018 09:54:02 +0200 Subject: [PATCH 17/50] Bundled newer versions of PrusaResearch profiles. --- resources/profiles/PrusaResearch.idx | 2 ++ resources/profiles/PrusaResearch.ini | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 7a0c58aa6e..63a81db49a 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 1.41.0-alpha +0.2.2 Edited MMU2 Single mode purge line 0.2.1 Added PET and BVOH settings for MMU2 0.2.0-beta5 Fixed MMU1 ramming parameters 0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index ab86af976e..3b44a4bc7e 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the Slic3r configuration to be downgraded. -config_version = 0.2.1 +config_version = 0.2.2 # Where to get the updates from? config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/ @@ -1269,7 +1269,7 @@ default_filament_profile = Prusa PLA MMU2 [printer:Original Prusa i3 MK3 MMU2 Single] inherits = *mm2* -start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT?\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n +start_gcode = M107\nM115 U3.4.0-RC2 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT?\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors [printer:Original Prusa i3 MK3 MMU2] @@ -1280,10 +1280,10 @@ inherits = *mm2* machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#0080FF;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\n;M221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n +start_gcode = M107\nM115 U3.4.0-RC2 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n # The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer. [obsolete_presets] print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2" -filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" \ No newline at end of file +filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" From 22569de00fb05d3e69695d1a07819b795bbde134 Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Sat, 25 Aug 2018 08:50:42 +0200 Subject: [PATCH 18/50] fixes date for uploaded files --- xs/src/slic3r/Utils/Duet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp index 865d2b4187..82a42eb710 100644 --- a/xs/src/slic3r/Utils/Duet.cpp +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -230,7 +230,7 @@ std::string Duet::timestamp_str() const auto tm = *std::localtime(&t); char buffer[BUFFER_SIZE]; - std::strftime(buffer, BUFFER_SIZE, "time=%Y-%d-%mT%H:%M:%S", &tm); + std::strftime(buffer, BUFFER_SIZE, "time=%Y-%m-%dT%H:%M:%S", &tm); return std::string(buffer); } From 5f2afad95eaa6dfeb81e772f37adfa1d0812788f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 4 Sep 2018 12:41:14 +0200 Subject: [PATCH 19/50] Fixed render of picking pass for flatten gizmo --- xs/src/slic3r/GUI/GLGizmo.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index be085e913a..2980e34a17 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1031,8 +1031,6 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const Vec3d dragged_offset = box.center() - *m_center; - bool blending_was_enabled = ::glIsEnabled(GL_BLEND); - bool depth_test_was_enabled = ::glIsEnabled(GL_DEPTH_TEST); ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); @@ -1053,16 +1051,11 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const ::glPopMatrix(); } } - - if (!blending_was_enabled) - ::glDisable(GL_BLEND); - if (!depth_test_was_enabled) - ::glDisable(GL_DEPTH_TEST); } void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const { - ::glDisable(GL_DEPTH_TEST); + ::glEnable(GL_DEPTH_TEST); for (unsigned int i = 0; i < m_planes.size(); ++i) { From 15b13405141ca7481f156d204a917fab38c30dc1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 4 Sep 2018 14:42:14 +0200 Subject: [PATCH 20/50] Added ModelInstance::world_matrix() method and stl_transform using eigen transform --- lib/Slic3r/GUI/Plater.pm | 5 +-- xs/src/admesh/stl.h | 1 + xs/src/admesh/util.cpp | 41 ++++++++++++++++++ xs/src/libslic3r/Model.cpp | 70 ++++++++++++++----------------- xs/src/libslic3r/Model.hpp | 2 + xs/src/libslic3r/TriangleMesh.cpp | 9 +++- xs/src/libslic3r/TriangleMesh.hpp | 1 + xs/src/slic3r/GUI/GLGizmo.cpp | 6 +-- 8 files changed, 89 insertions(+), 46 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 00267fe630..ce9e0da68e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1141,13 +1141,12 @@ sub rotate { } # Let's calculate vector of rotation axis (if we don't have it already) - # The minus is there so that the direction is the same as was established if (defined $axis) { if ($axis == X) { - $axis_x = -1; + $axis_x = 1; } if ($axis == Y) { - $axis_y = -1; + $axis_y = 1; } } diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index 5f7a3c5c14..096430d150 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -173,6 +173,7 @@ extern void stl_mirror_xy(stl_file *stl); extern void stl_mirror_yz(stl_file *stl); extern void stl_mirror_xz(stl_file *stl); extern void stl_transform(stl_file *stl, float *trafo3x4); +extern void stl_transform(stl_file *stl, const Eigen::Transform& t); extern void stl_open_merge(stl_file *stl, char *file); extern void stl_invalidate_shared_vertices(stl_file *stl); extern void stl_generate_shared_vertices(stl_file *stl); diff --git a/xs/src/admesh/util.cpp b/xs/src/admesh/util.cpp index fba1ee76bf..cc104fdd19 100644 --- a/xs/src/admesh/util.cpp +++ b/xs/src/admesh/util.cpp @@ -155,6 +155,47 @@ void stl_transform(stl_file *stl, float *trafo3x4) { calculate_normals(stl); } +void stl_transform(stl_file *stl, const Eigen::Transform& t) +{ + if (stl->error) + return; + + unsigned int vertices_count = 3 * (unsigned int)stl->stats.number_of_facets; + if (vertices_count == 0) + return; + + Eigen::MatrixXf src_vertices(3, vertices_count); + stl_facet* facet_ptr = stl->facet_start; + unsigned int v_id = 0; + while (facet_ptr < stl->facet_start + stl->stats.number_of_facets) + { + for (int i = 0; i < 3; ++i) + { + ::memcpy((void*)src_vertices.col(v_id).data(), (const void*)&facet_ptr->vertex[i], 3 * sizeof(float)); + ++v_id; + } + facet_ptr += 1; + } + + Eigen::MatrixXf dst_vertices(3, vertices_count); + dst_vertices = t * src_vertices.colwise().homogeneous(); + + facet_ptr = stl->facet_start; + v_id = 0; + while (facet_ptr < stl->facet_start + stl->stats.number_of_facets) + { + for (int i = 0; i < 3; ++i) + { + ::memcpy((void*)&facet_ptr->vertex[i], (const void*)dst_vertices.col(v_id).data(), 3 * sizeof(float)); + ++v_id; + } + facet_ptr += 1; + } + + stl_get_size(stl); + calculate_normals(stl); +} + void stl_rotate_x(stl_file *stl, float angle) { int i; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 1cd3b1413e..36c210844e 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -672,20 +672,18 @@ void ModelObject::center_around_origin() // First align to origin on XYZ, then center it on XY. Vec3d size = bb.size(); - size(2) = 0.; - Vec3d shift3 = - bb.min - 0.5 * size; - // Unaligned vector, for the Rotation2D to work on Visual Studio 2013. - Eigen::Vector2d shift2 = to_2d(shift3); - - this->translate(shift3); - this->origin_translation += shift3; - + size(2) = 0.0; + Vec3d shift = -bb.min - 0.5 * size; + + this->translate(shift); + this->origin_translation += shift; + if (!this->instances.empty()) { for (ModelInstance *i : this->instances) { // apply rotation and scaling to vector as well before translating instance, // in order to leave final position unaltered - Eigen::Rotation2Dd rot(i->rotation); - i->offset -= rot * shift2 * i->scaling_factor; + Vec3d i_shift = i->world_matrix(true) * shift; + i->offset -= to_2d(i_shift); } this->invalidate_bounding_box(); } @@ -861,12 +859,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_ { for (ModelInstance* inst : this->instances) { - Transform3d m = Transform3d::Identity(); - m.translate(Vec3d(inst->offset(0), inst->offset(1), 0.0)); - m.rotate(Eigen::AngleAxisd(inst->rotation, Vec3d::UnitZ())); - m.scale(inst->scaling_factor); - - BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(m); + BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(inst->world_matrix()); if (print_volume.contains(bb)) inst->print_volume_state = ModelInstance::PVS_Inside; @@ -995,26 +988,17 @@ size_t ModelVolume::split(unsigned int max_extruders) void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { - mesh->rotate_z(this->rotation); // rotate around mesh origin - mesh->scale(this->scaling_factor); // scale around mesh origin - if (!dont_translate) - mesh->translate(this->offset(0), this->offset(1), 0); + mesh->transform(world_matrix(dont_translate).cast()); } BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const { // Rotate around mesh origin. - double c = cos(this->rotation); - double s = sin(this->rotation); - BoundingBoxf3 bbox; - for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) { - const stl_facet &facet = mesh->stl.facet_start[i]; - for (int j = 0; j < 3; ++ j) { - const stl_vertex &v = facet.vertex[j]; - bbox.merge(Vec3d(c * v(0) - s * v(1), s * v(0) + c * v(1), v(2))); - } - } - if (! empty(bbox)) { + TriangleMesh copy(*mesh); + copy.transform(world_matrix(dont_translate, false, true).cast()); + BoundingBoxf3 bbox = copy.bounding_box(); + + if (!empty(bbox)) { // Scale the bounding box uniformly. if (std::abs(this->scaling_factor - 1.) > EPSILON) { bbox.min *= this->scaling_factor; @@ -1031,13 +1015,7 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const { - Transform3d matrix = Transform3d::Identity(); - if (!dont_translate) - matrix.translate(Vec3d(offset(0), offset(1), 0.0)); - - matrix.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); - matrix.scale(scaling_factor); - return bbox.transformed(matrix); + return bbox.transformed(world_matrix(dont_translate)); } void ModelInstance::transform_polygon(Polygon* polygon) const @@ -1046,4 +1024,20 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(this->scaling_factor); // scale around polygon origin } +Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale) const +{ + Transform3d m = Transform3d::Identity(); + + if (!dont_translate) + m.translate(Vec3d(offset(0), offset(1), 0.0)); + + if (!dont_rotate) + m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); + + if (!dont_scale) + m.scale(scaling_factor); + + return m; +} + } diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index f8a36527dc..66b53618a9 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -243,6 +243,8 @@ public: // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; + Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const; + bool is_printable() const { return print_volume_state == PVS_Inside; } private: diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 6840bc96ec..6d40731f83 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -264,7 +264,7 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis) Vec3f axis_norm = axis.cast().normalized(); Transform3f m = Transform3f::Identity(); m.rotate(Eigen::AngleAxisf(angle, axis_norm)); - stl_transform(&stl, (float*)m.data()); + stl_transform(&stl, m); } void TriangleMesh::mirror(const Axis &axis) @@ -279,6 +279,11 @@ void TriangleMesh::mirror(const Axis &axis) stl_invalidate_shared_vertices(&this->stl); } +void TriangleMesh::transform(const Transform3f& t) +{ + stl_transform(&stl, t); +} + void TriangleMesh::align_to_origin() { this->translate( @@ -523,9 +528,9 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0); src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1); src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2); + ++v_id; } facet_ptr += 1; - ++v_id; } } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 666252887a..aebed4a2e4 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -48,6 +48,7 @@ public: void mirror_x() { this->mirror(X); } void mirror_y() { this->mirror(Y); } void mirror_z() { this->mirror(Z); } + void transform(const Transform3f& t); void align_to_origin(); void rotate(double angle, Point* center); TriangleMeshPtrs split() const; diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 2980e34a17..a35165db4e 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1051,6 +1051,8 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const ::glPopMatrix(); } } + + ::glDisable(GL_BLEND); } void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const @@ -1278,9 +1280,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const } Vec3d GLGizmoFlatten::get_flattening_normal() const { - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-m_model_object->instances.front()->rotation, Vec3d::UnitZ())); - Vec3d normal = m * m_normal; + Vec3d normal = m_model_object->instances.front()->world_matrix().matrix().block(0, 0, 3, 3) * m_normal; m_normal = Vec3d::Zero(); return normal; } From 87c5c8e7f6de6e6773baa590ca43edb4fc71f5ac Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 09:11:58 +0200 Subject: [PATCH 21/50] Renamed GLVolume member variables to match the corresponding ModelInstance ones --- xs/src/libslic3r/Model.cpp | 3 +- xs/src/libslic3r/Model.hpp | 1 - xs/src/slic3r/GUI/3DScene.cpp | 70 ++++++++++++++++---------------- xs/src/slic3r/GUI/3DScene.hpp | 19 +++++---- xs/src/slic3r/GUI/GLCanvas3D.cpp | 22 +++++----- xs/xsp/GUI_3DScene.xsp | 4 +- 6 files changed, 61 insertions(+), 58 deletions(-) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 36c210844e..6ad8ebb49f 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -237,7 +237,6 @@ BoundingBoxf3 Model::bounding_box() const void Model::center_instances_around_point(const Vec2d &point) { -// BoundingBoxf3 bb = this->bounding_box(); BoundingBoxf3 bb; for (ModelObject *o : this->objects) for (size_t i = 0; i < o->instances.size(); ++ i) @@ -995,7 +994,7 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes { // Rotate around mesh origin. TriangleMesh copy(*mesh); - copy.transform(world_matrix(dont_translate, false, true).cast()); + copy.transform(world_matrix(true, false, true).cast()); BoundingBoxf3 bbox = copy.bounding_box(); if (!empty(bbox)) { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 66b53618a9..bbfd2bde23 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -224,7 +224,6 @@ public: friend class ModelObject; -// Transform3d transform; double rotation; // Rotation around the Z axis, in radians around mesh center point double scaling_factor; Vec2d offset; // in unscaled coordinates diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 08a1b1457d..3f44b727e1 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -195,9 +195,9 @@ const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) - : m_origin(0, 0, 0) - , m_angle_z(0.0f) - , m_scale_factor(1.0f) + : m_offset(Vec3d::Zero()) + , m_rotation(0.0) + , m_scaling_factor(1.0) , m_world_matrix(Transform3f::Identity()) , m_world_matrix_dirty(true) , m_transformed_bounding_box_dirty(true) @@ -255,43 +255,43 @@ void GLVolume::set_render_color() set_render_color(color, 4); } -const Vec3d& GLVolume::get_origin() const +double GLVolume::get_rotation() { - return m_origin; + return m_rotation; } -float GLVolume::get_angle_z() +void GLVolume::set_rotation(double rotation) { - return m_angle_z; -} - -void GLVolume::set_origin(const Vec3d& origin) -{ - if (m_origin != origin) + if (m_rotation != rotation) { - m_origin = origin; + m_rotation = rotation; m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } -void GLVolume::set_angle_z(float angle_z) +const Vec3d& GLVolume::get_offset() const { - if (m_angle_z != angle_z) + return m_offset; +} + +void GLVolume::set_offset(const Vec3d& offset) +{ + if (m_offset != offset) { - m_angle_z = angle_z; + m_offset = offset; m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } -void GLVolume::set_scale_factor(float scale_factor) +void GLVolume::set_scaling_factor(double factor) { - if (m_scale_factor != scale_factor) + if (m_scaling_factor != factor) { - m_scale_factor = scale_factor; + m_scaling_factor = factor; m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; @@ -308,9 +308,9 @@ const Transform3f& GLVolume::world_matrix() const if (m_world_matrix_dirty) { m_world_matrix = Transform3f::Identity(); - m_world_matrix.translate(Vec3f((float)m_origin(0), (float)m_origin(1), (float)m_origin(2))); - m_world_matrix.rotate(Eigen::AngleAxisf(m_angle_z, Vec3f::UnitZ())); - m_world_matrix.scale(m_scale_factor); + m_world_matrix.translate(m_offset.cast()); + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ())); + m_world_matrix.scale((float)m_scaling_factor); m_world_matrix_dirty = false; } return m_world_matrix; @@ -384,9 +384,9 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); - ::glTranslated(m_origin(0), m_origin(1), m_origin(2)); - ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); - ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); + ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else @@ -510,9 +510,9 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); ::glPushMatrix(); - ::glTranslated(m_origin(0), m_origin(1), m_origin(2)); - ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); - ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); + ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (n_triangles > 0) { @@ -555,9 +555,9 @@ void GLVolume::render_legacy() const ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()); ::glPushMatrix(); - ::glTranslated(m_origin(0), m_origin(1), m_origin(2)); - ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); - ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); + ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (n_triangles > 0) ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); @@ -675,9 +675,9 @@ std::vector GLVolumeCollection::load_object( } v.is_modifier = model_volume->modifier; v.shader_outside_printer_detection_enabled = !model_volume->modifier; - v.set_origin(Vec3d(instance->offset(0), instance->offset(1), 0.0)); - v.set_angle_z(instance->rotation); - v.set_scale_factor(instance->scaling_factor); + v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); + v.set_rotation(instance->rotation); + v.set_scaling_factor(instance->scaling_factor); } } @@ -746,7 +746,7 @@ int GLVolumeCollection::load_wipe_tower_preview( else v.indexed_vertex_array.load_mesh_flat_shading(mesh); - v.set_origin(Vec3d(pos_x, pos_y, 0.)); + v.set_offset(Vec3d(pos_x, pos_y, 0.0)); // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 69f1e1d358..1785ecf4a4 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -255,11 +255,11 @@ public: private: // Offset of the volume to be rendered. - Vec3d m_origin; + Vec3d m_offset; // Rotation around Z axis of the volume to be rendered. - float m_angle_z; + double m_rotation; // Scale factor of the volume to be rendered. - float m_scale_factor; + double m_scaling_factor; // World matrix of the volume to be rendered. mutable Transform3f m_world_matrix; // Whether or not is needed to recalculate the world matrix. @@ -327,11 +327,14 @@ public: // Sets render color in dependence of current state void set_render_color(); - float get_angle_z(); - const Vec3d& get_origin() const; - void set_origin(const Vec3d& origin); - void set_angle_z(float angle_z); - void set_scale_factor(float scale_factor); + double get_rotation(); + void set_rotation(double rotation); + + const Vec3d& get_offset() const; + void set_offset(const Vec3d& offset); + + void set_scaling_factor(double factor); + void set_convex_hull(const TriangleMesh& convex_hull); int object_idx() const { return this->composite_id / 1000000; } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index bbc73b8964..73966a7f54 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -3001,7 +3001,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) Vec3d normal = m_gizmos.get_flattening_normal(); if (normal != Vec3d::Zero()) { Vec3d axis = normal(2) > 0.999f ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()); - float angle = -acos(-normal(2)); + float angle = acos(-normal(2)); m_on_gizmo_rotate_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); } } @@ -3126,7 +3126,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Apply new temporary volume origin and ignore Z. for (GLVolume* v : volumes) - v->set_origin(v->get_origin() + Vec3d(vector(0), vector(1), 0.0)); + { + v->set_offset(v->get_offset() + Vec3d(vector(0), vector(1), 0.0)); + } m_mouse.drag.start_position_3D = cur_pos; m_gizmos.refresh(); @@ -3166,7 +3168,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float scale_factor = m_gizmos.get_scale(); for (GLVolume* v : volumes) { - v->set_scale_factor(scale_factor); + v->set_scaling_factor((double)scale_factor); } break; } @@ -3176,7 +3178,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float angle_z = m_gizmos.get_angle_z(); for (GLVolume* v : volumes) { - v->set_angle_z(angle_z); + v->set_rotation((double)angle_z); } break; } @@ -3194,7 +3196,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) const Vec3d& size = bb.size(); m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale()); update_scale_values(size, m_gizmos.get_scale()); - update_rotation_value(volumes[0]->get_angle_z(), "z"); + update_rotation_value(volumes[0]->get_rotation(), "z"); } if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1)) @@ -5219,7 +5221,7 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) std::set done; // prevent moving instances twice bool object_moved = false; - Vec3d wipe_tower_origin(0.0, 0.0, 0.0); + Vec3d wipe_tower_origin = Vec3d::Zero(); for (int volume_idx : volume_idxs) { GLVolume* volume = m_volumes.volumes[volume_idx]; @@ -5238,20 +5240,20 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) { // Move a regular object. ModelObject* model_object = m_model->objects[obj_idx]; - const Vec3d& origin = volume->get_origin(); - model_object->instances[instance_idx]->offset = Vec2d(origin(0), origin(1)); + const Vec3d& offset = volume->get_offset(); + model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); model_object->invalidate_bounding_box(); object_moved = true; } else if (obj_idx == 1000) // Move a wipe tower proxy. - wipe_tower_origin = volume->get_origin(); + wipe_tower_origin = volume->get_offset(); } if (object_moved) m_on_instance_moved_callback.call(); - if (wipe_tower_origin != Vec3d(0.0, 0.0, 0.0)) + if (wipe_tower_origin != Vec3d::Zero()) m_on_wipe_tower_moved_callback.call(wipe_tower_origin(0), wipe_tower_origin(1)); } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c15fdc196f..6c199aacb5 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -56,9 +56,9 @@ int volume_idx() const; int instance_idx() const; Clone origin() const - %code%{ RETVAL = THIS->get_origin(); %}; + %code%{ RETVAL = THIS->get_offset(); %}; void translate(double x, double y, double z) - %code%{ THIS->set_origin(THIS->get_origin() + Vec3d(x, y, z)); %}; + %code%{ THIS->set_offset(THIS->get_offset() + Vec3d(x, y, z)); %}; Clone bounding_box() const %code%{ RETVAL = THIS->bounding_box; %}; From f67060f0129cbe897e6a144b8181146c35ace174 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Sep 2018 09:47:36 +0200 Subject: [PATCH 22/50] Fixed bug from issue #1169 --- xs/src/slic3r/GUI/Field.cpp | 23 +++++++++++++++-------- xs/src/slic3r/GUI/Field.hpp | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 757a18f115..44cdb33f5f 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -234,14 +234,10 @@ namespace Slic3r { namespace GUI { }), temp->GetId()); #ifdef __WXGTK__ - temp->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) - { - if (bChangedValueEvent) { - on_change_field(); - bChangedValueEvent = false; - } - event.Skip(); - }); + // to correct value update on GTK we should call on_change_field() on + // wxEVT_KEY_UP or wxEVT_TEXT_PASTE instead of wxEVT_TEXT + temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this); + temp->Bind(wxEVT_TEXT_PASTE, &TextCtrl::change_field_value, this); #endif //__WXGTK__ // select all text using Ctrl+A @@ -267,6 +263,17 @@ namespace Slic3r { namespace GUI { void TextCtrl::enable() { dynamic_cast(window)->Enable(); dynamic_cast(window)->SetEditable(true); } void TextCtrl::disable() { dynamic_cast(window)->Disable(); dynamic_cast(window)->SetEditable(false); } +#ifdef __WXGTK__ + void TextCtrl::change_field_value(wxEvent& event) + { + if (bChangedValueEvent) { + on_change_field(); + bChangedValueEvent = false; + } + event.Skip(); + }; +#endif //__WXGTK__ + void CheckBox::BUILD() { auto size = wxSize(wxDefaultSize); if (m_opt.height >= 0) size.SetHeight(m_opt.height); diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index db8d2a4085..67c7692fcf 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -223,6 +223,7 @@ class TextCtrl : public Field { using Field::Field; #ifdef __WXGTK__ bool bChangedValueEvent = false; + void change_field_value(wxEvent& event); #endif //__WXGTK__ public: TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} From f95b84e913eee5b7cf800fee89c7b10bea05d090 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 09:49:10 +0200 Subject: [PATCH 23/50] Fixed stl import when endsolid lines contains extra data --- xs/src/admesh/stlinit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/admesh/stlinit.cpp b/xs/src/admesh/stlinit.cpp index e572ce9303..ed8beb9d19 100644 --- a/xs/src/admesh/stlinit.cpp +++ b/xs/src/admesh/stlinit.cpp @@ -287,7 +287,7 @@ stl_read(stl_file *stl, int first_facet, int first) { { // skip solid/endsolid // (in this order, otherwise it won't work when they are paired in the middle of a file) - fscanf(stl->fp, "endsolid\n"); + fscanf(stl->fp, "endsolid%*[^\n]\n"); fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs. int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); From e2830dc33736dc5afede0ea073a9e7671205fed3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Sep 2018 12:02:34 +0200 Subject: [PATCH 24/50] Fix to last commit --- xs/src/slic3r/GUI/Field.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 44cdb33f5f..228d320da0 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -224,10 +224,13 @@ namespace Slic3r { namespace GUI { }), temp->GetId()); #endif // __WXGTK__ - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent) + temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) { #ifdef __WXGTK__ - bChangedValueEvent = true; + if (bChangedValueEvent) + change_field_value(ent); + else + bChangedValueEvent = true; #else on_change_field(); #endif //__WXGTK__ @@ -266,7 +269,9 @@ namespace Slic3r { namespace GUI { #ifdef __WXGTK__ void TextCtrl::change_field_value(wxEvent& event) { - if (bChangedValueEvent) { + if (event.GetEventType() == wxEVT_TEXT_PASTE) + bChangedValueEvent = true; + else if (bChangedValueEvent) { on_change_field(); bChangedValueEvent = false; } From 8ba48f8dc508fcde572514052cfa1acb91644c4f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Sep 2018 12:33:04 +0200 Subject: [PATCH 25/50] fixed typo-bug --- xs/src/slic3r/GUI/Field.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 228d320da0..f679de1fab 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -228,7 +228,7 @@ namespace Slic3r { namespace GUI { { #ifdef __WXGTK__ if (bChangedValueEvent) - change_field_value(ent); + change_field_value(evt); else bChangedValueEvent = true; #else From 011281068b1a6047a5d1eab61cdeeafd10f15176 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 14:02:08 +0200 Subject: [PATCH 26/50] Fixed flatten gizmo's flattening normal --- xs/src/libslic3r/Format/3mf.cpp | 5 +---- xs/src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- xs/src/slic3r/GUI/GLGizmo.cpp | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index b838f65c11..4dc40bd3cb 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -1738,10 +1738,7 @@ namespace Slic3r { stream << " \n"; } - Transform3d t = Transform3d::Identity(); - t.translate(Vec3d(instance->offset(0), instance->offset(1), 0.0)); - t.rotate(Eigen::AngleAxisd(instance->rotation, Vec3d::UnitZ())); - t.scale(instance->scaling_factor); + Transform3d t = instance->world_matrix(); build_items.emplace_back(instance_id, t); stream << " \n"; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 73966a7f54..36f664f345 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2999,9 +2999,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.get_current_type() == Gizmos::Flatten) { // Rotate the object so the normal points downward: Vec3d normal = m_gizmos.get_flattening_normal(); - if (normal != Vec3d::Zero()) { - Vec3d axis = normal(2) > 0.999f ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()); - float angle = acos(-normal(2)); + if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) { + Vec3d axis = normal(2) > 0.999 ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()).normalized(); + float angle = acos(clamp(-1.0, 1.0, -normal(2))); m_on_gizmo_rotate_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); } } diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index a35165db4e..aa4152c855 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1282,7 +1282,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const Vec3d GLGizmoFlatten::get_flattening_normal() const { Vec3d normal = m_model_object->instances.front()->world_matrix().matrix().block(0, 0, 3, 3) * m_normal; m_normal = Vec3d::Zero(); - return normal; + return normal.normalized(); } } // namespace GUI From 82838cc640950a2505e77b0b5205f74cd86dfdf1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Sep 2018 15:03:20 +0200 Subject: [PATCH 27/50] Final bug-fix for #1169 considering issue #971 --- xs/src/slic3r/GUI/Field.cpp | 23 ++++++++--------------- xs/src/slic3r/GUI/Field.hpp | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index f679de1fab..c7f1d48ffd 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -227,20 +227,17 @@ namespace Slic3r { namespace GUI { temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) { #ifdef __WXGTK__ - if (bChangedValueEvent) - change_field_value(evt); - else - bChangedValueEvent = true; -#else - on_change_field(); + if (bChangedValueEvent) #endif //__WXGTK__ + on_change_field(); }), temp->GetId()); #ifdef __WXGTK__ - // to correct value update on GTK we should call on_change_field() on - // wxEVT_KEY_UP or wxEVT_TEXT_PASTE instead of wxEVT_TEXT + // to correct value updating on GTK we should: + // call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT + // and prevent value updating on wxEVT_KEY_DOWN + temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this); temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this); - temp->Bind(wxEVT_TEXT_PASTE, &TextCtrl::change_field_value, this); #endif //__WXGTK__ // select all text using Ctrl+A @@ -269,12 +266,8 @@ namespace Slic3r { namespace GUI { #ifdef __WXGTK__ void TextCtrl::change_field_value(wxEvent& event) { - if (event.GetEventType() == wxEVT_TEXT_PASTE) - bChangedValueEvent = true; - else if (bChangedValueEvent) { - on_change_field(); - bChangedValueEvent = false; - } + if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP) + on_change_field(); event.Skip(); }; #endif //__WXGTK__ diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index 67c7692fcf..923f0fd7e2 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -222,7 +222,7 @@ inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && ob class TextCtrl : public Field { using Field::Field; #ifdef __WXGTK__ - bool bChangedValueEvent = false; + bool bChangedValueEvent = true; void change_field_value(wxEvent& event); #endif //__WXGTK__ public: From df594fc5b023d23946fa27dad402cf0912498bc3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 15:09:13 +0200 Subject: [PATCH 28/50] Tweaks to update of object properties on gui --- xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 45 +++++++++++++++++++++------ xs/src/slic3r/GUI/GUI_ObjectParts.hpp | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index 94555f50e5..0c45b70acb 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1368,13 +1368,20 @@ void update_settings_value() { auto og = get_optgroup(ogFrequentlyObjectSettings); if (m_selected_object_id < 0 || m_objects->size() <= m_selected_object_id) { - og->set_value("scale_x", 0); + og->set_value("position_x", 0); + og->set_value("position_y", 0); + og->set_value("position_z", 0); + og->set_value("scale_x", 0); og->set_value("scale_y", 0); og->set_value("scale_z", 0); + og->set_value("rotation_x", 0); + og->set_value("rotation_y", 0); + og->set_value("rotation_z", 0); og->disable(); return; } g_is_percent_scale = boost::any_cast(og->get_value("scale_unit")) == _("%"); + update_position_values(); update_scale_values(); update_rotation_values(); og->enable(); @@ -1535,8 +1542,31 @@ void update_extruder_in_config(const wxString& selection) void update_scale_values() { - update_scale_values((*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(), - (*m_objects)[m_selected_object_id]->instances[0]->scaling_factor); + auto og = get_optgroup(ogFrequentlyObjectSettings); + auto instance = (*m_objects)[m_selected_object_id]->instances.front(); + auto size = (*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(); + + if (g_is_percent_scale) { + auto scale = instance->scaling_factor * 100; + og->set_value("scale_x", int(scale)); + og->set_value("scale_y", int(scale)); + og->set_value("scale_z", int(scale)); + } + else { + og->set_value("scale_x", int(instance->scaling_factor * size(0) + 0.5)); + og->set_value("scale_y", int(instance->scaling_factor * size(1) + 0.5)); + og->set_value("scale_z", int(instance->scaling_factor * size(2) + 0.5)); + } +} + +void update_position_values() +{ + auto og = get_optgroup(ogFrequentlyObjectSettings); + auto instance = (*m_objects)[m_selected_object_id]->instances.front(); + + og->set_value("position_x", int(instance->offset(0))); + og->set_value("position_y", int(instance->offset(1))); + og->set_value("position_z", 0); } void update_scale_values(const Vec3d& size, float scaling_factor) @@ -1559,15 +1589,10 @@ void update_scale_values(const Vec3d& size, float scaling_factor) void update_rotation_values() { auto og = get_optgroup(ogFrequentlyObjectSettings); - + auto instance = (*m_objects)[m_selected_object_id]->instances.front(); og->set_value("rotation_x", 0); og->set_value("rotation_y", 0); - - auto rotation_z = (*m_objects)[m_selected_object_id]->instances[0]->rotation; - auto deg = int(Geometry::rad2deg(rotation_z)); -// if (deg > 180) deg -= 360; - - og->set_value("rotation_z", deg); + og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation))); } void update_rotation_value(const double angle, const std::string& axis) diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp index 82bfe4a6c6..c64dd8f223 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp @@ -107,6 +107,7 @@ void update_settings_value(); void set_extruder_column_hidden(bool hide); // update extruder in current config void update_extruder_in_config(const wxString& selection); +void update_position_values(); // update scale values after scale unit changing or "gizmos" void update_scale_values(); void update_scale_values(const Vec3d& size, float scale); From aef871f1a078b378e59c68e94ec449e25c6880ab Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 15:13:29 +0200 Subject: [PATCH 29/50] Another fix in flatten gizmo's flattening normal --- xs/src/slic3r/GUI/GLGizmo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index aa4152c855..e46cfa9252 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1280,7 +1280,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const } Vec3d GLGizmoFlatten::get_flattening_normal() const { - Vec3d normal = m_model_object->instances.front()->world_matrix().matrix().block(0, 0, 3, 3) * m_normal; + Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal; m_normal = Vec3d::Zero(); return normal.normalized(); } From 42567e3af27bafa93d9e118b261795f3236cd124 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 15:35:09 +0200 Subject: [PATCH 30/50] Object's position updated on gui after displacement --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 36f664f345..b52bc2a547 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -5243,6 +5243,7 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) const Vec3d& offset = volume->get_offset(); model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); model_object->invalidate_bounding_box(); + update_position_values(); object_moved = true; } else if (obj_idx == 1000) From 677dad06695d7954fcabd681cca1975fb1a6e4d5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Sep 2018 16:22:48 +0200 Subject: [PATCH 31/50] Object's properties updated while manipulating gizmos --- lib/Slic3r/GUI/Plater.pm | 6 +-- xs/src/slic3r/GUI/GLCanvas3D.cpp | 7 +-- xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 63 +++++++++++++++++++-------- xs/src/slic3r/GUI/GUI_ObjectParts.hpp | 6 ++- xs/xsp/GUI.xsp | 5 +-- 5 files changed, 55 insertions(+), 32 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ce9e0da68e..b48ab0f97f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1181,11 +1181,7 @@ sub rotate { # $model_object->center_around_origin; # $self->reset_thumbnail($obj_idx); } - - if (defined $axis) { - Slic3r::GUI::update_rotation_value(deg2rad($angle), $axis == X ? "x" : ($axis == Y ? "y" : "z")); - } - + # update print and start background processing $self->{print}->add_model_object($model_object, $obj_idx); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index b52bc2a547..063b96cbe9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -3130,6 +3130,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->set_offset(v->get_offset() + Vec3d(vector(0), vector(1), 0.0)); } + update_position_values(volume->get_offset()); m_mouse.drag.start_position_3D = cur_pos; m_gizmos.refresh(); @@ -3169,6 +3170,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) for (GLVolume* v : volumes) { v->set_scaling_factor((double)scale_factor); + update_scale_values((double)scale_factor); } break; } @@ -3179,6 +3181,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) for (GLVolume* v : volumes) { v->set_rotation((double)angle_z); + update_rotation_value((double)angle_z, Z); } break; } @@ -3195,8 +3198,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } const Vec3d& size = bb.size(); m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale()); - update_scale_values(size, m_gizmos.get_scale()); - update_rotation_value(volumes[0]->get_rotation(), "z"); } if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1)) @@ -3297,7 +3298,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) case Gizmos::Scale: { m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); - Slic3r::GUI::update_settings_value(); break; } case Gizmos::Rotate: @@ -3309,6 +3309,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) break; } m_gizmos.stop_dragging(); + Slic3r::GUI::update_settings_value(); } m_mouse.drag.move_volume_idx = -1; diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index 0c45b70acb..9ec39b9761 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1547,7 +1547,7 @@ void update_scale_values() auto size = (*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(); if (g_is_percent_scale) { - auto scale = instance->scaling_factor * 100; + auto scale = instance->scaling_factor * 100.0; og->set_value("scale_x", int(scale)); og->set_value("scale_y", int(scale)); og->set_value("scale_z", int(scale)); @@ -1569,21 +1569,31 @@ void update_position_values() og->set_value("position_z", 0); } -void update_scale_values(const Vec3d& size, float scaling_factor) +void update_position_values(const Vec3d& position) +{ + auto og = get_optgroup(ogFrequentlyObjectSettings); + auto instance = (*m_objects)[m_selected_object_id]->instances.front(); + + og->set_value("position_x", int(position(0))); + og->set_value("position_y", int(position(1))); + og->set_value("position_z", int(position(2))); +} + +void update_scale_values(double scaling_factor) { auto og = get_optgroup(ogFrequentlyObjectSettings); - if (g_is_percent_scale) { - auto scale = scaling_factor * 100; - og->set_value("scale_x", int(scale)); - og->set_value("scale_y", int(scale)); - og->set_value("scale_z", int(scale)); - } - else { - og->set_value("scale_x", int(size(0) + 0.5)); - og->set_value("scale_y", int(size(1) + 0.5)); - og->set_value("scale_z", int(size(2) + 0.5)); - } + // this is temporary + // to be able to update the values as size + // we need to store somewhere the original size + // or have it passed as parameter + if (!g_is_percent_scale) + og->set_value("scale_unit", _("%")); + + auto scale = scaling_factor * 100.0; + og->set_value("scale_x", int(scale)); + og->set_value("scale_y", int(scale)); + og->set_value("scale_z", int(scale)); } void update_rotation_values() @@ -1595,14 +1605,31 @@ void update_rotation_values() og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation))); } -void update_rotation_value(const double angle, const std::string& axis) +void update_rotation_value(double angle, Axis axis) { auto og = get_optgroup(ogFrequentlyObjectSettings); - - int deg = int(Geometry::rad2deg(angle)); -// if (deg>180) deg -= 360; - og->set_value("rotation_"+axis, deg); + std::string axis_str; + switch (axis) + { + case X: + { + axis_str = "rotation_x"; + break; + } + case Y: + { + axis_str = "rotation_y"; + break; + } + case Z: + { + axis_str = "rotation_z"; + break; + } + } + + og->set_value(axis_str, int(Geometry::rad2deg(angle))); } void set_uniform_scaling(const bool uniform_scale) diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp index c64dd8f223..8a1499e030 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp @@ -107,14 +107,16 @@ void update_settings_value(); void set_extruder_column_hidden(bool hide); // update extruder in current config void update_extruder_in_config(const wxString& selection); +// update position values displacements or "gizmos" void update_position_values(); +void update_position_values(const Vec3d& position); // update scale values after scale unit changing or "gizmos" void update_scale_values(); -void update_scale_values(const Vec3d& size, float scale); +void update_scale_values(double scaling_factor); // update rotation values object selection changing void update_rotation_values(); // update rotation value after "gizmos" -void update_rotation_value(const double angle, const std::string& axis); +void update_rotation_value(double angle, Axis axis); void set_uniform_scaling(const bool uniform_scale); void on_begin_drag(wxDataViewEvent &event); diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index c18fce44d0..03da43abec 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -154,10 +154,7 @@ void select_current_object(int idx) void remove_obj() %code%{ Slic3r::GUI::remove(); %}; - -void update_rotation_value(double angle, const char *axis) - %code%{ Slic3r::GUI::update_rotation_value(angle, axis); %}; - + std::string fold_utf8_to_ascii(const char *src) %code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %}; From e1417f182701858cc369a233096eb845d7a081f7 Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Tue, 4 Sep 2018 23:55:37 +0200 Subject: [PATCH 32/50] allow whitespaces for duet upload * now url_encoding characters which could not used for URLs --- xs/src/slic3r/Utils/Duet.cpp | 7 ++++--- xs/src/slic3r/Utils/Http.cpp | 12 ++++++++++++ xs/src/slic3r/Utils/Http.hpp | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp index 82a42eb710..a3dc4a3bb9 100644 --- a/xs/src/slic3r/Utils/Duet.cpp +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -197,7 +198,7 @@ std::string Duet::get_upload_url(const std::string &filename) const { return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%") % get_base_url() - % filename + % Http::url_encode(filename) % timestamp_str()).str(); } @@ -248,9 +249,10 @@ wxString Duet::format_error(const std::string &body, const std::string &error, u bool Duet::start_print(wxString &msg, const std::string &filename) const { bool res = false; + auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"") % get_base_url() - % filename).str(); + % Http::url_encode(filename)).str(); auto http = Http::get(std::move(url)); http.on_error([&](std::string body, std::string error, unsigned status) { @@ -275,5 +277,4 @@ int Duet::get_err_code_from_body(const std::string &body) const return root.get("err", 0); } - } diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index a92e399a08..3024b08fe0 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -421,6 +421,18 @@ bool Http::ca_file_supported() return res; } +std::string Http::url_encode(const std::string &str) +{ + ::CURL *curl = ::curl_easy_init(); + char *ce = ::curl_easy_escape(curl, str.c_str(), str.length()); + std::string encoded = std::string(ce); + + ::curl_free(ce); + if (curl != nullptr) { ::curl_easy_cleanup(curl); } + + return encoded; +} + std::ostream& operator<<(std::ostream &os, const Http::Progress &progress) { os << "Http::Progress(" diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index f1302b0ed9..44580b7eaf 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -98,6 +98,9 @@ public: // Tells whether current backend supports seting up a CA file using ca_file() static bool ca_file_supported(); + + // converts the given string to an url_encoded_string + static std::string url_encode(const std::string &str); private: Http(const std::string &url); From b9287d51dd89c0fdec8dcea398285055e13e71ce Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 6 Sep 2018 08:56:33 +0200 Subject: [PATCH 33/50] Tweaks to ModelObject::center_around_origin() --- xs/src/libslic3r/Model.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 6ad8ebb49f..93f0152c8b 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -669,10 +669,9 @@ void ModelObject::center_around_origin() if (! v->modifier) bb.merge(v->mesh.bounding_box()); - // First align to origin on XYZ, then center it on XY. - Vec3d size = bb.size(); - size(2) = 0.0; - Vec3d shift = -bb.min - 0.5 * size; + // Shift is the vector from the center of the bottom face of the bounding box to the origin + Vec3d shift = -bb.center(); + shift(2) = -bb.min(2); this->translate(shift); this->origin_translation += shift; From c8f136982438b264ef7030d5f228479b7bbf8ab9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 6 Sep 2018 09:16:32 +0200 Subject: [PATCH 34/50] Added Vec3d ModelInstance::transform_vector() method --- xs/src/libslic3r/Model.cpp | 5 +++++ xs/src/libslic3r/Model.hpp | 2 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 13 ++++++++----- xs/src/slic3r/GUI/GLGizmo.cpp | 6 ++---- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 93f0152c8b..19c474cad9 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -1016,6 +1016,11 @@ BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, b return bbox.transformed(world_matrix(dont_translate)); } +Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const +{ + return world_matrix(dont_translate) * v; +} + void ModelInstance::transform_polygon(Polygon* polygon) const { polygon->rotate(this->rotation); // rotate around polygon origin diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index bbfd2bde23..8a8af481c5 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -239,6 +239,8 @@ public: BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const; // Transform an external bounding box. BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; + // Transform an external vector. + Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const; // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 063b96cbe9..2bec6b66ba 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -5241,11 +5241,14 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) { // Move a regular object. ModelObject* model_object = m_model->objects[obj_idx]; - const Vec3d& offset = volume->get_offset(); - model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); - model_object->invalidate_bounding_box(); - update_position_values(); - object_moved = true; + if (model_object != nullptr) + { + const Vec3d& offset = volume->get_offset(); + model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); + model_object->invalidate_bounding_box(); + update_position_values(); + object_moved = true; + } } else if (obj_idx == 1000) // Move a wipe tower proxy. diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index e46cfa9252..1c0456fa8e 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1061,8 +1061,6 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const for (unsigned int i = 0; i < m_planes.size(); ++i) { - // FIXME: the color assignement will fail if the planes count is greater than 254 - // use the other color components in that case !! ::glColor3f(1.0f, 1.0f, picking_color_component(i)); for (const Vec2d& offset : m_instances_positions) { ::glPushMatrix(); @@ -1240,9 +1238,9 @@ void GLGizmoFlatten::update_planes() polygon = transform(polygon, m); } - // We'll sort the planes by area and only keep the 255 largest ones (because of the picking pass limitations): + // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); - m_planes.resize(std::min((int)m_planes.size(), 255)); + m_planes.resize(std::min((int)m_planes.size(), 254)); // Planes are finished - let's save what we calculated it from: m_source_data.bounding_boxes.clear(); From 8460926d36f777282e0cfc47a86341a6b1991fc2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 6 Sep 2018 16:10:31 +0200 Subject: [PATCH 35/50] Added select by part tool to toolbar --- lib/Slic3r/GUI/Plater.pm | 46 +++++++++++++++- resources/icons/toolbar.png | Bin 20451 -> 21874 bytes xs/src/slic3r/GUI/3DScene.cpp | 53 ++++++++++++++---- xs/src/slic3r/GUI/3DScene.hpp | 9 +++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 70 +++++++++++++++++++++--- xs/src/slic3r/GUI/GLCanvas3D.hpp | 8 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 19 +++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 ++ xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 1 - xs/xsp/GUI_3DScene.xsp | 24 ++++++++ 10 files changed, 211 insertions(+), 24 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b48ab0f97f..8a9d13aae3 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -85,9 +85,13 @@ sub new { # Initialize handlers for canvases my $on_select_object = sub { - my ($obj_idx) = @_; - # Ignore the special objects (the wipe tower proxy and such). - $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); + my ($obj_idx, $vol_idx) = @_; + + if (($obj_idx != -1) && ($vol_idx == -1)) { + # Ignore the special objects (the wipe tower proxy and such). + $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); + $self->item_changed_selection($obj_idx) if (defined($obj_idx)); + } }; my $on_double_click = sub { $self->object_settings_dialog if $self->selected_object; @@ -217,6 +221,29 @@ sub new { my $state = Slic3r::GUI::_3DScene::is_toolbar_item_pressed($self->{canvas3D}, "layersediting"); $self->on_layer_editing_toggled($state); }; + + my $on_action_selectbyparts = sub { + my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D}); + if ($curr eq 'volume') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + } + elsif ($curr eq 'object') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume'); + my $selections = []; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + + my ($obj_idx, $object) = $self->selected_object; + if (defined $obj_idx) { + my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx); + Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1); + } + } + }; # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { @@ -247,6 +274,7 @@ sub new { Slic3r::GUI::_3DScene::register_action_cut_callback($self->{canvas3D}, $on_action_cut); Slic3r::GUI::_3DScene::register_action_settings_callback($self->{canvas3D}, $on_action_settings); Slic3r::GUI::_3DScene::register_action_layersediting_callback($self->{canvas3D}, $on_action_layersediting); + Slic3r::GUI::_3DScene::register_action_selectbyparts_callback($self->{canvas3D}, $on_action_selectbyparts); Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_toolbar($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); @@ -2331,12 +2359,24 @@ sub selection_changed { } Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", $layers_height_allowed); + + my $can_select_by_parts = 0; if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; + $can_select_by_parts = ($obj_idx >= 0) && ($obj_idx < 1000) && ($model_object->volumes_count > 1); Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "fewer", $model_object->instances_count > 1); } + if ($can_select_by_parts) { + # first disable to let the item in the toolbar to switch to the unpressed state + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0); + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 1); + } else { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0); + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + } + if ($self->{object_info_size}) { # have we already loaded the info pane? if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; diff --git a/resources/icons/toolbar.png b/resources/icons/toolbar.png index ce954143dbe98cc25d31ea8831332e402ee395cb..79246bcf91761ca4d6c89393cbfd9a5f6a28e6ea 100644 GIT binary patch delta 5304 zcmZ`+2{=^k+dq>RC8;kE9>+#BwhrLZ2S+Bu+?>>P6g)_F4xy$}2(=1cN zAG3V#>phv06_sR14Uw0Eqwu+eQ7n^DgOHg~qF|qltxPrVu)nOVTATgla3%Zj<+|*i zBcpzC?*6act-V}7c7BiT&3d#%3C*3^P+nD}aFxJiWd>VYTbH=GQ&3}-!ny;G{kj9Z zeN{B_jm#`8O6u#a9335_!anUdhmcvI_(3K#GZn=EL2S)B>UT_0W4H88A|8FDY;=n= zYh3@Y5u8YFKR-cAT28LeTkajZka=NqbC^z__YBo31KVipb)91jH@g-HK4@xbu}7(! z!d6zr8MjH&aU=v(X@&R;WnEBE;O60B8WdDlW>fdC2f|no=>AO%xFNhXECK26Ogt+m zdDa+YE4(MW)Z$PjwY3jQ%uDgn>_SpXN+RvzY_Xakw9~~1O-)TxGqZy2?Wc9)o@dUS znZ3>D%LsjRqvug}PsV89_}9V$>{d6v(s65he`^uE!xp731yXNrRxa(I$)3u()h?}X zGBY!ihQ z0uK%ixs6mhN9|f!TfcYtoI&Etq*YY_y89-cxjp{TML{8G=%JC(o3^$)4;urGJZr3%<#&Hqpbd&Y;#^Fp-@$T?&_!l9UteR}a*GS1 zHeVvnTxL7@+YKEJ4UKy5Z!g^3U^g~q2LGM$0NSXj><`5GD!-C*j&Q#iap}?}DS7!K zP+73GhxL9spFe*V6cbC`Ol`5e%FJM9X;~B%bqYnQHDYIH-|rL)OGr9_2Ky~5fnMdd zx@2H)bH}BK*@?zcMr$gBb-|QYzU#0g5eS5g^z?3|T9#D1G_x99@jwrY{8TBdW9;Ko zks@L(^EOwHko82sKIBIaJ*}bj&`E~y{U7aT&CJcQimQz#Z|-ZqfB#;=f8kFX(NvGI zq3$I9_nzYl05M=Zns%pSqBZjc1q6OAk)4%AoF}|B17i-FCwM-pu(Rv9u5i`4FEcoyKsWB7Q3*`E+cN z|Kd=$ls*8QzMVcw zs;8Nm*}SXDK{|jP&SNBz5Fv+(P?V5(v$p0#i?%RWvL4(74zQXI>Ez8YlRN;HrcJ!A zZ^QU#t&d~h{wvxfib8q>0LbL%n3%1jgVn{}bV;}pWj%&NB{^FLAMH_Qw}J;JCdyQK zXz#zg`vzCa6?jk&qIC!xlIXOk_+Yk}2a)LYKj3E-rCt&?1*BOk1D9F zG`=8aOB93mbiqUSo)nR&G%$8xD5yJ=<*fV$-$z_?lbtO=x;@C_ctuEcNC-+K4h>=F z=3J8h{PV^N>;un!Vh3#$;$kVOt~QNR+D_x+=LZE(uK56}JT^L7JUVIxMx1wrs-mP+ z3aAhe*YvbQ=tkxwkx2YgP*89kuk{OaJUT)aWqOCtmPg+QZw*?2(W>@eG_SJ}-J>2g zvhb{Nu7Cl~pFEtC1!ZptST(nU?Q##pU2g|4ruwy5@1)yMG z2e2f}Ob>b5ZrhH~k0;=ns}24*1#*KPARr;=9R#)FB`5~yNR}&=kr5HTM*;IDd(^PO z!NH>Ze8&kyW*SFa_k_^FTRS<--49MB?FLGWtw;$3gntL!9w#_n*rNs?u63P0|FJEc zk^Qwgu?{|NOfw%OvfaRLq@=X4x+%f;V>8=pFs6R1z9`;W|3V zro9x6fbwV?X9Fkt1i0L%efp4QrY7 zp{o4~!FZOtS58QGXLqnXEcxgAiqg{3UfA5m6Y=|2?(Xh=2Y}{i+{3gLK=HzHuB^}Y zTp3pR1(+Xj`_&`wHcQ9#-dh|o^EEA0)Wjt+hI3`MCzaB>EtyTd3Zn>Eefia^zi2sd zI*OlbjEDcTcTdm%hWRo$`2IWdt-ZCmOs~1pI|dowUcY|bchIXcE{|BFEH5wr^Zxgh zm8TxzKzmAyihOL2<6#;n(6=^+9sohd_kEg?=C63HlM-M1Ph~ngMpW zj=jvu3Qc8nIHRAdsxq>0-Ctf9q(9{p=%oH!)xHl0$QZ!zMJI7=EK>#xtdYj=2`@Ux z8wZ3BfdJP(z-gamMqZD1aUCCj{BkquBup73K~_&}xw^BS0{t z$$P)a0u89ISMc}q>s7R8B}_|nZ?TJ0 zISRaULO$?E-rotY6n0i~FA$0|GG27}h51Q2>RH!#ifF@nU9YcXvL-cIJnfI*mcJHo zpz>90_=zuXm+5y>r{lE673kJG=#RMAqPK6gs8lK_jG2C{LF3j?7HY=PaTGsc`&VB# z8=?KI%DW_fLyj9*@0*x4dv9AkvgcYF1R^d;(;=Y^$&p zff5U2z1>sE#uCX(^Hott-NYK%>pz`tF(j^UBAV(x0a1MG*qB~!E_=b&G#^HH_BPKL zZg6A-E&^_F@ngd8K)tPS9+n1-?Dsa&;x|<&^d);7}qt%#oVqG4MVey`K$p8WI zr^Ooct|&ew{gtaCDVhI-()W4Hb8I^cF|qsZ6MV{e(#d^jt%nC_aB zE?Ed)k@r;xj`Co8*+iIwu)RD+;p$~%U?7r6Sp;AJ(ALc%b-+GQJWex&gWqQWqQJM< z+uQdoe)E11)}q4yZz7y`sZOj8J$Sl2Ql%Ald;L|2@?;i*49ISOEe?KXVH?EzvmF;K zfQ5hLbvP2;*O0Q6!Fxq}P%~>=GGkFW7|`0aqP0Y|L1@8n)9A6a`E|5f-b-Z9*DyTyIdcnGGdqiN+d&*^jQHhCp55{O zaB*2#-(qV568&s0-Ss>jskIf|ypg?14n+$I37G(^9uXPoY?-3GM`qm%QI?jK?U`94 zm&SqVybqDYYsQ~uvRqYucd*)G7aAH$U#H$vL`TYcy%R`_|NQxLd42@Pc_1rHFq+=E z-r9O$n;0JSrhNG6-;l*)&?g>e9~M3ts$nAGPAoUxl>eB3UM=g!;k$?J1L_n6sWk$iod0iOgB zG%YiOOwG*~aATf?0XSbvlKT~EYzR=ktn0I87NP;?6tIxs5VdP=v{wNpW>#e6*XVq~ zuLrL5I%9$Y`Lk0phsyOC06WlNjYdBHrg^9|8)c2WK8Qe+v54kRSwpd$)z_ z#6C}ZHLv;MH>j4+hh3HD+?VGt9iwSOu>YMCoZOs7Jm=D?M9!Uj{D0h*Hik!H@9?Nv zLjHPyBTMi2#TGIY-*Uz)$96>_l8F&ZRg+Z3=mp+3weGULGw0or(8N6G}ZSs zVn2l}fr%7Gl3g+_JCBbGP0uGaJL!038oP=*eB5G!VEWe5hTIdi8Wzxv?)YX((h>D> z#xZNMj3xf62y!&>=v?xOo*);Cm9Z3(_nV#Yd?oKg@q(5r3|xiLO_6fD2WJ}lJxJMf zRoh=U^EfjdTd32)k>FW_Ij+PA=%Y%1<1N$64YdN8moWq!e=%am;BT|~F341{0+UW0CUjQcSK;e&(&0C5w&)v8 zT&egP=A?6syf>LS&ZsCnufjpg)*x=x{Hq~?G)>=n_C_ji6IZefmg}PHG-Em(V_d|m z-!-|DLe@D_?iM;ZpVQB+emszP6 zPc7l9B<6Kbccy1pzwHXBT6zmZDc%Skokv9ji4)qAlEK1A&Al!^Mx5aRDUt8Yd>csb z8r47KAolPNg)M2_eJ_(>CsRS4onuGiXNTxqT3NadT+{_+3POj2SXz~;Cd(yF6pbA| z(K0BoM0`YAb6m71tY}r0Q{#BItnd|`zA4L7W`(<+FR5&;=i!@>iz~;oq#BKa&nbR? zC%OpFtDBDcDbb?^l$>=~Q=}a39cRRy;1QO##DjTDzpbX1ArPC6XHCaB2g7*R5<&_w z^vYF7b^Js6zwT$9-kMpGhX~yisKc(`*_vZC1*wvFLv8=Eb>*4)H-<@7Hf&H`^v`C{ k7y^QlzTV^GMDtD^BTO`U7f*Rx!}F`mH@XLj&_;9(V|FrCWY zGTRtkSf~1r8HTDZ>tdM| zx+e3KHj~SW)frNwvCbF6PuKT_IiN)y`n*_WHn6QNNjGJfKfcyStL!vz2xHxFNX*T+Pf9n_sdJ#HWNbxN07TC&~5&9DmU|K`A-c z8UKS7)rwixLO*%(m&kOIY}blFjG=1TFKQ`5N}_8;O+`hbvpf+Y z1^3?lvtp8<3F+NZfT%wd2#B|hjdoR4RaZjSi%z%Sl24+4_z-=%y}LWxh%qow@G)IO z;WY1D2SM^hNy}Q81B1b!e2Y@3^!%zKoq((>Q5dXtu_oT_WBmO2PDog|>R!wKr!Du6 z966GtC9{V>e7NdHyo)u!W_IcHC-|IGsa?G#(S=ri?g%e$L1`)4qcd2hHWOD%n!4}l zc{AR5uzGfOR)9D1JEcS_KRf%{%khqCuh&|5Gj2Xnb0Z@on&yMuoeQ961`<*2;{f^Y zDU*=)>Q!%H^s$H#PGVS(qV{*}P_ zJCjos$`wG^?CMMpPh`u0>0_ntGeJ;9)*-0f7ss%$y8rOu-GqdMD8uY40kerEb(2CD z)zlF21qB7T+1|4kH8ecH0*w(kI6EyZZFRn)i@1f9k`pPJqwHdrk(Ir8@nQ}_DofSv zlRKlpqM*1qX|?`Gk(ij6RajV~jEqdB_wW^<{tUy);XIN0gqA!aH5Tk4Q`_2D4=A6TG|rH=kYP^A_&Wv z&mC;T(x0A#mLN7r?qg)$AU{7dqgYW33Bu=DMj`Md;21EFn3^}<9$Q#kZ2WRu4u}A^ z@aZOwUy^3M6|Uoi1neMOU|>N-#cd}ir!*3YI7e9yb5O7>`n)vW{vA*HQe-XJwc>0| zjHi)HnzloRaXrS;Vu-jaL+k5-RW&sp7>vY9I5f&+3=I|C&NHE{Xn`oX=+KEba(cgh z&7Ui&FL7SE2DrL@5{g$-SI>nBgrmg{P4@i!{2;sFe1OF!D;svZtF3ewx3w8IZY_82 z>RapU1YlM224OJzV7A%$`Ci~A;O*nU-a|vLE*~}BB}Q} zDVUT8wz9CYntP!{{aiB|t}x0B=saK3A(##K*@qf;yPFHEdzxOm_5>tNO;z>w@}u91 zTh_GDU_X$k&CR*e01yzsI0z58GDkwu1cZS{Ux|g6SFvK#mY+4zf__;7_^)!5bu$8J zAS5J2_wUlqL+fl9)Qp4_W_7>)wp0tQ4@dim=WQ$Dop9~#U*jrp+w^2QR6T)d06Pb10ii z)oC4H`cS^SOG3~Od$@6XbIztIJdCC}1Z?ga?(N~x^N5oh6uNiq*nZ&iR!mvB_C|sx z)GU1xs(bBiWo0G4U!a%QDjrZ|z1+i&2j%^hCnED8Ch`y%N&tFM*}_%F8IgHFl?#GQ zA#m`3&%4Y+5$OOAd{;xd@cGfw-USIKyo-x0_jxG<1IR46S%h|B&NsXDqv6zO?B zHWt+iWZG^}URztEDK6a)J@mYF9Ph+quxQAcQh#f^gF`&SbUkTg#2yA!@^U<}@pfTp zsTCTHmSd`8D8V+>d zP`VJl7z#Z;!4HXd@ig~n25nj_)ry8pzlr*-Je293a}K2`il5L_w(B0cOGq(!87&Zf zzbDuD`16_USRIPE3=9`;z3!Vg|J{$-ffcPSoMDCZ4OD^u9OvYoj8=AEF<9i8;)Q%f^Y-nBp@?1h|5Ztk1N@TPB?M>( zW$u3Y*U__+CZ!F_QPIkNb{hV^r)B-iyfjxzy#@!WJo>U;4F!#cS+xn`Ny&BODf!VM z?X90J(y`BnIG`v+Xt9xL@46UT%U&PkJjuUT2lK=AOwHF$fqf~VczCJxWT$$jct9<- zam_M!PDud0z9vfw&YJkArkJBJ?8q%Z=4HOrTs$ze6$k9r&DFd(Tz@FgWkV55-EjT7 zAKVTarG}lvm2Xf6<}D)JNci0U?vS)Ova#`HY$S|1{3u;)FE_!`j(dL&{Bik7ulujKR3Y}(q|Cl|yk zl*HgI-@PH2i^KlZ$qd#^gkM9h@m|q3Ha6}(D;`)m!3&w`bA}E{V!>}AY0mY%^SU0fP zQe7P##dhEKBl(QJzQVjb*~Cy`Wjiey;Oo5l5DQvL;6q0CXVerkPrU6nNX`^;++MDE zUpoCVaq7AHGi8p1Zqu2BzfD8n3l1CcU4n}2C7$6aW?0eFIKDVrsEJ<@ci|_wcx3?^ zLs6AB49jCyn@U#4H-g1UBG=v{975<$O^HM@mJj)6V~c!@%zkUObp8*vK=Z!E{n4v0 z5_|1woIzzGvw3EJC52+ z@WToTQLd1d? zHBY3~$tYEgnw4`@d}GGGI}r*{1$Sc88o8-+;rHm?AIL#8>L7Z+o9y7CJ+|{*$t}6a ztyK5YIg5IFh9GU7ykOlIS-vxxfx2f!X+=1k81!{LUkq=zC2gy*s^|Nod#8}PEX$~R zdTh*U>->5JX|=D#EZ|GyjL1Tg##19CrQEtnWlb5$wJ9x2 zT3h@NzcO|Q0*j|VsbKvbD9N6?>pMBAKK=i?C_$t+eZx%+TbpEm$5$p|&L*gXlF~ge zlVu*|tZTB)qfecy;O=e$bjD145EUG*Bf9(Z^^UroQq@qGGu9Kbk*j*6xVv6P_v5@+T^Ndc2Y%a+{LbArHM*A!`#p!5j!(TU17q5Wj0mP3nBLNKa{Kv9Y!%ol?%s4Ie$i0#MWegzT}6IzZCmb`MWiT z+{~TH;Kv{9#XgR#I~WdN$+3#W3QR_W{P9u!e&D#r`y8t)`qLV*ABo-<%$f+fF}+yS zoKg<-liVJoc8`1yz1i+30G-WmLHrQBuTp>Q!)&6V_Dq@nAkVPwTor>%2vHhyIjk_6 z2Tex#m*8K8@#(3sw4s?n)Ma8__3?pNDOytaEQ+eYo?uNB32^D GLVolumeCollection::load_object( v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; - if (select_by == "object") - v.select_group_id = obj_idx * 1000000; - else if (select_by == "volume") - v.select_group_id = obj_idx * 1000000 + volume_idx * 1000; - else if (select_by == "instance") - v.select_group_id = v.composite_id; + v.set_select_group_id(select_by); if (drag_by == "object") v.drag_group_id = obj_idx * 1000; else if (drag_by == "instance") @@ -949,6 +954,15 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con } } +void GLVolumeCollection::set_select_by(const std::string& select_by) +{ + for (GLVolume *vol : this->volumes) + { + if (vol != nullptr) + vol->set_select_group_id(select_by); + } +} + std::vector GLVolumeCollection::get_current_print_zs(bool active_only) const { // Collect layer top positions of all volumes. @@ -1772,12 +1786,12 @@ void _3DScene::set_model(wxGLCanvas* canvas, Model* model) void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { - return s_canvas_mgr.set_bed_shape(canvas, shape); + s_canvas_mgr.set_bed_shape(canvas, shape); } void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) { - return s_canvas_mgr.set_auto_bed_shape(canvas); + s_canvas_mgr.set_auto_bed_shape(canvas); } BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas) @@ -1792,22 +1806,27 @@ void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) { - return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); + s_canvas_mgr.set_cutting_plane(canvas, z, polygons); } void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_color_by(canvas, value); + s_canvas_mgr.set_color_by(canvas, value); } void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_select_by(canvas, value); + s_canvas_mgr.set_select_by(canvas, value); } void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_drag_by(canvas, value); + s_canvas_mgr.set_drag_by(canvas, value); +} + +std::string _3DScene::get_select_by(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_select_by(canvas); } bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) @@ -2080,6 +2099,11 @@ void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void* s_canvas_mgr.register_action_layersediting_callback(canvas, callback); } +void _3DScene::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_selectbyparts_callback(canvas, callback); +} + static inline int hex_digit_to_int(const char c) { return @@ -2117,6 +2141,11 @@ std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, i return s_canvas_mgr.load_object(canvas, model, obj_idx); } +int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) +{ + return s_canvas_mgr.get_first_volume_id(canvas, obj_idx); +} + void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) { s_canvas_mgr.reload_scene(canvas, force); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 1785ecf4a4..ec9d1a5017 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -337,6 +337,8 @@ public: void set_convex_hull(const TriangleMesh& convex_hull); + void set_select_group_id(const std::string& select_by); + int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } int instance_idx() const { return this->composite_id % 1000; } @@ -446,6 +448,8 @@ public: void update_colors_by_extruder(const DynamicPrintConfig* config); + void set_select_by(const std::string& select_by); + // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; @@ -499,6 +503,8 @@ public: static void set_select_by(wxGLCanvas* canvas, const std::string& value); static void set_drag_by(wxGLCanvas* canvas, const std::string& value); + static std::string get_select_by(wxGLCanvas* canvas); + static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); static bool is_shader_enabled(wxGLCanvas* canvas); @@ -562,10 +568,13 @@ public: static void register_action_cut_callback(wxGLCanvas* canvas, void* callback); static void register_action_settings_callback(wxGLCanvas* canvas, void* callback); static void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); + static void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback); static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx); + static void reload_scene(wxGLCanvas* canvas, bool force); static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 2bec6b66ba..56bfb8f373 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2018,6 +2018,9 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) if (m_model == nullptr) return; + if (selections.empty()) + return; + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) { if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size())) @@ -2144,6 +2147,7 @@ void GLCanvas3D::set_color_by(const std::string& value) void GLCanvas3D::set_select_by(const std::string& value) { m_select_by = value; + m_volumes.set_select_by(value); } void GLCanvas3D::set_drag_by(const std::string& value) @@ -2151,6 +2155,11 @@ void GLCanvas3D::set_drag_by(const std::string& value) m_drag_by = value; } +const std::string& GLCanvas3D::get_select_by() const +{ + return m_select_by; +} + float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; @@ -2432,6 +2441,17 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) return std::vector(); } +int GLCanvas3D::get_first_volume_id(int obj_idx) const +{ + for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) + { + if ((m_volumes.volumes[i] != nullptr) && (m_volumes.volumes[i]->object_idx() == obj_idx)) + return i; + } + + return -1; +} + void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) @@ -2757,6 +2777,12 @@ void GLCanvas3D::register_action_layersediting_callback(void* callback) m_action_layersediting_callback.register_callback(callback); } +void GLCanvas3D::register_action_selectbyparts_callback(void* callback) +{ + if (callback != nullptr) + m_action_selectbyparts_callback.register_callback(callback); +} + void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -3043,7 +3069,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // propagate event through callback if (m_picking_enabled && (volume_idx != -1)) - _on_select(volume_idx); + _on_select(volume_idx, selected_object_idx); if (volume_idx != -1) { @@ -3287,7 +3313,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_picking_enabled && !m_toolbar_action_running) { deselect_volumes(); - _on_select(-1); + _on_select(-1, -1); update_gizmos_data(); } } @@ -3512,6 +3538,17 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.add_item(item)) return false; + if (!m_toolbar.add_separator()) + return false; + + item.name = "selectbyparts"; + item.tooltip = GUI::L_str("Select by parts"); + item.sprite_id = 10; + item.is_toggable = true; + item.action_callback = &m_action_selectbyparts_callback; + if (!m_toolbar.add_item(item)) + return false; + enable_toolbar_item("add", true); return true; @@ -3751,6 +3788,7 @@ void GLCanvas3D::_deregister_callbacks() m_action_cut_callback.deregister_callback(); m_action_settings_callback.deregister_callback(); m_action_layersediting_callback.deregister_callback(); + m_action_selectbyparts_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -5262,17 +5300,35 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) m_on_wipe_tower_moved_callback.call(wipe_tower_origin(0), wipe_tower_origin(1)); } -void GLCanvas3D::_on_select(int volume_idx) +void GLCanvas3D::_on_select(int volume_idx, int object_idx) { - int id = -1; + int vol_id = -1; + int obj_id = -1; + if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size())) { if (m_select_by == "volume") - id = m_volumes.volumes[volume_idx]->volume_idx(); + { + if (m_volumes.volumes[volume_idx]->object_idx() != object_idx) + { + set_select_by("object"); + obj_id = m_volumes.volumes[volume_idx]->object_idx(); + vol_id = -1; + } + else + { + obj_id = object_idx; + vol_id = m_volumes.volumes[volume_idx]->volume_idx(); + } + } else if (m_select_by == "object") - id = m_volumes.volumes[volume_idx]->object_idx(); + { + obj_id = m_volumes.volumes[volume_idx]->object_idx(); + vol_id = -1; + } } - m_on_select_object_callback.call(id); + + m_on_select_object_callback.call(obj_id, vol_id); } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2969d12c69..541495eecd 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -514,6 +514,7 @@ private: PerlCallback m_action_cut_callback; PerlCallback m_action_settings_callback; PerlCallback m_action_layersediting_callback; + PerlCallback m_action_selectbyparts_callback; public: GLCanvas3D(wxGLCanvas* canvas); @@ -556,6 +557,8 @@ public: void set_select_by(const std::string& value); void set_drag_by(const std::string& value); + const std::string& get_select_by() const; + float get_camera_zoom() const; BoundingBoxf3 volumes_bounding_box() const; @@ -597,6 +600,8 @@ public: std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(const Model& model, int obj_idx); + int get_first_volume_id(int obj_idx) const; + void reload_scene(bool force); void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); @@ -631,6 +636,7 @@ public: void register_action_cut_callback(void* callback); void register_action_settings_callback(void* callback); void register_action_layersediting_callback(void* callback); + void register_action_selectbyparts_callback(void* callback); void bind_event_handlers(); void unbind_event_handlers(); @@ -733,7 +739,7 @@ private: void _show_warning_texture_if_needed(); void _on_move(const std::vector& volume_idxs); - void _on_select(int volume_idx); + void _on_select(int volume_idx, int object_idx); // generates the legend texture in dependence of the current shown view type void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 57a49d7ab4..a09d83b893 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -338,6 +338,12 @@ void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value it->second->set_drag_by(value); } +std::string GLCanvas3DManager::get_select_by(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_select_by() : ""; +} + bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -536,6 +542,12 @@ std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector(); } +int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1; +} + void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -765,6 +777,13 @@ void GLCanvas3DManager::register_action_layersediting_callback(wxGLCanvas* canva it->second->register_action_layersediting_callback(callback); } +void GLCanvas3DManager::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_action_selectbyparts_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index f7705a56c6..1c715a9a38 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -98,6 +98,8 @@ public: void set_select_by(wxGLCanvas* canvas, const std::string& value); void set_drag_by(wxGLCanvas* canvas, const std::string& value); + std::string get_select_by(wxGLCanvas* canvas) const; + bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; bool is_shader_enabled(wxGLCanvas* canvas) const; @@ -135,6 +137,8 @@ public: std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const; + void reload_scene(wxGLCanvas* canvas, bool force); void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); @@ -171,6 +175,7 @@ public: void register_action_cut_callback(wxGLCanvas* canvas, void* callback); void register_action_settings_callback(wxGLCanvas* canvas, void* callback); void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); + void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index 9ec39b9761..af57db8ed8 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1572,7 +1572,6 @@ void update_position_values() void update_position_values(const Vec3d& position) { auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); og->set_value("position_x", int(position(0))); og->set_value("position_y", int(position(1))); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 6c199aacb5..756f9e5478 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -337,6 +337,14 @@ set_drag_by(canvas, value) CODE: _3DScene::set_drag_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value); +std::string +get_select_by(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_select_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_layers_editing_enabled(canvas) SV *canvas; @@ -720,6 +728,13 @@ register_action_layersediting_callback(canvas, callback) CODE: _3DScene::register_action_layersediting_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_action_selectbyparts_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_selectbyparts_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void reset_legend_texture() CODE: @@ -736,6 +751,15 @@ load_model_object(canvas, model_object, obj_idx, instance_idxs) OUTPUT: RETVAL +int +get_first_volume_id(canvas, obj_idx) + SV *canvas; + int obj_idx; + CODE: + RETVAL = _3DScene::get_first_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), obj_idx); + OUTPUT: + RETVAL + std::vector load_model(canvas, model, obj_idx) SV *canvas; From 6960958276457fe5fbcee0ad10fb1d3c991a41fd Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Thu, 6 Sep 2018 19:07:54 +0200 Subject: [PATCH 36/50] minor changes for url_encode --- xs/src/slic3r/Utils/Duet.cpp | 1 - xs/src/slic3r/Utils/Http.cpp | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp index a3dc4a3bb9..f253271611 100644 --- a/xs/src/slic3r/Utils/Duet.cpp +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index 3024b08fe0..9b67ceea87 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -424,11 +424,14 @@ bool Http::ca_file_supported() std::string Http::url_encode(const std::string &str) { ::CURL *curl = ::curl_easy_init(); + if (curl == nullptr) { + return str; + } char *ce = ::curl_easy_escape(curl, str.c_str(), str.length()); std::string encoded = std::string(ce); ::curl_free(ce); - if (curl != nullptr) { ::curl_easy_cleanup(curl); } + ::curl_easy_cleanup(curl); return encoded; } From 0ea18d57d0c27e4fe8d46f90ac9d620140d90dc5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 09:21:12 +0200 Subject: [PATCH 37/50] Fixed wipe tower selection --- lib/Slic3r/GUI/Plater.pm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 8a9d13aae3..016c2e8005 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2129,17 +2129,18 @@ sub on_config_change { $self->schedule_background_process; } -sub item_changed_selection{ +sub item_changed_selection { my ($self, $obj_idx) = @_; -# $self->{canvas}->Refresh; - if ($self->{canvas3D}) { - Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); - if ($obj_idx >= 0){ - my $selections = $self->collect_selections; - Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections); + if (($obj_idx >= 0) && ($obj_idx < 1000)) { # skip if wipe tower selected + if ($self->{canvas3D}) { + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + if ($obj_idx >= 0) { + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections); + } +# Slic3r::GUI::_3DScene::render($self->{canvas3D}); } - Slic3r::GUI::_3DScene::render($self->{canvas3D}); } } From 125ece0aadb51814473787a33852cf94a48a53fd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Sep 2018 09:33:18 +0200 Subject: [PATCH 38/50] Fixed #1204 --- lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm index 3b10ed99f7..ea4ce71329 100644 --- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm +++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm @@ -136,7 +136,7 @@ sub update_optgroup { full_labels => 1, label_font => $Slic3r::GUI::small_font, sidetext_font => $Slic3r::GUI::small_font, - label_width => 120, + label_width => 150, on_change => sub { $self->{on_change}->() if $self->{on_change} }, extra_column => sub { my ($line) = @_; From e04805eaa2593a7c8a5c521c33a3c1adc05ff976 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 10:30:13 +0200 Subject: [PATCH 39/50] Fixed rendering of endcaps on open toolpaths --- xs/src/slic3r/GUI/3DScene.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 7053470943..9cf55513c8 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1182,7 +1182,7 @@ static void thick_lines_to_indexed_vertex_array( b1_prev = b1; v_prev = v; - if (bottom_z_different) + if (bottom_z_different && (closed || (!is_first && !is_last))) { // Found a change of the layer thickness -> Add a cap at the beginning of this segment. volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); @@ -1190,10 +1190,10 @@ static void thick_lines_to_indexed_vertex_array( if (! closed) { // Terminate open paths with caps. - if (is_first && !bottom_z_different) + if (is_first) volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); // We don't use 'else' because both cases are true if we have only one line. - if (is_last && !bottom_z_different) + if (is_last) volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); } From ea15475ad87db66bfde5fc2a65a2b831773d4518 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 12:00:04 +0200 Subject: [PATCH 40/50] Fixed double click on gizmo overlay and toolbar --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 56bfb8f373..edc5bc78ec 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2978,7 +2978,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.position = Vec2d(-1.0, -1.0); m_dirty = true; } - else if (evt.LeftDClick() && (m_hover_volume_id != -1)) + else if (evt.LeftDClick() && (m_hover_volume_id != -1) && !gizmos_overlay_contains_mouse && (toolbar_contains_mouse == -1)) m_on_double_click_callback.call(); else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) { From ada6dba00b8ff33be8087bbe276856a2742bc6ef Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 12:20:56 +0200 Subject: [PATCH 41/50] Selection of picking color for gizmo grabber moved into GLGizmoBase --- xs/src/slic3r/GUI/GLGizmo.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 1c0456fa8e..75d818a55c 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -199,8 +199,11 @@ void GLGizmoBase::render_grabbers() const void GLGizmoBase::render_grabbers_for_picking() const { - for (int i = 0; i < (int)m_grabbers.size(); ++i) + for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { + m_grabbers[i].color[0] = 1.0f; + m_grabbers[i].color[1] = 1.0f; + m_grabbers[i].color[2] = picking_color_component(i); m_grabbers[i].render_for_picking(); } } @@ -335,12 +338,8 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const ::glDisable(GL_DEPTH_TEST); ::glPushMatrix(); + transform_to_local(); - - m_grabbers[0].color[0] = 1.0f; - m_grabbers[0].color[1] = 1.0f; - m_grabbers[0].color[2] = picking_color_component(0); - render_grabbers_for_picking(); ::glPopMatrix(); @@ -815,13 +814,6 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const { ::glDisable(GL_DEPTH_TEST); - for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) - { - m_grabbers[i].color[0] = 1.0f; - m_grabbers[i].color[1] = 1.0f; - m_grabbers[i].color[2] = picking_color_component(i); - } - render_grabbers_for_picking(); } From 775ec1b5467d0d27776bc6c598cda010d1137f2c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 13:40:26 +0200 Subject: [PATCH 42/50] Tweaks to gizmo scale 3D --- xs/src/slic3r/GUI/GLGizmo.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 75d818a55c..4e8a6d3d5a 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -15,9 +15,7 @@ static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f }; static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f }; static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f }; -static const float RED[3] = { 1.0f, 0.0f, 0.0f }; -static const float GREEN[3] = { 0.0f, 1.0f, 0.0f }; -static const float BLUE[3] = { 0.0f, 0.0f, 1.0f }; +static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; namespace Slic3r { namespace GUI { @@ -525,9 +523,9 @@ bool GLGizmoRotate3D::on_init() if (!m_x.init() || !m_y.init() || !m_z.init()) return false; - m_x.set_highlight_color(RED); - m_y.set_highlight_color(GREEN); - m_z.set_highlight_color(BLUE); + m_x.set_highlight_color(AXES_COLOR[0]); + m_y.set_highlight_color(AXES_COLOR[1]); + m_z.set_highlight_color(AXES_COLOR[2]); std::string path = resources_dir() + "/icons/overlay/"; @@ -702,20 +700,20 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const // x axis m_grabbers[0].center = Vec3d(m_box.min(0), center(1), center(2)); m_grabbers[1].center = Vec3d(m_box.max(0), center(1), center(2)); - ::memcpy((void*)m_grabbers[0].color, (const void*)RED, 3 * sizeof(float)); - ::memcpy((void*)m_grabbers[1].color, (const void*)RED, 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); // y axis m_grabbers[2].center = Vec3d(center(0), m_box.min(1), center(2)); m_grabbers[3].center = Vec3d(center(0), m_box.max(1), center(2)); - ::memcpy((void*)m_grabbers[2].color, (const void*)GREEN, 3 * sizeof(float)); - ::memcpy((void*)m_grabbers[3].color, (const void*)GREEN, 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[3].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); // z axis m_grabbers[4].center = Vec3d(center(0), center(1), m_box.min(2)); m_grabbers[5].center = Vec3d(center(0), center(1), m_box.max(2)); - ::memcpy((void*)m_grabbers[4].color, (const void*)BLUE, 3 * sizeof(float)); - ::memcpy((void*)m_grabbers[5].color, (const void*)BLUE, 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[4].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[5].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); // uniform m_grabbers[6].center = Vec3d(m_box.min(0), m_box.min(1), m_box.min(2)); @@ -734,6 +732,13 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const // draw box ::glColor3fv(m_base_color); render_box(m_box); + // draw connections + ::glColor3fv(m_grabbers[0].color); + render_grabbers_connection(0, 1); + ::glColor3fv(m_grabbers[2].color); + render_grabbers_connection(2, 3); + ::glColor3fv(m_grabbers[4].color); + render_grabbers_connection(4, 5); // draw grabbers render_grabbers(); } From e1deb03d3cc469f29aff0bfe461b782330526250 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 14:21:04 +0200 Subject: [PATCH 43/50] Added perl callback for three doubles --- xs/src/libslic3r/Utils.hpp | 3 ++- xs/src/libslic3r/utils.cpp | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 3492228543..f1390b8a29 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -97,8 +97,9 @@ public: void call(int i) const; void call(int i, int j) const; void call(const std::vector& ints) const; - void call(double d) const; + void call(double a) const; void call(double a, double b) const; + void call(double a, double b, double c) const; void call(double a, double b, double c, double d) const; void call(bool b) const; private: diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 55164bbdd6..95aaf5453c 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -251,7 +251,7 @@ void PerlCallback::call(const std::vector& ints) const LEAVE; } -void PerlCallback::call(double d) const +void PerlCallback::call(double a) const { if (!m_callback) return; @@ -259,7 +259,7 @@ void PerlCallback::call(double d) const ENTER; SAVETMPS; PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(d))); + XPUSHs(sv_2mortal(newSVnv(a))); PUTBACK; perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); FREETMPS; @@ -282,6 +282,23 @@ void PerlCallback::call(double a, double b) const LEAVE; } +void PerlCallback::call(double a, double b, double c) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + void PerlCallback::call(double a, double b, double c, double d) const { if (!m_callback) From 62894d3f7b841436c1fb849336b4e0dd15726fad Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 7 Sep 2018 16:05:10 +0200 Subject: [PATCH 44/50] Export the first M73 line to gcode before custom gcode --- xs/src/libslic3r/GCode.cpp | 9 ++++++++- xs/src/libslic3r/GCodeTimeEstimator.cpp | 13 ++++++++++++- xs/src/libslic3r/GCodeTimeEstimator.hpp | 3 +++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index b34ba54415..67a682d188 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -665,6 +665,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write_format(file, "\n"); } + // adds tags for time estimators + if (print.config.remaining_times.value) + { + _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag); + if (m_silent_time_estimator_enabled) + _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag); + } + // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser; m_placeholder_parser.update_timestamp(); @@ -724,7 +732,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming); std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id); - // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); // Set extruder(s) temperature before and after start G-code. diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index c4ffb572a9..f4f6472e56 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -168,6 +168,9 @@ namespace Slic3r { } #endif // ENABLE_MOVE_STATS + const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER"; + const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; SILENT_FIRST_M73_OUTPUT_PLACEHOLDER"; + GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) : _mode(mode) { @@ -294,7 +297,15 @@ namespace Slic3r { throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n")); } - gcode_line += "\n"; + // replaces placeholders for initial line M73 with the real lines + if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || + ((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) + { + sprintf(time_line, time_mask.c_str(), std::to_string(0), _get_time_minutes(_time).c_str()); + gcode_line = time_line; + } + else + gcode_line += "\n"; // add remaining time lines where needed _parser.parse_line(gcode_line, diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp index 1fa74e304c..e9da584c31 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.hpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp @@ -17,6 +17,9 @@ namespace Slic3r { class GCodeTimeEstimator { public: + static const std::string Normal_First_M73_Output_Placeholder_Tag; + static const std::string Silent_First_M73_Output_Placeholder_Tag; + enum EMode : unsigned char { Normal, From 5e8bd4798834d0d5f22afb58acb13dd081585742 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 10 Sep 2018 10:01:49 +0200 Subject: [PATCH 45/50] Partial refactoring of 3d gizmos --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 23 +----- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 +- xs/src/slic3r/GUI/GLGizmo.cpp | 129 +++++++++++++------------------ xs/src/slic3r/GUI/GLGizmo.hpp | 74 +++++++++--------- 4 files changed, 91 insertions(+), 138 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index edc5bc78ec..161ce89da9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1320,16 +1320,6 @@ void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray) curr->update(mouse_ray); } -void GLCanvas3D::Gizmos::refresh() -{ - if (!m_enabled) - return; - - GLGizmoBase* curr = _get_current(); - if (curr != nullptr) - curr->refresh(); -} - GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const { return m_current; @@ -1349,12 +1339,12 @@ bool GLCanvas3D::Gizmos::is_dragging() const return m_dragging; } -void GLCanvas3D::Gizmos::start_dragging() +void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) { m_dragging = true; GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->start_dragging(); + curr->start_dragging(box); } void GLCanvas3D::Gizmos::stop_dragging() @@ -2487,8 +2477,6 @@ void GLCanvas3D::reload_scene(bool force) if (!m_objects_selections.empty()) update_gizmos_data(); - m_gizmos.refresh(); - if (m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? @@ -3019,7 +3007,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) { update_gizmos_data(); - m_gizmos.start_dragging(); + m_gizmos.start_dragging(_selected_volumes_bounding_box()); m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); if (m_gizmos.get_current_type() == Gizmos::Flatten) { @@ -3062,7 +3050,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } update_gizmos_data(); - m_gizmos.refresh(); m_dirty = true; } } @@ -3158,7 +3145,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) update_position_values(volume->get_offset()); m_mouse.drag.start_position_3D = cur_pos; - m_gizmos.refresh(); m_dirty = true; } @@ -3226,9 +3212,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale()); } - if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1)) - m_gizmos.refresh(); - m_dirty = true; } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 541495eecd..c5b4581fad 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -367,14 +367,13 @@ public: bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; void update(const Linef3& mouse_ray); - void refresh(); EType get_current_type() const; bool is_running() const; bool is_dragging() const; - void start_dragging(); + void start_dragging(const BoundingBoxf3& box); void stop_dragging(); float get_scale() const; diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 4e8a6d3d5a..ff0adac62f 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -129,6 +129,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent) , m_group_id(-1) , m_state(Off) , m_hover_id(-1) + , m_dragging(false) { ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float)); ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float)); @@ -150,18 +151,21 @@ void GLGizmoBase::set_highlight_color(const float* color) ::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float)); } -void GLGizmoBase::start_dragging() +void GLGizmoBase::start_dragging(const BoundingBoxf3& box) { + m_dragging = true; + for (int i = 0; i < (int)m_grabbers.size(); ++i) { m_grabbers[i].dragging = (m_hover_id == i); } - on_start_dragging(); + on_start_dragging(box); } void GLGizmoBase::stop_dragging() { + m_dragging = false; set_tooltip(""); for (int i = 0; i < (int)m_grabbers.size(); ++i) @@ -235,7 +239,6 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) , m_angle(0.0) , m_center(0.0, 0.0, 0.0) , m_radius(0.0f) - , m_keep_initial_values(false) { } @@ -253,6 +256,12 @@ bool GLGizmoRotate::on_init() return true; } +void GLGizmoRotate::on_start_dragging(const BoundingBoxf3& box) +{ + m_center = box.center(); + m_radius = Offset + box.radius(); +} + void GLGizmoRotate::on_update(const Linef3& mouse_ray) { Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(mouse_ray)); @@ -294,18 +303,16 @@ void GLGizmoRotate::on_update(const Linef3& mouse_ray) void GLGizmoRotate::on_render(const BoundingBoxf3& box) const { - if (m_grabbers[0].dragging) + if (m_dragging) set_tooltip(format(m_angle * 180.0f / (float)PI, 4)); - - ::glEnable(GL_DEPTH_TEST); - - if (!m_keep_initial_values) + else { m_center = box.center(); m_radius = Offset + box.radius(); - m_keep_initial_values = true; } + ::glEnable(GL_DEPTH_TEST); + ::glPushMatrix(); transform_to_local(); @@ -509,23 +516,29 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray) cons GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent) : GLGizmoBase(parent) - , m_x(parent, GLGizmoRotate::X) - , m_y(parent, GLGizmoRotate::Y) - , m_z(parent, GLGizmoRotate::Z) { - m_x.set_group_id(0); - m_y.set_group_id(1); - m_z.set_group_id(2); + m_gizmos.emplace_back(parent, GLGizmoRotate::X); + m_gizmos.emplace_back(parent, GLGizmoRotate::Y); + m_gizmos.emplace_back(parent, GLGizmoRotate::Z); + + for (unsigned int i = 0; i < 3; ++i) + { + m_gizmos[i].set_group_id(i); + } } bool GLGizmoRotate3D::on_init() { - if (!m_x.init() || !m_y.init() || !m_z.init()) - return false; + for (GLGizmoRotate& g : m_gizmos) + { + if (!g.init()) + return false; + } - m_x.set_highlight_color(AXES_COLOR[0]); - m_y.set_highlight_color(AXES_COLOR[1]); - m_z.set_highlight_color(AXES_COLOR[2]); + for (unsigned int i = 0; i < 3; ++i) + { + m_gizmos[i].set_highlight_color(AXES_COLOR[i]); + } std::string path = resources_dir() + "/icons/overlay/"; @@ -544,68 +557,28 @@ bool GLGizmoRotate3D::on_init() return true; } -void GLGizmoRotate3D::on_start_dragging() +void GLGizmoRotate3D::on_start_dragging(const BoundingBoxf3& box) { - switch (m_hover_id) - { - case 0: - { - m_x.start_dragging(); - break; - } - case 1: - { - m_y.start_dragging(); - break; - } - case 2: - { - m_z.start_dragging(); - break; - } - default: - { - break; - } - } + if ((0 <= m_hover_id) && (m_hover_id < 3)) + m_gizmos[m_hover_id].start_dragging(box); } void GLGizmoRotate3D::on_stop_dragging() { - switch (m_hover_id) - { - case 0: - { - m_x.stop_dragging(); - break; - } - case 1: - { - m_y.stop_dragging(); - break; - } - case 2: - { - m_z.stop_dragging(); - break; - } - default: - { - break; - } - } + if ((0 <= m_hover_id) && (m_hover_id < 3)) + m_gizmos[m_hover_id].stop_dragging(); } void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const { if ((m_hover_id == -1) || (m_hover_id == 0)) - m_x.render(box); + m_gizmos[X].render(box); if ((m_hover_id == -1) || (m_hover_id == 1)) - m_y.render(box); + m_gizmos[Y].render(box); if ((m_hover_id == -1) || (m_hover_id == 2)) - m_z.render(box); + m_gizmos[Z].render(box); } const float GLGizmoScale3D::Offset = 5.0f; @@ -652,13 +625,13 @@ bool GLGizmoScale3D::on_init() return true; } -void GLGizmoScale3D::on_start_dragging() +void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box) { if (m_hover_id != -1) { m_starting_drag_position = m_grabbers[m_hover_id].center; m_show_starting_box = true; - m_starting_box = m_box; + m_starting_box = box; } } @@ -989,7 +962,8 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) : GLGizmoBase(parent) - , m_normal(0.0, 0.0, 0.0) + , m_normal(Vec3d::Zero()) + , m_center(Vec3d::Zero()) { } @@ -1012,10 +986,13 @@ bool GLGizmoFlatten::on_init() return true; } -void GLGizmoFlatten::on_start_dragging() +void GLGizmoFlatten::on_start_dragging(const BoundingBoxf3& box) { if (m_hover_id != -1) + { m_normal = m_planes[m_hover_id].normal; + m_center = box.center(); + } } void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const @@ -1023,10 +1000,9 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const // the dragged_offset is a vector measuring where was the object moved // with the gizmo being on. This is reset in set_flattening_data and // does not work correctly when there are multiple copies. - if (!m_center) // this is the first bounding box that we see - m_center.reset(new Vec3d(box.center())); - - Vec3d dragged_offset = box.center() - *m_center; + Vec3d dragged_offset(Vec3d::Zero()); + if (m_dragging) + dragged_offset = box.center() - m_center; ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); @@ -1073,7 +1049,6 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) { - m_center.release(); // object is not being dragged (this would not be called otherwise) - we must forget about the bounding box position... m_model_object = model_object; // ...and save the updated positions of the object instances: diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 3041f2adf4..82a004b5b4 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -57,6 +57,7 @@ protected: // textures are assumed to be square and all with the same size in pixels, no internal check is done GLTexture m_textures[Num_States]; int m_hover_id; + bool m_dragging; float m_base_color[3]; float m_drag_color[3]; float m_highlight_color[3]; @@ -82,10 +83,11 @@ public: void set_highlight_color(const float* color); - void start_dragging(); + void start_dragging(const BoundingBoxf3& box); void stop_dragging(); + bool is_dragging() const { return m_dragging; } + void update(const Linef3& mouse_ray); - void refresh() { on_refresh(); } void render(const BoundingBoxf3& box) const { on_render(box); } void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); } @@ -94,10 +96,9 @@ protected: virtual bool on_init() = 0; virtual void on_set_state() {} virtual void on_set_hover_id() {} - virtual void on_start_dragging() {} + virtual void on_start_dragging(const BoundingBoxf3& box) {} virtual void on_stop_dragging() {} virtual void on_update(const Linef3& mouse_ray) = 0; - virtual void on_refresh() {} virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; @@ -136,7 +137,6 @@ private: mutable Vec3d m_center; mutable float m_radius; - mutable bool m_keep_initial_values; public: GLGizmoRotate(GLCanvas3D& parent, Axis axis); @@ -146,9 +146,8 @@ public: protected: virtual bool on_init(); - virtual void on_set_state() { m_keep_initial_values = (m_state == On) ? false : true; } + virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_update(const Linef3& mouse_ray); - virtual void on_refresh() { m_keep_initial_values = false; } virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; @@ -167,56 +166,52 @@ private: class GLGizmoRotate3D : public GLGizmoBase { - GLGizmoRotate m_x; - GLGizmoRotate m_y; - GLGizmoRotate m_z; + std::vector m_gizmos; public: explicit GLGizmoRotate3D(GLCanvas3D& parent); - double get_angle_x() const { return m_x.get_angle(); } - void set_angle_x(double angle) { m_x.set_angle(angle); } + double get_angle_x() const { return m_gizmos[X].get_angle(); } + void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); } - double get_angle_y() const { return m_y.get_angle(); } - void set_angle_y(double angle) { m_y.set_angle(angle); } + double get_angle_y() const { return m_gizmos[Y].get_angle(); } + void set_angle_y(double angle) { m_gizmos[Y].set_angle(angle); } - double get_angle_z() const { return m_z.get_angle(); } - void set_angle_z(double angle) { m_z.set_angle(angle); } + double get_angle_z() const { return m_gizmos[Z].get_angle(); } + void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); } protected: virtual bool on_init(); virtual void on_set_state() { - m_x.set_state(m_state); - m_y.set_state(m_state); - m_z.set_state(m_state); + for (GLGizmoRotate& g : m_gizmos) + { + g.set_state(m_state); + } } virtual void on_set_hover_id() { - m_x.set_hover_id(m_hover_id == 0 ? 0 : -1); - m_y.set_hover_id(m_hover_id == 1 ? 0 : -1); - m_z.set_hover_id(m_hover_id == 2 ? 0 : -1); + for (unsigned int i = 0; i < 3; ++i) + { + m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); + } } - virtual void on_start_dragging(); + virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_stop_dragging(); virtual void on_update(const Linef3& mouse_ray) { - m_x.update(mouse_ray); - m_y.update(mouse_ray); - m_z.update(mouse_ray); - } - virtual void on_refresh() - { - m_x.refresh(); - m_y.refresh(); - m_z.refresh(); + for (GLGizmoRotate& g : m_gizmos) + { + g.update(mouse_ray); + } } virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const { - m_x.render_for_picking(box); - m_y.render_for_picking(box); - m_z.render_for_picking(box); + for (const GLGizmoRotate& g : m_gizmos) + { + g.render_for_picking(box); + } } }; @@ -249,7 +244,7 @@ public: protected: virtual bool on_init(); - virtual void on_start_dragging(); + virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_stop_dragging() { m_show_starting_box = false; } virtual void on_update(const Linef3& mouse_ray); virtual void on_render(const BoundingBoxf3& box) const; @@ -291,7 +286,7 @@ private: std::vector m_planes; std::vector m_instances_positions; - mutable std::unique_ptr m_center = nullptr; + Vec3d m_center; const ModelObject* m_model_object = nullptr; void update_planes(); @@ -305,11 +300,12 @@ public: protected: virtual bool on_init(); - virtual void on_start_dragging(); + virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_update(const Linef3& mouse_ray) {} virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; - virtual void on_set_state() { + virtual void on_set_state() + { if (m_state == On && is_plane_update_necessary()) update_planes(); } From d8936b1ad8e14372e911c7a7ebb7f1adc30ceae3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 10 Sep 2018 11:58:24 +0200 Subject: [PATCH 46/50] Another partial refactoring of 3d gizmos --- xs/src/slic3r/GUI/GLGizmo.cpp | 154 ++++++++++++++++++++-------------- xs/src/slic3r/GUI/GLGizmo.hpp | 2 +- 2 files changed, 93 insertions(+), 63 deletions(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index ff0adac62f..1d02e17526 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -20,7 +20,90 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f namespace Slic3r { namespace GUI { -const float GLGizmoBase::Grabber::HalfSize = 2.0f; + // returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center + // coordinates are local to the plane + Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center) + { + Transform3d m = Transform3d::Identity(); + m.translate(-center); + Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); + return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0); + } + + // returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center + // coordinates are local to the plane + Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center) + { + Transform3d m = Transform3d::Identity(); + m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX())); + m.translate(-center); + Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); + return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1)); + } + + // returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center + // coordinates are local to the plane + Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center) + { + Transform3d m = Transform3d::Identity(); + m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY())); + m.translate(-center); + Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); + + return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0)); + } + + // return an index: + // 0 for plane XY + // 1 for plane XZ + // 2 for plane YZ + // which indicates which plane is best suited for intersecting the given unit vector + // giving precedence to the plane with the given index + unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane) + { + unsigned int ret = preferred_plane; + + // 1st checks if the given vector is not parallel to the given preferred plane + double dot_to_normal = 0.0; + switch (ret) + { + case 0: // plane xy + { + dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ())); + break; + } + case 1: // plane xz + { + dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY())); + break; + } + case 2: // plane yz + { + dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX())); + break; + } + default: + { + break; + } + } + + // if almost parallel, select the plane whose normal direction is closest to the given vector direction, + // otherwise return the given preferred plane index + if (dot_to_normal < 0.1) + { + typedef std::map ProjsMap; + ProjsMap projs_map; + projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy + projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz + projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz + ret = projs_map.rbegin()->second; + } + + return ret; + } + + const float GLGizmoBase::Grabber::HalfSize = 2.0f; const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; GLGizmoBase::Grabber::Grabber() @@ -881,77 +964,24 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& Vec3d starting_vec_dir = starting_vec.normalized(); Vec3d mouse_dir = mouse_ray.unit_vector(); - unsigned int plane_id = preferred_plane_id; - // 1st try to see if the mouse direction is close enough to the preferred plane normal - double dot_to_normal = 0.0; + unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id); + // ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length switch (plane_id) { case 0: { - dot_to_normal = std::abs(mouse_dir.dot(Vec3d::UnitZ())); + ratio = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, center)) / len_starting_vec; break; } case 1: { - dot_to_normal = std::abs(mouse_dir.dot(-Vec3d::UnitY())); + ratio = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, center)) / len_starting_vec; break; } case 2: { - dot_to_normal = std::abs(mouse_dir.dot(Vec3d::UnitX())); - break; - } - } - - if (dot_to_normal < 0.1) - { - // if not, select the plane who's normal is closest to the mouse direction - - typedef std::map ProjsMap; - ProjsMap projs_map; - - projs_map.insert(ProjsMap::value_type(std::abs(mouse_dir.dot(Vec3d::UnitZ())), 0)); // plane xy - projs_map.insert(ProjsMap::value_type(std::abs(mouse_dir.dot(-Vec3d::UnitY())), 1)); // plane xz - projs_map.insert(ProjsMap::value_type(std::abs(mouse_dir.dot(Vec3d::UnitX())), 2)); // plane yz - plane_id = projs_map.rbegin()->second; - } - - switch (plane_id) - { - case 0: - { - // calculates the intersection of the mouse ray with the plane parallel to plane XY and passing through the given center - Transform3d m = Transform3d::Identity(); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(mouse_ray, m).intersect_plane(0.0)); - - // ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length - ratio = starting_vec_dir.dot(Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0)) / len_starting_vec; - break; - } - case 1: - { - // calculates the intersection of the mouse ray with the plane parallel to plane XZ and passing through the given center - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX())); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(mouse_ray, m).intersect_plane(0.0)); - - // ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length - ratio = starting_vec_dir.dot(Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1))) / len_starting_vec; - break; - } - case 2: - { - // calculates the intersection of the mouse ray with the plane parallel to plane YZ and passing through the given center - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY())); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(mouse_ray, m).intersect_plane(0.0)); - - // ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length - ratio = starting_vec_dir.dot(Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0))) / len_starting_vec; + ratio = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, center)) / len_starting_vec; break; } } @@ -963,7 +993,7 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) : GLGizmoBase(parent) , m_normal(Vec3d::Zero()) - , m_center(Vec3d::Zero()) + , m_starting_center(Vec3d::Zero()) { } @@ -991,7 +1021,7 @@ void GLGizmoFlatten::on_start_dragging(const BoundingBoxf3& box) if (m_hover_id != -1) { m_normal = m_planes[m_hover_id].normal; - m_center = box.center(); + m_starting_center = box.center(); } } @@ -1002,7 +1032,7 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const // does not work correctly when there are multiple copies. Vec3d dragged_offset(Vec3d::Zero()); if (m_dragging) - dragged_offset = box.center() - m_center; + dragged_offset = box.center() - m_starting_center; ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 82a004b5b4..0599955ed5 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -286,7 +286,7 @@ private: std::vector m_planes; std::vector m_instances_positions; - Vec3d m_center; + Vec3d m_starting_center; const ModelObject* m_model_object = nullptr; void update_planes(); From be508b003a5348aaf3c79a7eeb14b3b90f197b41 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 10 Sep 2018 13:15:12 +0200 Subject: [PATCH 47/50] Fixed #1211 --- lib/Slic3r/GUI/Plater.pm | 9 +++++++++ xs/src/libslic3r/Format/3mf.cpp | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ee2689d164..e2c95859fa 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -767,6 +767,15 @@ sub load_files { $model->convert_multipart_object(scalar(@$nozzle_dmrs)) if $dialog->ShowModal() == wxID_YES; } + # objects imported from 3mf require a call to center_around_origin to have gizmos working properly and this call + # need to be done after looks_like_multipart_object detection + if ($input_file =~ /.3[mM][fF]$/) + { + foreach my $model_object (@{$model->objects}) { + $model_object->center_around_origin; # also aligns object to Z = 0 + } + } + if ($one_by_one) { push @obj_idx, $self->load_model_objects(@{$model->objects}); } else { diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 945bb1f868..5de1d26c5f 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -603,8 +603,6 @@ namespace Slic3r { if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr)) return false; - - object.second->center_around_origin(); } // fixes the min z of the model if negative From 78bc688a7fa601900502cf1821bd396e2a946af8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 10 Sep 2018 13:48:35 +0200 Subject: [PATCH 48/50] Fixed compile on Linux --- xs/src/libslic3r/GCodeTimeEstimator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index f4f6472e56..7471367fe5 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -301,7 +301,7 @@ namespace Slic3r { if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || ((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) { - sprintf(time_line, time_mask.c_str(), std::to_string(0), _get_time_minutes(_time).c_str()); + sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str()); gcode_line = time_line; } else From 781df150a13a4ae3b55109fb728ef469cfbc8f7a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 10 Sep 2018 14:10:08 +0200 Subject: [PATCH 49/50] Added perl callback for gizmo flatten --- lib/Slic3r/GUI/Plater.pm | 16 ++++++++-------- xs/src/slic3r/GUI/3DScene.cpp | 5 +++++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 9 ++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 +++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 7 +++++++ 8 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9b3457ef4b..951f3bd0f9 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -153,16 +153,15 @@ sub new { }; # callback to react to gizmo rotate - # omitting last three parameters means rotation around Z - # otherwise they are the components of the rotation axis vector my $on_gizmo_rotate = sub { + my ($angle) = @_; + $self->rotate(rad2deg($angle), Z, 'absolute'); + }; + + # callback to react to gizmo flatten + my $on_gizmo_flatten = sub { my ($angle, $axis_x, $axis_y, $axis_z) = @_; - if (!defined $axis_x) { - $self->rotate(rad2deg($angle), Z, 'absolute'); - } - else { - $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; - } + $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; }; # callback to update object's geometry info while using gizmos @@ -263,6 +262,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate); + Slic3r::GUI::_3DScene::register_on_gizmo_flatten_callback($self->{canvas3D}, $on_gizmo_flatten); Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info); Slic3r::GUI::_3DScene::register_action_add_callback($self->{canvas3D}, $on_action_add); Slic3r::GUI::_3DScene::register_action_delete_callback($self->{canvas3D}, $on_action_delete); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 57639fed0a..dc2f5dd5ae 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2044,6 +2044,11 @@ void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callb s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); } +void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback); +} + void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index ec9d1a5017..df749d1bb5 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -556,6 +556,7 @@ public: static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); static void register_action_add_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 161ce89da9..94de895b47 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2699,6 +2699,12 @@ void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback) m_on_gizmo_rotate_callback.register_callback(callback); } +void GLCanvas3D::register_on_gizmo_flatten_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_flatten_callback.register_callback(callback); +} + void GLCanvas3D::register_on_update_geometry_info_callback(void* callback) { if (callback != nullptr) @@ -3016,7 +3022,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) { Vec3d axis = normal(2) > 0.999 ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()).normalized(); float angle = acos(clamp(-1.0, 1.0, -normal(2))); - m_on_gizmo_rotate_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); + m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); } } @@ -3759,6 +3765,7 @@ void GLCanvas3D::_deregister_callbacks() m_on_enable_action_buttons_callback.deregister_callback(); m_on_gizmo_scale_uniformly_callback.deregister_callback(); m_on_gizmo_rotate_callback.deregister_callback(); + m_on_gizmo_flatten_callback.deregister_callback(); m_on_update_geometry_info_callback.deregister_callback(); m_action_add_callback.deregister_callback(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index c5b4581fad..55a3075cee 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -501,6 +501,7 @@ private: PerlCallback m_on_enable_action_buttons_callback; PerlCallback m_on_gizmo_scale_uniformly_callback; PerlCallback m_on_gizmo_rotate_callback; + PerlCallback m_on_gizmo_flatten_callback; PerlCallback m_on_update_geometry_info_callback; PerlCallback m_action_add_callback; @@ -623,6 +624,7 @@ public: void register_on_enable_action_buttons_callback(void* callback); void register_on_gizmo_scale_uniformly_callback(void* callback); void register_on_gizmo_rotate_callback(void* callback); + void register_on_gizmo_flatten_callback(void* callback); void register_on_update_geometry_info_callback(void* callback); void register_action_add_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index a09d83b893..3445d4b652 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -700,6 +700,13 @@ void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, vo it->second->register_on_gizmo_rotate_callback(callback); } +void GLCanvas3DManager::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_flatten_callback(callback); +} + void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 1c715a9a38..b808c022e9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -163,6 +163,7 @@ public: void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); + void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); void register_action_add_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 756f9e5478..4c3c5501e1 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -651,6 +651,13 @@ register_on_gizmo_rotate_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_flatten_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_flatten_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_update_geometry_info_callback(canvas, callback) SV *canvas; From 4479c5444a26f5ad77a16b26ed18bd61e24db592 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 11 Sep 2018 09:00:28 +0200 Subject: [PATCH 50/50] 1st installment of gizmo move 3D --- resources/icons/overlay/move_hover.png | Bin 0 -> 2812 bytes resources/icons/overlay/move_off.png | Bin 0 -> 2769 bytes resources/icons/overlay/move_on.png | Bin 0 -> 2447 bytes xs/src/slic3r/GUI/3DScene.cpp | 23 +++- xs/src/slic3r/GUI/3DScene.hpp | 2 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 81 +++++++++++-- xs/src/slic3r/GUI/GLCanvas3D.hpp | 6 +- xs/src/slic3r/GUI/GLGizmo.cpp | 151 +++++++++++++++++++++++++ xs/src/slic3r/GUI/GLGizmo.hpp | 25 ++++ 9 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 resources/icons/overlay/move_hover.png create mode 100644 resources/icons/overlay/move_off.png create mode 100644 resources/icons/overlay/move_on.png diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..99dc4cf8dbef9bf88bc6fa0a903705c400a815ed GIT binary patch literal 2812 zcmVyRv`cY3W-TX zK~#9!<(qqKQ`a5GKi78byn_={LW5mMNgSFLWvL6K0X00vpy3W7m>Se>>dM+KRo6|0 zGL5Q}+D%MA2Zd_dN*4{aE2W5{DVOpXq$Mg@6%hqcU|Ae1a~dZEC&aZA5}cR!hi^=r zhvTMp>}g-gfB4>W&%NK@`JMB7og)xth@zMSECX_Z2j$-!API<wP8n_Qg1rkHleGkwjYe)s~S4ol@qCx8jU?> zv$-j(8X$^d60i~YHSiG8g?eB&P%cT5E94p=iefrY3hW2ci9XB*MBuK`Xf&J6=AMuV zKorFXfM3ZYuZ%QPl}bfsW+wCJ&nGoC6}Q`s+wC6VvkK4{jmCeO&1UDfngXII3V>c# zt*0VQeAcX4tXZ>$qM{-U27~W=E?nUB>C?2fwvJr>5ZDE@NRs54mIfG&#&lpO z@LZ(H>-Boxc;gLLtXM%xN(wh_+@QX`9-GZZLP7%h`T49`wFgfO-&6Z zlZpKNe6C%)Mn^}73djd6X0y3&qP`WAjD8XDI*<}+@|l^Lm`o<@b~|NdWq3T3PG`RQ z>MM>MIRe0zEn5%-0hz;ZWKL5HKorFc;HR=%JJLzSke!{4APAf~b&B5J-YMJH)YRZ` zILOV-B_YA5yxG8WGPfx;U<>d>v>122UJu|m=L5m*c60giW#Z!ENKf~3SJuniCKrGl zyO#i3-zphl4Nxc)zIh&W=-AWaTO3D03v3ldaZw-*cpk{}i#P(zxtW07?u~I06N8Ve z7cKH~bUMfRL3uLA@d6M<(EtlOgZmMshF`SkR3j7B34hl7rejn9b&4rR?;qmKVQSQ5faX)YQcA@GvHm$rlI2#l?}Bn227lr?9Y)7hil4 zlgUJ9XD7RN@20)IJy4eez;(0P+@z8(Y>>mrkd1Jt*N%@r{+N!A4oXT&C@d_bu&{7k zo2sfRKKS4R+S=NJ4V}-A2w(;GQq&=z$DzV!pM6GERTUkkE>_@8U;jVhCa_usEFEk5X42%Pq@<9PltfZe5?Za6#Kc4{U%pIN zS68@S9}6s10pG=2um~$+Vq#dlcrlrondo#nQd3izJ$p8D=FA~BHWsB)={J}6?Aa6c zPXcmPK-NqMpt!i0XPOqR`c@9FB2CR=W}$*%ggV@u3GI(d`7$?QbxG6wA2^2*4EbY$tR!q;&_16 z)YO^DH{jizFr!$%em$#KuLhvKy`6&x590B7=;-L6udfflFYg~l^eO=0d(NqnGtrRq@?i5E3f!W+|i>)u~;nk zLbQPa?%cWKvjpE3!^6XYZVv&S3c!YUOG;Rw(P-GdeYJ7B1HQ#Bf*1g9Oe-7~1ObD=fWcruqtVdU*GFSxqc4Ly8FF%Ra5|k6rnDjz z4hsOHDAwXl83&cDGZ+jM6&0b=>Ck92ba!`Sv)QGT66oU$l^&2)AX|puwViq zeX)KN;i}A$2?ZcYQax}KaEu<2!NEb2lao=Yf=eB$)oPSVWwcoV2XIv8$YdG-Z~{0z zI-=_7=|QK{k(QPg)SEIhGl_|bp{1oIngno0=0-pPNRreF`~~lpbUYpp7K;UiLczLq z>w;>RPN znp&;KU@(xFn25{eqPe;G8*OC^GyVv?B}tNXO3yRc9*c{M4IYo@=Yk;oLZMK^5S&LO~F8QAWNV?>VOTB}uv+bnG#mH;JP7J>W;c zW+GHt8baoBiqqAB zK~#9!?OT6rQ`Z&#{A|DH*iIfv9wY%`5-bzqdbf z9U13`-8B9owMV*t*zdk~-}&x$&$;Kkb7hFX&;V2b7ywGr6aXjyGSWu^AOZ-G<_F*< zO^D~q$>Q`}4QXirwgkpQ>|2*cBv z#V;ciQJNzlBwP8=Br;?Pio}CR@c=*pfR)STIg%vFk$NI0o<|}eh%>nalmId*ipms4 zQJU_0`wEKPBYM4Fxqtuu;+&kEOpnLoJ#*$vXG=?qBdp6H00#l$D+8b)l+Pf)gGrcK z0KoR`+qt^By3P6d`42G+vv|xch@!`8wVF0ru6~hWm_h*Xcsy-huh%M*$rM_xb{S36^F>h{?&#=fT(M$Bi_hniD2j55 zqIh>I5U3~yk0mWx$^!rl2E*K4yLSDNVVFXm=gmf=aidnN{Tat`wb|L(PnDIGJ=E6L zW{@PwzjW!+y%vk*SLD{v`%}|lKn8$0=*+Yr$ZU=Q!@+ix)3G7_I+~9Xp-?Ais0x&bjN?ulpw1*0YfS zgpi?94S<%G7Dsz~`yT*c{rdI4Gn>uj6ZI=EFHh_3?R|#hxE~6F&{tPi_wp1QyfLQB z!YpLYBn${983Q>0bai#@TDWlGF9Cq(d9%f0dCzLKexXvSn1X_WlKlMqO$@^<2m}JQ z6DLmWs;a8GF~u|e0PX-74goNS_d5na>#MK6dbGa2 zz9)vX+7JMH2!I7sSpSp(0016%;DNN~pMQR>PN(~pN~M}3Ns`~`boRXa?z@+F?AUSh zOFY*Hphrgh;{1trNht!rjOP=G1OW6ZNGd65uCJn^VntC=QMyj2%h2g`SuD$HjYi`K z=gysLOF$}3C5y8DXN&>4xw%CL4<7vXvSrH_XJut^85tSbTCH}DTCL8dC@M`ZmygX< zPfyQ33knLh#hC#j0H=aj{yGCm^9|O<`eSNg@DHBr5RPx^?T>ef##^@_0N#S6A0yM@Prd zrAwFo{mLt^{N%C69xJ0H;^&_i=!|gZeF0ey862>yzs&?rBay| z3}VyN_f)qz<~qbuc)Y~Cf>kz=+L3x zo~{MnmoHy_J>K*O z;7{0A*km$QZQ8VHF90~5&dxP!);xXV#*P0bF&Gj7I4O*ln#7#}4u@kH00cpB9yoB| zP=b_?#sHrpQ2;#t_~WnJY__4!&Q9lRuf6tu3WUiL0B#=vhrcITAim* zC{%(V3_2W+Tg}bQ=Z_pYVoT+O%0vkf#9#*i0DJcA$u$@ZyE%@loM;CrilXQC?c2x8 z%F14}TCKqp24GxDOoP#+=ww+T96o%wpr)qg7{f4&0)c?FySw{TM@L7y!{Ko2bh;e9 zUSFD*m-l0eqSAPt|64&p!PDeJW-0;@z-Us`BMS=+^GSASoaps><@M{=-&CnoOZxl! zPw(8h^Es2rH12cr(W6Hf?%cWah)SheYPDMbGO;^OyBCX+SE08kUq-wjfB z8-8&RlN1##S+Zn309?3m!8q0Tc>t)ZtLwgb^X4%CSiO4nI;5WXF;3GOtx;_MqmYt3nE|13}0`MyoicGy;AJfNEold8c z$z<|mgxXOuTP{RYnNQ^RT?nho=XqZA`~5>SP3J!H$RjzijI?FTmJ*pvM!Vhat`q`b znCOOR00aq< z)YjJOIgXpv*Vo54G&Fqn`RAVp;+0XFN;YraoEZ_D%xJlMC?HBxDwT_EHXH45IEIj# zB9U1iiZ=6K8i0_|EGjC}4Gauud7c+iOnHH*1&J$7R(;_} z*m|E9BjsNad2dAUl!%Y5A{o;iM)_%bns1KI#%Y$mmWQXOVn*cgc{wINJ|9GakI(-f X`3>!?!WdHJ00000NkvXXu0mjffwVm9 literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/move_on.png b/resources/icons/overlay/move_on.png new file mode 100644 index 0000000000000000000000000000000000000000..7e78e0e6a3a0f36ce2a8a85e4ebdfa9ab6ec62c1 GIT binary patch literal 2447 zcmV;A32^p_P)j8 zK~#9!<(qqORP`0dKX-TYCXj@X1QLQkc*gQjM&8p1SPSxyN(Yd~tPfB-L_5%_V<|Xb ztL^w|r641qcWgjvsYTHU1qxOkQkjChMBWI27!qCy33=`9ALqAhmSnS+us3^WI5RhQ zbNB4M_k4fncb>nWhQ(yK=?)A4vVqR(NCo17SjAQjlmbP-8Q>VO7uc!k9JccPGz$cF z03(41fx$pqAPR^CA^;cQ4Dj<>phg{)>NpQ<2Q~ofHJw^p0${l520R7~2U36}phbvo zUj|%M8nPL9Pt*C%762G-?gnN81A%TzwXLL5;5%R^@Q$W)AS?hFZc>0Lz<6MY71Jr$ zD*)SowLqSxQ`Bq#7;Z)Y(|`$e3|Y9S9QX)$U(?xWDFB9>cwijx0?-qC(YL_Mz$co{ z)sO>VxJd(^@W6bBSads5HLws^uIUtp3;@GT2Vjl?xt2|IhZ8&|J)A2NGI~LLGE|jA z!67IsuFJ6qcwN&ueOmw+ZZx31QmrXrAK#X#F!XU4I0e$Peb=v-!k6#Dzu$(!8{Tmx z@EhQareib}fHXDVpAEb4?RrA)N08P{{y*|1Ts#8~7uSy+WUJpU>DOf!l3{PO*f`jnkujTXT7eLccGR#*p z1qZ-zlMXzo!dmZ5!bhF@%R6jZ1QjOS`@Vd*d>*XW%`N1P7v&rgUH57t zFxha^qcIJb3H0%w6Q1w_P^@B^xJ2{6=`vX5jTdhIrjL@bJ^+TBOdtn{_6NWSGXN*$ ze9yz?_)|JV=K*2|O6moIQ9zE0Z0l*j(?BQNMfZolkdYiX6?%^j&Odg(Oq*RFK=t*8 zI65j>eE=A4T&j}Ou-nrd;RB$o7(RSnK0m3$-48a*7ZnGS--N-_W#m77InW(j0mGEc z@&~wIm9YYZg-x&n4i|W8LIc9K_aG?)#xI09>m?feCSL@Nh?ZDkh#OM7z@@XW=x0z? z(&$7{GH>((FdAqRvQg6xIoh&J_TANL0oGY(bxp|<}lN=P=0Z zD+Vp09VBH+9v>Aa>9Ln~uYtusZ`yOr36GLF+XZwn4Z!Wth*>c4HFLaa-9$UA4Z>VN zmIKJB;|{H(y0JuUHC(#{r}x0F)o}F(f8N*xh={TJjUs@KF69!OmIJV95uDvG#f#D+ z@zYm}#G>~d>(ARF8Cs>mg%ei3krPOE0WF)w@Ks-j{p;(i`ZKG2hU@5d$bLXdT&Y=B z0{{@`AXsO|sxUY*1|DA_dAm2z+4$!=0c*2(`|V)08(ON}2Oy;(YMzp>U1AWdR_VK3V3^x zPd53u${IJMjNeN82lUzX4D$yUEjEl}(L z<@vtWRQQ=1Iy5uuX^0U#-vg@d7x!6+V5%>NlzePP%}jz)-AefOzTN@#5k@0-%)v zx@~(tmcx3g0ggFCUESi)vR~g_B=5RD|@W ztbuU#0u&vyO*-7Pf=GI{6e*M13jo(j;QV3O^*1R=*`IC{M7&ARcBFk2*XYj@GvJZm z!=PymH%!L8DAC;71)(pAg?K_LnQvr)>yd77U;xs(Lhf2=Rb4y-Tb9D{uSES5)1i9~ z{CEb8`K|cyrIW+oR0&KaN@l!MAOhF{^lLbCj)eLDh4#J0eXXADAGUg_ZFhKT6?Ete zoBslNxpoC$H!w)ksdfNpI@Q3IfKlO(MvCwU{sr$o(Xj9!c3|lwxKaqiXGw>~epCu< zQ8N2=hTeeyi;8+bBj9u>DY8u6AArbMS)pMzc)5H|3LMe#;wtS!dzFkd z2!N*ZEwBpkO>&P%YIrS@#Tc5Zw#9(cC0fvMgEAa+30S3MB#;IGtO4?UAww;cUy_kb zOgHza4t=Eze(n$)+hG>~K3B5QC;*yH0k9m@dVIrxle=Xq7~G^)8jiV6nB~$u zFyK*`z64@gK}{8$*d-;bR;fOBcj1HtONY1GVf5dksY_)Q09uvd$SkUfdNa*cIo_pt zq?SKomMmUu)6vI#?ht&n3cgq-4Uh2C?Jt)0Qo+=itkPQNybh7&)1x&N2}VRqS{#=o zYj_F{-rDk4YiTj?nx=CqXlcXj9Zyb)%dGG?b2D7!&&4}yBCscUH%zS1bk3Wq?c7oo z!A;BKRV^M(abABe-dWS#X2M&$W#%Rz9zZrtJ;I%|Y3e2rQh=$z!`x}x=57KaoEs;X zX*&CF=Vn%HogIO!!%HM|TW7D~+d8`mJ8hk900<|t-2NxPAk1%`HUJOOvZ}_ndAd-Q z>ox%Sw%t742p~O>?ejf>EZ{C68CjU&T?bR4V)jeGIiy>)hi2G5|9_-S+Q`YQ$SVK< N002ovPDHLkV1hbhW7_}# literal 0 HcmV?d00001 diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index dc2f5dd5ae..ed2f8690d7 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -313,6 +313,14 @@ void GLVolume::set_select_group_id(const std::string& select_by) select_group_id = composite_id; } +void GLVolume::set_drag_group_id(const std::string& drag_by) +{ + if (drag_by == "object") + drag_group_id = object_idx() * 1000; + else if (drag_by == "instance") + drag_group_id = object_idx() * 1000 + instance_idx(); +} + const Transform3f& GLVolume::world_matrix() const { if (m_world_matrix_dirty) @@ -666,11 +674,7 @@ std::vector GLVolumeCollection::load_object( v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; v.set_select_group_id(select_by); - if (drag_by == "object") - v.drag_group_id = obj_idx * 1000; - else if (drag_by == "instance") - v.drag_group_id = obj_idx * 1000 + instance_idx; - + v.set_drag_group_id(drag_by); if (!model_volume->modifier) { v.set_convex_hull(model_volume->get_convex_hull()); @@ -963,6 +967,15 @@ void GLVolumeCollection::set_select_by(const std::string& select_by) } } +void GLVolumeCollection::set_drag_by(const std::string& drag_by) +{ + for (GLVolume *vol : this->volumes) + { + if (vol != nullptr) + vol->set_drag_group_id(drag_by); + } +} + std::vector GLVolumeCollection::get_current_print_zs(bool active_only) const { // Collect layer top positions of all volumes. diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index df749d1bb5..6db58aa160 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -338,6 +338,7 @@ public: void set_convex_hull(const TriangleMesh& convex_hull); void set_select_group_id(const std::string& select_by); + void set_drag_group_id(const std::string& drag_by); int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } @@ -449,6 +450,7 @@ public: void update_colors_by_extruder(const DynamicPrintConfig* config); void set_select_by(const std::string& select_by); + void set_drag_by(const std::string& drag_by); // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 94de895b47..b98826a800 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1123,7 +1123,6 @@ const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) , m_current(Undefined) - , m_dragging(false) { } @@ -1134,7 +1133,16 @@ GLCanvas3D::Gizmos::~Gizmos() bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) { - GLGizmoBase* gizmo = new GLGizmoScale3D(parent); + GLGizmoBase* gizmo = new GLGizmoMove3D(parent); + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) + return false; + + m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); + + gizmo = new GLGizmoScale3D(parent); if (gizmo == nullptr) return false; @@ -1336,12 +1344,12 @@ bool GLCanvas3D::Gizmos::is_running() const bool GLCanvas3D::Gizmos::is_dragging() const { - return m_dragging; + GLGizmoBase* curr = _get_current(); + return (curr != nullptr) ? curr->is_dragging() : false; } void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) { - m_dragging = true; GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->start_dragging(box); @@ -1349,12 +1357,30 @@ void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) void GLCanvas3D::Gizmos::stop_dragging() { - m_dragging = false; GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->stop_dragging(); } +Vec3d GLCanvas3D::Gizmos::get_position() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Move); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_position() : Vec3d::Zero(); +} + +void GLCanvas3D::Gizmos::set_position(const Vec3d& position) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Move); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->set_position(position); +} + float GLCanvas3D::Gizmos::get_scale() const { if (!m_enabled) @@ -2143,6 +2169,7 @@ void GLCanvas3D::set_select_by(const std::string& value) void GLCanvas3D::set_drag_by(const std::string& value) { m_drag_by = value; + m_volumes.set_drag_by(value); } const std::string& GLCanvas3D::get_select_by() const @@ -2150,6 +2177,11 @@ const std::string& GLCanvas3D::get_select_by() const return m_select_by; } +const std::string& GLCanvas3D::get_drag_by() const +{ + return m_drag_by; +} + float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; @@ -2326,6 +2358,7 @@ void GLCanvas3D::update_gizmos_data() ModelInstance* model_instance = model_object->instances[0]; if (model_instance != nullptr) { + m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); m_gizmos.set_scale(model_instance->scaling_factor); m_gizmos.set_angle_z(model_instance->rotation); m_gizmos.set_flattening_data(model_object); @@ -2334,6 +2367,7 @@ void GLCanvas3D::update_gizmos_data() } else { + m_gizmos.set_position(Vec3d::Zero()); m_gizmos.set_scale(1.0f); m_gizmos.set_angle_z(0.0f); m_gizmos.set_flattening_data(nullptr); @@ -3181,6 +3215,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) switch (m_gizmos.get_current_type()) { + case Gizmos::Move: + { + // Apply new temporary offset + GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; + Vec3d offset = m_gizmos.get_position() - volume->get_offset(); + for (GLVolume* v : volumes) + { + v->set_offset(v->get_offset() + offset); + } + update_position_values(volume->get_offset()); + break; + } case Gizmos::Scale: { // Apply new temporary scale factor @@ -3188,8 +3234,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) for (GLVolume* v : volumes) { v->set_scaling_factor((double)scale_factor); - update_scale_values((double)scale_factor); } + update_scale_values((double)scale_factor); break; } case Gizmos::Rotate: @@ -3199,8 +3245,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) for (GLVolume* v : volumes) { v->set_rotation((double)angle_z); - update_rotation_value((double)angle_z, Z); } + update_rotation_value((double)angle_z, Z); break; } default: @@ -3310,6 +3356,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { switch (m_gizmos.get_current_type()) { + case Gizmos::Move: + { + // get all volumes belonging to the same group, if any + std::vector volume_idxs; + int vol_id = m_mouse.drag.gizmo_volume_idx; + int group_id = m_volumes.volumes[vol_id]->select_group_id; + if (group_id == -1) + volume_idxs.push_back(vol_id); + else + { + for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) + { + if (m_volumes.volumes[i]->select_group_id == group_id) + volume_idxs.push_back(i); + } + } + + _on_move(volume_idxs); + + break; + } case Gizmos::Scale: { m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 55a3075cee..a200e7fdbe 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -336,6 +336,7 @@ public: enum EType : unsigned char { Undefined, + Move, Scale, Rotate, Flatten, @@ -347,7 +348,6 @@ public: typedef std::map GizmosMap; GizmosMap m_gizmos; EType m_current; - bool m_dragging; public: Gizmos(); @@ -376,6 +376,9 @@ public: void start_dragging(const BoundingBoxf3& box); void stop_dragging(); + Vec3d get_position() const; + void set_position(const Vec3d& position); + float get_scale() const; void set_scale(float scale); @@ -558,6 +561,7 @@ public: void set_drag_by(const std::string& value); const std::string& get_select_by() const; + const std::string& get_drag_by() const; float get_camera_zoom() const; diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 1d02e17526..17c69749b4 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -989,6 +989,157 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& return ratio; } +const double GLGizmoMove3D::Offset = 10.0; + +GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) + : GLGizmoBase(parent) + , m_position(Vec3d::Zero()) + , m_starting_drag_position(Vec3d::Zero()) + , m_starting_box_center(Vec3d::Zero()) +{ +} + +bool GLGizmoMove3D::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + std::string filename = path + "move_off.png"; + if (!m_textures[Off].load_from_file(filename, false)) + return false; + + filename = path + "move_hover.png"; + if (!m_textures[Hover].load_from_file(filename, false)) + return false; + + filename = path + "move_on.png"; + if (!m_textures[On].load_from_file(filename, false)) + return false; + + for (int i = 0; i < 3; ++i) + { + m_grabbers.push_back(Grabber()); + } + + return true; +} + +void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) +{ + if (m_hover_id != -1) + { + m_starting_drag_position = m_grabbers[m_hover_id].center; + m_starting_box_center = box.center(); + } +} + +void GLGizmoMove3D::on_update(const Linef3& mouse_ray) +{ + if (m_hover_id == 0) + m_position(0) = 2.0 * m_starting_box_center(0) + calc_displacement(1, mouse_ray) - m_starting_drag_position(0); + else if (m_hover_id == 1) + m_position(1) = 2.0 * m_starting_box_center(1) + calc_displacement(2, mouse_ray) - m_starting_drag_position(1); + else if (m_hover_id == 2) + m_position(2) = 2.0 * m_starting_box_center(2) + calc_displacement(1, mouse_ray) - m_starting_drag_position(2); +} + +void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const +{ + if (m_grabbers[0].dragging) + set_tooltip("X: " + format(m_position(0), 2)); + else if (m_grabbers[1].dragging) + set_tooltip("Y: " + format(m_position(1), 2)); + else if (m_grabbers[2].dragging) + set_tooltip("Z: " + format(m_position(2), 2)); + + ::glEnable(GL_DEPTH_TEST); + + const Vec3d& center = box.center(); + + // x axis + m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2)); + ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); + + // y axis + m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2)); + ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); + + // z axis + m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset); + ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); + + ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); + + if (m_hover_id == -1) + { + // draw axes + for (unsigned int i = 0; i < 3; ++i) + { + ::glColor3fv(AXES_COLOR[i]); + ::glBegin(GL_LINES); + ::glVertex3f(center(0), center(1), center(2)); + ::glVertex3f((GLfloat)m_grabbers[i].center(0), (GLfloat)m_grabbers[i].center(1), (GLfloat)m_grabbers[i].center(2)); + ::glEnd(); + } + + // draw grabbers + render_grabbers(); + } + else + { + // draw axis + ::glColor3fv(AXES_COLOR[m_hover_id]); + ::glBegin(GL_LINES); + ::glVertex3f(center(0), center(1), center(2)); + ::glVertex3f((GLfloat)m_grabbers[m_hover_id].center(0), (GLfloat)m_grabbers[m_hover_id].center(1), (GLfloat)m_grabbers[m_hover_id].center(2)); + ::glEnd(); + + // draw grabber + m_grabbers[m_hover_id].render(true); + } +} + +void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(); +} + +double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const +{ + double displacement = 0.0; + + Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + double len_starting_vec = starting_vec.norm(); + if (len_starting_vec == 0.0) + return displacement; + + Vec3d starting_vec_dir = starting_vec.normalized(); + Vec3d mouse_dir = mouse_ray.unit_vector(); + + unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id); + + switch (plane_id) + { + case 0: + { + displacement = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, m_starting_box_center)); + break; + } + case 1: + { + displacement = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, m_starting_box_center)); + break; + } + case 2: + { + displacement = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, m_starting_box_center)); + break; + } + } + + return displacement; +} GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) : GLGizmoBase(parent) diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 0599955ed5..a7c6885311 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -262,6 +262,31 @@ private: double calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const; }; +class GLGizmoMove3D : public GLGizmoBase +{ + static const double Offset; + + Vec3d m_position; + Vec3d m_starting_drag_position; + Vec3d m_starting_box_center; + +public: + explicit GLGizmoMove3D(GLCanvas3D& parent); + + const Vec3d& get_position() const { return m_position; } + void set_position(const Vec3d& position) { m_position = position; } + +protected: + virtual bool on_init(); + virtual void on_start_dragging(const BoundingBoxf3& box); + virtual void on_update(const Linef3& mouse_ray); + virtual void on_render(const BoundingBoxf3& box) const; + virtual void on_render_for_picking(const BoundingBoxf3& box) const; + +private: + double calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const; +}; + class GLGizmoFlatten : public GLGizmoBase { // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.