diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 2d92187ba..c33983dfc 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -316,6 +316,35 @@ Point Polygon::point_projection(const Point &point) const return proj; } +size_t Polygon::remove_colinear_points(coord_t max_offset){ + size_t nb_del = 0; + if (points.size() < 3) return 0; + + coord_t min_dist = max_offset * max_offset; + while (points.size() > 2 && Line::distance_to_squared(points[0], points.back(), points[1]) < min_dist){ + //colinear! delete! + points.erase(points.begin()); + nb_del++; + } + for (size_t idx = 1; idx < points.size()-1; ) { + //if (Line(previous, points[idx + 1]).distance_to(points[idx]) < SCALED_EPSILON){ + if (Line::distance_to_squared(points[idx], points[idx-1], points[idx + 1]) < min_dist){ + //colinear! delete! + points.erase(points.begin() + idx); + nb_del++; + } else { + idx++; + } + } + while (points.size() > 2 && Line::distance_to_squared(points.back(), points[points.size()-2], points.front()) < min_dist) { + //colinear! delete! + points.erase(points.end()-1); + nb_del++; + } + + return nb_del; +} + BoundingBox get_extents(const Polygon &poly) { return poly.bounding_box(); diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 3fd974605..c1004330d 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -59,6 +59,9 @@ public: Points convex_points(double angle = PI) const; // Projection of a point onto the polygon. Point point_projection(const Point &point) const; + /// remove points that are (almost) on an existing line from previous & next point. + /// return number of point removed + size_t remove_colinear_points(coord_t max_offset); }; extern BoundingBox get_extents(const Polygon &poly); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 56973013b..171489a1f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1870,10 +1870,10 @@ void PrintConfigDef::init_fff_params() def = this->add("resolution", coFloat); def->label = L("Resolution"); def->tooltip = L("Minimum detail resolution, used to simplify the input file for speeding up " - "the slicing job and reducing memory usage. High-resolution models often carry " - "more detail than printers can render. Set to zero to disable any simplification " - "and use full resolution from input. " - "\nNote: slic3r simplify the geometry with a treshold of 0.0125mm and has an internal resolution of 0.0001mm."); + "the slicing job and reducing memory usage. High-resolution models often carry " + "more detail than printers can render. Set to zero to disable any simplification " + "and use full resolution from input. " + "\nNote: slic3r simplify the geometry with a treshold of 0.0125mm and has an internal resolution of 0.0001mm."); def->sidetext = L("mm"); def->cli = "resolution=f"; def->min = 0; @@ -2254,6 +2254,20 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->default_value = new ConfigOptionStrings { "; Filament gcode\n" }; + + def = this->add("model_precision", coFloat); + def->label = L("Model rounding precision"); + def->full_label = L("Model rounding precision"); + def->category = L("Advanced"); + def->tooltip = L("This is the rounding error of the input object." + " It's used to align points that should be in the same line." + " Put 0 to disable."); + def->sidetext = L("mm"); + def->cli = "model-precision=f"; + def->min = 0; + def->mode = comExpert; + def->default_value = new ConfigOptionFloat(0.0001); + def = this->add("single_extruder_multi_material", coBool); def->label = L("Single Extruder Multi Material"); def->tooltip = L("The printer multiplexes filaments into a single hot end."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 2f720afc8..5394355e5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -453,13 +453,14 @@ public: ConfigOptionBool clip_multipart_objects; ConfigOptionBool dont_support_bridges; ConfigOptionFloat elefant_foot_compensation; + ConfigOptionBool exact_last_layer_height; ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; ConfigOptionBool infill_only_where_needed; // Force the generation of solid shells between adjacent materials/volumes. ConfigOptionBool interface_shells; ConfigOptionFloat layer_height; - ConfigOptionBool exact_last_layer_height; + ConfigOptionFloat model_precision; ConfigOptionInt raft_layers; ConfigOptionEnum seam_position; ConfigOptionBool seam_travel; @@ -505,12 +506,13 @@ protected: OPT_PTR(clip_multipart_objects); OPT_PTR(dont_support_bridges); OPT_PTR(elefant_foot_compensation); + OPT_PTR(exact_last_layer_height); OPT_PTR(extrusion_width); OPT_PTR(first_layer_height); OPT_PTR(infill_only_where_needed); OPT_PTR(interface_shells); OPT_PTR(layer_height); - OPT_PTR(exact_last_layer_height); + OPT_PTR(model_precision); OPT_PTR(raft_layers); OPT_PTR(seam_position); OPT_PTR(seam_travel); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 66081bb8a..6bcb690d7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -813,7 +813,6 @@ void PrintObject::tag_under_bridge() { for (ExPolygon poly_inter : dense_polys) area_dense += poly_inter.area(); double area_sparse = 0; for (ExPolygon poly_inter : sparse_polys) area_sparse += poly_inter.area(); - std::cout << "need to split? " << area_sparse << " > " << area_dense << " * " << COEFF_SPLIT << "\n"; if (area_sparse > area_dense * COEFF_SPLIT) { //split dense_polys = union_ex(dense_polys); @@ -1913,27 +1912,27 @@ end: void PrintObject::_offsetHoles(float hole_delta, LayerRegion *layerm) { if (hole_delta != 0.f) { + std::cout << "offset_hole z="<layer()->id()<<"\n"; ExPolygons polys = to_expolygons(std::move(layerm->slices.surfaces)); ExPolygons new_polys; - for (ExPolygon ex_poly : polys) { + for (const ExPolygon &ex_poly : polys) { ExPolygon new_ex_poly(ex_poly); new_ex_poly.holes.clear(); - for (Polygon hole : ex_poly.holes) { + for (const Polygon &hole : ex_poly.holes) { //check if convex to reduce it // check whether first point forms a convex angle + //note: we allow a deviation of 5.7° (0.01rad = 0.57°) bool ok = true; - ok = (hole.points.front().ccw_angle(hole.points.back(), *(hole.points.begin() + 1)) <= PI + 0.001); - - + ok = (hole.points.front().ccw_angle(hole.points.back(), *(hole.points.begin() + 1)) <= PI + 0.1); // check whether points 1..(n-1) form convex angles if (ok) for (Points::const_iterator p = hole.points.begin() + 1; p != hole.points.end() - 1; ++p) { - ok = (p->ccw_angle(*(p - 1), *(p + 1)) <= PI + 0.001); + ok = (p->ccw_angle(*(p - 1), *(p + 1)) <= PI + 0.1); if (!ok) break; } // check whether last point forms a convex angle - ok &= (hole.points.back().ccw_angle(*(hole.points.end() - 2), hole.points.front()) <= PI + 0.001); + ok &= (hole.points.back().ccw_angle(*(hole.points.end() - 2), hole.points.front()) <= PI + 0.1); if (ok) { for (Polygon newHole : offset(hole, -hole_delta)) { @@ -1941,8 +1940,9 @@ void PrintObject::_offsetHoles(float hole_delta, LayerRegion *layerm) { newHole.make_clockwise(); new_ex_poly.holes.push_back(newHole); } - } else + } else { new_ex_poly.holes.push_back(hole); + } } new_polys.push_back(new_ex_poly); } @@ -2007,11 +2007,11 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, // apply XY shift mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0); // perform actual slicing - TriangleMeshSlicer mslicer; + TriangleMeshSlicer mslicer(float(m_config.slice_closing_radius.value), float(m_config.model_precision.value)); const Print *print = this->print(); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); mslicer.init(&mesh, callback); - mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); + mslicer.slice(z, &layers, callback); m_print->throw_if_canceled(); } } diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 6fd218402..62d8f0f30 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -561,7 +561,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, heights.emplace_back(hi); std::vector out; out.reserve(size_t(std::ceil(h/layerh))); - slicer.slice(heights, 0.f, &out, thrfn); + slicer.slice(heights, &out, thrfn); size_t count = 0; for(auto& o : out) count += o.size(); diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 0ba09ead8..913e9beda 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -2105,7 +2105,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const fullmesh.merge(get_pad()); TriangleMeshSlicer slicer(&fullmesh); SlicedSupports ret; - slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn); + slicer.slice(heights, &ret, get().ctl().cancelfn); return ret; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 23880cfa9..803b2acbe 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -629,7 +629,8 @@ void SLAPrint::process() double lh = po.m_config.layer_height.getFloat(); TriangleMesh mesh = po.transformed_mesh(); - TriangleMeshSlicer slicer(&mesh); + TriangleMeshSlicer slicer(float(po.config().slice_closing_radius.value),0); + slicer.init(&mesh, [](){}); // The 1D grid heights std::vector heights = calculate_heights(mesh.bounding_box(), @@ -637,7 +638,7 @@ void SLAPrint::process() ilh, float(lh)); auto& layers = po.m_model_slices; layers.clear(); - slicer.slice(heights, float(po.config().slice_closing_radius.value), &layers, [this](){ throw_if_canceled(); }); + slicer.slice(heights, &layers, [this](){ throw_if_canceled(); }); }; // In this step we check the slices, identify island and cover them with diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 785d9005c..c5ffe8614 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -852,22 +852,22 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector &z, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; this->slice(z, &layers_p, throw_on_cancel); - + BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start"; layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&layers_p, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range& range) { + [&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif throw_on_cancel(); - this->make_expolygons(layers_p[layer_id], closing_radius, &(*layers)[layer_id]); + this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]); } }); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end"; @@ -1002,7 +1002,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( line_out->a_id = points[1].point_id; line_out->b_id = points[0].point_id; line_out->edge_a_id = points[1].edge_id; - line_out->edge_b_id = points[0].edge_id; + line_out->edge_b_id = points[0].edge_id; // Not a zero lenght edge. //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. //assert(line_out->a != line_out->b); @@ -1184,7 +1184,7 @@ static void chain_lines_by_triangle_connectivity(std::vector & if (next_line == nullptr) { // Check whether we closed this loop. if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) || - (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) { + (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) { // The current loop is complete. Add it to the output. loops.emplace_back(std::move(loop_pts)); #ifdef SLIC3R_TRIANGLEMESH_DEBUG @@ -1600,7 +1600,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector &l #endif } -void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const +void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const { /* Input loops are not suitable for evenodd nor nonzero fill types, as we might get @@ -1651,11 +1651,19 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float clos // p_slices = diff(p_slices, *loop); //} + //remove point in the same plane (have to do that before the safety offset to avoid workgin on a distored polygon) + Polygons filered_polys = loops; + if (this->model_precision > 0){ + for (Polygon &hole : filered_polys){ + hole.remove_colinear_points(scale_(this->model_precision)); + } + } + // Perform a safety offset to merge very close facets (TODO: find test case for this) // 0.0499 comes from https://github.com/slic3r/Slic3r/issues/959 // double safety_offset = scale_(0.0499); // 0.0001 is set to satisfy GH #520, #1029, #1364 - double safety_offset = scale_(closing_radius); + double safety_offset = scale_(this->closing_radius); /* The following line is commented out because it can generate wrong polygons, see for example issue #661 */ @@ -1671,16 +1679,16 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float clos // append to the supplied collection if (safety_offset > 0) - expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset)); + expolygons_append(*slices, offset2_ex(union_(filered_polys, false), +safety_offset, -safety_offset)); else - expolygons_append(*slices, union_ex(loops, false)); + expolygons_append(*slices, union_ex(filered_polys, false)); } -void TriangleMeshSlicer::make_expolygons(std::vector &lines, const float closing_radius, ExPolygons* slices) const +void TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const { Polygons pp; this->make_loops(lines, &pp); - this->make_expolygons(pp, closing_radius, slices); + this->make_expolygons(pp, slices); } void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 04c13e876..736706cb2 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -159,13 +159,16 @@ typedef std::vector IntersectionLinePtrs; class TriangleMeshSlicer { public: + float closing_radius; + float model_precision; + typedef std::function throw_on_cancel_callback_type; - TriangleMeshSlicer() : mesh(nullptr) {} + TriangleMeshSlicer(float closing_radius, float model_precision) : mesh(nullptr), closing_radius(closing_radius), model_precision(model_precision) {} // Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices() - TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); } + TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); } void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; - void slice(const std::vector &z, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; enum FacetSliceType { NoSlice = 0, Slicing = 1, @@ -184,9 +187,9 @@ private: void _slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; void make_loops(std::vector &lines, Polygons* loops) const; - void make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const; + void make_expolygons(const Polygons &loops, ExPolygons* slices) const; void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; - void make_expolygons(std::vector &lines, const float closing_radius, ExPolygons* slices) const; + void make_expolygons(std::vector &lines, ExPolygons* slices) const; }; TriangleMesh make_cube(double x, double y, double z); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index bb378ae6c..ffd15cd98 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -402,6 +402,7 @@ const std::vector& Preset::print_options() , "first_layer_infill_speed" , "thin_walls_min_width" , "thin_walls_overlap" + , "model_precision" }; return s_opts; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1d54b65b0..b9a958d53 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1179,6 +1179,7 @@ void TabPrint::build() optgroup = page->new_optgroup(_(L("Other"))); optgroup->append_single_option_line("clip_multipart_objects"); optgroup->append_single_option_line("resolution"); + optgroup->append_single_option_line("model_precision"); page = add_options_page(_(L("Output options")), "page_white_go.png"); optgroup = page->new_optgroup(_(L("Sequential printing")));