Browse Source

Complete arrangement prevention of integer coordinate overflows

tamasmeszaros 1 year ago
parent
commit
cb3596e90b

+ 8 - 0
src/libslic3r/Arrange/Arrange.hpp

@@ -83,11 +83,19 @@ void Arranger<ArrItem>::arrange(std::vector<ArrItem> &items,
     arrange(items, fixed, bed, DefaultArrangerCtl<ArrItem>{ctl});
 }
 
+class EmptyItemOutlineError: public std::exception {
+    static constexpr const char *Msg = "No outline can be derived for object";
+
+public:
+    const char* what() const noexcept override { return Msg; }
+};
+
 template<class ArrItem> class ArrangeableToItemConverter
 {
 public:
     virtual ~ArrangeableToItemConverter() = default;
 
+    // May throw EmptyItemOutlineError
     virtual ArrItem convert(const Arrangeable &arrbl, coord_t offs = 0) const = 0;
 
     // Returns the extent of simplification that the converter utilizes when

+ 8 - 0
src/libslic3r/Arrange/ArrangeImpl.hpp

@@ -370,6 +370,10 @@ ArrItem ConvexItemConverter<ArrItem>::convert(const Arrangeable &arrbl,
 {
     auto bed_index = arrbl.get_bed_index();
     Polygon outline = arrbl.convex_outline();
+
+    if (outline.empty())
+        throw EmptyItemOutlineError{};
+
     Polygon envelope = arrbl.convex_envelope();
 
     coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.));
@@ -418,6 +422,10 @@ ArrItem AdvancedItemConverter<ArrItem>::get_arritem(const Arrangeable &arrbl,
     coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.));
 
     auto outline = arrbl.full_outline();
