diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index 76aa36194..217166f8c 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -15,11 +15,6 @@ #include "Utils.hpp" // for next_highest_power_of_2() -extern "C" -{ -// Ray-Triangle Intersection Test Routines by Tomas Moller, May 2000 -#include -} // Definition of the ray intersection hit structure. #include @@ -231,6 +226,9 @@ namespace detail { const VectorType origin; const VectorType dir; const VectorType invdir; + + // epsilon for ray-triangle intersection, see intersect_triangle1() + const double eps; }; template @@ -283,44 +281,91 @@ namespace detail { return tmin < t1 && tmax > t0; } + // The following intersect_triangle() is derived from raytri.c routine intersect_triangle1() + // Ray-Triangle Intersection Test Routines + // Different optimizations of my and Ben Trumbore's + // code from journals of graphics tools (JGT) + // http://www.acm.org/jgt/ + // by Tomas Moller, May 2000 template - std::enable_if_t::value && std::is_same::value, bool> - intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) { - return intersect_triangle1(const_cast(origin.data()), const_cast(dir.data()), - const_cast(v0.data()), const_cast(v1.data()), const_cast(v2.data()), - &t, &u, &v); + std::enable_if_t::value&& std::is_same::value, bool> + intersect_triangle(const V &orig, const V &dir, const W &vert0, const W &vert1, const W &vert2, double &t, double &u, double &v, double eps) + { + // find vectors for two edges sharing vert0 + const V edge1 = vert1 - vert0; + const V edge2 = vert2 - vert0; + // begin calculating determinant - also used to calculate U parameter + const V pvec = dir.cross(edge2); + // if determinant is near zero, ray lies in plane of triangle + const double det = edge1.dot(pvec); + V qvec; + + if (det > eps) { + // calculate distance from vert0 to ray origin + V tvec = orig - vert0; + // calculate U parameter and test bounds + u = tvec.dot(pvec); + if (u < 0.0 || u > det) + return false; + // prepare to test V parameter + qvec = tvec.cross(edge1); + // calculate V parameter and test bounds + v = dir.dot(qvec); + if (v < 0.0 || u + v > det) + return false; + } else if (det < -eps) { + // calculate distance from vert0 to ray origin + V tvec = orig - vert0; + // calculate U parameter and test bounds + u = tvec.dot(pvec); + if (u > 0.0 || u < det) + return false; + // prepare to test V parameter + qvec = tvec.cross(edge1); + // calculate V parameter and test bounds + v = dir.dot(qvec); + if (v > 0.0 || u + v < det) + return false; + } else + // ray is parallel to the plane of the triangle + return false; + + double inv_det = 1.0 / det; + // calculate t, ray intersects triangle + t = edge2.dot(qvec) * inv_det; + u *= inv_det; + v *= inv_det; + return true; } template std::enable_if_t::value && !std::is_same::value, bool> - intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) { - using Vector = Eigen::Matrix; - Vector w0 = v0.template cast(); - Vector w1 = v1.template cast(); - Vector w2 = v2.template cast(); - return intersect_triangle1(const_cast(origin.data()), const_cast(dir.data()), - w0.data(), w1.data(), w2.data(), &t, &u, &v); + intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) { + return intersect_triangle(origin, dir, v0.template cast(), v1.template cast(), v2.template cast(), t, u, v, eps); } template std::enable_if_t::value && std::is_same::value, bool> - intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) { - using Vector = Eigen::Matrix; - Vector o = origin.template cast(); - Vector d = dir.template cast(); - return intersect_triangle1(o.data(), d.data(), const_cast(v0.data()), const_cast(v1.data()), const_cast(v2.data()), &t, &u, &v); + intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) { + return intersect_triangle(origin.template cast(), dir.template cast(), v0, v1, v2, t, u, v, eps); } template std::enable_if_t::value && ! std::is_same::value, bool> - intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) { - using Vector = Eigen::Matrix; - Vector o = origin.template cast(); - Vector d = dir.template cast(); - Vector w0 = v0.template cast(); - Vector w1 = v1.template cast(); - Vector w2 = v2.template cast(); - return intersect_triangle1(o.data(), d.data(), w0.data(), w1.data(), w2.data(), &t, &u, &v); + intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) { + return intersect_triangle(origin.template cast(), dir.template cast(), v0.template cast(), v1.template cast(), v2.template cast(), t, u, v, eps); + } + + template + double intersect_triangle_epsilon(const Tree &tree) { + double eps = 0.000001; + if (! tree.empty()) { + const typename Tree::BoundingBox &bbox = tree.nodes().front().bbox; + double l = (bbox.max() - bbox.min()).cwiseMax(); + if (l > 0) + eps /= (l * l); + } + return eps; } template @@ -343,7 +388,7 @@ namespace detail { if (intersect_triangle( ray_intersector.origin, ray_intersector.dir, ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)], - t, u, v) + t, u, v, ray_intersector.eps) && t > 0.) { hit = igl::Hit { int(node.idx), -1, float(u), float(v), float(t) }; return true; @@ -388,7 +433,7 @@ namespace detail { if (intersect_triangle( ray_intersector.origin, ray_intersector.dir, ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)], - t, u, v) + t, u, v, ray_intersector.eps) && t > 0.) { ray_intersector.hits.emplace_back(igl::Hit{ int(node.idx), -1, float(u), float(v), float(t) }); } @@ -623,12 +668,15 @@ inline bool intersect_ray_first_hit( // Direction of the ray. const VectorType &dir, // First intersection of the ray with the indexed triangle set. - igl::Hit &hit) + igl::Hit &hit, + // Epsilon for the ray-triangle intersection, it should be proportional to an average triangle edge length. + const double eps = 0.000001) { using Scalar = typename VectorType::Scalar; - auto ray_intersector = detail::RayIntersector { + auto ray_intersector = detail::RayIntersector { vertices, faces, tree, - origin, dir, VectorType(dir.cwiseInverse()) + origin, dir, VectorType(dir.cwiseInverse()), + eps }; return ! tree.empty() && detail::intersect_ray_recursive_first_hit( ray_intersector, size_t(0), std::numeric_limits::infinity(), hit); @@ -652,11 +700,14 @@ inline bool intersect_ray_all_hits( // Direction of the ray. const VectorType &dir, // All intersections of the ray with the indexed triangle set, sorted by parameter t. - std::vector &hits) + std::vector &hits, + // Epsilon for the ray-triangle intersection, it should be proportional to an average triangle edge length. + const double eps = 0.000001) { auto ray_intersector = detail::RayIntersectorHits { { vertices, faces, {tree}, - origin, dir, VectorType(dir.cwiseInverse()) } + origin, dir, VectorType(dir.cwiseInverse()), + eps } }; if (! tree.empty()) { ray_intersector.hits.reserve(8); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 254f1d4fd..85e6f810b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -13,6 +13,7 @@ #include "ClipperUtils.hpp" #include "libslic3r.h" #include "LocalesUtils.hpp" +#include "libslic3r/format.hpp" #include #include @@ -512,7 +513,8 @@ std::vector GCode::collect_layers_to_print(const PrintObjec bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()); - // Check that there are extrusions on the very first layer. + // Check that there are extrusions on the very first layer. The case with empty + // first layer may result in skirt/brim in the air and maybe other issues. if (layers_to_print.size() == 1u) { if (!has_extrusions) throw Slic3r::SlicingError(_(L("There is an object with no extrusions in the first layer.")) + "\n" + @@ -534,11 +536,12 @@ std::vector GCode::collect_layers_to_print(const PrintObjec if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) { const_cast(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - _(L("Empty layers detected. Make sure the object is printable.")) + "\n" + - _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + - std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " - "usually caused by negligibly small extrusions or by a faulty model. Try to repair " - "the model or change its orientation on the bed."))); + Slic3r::format(_(L("Empty layer detected between heights %1% and %2%. Make sure the object is printable.")), + (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.), + layers_to_print.back().print_z()) + + "\n" + Slic3r::format(_(L("Object name: %1%")), object.model_object()->name) + "\n\n" + + _(L("This is usually caused by negligibly small extrusions or by a faulty model. " + "Try to repair the model or change its orientation on the bed."))); } // Remember last layer with extrusions. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ee09e0f5b..d8d26baa6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2294,9 +2294,13 @@ void PrintObject::project_and_append_custom_facets( ? mv->seam_facets.get_facets_strict(*mv, type) : mv->supported_facets.get_facets_strict(*mv, type); if (! custom_facets.indices.empty()) +#if 0 project_triangles_to_slabs(this->layers(), custom_facets, (this->trafo_centered() * mv->get_matrix()).cast(), seam, out); +#else + slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &out, [](){}); +#endif } } diff --git a/src/libslic3r/SLA/IndexedMesh.cpp b/src/libslic3r/SLA/IndexedMesh.cpp index 887ef1555..07c4203ab 100644 --- a/src/libslic3r/SLA/IndexedMesh.cpp +++ b/src/libslic3r/SLA/IndexedMesh.cpp @@ -17,10 +17,18 @@ namespace sla { class IndexedMesh::AABBImpl { private: AABBTreeIndirect::Tree3f m_tree; + double m_triangle_ray_epsilon; public: - void init(const indexed_triangle_set &its) + void init(const indexed_triangle_set &its, bool calculate_epsilon) { + m_triangle_ray_epsilon = 0.000001; + if (calculate_epsilon) { + // Calculate epsilon from average triangle edge length. + double l = its_average_edge_length(its); + if (l > 0) + m_triangle_ray_epsilon = 0.000001 * l * l; + } m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( its.vertices, its.indices); } @@ -31,7 +39,7 @@ public: igl::Hit & hit) { AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices, - m_tree, s, dir, hit); + m_tree, s, dir, hit, m_triangle_ray_epsilon); } void intersect_ray(const indexed_triangle_set &its, @@ -40,7 +48,7 @@ public: std::vector & hits) { AABBTreeIndirect::intersect_ray_all_hits(its.vertices, its.indices, - m_tree, s, dir, hits); + m_tree, s, dir, hits, m_triangle_ray_epsilon); } double squared_distance(const indexed_triangle_set & its, @@ -60,25 +68,25 @@ public: } }; -template void IndexedMesh::init(const M &mesh) +template void IndexedMesh::init(const M &mesh, bool calculate_epsilon) { BoundingBoxf3 bb = bounding_box(mesh); m_ground_level += bb.min(Z); // Build the AABB accelaration tree - m_aabb->init(*m_tm); + m_aabb->init(*m_tm, calculate_epsilon); } -IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh) +IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh, bool calculate_epsilon) : m_aabb(new AABBImpl()), m_tm(&tmesh) { - init(tmesh); + init(tmesh, calculate_epsilon); } -IndexedMesh::IndexedMesh(const TriangleMesh &mesh) +IndexedMesh::IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon) : m_aabb(new AABBImpl()), m_tm(&mesh.its) { - init(mesh); + init(mesh, calculate_epsilon); } IndexedMesh::~IndexedMesh() {} diff --git a/src/libslic3r/SLA/IndexedMesh.hpp b/src/libslic3r/SLA/IndexedMesh.hpp index 25ab75b83..9348a97c9 100644 --- a/src/libslic3r/SLA/IndexedMesh.hpp +++ b/src/libslic3r/SLA/IndexedMesh.hpp @@ -42,12 +42,14 @@ class IndexedMesh { std::vector m_holes; #endif - template void init(const M &mesh); + template void init(const M &mesh, bool calculate_epsilon); public: - explicit IndexedMesh(const indexed_triangle_set&); - explicit IndexedMesh(const TriangleMesh &mesh); + // calculate_epsilon ... calculate epsilon for triangle-ray intersection from an average triangle edge length. + // If set to false, a default epsilon is used, which works for "reasonable" meshes. + explicit IndexedMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon = false); + explicit IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon = false); IndexedMesh(const IndexedMesh& other); IndexedMesh& operator=(const IndexedMesh&); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index e2d7bf199..2e4a47954 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -37,6 +37,7 @@ #define DEBUG #define _DEBUG #undef NDEBUG + #include "utils.hpp" #include "SVG.hpp" #endif @@ -429,7 +430,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) for (const MyLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), - union_ex(layer->polygons, false)); + union_ex(layer->polygons)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts"; @@ -447,7 +448,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id) Slic3r::SVG::export_expolygons( debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), - union_ex(layer_support_areas[layer_id], false)); + union_ex(layer_support_areas[layer_id])); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Creating intermediate layers - indices"; @@ -466,7 +467,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) for (const MyLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), - union_ex(layer->polygons, false)); + union_ex(layer->polygons)); #endif BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; @@ -478,7 +479,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) Slic3r::SVG::export_expolygons( debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), - union_ex((*it)->polygons, false)); + union_ex((*it)->polygons)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; @@ -507,11 +508,11 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) for (const MyLayer *l : interface_layers) Slic3r::SVG::export_expolygons( debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z), - union_ex(l->polygons, false)); + union_ex(l->polygons)); for (const MyLayer *l : base_interface_layers) Slic3r::SVG::export_expolygons( debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z), - union_ex(l->polygons, false)); + union_ex(l->polygons)); #endif // SLIC3R_DEBUG /* @@ -1308,9 +1309,9 @@ namespace SupportMaterialInternal { #ifdef SLIC3R_DEBUG static int iRun = 0; SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++), - { { { union_ex(offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), false) }, { "unsupported_bridge_edges", "orange", 0.5f } }, - { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(bridges, false) }, { "bridges", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } }, + { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(bridges) }, { "bridges", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ } } @@ -1416,13 +1417,35 @@ static inline std::tuple detect_overhangs( // Generate overhang / contact_polygons for non-raft layers. const Layer &lower_layer = *layer.lower_layer; const bool has_enforcer = ! annotations.enforcers_layers.empty() && ! annotations.enforcers_layers[layer_id].empty(); - float fw = 0; + + // Cache support trimming polygons derived from lower layer polygons, possible merged with "on build plate only" trimming polygons. + auto slices_margin_update = + [&slices_margin, &lower_layer, &lower_layer_polygons, buildplate_only, has_enforcer, &annotations, layer_id] + (float slices_margin_offset, float no_interface_offset) { + if (slices_margin.offset != slices_margin_offset) { + slices_margin.offset = slices_margin_offset; + slices_margin.polygons = (slices_margin_offset == 0.f) ? + lower_layer_polygons : + offset2(lower_layer.lslices, -no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); + if (buildplate_only && !annotations.buildplate_covered[layer_id].empty()) { + if (has_enforcer) + // Make a backup of trimming polygons before enforcing "on build plate only". + slices_margin.all_polygons = slices_margin.polygons; + // Trim the inflated contact surfaces by the top surfaces as well. + slices_margin.polygons = union_(slices_margin.polygons, annotations.buildplate_covered[layer_id]); + } + } + }; + + float fw = 0; + float lower_layer_offset = 0; + float no_interface_offset = 0; for (LayerRegion *layerm : layer.regions()) { // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. fw = float(layerm->flow(frExternalPerimeter).scaled_width()); no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); - float lower_layer_offset = + lower_layer_offset = (layer_id < (size_t)object_config.support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. 0.f : @@ -1494,7 +1517,7 @@ static inline std::tuple detect_overhangs( iRun, layer_id, std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()), get_extents(diff_polygons)); - Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); + Slic3r::ExPolygons expolys = union_ex(diff_polygons); svg.draw(expolys); } #endif /* SLIC3R_DEBUG */ @@ -1512,7 +1535,7 @@ static inline std::tuple detect_overhangs( iRun, layer_id, std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(), layer.print_z), - union_ex(diff_polygons, false)); + union_ex(diff_polygons)); #endif /* SLIC3R_DEBUG */ //FIXME the overhang_polygons are used to construct the support towers as well. @@ -1529,20 +1552,7 @@ static inline std::tuple detect_overhangs( //FIXME one should trim with the layer span colliding with the support layer, this layer // may be lower than lower_layer, so the support area needed may need to be actually bigger! // For the same reason, the non-bridging support area may be smaller than the bridging support area! - float slices_margin_offset = std::min(lower_layer_offset, float(scale_(gap_xy))); - if (slices_margin.offset != slices_margin_offset) { - slices_margin.offset = slices_margin_offset; - slices_margin.polygons = (slices_margin_offset == 0.f) ? - lower_layer_polygons : - offset2(lower_layer.lslices, - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); - if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) { - if (has_enforcer) - // Make a backup of trimming polygons before enforcing "on build plate only". - slices_margin.all_polygons = slices_margin.polygons; - // Trim the inflated contact surfaces by the top surfaces as well. - slices_margin.polygons = union_(slices_margin.polygons, annotations.buildplate_covered[layer_id]); - } - } + slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset); // Offset the contact polygons outside. #if 0 for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) { @@ -1572,12 +1582,13 @@ static inline std::tuple detect_overhangs( offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { layer.lslices, { "layer.lslices", "gray", 0.2f } }, - { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "green", 0.5f } }, - { enforcers_united, { "enforcers", "blue", 0.5f } }, - { { union_ex(enforcer_polygons, true) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { layer.lslices, { "layer.lslices", "gray", 0.2f } }, + { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "green", 0.5f } }, + { enforcers_united, { "enforcers", "blue", 0.5f } }, + { { union_safety_offset_ex(enforcer_polygons) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ polygons_append(overhang_polygons, enforcer_polygons); + slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset); polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons)); } } @@ -1738,19 +1749,19 @@ static inline void fill_contact_layer( #endif // SLIC3R_DEBUG ); #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, - { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, - { { union_ex(slices_margin.polygons, false) }, { "slices_margin_cached", "blue", 0.5f } }, - { { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } }, - { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); + SVG::export_expolygons(debug_out_path("support-top-contacts-final1-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "gray", 0.2f } }, + { { union_ex(*new_layer.contact_polygons) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, + { { union_ex(slices_margin.polygons) }, { "slices_margin_cached", "blue", 0.5f } }, + { { union_ex(dense_interface_polygons) }, { "dense_interface_polygons", "green", 0.5f } }, + { { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); //support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z)); - SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, - { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, - { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } }, - { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); + SVG::export_expolygons(debug_out_path("support-top-contacts-final2-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "gray", 0.2f } }, + { { union_ex(*new_layer.contact_polygons) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, + { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(dense_interface_polygons) }, { "dense_interface_polygons", "green", 0.5f } }, + { { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ } } @@ -1796,11 +1807,11 @@ static inline void fill_contact_layer( #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, - { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, - { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(overhang_polygons, false) }, { "overhang_polygons", "green", 0.5f } }, - { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "gray", 0.2f } }, + { { union_ex(*new_layer.contact_polygons) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, + { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(overhang_polygons) }, { "overhang_polygons", "green", 0.5f } }, + { { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. @@ -1964,10 +1975,10 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( Polygons top = collect_region_slices_by_type(layer, stTop); #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), - { { { union_ex(top, false) }, { "top", "blue", 0.5f } }, - { { union_ex(supports_projected, true) }, { "overhangs", "magenta", 0.5f } }, - { layer.lslices, { "layer.lslices", "green", 0.5f } }, - { { union_ex(polygons_new, true) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(top) }, { "top", "blue", 0.5f } }, + { { union_safety_offset_ex(supports_projected) }, { "overhangs", "magenta", 0.5f } }, + { layer.lslices, { "layer.lslices", "green", 0.5f } }, + { { union_safety_offset_ex(polygons_new) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any @@ -2037,7 +2048,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), - union_ex(layer_new.polygons, false)); + union_ex(layer_new.polygons)); #endif /* SLIC3R_DEBUG */ // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer. @@ -2050,14 +2061,14 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( if (! layer_support_areas[layer_id_above].empty()) { #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), - { { { union_ex(touching, false) }, { "touching", "blue", 0.5f } }, - { { union_ex(layer_support_areas[layer_id_above], true) }, { "above", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(touching) }, { "touching", "blue", 0.5f } }, + { { union_safety_offset_ex(layer_support_areas[layer_id_above]) }, { "above", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching); #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), - union_ex(layer_support_areas[layer_id_above], false)); + union_ex(layer_support_areas[layer_id_above])); #endif /* SLIC3R_DEBUG */ } } @@ -2080,8 +2091,8 @@ static inline std::pair project_support_to_grid(const Layer #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-support-areas-%s-raw-%d-%lf.svg", debug_name, iRun, layer.print_z), - { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, - { { union_ex(overhangs_projection, true) }, { "overhangs_projection", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(trimming) }, { "trimming", "blue", 0.5f } }, + { { union_safety_offset_ex(overhangs_projection) }, { "overhangs_projection", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ remove_sticks(overhangs_projection); @@ -2089,8 +2100,8 @@ static inline std::pair project_support_to_grid(const Layer #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-support-areas-%s-raw-cleaned-%d-%lf.svg", debug_name, iRun, layer.print_z), - { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, - { { union_ex(overhangs_projection, false) }, { "overhangs_projection", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(trimming) }, { "trimming", "blue", 0.5f } }, + { { union_ex(overhangs_projection) }, { "overhangs_projection", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ SupportGridPattern support_grid_pattern(&overhangs_projection, &trimming, grid_params); @@ -2113,7 +2124,7 @@ static inline std::pair project_support_to_grid(const Layer #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-layer_support_area-gridded-%s-%d-%lf.svg", debug_name, iRun, layer.print_z), - union_ex(out.first, false)); + union_ex(out.first)); #endif /* SLIC3R_DEBUG */ }); @@ -2131,13 +2142,13 @@ static inline std::pair project_support_to_grid(const Layer #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), - union_ex(out.second, false)); + union_ex(out.second)); #endif /* SLIC3R_DEBUG */ #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), - { { { union_ex(trimming, false) }, { "trimming", "gray", 0.5f } }, - { { union_ex(overhangs_projection, true) }, { "overhangs_projection", "blue", 0.5f } }, - { { union_ex(out.second, true) }, { "projection_new", "red", "black", "", scaled(0.1f), 0.5f } } }); + { { { union_ex(trimming) }, { "trimming", "gray", 0.5f } }, + { { union_safety_offset_ex(overhangs_projection) }, { "overhangs_projection", "blue", 0.5f } }, + { { union_safety_offset_ex(out.second) }, { "projection_new", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ }); @@ -2667,10 +2678,10 @@ void PrintObjectSupportMaterial::generate_base_layers( BoundingBox bbox = get_extents(polygons_new); bbox.merge(get_extents(polygons_trimming)); ::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-raw-%d-%lf.svg", iRun, layer_intermediate.print_z), bbox); - svg.draw(union_ex(polygons_new, false), "blue", 0.5f); - svg.draw(to_polylines(polygons_new), "blue"); - svg.draw(union_ex(polygons_trimming, true), "red", 0.5f); - svg.draw(to_polylines(polygons_trimming), "red"); + svg.draw(union_ex(polygons_new), "blue", 0.5f); + svg.draw(to_polylines(polygons_new), "blue"); + svg.draw(union_safety_offset_ex(polygons_trimming), "red", 0.5f); + svg.draw(to_polylines(polygons_trimming), "red"); } #endif /* SLIC3R_DEBUG */ @@ -2706,7 +2717,7 @@ void PrintObjectSupportMaterial::generate_base_layers( for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it) ::Slic3r::SVG::export_expolygons( debug_out_path("support-intermediate-layers-untrimmed-%d-%lf.svg", iRun, (*it)->print_z), - union_ex((*it)->polygons, false)); + union_ex((*it)->polygons)); ++ iRun; #endif /* SLIC3R_DEBUG */ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index d4baabc97..fa8f8bce6 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1275,6 +1275,21 @@ float its_volume(const indexed_triangle_set &its) return volume; } +float its_average_edge_length(const indexed_triangle_set &its) +{ + if (its.indices.empty()) + return 0.f; + + double edge_length = 0.f; + for (size_t i = 0; i < its.indices.size(); ++ i) { + const its_triangle v = its_triangle_vertices(its, i); + edge_length += (v[1] - v[0]).cast().norm() + + (v[2] - v[0]).cast().norm() + + (v[1] - v[2]).cast().norm(); + } + return float(edge_length / (3 * its.indices.size())); +} + std::vector its_split(const indexed_triangle_set &its) { return its_split<>(its); diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index b7a1bebb1..c463af5a2 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -199,6 +199,7 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its, } float its_volume(const indexed_triangle_set &its); +float its_average_edge_length(const indexed_triangle_set &its); void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B); void its_merge(indexed_triangle_set &A, const std::vector &triangles); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 1b02e0331..a01e63166 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -58,6 +58,11 @@ void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. const std::string& data_dir(); +// Format an output path for debugging purposes. +// Writes out the output path prefix to the console for the first time the function is called, +// so the user knows where to search for the debugging output. +std::string debug_out_path(const char *name, ...); + // A special type for strings encoded in the local Windows 8-bit code page. // This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded. typedef std::string local_encoded_string; diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 8d8d15df9..a29436189 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -65,18 +65,6 @@ static constexpr double EXTERNAL_INFILL_MARGIN = 3.; #define SCALED_EPSILON scale_(EPSILON) -#define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/" - -inline std::string debug_out_path(const char *name, ...) -{ - char buffer[2048]; - va_list args; - va_start(args, name); - std::vsprintf(buffer, name, args); - va_end(args); - return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer); -} - #ifndef UNUSED #define UNUSED(x) (void)(x) #endif /* UNUSED */ diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 29f955e92..c330f34b2 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -1,6 +1,7 @@ #include "Utils.hpp" #include "I18N.hpp" +#include #include #include #include @@ -207,6 +208,23 @@ std::string custom_shapes_dir() return (boost::filesystem::path(g_data_dir) / "shapes").string(); } +static std::atomic debug_out_path_called(false); + +std::string debug_out_path(const char *name, ...) +{ + static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; + if (! debug_out_path_called.exchange(true)) { + std::string path = boost::filesystem::system_complete(SLIC3R_DEBUG_OUT_PATH_PREFIX).string(); + printf("Debugging output files will be written to %s\n", path.c_str()); + } + char buffer[2048]; + va_list args; + va_start(args, name); + std::vsprintf(buffer, name, args); + va_end(args); + return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer); +} + #ifdef _WIN32 // The following helpers are borrowed from the LLVM project https://github.com/llvm namespace WindowsSupport diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index fb4a62f91..935121935 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -22,6 +22,95 @@ namespace Slic3r { namespace GUI { namespace { + +// escaping of path string according to +// https://cgit.freedesktop.org/xdg/xdg-specs/tree/desktop-entry/desktop-entry-spec.xml +std::string escape_string(const std::string& str) +{ + // The buffer needs to be bigger if escaping <,>,& + std::vector out(str.size() * 4, 0); + char *outptr = out.data(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + // must be escaped + if (c == '\"') { //double quote + (*outptr ++) = '\\'; + (*outptr ++) = '\"'; + } else if (c == '`') { // backtick character + (*outptr ++) = '\\'; + (*outptr ++) = '`'; + } else if (c == '$') { // dollar sign + (*outptr ++) = '\\'; + (*outptr ++) = '$'; + } else if (c == '\\') { // backslash character + (*outptr ++) = '\\'; + (*outptr ++) = '\\'; + (*outptr ++) = '\\'; + (*outptr ++) = '\\'; + // Reserved characters + // At Ubuntu, all these characters must NOT be escaped for desktop integration to work + /* + } else if (c == ' ') { // space + (*outptr ++) = '\\'; + (*outptr ++) = ' '; + } else if (c == '\t') { // tab + (*outptr ++) = '\\'; + (*outptr ++) = '\t'; + } else if (c == '\n') { // newline + (*outptr ++) = '\\'; + (*outptr ++) = '\n'; + } else if (c == '\'') { // single quote + (*outptr ++) = '\\'; + (*outptr ++) = '\''; + } else if (c == '>') { // greater-than sign + (*outptr ++) = '\\'; + (*outptr ++) = '&'; + (*outptr ++) = 'g'; + (*outptr ++) = 't'; + (*outptr ++) = ';'; + } else if (c == '<') { //less-than sign + (*outptr ++) = '\\'; + (*outptr ++) = '&'; + (*outptr ++) = 'l'; + (*outptr ++) = 't'; + (*outptr ++) = ';'; + } else if (c == '~') { // tilde + (*outptr ++) = '\\'; + (*outptr ++) = '~'; + } else if (c == '|') { // vertical bar + (*outptr ++) = '\\'; + (*outptr ++) = '|'; + } else if (c == '&') { // ampersand + (*outptr ++) = '\\'; + (*outptr ++) = '&'; + (*outptr ++) = 'a'; + (*outptr ++) = 'm'; + (*outptr ++) = 'p'; + (*outptr ++) = ';'; + } else if (c == ';') { // semicolon + (*outptr ++) = '\\'; + (*outptr ++) = ';'; + } else if (c == '*') { //asterisk + (*outptr ++) = '\\'; + (*outptr ++) = '*'; + } else if (c == '?') { // question mark + (*outptr ++) = '\\'; + (*outptr ++) = '?'; + } else if (c == '#') { // hash mark + (*outptr ++) = '\\'; + (*outptr ++) = '#'; + } else if (c == '(') { // parenthesis + (*outptr ++) = '\\'; + (*outptr ++) = '('; + } else if (c == ')') { + (*outptr ++) = '\\'; + (*outptr ++) = ')'; + */ + } else + (*outptr ++) = c; + } + return std::string(out.data(), outptr - out.data()); +} // Disects path strings stored in system variable divided by ':' and adds into vector void resolve_path_from_var(const std::string& var, std::vector& paths) { @@ -157,7 +246,8 @@ void DesktopIntegrationDialog::perform_desktop_integration() } // Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path' - boost::replace_all(excutable_path, "'", "'\\''"); + //boost::replace_all(excutable_path, "'", "'\\''"); + excutable_path = escape_string(excutable_path); // Find directories icons and applications // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. @@ -243,14 +333,14 @@ void DesktopIntegrationDialog::perform_desktop_integration() "Name=PrusaSlicer%1%\n" "GenericName=3D Printing Software\n" "Icon=PrusaSlicer%2%\n" - "Exec=\'%3%\' %%F\n" + "Exec=\"%3%\" %%F\n" "Terminal=false\n" "Type=Application\n" "MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n" "Categories=Graphics;3DGraphics;Engineering;\n" "Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n" "StartupNotify=false\n" - "StartupWMClass=prusa-slicer", name_suffix, version_suffix, excutable_path); + "StartupWMClass=prusa-slicer\n", name_suffix, version_suffix, excutable_path); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); if (create_desktop_file(path, desktop_file)){ @@ -310,13 +400,13 @@ void DesktopIntegrationDialog::perform_desktop_integration() "Name=Prusa Gcode Viewer%1%\n" "GenericName=3D Printing Software\n" "Icon=PrusaSlicer-gcodeviewer%2%\n" - "Exec=\'%3%\' --gcodeviwer %%F\n" + "Exec=\"%3%\" --gcodeviewer %%F\n" "Terminal=false\n" "Type=Application\n" "MimeType=text/x.gcode;\n" "Categories=Graphics;3DGraphics;\n" "Keywords=3D;Printing;Slicer;\n" - "StartupNotify=false", name_suffix, version_suffix, excutable_path); + "StartupNotify=false\n", name_suffix, version_suffix, excutable_path); std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix); if (create_desktop_file(desktop_path, desktop_file)) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d4030d41f..b85219c0b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1049,7 +1049,7 @@ void ObjectList::key_event(wxKeyEvent& event) || event.GetKeyCode() == WXK_BACK #endif //__WXOSX__ ) { - wxGetApp().plater()->remove_selected(); + remove(); } else if (event.GetKeyCode() == WXK_F5) wxGetApp().plater()->reload_all_from_disk(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 5ce0064db..b9e5d111b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -199,7 +199,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::SameLine(m_gui_cfg->bottom_left_width); if (m_imgui->button(_L("Preview"))) { m_state = State::preview; - // simplify but not aply on mesh + // simplify but not apply on mesh process(); } ImGui::SameLine(); @@ -207,15 +207,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (!m_is_valid_result) { m_state = State::close_on_end; process(); - } else { + } else if (m_exist_preview) { // use preview and close - if (m_exist_preview) { - // fix hollowing, sla support points, modifiers, ... - auto plater = wxGetApp().plater(); - plater->changed_mesh(m_obj_index); - } + after_apply(); + } else { // no changes made close(); - } + } } } else { m_imgui->disabled_begin(m_state == State::canceling); @@ -237,18 +234,22 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_parent.reload_scene(true); // set m_state must be before close() !!! m_state = State::settings; - if (close_on_end) { - // fix hollowing, sla support points, modifiers, ... - auto plater = wxGetApp().plater(); - plater->changed_mesh(m_obj_index); - close(); - } + if (close_on_end) after_apply(); // Fix warning icon in object list wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1); } } +void GLGizmoSimplify::after_apply() { + // set flag to NOT revert changes when switch GLGizmoBase::m_state + m_exist_preview = false; + // fix hollowing, sla support points, modifiers, ... + auto plater = wxGetApp().plater(); + plater->changed_mesh(m_obj_index); + close(); +} + void GLGizmoSimplify::close() { // close gizmo == open it again GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager(); @@ -282,11 +283,11 @@ void GLGizmoSimplify::process() } }; - std::function statusfn = [this](int percent) { + int64_t last = 0; + std::function statusfn = [this, &last](int percent) { m_progress = percent; // check max 4fps - static int64_t last = 0; int64_t now = m_parent.timestamp_now(); if ((now - last) < 250) return; last = now; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 1b25c4ac9..0f5bd5991 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -32,6 +32,7 @@ protected: virtual void on_set_state() override; private: + void after_apply(); void close(); void process(); void set_its(indexed_triangle_set &its); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index ba83bdbff..75232c930 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -106,7 +106,6 @@ void MeshClipper::recalculate_triangles() Transform3d tr = Transform3d::Identity(); tr.rotate(q); tr = m_trafo.get_matrix() * tr; - height_mesh += 0.001f; // to avoid z-fighting if (m_limiting_plane != ClippingPlane::ClipsNothing()) { @@ -165,6 +164,8 @@ void MeshClipper::recalculate_triangles() m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.); + tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting + m_vertex_array.release_geometry(); for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index ec6c337c0..65c326116 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -112,7 +112,7 @@ public: // The class references extern TriangleMesh, which must stay alive // during MeshRaycaster existence. MeshRaycaster(const TriangleMesh& mesh) - : m_emesh(mesh) + : m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length { m_normals.reserve(mesh.stl.facet_start.size()); for (const stl_facet& facet : mesh.stl.facet_start) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1841220d5..d315e7734 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2915,6 +2915,7 @@ void Plater::priv::update_print_volume_state() BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt("bed_shape")->values)); BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height")))); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. + print_volume.offset(BedEpsilon); print_volume.min(2) = -1e10; this->q->model().update_print_volume_state(print_volume); } diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index 083d21d38..68ea282bc 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -23,12 +23,6 @@ BUILD() RETVAL = newSVpv(SLIC3R_BUILD_ID, 0); OUTPUT: RETVAL -SV* -DEBUG_OUT_PATH_PREFIX() - CODE: - RETVAL = newSVpv(SLIC3R_DEBUG_OUT_PATH_PREFIX, 0); - OUTPUT: RETVAL - SV* FORK_NAME() CODE: