diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index b84e2f13d..d8770cbfe 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -207,8 +207,7 @@ static bool sort_pointfs(const Vec3d& a, const Vec3d& b) } // This implementation is based on Andrew's monotone chain 2D convex hull algorithm -Polygon -convex_hull(Points points) +Polygon convex_hull(Points points) { assert(points.size() >= 3); // sort input points diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 593f716fa..d86a180b8 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -912,6 +912,60 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ return bb; } +// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane. +// This method is cheap in that it does not make any unnecessary copy of the volume meshes. +// This method is used by the auto arrange function. +Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) +{ + Points pts; + for (const ModelVolume *v : this->volumes) + if (v->is_model_part()) { + const stl_file &stl = v->mesh.stl; + Transform3d trafo = trafo_instance * v->get_matrix(); + if (stl.v_shared == nullptr) { + // Using the STL faces. + for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) { + const stl_facet &facet = stl.facet_start[i]; + for (size_t j = 0; j < 3; ++ j) { + Vec3d p = trafo * facet.vertex[j].cast(); + pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); + } + } + } else { + // Using the shared vertices should be a bit quicker than using the STL faces. + for (int i = 0; i < stl.stats.shared_vertices; ++ i) { + Vec3d p = trafo * stl.v_shared[i].cast(); + pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); + } + } + } + std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); }); + pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end()); + + Polygon hull; + int n = (int)pts.size(); + if (n >= 3) { + int k = 0; + hull.points.resize(2 * n); + // Build lower hull + for (int i = 0; i < n; ++ i) { + while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) + -- k; + hull[k ++] = pts[i]; + } + // Build upper hull + for (int i = n-2, t = k+1; i >= 0; i--) { + while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) + -- k; + hull[k ++] = pts[i]; + } + hull.points.resize(k); + assert(hull.points.front() == hull.points.back()); + hull.points.pop_back(); + } + return hull; +} + void ModelObject::center_around_origin() { // calculate the displacements needed to @@ -1099,7 +1153,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b // Perform cut TriangleMeshSlicer tms(&volume->mesh); - tms.cut(z, &upper_mesh, &lower_mesh); + tms.cut(float(z), &upper_mesh, &lower_mesh); // Reset volume transformation except for offset const Vec3d offset = volume->get_offset(); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0dd73f62f..afc5d0741 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -223,6 +223,12 @@ public: BoundingBoxf3 raw_mesh_bounding_box() const; // A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. BoundingBoxf3 full_raw_mesh_bounding_box() const; + + // Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane. + // This method is cheap in that it does not make any unnecessary copy of the volume meshes. + // This method is used by the auto arrange function. + Polygon convex_hull_2d(const Transform3d &trafo_instance); + void center_around_origin(); void ensure_on_bed(); void translate_instances(const Vec3d& vector); diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index d527db9da..fd3cf8648 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -553,24 +553,47 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { for(ModelObject* objptr : model.objects) { if(objptr) { - TriangleMesh rmesh = objptr->raw_mesh(); - - ModelInstance * finst = objptr->instances.front(); - - // Object instances should carry the same scaling and - // x, y rotation that is why we use the first instance. - // The next line will apply only the full mirroring and scaling - rmesh.transform(finst->get_matrix(true, true, false, false)); - rmesh.rotate_x(float(finst->get_rotation()(X))); - rmesh.rotate_y(float(finst->get_rotation()(Y))); - // TODO export the exact 2D projection. Cannot do it as libnest2d // does not support concave shapes (yet). - auto p = rmesh.convex_hull(); + ClipperLib::Path clpath; +//WIP Vojtech's optimization of the calculation of the convex hull is not working correctly yet. +#if 1 + { + TriangleMesh rmesh = objptr->raw_mesh(); - p.make_clockwise(); - p.append(p.first_point()); - auto clpath = Slic3rMultiPoint_to_ClipperPath(p); + ModelInstance * finst = objptr->instances.front(); + + // Object instances should carry the same scaling and + // x, y rotation that is why we use the first instance. + // The next line will apply only the full mirroring and scaling + rmesh.transform(finst->get_matrix(true, true, false, false)); + rmesh.rotate_x(float(finst->get_rotation()(X))); + rmesh.rotate_y(float(finst->get_rotation()(Y))); + + // TODO export the exact 2D projection. Cannot do it as libnest2d + // does not support concave shapes (yet). + auto p = rmesh.convex_hull(); + + p.make_clockwise(); + p.append(p.first_point()); + clpath = Slic3rMultiPoint_to_ClipperPath(p); + } +#else + // Object instances should carry the same scaling and + // x, y rotation that is why we use the first instance. + { + ModelInstance *finst = objptr->instances.front(); + Vec3d rotation = finst->get_rotation(); + rotation.z() = 0.; + Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror()); + Polygon p = objptr->convex_hull_2d(trafo_instance); + assert(! p.points.empty()); + p.reverse(); + assert(! p.is_counter_clockwise()); + p.append(p.first_point()); + clpath = Slic3rMultiPoint_to_ClipperPath(p); + } +#endif for(ModelInstance* objinst : objptr->instances) { if(objinst) {