diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index 986c139a24..140468f05a 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -82,6 +82,7 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_external_perimeter() const { return this->is_perimeter() && this->is_external(); } bool is_infill() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Infill); } bool is_solid_infill() const { return this->is_infill() && this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } + bool is_sparse_infill() const { return this->is_infill() && ! this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } bool is_external() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::External); } bool is_bridge() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Bridge); } @@ -89,6 +90,9 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_support_base() const { return this->is_support() && ! this->is_external(); } bool is_support_interface() const { return this->is_support() && this->is_external(); } bool is_mixed() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Mixed); } + + // Brim is currently marked as skirt. + bool is_skirt() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Skirt); } }; // Special flags describing loop diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 07c9849127..168dad84ce 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -798,6 +798,20 @@ std::vector sort_object_instances_by_model_order(const Pri return instances; } +static inline GCode::SmoothPathCache smooth_path_interpolate_global(const Print& print) +{ + const GCode::SmoothPathCache::InterpolationParameters interpolation_params { + scaled(print.config().gcode_resolution.value), + print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? + Geometry::ArcWelder::default_arc_length_percent_tolerance : + 0 + }; + GCode::SmoothPathCache out; + out.interpolate_add(print.skirt(), interpolation_params); + out.interpolate_add(print.brim(), interpolation_params); + return out; +} + void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { // modifies m_silent_time_estimator_enabled @@ -1058,6 +1072,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write(this->set_extruder(initial_extruder_id, 0.)); } + GCode::SmoothPathCache smooth_path_cache_global = smooth_path_interpolate_global(print); + // Do all objects for each layer. if (print.config().complete_objects.value) { size_t finished_objects = 0; @@ -1102,7 +1118,9 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. - this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file); + this->process_layers(print, tool_ordering, collect_layers_to_print(object), + *print_object_instance_sequential_active - object.instances().data(), + smooth_path_cache_global, file); ++ finished_objects; // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. // Reset it when starting another object from 1st layer. @@ -1158,7 +1176,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. - this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file); + this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, + smooth_path_cache_global, file); if (m_wipe_tower) // Purge the extruder, pull out the active filament. file.write(m_wipe_tower->finalize(*this)); @@ -1258,6 +1277,7 @@ void GCodeGenerator::process_layers( const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream) { size_t layer_to_print_idx = 0; @@ -1268,7 +1288,7 @@ void GCodeGenerator::process_layers( 0 }; const auto smooth_path_interpolator = tbb::make_filter>(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &layers_to_print, &layer_to_print_idx, interpolation_params](tbb::flow_control &fc) -> std::pair { + [this, &print, &layers_to_print, &layer_to_print_idx, &interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { fc.stop(); @@ -1288,7 +1308,8 @@ void GCodeGenerator::process_layers( } }); const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print](std::pair in) -> LayerResult { + [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &smooth_path_cache_global]( + std::pair in) -> LayerResult { size_t layer_to_print_idx = in.first; if (layer_to_print_idx == layers_to_print.size()) { // Pressure equalizer need insert empty input. Because it returns one layer back. @@ -1300,7 +1321,9 @@ void GCodeGenerator::process_layers( if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); print.throw_if_canceled(); - return this->process_layer(print, layer.second, layer_tools, &in.second, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); + return this->process_layer(print, layer.second, layer_tools, + GCode::SmoothPathCaches{ smooth_path_cache_global, in.second }, + &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); } }); // The pipeline is variable: The vase mode filter is optional. @@ -1357,6 +1380,7 @@ void GCodeGenerator::process_layers( const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream) { size_t layer_to_print_idx = 0; @@ -1386,7 +1410,7 @@ void GCodeGenerator::process_layers( } }); const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &layers_to_print, single_object_idx](std::pair in) -> LayerResult { + [this, &print, &tool_ordering, &layers_to_print, &smooth_path_cache_global, single_object_idx](std::pair in) -> LayerResult { size_t layer_to_print_idx = in.first; if (layer_to_print_idx == layers_to_print.size()) { // Pressure equalizer need insert empty input. Because it returns one layer back. @@ -1395,7 +1419,9 @@ void GCodeGenerator::process_layers( } else { ObjectLayerToPrint &layer = layers_to_print[layer_to_print_idx]; print.throw_if_canceled(); - return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &in.second, &layer == &layers_to_print.back(), nullptr, single_object_idx); + return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), + GCode::SmoothPathCaches{ smooth_path_cache_global, in.second }, + &layer == &layers_to_print.back(), nullptr, single_object_idx); } }); // The pipeline is variable: The vase mode filter is optional. @@ -1877,7 +1903,7 @@ LayerResult GCodeGenerator::process_layer( // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCaches &smooth_path_caches, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -2044,13 +2070,11 @@ LayerResult GCodeGenerator::process_layer( double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. - const ExtrusionLoop src = *dynamic_cast(print.skirt().entities[i]); - ExtrusionLoop loop(src.loop_role()); - loop.paths.reserve(src.paths.size()); - for (const ExtrusionPath &path : src.paths) - loop.paths.emplace_back(path.polyline, ExtrusionAttributes{ path.role(), ExtrusionFlow{ mm3_per_mm, path.width(), layer_skirt_flow.height() } }); //FIXME using the support_material_speed of the 1st object printed. - gcode += this->extrude_loop(loop, smooth_path_cache, "skirt"sv, m_config.support_material_speed.value); + gcode += this->extrude_skirt(dynamic_cast(*print.skirt().entities[i]), + // Override of skirt extrusion parameters. extrude_skirt() will fill in the extrusion width. + ExtrusionFlow{ mm3_per_mm, 0., layer_skirt_flow.height() }, + smooth_path_caches.global(), "skirt"sv, m_config.support_material_speed.value); } m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). @@ -2063,7 +2087,7 @@ LayerResult GCodeGenerator::process_layer( this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); for (const ExtrusionEntity *ee : print.brim().entities) - gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, "brim"sv, m_config.support_material_speed.value); + gcode += this->extrude_entity({ *ee, false }, smooth_path_caches.global(), "brim"sv, m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point. @@ -2080,7 +2104,7 @@ LayerResult GCodeGenerator::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, smooth_path_cache, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_caches.layer_local(), is_anything_overridden, true /* print_wipe_extrusions */); if (gcode_size_old < gcode.size()) gcode+="; PURGING FINISHED\n"; @@ -2089,7 +2113,7 @@ LayerResult GCodeGenerator::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, smooth_path_cache, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_caches.layer_local(), is_anything_overridden, false /* print_wipe_extrusions */); } @@ -2119,7 +2143,7 @@ void GCodeGenerator::process_layer_single_object( // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, // Optional smooth path interpolating extrusion polylines. - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCache &smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). @@ -2413,7 +2437,19 @@ std::string GCodeGenerator::change_layer(coordf_t print_z) return gcode; } -std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) +#ifndef NDEBUG +static inline bool validate_smooth_path(const GCode::SmoothPath &smooth_path, bool loop) +{ + for (auto it = std::next(smooth_path.begin()); it != smooth_path.end(); ++ it) { + assert(it->path.size() >= 2); + assert(std::prev(it)->path.back().point == it->path.front().point); + } + assert(! loop || smooth_path.front().path.front().point == smooth_path.back().path.back().point); + return true; +} +#endif //NDEBUG + +std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { // Extrude all loops CCW. bool is_hole = loop_src.is_clockwise(); @@ -2424,7 +2460,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC } // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, // thus empty path segments will not be produced by G-code export. - GCode::SmoothPath smooth_path = smooth_path_cache->resolve_or_fit_split_with_seam( + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( loop_src, is_hole, m_scaled_resolution, seam_point, scaled(0.0015)); // Clip the path to avoid the extruder to get exactly on the first point of the loop; @@ -2436,13 +2472,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC if (smooth_path.empty()) return {}; -#ifndef NDEBUG - for (auto it = std::next(smooth_path.begin()); it != smooth_path.end(); ++ it) { - assert(it->path.size() >= 2); - assert(std::prev(it)->path.back().point == it->path.front().point); - } - assert(m_enable_loop_clipping || smooth_path.front().path.front().point == smooth_path.back().path.back().point); -#endif //NDEBUG + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); // Apply the small perimeter speed. if (loop_src.paths.front().role().is_perimeter() && loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1) @@ -2470,7 +2500,45 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC return gcode; } -std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_skirt( + const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, + const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) +{ + assert(loop_src.is_counter_clockwise()); + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( + loop_src, false, m_scaled_resolution, this->last_pos(), scaled(0.0015)); + + // Clip the path to avoid the extruder to get exactly on the first point of the loop; + // if polyline was shorter than the clipping distance we'd get a null polyline, so + // we discard it in that case. + if (m_enable_loop_clipping) + clip_end(smooth_path, scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); + + if (smooth_path.empty()) + return {}; + + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); + + // Extrude along the smooth path. + std::string gcode; + for (GCode::SmoothPathElement &el : smooth_path) { + // Override extrusion parameters. + el.path_attributes.mm3_per_mm = extrusion_flow_override.mm3_per_mm; + el.path_attributes.height = extrusion_flow_override.height; + gcode += this->_extrude(el.path_attributes, el.path, description, speed); + } + + // reset acceleration + gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); + + if (m_wipe.enabled()) + // Wipe will hide the seam. + m_wipe.set_path(std::move(smooth_path), false); + + return gcode; +} + +std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { #ifndef NDEBUG for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++ it) { @@ -2478,7 +2546,7 @@ std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipa assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } #endif // NDEBUG - GCode::SmoothPath smooth_path = smooth_path_cache->resolve_or_fit(multipath, reverse, m_scaled_resolution); + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit(multipath, reverse, m_scaled_resolution); // extrude along the path std::string gcode; @@ -2490,7 +2558,7 @@ std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipa return gcode; } -std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { if (const ExtrusionPath *path = dynamic_cast(&entity.extrusion_entity())) return this->extrude_path(*path, entity.flipped(), smooth_path_cache, description, speed); @@ -2503,9 +2571,9 @@ std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entit return {}; } -std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, std::string_view description, double speed) +std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, std::string_view description, double speed) { - Geometry::ArcWelder::Path smooth_path = smooth_path_cache->resolve_or_fit(path, reverse, m_scaled_resolution); + Geometry::ArcWelder::Path smooth_path = smooth_path_cache.resolve_or_fit(path, reverse, m_scaled_resolution); std::string gcode = this->_extrude(path.attributes(), smooth_path, description, speed); Geometry::ArcWelder::reverse(smooth_path); m_wipe.set_path(std::move(smooth_path)); @@ -2514,7 +2582,7 @@ std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse return gcode; } -std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache *smooth_path_cache) +std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache) { static constexpr const auto support_label = "support material"sv; static constexpr const auto support_interface_label = "support material interface"sv; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 42d6996e6a..73e1a793fa 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -202,7 +202,7 @@ private: // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCaches &smooth_path_caches, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -217,6 +217,7 @@ private: const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream); // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser @@ -226,6 +227,7 @@ private: const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream); void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } @@ -233,10 +235,13 @@ private: void set_extruders(const std::vector &extruder_ids); std::string preamble(); std::string change_layer(coordf_t print_z); - std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); - std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); - std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); - std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_skirt(const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, + const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed); + + std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); struct InstanceToPrint { @@ -271,13 +276,13 @@ private: // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, // Optional smooth path interpolating extrusion polylines. - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCache &smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). const bool print_wipe_extrusions); - std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache *smooth_path_cache); + std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); diff --git a/src/libslic3r/GCode/SmoothPath.cpp b/src/libslic3r/GCode/SmoothPath.cpp index 7f4ee21785..ba793b465e 100644 --- a/src/libslic3r/GCode/SmoothPath.cpp +++ b/src/libslic3r/GCode/SmoothPath.cpp @@ -121,26 +121,32 @@ double clip_end(SmoothPath &path, double distance) return distance; } -void SmoothPathCache::interpolate_add(const Polyline &polyline, const InterpolationParameters ¶ms) -{ - m_cache[&polyline] = Slic3r::Geometry::ArcWelder::fit_path(polyline.points, params.tolerance, params.fit_circle_tolerance); -} - void SmoothPathCache::interpolate_add(const ExtrusionPath &path, const InterpolationParameters ¶ms) { - this->interpolate_add(path.polyline, params); + double tolerance = params.tolerance; + if (path.role().is_sparse_infill()) + // Use 3x lower resolution than the object fine detail for sparse infill. + tolerance *= 3.; + else if (path.role().is_support()) + // Use 4x lower resolution than the object fine detail for support. + tolerance *= 4.; + else if (path.role().is_skirt()) + // Brim is currently marked as skirt. + // Use 4x lower resolution than the object fine detail for skirt & brim. + tolerance *= 4.; + m_cache[&path.polyline] = Slic3r::Geometry::ArcWelder::fit_path(path.polyline.points, tolerance, params.fit_circle_tolerance); } void SmoothPathCache::interpolate_add(const ExtrusionMultiPath &multi_path, const InterpolationParameters ¶ms) { for (const ExtrusionPath &path : multi_path.paths) - this->interpolate_add(path.polyline, params); + this->interpolate_add(path, params); } void SmoothPathCache::interpolate_add(const ExtrusionLoop &loop, const InterpolationParameters ¶ms) { for (const ExtrusionPath &path : loop.paths) - this->interpolate_add(path.polyline, params); + this->interpolate_add(path, params); } void SmoothPathCache::interpolate_add(const ExtrusionEntityCollection &eec, const InterpolationParameters ¶ms) @@ -219,6 +225,9 @@ SmoothPath SmoothPathCache::resolve_or_fit_split_with_seam( assert(this_proj.segment_id >= 0 && this_proj.segment_id < el.path.size()); proj = this_proj; proj_path = &el - out.data(); + if (proj.distance2 == 0) + // There will be no better split point found than one with zero distance. + break; } assert(proj_path >= 0); // Split the path at the closest point. diff --git a/src/libslic3r/GCode/SmoothPath.hpp b/src/libslic3r/GCode/SmoothPath.hpp index cd686564a9..b2eb335a32 100644 --- a/src/libslic3r/GCode/SmoothPath.hpp +++ b/src/libslic3r/GCode/SmoothPath.hpp @@ -39,7 +39,6 @@ public: double fit_circle_tolerance; }; - void interpolate_add(const Polyline &pl, const InterpolationParameters ¶ms); void interpolate_add(const ExtrusionPath &ee, const InterpolationParameters ¶ms); void interpolate_add(const ExtrusionMultiPath &ee, const InterpolationParameters ¶ms); void interpolate_add(const ExtrusionLoop &ee, const InterpolationParameters ¶ms); @@ -64,6 +63,24 @@ private: ankerl::unordered_dense::map m_cache; }; +// Encapsulates references to global and layer local caches of smooth extrusion paths. +class SmoothPathCaches final +{ +public: + SmoothPathCaches() = delete; + SmoothPathCaches(const SmoothPathCache &global, const SmoothPathCache &layer_local) : + m_global(&global), m_layer_local(&layer_local) {} + SmoothPathCaches operator=(const SmoothPathCaches &rhs) + { m_global = rhs.m_global; m_layer_local = rhs.m_layer_local; return *this; } + + const SmoothPathCache& global() const { return *m_global; } + const SmoothPathCache& layer_local() const { return *m_layer_local; } + +private: + const SmoothPathCache *m_global; + const SmoothPathCache *m_layer_local; +}; + } // namespace GCode } // namespace Slic3r diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 25426d68fc..bb23381de8 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -590,6 +590,7 @@ std::pair split_at(const Path &path, const PathSegmentProjection &pr { assert(proj.valid()); assert(! proj.valid() || (proj.segment_id >= 0 && proj.segment_id < path.size())); + assert(path.size() > 1); std::pair out; if (! proj.valid() || proj.segment_id + 1 == path.size() || (proj.segment_id + 2 == path.size() && proj.point == path.back().point)) out.first = path; @@ -637,18 +638,28 @@ std::pair split_at(const Path &path, const PathSegmentProjection &pr if ((cross2(vproj, vend) > 0) == end.ccw()) // Make the radius of a minor arc positive. out.second[1].radius *= -1.f; + assert(out.first.size() > 1); + assert(out.second.size() > 1); + out.second.front().radius = 0; } } else { - // Split at the start of proj.segment_id. - out.first.assign(path.begin(), path.begin() + split_segment_id + 1); - out.second.assign(path.begin() + split_segment_id, path.end()); - assert(out.first.size() + out.second.size() == path.size() + 1); - assert(out.first.back() == (split_segment_id == proj.segment_id ? start : end)); - assert(out.second.front() == (split_segment_id == proj.segment_id ? start : end)); + assert(split_segment_id >= 0 && split_segment_id < path.size()); + if (split_segment_id + 1 == path.size()) + out.first = path; + else if (split_segment_id == 0) + out.second = path; + else { + // Split at the start of proj.segment_id. + out.first.assign(path.begin(), path.begin() + split_segment_id + 1); + out.second.assign(path.begin() + split_segment_id, path.end()); + assert(out.first.size() + out.second.size() == path.size() + 1); + assert(out.first.back() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.second.front() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.first.size() > 1); + assert(out.second.size() > 1); + out.second.front().radius = 0; + } } - assert(out.first.size() > 1); - assert(out.second.size() > 1); - out.second.front().radius = 0; } return out;