diff options
author | supermerill <merill@free.fr> | 2021-12-23 01:26:51 +0300 |
---|---|---|
committer | supermerill <merill@free.fr> | 2021-12-23 01:26:51 +0300 |
commit | de7bfabae8677914aab6bb047e5fcaf24f7b7d6d (patch) | |
tree | 9c88fd4ee36aa796d5733583d296715fedbcd4ea /src | |
parent | 8dc30cf6353e085134d5d8147437e368f269e924 (diff) | |
parent | 9198f9e26eb4f49296d3a88e6f182ae95c908968 (diff) |
merge fixes
Diffstat (limited to 'src')
35 files changed, 545 insertions, 234 deletions
diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp index 29a1ccd04..07b34f47d 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp @@ -281,9 +281,10 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh, // cos function. But with the quadrant info we can get the sign back int sign = q[0] == 1 || q[0] == 2 ? -1 : 1; + // supermerill: add safe-check for when two points are on the same position // If Ratio is an actual rational type, there is no precision loss - auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0]; - auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1]; + auto pcos1 = lsq1 != 0 ? Ratio(lcos[0]) / lsq1 * sign * lcos[0] : 1 * sign * lcos[0]; + auto pcos2 = lsq2 != 0 ? Ratio(lcos[1]) / lsq2 * sign * lcos[1] : 1 * sign * lcos[1]; return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2; } diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 7ea96372c..60e02e626 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -9,15 +9,16 @@ #include <vector> #include <stdexcept> -#include <boost/filesystem/path.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/format/format_fwd.hpp> +#include <boost/locale.hpp> +#include <boost/log/trivial.hpp> #include <boost/nowide/cenv.hpp> #include <boost/nowide/fstream.hpp> #include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ptree_fwd.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/format/format_fwd.hpp> -#include <boost/log/trivial.hpp> #ifdef WIN32 //FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream. diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 5ecd25ff2..5c8058aef 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -305,6 +305,7 @@ public: enum FlagsConfigOption : uint32_t { FCO_PHONY = 1, FCO_EXTRUDER_ARRAY = 1 << 1, + FCO_PLACEHOLDER_TEMP = 1 << 2, }; ConfigOption() : flags(uint32_t(0)) {} diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index c8616f42f..fd3270663 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -78,6 +78,8 @@ FillConcentricWGapFill::fill_surface_extrusion( const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { + double min_gapfill_area = double(params.flow.scaled_width()) * double(params.flow.scaled_width()); + if (params.config != nullptr) min_gapfill_area = scale_d(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * double(params.flow.scaled_width()); // Perform offset. Slic3r::ExPolygons expp = offset_ex(surface->expolygon, double(scale_(0 - 0.5 * this->get_spacing()))); // Create the infills for each of the regions. @@ -149,7 +151,7 @@ FillConcentricWGapFill::fill_surface_extrusion( for (const ExPolygon &ex : gaps_ex) { //remove too small gaps that are too hard to fill. //ie one that are smaller than an extrusion with width of min and a length of max. - if (ex.area() > min*max) { + if (ex.area() > min_gapfill_area) { MedialAxis{ ex, coord_t(max), coord_t(min), coord_t(params.flow.height) }.build(polylines); } } diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 0ff0972c5..461fc0e62 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -168,8 +168,6 @@ void FillGyroid::_fill_surface_single( const double tolerance_old = std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) / unscaled(scaleFactor); const double tolerance_old2 = std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) * density_adjusted / this->get_spacing(); const double tolerance = params.config->get_computed_value("resolution_internal") * density_adjusted / this->get_spacing(); - std::cout << "gyroid tolerance: " << tolerance_old << " == " << tolerance_old2 << " ? "<< tolerance << "\n"; - std::cout << "this->get_spacing(): " << this->get_spacing() << " , scaleFactor= " << unscaled(scaleFactor) << " , min(spa, 0.2)= " << std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) << "\n"; // generate pattern Polylines polylines = make_gyroid_waves( diff --git a/src/libslic3r/Format/CWS.cpp b/src/libslic3r/Format/CWS.cpp index b28a47f50..395be5044 100644 --- a/src/libslic3r/Format/CWS.cpp +++ b/src/libslic3r/Format/CWS.cpp @@ -2,6 +2,10 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/Time.hpp" +#include <boost/log/trivial.hpp> +#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp> + namespace Slic3r { using ConfMap = std::map<std::string, std::string>; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d8c6d6285..6a4b0a97a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -16,9 +16,10 @@ #include <algorithm> #include <cstdlib> +#include <map> #include <math.h> +#include <unordered_set> #include <string_view> -#include <map> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/find.hpp> @@ -3338,12 +3339,16 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s Point current_point = paths.front().first_point(); Point next_point = paths.front().polyline.points[1]; // second point + gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n"; //extra wipe before the little move. if (EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter, 0) > 0) { coordf_t wipe_dist = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter,0)); ExtrusionPaths paths_wipe; + m_wipe.reset_path(); for (int i = 0; i < paths.size(); i++) { ExtrusionPath& path = paths[i]; + if (wipe_dist > 0) { + //first, we use the polyline for wipe_extra_perimeter if (path.length() < wipe_dist) { wipe_dist -= path.length(); paths_wipe.push_back(path); @@ -3362,8 +3367,14 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s } else { next_point = paths[0].first_point(); } - break; + m_wipe.path.append(path.polyline); + m_wipe.path.clip_start(wipe_dist); + wipe_dist -= path.length(); } + } else { + //then, it's stored for the wipe on retract + m_wipe.path.append(path.polyline); + } } //move for (ExtrusionPath& path : paths_wipe) { @@ -3371,6 +3382,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s prev_point = current_point; current_point = pt; gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), 0.0, config().gcode_comments ? "; extra wipe" : ""); + this->set_last_pos(pt); } } } @@ -3398,7 +3410,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s Vec2d current_pos = current_point.cast<double>(); Vec2d next_pos = next_point.cast<double>(); Vec2d vec_dist = next_pos - current_pos; - double nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)); + const coordf_t nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)); double l2 = vec_dist.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -3406,6 +3418,65 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s pt.rotate(angle, current_point); // generate the travel move gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), 0.0, "move inwards before travel"); + this->set_last_pos(pt); + gcode += ";" + GCodeProcessor::Wipe_End_Tag + "\n"; + + // also shift the wipe on retract + if (m_wipe.enable) { + current_pos = pt.cast<double>(); + //go to the inside (use clipper for easy shift) + Polygon original_polygon = original_loop.polygon(); + Polygons polys = offset(original_polygon, (original_polygon.is_clockwise()?1:-1) * nd / 2); + //find nearest point + size_t best_poly_idx = 0; + size_t best_pt_idx = 0; + coordf_t best_sqr_dist = nd*nd*2; + for (size_t poly_idx = 0; poly_idx < polys.size(); poly_idx++) { + Polygon& poly = polys[poly_idx]; + if (poly.is_clockwise() ^ original_polygon.is_clockwise()) + poly.reverse(); + for (size_t pt_idx = 0; pt_idx < poly.points.size(); pt_idx++) { + if (poly.points[pt_idx].distance_to_square(pt) < best_sqr_dist) { + best_sqr_dist = poly.points[pt_idx].distance_to_square(pt); + best_poly_idx = poly_idx; + best_pt_idx = pt_idx; + } + } + } + if (best_sqr_dist == nd * nd * 2) { + //try to find an edge + for (size_t poly_idx = 0; poly_idx < polys.size(); poly_idx++) { + Polygon& poly = polys[poly_idx]; + if (poly.is_clockwise() ^ original_polygon.is_clockwise()) + poly.reverse(); + poly.points.push_back(poly.points.front()); + for (size_t pt_idx = 0; pt_idx < poly.points.size()-1; pt_idx++) { + if (Line{ poly.points[pt_idx], poly.points[pt_idx + 1] }.distance_to_squared(pt) < best_sqr_dist) { + poly.points.insert(poly.points.begin() + pt_idx + 1, pt); + best_sqr_dist = 0; + best_poly_idx = poly_idx; + best_pt_idx = pt_idx + 1; + poly.points.erase(poly.points.end() - 1); + break; + } + } + } + } + if (best_sqr_dist == nd * nd * 2) { + //can't find a path, use the old one + //BOOST_LOG_TRIVIAL(warning) << "Warn: can't find a proper path for wipe on retract. Layer " << m_layer_index << ", pos " << this->point_to_gcode(pt).x() << " : " << this->point_to_gcode(pt).y() << " !"; + } else { + m_wipe.reset_path(); + //get the points from here + Polygon& poly = polys[best_poly_idx]; + for (size_t pt_idx = best_pt_idx; pt_idx < poly.points.size(); pt_idx++) { + m_wipe.path.append(poly.points[pt_idx]); + } + for (size_t pt_idx = 0; pt_idx < best_pt_idx; pt_idx++) { + m_wipe.path.append(poly.points[pt_idx]); + } + } + } } return gcode; @@ -4330,40 +4401,49 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole this->origin in order to get G-code coordinates. */ Polyline travel { this->last_pos(), point }; - // check / compute avoid_crossing_perimeters - bool will_cross_perimeter = this->can_cross_perimeter(travel); // check whether wipe could be disabled without causing visible stringing bool could_be_wipe_disabled = false; + //can use the avoid crossing algo? + bool can_avoid_cross_peri = m_config.avoid_crossing_perimeters + && !m_avoid_crossing_perimeters.disabled_once() + && m_avoid_crossing_perimeters.is_init() + && !(m_config.avoid_crossing_not_first_layer && this->on_first_layer()); + + // check / compute avoid_crossing_perimeters + bool will_cross_perimeter = this->can_cross_perimeter(travel, can_avoid_cross_peri); + // if a retraction would be needed (with a low min_dist threshold), try to use avoid_crossing_perimeters to plan a // multi-hop travel path inside the configuration space if (will_cross_perimeter && this->needs_retraction(travel, role, scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.4)) * 3) - && m_config.avoid_crossing_perimeters - && ! m_avoid_crossing_perimeters.disabled_once() - && m_avoid_crossing_perimeters.is_init() - && !(m_config.avoid_crossing_not_first_layer && this->on_first_layer())) { + && can_avoid_cross_peri) { travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); } + if(can_avoid_cross_peri) + will_cross_perimeter = this->can_cross_perimeter(travel, false); // check whether a straight travel move would need retraction bool needs_retraction = this->needs_retraction(travel, role); + if (m_config.only_retract_when_crossing_perimeters) + needs_retraction = needs_retraction && will_cross_perimeter; // Re-allow avoid_crossing_perimeters for the next travel moves m_avoid_crossing_perimeters.reset_once_modifiers(); // generate G-code for the travel move if (needs_retraction) { - if (m_config.avoid_crossing_perimeters && could_be_wipe_disabled) + if (m_config.avoid_crossing_perimeters && could_be_wipe_disabled && EXTRUDER_CONFIG_WITH_DEFAULT(wipe_only_crossing, true)) m_wipe.reset_path(); Point last_post_before_retract = this->last_pos(); gcode += this->retract(); // When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters. - // Because of it, it is necessary to call avoid crossing perimeters for the path between previous last_post and last_post after calling retraction() - if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters) { - Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, last_post_before_retract); - append(retract_travel.points, travel.points); - travel = std::move(retract_travel); + if (last_post_before_retract != this->last_pos() && can_avoid_cross_peri) { + // Is the distance is short enough to just shortcut it? + if (last_post_before_retract.distance_to(this->last_pos()) > scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.4)) * 2) { + // Because of it, it is necessary to redo the thing + travel = m_avoid_crossing_perimeters.travel_to(*this, point); + } } } else { // Reset the wipe path when traveling, so one would not wipe along an old path. @@ -4471,7 +4551,7 @@ bool GCode::needs_retraction(const Polyline& travel, ExtrusionRole role /*=erNon return true; } -bool GCode::can_cross_perimeter(const Polyline& travel) +bool GCode::can_cross_perimeter(const Polyline& travel, bool offset) { if(m_layer != nullptr) if ( (m_config.only_retract_when_crossing_perimeters && m_config.fill_density.value > 0) || m_config.avoid_crossing_perimeters) @@ -4490,13 +4570,13 @@ bool GCode::can_cross_perimeter(const Polyline& travel) //if (inside) { //contained inside at least one bb //construct m_layer_slices_offseted if needed - if (m_layer_slices_offseted.layer != m_layer) { + if (m_layer_slices_offseted.layer != m_layer && offset) { m_layer_slices_offseted.layer = m_layer; m_layer_slices_offseted.diameter = scale_t(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.4)); - m_layer_slices_offseted.slices = offset_ex(m_layer->lslices, - m_layer_slices_offseted.diameter * 1.5); + m_layer_slices_offseted.slices = offset_ex(m_layer->lslices, -m_layer_slices_offseted.diameter * 1.5f); } // test if a expoly contains the entire travel - for (const ExPolygon &poly : m_layer_slices_offseted.slices) + for (const ExPolygon &poly : offset ? m_layer_slices_offseted.slices : m_layer->lslices) if (poly.contains(travel)) { return false; } @@ -4529,6 +4609,8 @@ std::string GCode::retract(bool toolchange) methods even if we performed wipe, since this will ensure the entire retraction length is honored in case wipe path was too short. */ gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); + + //check if need to lift bool need_lift = !m_writer.tool_is_extruder() || toolchange || (BOOL_EXTRUDER_CONFIG(retract_lift_first_layer) && m_config.print_retract_lift.value != 0 && this->m_layer_index == 0) || this->m_writer.get_extra_lift() > 0; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 7fab43e75..bfa644595 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -318,7 +318,7 @@ private: Polyline travel_to(std::string& gcode, const Point &point, ExtrusionRole role); void write_travel_to(std::string& gcode, const Polyline& travel, std::string comment); - bool can_cross_perimeter(const Polyline& travel); + bool can_cross_perimeter(const Polyline& travel, bool offset); bool needs_retraction(const Polyline& travel, ExtrusionRole role = erNone, coordf_t max_min_dist = 0); std::string retract(bool toolchange = false); std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 1d40a62bd..cad59bd32 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -9,6 +9,8 @@ #include "../SVG.hpp" #include "AvoidCrossingPerimeters.hpp" +#include <boost/log/trivial.hpp> + #include <numeric> #include <unordered_set> diff --git a/src/libslic3r/GCode/FanMover.cpp b/src/libslic3r/GCode/FanMover.cpp index ca6db9ecf..6e138d1d2 100644 --- a/src/libslic3r/GCode/FanMover.cpp +++ b/src/libslic3r/GCode/FanMover.cpp @@ -242,6 +242,8 @@ void FanMover::_process_gcode_line(GCodeReader& reader, const GCodeReader::GCode if (fan_speed >= 0) { const auto fan_baseline = (m_writer.config.fan_percentage.value ? 100.0 : 255.0); fan_speed = 100 * fan_speed / fan_baseline; + //speed change: stop kickstart reverting if any + m_current_kickstart.time = -1; if (!m_is_custom_gcode) { // if slow down => put in the queue. if not => if (m_back_buffer_fan_speed < fan_speed) { diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index ede145d8d..09e45f1fe 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1,10 +1,13 @@ #include "GCodeWriter.hpp" #include "CustomGCode.hpp" + +#include <boost/lexical_cast.hpp> + #include <algorithm> +#include <assert.h> #include <iomanip> #include <iostream> #include <map> -#include <assert.h> #define FLAVOR_IS(val) this->config.gcode_flavor.value == val #define FLAVOR_IS_NOT(val) this->config.gcode_flavor.value != val diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index c44c1d97a..02b4816e2 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -8,6 +8,9 @@ #include "SVG.hpp" #include "polypartition.h" #include "poly2tri/poly2tri.h" + +#include <boost/log/trivial.hpp> + #include <algorithm> #include <cassert> #include <list> @@ -462,7 +465,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi if (angle >= PI) angle = 2 * PI - angle; // smaller angle //compute the diff from 90° angle = abs(angle - PI / 2); - if (point_near.coincides_with(point_nearest) && std::max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) { + if (point_near.coincides_with_epsilon(point_nearest) && std::max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) { //not only nearest Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1]; Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1]; @@ -499,7 +502,7 @@ MedialAxis::fusion_curve(ThickPolylines &pp) //check my length is small coord_t length = (coord_t)polyline.length(); - if (length > max_width) continue; + if (length > this->max_width) continue; size_t closest_point_idx = this->expolygon.contour.closest_point_index(polyline.points.back()); @@ -529,13 +532,13 @@ MedialAxis::fusion_curve(ThickPolylines &pp) for (size_t j = 0; j < pp.size(); ++j) { if (j == i) continue; ThickPolyline& other = pp[j]; - if (polyline.first_point().coincides_with(other.last_point())) { + if (polyline.first_point().coincides_with_epsilon(other.last_point())) { other.reverse(); crosspoint.push_back(j); double dot_temp = dot(Line(polyline.points[0], polyline.points[1]), (Line(other.points[0], other.points[1]))); min_dot = std::min(min_dot, abs(dot_temp)); sum_dot += dot_temp; - } else if (polyline.first_point().coincides_with(other.first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) { crosspoint.push_back(j); double dot_temp = dot(Line(polyline.points[0], polyline.points[1]), (Line(other.points[0], other.points[1]))); min_dot = std::min(min_dot, abs(dot_temp)); @@ -604,7 +607,7 @@ MedialAxis::remove_bits(ThickPolylines &pp) //check my length is small coordf_t length = polyline.length(); - if (length > coordf_t(max_width) * 1.5) { + if (length > coordf_t(this->max_width) * 1.5) { continue; } @@ -613,10 +616,10 @@ MedialAxis::remove_bits(ThickPolylines &pp) for (size_t j = 0; j < pp.size(); ++j) { if (j == i) continue; ThickPolyline& other = pp[j]; - if (polyline.first_point().coincides_with(other.last_point())) { + if (polyline.first_point().coincides_with_epsilon(other.last_point())) { other.reverse(); crosspoint.push_back(j); - } else if (polyline.first_point().coincides_with(other.first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) { crosspoint.push_back(j); } } @@ -631,11 +634,11 @@ MedialAxis::remove_bits(ThickPolylines &pp) if (nb_better_than_me < 2) continue; //check if the length of the polyline is small vs width of the other lines - coord_t max_width = 0; + coord_t local_max_width = 0; for (int i = 0; i < crosspoint.size(); i++) { - max_width = std::max(max_width, pp[crosspoint[i]].width[0]); + local_max_width = std::max(local_max_width, pp[crosspoint[i]].width[0]); } - if (length > coordf_t(max_width + min_width)) + if (length > coordf_t(local_max_width + min_width)) continue; //delete the now unused polyline @@ -665,17 +668,17 @@ MedialAxis::fusion_corners(ThickPolylines &pp) //check my length is small coord_t length = (coord_t)polyline.length(); - if (length > max_width) continue; + if (length > this->max_width) continue; // look if other end is a cross point with multiple other branch std::vector<size_t> crosspoint; for (size_t j = 0; j < pp.size(); ++j) { if (j == i) continue; ThickPolyline& other = pp[j]; - if (polyline.first_point().coincides_with(other.last_point())) { + if (polyline.first_point().coincides_with_epsilon(other.last_point())) { other.reverse(); crosspoint.push_back(j); - } else if (polyline.first_point().coincides_with(other.first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) { crosspoint.push_back(j); } } @@ -764,7 +767,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con // prevent the line from touching on the other side, otherwise intersection() might return that solution if (polyline.points.size() == 2 && this->expolygon.contains(line.midpoint())) line.a = line.midpoint(); - line.extend_end((double)max_width); + line.extend_end((double)this->max_width); Point new_back; if (this->expolygon.contour.has_boundary_point(polyline.points.back())) { new_back = polyline.points.back(); @@ -842,14 +845,14 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con }*/ // find anchor Point best_anchor; - coordf_t shortest_dist = (coordf_t)max_width; + coordf_t shortest_dist = (coordf_t)this->max_width; for (const ExPolygon& a : anchors) { Point p_maybe_inside = a.contour.centroid(); coordf_t test_dist = new_bound.distance_to(p_maybe_inside) + new_back.distance_to(p_maybe_inside); //if (test_dist < max_width / 2 && (test_dist < shortest_dist || shortest_dist < 0)) { double angle_test = new_back.ccw_angle(p_maybe_inside, line.a); if (angle_test > PI) angle_test = 2 * PI - angle_test; - if (test_dist < max_width && test_dist<shortest_dist && abs(angle_test) > PI / 2) { + if (test_dist < (coordf_t)this->max_width && test_dist<shortest_dist && abs(angle_test) > PI / 2) { shortest_dist = test_dist; best_anchor = p_maybe_inside; } @@ -859,7 +862,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con p_obj.x() /= 2; p_obj.y() /= 2; Line l2 = Line(new_back, p_obj); - l2.extend_end((double)max_width); + l2.extend_end((coordf_t)this->max_width); (void)bounds->contour.first_intersection(l2, &new_bound); } if (new_bound.coincides_with_epsilon(new_back)) @@ -909,13 +912,13 @@ MedialAxis::main_fusion(ThickPolylines& pp) // find another polyline starting here for (size_t j = i + 1; j < pp.size(); ++j) { ThickPolyline& other = pp[j]; - if (polyline.last_point().coincides_with(other.last_point())) { + if (polyline.last_point().coincides_with_epsilon(other.last_point())) { polyline.reverse(); other.reverse(); - } else if (polyline.first_point().coincides_with(other.last_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.last_point())) { other.reverse(); - } else if (polyline.first_point().coincides_with(other.first_point())) { - } else if (polyline.last_point().coincides_with(other.first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) { + } else if (polyline.last_point().coincides_with_epsilon(other.first_point())) { polyline.reverse(); } else { continue; @@ -936,10 +939,10 @@ MedialAxis::main_fusion(ThickPolylines& pp) if ( ((polyline.points.back().distance_to(other.points.back()) + (polyline.width.back() + other.width.back()) / 4) - > max_width*1.05)) + > this->max_width *1.05)) continue; // test if the lines are not too different in length. - if (abs(polyline.length() - other.length()) > max_width) continue; + if (abs(polyline.length() - other.length()) > (coordf_t)this->max_width) continue; //test if we don't merge with something too different and without any relevance. @@ -955,7 +958,7 @@ MedialAxis::main_fusion(ThickPolylines& pp) // << (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > max_width / 2) // << (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > max_width) // << "\n"; - if (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > max_width / 2) continue; + if (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > (coordf_t)(this->max_width / 2)) continue; //compute angle to see if it's better than previous ones (straighter = better). @@ -975,7 +978,7 @@ MedialAxis::main_fusion(ThickPolylines& pp) //std::cout << "try to find main : " << k << " ? " << i << " " << j << " "; if (k == i || k == j) continue; ThickPolyline& main = pp[k]; - if (polyline.first_point().coincides_with(main.last_point())) { + if (polyline.first_point().coincides_with_epsilon(main.last_point())) { main.reverse(); if (!main.endpoints.second) find_main_branch = true; @@ -983,7 +986,7 @@ MedialAxis::main_fusion(ThickPolylines& pp) biggest_main_branch_id = k; biggest_main_branch_length = (coord_t)main.length(); } - } else if (polyline.first_point().coincides_with(main.first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(main.first_point())) { if (!main.endpoints.second) find_main_branch = true; else if (biggest_main_branch_length < main.length()) { @@ -1111,8 +1114,8 @@ MedialAxis::main_fusion(ThickPolylines& pp) //std::cout << "width:" << polyline.width[idx_point] << " = " << value_from_current_width << " + " << value_from_dist // << " (<" << max_width << " && " << (bounds.contour.closest_point(polyline.points[idx_point])->distance_to(polyline.points[idx_point]) * 2.1)<<")\n"; //failsafes - if (polyline.width[idx_point] > max_width) - polyline.width[idx_point] = max_width; + if (polyline.width[idx_point] > this->max_width) + polyline.width[idx_point] = this->max_width; //failsafe: try to not go out of the radius of the section, take the width of the merging point for that. (and with some offset) coord_t main_branch_width = pp[biggest_main_branch_id].width.front(); coordf_t main_branch_dist = pp[biggest_main_branch_id].points.front().distance_to(polyline.points[idx_point]); @@ -1249,7 +1252,7 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) changes = true; } //remove points and bits that comes from a "main line" - if (polyline.points.size() < 2 || (changes && polyline.length() < max_width && polyline.points.size() ==2)) { + if (polyline.points.size() < 2 || (changes && polyline.length() < this->max_width && polyline.points.size() ==2)) { //remove self if too small pp.erase(pp.begin() + i); --i; @@ -1288,14 +1291,14 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) if (other.endpoints.first && other.endpoints.second) continue; bool me_reverse = false; bool other_reverse = false; - if (polyline.last_point().coincides_with(other.last_point())) { + if (polyline.last_point().coincides_with_epsilon(other.last_point())) { other_reverse = true; - } else if (polyline.first_point().coincides_with(other.last_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.last_point())) { me_reverse = true; other_reverse = true; - } else if (polyline.first_point().coincides_with(other.first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) { me_reverse = true; - } else if (!polyline.last_point().coincides_with(other.first_point())) { + } else if (!polyline.last_point().coincides_with_epsilon(other.first_point())) { continue; } @@ -1313,12 +1316,12 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) } } if (best_candidate != nullptr && best_candidate->points.size() > 1) { - if (polyline.last_point().coincides_with(best_candidate->last_point())) { + if (polyline.last_point().coincides_with_epsilon(best_candidate->last_point())) { best_candidate->reverse(); - } else if (polyline.first_point().coincides_with(best_candidate->last_point())) { + } else if (polyline.first_point().coincides_with_epsilon(best_candidate->last_point())) { polyline.reverse(); best_candidate->reverse(); - } else if (polyline.first_point().coincides_with(best_candidate->first_point())) { + } else if (polyline.first_point().coincides_with_epsilon(best_candidate->first_point())) { polyline.reverse(); } //intersections may create over-extrusion because the included circle can be a bit larger. We have to make it short again if needed. @@ -1327,8 +1330,12 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) && polyline.width.back() > best_candidate->width[1]) { polyline.width.back() = std::min(polyline.width[polyline.width.size() - 2], best_candidate->width[1]); } - polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end()); - polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end()); + //be far enough + int far_idx = 1; + while (far_idx < best_candidate->points.size() && polyline.last_point().coincides_with_epsilon(best_candidate->points[far_idx])) + far_idx++; + polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + far_idx, best_candidate->points.end()); + polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + far_idx, best_candidate->width.end()); polyline.endpoints.second = best_candidate->endpoints.second; assert(polyline.width.size() == polyline.points.size()); if (best_idx < i) i--; @@ -1412,18 +1419,18 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz // if (endpoint_not_used[idx_endpoint]) { // int nb_endpoints; // Point pt = idx_endpoint % 2 == 0 ? polyline.first_point() : polyline.last_point(); - // if (idx_endpoint % 2 == 0 && pt.coincides_with(polyline.last_point())) { + // if (idx_endpoint % 2 == 0 && pt.coincides_with_epsilon(polyline.last_point())) { // nb_endpoints++; // endpoint_not_used[(idx_endpoint / 2) + 1] = false; // } // //good, now find other points // for (size_t idx_other_pp = (idx_endpoint / 2) + 1; idx_other_pp < pp.size(); idx_other_pp++) { // ThickPolyline& other = pp[idx_other_pp]; - // if (pt.coincides_with(other.first_point())) { + // if (pt.coincides_with_epsilon(other.first_point())) { // nb_endpoints++; // endpoint_not_used[idx_other_pp * 2] = false; // } - // if (pt.coincides_with(other.last_point())) { + // if (pt.coincides_with_epsilon(other.last_point())) { // nb_endpoints++; // endpoint_not_used[idx_other_pp * 2 + 1] = false; // } @@ -1435,7 +1442,7 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz // std::cout << "reduce " << reduction << " points!\n"; // if (idx_endpoint % 2 == 0 ) { // polyline.width.front() *= reduction; - // if(pt.coincides_with(polyline.last_point())) + // if(pt.coincides_with_epsilon(polyline.last_point())) // polyline.width.back() *= reduction; // } else { // polyline.width.back() *= reduction; @@ -1443,10 +1450,10 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz // //good, now find other points // for (size_t idx_other_pp = (idx_endpoint / 2) + 1; idx_other_pp < pp.size(); idx_other_pp++) { // ThickPolyline& other = pp[idx_other_pp]; - // if (pt.coincides_with(other.first_point())) { + // if (pt.coincides_with_epsilon(other.first_point())) { // other.width.front() *= reduction; // } - // if (pt.coincides_with(other.last_point())) { + // if (pt.coincides_with_epsilon(other.last_point())) { // other.width.back() *= reduction; // } // } @@ -1468,17 +1475,15 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz // know how long will the endpoints be extended since it depends on polygon thickness // which is variable - extension will be <= max_width/2 on each side) if ((polyline.endpoints.first || polyline.endpoints.second)) { - coordf_t max_width = max_width / 2; + coordf_t local_max_width = this->max_width / 2; for (coordf_t w : polyline.width) - max_width = std::max(max_width, w); - if(polyline.length() < max_width) { + local_max_width = std::max(local_max_width, w); + if(polyline.length() < local_max_width) { if (shortest_size > polyline.length()) { shortest_size = polyline.length(); shortest_idx = i; } - } - } } if (shortest_idx < pp.size()) { @@ -1490,14 +1495,14 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz } void -MedialAxis::check_width(ThickPolylines& pp, coord_t max_width, std::string msg) +MedialAxis::check_width(ThickPolylines& pp, coord_t local_max_width, std::string msg) { //remove empty polyline int nb = 0; for (size_t i = 0; i < pp.size(); ++i) { for (size_t j = 0; j < pp[i].width.size(); ++j) { - if (pp[i].width[j] > coord_t(max_width * 1.01)) { - BOOST_LOG_TRIVIAL(error) << "Error " << msg << " width " << unscaled(pp[i].width[j]) << "(" << i << ":" << j << ") > " << unscaled(max_width) << "\n"; + if (pp[i].width[j] > coord_t(local_max_width * 1.01)) { + BOOST_LOG_TRIVIAL(error) << "Error " << msg << " width " << unscaled(pp[i].width[j]) << "(" << i << ":" << j << ") > " << unscaled(local_max_width) << "\n"; nb++; } } @@ -1696,7 +1701,7 @@ MedialAxis::build(ThickPolylines &polylines_out) { //static int id = 0; //id++; - //std::cout << this->id << "\n"; + //std::cout << id << "\n"; //{ // std::stringstream stri; // stri << "medial_axis_0_enter_" << id << ".svg"; @@ -1709,8 +1714,8 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_0.5_simplified_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(bounds); - // svg.draw(this->expolygon); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); // svg.Close(); //} //safety check @@ -1777,9 +1782,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_0.9_voronoi_" << id << ".svg"; // SVG svg(stri.str()); - // //svg.draw(bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1798,15 +1803,33 @@ MedialAxis::build(ThickPolylines &polylines_out) // pp.erase(pp.begin() + tp_idx); // --tp_idx; //} + //voronoi problem: can put two consecutive points at the same position. Delete one. + for (size_t i = 1; i < tp.points.size()-1; i++) { + if (tp.points[i-1].distance_to_square(tp.points[i]) < SCALED_EPSILON) { + tp.points.erase(tp.points.begin() + i); + tp.width.erase(tp.width.begin() + i); + i--; + } + } + //delete the inner one + if (tp.points.size()>2 && tp.points[tp.points.size() - 2].distance_to_square(tp.points.back()) < SCALED_EPSILON) { + tp.points.erase(tp.points.end() - 2); + tp.width.erase(tp.width.end() - 2); + } + //delete null-length polylines + if (tp.length() < SCALED_EPSILON && tp.first_point().coincides_with_epsilon(tp.last_point())) { + pp.erase(pp.begin() + tp_idx); + --tp_idx; + } } //std::cout << "polyline_from_voronoi\n"; //{ // std::stringstream stri; // stri << "medial_axis_1_voronoi_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1819,9 +1842,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_1_voronoi_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1845,9 +1868,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_2_curve_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1862,9 +1885,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_3_fusion_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1874,6 +1897,15 @@ MedialAxis::build(ThickPolylines &polylines_out) // Loop through all returned polylines in order to extend their endpoints to the // expolygon boundaries (if done here, it may be cut later if not thick enough) if (stop_at_min_width) { + //{ + // std::stringstream stri; + // stri << "medial_axis_3_3_extends_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); + // svg.Close(); + //} extends_line_both_side(pp); } @@ -1885,14 +1917,24 @@ MedialAxis::build(ThickPolylines &polylines_out) std::cout << "\n"; }*/ //reduce extrusion when it's too thin to be printable + //{ + // std::stringstream stri; + // stri << "medial_axis_3_6_remove_thin_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); + // svg.Close(); + //} + remove_too_thin_extrusion(pp); //{ // std::stringstream stri; // stri << "medial_axis_4_thinok_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1901,9 +1943,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_5.0_thuinner_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1916,9 +1958,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_5_expand_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} //TODO: reduce the flow at the intersection ( + ) points on crossing? @@ -1927,9 +1969,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_6_concat_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1938,9 +1980,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_8_tooshort_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -1949,9 +1991,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_9.1_end_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} if (nozzle_diameter != min_width) { @@ -1964,9 +2006,9 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::stringstream stri; // stri << "medial_axis_9.9_endnwithtaper_" << id << ".svg"; // SVG svg(stri.str()); - // svg.draw(*bounds); - // svg.draw(this->expolygon); - // svg.draw(pp); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); // svg.Close(); //} @@ -2058,8 +2100,11 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl continue; } } else if (i > 0 && resolution_internal > line_len + prev_line_len) { - ThickLine& prev_line = lines[i - 1]; //merge lines? + //if it's a loop, merge only if the distance is high enough + if (p.first_point() == p.last_point() && p.length() < (line_len + prev_line_len) * 6) + continue; + ThickLine& prev_line = lines[i - 1]; coordf_t width = prev_line_len * (prev_line.a_width + prev_line.b_width) / 2; width += line_len * (line.a_width + line.b_width) / 2; prev_line.b = line.b; @@ -2129,13 +2174,14 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl path.height = flow.height; } } + assert(path.polyline.points.size() > 2 || path.first_point() != path.last_point()); } if (path.polyline.is_valid()) paths.emplace_back(std::move(path)); // Append paths to collection. if (!paths.empty()) { - if (paths.front().first_point().coincides_with(paths.back().last_point())) { + if (paths.front().first_point().coincides_with_epsilon(paths.back().last_point())) { coll.append(ExtrusionLoop(paths)); } else { if (role == erThinWall){ diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 6ba0c0815..aba01cfd1 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -462,7 +462,7 @@ void PerimeterGenerator::process() // In case no perimeters are to be generated, loop_number will equal to -1. std::vector<PerimeterGeneratorLoops> contours(loop_number + 1); // depth => loops std::vector<PerimeterGeneratorLoops> holes(loop_number + 1); // depth => loops - ThickPolylines thin_walls; + ThickPolylines thin_walls_thickpolys; ExPolygons no_last_gapfill; // we loop one time more than needed in order to find gaps after the last perimeter was applied for (int i = 0;; ++i) { // outer loop is 0 @@ -492,15 +492,15 @@ void PerimeterGenerator::process() next_onion = offset_ex( last, -(float)(ext_perimeter_width / 2), - (round_peri ? ClipperLib::JoinType::jtRound : ClipperLib::JoinType::jtMiter), - (round_peri ? min_round_spacing : 3)); + ClipperLib::JoinType::jtMiter, + 3); else next_onion = offset2_ex( last, -(float)(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1), +(float)(ext_min_spacing / 2 - 1), - (round_peri ? ClipperLib::JoinType::jtRound : ClipperLib::JoinType::jtMiter), - (round_peri ? min_round_spacing : 3)); + ClipperLib::JoinType::jtMiter, + 3); // look for thin walls if (this->config->thin_walls) { @@ -562,27 +562,27 @@ void PerimeterGenerator::process() ma.use_bounds(bound) .use_min_real_width(scale_t(this->ext_perimeter_flow.nozzle_diameter)) .use_tapers(thin_walls_overlap) - .build(thin_walls); + .build(thin_walls_thickpolys); } break; } } } // use perimeters to extrude area that can't be printed by thin walls - // it's a bit like re-add thin area in to perimeter area. + // it's a bit like re-add thin area into perimeter area. // it can over-extrude a bit, but it's for a better good. { if (thin_perimeter) next_onion = union_ex(next_onion, offset_ex(diff_ex(last, thins, true), -(float)(ext_perimeter_width / 2), - (round_peri ? ClipperLib::JoinType::jtRound : ClipperLib::JoinType::jtMiter), - (round_peri ? min_round_spacing : 3))); + ClipperLib::JoinType::jtMiter, + 3)); else next_onion = union_ex(next_onion, offset2_ex(diff_ex(last, thins, true), -(float)((ext_perimeter_width / 2) + (ext_min_spacing / 4)), (float)(ext_min_spacing / 4), - (round_peri ? ClipperLib::JoinType::jtRound : ClipperLib::JoinType::jtMiter), - (round_peri ? min_round_spacing : 3))); + ClipperLib::JoinType::jtMiter, + 3)); next_onion = intersection_ex(next_onion, last); } @@ -937,20 +937,23 @@ void PerimeterGenerator::process() } // append thin walls - if (!thin_walls.empty()) { - ExtrusionEntityCollection tw = thin_variable_width - (thin_walls, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_width/4, scale_t(this->print_config->resolution))); - - entities.append(tw.entities); - thin_walls.clear(); + if (!thin_walls_thickpolys.empty()) { + if (this->object_config->thin_walls_merge) { + _merge_thin_walls(entities, thin_walls_thickpolys); + } else { + ExtrusionEntityCollection tw = thin_variable_width + (thin_walls_thickpolys, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_width / 4, scale_t(this->print_config->resolution))); + entities.append(tw.entities); + } + thin_walls_thickpolys.clear(); } } else { if (this->object_config->thin_walls_merge) { ThickPolylines no_thin_walls; entities = this->_traverse_loops(contours.front(), no_thin_walls); - _merge_thin_walls(entities, thin_walls); + _merge_thin_walls(entities, thin_walls_thickpolys); } else { - entities = this->_traverse_loops(contours.front(), thin_walls); + entities = this->_traverse_loops(contours.front(), thin_walls_thickpolys); } } @@ -1697,11 +1700,12 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions } else { //first add the return path ExtrusionEntityCollection tws_second = tws; - tws_second.reverse(); change_flow.percent_extrusion = 0.1f; change_flow.use(tws_second); + //force reverse for (ExtrusionPath &path : change_flow.paths) path.reverse(); + std::reverse(change_flow.paths.begin(), change_flow.paths.end()); //std::reverse(change_flow.paths.begin(), change_flow.paths.end()); searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, change_flow.paths.begin(), change_flow.paths.end()); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index c077d6cd7..26f77b1ca 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,11 +1,13 @@ #include "PlaceholderParser.hpp" #include "Exception.hpp" #include "Flow.hpp" + #include <cstring> #include <ctime> #include <iomanip> #include <sstream> #include <map> + #ifdef _MSC_VER #include <stdlib.h> // provides **_environ #else @@ -25,6 +27,7 @@ #endif #include <boost/algorithm/string.hpp> +#include <boost/log/trivial.hpp> #include <boost/nowide/convert.hpp> // Spirit v2.5 allows you to suppress automatic generation @@ -649,6 +652,8 @@ namespace client bool just_boolean_expression = false; std::string error_message; + static std::map<t_config_option_key, std::unique_ptr<ConfigOption>> checked_vars; + // Table to translate symbol tag to a human readable error message. static std::map<std::string, std::string> tag_to_error_message; @@ -663,6 +668,11 @@ namespace client opt = config->option(opt_key); if (opt == nullptr && external_config != nullptr) opt = external_config->option(opt_key); + if (opt == nullptr) { + auto it = MyContext::checked_vars.find(opt_key); + if (it != MyContext::checked_vars.end()) + opt = it->second.get(); + } return opt; } @@ -822,12 +832,52 @@ namespace client boost::iterator_range<Iterator> &opt_key, OptWithPos<Iterator> &output) { - const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); + std::string str_key = std::string(opt_key.begin(), opt_key.end()); + const ConfigOption *opt = ctx->resolve_symbol(str_key); if (opt == nullptr) ctx->throw_exception("Not a variable name", opt_key); output.opt = opt; output.it_range = opt_key; } + + // function to check if a var exist & add a dummy var if not + template <typename Iterator> + static void check_variable( + const MyContext* ctx, + boost::iterator_range<Iterator>& opt_key, + Iterator& end_pos, + expr<Iterator>& out, + ConfigOption* default_val = nullptr) + { + t_config_option_key key = std::string(opt_key.begin(), opt_key.end()); + const ConfigOption* opt = nullptr; + if (ctx->config_override != nullptr) + opt = ctx->config_override->option(key); + if (opt == nullptr) + opt = ctx->config->option(key); + if (opt == nullptr && ctx->external_config != nullptr) + opt = ctx->external_config->option(key); + if (opt == nullptr) { + std::unique_ptr<ConfigOption> ppt; + if(default_val == nullptr) + ppt = std::unique_ptr<ConfigOption>(new ConfigOptionBool(false)); + else + ppt = std::unique_ptr<ConfigOption>(default_val); + // set flag to say "it's a var that isn't here, please ignore it" + ppt->flags |= ConfigOption::FCO_PLACEHOLDER_TEMP; + if (MyContext::checked_vars.find(key) != MyContext::checked_vars.end()) { + if (default_val != nullptr) { + // erase previous value + MyContext::checked_vars[key] = std::move(ppt); + } + } else { + // put the var + MyContext::checked_vars.emplace(std::move(key), std::move(ppt)); + } + } + //return + out = expr<Iterator>(opt != nullptr, out.it_range.begin(), end_pos); + } template <typename Iterator> static void scalar_variable_reference( @@ -941,8 +991,11 @@ namespace client Iterator it_end, expr<Iterator> &output) { - if (opt.opt->is_scalar()) + if (opt.opt->is_scalar()) { + if (0 != (opt.opt->flags & ConfigOption::FCO_PLACEHOLDER_TEMP)) // fake var, from checked_vars + return scalar_variable_reference(ctx, opt, output); ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); + } const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt); if (vec->empty()) ctx->throw_exception("Indexing an empty vector variable", opt.it_range); @@ -1061,6 +1114,7 @@ namespace client { "variable_reference", "Expecting a variable reference."}, { "regular_expression", "Expecting a regular expression."} }; + std::map<t_config_option_key, std::unique_ptr<ConfigOption>> MyContext::checked_vars = {}; // For debugging the boost::spirit parsers. Print out the string enclosed in it_range. template<typename Iterator> @@ -1321,6 +1375,15 @@ namespace client { out = value.unary_not(out.it_range.begin()); } static void to_int(expr<Iterator> &value, expr<Iterator> &out) { out = value.unary_integer(out.it_range.begin()); } + //function for default keyword + static void default_bool_(bool &value, const MyContext* ctx, boost::iterator_range<Iterator>& opt_key, Iterator& end_pos, expr<Iterator>& out) + { MyContext::check_variable<Iterator>(ctx, opt_key, end_pos, out, new ConfigOptionBool(value)); } + static void default_int_(int &value, const MyContext* ctx, boost::iterator_range<Iterator>& opt_key, Iterator& end_pos, expr<Iterator>& out) + { MyContext::check_variable<Iterator>(ctx, opt_key, end_pos, out, new ConfigOptionInt(value)); } + static void default_double_(double &value, const MyContext* ctx, boost::iterator_range<Iterator>& opt_key, Iterator& end_pos, expr<Iterator>& out) + { MyContext::check_variable<Iterator>(ctx, opt_key, end_pos, out, new ConfigOptionFloat(value)); } + static void default_string_(boost::iterator_range<Iterator>& it_range, const MyContext* ctx, boost::iterator_range<Iterator>& opt_key, Iterator& end_pos, expr<Iterator>& out) + { MyContext::check_variable<Iterator>(ctx, opt_key, end_pos, out, new ConfigOptionString(std::string(it_range.begin() + 1, it_range.end() - 1))); } }; unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( scalar_variable_reference(_r1) [ _val = _1 ] @@ -1335,6 +1398,15 @@ namespace client | (kw["random"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&MyContext::random<Iterator>, _r1, _val, _2) ] | (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] + | (kw["exists"] > '(' > identifier > ')' > iter_pos) [ px::bind(&MyContext::check_variable<Iterator>, _r1, _1, _2, _val, nullptr) ] + | (kw["default"] > '(' > identifier > ',' > strict_double > ')' > iter_pos) + [px::bind(&FactorActions::default_double_, _2, _r1, _1, _3, _val)] + | (kw["default"] > '(' > identifier > ',' > int_ > ')' > iter_pos) + [px::bind(&FactorActions::default_int_, _2, _r1, _1, _3, _val)] + | (kw["default"] > '(' > identifier > ',' > kw[bool_] > ')' > iter_pos) + [ px::bind(&FactorActions::default_bool_, _2, _r1, _1, _3, _val) ] + | (kw["default"] > '(' > identifier > ',' > raw[lexeme['"' > *((utf8char - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] > ')' > iter_pos) + [px::bind(&FactorActions::default_string_, _2, _r1, _1, _3, _val)] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1373,7 +1445,9 @@ namespace client ("random") ("not") ("or") - ("true"); + ("true") + ("exists") + ("default"); if (0) { debug(start); @@ -1499,11 +1573,11 @@ void PlaceholderParser::append_custom_variables(std::map<std::string, std::vecto bool is_array = nb_extruders > 0; if (!is_array) nb_extruders = 1; - std::regex is_a_name("[a-zA-Z_]+"); + SLIC3R_REGEX_NAMESPACE::regex is_a_name("[a-zA-Z_]+"); for (const auto& entry : name2var_array) { if (entry.first.empty()) continue; - if (!std::regex_match(entry.first, is_a_name)) + if (!SLIC3R_REGEX_NAMESPACE::regex_match(entry.first, is_a_name)) continue; const std::vector<std::string>& values = entry.second; //check if all values are empty @@ -1552,7 +1626,7 @@ void PlaceholderParser::append_custom_variables(std::map<std::string, std::vecto //check if all values are numeric bool is_not_numeric = !is_not_string || !is_not_bool; std::vector<double> double_values; - //std::regex("\\s*[+-]?([0-9]+\\.[0-9]*([Ee][+-]?[0-9]+)?|\\.[0-9]+([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)"); + //SLIC3R_REGEX_NAMESPACE::regex("\\s*[+-]?([0-9]+\\.[0-9]*([Ee][+-]?[0-9]+)?|\\.[0-9]+([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)"); if (!is_not_numeric) { for (int extruder_id = 0; extruder_id < nb_extruders; ++extruder_id) { if (!values[extruder_id].empty()) { diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index aaf1e7e48..b6a511b88 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -155,7 +155,7 @@ public: double distance_to(const Line &line) const; bool coincides_with(const Point &point) const { return this->x() == point.x() && this->y() == point.y(); } bool coincides_with_epsilon(const Point &point) const { - return std::abs(this->x() - point.x()) < SCALED_EPSILON && std::abs(this->y() - point.y()) < SCALED_EPSILON; + return std::abs(this->x() - point.x()) < SCALED_EPSILON/2 && std::abs(this->y() - point.y()) < SCALED_EPSILON/2; } }; diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 8f294f162..c7cf15c75 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -273,7 +273,7 @@ void concatThickPolylines(ThickPolylines& pp) { //concat polyline if only 2 polyline at a point for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline *polyline = &pp[i]; - if (polyline->first_point().coincides_with(polyline->last_point())) { + if (polyline->first_point().coincides_with_epsilon(polyline->last_point())) { polyline->endpoints.first = false; polyline->endpoints.second = false; continue; @@ -287,25 +287,25 @@ void concatThickPolylines(ThickPolylines& pp) { for (size_t j = 0; j < pp.size(); ++j) { if (j == i) continue; ThickPolyline *other = &pp[j]; - if (polyline->last_point().coincides_with(other->last_point())) { + if (polyline->last_point().coincides_with_epsilon(other->last_point())) { id_candidate_last_point = j; nbCandidate_last_point++; } - if (polyline->last_point().coincides_with(other->first_point())) { + if (polyline->last_point().coincides_with_epsilon(other->first_point())) { id_candidate_last_point = j; nbCandidate_last_point++; } - if (polyline->first_point().coincides_with(other->last_point())) { + if (polyline->first_point().coincides_with_epsilon(other->last_point())) { id_candidate_first_point = j; nbCandidate_first_point++; } - if (polyline->first_point().coincides_with(other->first_point())) { + if (polyline->first_point().coincides_with_epsilon(other->first_point())) { id_candidate_first_point = j; nbCandidate_first_point++; } } if (id_candidate_last_point == id_candidate_first_point && nbCandidate_first_point == 1 && nbCandidate_last_point == 1) { - if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); + if (polyline->first_point().coincides_with_epsilon(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); // it's a trap! it's a loop! polyline->points.insert(polyline->points.end(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end()); polyline->width.insert(polyline->width.end(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end()); @@ -316,7 +316,7 @@ void concatThickPolylines(ThickPolylines& pp) { } else { if (nbCandidate_first_point == 1) { - if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); + if (polyline->first_point().coincides_with_epsilon(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); //concat at front polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back()); polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1); @@ -336,7 +336,7 @@ void concatThickPolylines(ThickPolylines& pp) { polyline->endpoints.first = true; } if (nbCandidate_last_point == 1) { - if (polyline->last_point().coincides_with(pp[id_candidate_last_point].last_point())) pp[id_candidate_last_point].reverse(); + if (polyline->last_point().coincides_with_epsilon(pp[id_candidate_last_point].last_point())) pp[id_candidate_last_point].reverse(); //concat at back polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front()); polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end()); @@ -353,7 +353,7 @@ void concatThickPolylines(ThickPolylines& pp) { polyline->endpoints.second = true; } - if (polyline->last_point().coincides_with(polyline->first_point())) { + if (polyline->last_point().coincides_with_epsilon(polyline->first_point())) { //the concat has created a loop : update endpoints polyline->endpoints.first = false; polyline->endpoints.second = false; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 30ce2a8c0..eefb4f4bb 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -710,7 +710,7 @@ const std::vector<std::string>& Preset::filament_options() "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", "filament_retract_layer_change", "filament_retract_before_wipe", "filament_seam_gap", - "filament_wipe", "filament_wipe_extra_perimeter", "filament_wipe_speed", + "filament_wipe", "filament_wipe_only_crossing", "filament_wipe_extra_perimeter", "filament_wipe_speed", // Profile compatibility "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" //merill adds diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c5841ceea..2394adbb7 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -196,6 +196,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option "use_volumetric_e", "variable_layer_height", "wipe", + "wipe_only_crossing", "wipe_speed", "wipe_extra_perimeter" }; @@ -1644,7 +1645,7 @@ std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const //check not-first layer if (object->region_volumes[region_id].front().first.second > layer_height) { if (layer_height + EPSILON < min_layer_height) - return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be higher than %s")) % "min layer height").str() }; + return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("Layer height can't be higher than %s")) % "min layer height").str() }; for (auto tuple : std::vector<std::pair<double, const char*>>{ {nozzle_diameter, "nozzle diameter"}, {max_layer_height, "max layer height"}, diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index e4a0b45cc..5ca0198d5 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -4,6 +4,8 @@ #include <boost/filesystem.hpp> #include <boost/lexical_cast.hpp> +#include <regex> + #include "I18N.hpp" //! macro used to mark string used at localization, diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d50cbecf8..de22a6689 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -138,7 +138,7 @@ void PrintConfigDef::init_common_params() def = this->add("slice_closing_radius", coFloat); def->label = L("Slice gap closing radius"); def->category = OptionCategory::slicing; - def->tooltip = L("Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. " + def->tooltip = L("Fill cracks smaller than 2x gap closing radius during the triangle mesh slicing. " "The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low."); def->sidetext = L("mm"); def->min = 0; @@ -223,7 +223,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Allow empty layers"); def->full_label = L("Allow empty layers"); def->category = OptionCategory::slicing; - def->tooltip = L("Do not prevent the gcode builder to trigger an exception if a full layer is empty and so the print will have to start from thin air afterward."); + def->tooltip = L("Prevent the gcode builder from triggering an exception if a full layer is empty, and allow the print to start from thin air afterward."); def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); @@ -240,7 +240,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Don't avoid crossing on 1st layer"); def->full_label = L("Don't avoid crossing on 1st layer"); def->category = OptionCategory::perimeter; - def->tooltip = L("Do not use the 'Avoid crossing perimeters' on the first layer."); + def->tooltip = L("Disable 'Avoid crossing perimeters' for the first layer."); def->mode = comExpert; def->set_default_value(new ConfigOptionBool(true)); @@ -249,7 +249,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::perimeter; def->tooltip = L("The maximum detour length for avoid crossing perimeters. " "If the detour is longer than this value, avoid crossing perimeters is not applied for this travel path. " - "Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path."); + "Detour length can be specified either as an absolute value or as percentage (for example 50%) of a direct travel path."); def->sidetext = L("mm or % (zero to disable)"); def->min = 0; def->mode = comExpert; @@ -259,7 +259,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Other layers"); def->category = OptionCategory::filament; def->tooltip = L("Bed temperature for layers after the first one. " - "Set this to zero to disable bed temperature control commands in the output."); + "Set zero to disable bed temperature control commands in the output."); def->sidetext = L("°C"); def->full_label = L("Bed temperature"); def->sidetext = L("°C"); @@ -627,7 +627,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Allow only one skirt loop"); def->category = OptionCategory::output; def->tooltip = L("When using 'Complete individual objects', the default behavior is to draw the skirt around each object." - " if you prefer to have only one skirt for the whole plater, use this option."); + " if you prefer to have only one skirt for the whole platter, use this option."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); @@ -756,7 +756,7 @@ void PrintConfigDef::init_fff_params() def = this->add("duplicate_distance", coFloat); def->label = L("Default distance between objects"); def->category = OptionCategory::output; - def->tooltip = L("Default distance used for the auto-arrange feature of the plater.\nSet to 0 to use the last value instead."); + def->tooltip = L("Default distance used for the auto-arrange feature of the platter.\nSet to 0 to use the last value instead."); def->sidetext = L("mm"); def->aliases = { "multiply_distance" }; def->min = 0; @@ -1215,7 +1215,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Set this to the clearance radius around your extruder. " "If the extruder is not centered, choose the largest value for safety. " "This setting is used to check for collisions and to display the graphical preview " - "in the plater." + "in the platter." "\nSet zero to disable clearance checking."); def->sidetext = L("mm"); def->min = 0; @@ -1329,7 +1329,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, false)); def = this->add("fan_always_on", coBools); def->label = L("Keep fan always on"); @@ -1399,7 +1399,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::filament; def->tooltip = L("Maximum speed allowed for this filament. Limits the maximum " "speed of a print to the minimum of the print speed and the filament speed. " - "Set to zero for no limit."); + "Set zero for no limit."); def->sidetext = L("mm/s"); def->min = 0; def->mode = comAdvanced; @@ -1411,7 +1411,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::filament; def->tooltip = L("Maximum volumetric speed allowed for this filament. Limits the maximum volumetric " "speed of a print to the minimum of print and filament volumetric speed. " - "Set to zero for no limit."); + "Set zero for no limit."); def->sidetext = L("mm³/s"); def->min = 0; def->mode = comAdvanced; @@ -1964,7 +1964,7 @@ void PrintConfigDef::init_fff_params() def->label = L("First layer"); def->full_label = L("First layer bed temperature"); def->category = OptionCategory::filament; - def->tooltip = L("Heated build plate temperature for the first layer. Set this to zero to disable " + def->tooltip = L("Heated build plate temperature for the first layer. Set zero to disable " "bed temperature control commands in the output."); def->sidetext = L("°C"); def->max = 0; @@ -2064,7 +2064,7 @@ void PrintConfigDef::init_fff_params() def->full_label = L("First layer nozzle temperature"); def->category = OptionCategory::filament; def->tooltip = L("Extruder nozzle temperature for first layer. If you want to control temperature manually " - "during print, set this to zero to disable temperature control commands in the output file."); + "during print, set zero to disable temperature control commands in the output file."); def->sidetext = L("°C"); def->min = 0; def->max = max_temp; @@ -2917,7 +2917,10 @@ void PrintConfigDef::init_fff_params() def = this->add("max_speed_reduction", coPercents); def->label = L("Max speed reduction"); def->category = OptionCategory::speed; - def->tooltip = L("Set to 90% if you don't want the speed to be reduced by more than 90%."); + def->tooltip = L("This setting control by how much the speed can be reduced to increase the layer time." + " It's a maximum reduction, so a lower value makes the minimum speed higher." + " Set to 90% if you don't want the speed to go below 10% of the current speed." + "\nSet zero to disable"); def->sidetext = L("%"); def->min = 0; def->max = 100; @@ -3580,7 +3583,7 @@ void PrintConfigDef::init_fff_params() def->full_label = L("Enforce lift on first layer"); def->category = OptionCategory::extruders; def->tooltip = L("Select this option to enforce z-lift on the first layer." - "\nIf this is enabled and the lift value is 0 or deactivated, then every first move before each object will be lifted by the first layer height."); + "\nUseful to still use the lift on the first layer even if the 'Only lift Z below' (retract_lift_above) is higher than 0."); def->mode = comAdvanced; def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools{ false }); @@ -3726,7 +3729,7 @@ void PrintConfigDef::init_fff_params() def = this->add("skirt_distance", coFloat); def->label = L("Distance from object"); def->category = OptionCategory::skirtBrim; - def->tooltip = L("Distance between skirt and object(s). Set this to zero to attach the skirt " + def->tooltip = L("Distance between skirt and object(s). Set zero to attach the skirt " "to the object(s) and get a brim for better adhesion."); def->sidetext = L("mm"); def->min = 0; @@ -3763,14 +3766,14 @@ void PrintConfigDef::init_fff_params() def->max_literal = { 10, true }; def->precision = 6; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(130, true)); def = this->add("skirts", coInt); def->label = L("Loops (minimum)"); def->full_label = L("Skirt Loops"); def->category = OptionCategory::skirtBrim; def->tooltip = L("Number of loops for the skirt. If the Minimum Extrusion Length option is set, " - "the number of loops might be greater than the one configured here. Set this to zero " + "the number of loops might be greater than the one configured here. Set zero " "to disable skirt completely."); def->min = 0; def->mode = comSimple; @@ -4135,14 +4138,16 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("perimeters"); def->sidetext_width = 20; def->category = OptionCategory::perimeter; - def->tooltip = L("When you have a medium/hight number of top/bottom solid layers, and a low/medium of perimeters," - " then it have to put some solid infill inside the part to have enough solid layers." - "\nBy setting this to something higher than 0, you can remove this 'inside filling'." - " This number allow to keep some if there is a low number of perimeter over the void." - "\nIf this setting is equal or higher than the top/bottom solid layer count, it won't evict anything." - "\nIf this setting is set to 1, it will evict all solid fill are are only over perimeters." + def->tooltip = L("In sloping areas, when you have a number of top / bottom solid layers and few perimeters, " + " it may be necessary to put some solid infill above/below the perimeters to fulfill the top/bottom layers criteria." + "\nBy setting this to something higher than 0, you can control this behaviour, which might be desirable if " + "\nundesirable solid infill is being generated on slopes." + "\nThe number set here indicates the number of layers between the inside of the part and the air" + " at and beyond which solid infill should no longer be added above/below. If this setting is equal or higher than " + " the top/bottom solid layer count, it won't do anything. If this setting is set to 1, it will evict " + " all solid fill above/below perimeters. " "\nSet zero to disable." - "\n!! ensure_vertical_shell_thickness may be erased by this setting !! You may want to deactivate at least one of the two."); + "\n!! ensure_vertical_shell_thickness may be erased by this setting !!."); def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(2)); @@ -4426,7 +4431,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Support material will not be generated for overhangs whose slope angle " "(90° = vertical) is above the given threshold. In other words, this value " "represent the most horizontal slope (measured from the horizontal plane) " - "that you can print without support material. Set to zero for automatic detection " + "that you can print without support material. Set zero for automatic detection " "(recommended)."); def->sidetext = L("°"); def->min = 0; @@ -4446,7 +4451,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Other layers"); def->full_label = L("Temperature"); def->category = OptionCategory::filament; - def->tooltip = L("Extruder nozzle temperature for layers after the first one. Set this to zero to disable " + def->tooltip = L("Extruder nozzle temperature for layers after the first one. Set zero to disable " "temperature control commands in the output G-code."); def->sidetext = L("°C"); def->full_label = L("Nozzle temperature"); @@ -4773,6 +4778,14 @@ void PrintConfigDef::init_fff_params() def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools{ false }); + def = this->add("wipe_only_crossing", coBools); + def->label = L("Wipe only when crossing perimeters"); + def->category = OptionCategory::extruders; + def->tooltip = L("Don't wipe when you don't cross a perimeter."); + def->mode = comAdvanced; + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionBools{ true }); + def = this->add("wipe_speed", coFloats); def->label = L("Wipe speed"); def->category = OptionCategory::extruders; @@ -5021,7 +5034,7 @@ void PrintConfigDef::init_fff_params() def = this->add("z_step", coFloat); def->label = L("Z full step"); def->tooltip = L("Set this to the height moved when your Z motor (or equivalent) turns one step." - "If your motor needs 200 steps to move your head/plater by 1mm, this field should be 1/200 = 0.005." + "If your motor needs 200 steps to move your head/platter by 1mm, this field should be 1/200 = 0.005." "\nNote that the gcode will write the z values with 6 digits after the dot if z_step is set (it's 3 digits if it's disabled)." "\nSet zero to disable."); def->cli = "z-step=f"; @@ -5047,7 +5060,7 @@ void PrintConfigDef::init_fff_params() "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", "retract_restart_extra", "retract_before_travel", "wipe_extra_perimeter", "wipe_speed", // bools - "retract_layer_change", "wipe", + "retract_layer_change", "wipe", "wipe_only_crossing", // percents "retract_before_wipe", // floatsOrPercents @@ -5100,6 +5113,7 @@ void PrintConfigDef::init_extruder_option_keys() "tool_name", "wipe", "wipe_extra_perimeter", + "wipe_only_crossing", "wipe_speed", }; @@ -5117,6 +5131,7 @@ void PrintConfigDef::init_extruder_option_keys() "seam_gap", "wipe", "wipe_extra_perimeter", + "wipe_only_crossing", "wipe_speed", }; assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end())); @@ -5736,7 +5751,7 @@ void PrintConfigDef::init_sla_params() def = this->add("pad_wall_height", coFloat); def->label = L("Pad wall height"); - def->tooltip = L("Defines the pad cavity depth. Set to zero to disable the cavity. " + def->tooltip = L("Defines the pad cavity depth. Set zero to disable the cavity. " "Be careful when enabling this feature, as some resins may " "produce an extreme suction effect inside the cavity, " "which makes peeling the print off the vat foil difficult."); @@ -6328,6 +6343,7 @@ std::unordered_set<std::string> prusa_export_to_remove_keys = { "wipe_advanced_nozzle_melted_volume", "wipe_advanced", "wipe_extra_perimeter", +"wipe_only_crossing", "wipe_speed", "wipe_tower_brim", "xy_inner_size_compensation", @@ -6754,6 +6770,9 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if (spacing_value == 0) + width_option->value = 0; + else width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); width_option->percent = spacing_option->percent; something_changed = true; @@ -6764,6 +6783,9 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if (spacing_value == 0) + width_option->value = 0; + else width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); width_option->percent = spacing_option->percent; something_changed = true; @@ -6775,9 +6797,13 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option && perimeter_overlap_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if(spacing_value == 0) + width_option->value = 0; + else { flow.spacing_ratio = std::min(flow.spacing_ratio, float(perimeter_overlap_option->get_abs_value(1))); flow.width = spacing_option->get_abs_value(max_nozzle_diameter) + layer_height_option->value * (1. - 0.25 * PI) * flow.spacing_ratio; width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + } width_option->percent = spacing_option->percent; something_changed = true; } @@ -6789,9 +6815,13 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option && external_perimeter_overlap_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if (spacing_value == 0) + width_option->value = 0; + else { flow.spacing_ratio = std::min(flow.spacing_ratio * 0.5f, float(external_perimeter_overlap_option->get_abs_value(0.25) + perimeter_overlap_option->get_abs_value(0.25))); flow.width = spacing_option->get_abs_value(max_nozzle_diameter) + layer_height_option->value * (1. - 0.25 * PI) * flow.spacing_ratio; width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + } width_option->percent = spacing_option->percent; something_changed = true; } @@ -6801,6 +6831,9 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if (spacing_value == 0) + width_option->value = 0; + else width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); width_option->percent = spacing_option->percent; something_changed = true; @@ -6811,6 +6844,9 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if (spacing_value == 0) + width_option->value = 0; + else width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); width_option->percent = spacing_option->percent; something_changed = true; @@ -6821,6 +6857,9 @@ std::set<const DynamicPrintConfig*> DynamicPrintConfig::value_changed(const t_co if (width_option) { width_option->set_phony(true); spacing_option->set_phony(false); + if (spacing_value == 0) + width_option->value = 0; + else width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); width_option->percent = spacing_option->percent; something_changed = true; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index ce949a135..faa733bae 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1183,6 +1183,7 @@ public: ConfigOptionFloat wipe_advanced_multiplier; ConfigOptionFloats wipe_extra_perimeter; ConfigOptionEnum<WipeAlgo> wipe_advanced_algo; + ConfigOptionBools wipe_only_crossing; ConfigOptionFloats wipe_speed; ConfigOptionFloat z_step; ConfigOptionString color_change_gcode; @@ -1300,6 +1301,7 @@ protected: OPT_PTR(wipe_advanced_multiplier); OPT_PTR(wipe_advanced_algo); OPT_PTR(wipe_extra_perimeter); + OPT_PTR(wipe_only_crossing); OPT_PTR(wipe_speed); OPT_PTR(z_step); OPT_PTR(color_change_gcode); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8e8a725dc..9aff46af9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -14,12 +14,12 @@ #include "Fill/FillAdaptive.hpp" #include "Format/STL.hpp" +#include <atomic> #include <utility> #include <boost/log/trivial.hpp> #include <float.h> #include <tbb/parallel_for.h> -#include <tbb/atomic.h> #include <Shiny/Shiny.h> diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 3b4f34264..d1c816bcb 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -34,32 +34,27 @@ using coord_t = int64_t; using coordf_t = double; -//FIXME This epsilon value is used for many non-related purposes: -// For a threshold of a squared Euclidean distance, -// for a trheshold in a difference of radians, -// for a threshold of a cross product of two non-normalized vectors etc. -static constexpr double EPSILON = 1e-4; // Scaling factor for a conversion from coord_t to coordf_t: 10e-6 // This scaling generates a following fixed point representation with for a 32bit integer: // 0..4294mm with 1nm resolution // int32_t fits an interval of (-2147.48mm, +2147.48mm) // with int64_t we don't have to worry anymore about the size of the int. static constexpr double SCALING_FACTOR = 0.000001; -#ifdef __linux__ -static constexpr double UNSCALING_FACTOR = 1000000; -#else -static constexpr double UNSCALING_FACTOR = 1 / SCALING_FACTOR; -#endif +static constexpr double UNSCALING_FACTOR = 1000000; // 1 / SCALING_FACTOR; + +//FIXME This epsilon value is used for many non-related purposes: +// For a threshold of a squared Euclidean distance, +// for a trheshold in a difference of radians, +// for a threshold of a cross product of two non-normalized vectors etc. +static constexpr double EPSILON = 1e-4; +static constexpr coord_t SCALED_EPSILON = 100; // coord_t(EPSILON/ SCALING_FACTOR); // RESOLUTION, SCALED_RESOLUTION: Used as an error threshold for a Douglas-Peucker polyline simplification algorithm. //#define RESOLUTION 0.0125 //#define SCALED_RESOLUTION 12500 //#define SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR) static constexpr coordf_t RESOLUTION = 0.0125; -#ifdef __linux__ -static constexpr coord_t SCALED_RESOLUTION = 12500; -#else -static constexpr coord_t SCALED_RESOLUTION = coord_t(0.0125 * UNSCALING_FACTOR); -#endif +static constexpr coord_t SCALED_RESOLUTION = 12500; // coord_t(0.0125 * UNSCALING_FACTOR); + //for creating circles (for brim_ear) #define POLY_SIDES 24 #define PI 3.141592653589793238 @@ -72,7 +67,6 @@ static constexpr double INSET_OVERLAP_TOLERANCE = 0.4; //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } #define scale_(val) (coord_t)((val) / SCALING_FACTOR) -#define SCALED_EPSILON scale_(EPSILON) #define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/" diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 54ddeac97..b5e29302d 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -16,7 +16,11 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem/operations.hpp> +#include <boost/locale/generator.hpp> #include <boost/log/trivial.hpp> +#include <boost/nowide/fstream.hpp> +#include <boost/property_tree/ini_parser.hpp> +#include <boost/property_tree/ptree.hpp> static const float GROUND_Z = -0.02f; diff --git a/src/slic3r/GUI/CalibrationAbstractDialog.cpp b/src/slic3r/GUI/CalibrationAbstractDialog.cpp index 6fa3c1a6b..385d5e0cf 100644 --- a/src/slic3r/GUI/CalibrationAbstractDialog.cpp +++ b/src/slic3r/GUI/CalibrationAbstractDialog.cpp @@ -5,10 +5,14 @@ #include "GUI.hpp" #include "GUI_ObjectList.hpp" #include "Tab.hpp" + #include <wx/scrolwin.h> #include <wx/display.h> #include <wx/file.h> +#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp> + #if ENABLE_SCROLLABLE static wxSize get_screen_size(wxWindow* window) { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index dd90cea9f..2dabd7dc4 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -319,7 +319,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) { bool have_perimeters = config->opt_int("perimeters") > 0; for (auto el : { "ensure_vertical_shell_thickness", "external_perimeter_speed", "extra_perimeters", "extra_perimeters_overhangs", "extra_perimeters_odd_layers", - "external_perimeters_first", "external_perimeters_vase", "external_perimeter_extrusion_width", + "external_perimeters_first", "external_perimeters_vase", "external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing", "no_perimeter_unsupported_algo", "only_one_perimeter_top", "overhangs", "overhangs_reverse", "perimeter_loop", "perimeter_loop_seam","perimeter_speed", "seam_position", "small_perimeter_speed", "small_perimeter_min_length", " small_perimeter_max_length", "spiral_vase", @@ -375,12 +375,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool has_solid_infill = has_top_solid_infill || has_bottom_solid_infill || (have_infill && (config->opt_int("solid_infill_every_layers") > 0 || config->opt_float("solid_infill_below_area") > 0)); // solid_infill_extruder uses the same logic as in Print::extruders() for (auto el : { "top_fill_pattern", "bottom_fill_pattern", "solid_fill_pattern", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin", - "solid_infill_extruder", "solid_infill_extrusion_width", "solid_infill_speed" }) + "solid_infill_extruder", "solid_infill_extrusion_width", "solid_infill_extrusion_spacing", "solid_infill_speed" }) toggle_field(el, has_solid_infill); toggle_field("infill_first", (has_solid_infill || have_infill)); - for (auto el : { "fill_angle", "fill_angle_increment", "bridge_angle", "infill_extrusion_width", + for (auto el : { "fill_angle", "fill_angle_increment", "bridge_angle", "infill_extrusion_width", "infill_extrusion_spacing", "infill_speed" }) toggle_field(el, have_infill || has_solid_infill); @@ -396,7 +396,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) for (auto el : {"fill_smooth_width, fill_smooth_distribution" }) toggle_field(el, has_ironing_pattern); - for (auto el : { "ironing", "top_fill_pattern", "infill_connection_top", "top_infill_extrusion_width", "top_solid_infill_speed" }) + for (auto el : { "ironing", "top_fill_pattern", "infill_connection_top", "top_infill_extrusion_width", "top_infill_extrusion_spacing", "top_solid_infill_speed" }) toggle_field(el, has_top_solid_infill); for (auto el : { "bottom_fill_pattern", "infill_connection_bottom" }) @@ -445,13 +445,27 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field(el, have_support_material && have_support_interface); toggle_field("support_material_synchronize_layers", have_support_soluble); - toggle_field("perimeter_extrusion_width", have_perimeters || have_skirt || have_brim); + toggle_field("perimeter_extrusion_width", have_perimeters || have_brim); + toggle_field("perimeter_extrusion_spacing", have_perimeters || have_brim); + toggle_field("skirt_extrusion_width", have_skirt); toggle_field("support_material_extruder", have_support_material || have_skirt); toggle_field("support_material_speed", have_support_material || have_brim || have_skirt); + //for default_extrusion_width/spacing, you need to ahve at least an extrusion_width with 0 + bool have_default_width = config->option("first_layer_extrusion_width")->getFloat() == 0 || + (config->option("perimeter_extrusion_width")->getFloat() == 0 && (have_perimeters || have_brim)) || + (config->option("external_perimeter_extrusion_width")->getFloat() == 0 && have_perimeters) || + (config->option("infill_extrusion_width")->getFloat() == 0 && (have_infill || has_solid_infill)) || + (config->option("solid_infill_extrusion_width")->getFloat() == 0 && has_solid_infill) || + (config->option("top_infill_extrusion_width")->getFloat() == 0 && has_top_solid_infill) || + (config->option("support_material_extrusion_width")->getFloat() == 0 && have_support_material) || + (config->option("skirt_extrusion_width")->getFloat() == 0 && have_skirt); + toggle_field("extrusion_width", have_default_width); + toggle_field("extrusion_spacing", have_default_width); + bool has_PP_ironing = has_top_solid_infill && config->opt_bool("ironing"); for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_angle" }) - toggle_field(el, has_PP_ironing); + toggle_field(el, has_PP_ironing); bool has_ironing = has_PP_ironing || has_ironing_pattern; for (auto el : { "ironing_speed" }) @@ -460,7 +474,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_sequential_printing = config->opt_bool("complete_objects"); for (auto el : { /*"extruder_clearance_radius", "extruder_clearance_height",*/ "complete_objects_one_skirt", - "complete_objects_sort", "complete_objects_one_brim"}) + "complete_objects_sort", "complete_objects_one_brim"}) toggle_field(el, have_sequential_printing); bool have_ooze_prevention = config->opt_bool("ooze_prevention"); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a6a72e3c9..4bc3360e5 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1908,7 +1908,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) title += " - " + _L("Language selection"); wxMessageDialog dialog(nullptr, _L("Switching the language will trigger application restart.\n" - "You will lose content of the plater.") + "\n\n" + + "You will lose content of the platter.") + "\n\n" + _L("Do you want to proceed?"), title, wxICON_QUESTION | wxOK | wxCANCEL); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index bcc1eef24..3d2280523 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -15,6 +15,8 @@ #include "Selection.hpp" #include <boost/algorithm/string.hpp> +#include <boost/log/trivial.hpp> + #include "slic3r/Utils/FixModelByWin10.hpp" #ifdef __WXMSW__ diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 42a92b8f1..6603bb782 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -66,13 +66,13 @@ void KBShortcutsDialog::fill_shortcuts() if (wxGetApp().is_editor()) { Shortcuts commands_shortcuts = { // File - { ctrl + "N", L("New project, clear plater") }, - { ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear plater") }, + { ctrl + "N", L("New project, clear platter") }, + { ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear platter") }, { ctrl + "S", L("Save project (3mf)") }, { ctrl + alt + "S", L("Save project as (3mf)") }, { ctrl + "R", L("(Re)slice") }, // File>Import - { ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") }, + { ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep platter") }, { ctrl + "L", L("Import Config from ini/amf/3mf/gcode") }, { ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") }, // File>Export @@ -91,9 +91,9 @@ void KBShortcutsDialog::fill_shortcuts() { ctrl + "C", L("Copy to clipboard") }, { ctrl + "V", L("Paste from clipboard") }, #ifdef __APPLE__ - { ctrl + "Shift+" + "R", L("Reload plater from disk") }, + { ctrl + "Shift+" + "R", L("Reload platter from disk") }, #else - { "F5", L("Reload plater from disk") }, + { "F5", L("Reload platter from disk") }, #endif // __APPLE__ { ctrl + "F", L("Search") }, // Window @@ -169,7 +169,7 @@ void KBShortcutsDialog::fill_shortcuts() #endif // ENABLE_RENDER_PICKING_PASS }; - m_full_shortcuts.push_back({ { _L("Plater"), "" }, plater_shortcuts }); + m_full_shortcuts.push_back({ { _L("Platter"), "" }, plater_shortcuts }); Shortcuts gizmos_shortcuts = { { ctrl, L("All gizmos: Rotate - left mouse button; Pan - right mouse button") }, @@ -187,9 +187,9 @@ void KBShortcutsDialog::fill_shortcuts() Shortcuts commands_shortcuts = { { ctrl + "O", L("Open a G-code file") }, #ifdef __APPLE__ - { ctrl + "Shift+" + "R", L("Reload the plater from disk") }, + { ctrl + "Shift+" + "R", L("Reload the platter from disk") }, #else - { "F5", L("Reload plater from disk") }, + { "F5", L("Reload platter from disk") }, #endif // __APPLE__ }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 1804cb13d..c3721a4b9 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -428,7 +428,7 @@ void MainFrame::update_layout() { //layout m_plater->Reparent(m_tabpanel); - m_tabpanel->InsertPage(0, m_plater, _L("Plater")); + m_tabpanel->InsertPage(0, m_plater, _L("Platter")); m_main_sizer->Add(m_tabpanel, 1, wxEXPAND); update_icon(); // show @@ -467,7 +467,7 @@ void MainFrame::update_layout() m_tabpanel->Hide(); m_main_sizer->Add(m_tabpanel, 1, wxEXPAND); m_plater_page = new wxPanel(m_tabpanel); - m_tabpanel->InsertPage(0, m_plater_page, _L("Plater")); // empty panel just for Plater tab */ + m_tabpanel->InsertPage(0, m_plater_page, _L("Platter")); // empty panel just for Plater tab */ update_icon(); m_plater->Show(); break; diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 6d54b2d71..91be4c917 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -5,6 +5,7 @@ #include <vector> #include <string> #include <boost/algorithm/string.hpp> +#include <boost/log/trivial.hpp> #include <wx/sizer.h> #include <wx/stattext.h> diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index cc44d626a..25941120a 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -4,8 +4,12 @@ #include "Plater.hpp" #include "I18N.hpp" #include "libslic3r/AppConfig.hpp" + #include <wx/notebook.h> +#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp> + namespace Slic3r { namespace GUI { @@ -134,7 +138,7 @@ void PreferencesDialog::build() def_combobox_auto_switch_preview.gui_flags = "show_value"; def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Don't switch")); def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Switch when possible")); - def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Only if on plater")); + def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Only if on platter")); def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Only when GCode is ready")); if (app_config->get("auto_switch_preview") == "0") def_combobox_auto_switch_preview.set_default_value(new ConfigOptionStrings{ def_combobox_auto_switch_preview.enum_values[0] }); @@ -807,7 +811,7 @@ void PreferencesDialog::create_settings_mode_widget(wxNotebook* tabs) #ifndef WIN32 + " " + unstable_warning #endif - + "\n* " + _L("Old layout: all windows are in the application, settings are on the top tab bar and the plater choice in on the bottom of the plater view.") + + "\n* " + _L("Old layout: all windows are in the application, settings are on the top tab bar and the platter choice in on the bottom of the platter view.") + "\n* " + _L("Settings button: all windows are in the application, no tabs: you have to clic on settings gears to switch to settings tabs.") + "\n* " + _L("Settings window: settings are displayed in their own window. You have to clic on settings gears to show the settings window.") ); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 6c75eb153..b35efd92c 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -1,7 +1,9 @@ #include "Search.hpp" #include <cstddef> +#include <regex> #include <string> + #include <boost/algorithm/string.hpp> #include <boost/optional.hpp> #include <boost/nowide/convert.hpp> diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 7fe2c5c7b..38ec11954 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -22,6 +22,7 @@ #include <wx/bmpcbox.h> #include <wx/bmpbuttn.h> +#include <wx/collpane.h> #include <wx/treectrl.h> #include <wx/imaglist.h> #include <wx/settings.h> @@ -29,10 +30,12 @@ #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/predicate.hpp> - -#include <boost/algorithm/string/replace.hpp>#include <boost/algorithm/string/trim.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/trim.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/path.hpp> +#include <boost/lexical_cast.hpp> + #include "wxExtensions.hpp" #include "PresetComboBoxes.hpp" #include <wx/wupdlock.h> @@ -2345,6 +2348,7 @@ void TabFilament::add_filament_overrides_page() "filament_retract_layer_change", "filament_seam_gap", "filament_wipe", + "filament_wipe_extra_perimeter", "filament_wipe_speed", "filament_wipe_extra_perimeter" }) @@ -2374,6 +2378,7 @@ void TabFilament::update_filament_overrides_page() "filament_retract_layer_change", "filament_seam_gap", "filament_wipe", + "filament_wipe_extra_perimeter", "filament_wipe_speed", "filament_wipe_extra_perimeter" }; @@ -2920,11 +2925,21 @@ void TabPrinter::clear_pages() void TabPrinter::toggle_options() { - if (!m_active_page || m_presets->get_edited_preset().printer_technology() == ptSLA) + if (!m_active_page || m_presets->get_edited_preset().printer_technology() != ptFFF) return; Field* field; + const DynamicPrintConfig& print_config = m_preset_bundle->fff_prints.get_edited_preset().config; + const DynamicPrintConfig& filament_config = m_preset_bundle->filaments.get_edited_preset().config; + const DynamicPrintConfig& printer_config = m_preset_bundle->printers.get_edited_preset().config; + + // Print config values + DynamicPrintConfig full_print_config; + full_print_config.apply(print_config); + full_print_config.apply(filament_config); + full_print_config.apply(printer_config); + bool have_multiple_extruders = m_extruders_count > 1; field = get_field("toolchange_gcode"); if (field) field->toggle(have_multiple_extruders); @@ -3001,7 +3016,7 @@ void TabPrinter::toggle_options() // some options only apply when not using firmware retraction vec.resize(0); - vec = { "retract_speed", "deretract_speed", "retract_before_wipe", "retract_restart_extra", "wipe", "wipe_speed" }; + vec = { "retract_speed", "deretract_speed", "retract_before_wipe", "retract_restart_extra", "wipe", "wipe_speed" , "wipe_only_crossing"}; for (auto el : vec) { field = get_field(el, i); if (field) @@ -3009,13 +3024,17 @@ void TabPrinter::toggle_options() } bool wipe = m_config->opt_bool("wipe", i) && have_retract_length; - vec = { "retract_before_wipe", "wipe_speed" }; + vec = { "retract_before_wipe", "wipe_only_crossing", "wipe_speed" }; for (auto el : vec) { field = get_field(el, i); if (field) field->toggle(wipe); } + // wipe_only_crossing can only work if avoid_crossing_perimeters + if (!full_print_config.opt_bool("avoid_crossing_perimeters")) + get_field("wipe_only_crossing", i)->toggle(false); + if (use_firmware_retraction && wipe) { wxMessageDialog dialog(parent(), _(L("The Wipe option is not available when using the Firmware Retraction mode.\n" diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index bf952bb8c..37821d7c8 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -6,12 +6,15 @@ #include <ostream> #include <utility> #include <stdexcept> -#include <boost/format.hpp> + #include <boost/algorithm/string.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> +#include <boost/format.hpp> #include <boost/lexical_cast.hpp> #include <boost/log/trivial.hpp> +#include <boost/property_tree/json_parser.hpp> +#include <boost/property_tree/ptree.hpp> #include <wx/app.h> #include <wx/msgdlg.h> |