Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorenricoturri1966 <enricoturri@seznam.cz>2021-09-14 11:05:18 +0300
committerenricoturri1966 <enricoturri@seznam.cz>2021-09-14 11:05:18 +0300
commita74f3e3fc0e49f217c89d4388db5b04d89de2e13 (patch)
treecc9c02edbf0691cff9a6df48a11eda2a6fd9ba44 /src/libslic3r
parent37c9e207785567744f874f8729dc014239c53775 (diff)
parentec976cbe0539c8dbb6c3adcd0f6fe15a1b389492 (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.cpp14
-rw-r--r--src/libslic3r/ClipperUtils.hpp5
-rw-r--r--src/libslic3r/ExPolygon.cpp2
-rw-r--r--src/libslic3r/Fill/FillAdaptive.cpp4
-rw-r--r--src/libslic3r/GCode.cpp140
-rw-r--r--src/libslic3r/GCode.hpp31
-rw-r--r--src/libslic3r/GCode/CoolingBuffer.cpp65
-rw-r--r--src/libslic3r/GCode/CoolingBuffer.hpp23
-rw-r--r--src/libslic3r/GCode/SpiralVase.cpp2
-rw-r--r--src/libslic3r/GCode/SpiralVase.hpp10
-rw-r--r--src/libslic3r/GCodeReader.cpp11
-rw-r--r--src/libslic3r/GCodeWriter.cpp269
-rw-r--r--src/libslic3r/GCodeWriter.hpp89
-rw-r--r--src/libslic3r/MultiPoint.hpp4
-rw-r--r--src/libslic3r/Polygon.hpp8
-rw-r--r--src/libslic3r/Polyline.cpp14
-rw-r--r--src/libslic3r/Polyline.hpp6
-rw-r--r--src/libslic3r/Print.cpp4
-rw-r--r--src/libslic3r/SLA/Pad.cpp2
-rw-r--r--src/libslic3r/SLAPrint.cpp2
-rw-r--r--src/libslic3r/SupportMaterial.cpp2
-rw-r--r--src/libslic3r/Thread.cpp18
-rw-r--r--src/libslic3r/Thread.hpp3
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 &ltp : 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()), &ltp == &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> &current_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 &center);
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)