+
+    if (outline.empty())
+        throw EmptyItemOutlineError{};
+
     auto envelope = arrbl.full_envelope();
 
     if (infl != 0) {

+ 52 - 7
src/libslic3r/Arrange/SceneBuilder.cpp

@@ -64,16 +64,18 @@ void transform_instance(ModelInstance     &mi,
     mi.invalidate_object_bounding_box();
 }
 
-BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate)
+BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
+                                    const Transform3d &tr,
+                                    bool dont_translate)
 {
     BoundingBoxf3 bb;
     const Transform3d inst_matrix
         = dont_translate ? mi.get_transformation().get_matrix_no_offset()
-                           : mi.get_transformation().get_matrix();
+                         : mi.get_transformation().get_matrix();
 
     for (ModelVolume *v : mi.get_object()->volumes) {
         if (v->is_model_part()) {
-            bb.merge(v->mesh().transformed_bounding_box(inst_matrix
+            bb.merge(v->mesh().transformed_bounding_box(tr * inst_matrix
                                                         * v->get_matrix()));
         }
     }
@@ -81,11 +83,32 @@ BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate
     return bb;
 }
 
+BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate)
+{
+    return instance_bounding_box(mi, Transform3d::Identity(), dont_translate);
+}
+
+bool check_coord_bounds(const BoundingBoxf &bb)
+{
+    constexpr double hi = 1000.;
+
+    return std::abs(bb.min.x()) < hi &&
+           std::abs(bb.min.y()) < hi &&
+           std::abs(bb.max.x()) < hi &&
+           std::abs(bb.max.y()) < hi;
+}
+
 ExPolygons extract_full_outline(const ModelInstance &inst, const Transform3d &tr)
 {
     ExPolygons outline;
     for (const ModelVolume *v : inst.get_object()->volumes) {
         Polygons vol_outline;
+
+        if (!check_coord_bounds(to_2d(v->mesh().transformed_bounding_box(tr)))) {
+            outline.clear();
+            break;
+        }
+
         vol_outline = project_mesh(v->mesh().its,
                                    tr * inst.get_matrix() * v->get_matrix(),
                                    [] {});
@@ -105,7 +128,14 @@ ExPolygons extract_full_outline(const ModelInstance &inst, const Transform3d &tr
 
 Polygon extract_convex_outline(const ModelInstance &inst, const Transform3d &tr)
 {
-    return inst.get_object()->convex_hull_2d(tr * inst.get_matrix());
+    auto bb = to_2d(instance_bounding_box(inst, tr));
+    Polygon ret;
+
+    if (check_coord_bounds(bb)) {
+        ret = inst.get_object()->convex_hull_2d(tr * inst.get_matrix());
+    }
+
+    return ret;
 }
 
 inline static bool is_infinite_bed(const ExtendedBed &ebed) noexcept
@@ -190,7 +220,14 @@ int XStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const
         auto reference_pos_x = (instance_bb.min.x() - bedx);
         auto stride = unscaled(stride_s);
 
-        bedidx = static_cast<int>(std::floor(reference_pos_x / stride));
+        auto bedidx_d = std::floor(reference_pos_x / stride);
+
+        if (bedidx_d < std::numeric_limits<int>::min())
+            bedidx = std::numeric_limits<int>::min();
+        else if (bedidx_d > std::numeric_limits<int>::max())
+            bedidx = std::numeric_limits<int>::max();
+        else
+            bedidx = static_cast<int>(bedidx_d);
     }
 
     return bedidx;
@@ -231,7 +268,14 @@ int YStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const
         auto reference_pos_y = (instance_bb.min.y() - ystart);
         auto stride = unscaled(stride_s);
 
-        bedidx = static_cast<int>(std::floor(reference_pos_y / stride));
+        auto bedidx_d = std::floor(reference_pos_y / stride);
+
+        if (bedidx_d < std::numeric_limits<int>::min())
+            bedidx = std::numeric_limits<int>::min();
+        else if (bedidx_d > std::numeric_limits<int>::max())
+            bedidx = std::numeric_limits<int>::max();
+        else
+            bedidx = static_cast<int>(bedidx_d);
     }
 
     return bedidx;
@@ -274,7 +318,8 @@ Vec2i GridStriderVBedHandler::raw2grid(int bed_idx) const
 
 int GridStriderVBedHandler::grid2raw(const Vec2i &crd) const
 {
-    assert(crd.x() < ColsOutside - 1 && crd.y() < ColsOutside - 1);
+    assert(std::abs(crd.x()) < ColsOutside - 1 &&
+           std::abs(crd.y()) < ColsOutside - 1);
 
     return crd.y() * ColsOutside + crd.x();
 }

+ 3 - 0
src/libslic3r/Arrange/SceneBuilder.hpp

@@ -377,6 +377,9 @@ void transform_instance(ModelInstance     &mi,
 BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
                                     bool dont_translate = false);
 
+BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
+                                    const Transform3d &tr,
+                                    bool dont_translate = false);
 
 ExPolygons extract_full_outline(const ModelInstance &inst,
                                 const Transform3d &tr = Transform3d::Identity());

+ 11 - 4
src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp

@@ -3,6 +3,8 @@
 
 #include <random>
 
+#include <boost/log/trivial.hpp>
+
 #include "ArrangeTask.hpp"
 
 namespace Slic3r { namespace arr2 {
@@ -20,16 +22,21 @@ void extract_selected(ArrangeTask<ArrItem> &task,
             bool selected = arrbl.is_selected();
             bool printable = arrbl.is_printable();
 
-            auto itm = itm_conv.convert(arrbl, selected ? 0 : -SCALED_EPSILON);
+            try {
+                auto itm = itm_conv.convert(arrbl, selected ? 0 : -SCALED_EPSILON);
 
-            auto &container_parent = printable ? task.printable :
+                auto &container_parent = printable ? task.printable :
                                                  task.unprintable;
 
-            auto &container = selected ?
+                auto &container = selected ?
                                        container_parent.selected :
                                        container_parent.unselected;
 
-            container.emplace_back(std::move(itm));
+                container.emplace_back(std::move(itm));
+            } catch (const EmptyItemOutlineError &ex) {
+                BOOST_LOG_TRIVIAL(error)
+                    << "ObjectID " << std::to_string(arrbl.id().id) << ": " << ex.what();
+            }
         });
 
     // If the selection was empty arrange everything

+ 15 - 8
src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp

@@ -5,6 +5,8 @@
 
 #include "Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
 
+#include <boost/log/trivial.hpp>
+
 namespace Slic3r { namespace arr2 {
 
 template<class ArrItem>
@@ -81,15 +83,20 @@ void extract(FillBedTask<ArrItem> &task,
 
     auto collect_task_items = [&prototype_geometry_id, &task,
                                &itm_conv](const Arrangeable &arrbl) {
-        if (arrbl.geometry_id() == prototype_geometry_id) {
-            if (arrbl.is_printable()) {
-                auto itm = itm_conv.convert(arrbl);
-                raise_priority(itm);
-                task.selected.emplace_back(std::move(itm));
+        try {
+            if (arrbl.geometry_id() == prototype_geometry_id) {
+                if (arrbl.is_printable()) {
+                    auto itm = itm_conv.convert(arrbl);
+                    raise_priority(itm);
+                    task.selected.emplace_back(std::move(itm));
+                }
+            } else {
+                auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON);
+                task.unselected.emplace_back(std::move(itm));
             }
-        } else {
-            auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON);
-            task.unselected.emplace_back(std::move(itm));
+        } catch (const EmptyItemOutlineError &ex) {
+            BOOST_LOG_TRIVIAL(error)
+                << "ObjectID " << std::to_string(arrbl.id().id) << ": " << ex.what();
         }
     };
 

+ 15 - 8
src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp

@@ -3,6 +3,8 @@
 
 #include "MultiplySelectionTask.hpp"
 
+#include <boost/log/trivial.hpp>
+
 namespace Slic3r { namespace arr2 {
 
 template<class ArrItem>
@@ -45,15 +47,20 @@ std::unique_ptr<MultiplySelectionTask<ArrItem>> MultiplySelectionTask<ArrItem>::
 
     auto collect_task_items = [&prototype_geometry_id, &task,
                                &itm_conv](const Arrangeable &arrbl) {
-        if (arrbl.geometry_id() == prototype_geometry_id) {
-            if (arrbl.is_printable()) {
-                auto itm = itm_conv.convert(arrbl);
-                raise_priority(itm);
-                task.selected.emplace_back(std::move(itm));
+        try {
+            if (arrbl.geometry_id() == prototype_geometry_id) {
+                if (arrbl.is_printable()) {
+                    auto itm = itm_conv.convert(arrbl);
+                    raise_priority(itm);
+                    task.selected.emplace_back(std::move(itm));
+                }
+            } else {
+                auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON);
+                task.unselected.emplace_back(std::move(itm));
             }
-        } else {
-            auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON);
-            task.unselected.emplace_back(std::move(itm));
+        } catch (const EmptyItemOutlineError &ex) {
+            BOOST_LOG_TRIVIAL(error)
+                << "ObjectID " << std::to_string(arrbl.id().id) << ": " << ex.what();
         }
     };