diff options
author | enricoturri1966 <enricoturri@seznam.cz> | 2021-09-14 11:05:18 +0300 |
---|---|---|
committer | enricoturri1966 <enricoturri@seznam.cz> | 2021-09-14 11:05:18 +0300 |
commit | a74f3e3fc0e49f217c89d4388db5b04d89de2e13 (patch) | |
tree | cc9c02edbf0691cff9a6df48a11eda2a6fd9ba44 /src/libslic3r | |
parent | 37c9e207785567744f874f8729dc014239c53775 (diff) | |
parent | ec976cbe0539c8dbb6c3adcd0f6fe15a1b389492 (diff) |
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_reload_from_disk_changes
Diffstat (limited to 'src/libslic3r')
-rw-r--r-- | src/libslic3r/ClipperUtils.cpp | 14 | ||||
-rw-r--r-- | src/libslic3r/ClipperUtils.hpp | 5 | ||||
-rw-r--r-- | src/libslic3r/ExPolygon.cpp | 2 | ||||
-rw-r--r-- | src/libslic3r/Fill/FillAdaptive.cpp | 4 | ||||
-rw-r--r-- | src/libslic3r/GCode.cpp | 140 | ||||
-rw-r--r-- | src/libslic3r/GCode.hpp | 31 | ||||
-rw-r--r-- | src/libslic3r/GCode/CoolingBuffer.cpp | 65 | ||||
-rw-r--r-- | src/libslic3r/GCode/CoolingBuffer.hpp | 23 | ||||
-rw-r--r-- | src/libslic3r/GCode/SpiralVase.cpp | 2 | ||||
-rw-r--r-- | src/libslic3r/GCode/SpiralVase.hpp | 10 | ||||
-rw-r--r-- | src/libslic3r/GCodeReader.cpp | 11 | ||||
-rw-r--r-- | src/libslic3r/GCodeWriter.cpp | 269 | ||||
-rw-r--r-- | src/libslic3r/GCodeWriter.hpp | 89 | ||||
-rw-r--r-- | src/libslic3r/MultiPoint.hpp | 4 | ||||
-rw-r--r-- | src/libslic3r/Polygon.hpp | 8 | ||||
-rw-r--r-- | src/libslic3r/Polyline.cpp | 14 | ||||
-rw-r--r-- | src/libslic3r/Polyline.hpp | 6 | ||||
-rw-r--r-- | src/libslic3r/Print.cpp | 4 | ||||
-rw-r--r-- | src/libslic3r/SLA/Pad.cpp | 2 | ||||
-rw-r--r-- | src/libslic3r/SLAPrint.cpp | 2 | ||||
-rw-r--r-- | src/libslic3r/SupportMaterial.cpp | 2 | ||||
-rw-r--r-- | src/libslic3r/Thread.cpp | 18 | ||||
-rw-r--r-- | src/libslic3r/Thread.hpp | 3 |
23 files changed, 450 insertions, 278 deletions
diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 8a4dfc03c..a1dd638ca 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -467,6 +467,8 @@ Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons { return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) + { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) @@ -496,6 +498,8 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfac { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) + { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) @@ -610,12 +614,18 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip) { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); } +Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip) + { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip) { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip) { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonsProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip) { return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } +Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip) + { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); } +Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip) + { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip) { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip) @@ -637,7 +647,9 @@ Lines _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Pol // convert Polylines to Lines Lines retval; for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) - retval.emplace_back(polyline->operator Line()); + if (polyline->size() >= 2) + //FIXME It may happen, that Clipper produced a polyline with more than 2 collinear points by clipping a single line with polygons. It is a very rare issue, but it happens, see GH #6933. + retval.push_back({ polyline->front(), polyline->back() }); return retval; } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index c4e014a74..f49d922c1 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -303,6 +303,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygo Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); @@ -312,6 +313,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surf Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); +Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip); @@ -322,6 +324,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon } // Safety offset is applied to the clipping polygons only. +Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); @@ -337,6 +340,8 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); +Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip); diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 506ba8cb6..4a40c02c3 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -92,7 +92,7 @@ bool ExPolygon::contains(const Line &line) const bool ExPolygon::contains(const Polyline &polyline) const { - return diff_pl((Polylines)polyline, *this).empty(); + return diff_pl(polyline, *this).empty(); } bool ExPolygon::contains(const Polylines &polylines) const diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 3eabc6106..29b343db0 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -928,7 +928,9 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } }; assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000); #endif // NDEBUG - } else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000) + } else if (pl.size() >= 2 && + //FIXME Hoping that pl is really a line, trimmed by a polygon using ClipperUtils. Sometimes Clipper leaves some additional collinear points on the polyline, let's hope it is all right. + Line{ pl.front(), pl.back() }.distance_to_squared(pt) <= 1000 * 1000) out = closest.front().second; } return out; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index efea240e5..cebc9136f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -35,6 +35,7 @@ #include "SVG.hpp" #include <tbb/parallel_for.h> +#include <tbb/pipeline.h> #include <Shiny/Shiny.h> @@ -1099,7 +1100,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_volumetric_speed = DoExport::autospeed_volumetric_limit(print); print.throw_if_canceled(); - m_cooling_buffer = make_unique<CoolingBuffer>(*this); if (print.config().spiral_vase.value) m_spiral_vase = make_unique<SpiralVase>(print.config()); #ifdef HAS_PRESSURE_EQUALIZER @@ -1212,6 +1212,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } print.throw_if_canceled(); + m_cooling_buffer = make_unique<CoolingBuffer>(*this); m_cooling_buffer->set_current_extruder(initial_extruder_id); // Emit machine envelope limits for the Marlin firmware. @@ -1219,7 +1220,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Disable fan. if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id)) - file.write(m_writer.set_fan(0, true)); + file.write(m_writer.set_fan(0)); // Let the start-up script prime the 1st printing tool. m_placeholder_parser.set("initial_tool", initial_extruder_id); @@ -1334,17 +1335,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.writeln(between_objects_gcode); } // Reset the cooling buffer internal state (the current position, feed rate, accelerations). - m_cooling_buffer->reset(); + m_cooling_buffer->reset(this->writer().get_position()); m_cooling_buffer->set_current_extruder(initial_extruder_id); - // Pair the object layers with the support layers by z, extrude them. - std::vector<LayerToPrint> layers_to_print = collect_layers_to_print(object); - for (const LayerToPrint <p : layers_to_print) { - std::vector<LayerToPrint> lrs; - lrs.emplace_back(std::move(ltp)); - this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), <p == &layers_to_print.back(), - nullptr, *print_object_instance_sequential_active - object.instances().data()); - print.throw_if_canceled(); - } + // 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); #ifdef HAS_PRESSURE_EQUALIZER if (m_pressure_equalizer) file.write(m_pressure_equalizer->process("", true)); @@ -1401,14 +1397,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } print.throw_if_canceled(); } - // Extrude the layers. - for (auto &layer : layers_to_print) { - const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first); - if (m_wipe_tower && layer_tools.has_wipe_tower) - m_wipe_tower->next_layer(); - this->process_layer(file, print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); - print.throw_if_canceled(); - } + // 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); #ifdef HAS_PRESSURE_EQUALIZER if (m_pressure_equalizer) file.write(m_pressure_equalizer->process("", true)); @@ -1420,7 +1412,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Write end commands to file. file.write(this->retract()); - file.write(m_writer.set_fan(false)); + file.write(m_writer.set_fan(0)); // adds tag for processor file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); @@ -1481,6 +1473,88 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.throw_if_canceled(); } +// 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. +void GCode::process_layers( + const Print &print, + const ToolOrdering &tool_ordering, + const std::vector<const PrintInstance*> &print_object_instances_ordering, + const std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> &layers_to_print, + GCodeOutputStream &output_stream) +{ + // The pipeline is variable: The vase mode filter is optional. + size_t layer_to_print_idx = 0; + const auto generator = tbb::make_filter<void, GCode::LayerResult>(tbb::filter::serial_in_order, + [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult { + if (layer_to_print_idx == layers_to_print.size()) { + fc.stop(); + return {}; + } else { + const std::pair<coordf_t, std::vector<LayerToPrint>>& layer = layers_to_print[layer_to_print_idx++]; + const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first); + 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, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); + } + }); + const auto spiral_vase = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(tbb::filter::serial_in_order, + [&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in) -> GCode::LayerResult { + spiral_vase.enable(in.spiral_vase_enable); + return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; + }); + const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(tbb::filter::serial_in_order, + [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string { + return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); + }); + const auto output = tbb::make_filter<std::string, void>(tbb::filter::serial_in_order, + [&output_stream](std::string s) { output_stream.write(s); } + ); + + // The pipeline elements are joined using const references, thus no copying is performed. + if (m_spiral_vase) + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); + else + tbb::parallel_pipeline(12, generator & cooling & output); +} + +// 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. +void GCode::process_layers( + const Print &print, + const ToolOrdering &tool_ordering, + std::vector<LayerToPrint> layers_to_print, + const size_t single_object_idx, + GCodeOutputStream &output_stream) +{ + // The pipeline is fixed: Neither wipe tower nor vase mode are implemented for sequential print. + size_t layer_to_print_idx = 0; + tbb::parallel_pipeline(12, + tbb::make_filter<void, GCode::LayerResult>( + tbb::filter::serial_in_order, + [this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult { + if (layer_to_print_idx == layers_to_print.size()) { + fc.stop(); + return {}; + } else { + LayerToPrint &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()), &layer == &layers_to_print.back(), nullptr, single_object_idx); + } + }) & + tbb::make_filter<GCode::LayerResult, std::string>( + tbb::filter::serial_in_order, + [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string { + return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); + }) & + tbb::make_filter<std::string, void>( + tbb::filter::serial_in_order, + [&output_stream](std::string s) { output_stream.write(s); } + )); +} + std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) { try { @@ -1890,9 +1964,7 @@ namespace Skirt { // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // and performing the extruder specific extrusions together. -void GCode::process_layer( - // Write into the output file. - GCodeOutputStream &file, +GCode::LayerResult GCode::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector<LayerToPrint> &layers, @@ -1908,11 +1980,6 @@ void GCode::process_layer( // Either printing all copies of all objects, or just a single copy of a single object. assert(single_object_instance_idx == size_t(-1) || layers.size() == 1); - if (layer_tools.extruders.empty()) - // Nothing to extrude. - return; - - // Extract 1st object_layer and support_layer of this set of layers with an equal print_z. const Layer *object_layer = nullptr; const SupportLayer *support_layer = nullptr; for (const LayerToPrint &l : layers) { @@ -1922,6 +1989,12 @@ void GCode::process_layer( support_layer = l.support_layer; } const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; + GCode::LayerResult result { {}, layer.id(), false, last_layer }; + if (layer_tools.extruders.empty()) + // Nothing to extrude. + return result; + + // Extract 1st object_layer and support_layer of this set of layers with an equal print_z. coordf_t print_z = layer.print_z; bool first_layer = layer.id() == 0; unsigned int first_extruder_id = layer_tools.extruders.front(); @@ -1943,7 +2016,7 @@ void GCode::process_layer( break; } } - m_spiral_vase->enable(enable); + result.spiral_vase_enable = enable; // If we're going to apply spiralvase to this layer, disable loop clipping. m_enable_loop_clipping = !enable; } @@ -2285,6 +2358,7 @@ void GCode::process_layer( } } +#if 0 // Apply spiral vase post-processing if this layer contains suitable geometry // (we must feed all the G-code into the post-processor, including the first // bottom non-spiral layers otherwise it will mess with positions) @@ -2308,8 +2382,14 @@ void GCode::process_layer( #endif /* HAS_PRESSURE_EQUALIZER */ file.write(gcode); +#endif + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); + + result.gcode = std::move(gcode); + result.cooling_buffer_flush = object_layer || last_layer; + return result; } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1dbc5b70a..93a11821e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -215,9 +215,16 @@ private: static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object); static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print); - void process_layer( - // Write into the output file. - GCodeOutputStream &file, + + struct LayerResult { + std::string gcode; + size_t layer_id; + // Is spiral vase post processing enabled for this layer? + bool spiral_vase_enable { false }; + // Should the cooling buffer content be flushed at the end of this layer? + bool cooling_buffer_flush { false }; + }; + LayerResult process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector<LayerToPrint> &layers, @@ -228,6 +235,24 @@ private: // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx = size_t(-1)); + // 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. + void process_layers( + const Print &print, + const ToolOrdering &tool_ordering, + const std::vector<const PrintInstance*> &print_object_instances_ordering, + const std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> &layers_to_print, + 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 + // and export G-code into file. + void process_layers( + const Print &print, + const ToolOrdering &tool_ordering, + std::vector<LayerToPrint> layers_to_print, + const size_t single_object_idx, + GCodeOutputStream &output_stream); void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } bool last_pos_defined() const { return m_last_pos_defined; } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 17f3e0b73..9ca85c728 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -16,19 +16,25 @@ namespace Slic3r { -CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_extruder(0) +CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0) { - this->reset(); + this->reset(gcodegen.writer().get_position()); + + const std::vector<Extruder> &extruders = gcodegen.writer().extruders(); + m_extruder_ids.reserve(extruders.size()); + for (const Extruder &ex : extruders) { + m_num_extruders = std::max(ex.id() + 1, m_num_extruders); + m_extruder_ids.emplace_back(ex.id()); + } } -void CoolingBuffer::reset() +void CoolingBuffer::reset(const Vec3d &position) { m_current_pos.assign(5, 0.f); - Vec3d pos = m_gcodegen.writer().get_position(); - m_current_pos[0] = float(pos(0)); - m_current_pos[1] = float(pos(1)); - m_current_pos[2] = float(pos(2)); - m_current_pos[4] = float(m_gcodegen.config().travel_speed.value); + m_current_pos[0] = float(position.x()); + m_current_pos[1] = float(position.y()); + m_current_pos[2] = float(position.z()); + m_current_pos[4] = float(m_config.travel_speed.value); } struct CoolingLine @@ -303,30 +309,23 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b // Return the list of parsed lines, bucketed by an extruder. std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector<float> ¤t_pos) const { - const FullPrintConfig &config = m_gcodegen.config(); - const std::vector<Extruder> &extruders = m_gcodegen.writer().extruders(); - unsigned int num_extruders = 0; - for (const Extruder &ex : extruders) - num_extruders = std::max(ex.id() + 1, num_extruders); - - std::vector<PerExtruderAdjustments> per_extruder_adjustments(extruders.size()); - std::vector<size_t> map_extruder_to_per_extruder_adjustment(num_extruders, 0); - for (size_t i = 0; i < extruders.size(); ++ i) { + std::vector<PerExtruderAdjustments> per_extruder_adjustments(m_extruder_ids.size()); + std::vector<size_t> map_extruder_to_per_extruder_adjustment(m_num_extruders, 0); + for (size_t i = 0; i < m_extruder_ids.size(); ++ i) { PerExtruderAdjustments &adj = per_extruder_adjustments[i]; - unsigned int extruder_id = extruders[i].id(); + unsigned int extruder_id = m_extruder_ids[i]; adj.extruder_id = extruder_id; - adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); - adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id)); - adj.min_print_speed = float(config.min_print_speed.get_at(extruder_id)); + adj.cooling_slow_down_enabled = m_config.cooling.get_at(extruder_id); + adj.slowdown_below_layer_time = float(m_config.slowdown_below_layer_time.get_at(extruder_id)); + adj.min_print_speed = float(m_config.min_print_speed.get_at(extruder_id)); map_extruder_to_per_extruder_adjustment[extruder_id] = i; } - const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix(); unsigned int current_extruder = m_current_extruder; PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; const char *line_start = gcode.c_str(); const char *line_end = line_start; - const char extrusion_axis = get_extrusion_axis(config)[0]; + const char extrusion_axis = get_extrusion_axis(m_config)[0]; // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); @@ -387,7 +386,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std:: } if ((line.type & CoolingLine::TYPE_G92) == 0) { // G0 or G1. Calculate the duration. - if (config.use_relative_e_distances.value) + if (m_config.use_relative_e_distances.value) // Reset extruder accumulator. current_pos[3] = 0.f; float dif[4]; @@ -430,8 +429,8 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std:: } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { line.type = CoolingLine::TYPE_EXTRUDE_END; active_speed_modifier = size_t(-1); - } else if (boost::starts_with(sline, toolchange_prefix)) { - unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size()); + } else if (boost::starts_with(sline, m_toolchange_prefix)) { + unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + m_toolchange_prefix.size()); // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored. if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) { if (new_extruder != current_extruder) { @@ -641,7 +640,7 @@ float CoolingBuffer::calculate_layer_slowdown(std::vector<PerExtruderAdjustments if (total > slowdown_below_layer_time) { // The current total time is above the minimum threshold of the rest of the extruders, don't adjust anything. } else { - // Adjust this and all the following (higher config.slowdown_below_layer_time) extruders. + // Adjust this and all the following (higher m_config.slowdown_below_layer_time) extruders. // Sum maximum slow down time as if everything was slowed down including the external perimeters. float max_time = elapsed_time_total0; for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it) @@ -694,8 +693,7 @@ std::string CoolingBuffer::apply_layer_cooldown( bool bridge_fan_control = false; int bridge_fan_speed = 0; auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed ]() { - const FullPrintConfig &config = m_gcodegen.config(); -#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder) +#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0; int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers); @@ -737,13 +735,12 @@ std::string CoolingBuffer::apply_layer_cooldown( } if (fan_speed_new != fan_speed) { fan_speed = fan_speed_new; - new_gcode += m_gcodegen.writer().set_fan(fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, fan_speed); } }; const char *pos = gcode.c_str(); int current_feedrate = 0; - const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix(); change_extruder_set_fan(); for (const CoolingLine *line : lines) { const char *line_start = gcode.c_str() + line->line_start; @@ -751,7 +748,7 @@ std::string CoolingBuffer::apply_layer_cooldown( if (line_start > pos) new_gcode.append(pos, line_start - pos); if (line->type & CoolingLine::TYPE_SET_TOOL) { - unsigned int new_extruder = (unsigned int)atoi(line_start + toolchange_prefix.size()); + unsigned int new_extruder = (unsigned int)atoi(line_start + m_toolchange_prefix.size()); if (new_extruder != m_current_extruder) { m_current_extruder = new_extruder; change_extruder_set_fan(); @@ -759,10 +756,10 @@ std::string CoolingBuffer::apply_layer_cooldown( new_gcode.append(line_start, line_end - line_start); } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) { if (bridge_fan_control) - new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed); } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_END) { if (bridge_fan_control) - new_gcode += m_gcodegen.writer().set_fan(fan_speed, true); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, fan_speed); } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) { // Just remove this comment. } else if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE | CoolingLine::TYPE_HAS_F)) { diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 0932d62d3..5f49ef455 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -23,10 +23,9 @@ struct PerExtruderAdjustments; class CoolingBuffer { public: CoolingBuffer(GCode &gcodegen); - void reset(); + void reset(const Vec3d &position); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } std::string process_layer(std::string &&gcode, size_t layer_id, bool flush); - GCode* gcodegen() { return &m_gcodegen; } private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; @@ -36,17 +35,25 @@ private: // Returns the adjusted G-code. std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector<PerExtruderAdjustments> &per_extruder_adjustments); - GCode& m_gcodegen; // G-code snippet cached for the support layers preceding an object layer. - std::string m_gcode; + std::string m_gcode; // Internal data. // X,Y,Z,E,F - std::vector<char> m_axis; - std::vector<float> m_current_pos; - unsigned int m_current_extruder; + std::vector<char> m_axis; + std::vector<float> m_current_pos; + // Cached from GCodeWriter. + // Printing extruder IDs, zero based. + std::vector<unsigned int> m_extruder_ids; + // Highest of m_extruder_ids plus 1. + unsigned int m_num_extruders { 0 }; + const std::string m_toolchange_prefix; + // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, + // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. + const PrintConfig &m_config; + unsigned int m_current_extruder; // Old logic: proportional. - bool m_cooling_logic_proportional = false; + bool m_cooling_logic_proportional = false; }; } diff --git a/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp index acb6ad034..c3caee2dc 100644 --- a/src/libslic3r/GCode/SpiralVase.cpp +++ b/src/libslic3r/GCode/SpiralVase.cpp @@ -54,7 +54,7 @@ std::string SpiralVase::process_layer(const std::string &gcode) // For absolute extruder distances it will be switched off. // Tapering the absolute extruder distances requires to process every extrusion value after the first transition // layer. - bool transition = m_transition_layer && m_config->use_relative_e_distances.value; + bool transition = m_transition_layer && m_config.use_relative_e_distances.value; float layer_height_factor = layer_height / total_layer_length; float len = 0.f; m_reader.parse_buffer(gcode, [&new_gcode, &z, total_layer_length, layer_height_factor, transition, &len] diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index 5353901fe..fb461c201 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -8,10 +8,10 @@ namespace Slic3r { class SpiralVase { public: - SpiralVase(const PrintConfig &config) : m_config(&config) + SpiralVase(const PrintConfig &config) : m_config(config) { - m_reader.z() = (float)m_config->z_offset; - m_reader.apply_config(*m_config); + m_reader.z() = (float)m_config.z_offset; + m_reader.apply_config(m_config); }; void enable(bool en) { @@ -22,7 +22,7 @@ public: std::string process_layer(const std::string &gcode); private: - const PrintConfig *m_config; + const PrintConfig &m_config; GCodeReader m_reader; bool m_enabled = false; @@ -32,4 +32,4 @@ private: } -#endif +#endif // slic3r_SpiralVase_hpp_ diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 4c4bebee4..61ab10f22 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -2,7 +2,6 @@ #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/nowide/fstream.hpp> -#include <charconv> #include <fstream> #include <iostream> #include <iomanip> @@ -10,6 +9,7 @@ #include "LocalesUtils.hpp" #include <Shiny/Shiny.h> +#include <fast_float/fast_float.h> namespace Slic3r { @@ -71,16 +71,9 @@ const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, G } if (axis != NUM_AXES_WITH_UNKNOWN) { // Try to parse the numeric value. -#ifdef WIN32 double v; - auto [pend, ec] = std::from_chars(++ c, end, v); + auto [pend, ec] = fast_float::from_chars(++ c, end, v); if (pend != c && is_end_of_word(*pend)) { -#else - // The older version of GCC and Clang support std::from_chars just for integers, so strtod we used it instead. - char *pend = nullptr; - double v = strtod(++ c, &pend); - if (pend != nullptr && is_end_of_word(*pend)) { -#endif // The axis value has been parsed correctly. if (axis != UNKNOWN_AXIS) gline.m_axis[int(axis)] = float(v); diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index c97180982..f57e25406 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1,21 +1,17 @@ #include "GCodeWriter.hpp" #include "CustomGCode.hpp" #include <algorithm> -#include <charconv> #include <iomanip> #include <iostream> #include <map> #include <assert.h> -#define XYZF_EXPORT_DIGITS 3 -#define E_EXPORT_DIGITS 5 +#ifdef __APPLE__ + #include <boost/spirit/include/karma.hpp> +#endif #define FLAVOR_IS(val) this->config.gcode_flavor == val #define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val -#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment; -#define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val) -#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS) -#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS) namespace Slic3r { @@ -156,41 +152,6 @@ std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait return gcode.str(); } -std::string GCodeWriter::set_fan(unsigned int speed, bool dont_save) -{ - std::ostringstream gcode; - if (m_last_fan_speed != speed || dont_save) { - if (!dont_save) m_last_fan_speed = speed; - - if (speed == 0) { - if (FLAVOR_IS(gcfTeacup)) { - gcode << "M106 S0"; - } else if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) { - gcode << "M127"; - } else { - gcode << "M107"; - } - if (this->config.gcode_comments) gcode << " ; disable fan"; - gcode << "\n"; - } else { - if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) { - gcode << "M126"; - } else { - gcode << "M106 "; - if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) { - gcode << "P"; - } else { - gcode << "S"; - } - gcode << (255.0 * speed / 100.0); - } - if (this->config.gcode_comments) gcode << " ; enable fan"; - gcode << "\n"; - } - } - return gcode.str(); -} - std::string GCodeWriter::set_acceleration(unsigned int acceleration) { // Clamp the acceleration to the allowed maximum. @@ -292,85 +253,12 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id) return gcode.str(); } -class G1Writer { -private: - static constexpr const size_t buflen = 256; - char buf[buflen]; - char *buf_end; - std::to_chars_result ptr_err; - -public: - G1Writer() { - this->buf[0] = 'G'; - this->buf[1] = '1'; - this->buf_end = this->buf + buflen; - this->ptr_err.ptr = this->buf + 2; - } - - void emit_axis(const char axis, const double v, size_t digits) { - *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = axis; -#ifdef WIN32 - this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end, v, std::chars_format::fixed, digits); -#else - int buf_capacity = int(this->buf_end - this->ptr_err.ptr); - int ret = snprintf(this->ptr_err.ptr, buf_capacity, "%.*lf", int(digits), v); - if (ret <= 0 || ret > buf_capacity) - ptr_err.ec = std::errc::value_too_large; - else - this->ptr_err.ptr = this->ptr_err.ptr + ret; -#endif - } - - void emit_xy(const Vec2d &point) { - this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS); - this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS); - } - - void emit_xyz(const Vec3d &point) { - this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS); - this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS); - this->emit_z(point.z()); - } - - void emit_z(const double z) { - this->emit_axis('Z', z, XYZF_EXPORT_DIGITS); - } - - void emit_e(const std::string &axis, double v) { - if (! axis.empty()) { - // not gcfNoExtrusion - this->emit_axis(axis[0], v, E_EXPORT_DIGITS); - } - } - - void emit_f(double speed) { - this->emit_axis('F', speed, XYZF_EXPORT_DIGITS); - } - - void emit_string(const std::string &s) { - strncpy(ptr_err.ptr, s.c_str(), s.size()); - ptr_err.ptr += s.size(); - } - - void emit_comment(bool allow_comments, const std::string &comment) { - if (allow_comments && ! comment.empty()) { - *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' '; - this->emit_string(comment); - } - } - - std::string string() { - *ptr_err.ptr ++ = '\n'; - return std::string(this->buf, ptr_err.ptr - buf); - } -}; - std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const { assert(F > 0.); assert(F < 100000.); - G1Writer w; + GCodeG1Formatter w; w.emit_f(F); w.emit_comment(this->config.gcode_comments, comment); w.emit_string(cooling_marker); @@ -382,7 +270,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com m_pos(0) = point(0); m_pos(1) = point(1); - G1Writer w; + GCodeG1Formatter w; w.emit_xy(point); w.emit_f(this->config.travel_speed.value * 60.0); w.emit_comment(this->config.gcode_comments, comment); @@ -415,7 +303,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co m_lifted = 0; m_pos = point; - G1Writer w; + GCodeG1Formatter w; w.emit_xyz(point); w.emit_f(this->config.travel_speed.value * 60.0); w.emit_comment(this->config.gcode_comments, comment); @@ -449,7 +337,7 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) if (speed == 0.) speed = this->config.travel_speed.value; - G1Writer w; + GCodeG1Formatter w; w.emit_z(z); w.emit_f(speed * 60.0); w.emit_comment(this->config.gcode_comments, comment); @@ -474,7 +362,7 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: m_pos(1) = point(1); m_extruder->extrude(dE); - G1Writer w; + GCodeG1Formatter w; w.emit_xy(point); w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_comment(this->config.gcode_comments, comment); @@ -487,7 +375,7 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std m_lifted = 0; m_extruder->extrude(dE); - G1Writer w; + GCodeG1Formatter w; w.emit_xyz(point); w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_comment(this->config.gcode_comments, comment); @@ -518,12 +406,11 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe) std::string GCodeWriter::_retract(double length, double restart_extra, const std::string &comment) { - std::ostringstream gcode; - /* If firmware retraction is enabled, we use a fake value of 1 since we ignore the actual configured retract_length which might be 0, in which case the retraction logic gets skipped. */ - if (this->config.use_firmware_retraction) length = 1; + if (this->config.use_firmware_retraction) + length = 1; // If we use volumetric E values we turn lengths into volumes */ if (this->config.use_volumetric_e) { @@ -533,52 +420,48 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std restart_extra = restart_extra * area; } - double dE = m_extruder->retract(length, restart_extra); - if (dE != 0) { + + std::string gcode; + if (double dE = m_extruder->retract(length, restart_extra); dE != 0) { if (this->config.use_firmware_retraction) { - if (FLAVOR_IS(gcfMachinekit)) - gcode << "G22 ; retract\n"; - else - gcode << "G10 ; retract\n"; + gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n"; } else if (! m_extrusion_axis.empty()) { - gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) - << " F" << XYZF_NUM(m_extruder->retract_speed() * 60.); - COMMENT(comment); - gcode << "\n"; + GCodeG1Formatter w; + w.emit_e(m_extrusion_axis, m_extruder->E()); + w.emit_f(m_extruder->retract_speed() * 60.); + w.emit_comment(this->config.gcode_comments, comment); + gcode = w.string(); } } if (FLAVOR_IS(gcfMakerWare)) - gcode << "M103 ; extruder off\n"; - - return gcode.str(); + gcode += "M103 ; extruder off\n"; + + return gcode; } std::string GCodeWriter::unretract() { - std::ostringstream gcode; + std::string gcode; if (FLAVOR_IS(gcfMakerWare)) - gcode << "M101 ; extruder on\n"; + gcode = "M101 ; extruder on\n"; - double dE = m_extruder->unretract(); - if (dE != 0) { + if (double dE = m_extruder->unretract(); dE != 0) { if (this->config.use_firmware_retraction) { - if (FLAVOR_IS(gcfMachinekit)) - gcode << "G23 ; unretract\n"; - else - gcode << "G11 ; unretract\n"; - gcode << this->reset_e(); + gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n"; + gcode += this->reset_e(); } else if (! m_extrusion_axis.empty()) { // use G1 instead of G0 because G0 will blend the restart with the previous travel move - gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) - << " F" << XYZF_NUM(m_extruder->deretract_speed() * 60.); - if (this->config.gcode_comments) gcode << " ; unretract"; - gcode << "\n"; + GCodeG1Formatter w; + w.emit_e(m_extrusion_axis, m_extruder->E()); + w.emit_f(m_extruder->deretract_speed() * 60.); + w.emit_comment(this->config.gcode_comments, " ; unretract"); + gcode += w.string(); } } - return gcode.str(); + return gcode; } /* If this method is called more than once before calling unlift(), @@ -611,4 +494,88 @@ std::string GCodeWriter::unlift() return gcode; } +std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed) +{ + std::ostringstream gcode; + if (speed == 0) { + switch (gcode_flavor) { + case gcfTeacup: + gcode << "M106 S0"; break; + case gcfMakerWare: + case gcfSailfish: + gcode << "M127"; break; + default: + gcode << "M107"; break; + } + if (gcode_comments) + gcode << " ; disable fan"; + gcode << "\n"; + } else { + switch (gcode_flavor) { + case gcfMakerWare: + case gcfSailfish: + gcode << "M126"; break; + case gcfMach3: + case gcfMachinekit: + gcode << "M106 P" << 255.0 * speed / 100.0; break; + default: + gcode << "M106 S" << 255.0 * speed / 100.0; break; + } + if (gcode_comments) + gcode << " ; enable fan"; + gcode << "\n"; + } + return gcode.str(); +} + +std::string GCodeWriter::set_fan(unsigned int speed) const +{ + return GCodeWriter::set_fan(this->config.gcode_flavor, this->config.gcode_comments, speed); } + + +void GCodeFormatter::emit_axis(const char axis, const double v, size_t digits) { + assert(digits <= 6); + static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000}; + *ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis; + + char *base_ptr = this->ptr_err.ptr; + auto v_int = int64_t(std::round(v * pow_10[digits])); + // Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it. + // That is a little bit slower than std::to_chars but not much. +#ifdef __APPLE__ + boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int); +#else + // this->buf_end minus 1 because we need space for adding the extra decimal point. + this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int); +#endif + size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0); + if (writen_digits < digits) { + // Number is smaller than 10^digits, so that we will pad it with zeros. + size_t remaining_digits = digits - writen_digits; + // Move all newly inserted chars by remaining_digits to allocate space for padding with zeros. + for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr) + *to_ptr = *from_ptr; + + memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits); + this->ptr_err.ptr += remaining_digits; + } + + // Move all newly inserted chars by one to allocate space for a decimal point. + for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr) + *to_ptr = *from_ptr; + + *(this->ptr_err.ptr - digits) = '.'; + for (size_t i = 0; i < digits; ++i) { + if (*this->ptr_err.ptr != '0') + break; + this->ptr_err.ptr--; + } + if (*this->ptr_err.ptr == '.') + this->ptr_err.ptr--; + if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-') + *(++this->ptr_err.ptr) = '0'; + this->ptr_err.ptr++; +} + +} // namespace Slic3r diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 2de95ecc5..6e2c08d3b 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include <string> +#include <charconv> #include "Extruder.hpp" #include "Point.hpp" #include "PrintConfig.hpp" @@ -18,7 +19,7 @@ public: GCodeWriter() : multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr), m_single_extruder_multi_material(false), - m_last_acceleration(0), m_max_acceleration(0), m_last_fan_speed(0), + m_last_acceleration(0), m_max_acceleration(0), m_last_bed_temperature(0), m_last_bed_temperature_reached(true), m_lifted(0) {} @@ -42,7 +43,6 @@ public: std::string postamble() const; std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; std::string set_bed_temperature(unsigned int temperature, bool wait = false); - std::string set_fan(unsigned int speed, bool dont_save = false); std::string set_acceleration(unsigned int acceleration); std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; @@ -69,6 +69,12 @@ public: std::string unlift(); Vec3d get_position() const { return m_pos; } + // To be called by the CoolingBuffer from another thread. + static std::string set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed); + // To be called by the main thread. It always emits the G-code, it does not remember the previous state. + // Keeping the state is left to the CoolingBuffer, which runs asynchronously on another thread. + std::string set_fan(unsigned int speed) const; + private: // Extruders are sorted by their ID, so that binary search is possible. std::vector<Extruder> m_extruders; @@ -79,7 +85,6 @@ private: // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware. // If set to zero, the limit is not in action. unsigned int m_max_acceleration; - unsigned int m_last_fan_speed; unsigned int m_last_bed_temperature; bool m_last_bed_temperature_reached; double m_lifted; @@ -89,6 +94,84 @@ private: std::string _retract(double length, double restart_extra, const std::string &comment); }; +class GCodeFormatter { +public: + GCodeFormatter() { + this->buf_end = buf + buflen; + this->ptr_err.ptr = this->buf; + } + + GCodeFormatter(const GCodeFormatter&) = delete; + GCodeFormatter& operator=(const GCodeFormatter&) = delete; + + static constexpr const int XYZF_EXPORT_DIGITS = 3; + static constexpr const int E_EXPORT_DIGITS = 5; + + void emit_axis(const char axis, const double v, size_t digits); + + void emit_xy(const Vec2d &point) { + this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS); + this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS); + } + + void emit_xyz(const Vec3d &point) { + this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS); + this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS); + this->emit_z(point.z()); + } + + void emit_z(const double z) { + this->emit_axis('Z', z, XYZF_EXPORT_DIGITS); + } + + void emit_e(const std::string &axis, double v) { + if (! axis.empty()) { + // not gcfNoExtrusion + this->emit_axis(axis[0], v, E_EXPORT_DIGITS); + } + } + + void emit_f(double speed) { + this->emit_axis('F', speed, XYZF_EXPORT_DIGITS); + } + + void emit_string(const std::string &s) { + strncpy(ptr_err.ptr, s.c_str(), s.size()); + ptr_err.ptr += s.size(); + } + + void emit_comment(bool allow_comments, const std::string &comment) { + if (allow_comments && ! comment.empty()) { + *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' '; + this->emit_string(comment); + } + } + + std::string string() { + *ptr_err.ptr ++ = '\n'; + return std::string(this->buf, ptr_err.ptr - buf); + } + +protected: + static constexpr const size_t buflen = 256; + char buf[buflen]; + char* buf_end; + std::to_chars_result ptr_err; +}; + +class GCodeG1Formatter : public GCodeFormatter { +public: + GCodeG1Formatter() { + this->buf[0] = 'G'; + this->buf[1] = '1'; + this->buf_end = buf + buflen; + this->ptr_err.ptr = this->buf + 2; + } + + GCodeG1Formatter(const GCodeG1Formatter&) = delete; + GCodeG1Formatter& operator=(const GCodeG1Formatter&) = delete; +}; + } /* namespace Slic3r */ #endif /* slic3r_GCodeWriter_hpp_ */ diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 46b47832a..935348279 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -33,7 +33,9 @@ public: void rotate(double angle, const Point ¢er); void reverse() { std::reverse(this->points.begin(), this->points.end()); } - const Point& first_point() const { return this->points.front(); } + const Point& front() const { return this->points.front(); } + const Point& back() const { return this->points.back(); } + const Point& first_point() const { return this->front(); } virtual const Point& last_point() const = 0; virtual Lines lines() const = 0; size_t size() const { return points.size(); } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 333f1e6b1..3c46a564b 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -18,11 +18,6 @@ using ConstPolygonPtrs = std::vector<const Polygon*>; class Polygon : public MultiPoint { public: - explicit operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; } - explicit operator Polyline() const { return this->split_at_first_point(); } - Point& operator[](Points::size_type idx) { return this->points[idx]; } - const Point& operator[](Points::size_type idx) const { return this->points[idx]; } - Polygon() = default; virtual ~Polygon() = default; explicit Polygon(const Points &points) : MultiPoint(points) {} @@ -39,6 +34,9 @@ public: Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } + Point& operator[](Points::size_type idx) { return this->points[idx]; } + const Point& operator[](Points::size_type idx) const { return this->points[idx]; } + // last point == first point for polygons const Point& last_point() const override { return this->points.front(); } diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 68c40fc20..1255d5473 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -10,20 +10,6 @@ namespace Slic3r { -Polyline::operator Polylines() const -{ - Polylines polylines; - polylines.push_back(*this); - return polylines; -} - -Polyline::operator Line() const -{ - if (this->points.size() > 2) - throw Slic3r::InvalidArgument("Can't convert polyline with more than two points to a line"); - return Line(this->points.front(), this->points.back()); -} - const Point& Polyline::leftmost_point() const { const Point *p = &this->points.front(); diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 758fc38cd..31e0b88d3 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -59,13 +59,11 @@ public: src.points.clear(); } } - - explicit operator Polylines() const; - explicit operator Line() const; + const Point& last_point() const override { return this->points.back(); } - const Point& leftmost_point() const; Lines lines() const override; + void clip_end(double distance); void clip_start(double distance); void extend_end(double distance); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 06052a62f..c673f8810 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -397,7 +397,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly convex_hull.translate(instance.shift - print_object->center_offset()); // if output needed, collect indices (inside convex_hulls_other) of intersecting hulls for (size_t i = 0; i < convex_hulls_other.size(); ++i) { - if (!intersection((Polygons)convex_hulls_other[i], (Polygons)convex_hull).empty()) { + if (! intersection(convex_hulls_other[i], convex_hull).empty()) { if (polygons == nullptr) return false; else { @@ -812,7 +812,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // Slicing process, running at a background thread. void Print::process() { - name_tbb_thread_pool_threads(); + name_tbb_thread_pool_threads_set_locale(); BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index 8d995b59e..1e5de5158 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -370,7 +370,7 @@ bool add_cavity(indexed_triangle_set &pad, if (inner_base.empty() || middle_base.empty()) { logerr(); return false; } - ExPolygons pdiff = diff_ex((Polygons)top_poly, (Polygons)middle_base.contour); + ExPolygons pdiff = diff_ex(top_poly, middle_base.contour); if (pdiff.size() != 1) { logerr(); return false; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0b9dde122..61ff908d3 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -693,7 +693,7 @@ void SLAPrint::process() if (m_objects.empty()) return; - name_tbb_thread_pool_threads(); + name_tbb_thread_pool_threads_set_locale(); // Assumption: at this point the print objects should be populated only with // the model objects we have to process and the instances are also filtered diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1b66bcc53..f727d334d 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -3338,7 +3338,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1]; const Point *seg_current_pt = nullptr; coordf_t seg_current_t = 0.; - if (! intersection_pl((Polylines)contour.split_at_first_point(), overhang_with_margin).empty()) { + if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) { // The contour is below the overhang at least to some extent. //FIXME ideally one would place the circles below the overhang only. // Walk around the contour and place circles so their centers are not closer than circle_distance from each other. diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp index 25c29d273..106da4a78 100644 --- a/src/libslic3r/Thread.cpp +++ b/src/libslic3r/Thread.cpp @@ -188,7 +188,8 @@ std::optional<std::string> get_current_thread_name() #endif // _WIN32 // Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID. -void name_tbb_thread_pool_threads() +// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator. +void name_tbb_thread_pool_threads_set_locale() { static bool initialized = false; if (initialized) @@ -233,6 +234,21 @@ void name_tbb_thread_pool_threads() std::ostringstream name; name << "slic3r_tbb_" << range.begin(); set_current_thread_name(name.str().c_str()); + // Set locales of the worker thread to "C". +#ifdef _WIN32 + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + std::setlocale(LC_ALL, "C"); +#else + // We are leaking some memory here, because the newlocale() produced memory will never be released. + // This is not a problem though, as there will be a maximum one worker thread created per physical thread. + uselocale(newlocale( +#ifdef __APPLE__ + LC_ALL_MASK +#else // some Unix / Linux / BSD + LC_ALL +#endif + , "C", nullptr)); +#endif } }); } diff --git a/src/libslic3r/Thread.hpp b/src/libslic3r/Thread.hpp index a86123796..9afe13b42 100644 --- a/src/libslic3r/Thread.hpp +++ b/src/libslic3r/Thread.hpp @@ -33,7 +33,8 @@ std::optional<std::string> get_current_thread_name(); // To be called somewhere before the TBB threads are spinned for the first time, to // give them names recognizible in the debugger. -void name_tbb_thread_pool_threads(); +// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator. +void name_tbb_thread_pool_threads_set_locale(); template<class Fn> inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn) |