Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_reload_from_disk_changes

This commit is contained in:
enricoturri1966 2021-09-14 10:05:18 +02:00
commit a74f3e3fc0
66 changed files with 3959 additions and 633 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -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; }

View file

@ -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)) {

View file

@ -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;
};
}

View file

@ -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]

View file

@ -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_

View file

@ -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);

View file

@ -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

View file

@ -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_ */

View file

@ -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(); }

View file

@ -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(); }

View file

@ -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();

View file

@ -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);

View file

@ -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)

View file

@ -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; }

View file

@ -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

View file

@ -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.

View file

@ -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
}
});
}

View file

@ -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)