Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_reload_from_disk_changes
This commit is contained in:
commit
a74f3e3fc0
2449
src/fast_float/fast_float.h
Normal file
2449
src/fast_float/fast_float.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/pipeline.h>
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
@ -1099,7 +1100,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
m_volumetric_speed = DoExport::autospeed_volumetric_limit(print);
|
||||
print.throw_if_canceled();
|
||||
|
||||
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
|
||||
if (print.config().spiral_vase.value)
|
||||
m_spiral_vase = make_unique<SpiralVase>(print.config());
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
@ -1212,6 +1212,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
||||
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
|
||||
m_cooling_buffer->set_current_extruder(initial_extruder_id);
|
||||
|
||||
// Emit machine envelope limits for the Marlin firmware.
|
||||
@ -1219,7 +1220,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
// Disable fan.
|
||||
if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
|
||||
file.write(m_writer.set_fan(0, true));
|
||||
file.write(m_writer.set_fan(0));
|
||||
|
||||
// Let the start-up script prime the 1st printing tool.
|
||||
m_placeholder_parser.set("initial_tool", initial_extruder_id);
|
||||
@ -1334,17 +1335,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
file.writeln(between_objects_gcode);
|
||||
}
|
||||
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
|
||||
m_cooling_buffer->reset();
|
||||
m_cooling_buffer->reset(this->writer().get_position());
|
||||
m_cooling_buffer->set_current_extruder(initial_extruder_id);
|
||||
// Pair the object layers with the support layers by z, extrude them.
|
||||
std::vector<LayerToPrint> layers_to_print = collect_layers_to_print(object);
|
||||
for (const LayerToPrint <p : layers_to_print) {
|
||||
std::vector<LayerToPrint> lrs;
|
||||
lrs.emplace_back(std::move(ltp));
|
||||
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), <p == &layers_to_print.back(),
|
||||
nullptr, *print_object_instance_sequential_active - object.instances().data());
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file);
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
@ -1401,14 +1397,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
// Extrude the layers.
|
||||
for (auto &layer : layers_to_print) {
|
||||
const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
|
||||
if (m_wipe_tower && layer_tools.has_wipe_tower)
|
||||
m_wipe_tower->next_layer();
|
||||
this->process_layer(file, print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1));
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file);
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
@ -1420,7 +1412,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
// Write end commands to file.
|
||||
file.write(this->retract());
|
||||
file.write(m_writer.set_fan(false));
|
||||
file.write(m_writer.set_fan(0));
|
||||
|
||||
// adds tag for processor
|
||||
file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
@ -1481,6 +1473,88 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
|
||||
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
void GCode::process_layers(
|
||||
const Print &print,
|
||||
const ToolOrdering &tool_ordering,
|
||||
const std::vector<const PrintInstance*> &print_object_instances_ordering,
|
||||
const std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> &layers_to_print,
|
||||
GCodeOutputStream &output_stream)
|
||||
{
|
||||
// The pipeline is variable: The vase mode filter is optional.
|
||||
size_t layer_to_print_idx = 0;
|
||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(tbb::filter::serial_in_order,
|
||||
[this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
const std::pair<coordf_t, std::vector<LayerToPrint>>& layer = layers_to_print[layer_to_print_idx++];
|
||||
const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first);
|
||||
if (m_wipe_tower && layer_tools.has_wipe_tower)
|
||||
m_wipe_tower->next_layer();
|
||||
print.throw_if_canceled();
|
||||
return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1));
|
||||
}
|
||||
});
|
||||
const auto spiral_vase = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(tbb::filter::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in) -> GCode::LayerResult {
|
||||
spiral_vase.enable(in.spiral_vase_enable);
|
||||
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||
});
|
||||
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(tbb::filter::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(tbb::filter::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
);
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
||||
}
|
||||
|
||||
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
void GCode::process_layers(
|
||||
const Print &print,
|
||||
const ToolOrdering &tool_ordering,
|
||||
std::vector<LayerToPrint> layers_to_print,
|
||||
const size_t single_object_idx,
|
||||
GCodeOutputStream &output_stream)
|
||||
{
|
||||
// The pipeline is fixed: Neither wipe tower nor vase mode are implemented for sequential print.
|
||||
size_t layer_to_print_idx = 0;
|
||||
tbb::parallel_pipeline(12,
|
||||
tbb::make_filter<void, GCode::LayerResult>(
|
||||
tbb::filter::serial_in_order,
|
||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
|
||||
print.throw_if_canceled();
|
||||
return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx);
|
||||
}
|
||||
}) &
|
||||
tbb::make_filter<GCode::LayerResult, std::string>(
|
||||
tbb::filter::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
}) &
|
||||
tbb::make_filter<std::string, void>(
|
||||
tbb::filter::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
));
|
||||
}
|
||||
|
||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||
{
|
||||
try {
|
||||
@ -1890,9 +1964,7 @@ namespace Skirt {
|
||||
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
|
||||
// For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths
|
||||
// and performing the extruder specific extrusions together.
|
||||
void GCode::process_layer(
|
||||
// Write into the output file.
|
||||
GCodeOutputStream &file,
|
||||
GCode::LayerResult GCode::process_layer(
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
const std::vector<LayerToPrint> &layers,
|
||||
@ -1908,11 +1980,6 @@ void GCode::process_layer(
|
||||
// Either printing all copies of all objects, or just a single copy of a single object.
|
||||
assert(single_object_instance_idx == size_t(-1) || layers.size() == 1);
|
||||
|
||||
if (layer_tools.extruders.empty())
|
||||
// Nothing to extrude.
|
||||
return;
|
||||
|
||||
// Extract 1st object_layer and support_layer of this set of layers with an equal print_z.
|
||||
const Layer *object_layer = nullptr;
|
||||
const SupportLayer *support_layer = nullptr;
|
||||
for (const LayerToPrint &l : layers) {
|
||||
@ -1922,6 +1989,12 @@ void GCode::process_layer(
|
||||
support_layer = l.support_layer;
|
||||
}
|
||||
const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer;
|
||||
GCode::LayerResult result { {}, layer.id(), false, last_layer };
|
||||
if (layer_tools.extruders.empty())
|
||||
// Nothing to extrude.
|
||||
return result;
|
||||
|
||||
// Extract 1st object_layer and support_layer of this set of layers with an equal print_z.
|
||||
coordf_t print_z = layer.print_z;
|
||||
bool first_layer = layer.id() == 0;
|
||||
unsigned int first_extruder_id = layer_tools.extruders.front();
|
||||
@ -1943,7 +2016,7 @@ void GCode::process_layer(
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_spiral_vase->enable(enable);
|
||||
result.spiral_vase_enable = enable;
|
||||
// If we're going to apply spiralvase to this layer, disable loop clipping.
|
||||
m_enable_loop_clipping = !enable;
|
||||
}
|
||||
@ -2285,6 +2358,7 @@ void GCode::process_layer(
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Apply spiral vase post-processing if this layer contains suitable geometry
|
||||
// (we must feed all the G-code into the post-processor, including the first
|
||||
// bottom non-spiral layers otherwise it will mess with positions)
|
||||
@ -2308,8 +2382,14 @@ void GCode::process_layer(
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
file.write(gcode);
|
||||
#endif
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
|
||||
log_memory_info();
|
||||
|
||||
result.gcode = std::move(gcode);
|
||||
result.cooling_buffer_flush = object_layer || last_layer;
|
||||
return result;
|
||||
}
|
||||
|
||||
void GCode::apply_print_config(const PrintConfig &print_config)
|
||||
|
@ -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; }
|
||||
|
@ -16,19 +16,25 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_extruder(0)
|
||||
CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0)
|
||||
{
|
||||
this->reset();
|
||||
this->reset(gcodegen.writer().get_position());
|
||||
|
||||
const std::vector<Extruder> &extruders = gcodegen.writer().extruders();
|
||||
m_extruder_ids.reserve(extruders.size());
|
||||
for (const Extruder &ex : extruders) {
|
||||
m_num_extruders = std::max(ex.id() + 1, m_num_extruders);
|
||||
m_extruder_ids.emplace_back(ex.id());
|
||||
}
|
||||
}
|
||||
|
||||
void CoolingBuffer::reset()
|
||||
void CoolingBuffer::reset(const Vec3d &position)
|
||||
{
|
||||
m_current_pos.assign(5, 0.f);
|
||||
Vec3d pos = m_gcodegen.writer().get_position();
|
||||
m_current_pos[0] = float(pos(0));
|
||||
m_current_pos[1] = float(pos(1));
|
||||
m_current_pos[2] = float(pos(2));
|
||||
m_current_pos[4] = float(m_gcodegen.config().travel_speed.value);
|
||||
m_current_pos[0] = float(position.x());
|
||||
m_current_pos[1] = float(position.y());
|
||||
m_current_pos[2] = float(position.z());
|
||||
m_current_pos[4] = float(m_config.travel_speed.value);
|
||||
}
|
||||
|
||||
struct CoolingLine
|
||||
@ -303,30 +309,23 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b
|
||||
// Return the list of parsed lines, bucketed by an extruder.
|
||||
std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector<float> ¤t_pos) const
|
||||
{
|
||||
const FullPrintConfig &config = m_gcodegen.config();
|
||||
const std::vector<Extruder> &extruders = m_gcodegen.writer().extruders();
|
||||
unsigned int num_extruders = 0;
|
||||
for (const Extruder &ex : extruders)
|
||||
num_extruders = std::max(ex.id() + 1, num_extruders);
|
||||
|
||||
std::vector<PerExtruderAdjustments> per_extruder_adjustments(extruders.size());
|
||||
std::vector<size_t> map_extruder_to_per_extruder_adjustment(num_extruders, 0);
|
||||
for (size_t i = 0; i < extruders.size(); ++ i) {
|
||||
std::vector<PerExtruderAdjustments> per_extruder_adjustments(m_extruder_ids.size());
|
||||
std::vector<size_t> map_extruder_to_per_extruder_adjustment(m_num_extruders, 0);
|
||||
for (size_t i = 0; i < m_extruder_ids.size(); ++ i) {
|
||||
PerExtruderAdjustments &adj = per_extruder_adjustments[i];
|
||||
unsigned int extruder_id = extruders[i].id();
|
||||
unsigned int extruder_id = m_extruder_ids[i];
|
||||
adj.extruder_id = extruder_id;
|
||||
adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id);
|
||||
adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id));
|
||||
adj.min_print_speed = float(config.min_print_speed.get_at(extruder_id));
|
||||
adj.cooling_slow_down_enabled = m_config.cooling.get_at(extruder_id);
|
||||
adj.slowdown_below_layer_time = float(m_config.slowdown_below_layer_time.get_at(extruder_id));
|
||||
adj.min_print_speed = float(m_config.min_print_speed.get_at(extruder_id));
|
||||
map_extruder_to_per_extruder_adjustment[extruder_id] = i;
|
||||
}
|
||||
|
||||
const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix();
|
||||
unsigned int current_extruder = m_current_extruder;
|
||||
PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
|
||||
const char *line_start = gcode.c_str();
|
||||
const char *line_end = line_start;
|
||||
const char extrusion_axis = get_extrusion_axis(config)[0];
|
||||
const char extrusion_axis = get_extrusion_axis(m_config)[0];
|
||||
// Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
|
||||
// for a sequence of extrusion moves.
|
||||
size_t active_speed_modifier = size_t(-1);
|
||||
@ -387,7 +386,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
||||
}
|
||||
if ((line.type & CoolingLine::TYPE_G92) == 0) {
|
||||
// G0 or G1. Calculate the duration.
|
||||
if (config.use_relative_e_distances.value)
|
||||
if (m_config.use_relative_e_distances.value)
|
||||
// Reset extruder accumulator.
|
||||
current_pos[3] = 0.f;
|
||||
float dif[4];
|
||||
@ -430,8 +429,8 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
||||
} else if (boost::starts_with(sline, ";_EXTRUDE_END")) {
|
||||
line.type = CoolingLine::TYPE_EXTRUDE_END;
|
||||
active_speed_modifier = size_t(-1);
|
||||
} else if (boost::starts_with(sline, toolchange_prefix)) {
|
||||
unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size());
|
||||
} else if (boost::starts_with(sline, m_toolchange_prefix)) {
|
||||
unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + m_toolchange_prefix.size());
|
||||
// Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored.
|
||||
if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) {
|
||||
if (new_extruder != current_extruder) {
|
||||
@ -641,7 +640,7 @@ float CoolingBuffer::calculate_layer_slowdown(std::vector<PerExtruderAdjustments
|
||||
if (total > slowdown_below_layer_time) {
|
||||
// The current total time is above the minimum threshold of the rest of the extruders, don't adjust anything.
|
||||
} else {
|
||||
// Adjust this and all the following (higher config.slowdown_below_layer_time) extruders.
|
||||
// Adjust this and all the following (higher m_config.slowdown_below_layer_time) extruders.
|
||||
// Sum maximum slow down time as if everything was slowed down including the external perimeters.
|
||||
float max_time = elapsed_time_total0;
|
||||
for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
|
||||
@ -694,8 +693,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
bool bridge_fan_control = false;
|
||||
int bridge_fan_speed = 0;
|
||||
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed ]() {
|
||||
const FullPrintConfig &config = m_gcodegen.config();
|
||||
#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder)
|
||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
|
||||
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
|
||||
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
|
||||
int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers);
|
||||
@ -737,13 +735,12 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
}
|
||||
if (fan_speed_new != fan_speed) {
|
||||
fan_speed = fan_speed_new;
|
||||
new_gcode += m_gcodegen.writer().set_fan(fan_speed);
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, fan_speed);
|
||||
}
|
||||
};
|
||||
|
||||
const char *pos = gcode.c_str();
|
||||
int current_feedrate = 0;
|
||||
const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix();
|
||||
change_extruder_set_fan();
|
||||
for (const CoolingLine *line : lines) {
|
||||
const char *line_start = gcode.c_str() + line->line_start;
|
||||
@ -751,7 +748,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
if (line_start > pos)
|
||||
new_gcode.append(pos, line_start - pos);
|
||||
if (line->type & CoolingLine::TYPE_SET_TOOL) {
|
||||
unsigned int new_extruder = (unsigned int)atoi(line_start + toolchange_prefix.size());
|
||||
unsigned int new_extruder = (unsigned int)atoi(line_start + m_toolchange_prefix.size());
|
||||
if (new_extruder != m_current_extruder) {
|
||||
m_current_extruder = new_extruder;
|
||||
change_extruder_set_fan();
|
||||
@ -759,10 +756,10 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
new_gcode.append(line_start, line_end - line_start);
|
||||
} else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) {
|
||||
if (bridge_fan_control)
|
||||
new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true);
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed);
|
||||
} else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_END) {
|
||||
if (bridge_fan_control)
|
||||
new_gcode += m_gcodegen.writer().set_fan(fan_speed, true);
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, fan_speed);
|
||||
} else if (line->type & CoolingLine::TYPE_EXTRUDE_END) {
|
||||
// Just remove this comment.
|
||||
} else if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE | CoolingLine::TYPE_HAS_F)) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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_
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -33,7 +33,9 @@ public:
|
||||
void rotate(double angle, const Point ¢er);
|
||||
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
|
||||
|
||||
const Point& first_point() const { return this->points.front(); }
|
||||
const Point& front() const { return this->points.front(); }
|
||||
const Point& back() const { return this->points.back(); }
|
||||
const Point& first_point() const { return this->front(); }
|
||||
virtual const Point& last_point() const = 0;
|
||||
virtual Lines lines() const = 0;
|
||||
size_t size() const { return points.size(); }
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -92,7 +92,7 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
|
||||
for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) {
|
||||
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
|
||||
}
|
||||
polylines = intersection_pl(polylines, (Polygons)bed_polygon);
|
||||
polylines = intersection_pl(polylines, bed_polygon);
|
||||
|
||||
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
|
||||
for (auto pl : polylines)
|
||||
|
@ -409,6 +409,11 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
set_render_color(color);
|
||||
}
|
||||
|
||||
void GLVolume::set_color(const std::array<float, 4>& rgba)
|
||||
{
|
||||
color = rgba;
|
||||
}
|
||||
|
||||
void GLVolume::set_render_color(float r, float g, float b, float a)
|
||||
{
|
||||
render_color = { r, g, b, a };
|
||||
@ -458,8 +463,9 @@ void GLVolume::set_render_color()
|
||||
render_color[3] = color[3];
|
||||
}
|
||||
|
||||
void GLVolume::set_color_from_model_volume(const ModelVolume& model_volume)
|
||||
std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume)
|
||||
{
|
||||
std::array<float, 4> color;
|
||||
if (model_volume.is_negative_volume()) {
|
||||
color[0] = 0.2f;
|
||||
color[1] = 0.2f;
|
||||
@ -481,6 +487,7 @@ void GLVolume::set_color_from_model_volume(const ModelVolume& model_volume)
|
||||
color[2] = 1.0f;
|
||||
}
|
||||
color[3] = model_volume.is_model_part() ? 1.f : 0.5f;
|
||||
return color;
|
||||
}
|
||||
|
||||
Transform3d GLVolume::world_matrix() const
|
||||
@ -635,7 +642,7 @@ int GLVolumeCollection::load_object_volume(
|
||||
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
|
||||
this->volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *this->volumes.back();
|
||||
v.set_color_from_model_volume(*model_volume);
|
||||
v.set_color(color_from_model_volume(*model_volume));
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
v.indexed_vertex_array.load_mesh(mesh, true);
|
||||
#else
|
||||
|
@ -39,6 +39,10 @@ class ModelObject;
|
||||
class ModelVolume;
|
||||
enum ModelInstanceEPrintVolumeState : unsigned char;
|
||||
|
||||
// Return appropriate color based on the ModelVolume.
|
||||
std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume);
|
||||
|
||||
|
||||
// A container for interleaved arrays of 3D vertices and normals,
|
||||
// possibly indexed by triangles and / or quads.
|
||||
class GLIndexedVertexArray {
|
||||
@ -393,6 +397,7 @@ public:
|
||||
return out;
|
||||
}
|
||||
|
||||
void set_color(const std::array<float, 4>& rgba);
|
||||
void set_render_color(float r, float g, float b, float a);
|
||||
void set_render_color(const std::array<float, 4>& rgba);
|
||||
// Sets render color in dependence of current state
|
||||
|
@ -122,7 +122,9 @@ void CopyrightsDialog::fill_entries()
|
||||
{ "AppImage packaging for Linux using AppImageKit"
|
||||
, "2004-2019 Simon Peter and contributors" , "https://appimage.org/" },
|
||||
{ "lib_fts"
|
||||
, "Forrest Smith" , "https://www.forrestthewoods.com/" }
|
||||
, "Forrest Smith" , "https://www.forrestthewoods.com/" },
|
||||
{ "fast_float"
|
||||
, "Daniel Lemire, João Paulo Magalhaes and contributors", "https://github.com/fastfloat/fast_float" }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -209,8 +209,8 @@ void BackgroundSlicingProcess::process_sla()
|
||||
|
||||
void BackgroundSlicingProcess::thread_proc()
|
||||
{
|
||||
set_current_thread_name("slic3r_BgSlcPcs");
|
||||
name_tbb_thread_pool_threads();
|
||||
set_current_thread_name("slic3r_BgSlcPcs");
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
assert(m_print != nullptr);
|
||||
assert(m_print == m_fff_print || m_print == m_sla_print);
|
||||
|
@ -171,23 +171,14 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
"- Detect bridging perimeters"));
|
||||
if (is_global_config)
|
||||
msg_text += "\n\n" + _(L("Shall I adjust those settings for supports?"));
|
||||
//wxMessageDialog dialog(nullptr, msg_text, _(L("Support Generator")),
|
||||
MessageDialog dialog(nullptr, msg_text, _(L("Support Generator")),
|
||||
wxICON_WARNING | (is_global_config ? wxYES | wxNO | wxCANCEL : wxOK));
|
||||
MessageDialog dialog(nullptr, msg_text, _L("Support Generator"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
auto answer = dialog.ShowModal();
|
||||
if (!is_global_config || answer == wxID_YES) {
|
||||
// Enable "detect bridging perimeters".
|
||||
new_conf.set_key_value("overhangs", new ConfigOptionBool(true));
|
||||
}
|
||||
else if (answer == wxID_NO) {
|
||||
// Do nothing, leave supports on and "detect bridging perimeters" off.
|
||||
}
|
||||
else if (answer == wxID_CANCEL) {
|
||||
// Disable supports.
|
||||
new_conf.set_key_value("support_material", new ConfigOptionBool(false));
|
||||
support_material_overhangs_queried = false;
|
||||
}
|
||||
//else Do nothing, leave supports on and "detect bridging perimeters" off.
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
}
|
||||
|
@ -197,6 +197,17 @@ wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRec
|
||||
labelRect.SetWidth(labelRect.GetWidth() - bmp_width);
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Case when from some reason we try to create next EditorCtrl till old one was not deleted
|
||||
if (auto children = parent->GetChildren(); children.GetCount() > 0)
|
||||
for (auto child : children)
|
||||
if (dynamic_cast<wxTextCtrl*>(child)) {
|
||||
parent->RemoveChild(child);
|
||||
child->Destroy();
|
||||
break;
|
||||
}
|
||||
#endif // __WXMSW__
|
||||
|
||||
wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(),
|
||||
position, labelRect.GetSize(), wxTE_PROCESS_ENTER);
|
||||
text_editor->SetInsertionPointEnd();
|
||||
|
@ -284,7 +284,7 @@ void GCodeViewer::SequentialView::Marker::render() const
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends)
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends)
|
||||
{
|
||||
assert(! m_file.is_open());
|
||||
if (m_file.is_open())
|
||||
@ -563,6 +563,8 @@ GCodeViewer::GCodeViewer()
|
||||
set_toolpath_move_type_visible(EMoveType::Extrude, true);
|
||||
#endif // !ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
m_extrusions.reset_role_visibility_flags();
|
||||
|
||||
// m_sequential_view.skip_invisible_moves = true;
|
||||
}
|
||||
|
||||
@ -577,7 +579,9 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
|
||||
// release gpu memory, if used
|
||||
reset();
|
||||
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename,
|
||||
// Stealing out lines_ends should be safe because this gcode_result is processed only once (see the 1st if in this function).
|
||||
std::move(const_cast<std::vector<size_t>&>(gcode_result.lines_ends)));
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
@ -734,7 +738,6 @@ void GCodeViewer::reset()
|
||||
m_extruder_ids = std::vector<unsigned char>();
|
||||
m_filament_diameters = std::vector<float>();
|
||||
m_filament_densities = std::vector<float>();
|
||||
m_extrusions.reset_role_visibility_flags();
|
||||
m_extrusions.reset_ranges();
|
||||
m_shells.volumes.clear();
|
||||
m_layers.reset();
|
||||
|
@ -702,7 +702,7 @@ public:
|
||||
public:
|
||||
GCodeWindow() = default;
|
||||
~GCodeWindow() { stop_mapping_file(); }
|
||||
void load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends);
|
||||
void load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends);
|
||||
void reset() {
|
||||
stop_mapping_file();
|
||||
m_lines_ends.clear();
|
||||
|
@ -1845,7 +1845,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
volume->extruder_id = extruder_id;
|
||||
|
||||
volume->is_modifier = !mvs->model_volume->is_model_part();
|
||||
volume->set_color_from_model_volume(*mvs->model_volume);
|
||||
volume->set_color(color_from_model_volume(*mvs->model_volume));
|
||||
|
||||
// updates volumes transformations
|
||||
volume->set_instance_transformation(mvs->model_volume->get_object()->instances[mvs->composite_id.instance_id]->get_transformation());
|
||||
@ -2092,7 +2092,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
|
||||
vol_old.finalize_geometry(gl_initialized);
|
||||
}
|
||||
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
|
||||
|
||||
@ -2100,10 +2100,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
|
||||
m_gcode_viewer.update_shells_color_by_extruder(m_config);
|
||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
m_gcode_viewer.refresh(gcode_result, str_tool_colors);
|
||||
set_as_dirty();
|
||||
request_extra_frame();
|
||||
|
@ -722,8 +722,7 @@ public:
|
||||
|
||||
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
|
||||
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result);
|
||||
void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_gcode_preview_render_paths();
|
||||
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
|
||||
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
|
||||
|
@ -383,6 +383,7 @@ void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, cons
|
||||
|
||||
// the following line messes up the popup size the first time it is shown on wxWidgets 3.1.3
|
||||
// comboCtrl->EnablePopupAnimation(false);
|
||||
popup->SetFont(comboCtrl->GetFont());
|
||||
comboCtrl->SetPopupControl(popup);
|
||||
wxString title = from_u8(text);
|
||||
max_width = std::max(max_width, 60 + comboCtrl->GetTextExtent(title).x);
|
||||
|
@ -958,6 +958,8 @@ bool GUI_App::on_init_inner()
|
||||
// update_mode(); // !!! do that later
|
||||
SetTopWindow(mainframe);
|
||||
|
||||
plater_->init_notification_manager();
|
||||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
if (is_gcode_viewer()) {
|
||||
@ -2278,7 +2280,7 @@ wxBookCtrlBase* GUI_App::tab_panel() const
|
||||
return mainframe->m_tabpanel;
|
||||
}
|
||||
|
||||
NotificationManager* GUI_App::notification_manager()
|
||||
std::shared_ptr<NotificationManager> GUI_App::notification_manager()
|
||||
{
|
||||
return plater_->get_notification_manager();
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ public:
|
||||
ObjectLayers* obj_layers();
|
||||
Plater* plater();
|
||||
Model& model();
|
||||
NotificationManager* notification_manager();
|
||||
std::shared_ptr<NotificationManager> notification_manager();
|
||||
|
||||
// Parameters extracted from the command line to be passed to GUI after initialization.
|
||||
GUI_InitParams* init_params { nullptr };
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||
#ifdef __APPLE__
|
||||
#include "wx/dcclient.h"
|
||||
#include "slic3r/Utils/MacDarkMode.hpp"
|
||||
#endif
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@ -211,7 +215,6 @@ static int GetSelectedChoices( wxArrayInt& selections,
|
||||
const wxString& caption,
|
||||
const wxArrayString& choices)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wxMultiChoiceDialog dialog(nullptr, message, caption, choices);
|
||||
wxGetApp().UpdateDlgDarkUI(&dialog);
|
||||
|
||||
@ -219,6 +222,39 @@ static int GetSelectedChoices( wxArrayInt& selections,
|
||||
// deselects the first item which is selected by default
|
||||
dialog.SetSelections(selections);
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Improvements for ChoiceListBox: Height of control will restect to items count
|
||||
for (auto child : dialog.GetChildren())
|
||||
if (dynamic_cast<wxListBox*>(child) && !choices.IsEmpty()) {
|
||||
wxClientDC dc(child);
|
||||
|
||||
int height = dc.GetTextExtent(choices[0]).y;
|
||||
int width = 0;
|
||||
for (const auto& string : choices)
|
||||
width = std::max(width, dc.GetTextExtent(string).x);
|
||||
|
||||
// calculate best size of ListBox
|
||||
height += 3 * mac_max_scaling_factor(); // extend height by margins
|
||||
width += 3 * height; // extend width by checkbox width and margins
|
||||
|
||||
// don't make the listbox too tall (limit height to around 10 items)
|
||||
// but don't make it too small neither
|
||||
int list_height = wxMax(height * wxMin(wxMax(choices.Count(), 3), 10), 70);
|
||||
wxSize sz_best = wxSize(width, list_height);
|
||||
|
||||
wxSize sz = child->GetSize();
|
||||
child->SetMinSize(sz_best);
|
||||
|
||||
// extend Dialog size, if calculated best size of ListBox is bigger then its size
|
||||
wxSize dlg_sz = dialog.GetSize();
|
||||
if (int delta_x = sz_best.x - sz.x; delta_x > 0) dlg_sz.x += delta_x;
|
||||
if (int delta_y = sz_best.y - sz.y; delta_y > 0) dlg_sz.y += delta_y;
|
||||
dialog.SetSize(dlg_sz);
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
{
|
||||
// NB: intentionally do not clear the selections array here, the caller
|
||||
@ -229,9 +265,6 @@ static int GetSelectedChoices( wxArrayInt& selections,
|
||||
|
||||
selections = dialog.GetSelections();
|
||||
return static_cast<int>(selections.GetCount());
|
||||
#else
|
||||
return wxGetSelectedChoices(selections, message, caption, choices);
|
||||
#endif
|
||||
}
|
||||
|
||||
static wxMenu* create_settings_popupmenu(wxMenu* parent_menu, const bool is_object_settings, wxDataViewItem item/*, ModelConfig& config*/)
|
||||
|
@ -734,9 +734,9 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
|
||||
double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices);
|
||||
if( bottom_area - top_area > delta_area) {
|
||||
NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||
std::shared_ptr<NotificationManager> notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||
notif_mngr->push_notification(
|
||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification,
|
||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
|
||||
_u8L("Apply auto color change to print"),
|
||||
[this](wxEvtHandler*) {
|
||||
@ -943,8 +943,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
||||
m_canvas->set_selected_extruder(0);
|
||||
if (gcode_preview_data_valid) {
|
||||
// Load the real G-code preview.
|
||||
m_canvas->load_gcode_preview(*m_gcode_result);
|
||||
m_canvas->refresh_gcode_preview(*m_gcode_result, colors);
|
||||
m_canvas->load_gcode_preview(*m_gcode_result, colors);
|
||||
m_left_sizer->Show(m_bottom_toolbar_panel);
|
||||
m_left_sizer->Layout();
|
||||
Refresh();
|
||||
|
@ -22,7 +22,7 @@ static inline void show_notification_extruders_limit_exceeded()
|
||||
wxGetApp()
|
||||
.plater()
|
||||
->get_notification_manager()
|
||||
->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::RegularNotification,
|
||||
->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
GUI::format(_L("Your printer has more extruders than the multi-material painting gizmo supports. For this reason, only the "
|
||||
"first %1% extruders will be able to be used for painting."), GLGizmoMmuSegmentation::EXTRUDERS_LIMIT));
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ void GLGizmoSimplify::on_set_state()
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
_u8L("ERROR: Wait until Simplification ends or Cancel process."));
|
||||
return;
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ void InstancesHider::on_update()
|
||||
canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst);
|
||||
canvas->set_use_clipping_planes(true);
|
||||
// Some objects may be sinking, do not show whatever is below the bed.
|
||||
canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), 0.));
|
||||
canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), std::numeric_limits<double>::max()));
|
||||
|
||||
|
||||
@ -164,12 +164,7 @@ void InstancesHider::on_update()
|
||||
m_clippers.clear();
|
||||
for (const TriangleMesh* mesh : meshes) {
|
||||
m_clippers.emplace_back(new MeshClipper);
|
||||
if (mo->get_instance_min_z(active_inst) < SINKING_Z_THRESHOLD)
|
||||
m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), 0.));
|
||||
else {
|
||||
m_clippers.back()->set_plane(ClippingPlane::ClipsNothing());
|
||||
m_clippers.back()->set_limiting_plane(ClippingPlane::ClipsNothing());
|
||||
}
|
||||
m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
m_clippers.back()->set_mesh(*mesh);
|
||||
}
|
||||
m_old_meshes = meshes;
|
||||
@ -218,8 +213,16 @@ void InstancesHider::render_cut() const
|
||||
clipper->set_limiting_plane(ClippingPlane::ClipsNothing());
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glColor3f(0.8f, 0.3f, 0.0f));
|
||||
if (mv->is_model_part())
|
||||
glsafe(::glColor3f(0.8f, 0.3f, 0.0f));
|
||||
else {
|
||||
const std::array<float, 4>& c = color_from_model_volume(*mv);
|
||||
glsafe(::glColor4f(c[0], c[1], c[2], c[3]));
|
||||
}
|
||||
glsafe(::glPushAttrib(GL_DEPTH_TEST));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
clipper->render_cut();
|
||||
glsafe(::glPopAttrib());
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
++clipper_id;
|
||||
@ -385,8 +388,6 @@ void ObjectClipper::on_update()
|
||||
|
||||
m_active_inst_bb_radius =
|
||||
mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
|
||||
//if (has_hollowed && m_clp_ratio != 0.)
|
||||
// m_clp_ratio = 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,7 +408,6 @@ void ObjectClipper::render_cut() const
|
||||
const SelectionInfo* sel_info = get_pool()->selection_info();
|
||||
const ModelObject* mo = sel_info->model_object();
|
||||
Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
|
||||
const bool sinking = mo->bounding_box().min.z() < SINKING_Z_THRESHOLD;
|
||||
|
||||
size_t clipper_id = 0;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
@ -418,9 +418,7 @@ void ObjectClipper::render_cut() const
|
||||
auto& clipper = m_clippers[clipper_id];
|
||||
clipper->set_plane(*m_clp);
|
||||
clipper->set_transformation(trafo);
|
||||
clipper->set_limiting_plane(sinking ?
|
||||
ClippingPlane(Vec3d::UnitZ(), 0.)
|
||||
: ClippingPlane::ClipsNothing());
|
||||
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
|
||||
clipper->render_cut();
|
||||
|
@ -194,7 +194,7 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
|
||||
if (get_current_type() != type && get_current_type() != Undefined) {
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
_u8L("ERROR: Please close all manipulators available from "
|
||||
"the left toolbar first"));
|
||||
return false;
|
||||
@ -1256,7 +1256,7 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const
|
||||
if (error_notification)
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(
|
||||
NotificationType::QuitSLAManualMode,
|
||||
NotificationManager::NotificationLevel::ErrorNotification,
|
||||
NotificationManager::NotificationLevel::ErrorNotificationLevel,
|
||||
_u8L("You are currently editing SLA support points. Please, "
|
||||
"apply or discard your changes first."));
|
||||
|
||||
|
@ -1012,7 +1012,7 @@ void NotificationManager::HintNotification::retrieve_data(bool new_hint/* = true
|
||||
if(hint_data != nullptr)
|
||||
{
|
||||
NotificationData nd { NotificationType::DidYouKnowHint,
|
||||
NotificationLevel::RegularNotification,
|
||||
NotificationLevel::RegularNotificationLevel,
|
||||
0,
|
||||
hint_data->text,
|
||||
hint_data->hypertext, nullptr,
|
||||
|
@ -9,6 +9,7 @@ namespace Slic3r {
|
||||
class ModelInstance;
|
||||
|
||||
namespace GUI {
|
||||
class NotificationManager;
|
||||
|
||||
class ArrangeJob : public PlaterJob
|
||||
{
|
||||
@ -39,8 +40,8 @@ protected:
|
||||
void process() override;
|
||||
|
||||
public:
|
||||
ArrangeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
ArrangeJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
|
||||
: PlaterJob{nm, plater}
|
||||
{}
|
||||
|
||||
int status_range() const override
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class Plater;
|
||||
class NotificationManager;
|
||||
|
||||
class FillBedJob : public PlaterJob
|
||||
{
|
||||
@ -27,8 +28,8 @@ protected:
|
||||
void process() override;
|
||||
|
||||
public:
|
||||
FillBedJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
FillBedJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
|
||||
: PlaterJob{nm, plater}
|
||||
{}
|
||||
|
||||
int status_range() const override
|
||||
|
@ -2,9 +2,11 @@
|
||||
#include <exception>
|
||||
|
||||
#include "Job.hpp"
|
||||
#include "../NotificationManager.hpp"
|
||||
#include <libslic3r/Thread.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void GUI::Job::run(std::exception_ptr &eptr)
|
||||
@ -30,8 +32,8 @@ void GUI::Job::update_status(int st, const wxString &msg)
|
||||
wxQueueEvent(this, evt);
|
||||
}
|
||||
|
||||
GUI::Job::Job(std::shared_ptr<ProgressIndicator> pri)
|
||||
: m_progress(std::move(pri))
|
||||
GUI::Job::Job(std::shared_ptr<NotificationManager> nm)
|
||||
: m_notifications(nm)
|
||||
{
|
||||
m_thread_evt_id = wxNewId();
|
||||
|
||||
@ -40,21 +42,21 @@ GUI::Job::Job(std::shared_ptr<ProgressIndicator> pri)
|
||||
|
||||
auto msg = evt.GetString();
|
||||
if (!msg.empty() && !m_worker_error)
|
||||
m_progress->set_status_text(msg.ToUTF8().data());
|
||||
m_notifications->progress_indicator_set_status_text(msg.ToUTF8().data());
|
||||
|
||||
if (m_finalized) return;
|
||||
|
||||
m_progress->set_progress(evt.GetInt());
|
||||
m_notifications->progress_indicator_set_progress(evt.GetInt());
|
||||
if (evt.GetInt() == status_range() || m_worker_error) {
|
||||
// set back the original range and cancel callback
|
||||
m_progress->set_range(m_range);
|
||||
m_progress->set_cancel_callback();
|
||||
m_notifications->progress_indicator_set_range(m_range);
|
||||
m_notifications->progress_indicator_set_cancel_callback();
|
||||
wxEndBusyCursor();
|
||||
|
||||
if (m_worker_error) {
|
||||
m_finalized = true;
|
||||
m_progress->set_status_text("");
|
||||
m_progress->set_progress(m_range);
|
||||
m_notifications->progress_indicator_set_status_text("");
|
||||
m_notifications->progress_indicator_set_progress(m_range);
|
||||
on_exception(m_worker_error);
|
||||
}
|
||||
else {
|
||||
@ -86,12 +88,12 @@ void GUI::Job::start()
|
||||
prepare();
|
||||
|
||||
// Save the current status indicatior range and push the new one
|
||||
m_range = m_progress->get_range();
|
||||
m_progress->set_range(status_range());
|
||||
m_range = m_notifications->progress_indicator_get_range();
|
||||
m_notifications->progress_indicator_set_range(status_range());
|
||||
|
||||
// init cancellation flag and set the cancel callback
|
||||
m_canceled.store(false);
|
||||
m_progress->set_cancel_callback(
|
||||
m_notifications->progress_indicator_set_cancel_callback(
|
||||
[this]() { m_canceled.store(true); });
|
||||
|
||||
m_finalized = false;
|
||||
|
@ -8,14 +8,13 @@
|
||||
|
||||
#include <slic3r/GUI/I18N.hpp>
|
||||
|
||||
#include "ProgressIndicator.hpp"
|
||||
|
||||
#include <wx/event.h>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class NotificationManager;
|
||||
// A class to handle UI jobs like arranging and optimizing rotation.
|
||||
// These are not instant jobs, the user has to be informed about their
|
||||
// state in the status progress indicator. On the other hand they are
|
||||
@ -33,7 +32,7 @@ class Job : public wxEvtHandler
|
||||
boost::thread m_thread;
|
||||
std::atomic<bool> m_running{false}, m_canceled{false};
|
||||
bool m_finalized = false, m_finalizing = false;
|
||||
std::shared_ptr<ProgressIndicator> m_progress;
|
||||
std::shared_ptr<NotificationManager> m_notifications;
|
||||
std::exception_ptr m_worker_error = nullptr;
|
||||
|
||||
void run(std::exception_ptr &);
|
||||
@ -65,7 +64,7 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
Job(std::shared_ptr<ProgressIndicator> pri);
|
||||
Job(std::shared_ptr<NotificationManager> nm);
|
||||
|
||||
bool is_finalized() const { return m_finalized; }
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class Plater;
|
||||
class NotificationManager;
|
||||
|
||||
class PlaterJob : public Job {
|
||||
protected:
|
||||
@ -15,8 +16,8 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
PlaterJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater):
|
||||
Job{std::move(pri)}, m_plater{plater} {}
|
||||
PlaterJob(std::shared_ptr<NotificationManager> nm, Plater *plater):
|
||||
Job{nm}, m_plater{plater} {}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
@ -10,6 +10,8 @@ namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class NotificationManager;
|
||||
|
||||
class RotoptimizeJob : public PlaterJob
|
||||
{
|
||||
using FindFn = std::function<Vec2d(const ModelObject & mo,
|
||||
@ -52,8 +54,8 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
RotoptimizeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
RotoptimizeJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
|
||||
: PlaterJob{nm, plater}
|
||||
{}
|
||||
|
||||
void finalize() override;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
@ -124,8 +125,8 @@ public:
|
||||
priv(Plater *plt) : plater{plt} {}
|
||||
};
|
||||
|
||||
SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}, p{std::make_unique<priv>(plater)}
|
||||
SLAImportJob::SLAImportJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
|
||||
: PlaterJob{nm, plater}, p{std::make_unique<priv>(plater)}
|
||||
{}
|
||||
|
||||
SLAImportJob::~SLAImportJob() = default;
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class NotificationManager;
|
||||
|
||||
class SLAImportJob : public PlaterJob {
|
||||
class priv;
|
||||
|
||||
@ -16,7 +18,7 @@ protected:
|
||||
void finalize() override;
|
||||
|
||||
public:
|
||||
SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater);
|
||||
SLAImportJob(std::shared_ptr<NotificationManager> nm, Plater *plater);
|
||||
~SLAImportJob();
|
||||
|
||||
void reset();
|
||||
|
@ -155,13 +155,13 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
SetIcon(main_frame_icon(wxGetApp().get_app_mode()));
|
||||
|
||||
// initialize status bar
|
||||
m_statusbar = std::make_shared<ProgressStatusBar>(this);
|
||||
m_statusbar->set_font(GUI::wxGetApp().normal_font());
|
||||
if (wxGetApp().is_editor())
|
||||
m_statusbar->embed(this);
|
||||
m_statusbar->set_status_text(_L("Version") + " " +
|
||||
SLIC3R_VERSION + " - " +
|
||||
_L("Remember to check for updates at https://github.com/prusa3d/PrusaSlicer/releases"));
|
||||
// m_statusbar = std::make_shared<ProgressStatusBar>(this);
|
||||
// m_statusbar->set_font(GUI::wxGetApp().normal_font());
|
||||
// if (wxGetApp().is_editor())
|
||||
// m_statusbar->embed(this);
|
||||
// m_statusbar->set_status_text(_L("Version") + " " +
|
||||
// SLIC3R_VERSION + " - " +
|
||||
// _L("Remember to check for updates at https://github.com/prusa3d/PrusaSlicer/releases"));
|
||||
|
||||
// initialize tabpanel and menubar
|
||||
init_tabpanel();
|
||||
@ -1033,7 +1033,7 @@ void MainFrame::on_sys_color_changed()
|
||||
wxGetApp().init_label_colours();
|
||||
#ifdef __WXMSW__
|
||||
wxGetApp().UpdateDarkUI(m_tabpanel);
|
||||
m_statusbar->update_dark_ui();
|
||||
// m_statusbar->update_dark_ui();
|
||||
#ifdef _MSW_DARK_MODE
|
||||
// update common mode sizer
|
||||
if (!wxGetApp().tabs_as_menu())
|
||||
|
@ -204,7 +204,7 @@ public:
|
||||
wxWindow* m_plater_page{ nullptr };
|
||||
wxProgressDialog* m_progress_dialog { nullptr };
|
||||
PrintHostQueueDialog* m_printhost_queue_dlg;
|
||||
std::shared_ptr<ProgressStatusBar> m_statusbar;
|
||||
// std::shared_ptr<ProgressStatusBar> m_statusbar;
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::unique_ptr<wxTaskBarIcon> m_taskbar_icon;
|
||||
|
@ -33,32 +33,32 @@ wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClic
|
||||
wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent);
|
||||
|
||||
const NotificationManager::NotificationData NotificationManager::basic_notifications[] = {
|
||||
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
|
||||
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
|
||||
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotificationLevel, 10, _u8L("3D Mouse disconnected.") },
|
||||
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("Configuration update is available."), _u8L("See more."),
|
||||
[](wxEvtHandler* evnthndlr) {
|
||||
if (evnthndlr != nullptr)
|
||||
wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
|
||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotificationLevel, 10,
|
||||
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
||||
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
||||
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
|
||||
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotificationLevel, 10,
|
||||
_u8L("No color change event was added to the print. The print does not look like a sign.") },
|
||||
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
|
||||
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotificationLevel, 10,
|
||||
_u8L("Desktop integration was successful.") },
|
||||
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
|
||||
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10,
|
||||
_u8L("Desktop integration failed.") },
|
||||
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
|
||||
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotificationLevel, 10,
|
||||
_u8L("Undo desktop integration was successful.") },
|
||||
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
|
||||
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10,
|
||||
_u8L("Undo desktop integration failed.") },
|
||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
|
||||
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotificationLevel, 20, _u8L("Loading of model has Failed") },
|
||||
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotificationLevel, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -255,13 +255,13 @@ bool NotificationManager::PopNotification::push_background_color()
|
||||
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
return true;
|
||||
}
|
||||
if (m_data.level == NotificationLevel::ErrorNotification) {
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel) {
|
||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||
backcolor.x += 0.3f;
|
||||
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
return true;
|
||||
}
|
||||
if (m_data.level == NotificationLevel::WarningNotification) {
|
||||
if (m_data.level == NotificationLevel::WarningNotificationLevel) {
|
||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||
backcolor.x += 0.3f;
|
||||
backcolor.y += 0.15f;
|
||||
@ -276,9 +276,9 @@ void NotificationManager::PopNotification::count_spaces()
|
||||
m_line_height = ImGui::CalcTextSize("A").y;
|
||||
|
||||
m_left_indentation = m_line_height;
|
||||
if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::WarningNotificationLevel) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
m_left_indentation = picture_width + m_line_height / 2;
|
||||
}
|
||||
@ -505,9 +505,9 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
||||
|
||||
void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
|
||||
{
|
||||
if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::WarningNotificationLevel) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
ImGui::SetCursorPosX(m_line_height / 3);
|
||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||
imgui.text(text.c_str());
|
||||
@ -583,19 +583,23 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
|
||||
|
||||
int64_t now = GLCanvas3D::timestamp_now();
|
||||
|
||||
// reset fade opacity for non-closing notifications or hover during fading
|
||||
if (m_state != EState::FadingOut && m_state != EState::ClosePending && m_state != EState::Finished) {
|
||||
m_current_fade_opacity = 1.0f;
|
||||
}
|
||||
|
||||
// reset timers - hovered state is set in render
|
||||
if (m_state == EState::Hovered) {
|
||||
m_current_fade_opacity = 1.0f;
|
||||
m_state = EState::Unknown;
|
||||
init();
|
||||
// Timers when not fading
|
||||
} else if (m_state != EState::NotFading && m_state != EState::FadingOut && m_data.duration != 0 && !paused) {
|
||||
} else if (m_state != EState::NotFading && m_state != EState::FadingOut && get_duration() != 0 && !paused) {
|
||||
int64_t up_time = now - m_notification_start;
|
||||
if (up_time >= m_data.duration * 1000) {
|
||||
if (up_time >= get_duration() * 1000) {
|
||||
m_state = EState::FadingOut;
|
||||
m_fading_start = now;
|
||||
} else {
|
||||
m_next_render = m_data.duration * 1000 - up_time;
|
||||
m_next_render = get_duration() * 1000 - up_time;
|
||||
}
|
||||
}
|
||||
// Timers when fading
|
||||
@ -626,53 +630,6 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
|
||||
return false;
|
||||
}
|
||||
|
||||
NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) :
|
||||
NotificationManager::PopNotification(n, id_provider, evt_handler)
|
||||
{
|
||||
set_large(large);
|
||||
}
|
||||
void NotificationManager::SlicingCompleteLargeNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
if (!m_is_large)
|
||||
PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
else {
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str());
|
||||
float x_offset = m_left_indentation;
|
||||
std::string fulltext = m_text1 + m_hypertext + m_text2;
|
||||
ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
|
||||
float cursor_y = win_size.y / 2 - text_size.y / 2;
|
||||
if (m_has_print_info) {
|
||||
x_offset = 20;
|
||||
cursor_y = win_size.y / 2 + win_size.y / 6 - text_size.y / 2;
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
imgui.text(m_print_info.c_str());
|
||||
cursor_y = win_size.y / 2 - win_size.y / 6 - text_size.y / 2;
|
||||
}
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
imgui.text(m_text1.c_str());
|
||||
|
||||
render_hypertext(imgui, x_offset + text1_size.x + 4, cursor_y, m_hypertext);
|
||||
}
|
||||
}
|
||||
void NotificationManager::SlicingCompleteLargeNotification::set_print_info(const std::string &info)
|
||||
{
|
||||
m_print_info = info;
|
||||
m_has_print_info = true;
|
||||
if (m_is_large)
|
||||
m_lines_count = 2;
|
||||
}
|
||||
void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
|
||||
{
|
||||
m_is_large = l;
|
||||
//FIXME this information should not be lost (change m_data?)
|
||||
// m_counting_down = !l;
|
||||
m_hypertext = l ? _u8L("Export G-Code.") : std::string();
|
||||
m_state = l ? EState::Shown : EState::Hidden;
|
||||
init();
|
||||
}
|
||||
//---------------ExportFinishedNotification-----------
|
||||
void NotificationManager::ExportFinishedNotification::count_spaces()
|
||||
{
|
||||
@ -680,9 +637,9 @@ void NotificationManager::ExportFinishedNotification::count_spaces()
|
||||
m_line_height = ImGui::CalcTextSize("A").y;
|
||||
|
||||
m_left_indentation = m_line_height;
|
||||
if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::WarningNotificationLevel) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
m_left_indentation = picture_width + m_line_height / 2;
|
||||
}
|
||||
@ -793,6 +750,9 @@ void NotificationManager::ProgressBarNotification::init()
|
||||
{
|
||||
PopNotification::init();
|
||||
//m_lines_count++;
|
||||
if (m_endlines.empty()) {
|
||||
m_endlines.push_back(0);
|
||||
}
|
||||
if(m_lines_count >= 2) {
|
||||
m_lines_count = 3;
|
||||
m_multiline = true;
|
||||
@ -897,8 +857,17 @@ void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgu
|
||||
ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0));
|
||||
ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0));
|
||||
ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (m_current_fade_opacity * 255.f)), m_line_height * 0.2f);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (m_current_fade_opacity * 255.f)), m_line_height * 0.2f);
|
||||
if (m_render_percentage) {
|
||||
std::string text;
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "%";
|
||||
text = stream.str();
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
}
|
||||
//------PrintHostUploadNotification----------------
|
||||
void NotificationManager::PrintHostUploadNotification::init()
|
||||
@ -915,7 +884,7 @@ void NotificationManager::PrintHostUploadNotification::count_spaces()
|
||||
m_left_indentation = m_line_height;
|
||||
if (m_uj_state == UploadJobState::PB_ERROR) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
m_left_indentation = picture_width + m_line_height / 2;
|
||||
}
|
||||
@ -1102,15 +1071,382 @@ void NotificationManager::UpdatedItemsInfoNotification::render_left_sign(ImGuiWr
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
|
||||
//------SlicingProgressNotificastion
|
||||
void NotificationManager::SlicingProgressNotification::init()
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_PROGRESS) {
|
||||
ProgressBarNotification::init();
|
||||
//if (m_state == EState::NotFading && m_percentage >= 1.0f)
|
||||
// m_state = EState::Shown;
|
||||
}
|
||||
else {
|
||||
PopNotification::init();
|
||||
}
|
||||
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_progress_state(float percent)
|
||||
{
|
||||
if (percent < 0.f)
|
||||
set_progress_state(SlicingProgressState::SP_CANCELLED);
|
||||
else if (percent >= 1.f)
|
||||
set_progress_state(SlicingProgressState::SP_COMPLETED);
|
||||
else
|
||||
set_progress_state(SlicingProgressState::SP_PROGRESS, percent);
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_progress_state(NotificationManager::SlicingProgressNotification::SlicingProgressState state, float percent/* = 0.f*/)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_NO_SLICING:
|
||||
m_state = EState::Hidden;
|
||||
set_percentage(-1);
|
||||
m_has_print_info = false;
|
||||
set_export_possible(false);
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_PROGRESS:
|
||||
set_percentage(percent);
|
||||
m_has_cancel_button = true;
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_CANCELLED:
|
||||
set_percentage(-1);
|
||||
m_has_cancel_button = false;
|
||||
m_has_print_info = false;
|
||||
set_export_possible(false);
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_COMPLETED:
|
||||
set_percentage(1);
|
||||
m_has_cancel_button = false;
|
||||
m_has_print_info = false;
|
||||
// m_export_possible is important only for PROGRESS state, thus we can reset it here
|
||||
set_export_possible(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_sp_state = state;
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_status_text(const std::string& text)
|
||||
{
|
||||
switch (m_sp_state)
|
||||
{
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_NO_SLICING:
|
||||
m_state = EState::Hidden;
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_PROGRESS:
|
||||
{
|
||||
NotificationData data{ NotificationType::SlicingProgress, NotificationLevel::ProgressBarNotificationLevel, 0, text + ".", m_is_fff ? _u8L("Export G-Code.") : _u8L("Export.") };
|
||||
update(data);
|
||||
m_state = EState::NotFading;
|
||||
}
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_CANCELLED:
|
||||
{
|
||||
NotificationData data{ NotificationType::SlicingProgress, NotificationLevel::ProgressBarNotificationLevel, 0, text };
|
||||
update(data);
|
||||
m_state = EState::Shown;
|
||||
}
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_COMPLETED:
|
||||
{
|
||||
NotificationData data{ NotificationType::SlicingProgress, NotificationLevel::ProgressBarNotificationLevel, 0, _u8L("Slicing finished."), m_is_fff ? _u8L("Export G-Code.") : _u8L("Export.") };
|
||||
update(data);
|
||||
m_state = EState::Shown;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_print_info(const std::string& info)
|
||||
{
|
||||
if (m_sp_state != SlicingProgressState::SP_COMPLETED) {
|
||||
set_progress_state (SlicingProgressState::SP_COMPLETED);
|
||||
} else {
|
||||
m_has_print_info = true;
|
||||
m_print_info = info;
|
||||
}
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_sidebar_collapsed(bool collapsed)
|
||||
{
|
||||
m_sidebar_collapsed = collapsed;
|
||||
if (m_sp_state == SlicingProgressState::SP_COMPLETED)
|
||||
m_state = EState::Shown;
|
||||
}
|
||||
|
||||
void NotificationManager::SlicingProgressNotification::on_cancel_button()
|
||||
{
|
||||
if (m_cancel_callback){
|
||||
m_cancel_callback();
|
||||
}
|
||||
}
|
||||
int NotificationManager::SlicingProgressNotification::get_duration()
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_CANCELLED)
|
||||
return 10;
|
||||
else if (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)
|
||||
return 5;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
bool NotificationManager::SlicingProgressNotification::update_state(bool paused, const int64_t delta)
|
||||
{
|
||||
bool ret = ProgressBarNotification::update_state(paused, delta);
|
||||
// sets Estate to hidden
|
||||
if (get_state() == PopNotification::EState::ClosePending || get_state() == PopNotification::EState::Finished)
|
||||
set_progress_state(SlicingProgressState::SP_NO_SLICING);
|
||||
return ret;
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_PROGRESS || (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)) {
|
||||
ProgressBarNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
/* // enable for hypertext during slicing (correct call of export_enabled needed)
|
||||
if (m_multiline) {
|
||||
// two lines text, one line bar
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(m_line_height / 4);
|
||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(m_line_height + m_line_height / 4);
|
||||
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||
imgui.text(line.c_str());
|
||||
if (m_sidebar_collapsed && m_sp_state == SlicingProgressState::SP_PROGRESS && m_export_possible) {
|
||||
ImVec2 text_size = ImGui::CalcTextSize(line.c_str());
|
||||
render_hypertext(imgui, m_left_indentation + text_size.x + 4, m_line_height + m_line_height / 4, m_hypertext);
|
||||
}
|
||||
if (m_has_cancel_button)
|
||||
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
else {
|
||||
//one line text, one line bar
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(m_line_height / 4);
|
||||
std::string line = m_text1.substr(0, m_endlines[0]);
|
||||
imgui.text(line.c_str());
|
||||
if (m_sidebar_collapsed && m_sp_state == SlicingProgressState::SP_PROGRESS && m_export_possible) {
|
||||
ImVec2 text_size = ImGui::CalcTextSize(line.c_str());
|
||||
render_hypertext(imgui, m_left_indentation + text_size.x + 4, m_line_height / 4, m_hypertext);
|
||||
}
|
||||
if (m_has_cancel_button)
|
||||
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
*/
|
||||
} else if (m_sp_state == SlicingProgressState::SP_COMPLETED) {
|
||||
// "Slicing Finished" on line 1 + hypertext, print info on line
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str());
|
||||
float x_offset = m_left_indentation;
|
||||
std::string fulltext = m_text1 + m_hypertext + m_text2;
|
||||
ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
|
||||
float cursor_y = win_size.y / 2 - text_size.y / 2;
|
||||
if (m_sidebar_collapsed && m_has_print_info) {
|
||||
x_offset = 20;
|
||||
cursor_y = win_size.y / 2 + win_size.y / 6 - text_size.y / 2;
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
imgui.text(m_print_info.c_str());
|
||||
cursor_y = win_size.y / 2 - win_size.y / 6 - text_size.y / 2;
|
||||
}
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
imgui.text(m_text1.c_str());
|
||||
if (m_sidebar_collapsed)
|
||||
render_hypertext(imgui, x_offset + text1_size.x + 4, cursor_y, m_hypertext);
|
||||
} else {
|
||||
PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
if (!(m_sp_state == SlicingProgressState::SP_PROGRESS || (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed))) {
|
||||
return;
|
||||
}
|
||||
//std::string text;
|
||||
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
/*
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "%";
|
||||
text = stream.str();
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
|
||||
imgui.text(text.c_str());
|
||||
*/
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CancelButton;
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
|
||||
ImVec2(win_pos.x, win_pos.y + win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::CancelHoverButton;
|
||||
}
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
on_cancel_button();
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.125, win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)))
|
||||
{
|
||||
on_cancel_button();
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void NotificationManager::SlicingProgressNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
// Do not render close button while showing progress - cancel button is rendered instead
|
||||
if (m_sp_state != SlicingProgressState::SP_PROGRESS) {
|
||||
ProgressBarNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
}
|
||||
//------ProgressIndicatorNotification-------
|
||||
void NotificationManager::ProgressIndicatorNotification::set_status_text(const char* text)
|
||||
{
|
||||
NotificationData data{ NotificationType::ProgressIndicator, NotificationLevel::ProgressBarNotificationLevel, 0, text };
|
||||
update(data);
|
||||
}
|
||||
|
||||
void NotificationManager::ProgressIndicatorNotification::init()
|
||||
{
|
||||
// skip ProgressBarNotification::init (same code here)
|
||||
PopNotification::init();
|
||||
if (m_endlines.empty()) {
|
||||
m_endlines.push_back(0);
|
||||
}
|
||||
if (m_lines_count >= 2) {
|
||||
m_lines_count = 3;
|
||||
m_multiline = true;
|
||||
while (m_endlines.size() < 3)
|
||||
m_endlines.push_back(m_endlines.back());
|
||||
}
|
||||
else {
|
||||
m_lines_count = 2;
|
||||
m_endlines.push_back(m_endlines.back());
|
||||
}
|
||||
switch (m_progress_state)
|
||||
{
|
||||
case Slic3r::GUI::NotificationManager::ProgressIndicatorNotification::ProgressIndicatorState::PIS_HIDDEN:
|
||||
m_state = EState::Hidden;
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::ProgressIndicatorNotification::ProgressIndicatorState::PIS_PROGRESS_REQUEST:
|
||||
case Slic3r::GUI::NotificationManager::ProgressIndicatorNotification::ProgressIndicatorState::PIS_PROGRESS_UPDATED:
|
||||
m_state = EState::NotFading;
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::ProgressIndicatorNotification::ProgressIndicatorState::PIS_COMPLETED:
|
||||
m_state = EState::Shown;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void NotificationManager::ProgressIndicatorNotification::set_percentage(float percent)
|
||||
{
|
||||
ProgressBarNotification::set_percentage(percent);
|
||||
if (percent >= 0.0f && percent < 1.0f) {
|
||||
m_state = EState::NotFading;
|
||||
m_has_cancel_button = true;
|
||||
m_progress_state = ProgressIndicatorState::PIS_PROGRESS_REQUEST;
|
||||
} else if (percent >= 1.0f) {
|
||||
m_state = EState::Shown;
|
||||
m_progress_state = ProgressIndicatorState::PIS_COMPLETED;
|
||||
m_has_cancel_button = false;
|
||||
} else {
|
||||
m_progress_state = ProgressIndicatorState::PIS_HIDDEN;
|
||||
m_state = EState::Hidden;
|
||||
}
|
||||
}
|
||||
bool NotificationManager::ProgressIndicatorNotification::update_state(bool paused, const int64_t delta)
|
||||
{
|
||||
if (m_progress_state == ProgressIndicatorState::PIS_PROGRESS_REQUEST) {
|
||||
// percentage was changed (and it called schedule_extra_frame), now update must know this needs render
|
||||
m_next_render = 0;
|
||||
m_progress_state = ProgressIndicatorState::PIS_PROGRESS_UPDATED;
|
||||
return true;
|
||||
}
|
||||
bool ret = ProgressBarNotification::update_state(paused, delta);
|
||||
if (get_state() == PopNotification::EState::ClosePending || get_state() == PopNotification::EState::Finished)
|
||||
// go to PIS_HIDDEN state
|
||||
set_percentage(-1.0f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void NotificationManager::ProgressIndicatorNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CancelButton;
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
|
||||
ImVec2(win_pos.x, win_pos.y + win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::CancelHoverButton;
|
||||
}
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
on_cancel_button();
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.125, win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)))
|
||||
{
|
||||
on_cancel_button();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
void NotificationManager::ProgressIndicatorNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
// Do not render close button while showing progress - cancel button is rendered instead
|
||||
if (m_percentage >= 1.0f)
|
||||
{
|
||||
ProgressBarNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
}
|
||||
//------NotificationManager--------
|
||||
NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
|
||||
m_evt_handler(evt_handler)
|
||||
{
|
||||
}
|
||||
NotificationManager::~NotificationManager()
|
||||
{
|
||||
HintDatabase::get_instance().uninit();
|
||||
}
|
||||
|
||||
void NotificationManager::push_notification(const NotificationType type, int timestamp)
|
||||
{
|
||||
auto it = std::find_if(std::begin(basic_notifications), std::end(basic_notifications),
|
||||
@ -1121,7 +1457,7 @@ void NotificationManager::push_notification(const NotificationType type, int tim
|
||||
}
|
||||
void NotificationManager::push_notification(const std::string& text, int timestamp)
|
||||
{
|
||||
push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, timestamp);
|
||||
push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotificationLevel, 10, text }, timestamp);
|
||||
}
|
||||
|
||||
void NotificationManager::push_notification(NotificationType type,
|
||||
@ -1133,11 +1469,11 @@ void NotificationManager::push_notification(NotificationType type,
|
||||
{
|
||||
int duration = 0;
|
||||
switch (level) {
|
||||
case NotificationLevel::RegularNotification: duration = 10; break;
|
||||
case NotificationLevel::ErrorNotification: break;
|
||||
case NotificationLevel::WarningNotification: break;
|
||||
case NotificationLevel::ImportantNotification: break;
|
||||
case NotificationLevel::ProgressBarNotification: break;
|
||||
case NotificationLevel::RegularNotificationLevel: duration = 10; break;
|
||||
case NotificationLevel::ErrorNotificationLevel: break;
|
||||
case NotificationLevel::WarningNotificationLevel: break;
|
||||
case NotificationLevel::ImportantNotificationLevel: break;
|
||||
case NotificationLevel::ProgressBarNotificationLevel: break;
|
||||
default:
|
||||
assert(false);
|
||||
return;
|
||||
@ -1146,18 +1482,19 @@ void NotificationManager::push_notification(NotificationType type,
|
||||
}
|
||||
void NotificationManager::push_validate_error_notification(const std::string& text)
|
||||
{
|
||||
push_notification_data({ NotificationType::ValidateError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
push_notification_data({ NotificationType::ValidateError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
void NotificationManager::push_slicing_error_notification(const std::string& text)
|
||||
{
|
||||
set_all_slicing_errors_gray(false);
|
||||
push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
close_notification_of_type(NotificationType::SlicingComplete);
|
||||
push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
set_slicing_progress_hidden();
|
||||
}
|
||||
void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, ObjectID oid, int warning_step)
|
||||
{
|
||||
NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text };
|
||||
NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotificationLevel, 0, _u8L("WARNING:") + "\n" + text };
|
||||
|
||||
auto notification = std::make_unique<NotificationManager::SlicingWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
notification->object_id = oid;
|
||||
@ -1168,7 +1505,7 @@ void NotificationManager::push_slicing_warning_notification(const std::string& t
|
||||
}
|
||||
void NotificationManager::push_plater_error_notification(const std::string& text)
|
||||
{
|
||||
push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
}
|
||||
|
||||
void NotificationManager::close_plater_error_notification(const std::string& text)
|
||||
@ -1192,7 +1529,7 @@ void NotificationManager::push_plater_warning_notification(const std::string& te
|
||||
}
|
||||
}
|
||||
|
||||
NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text };
|
||||
NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotificationLevel, 0, _u8L("WARNING:") + "\n" + text };
|
||||
|
||||
auto notification = std::make_unique<NotificationManager::PlaterWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
push_notification_data(std::move(notification), 0);
|
||||
@ -1250,42 +1587,13 @@ void NotificationManager::close_slicing_error_notification(const std::string& te
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::push_slicing_complete_notification(int timestamp, bool large)
|
||||
void NotificationManager::push_object_warning_notification(const std::string& text, ObjectID object_id, const std::string& hypertext/* = ""*/, std::function<bool(wxEvtHandler*)> callback/* = std::function<bool(wxEvtHandler*)>()*/)
|
||||
{
|
||||
std::string hypertext;
|
||||
int time = 10;
|
||||
if (has_slicing_error_notification())
|
||||
return;
|
||||
if (large) {
|
||||
hypertext = _u8L("Export G-Code.");
|
||||
time = 0;
|
||||
}
|
||||
NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext,
|
||||
[](wxEvtHandler* evnthndlr){
|
||||
if (evnthndlr != nullptr)
|
||||
wxPostEvent(evnthndlr, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
push_notification_data(std::make_unique<NotificationManager::SlicingCompleteLargeNotification>(data, m_id_provider, m_evt_handler, large), timestamp);
|
||||
}
|
||||
void NotificationManager::set_slicing_complete_print_time(const std::string &info)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingComplete) {
|
||||
dynamic_cast<SlicingCompleteLargeNotification*>(notification.get())->set_print_info(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_slicing_complete_large(bool large)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingComplete) {
|
||||
dynamic_cast<SlicingCompleteLargeNotification*>(notification.get())->set_large(large);
|
||||
break;
|
||||
}
|
||||
}
|
||||
NotificationData data{ NotificationType::ObjectWarning, NotificationLevel::WarningNotificationLevel, 0, text, hypertext, callback };
|
||||
auto notification = std::make_unique<NotificationManager::SlicingWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
notification->object_id = object_id;
|
||||
notification->warning_step = 0;
|
||||
push_notification_data(std::move(notification), 0);
|
||||
}
|
||||
void NotificationManager::close_notification_of_type(const NotificationType type)
|
||||
{
|
||||
@ -1304,10 +1612,19 @@ void NotificationManager::remove_slicing_warnings_of_released_objects(const std:
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
void NotificationManager::remove_object_warnings_of_released_objects(const std::vector<ObjectID>& living_oids)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications)
|
||||
if (notification->get_type() == NotificationType::ObjectWarning) {
|
||||
if (!std::binary_search(living_oids.begin(), living_oids.end(),
|
||||
static_cast<SlicingWarningNotification*>(notification.get())->object_id))
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
void NotificationManager::push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable)
|
||||
{
|
||||
close_notification_of_type(NotificationType::ExportFinished);
|
||||
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path };
|
||||
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotificationLevel, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path };
|
||||
push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0);
|
||||
}
|
||||
|
||||
@ -1321,7 +1638,7 @@ void NotificationManager::push_upload_job_notification(int id, float filesize,
|
||||
}
|
||||
}
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 10, text };
|
||||
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, text };
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize), 0);
|
||||
}
|
||||
void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
|
||||
@ -1363,6 +1680,153 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::init_slicing_progress_notification(std::function<void()> cancel_callback)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
dynamic_cast<SlicingProgressNotification*>(notification.get())->set_cancel_callback(cancel_callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
NotificationData data{ NotificationType::SlicingProgress, NotificationLevel::ProgressBarNotificationLevel, 0, std::string(),std::string(),
|
||||
[](wxEvtHandler* evnthndlr) {
|
||||
if (evnthndlr != nullptr)
|
||||
wxPostEvent(evnthndlr, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
push_notification_data(std::make_unique<NotificationManager::SlicingProgressNotification>(data, m_id_provider, m_evt_handler, cancel_callback), 0);
|
||||
}
|
||||
void NotificationManager::set_slicing_progress_percentage(const std::string& text, float percentage)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
|
||||
spn->set_progress_state(percentage);
|
||||
spn->set_status_text(text);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
|
||||
wxGetApp().plater()->init_notification_manager();
|
||||
}
|
||||
|
||||
void NotificationManager::set_slicing_progress_hidden()
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
SlicingProgressNotification* notif = dynamic_cast<SlicingProgressNotification*>(notification.get());
|
||||
notif->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_NO_SLICING);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
|
||||
wxGetApp().plater()->init_notification_manager();
|
||||
}
|
||||
void NotificationManager::set_slicing_complete_print_time(const std::string& info, bool sidebar_colapsed)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
dynamic_cast<SlicingProgressNotification*>(notification.get())->set_sidebar_collapsed(sidebar_colapsed);
|
||||
dynamic_cast<SlicingProgressNotification*>(notification.get())->set_print_info(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_sidebar_collapsed(bool collapsed)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
dynamic_cast<SlicingProgressNotification*>(notification.get())->set_sidebar_collapsed(collapsed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_fff(bool fff)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
dynamic_cast<SlicingProgressNotification*>(notification.get())->set_fff(fff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_slicing_progress_export_possible()
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
dynamic_cast<SlicingProgressNotification*>(notification.get())->set_export_possible(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::init_progress_indicator()
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressIndicator) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
NotificationData data{ NotificationType::ProgressIndicator, NotificationLevel::ProgressBarNotificationLevel, 2};
|
||||
auto notification = std::make_unique<NotificationManager::ProgressIndicatorNotification>(data, m_id_provider, m_evt_handler);
|
||||
push_notification_data(std::move(notification), 0);
|
||||
}
|
||||
|
||||
void NotificationManager::progress_indicator_set_range(int range)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressIndicator) {
|
||||
dynamic_cast<ProgressIndicatorNotification*>(notification.get())->set_range(range);
|
||||
return;
|
||||
}
|
||||
}
|
||||
init_progress_indicator();
|
||||
}
|
||||
void NotificationManager::progress_indicator_set_cancel_callback(CancelFn callback/* = CancelFn()*/)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressIndicator) {
|
||||
dynamic_cast<ProgressIndicatorNotification*>(notification.get())->set_cancel_callback(callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
init_progress_indicator();
|
||||
}
|
||||
void NotificationManager::progress_indicator_set_progress(int pr)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressIndicator) {
|
||||
dynamic_cast<ProgressIndicatorNotification*>(notification.get())->set_progress(pr);
|
||||
// Ask for rendering - needs to be done on every progress. Calls to here doesnt trigger IDLE event or rendering.
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
init_progress_indicator();
|
||||
}
|
||||
void NotificationManager::progress_indicator_set_status_text(const char* text)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressIndicator) {
|
||||
dynamic_cast<ProgressIndicatorNotification*>(notification.get())->set_status_text(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
init_progress_indicator();
|
||||
}
|
||||
int NotificationManager::progress_indicator_get_range() const
|
||||
{
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressIndicator) {
|
||||
return dynamic_cast<ProgressIndicatorNotification*>(notification.get())->get_range();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NotificationManager::push_hint_notification(bool open_next)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
@ -1372,7 +1836,7 @@ void NotificationManager::push_hint_notification(bool open_next)
|
||||
}
|
||||
}
|
||||
|
||||
NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 300, "" };
|
||||
NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::HintNotificationLevel, 300, "" };
|
||||
// from user - open now
|
||||
if (!open_next) {
|
||||
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), 0);
|
||||
@ -1380,8 +1844,8 @@ void NotificationManager::push_hint_notification(bool open_next)
|
||||
// at startup - delay for half a second to let other notification pop up, than try every 30 seconds
|
||||
// show only if no notifications are shown
|
||||
} else {
|
||||
auto condition = [this]() {
|
||||
return this->get_notification_count() == 0;
|
||||
auto condition = [&self = std::as_const(*this)]() {
|
||||
return self.get_notification_count() == 0;
|
||||
};
|
||||
push_delayed_notification(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), condition, 500, 30000);
|
||||
}
|
||||
@ -1395,7 +1859,10 @@ bool NotificationManager::is_hint_notification_open()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotificationManager::deactivate_loaded_hints()
|
||||
{
|
||||
HintDatabase::get_instance().uninit();
|
||||
}
|
||||
void NotificationManager::push_updated_item_info_notification(InfoItemType type)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
@ -1405,7 +1872,7 @@ void NotificationManager::push_updated_item_info_notification(InfoItemType type)
|
||||
}
|
||||
}
|
||||
|
||||
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::RegularNotification, 5, "" };
|
||||
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::RegularNotificationLevel, 5, "" };
|
||||
auto notification = std::make_unique<NotificationManager::UpdatedItemsInfoNotification>(data, m_id_provider, m_evt_handler, type);
|
||||
if (push_notification_data(std::move(notification), 0)) {
|
||||
(dynamic_cast<UpdatedItemsInfoNotification*>(m_pop_notifications.back().get()))->add_type(type);
|
||||
@ -1427,17 +1894,20 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
|
||||
}
|
||||
}
|
||||
|
||||
GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D();
|
||||
|
||||
bool retval = false;
|
||||
if (this->activate_existing(notification.get())) {
|
||||
m_pop_notifications.back()->update(notification->get_data());
|
||||
canvas.schedule_extra_frame(0);
|
||||
return false;
|
||||
if (m_initialized) { // ignore update action - it cant be initialized if canvas and imgui context is not ready
|
||||
m_pop_notifications.back()->update(notification->get_data());
|
||||
}
|
||||
} else {
|
||||
m_pop_notifications.emplace_back(std::move(notification));
|
||||
canvas.schedule_extra_frame(0);
|
||||
return true;
|
||||
retval = true;
|
||||
}
|
||||
if (!m_initialized)
|
||||
return retval;
|
||||
GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D();
|
||||
canvas.schedule_extra_frame(0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void NotificationManager::push_delayed_notification(std::unique_ptr<NotificationManager::PopNotification> notification, std::function<bool(void)> condition_callback, int64_t initial_delay, int64_t delay_interval)
|
||||
@ -1591,6 +2061,8 @@ void NotificationManager::set_in_preview(bool preview)
|
||||
notification->hide(preview);
|
||||
if (notification->get_type() == NotificationType::SignDetected)
|
||||
notification->hide(!preview);
|
||||
if (m_in_preview && notification->get_type() == NotificationType::DidYouKnowHint)
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
#include <libslic3r/ObjectID.hpp>
|
||||
#include <libslic3r/Technologies.hpp>
|
||||
@ -27,6 +28,8 @@ wxDECLARE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationCli
|
||||
using PresetUpdateAvailableClickedEvent = SimpleEvent;
|
||||
wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent);
|
||||
|
||||
using CancelFn = std::function<void()>;
|
||||
|
||||
class GLCanvas3D;
|
||||
class ImGuiWrapper;
|
||||
enum class InfoItemType;
|
||||
@ -34,9 +37,6 @@ enum class InfoItemType;
|
||||
enum class NotificationType
|
||||
{
|
||||
CustomNotification,
|
||||
// Notification on end of slicing and G-code processing (the full G-code preview is available).
|
||||
// Contains a hyperlink to export the G-code to a removable media.
|
||||
SlicingComplete,
|
||||
// SlicingNotPossible,
|
||||
// Notification on end of export to a removable media, with hyperling to eject the external media.
|
||||
// Obsolete by ExportFinished
|
||||
@ -71,10 +71,17 @@ enum class NotificationType
|
||||
PlaterError,
|
||||
// Object fully outside the print volume, or extrusion outside the print volume. Slicing is not disabled.
|
||||
PlaterWarning,
|
||||
// Warning connected to single object id, appears at loading object, disapears at deletition.
|
||||
// Example: advice to simplify object with big amount of triangles.
|
||||
ObjectWarning,
|
||||
// Progress bar instead of text.
|
||||
ProgressBar,
|
||||
// Progress bar with info from Print Host Upload Queue dialog.
|
||||
PrintHostUpload,
|
||||
// Progress bar with cancel button, cannot be closed
|
||||
// On end of slicing and G-code processing (the full G-code preview is available),
|
||||
// contains a hyperlink to export the G-code to a removable media or hdd.
|
||||
SlicingProgress,
|
||||
// Notification, when Color Change G-code is empty and user try to add color change on DoubleSlider.
|
||||
EmptyColorChangeCode,
|
||||
// Notification that custom supports/seams were deleted after mesh repair.
|
||||
@ -97,8 +104,8 @@ enum class NotificationType
|
||||
// Shows when ObjectList::update_info_items finds information that should be stressed to the user
|
||||
// Might contain logo taken from gizmos
|
||||
UpdatedItemsInfo,
|
||||
// Give user advice to simplify object with big amount of triangles
|
||||
SimplifySuggestion
|
||||
// Progress bar notification with methods to replace ProgressIndicator class.
|
||||
ProgressIndicator
|
||||
};
|
||||
|
||||
class NotificationManager
|
||||
@ -108,27 +115,31 @@ public:
|
||||
{
|
||||
// The notifications will be presented in the order of importance, thus these enum values
|
||||
// are sorted by the importance.
|
||||
// "Good to know" notification, usually but not always with a quick fade-out.
|
||||
RegularNotification = 1,
|
||||
// Important notification with progress bar, no fade-out, might appear again after closing. Position at the bottom.
|
||||
ProgressBarNotificationLevel = 1,
|
||||
// "Did you know" notification with special icon and buttons, Position close to bottom.
|
||||
HintNotificationLevel,
|
||||
// "Good to know" notification, usually but not always with a quick fade-out.
|
||||
RegularNotificationLevel,
|
||||
// Information notification without a fade-out or with a longer fade-out.
|
||||
ImportantNotification,
|
||||
// Important notification with progress bar, no fade-out, might appear again after closing.
|
||||
ProgressBarNotification,
|
||||
ImportantNotificationLevel,
|
||||
// Warning, no fade-out.
|
||||
WarningNotification,
|
||||
// Error, no fade-out.
|
||||
ErrorNotification,
|
||||
WarningNotificationLevel,
|
||||
// Error, no fade-out. Top most position.
|
||||
ErrorNotificationLevel,
|
||||
};
|
||||
|
||||
NotificationManager(wxEvtHandler* evt_handler);
|
||||
~NotificationManager();
|
||||
~NotificationManager(){}
|
||||
|
||||
// init is called after canvas3d is created. Notifications added before init are not showed or updated
|
||||
void init() { m_initialized = true; }
|
||||
// Push a prefabricated notification from basic_notifications (see the table at the end of this file).
|
||||
void push_notification(const NotificationType type, int timestamp = 0);
|
||||
// Push a NotificationType::CustomNotification with NotificationLevel::RegularNotification and 10s fade out interval.
|
||||
// Push a NotificationType::CustomNotification with NotificationLevel::RegularNotificationLevel and 10s fade out interval.
|
||||
void push_notification(const std::string& text, int timestamp = 0);
|
||||
// Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotification.
|
||||
// ErrorNotification and ImportantNotification are never faded out.
|
||||
// Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotificationLevel.
|
||||
// ErrorNotificationLevel and ImportantNotificationLevel are never faded out.
|
||||
void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "",
|
||||
std::function<bool(wxEvtHandler*)> callback = std::function<bool(wxEvtHandler*)>(), int timestamp = 0);
|
||||
// Creates Validate Error notification with a custom text and no fade out.
|
||||
@ -155,25 +166,50 @@ public:
|
||||
// Closes error or warning of the same text
|
||||
void close_plater_error_notification(const std::string& text);
|
||||
void close_plater_warning_notification(const std::string& text);
|
||||
// Creates special notification slicing complete.
|
||||
// If large = true (Plater side bar is closed), then printing time and export button is shown
|
||||
// at the notification and fade-out is disabled. Otherwise the fade out time is set to 10s.
|
||||
void push_slicing_complete_notification(int timestamp, bool large);
|
||||
// Add a print time estimate to an existing SlicingComplete notification.
|
||||
void set_slicing_complete_print_time(const std::string &info);
|
||||
// Object warning with ObjectID, closes when object is deleted. ID used is of object not print like in slicing warning.
|
||||
void push_object_warning_notification(const std::string& text, ObjectID object_id, const std::string& hypertext = "",
|
||||
std::function<bool(wxEvtHandler*)> callback = std::function<bool(wxEvtHandler*)>());
|
||||
// Close object warnings, whose ObjectID is not in the list.
|
||||
// living_oids is expected to be sorted.
|
||||
void remove_object_warnings_of_released_objects(const std::vector<ObjectID>& living_oids);
|
||||
// Called when the side bar changes its visibility, as the "slicing complete" notification supplements
|
||||
// the "slicing info" normally shown at the side bar.
|
||||
void set_slicing_complete_large(bool large);
|
||||
void set_sidebar_collapsed(bool collapsed);
|
||||
// Set technology for correct text in SlicingProgress.
|
||||
void set_fff(bool b);
|
||||
void set_fdm(bool b) { set_fff(b); }
|
||||
void set_sla(bool b) { set_fff(!b); }
|
||||
// Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button
|
||||
void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable);
|
||||
// notification with progress bar
|
||||
// notifications with progress bar
|
||||
// print host upload
|
||||
void push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
|
||||
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
|
||||
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
// slicing progress
|
||||
void init_slicing_progress_notification(std::function<void()> cancel_callback);
|
||||
// percentage negative = canceled, <0-1) = progress, 1 = completed
|
||||
void set_slicing_progress_percentage(const std::string& text, float percentage);
|
||||
// hides slicing progress notification imidietly
|
||||
void set_slicing_progress_hidden();
|
||||
// Add a print time estimate to an existing SlicingProgress notification. Set said notification to SP_COMPLETED state.
|
||||
void set_slicing_complete_print_time(const std::string& info, bool sidebar_colapsed);
|
||||
void set_slicing_progress_export_possible();
|
||||
// ProgressIndicator notification
|
||||
// init adds hidden instance of progress indi notif that should always live (goes to hidden instead of erasing)
|
||||
void init_progress_indicator();
|
||||
// functions equal to ProgressIndicator class
|
||||
void progress_indicator_set_range(int range);
|
||||
void progress_indicator_set_cancel_callback(CancelFn callback = CancelFn());
|
||||
void progress_indicator_set_progress(int pr);
|
||||
void progress_indicator_set_status_text(const char*); // utf8 char array
|
||||
int progress_indicator_get_range() const;
|
||||
// Hint (did you know) notification
|
||||
void push_hint_notification(bool open_next);
|
||||
bool is_hint_notification_open();
|
||||
// Forces Hints to reload its content when next hint should be showed
|
||||
void deactivate_loaded_hints();
|
||||
void push_updated_item_info_notification(InfoItemType type);
|
||||
// Close old notification ExportFinished.
|
||||
void new_export_began(bool on_removable);
|
||||
@ -260,7 +296,7 @@ private:
|
||||
virtual bool compare_text(const std::string& text) const;
|
||||
void hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; }
|
||||
// sets m_next_render with time of next mandatory rendering. Delta is time since last render.
|
||||
bool update_state(bool paused, const int64_t delta);
|
||||
virtual bool update_state(bool paused, const int64_t delta);
|
||||
int64_t next_render() const { return is_finished() ? 0 : m_next_render; }
|
||||
EState get_state() const { return m_state; }
|
||||
bool is_hovered() const { return m_state == EState::Hovered; }
|
||||
@ -296,6 +332,8 @@ private:
|
||||
virtual void count_lines();
|
||||
// returns true if PopStyleColor should be called later to pop this push
|
||||
virtual bool push_background_color();
|
||||
// used this function instead of reading directly m_data.duration. Some notifications might need to return changing value.
|
||||
virtual int get_duration() { return m_data.duration; }
|
||||
|
||||
const NotificationData m_data;
|
||||
// For reusing ImGUI windows.
|
||||
@ -352,29 +390,7 @@ private:
|
||||
wxEvtHandler* m_evt_handler;
|
||||
};
|
||||
|
||||
class SlicingCompleteLargeNotification : public PopNotification
|
||||
{
|
||||
public:
|
||||
SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool largeds);
|
||||
void set_large(bool l);
|
||||
bool get_large() { return m_is_large; }
|
||||
void set_print_info(const std::string &info);
|
||||
void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override
|
||||
{
|
||||
// This notification is always hidden if !large (means side bar is collapsed)
|
||||
if (!get_large() && !is_finished())
|
||||
m_state = EState::Hidden;
|
||||
PopNotification::render(canvas, initial_y, move_from_overlay, overlay_width);
|
||||
}
|
||||
protected:
|
||||
void render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y)
|
||||
override;
|
||||
bool m_is_large;
|
||||
bool m_has_print_info { false };
|
||||
std::string m_print_info;
|
||||
};
|
||||
|
||||
|
||||
class SlicingWarningNotification : public PopNotification
|
||||
{
|
||||
@ -398,7 +414,7 @@ private:
|
||||
{
|
||||
public:
|
||||
|
||||
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { }
|
||||
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) { }
|
||||
virtual void set_percentage(float percent) { m_percentage = percent; }
|
||||
protected:
|
||||
virtual void init() override;
|
||||
@ -416,9 +432,10 @@ private:
|
||||
{}
|
||||
void render_minimize_button(ImGuiWrapper& imgui,
|
||||
const float win_pos_x, const float win_pos_y) override {}
|
||||
float m_percentage;
|
||||
float m_percentage {0.0f};
|
||||
|
||||
bool m_has_cancel_button {false};
|
||||
bool m_render_percentage {false};
|
||||
// local time of last hover for showing tooltip
|
||||
|
||||
};
|
||||
@ -436,7 +453,7 @@ private:
|
||||
PB_COMPLETED
|
||||
};
|
||||
PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize)
|
||||
:ProgressBarNotification(n, id_provider, evt_handler, percentage)
|
||||
:ProgressBarNotification(n, id_provider, evt_handler)
|
||||
, m_job_id(job_id)
|
||||
, m_file_size(filesize)
|
||||
{
|
||||
@ -465,7 +482,117 @@ private:
|
||||
// Size of uploaded size to be displayed in MB
|
||||
float m_file_size;
|
||||
long m_hover_time{ 0 };
|
||||
UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS };
|
||||
UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS };
|
||||
};
|
||||
|
||||
class SlicingProgressNotification : public ProgressBarNotification
|
||||
{
|
||||
public:
|
||||
// Inner state of notification, Each state changes bahaviour of the notification
|
||||
enum class SlicingProgressState
|
||||
{
|
||||
SP_NO_SLICING, // hidden
|
||||
SP_PROGRESS, // never fades outs, no close button, has cancel button
|
||||
SP_CANCELLED, // fades after 10 seconds, simple message
|
||||
SP_COMPLETED // Has export hyperlink and print info, fades after 20 sec if sidebar is shown, otherwise no fade out
|
||||
};
|
||||
SlicingProgressNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, std::function<void()> callback)
|
||||
: ProgressBarNotification(n, id_provider, evt_handler)
|
||||
, m_cancel_callback(callback)
|
||||
{
|
||||
set_progress_state(SlicingProgressState::SP_NO_SLICING);
|
||||
m_has_cancel_button = false;
|
||||
m_render_percentage = true;
|
||||
}
|
||||
// sets text of notification - call after setting progress state
|
||||
void set_status_text(const std::string& text);
|
||||
// sets cancel button callback
|
||||
void set_cancel_callback(std::function<void()> callback) { m_cancel_callback = callback; }
|
||||
bool has_cancel_callback() const { return m_cancel_callback != nullptr; }
|
||||
// sets SlicingProgressState, negative percent means canceled
|
||||
void set_progress_state(float percent);
|
||||
// sets SlicingProgressState, percent is used only at progress state.
|
||||
void set_progress_state(SlicingProgressState state,float percent = 0.f);
|
||||
// sets additional string of print info and puts notification into Completed state.
|
||||
void set_print_info(const std::string& info);
|
||||
// sets fading if in Completed state.
|
||||
void set_sidebar_collapsed(bool collapsed);
|
||||
// Calls inherited update_state and ensures Estate goes to hidden not closing.
|
||||
bool update_state(bool paused, const int64_t delta) override;
|
||||
// Switch between technology to provide correct text.
|
||||
void set_fff(bool b) { m_is_fff = b; }
|
||||
void set_fdm(bool b) { m_is_fff = b; }
|
||||
void set_sla(bool b) { m_is_fff = !b; }
|
||||
void set_export_possible(bool b) { m_export_possible = b; }
|
||||
protected:
|
||||
void init() override;
|
||||
void count_lines() override
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_PROGRESS)
|
||||
ProgressBarNotification::count_lines();
|
||||
else
|
||||
PopNotification::count_lines();
|
||||
}
|
||||
void render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) override;
|
||||
void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_close_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void on_cancel_button();
|
||||
int get_duration() override;
|
||||
std::function<void()> m_cancel_callback;
|
||||
SlicingProgressState m_sp_state { SlicingProgressState::SP_PROGRESS };
|
||||
bool m_has_print_info { false };
|
||||
std::string m_print_info;
|
||||
bool m_sidebar_collapsed { false };
|
||||
bool m_is_fff { true };
|
||||
// if true, it is possible show export hyperlink in state SP_PROGRESS
|
||||
bool m_export_possible { false };
|
||||
};
|
||||
|
||||
class ProgressIndicatorNotification : public ProgressBarNotification
|
||||
{
|
||||
public:
|
||||
enum class ProgressIndicatorState
|
||||
{
|
||||
PIS_HIDDEN, // hidden
|
||||
PIS_PROGRESS_REQUEST, // progress was updated, request render on next update_state() call
|
||||
PIS_PROGRESS_UPDATED, // render was requested
|
||||
PIS_COMPLETED // both completed and canceled state. fades out into PIS_NO_SLICING
|
||||
};
|
||||
ProgressIndicatorNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler)
|
||||
: ProgressBarNotification(n, id_provider, evt_handler)
|
||||
{
|
||||
m_render_percentage = true;
|
||||
}
|
||||
// ProgressIndicator
|
||||
void set_range(int range) { m_range = range; }
|
||||
void set_cancel_callback(CancelFn callback) { m_cancel_callback = callback; }
|
||||
void set_progress(int pr) { set_percentage((float)pr / (float)m_range); }
|
||||
void set_status_text(const char*); // utf8 char array
|
||||
int get_range() const { return m_range; }
|
||||
// ProgressBarNotification
|
||||
void init() override;
|
||||
void set_percentage(float percent) override;
|
||||
bool update_state(bool paused, const int64_t delta) override;
|
||||
// Own
|
||||
protected:
|
||||
int m_range { 100 };
|
||||
CancelFn m_cancel_callback { nullptr };
|
||||
ProgressIndicatorState m_progress_state { ProgressIndicatorState::PIS_HIDDEN };
|
||||
|
||||
void render_close_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void on_cancel_button() { if (m_cancel_callback) m_cancel_callback(); }
|
||||
};
|
||||
|
||||
class ExportFinishedNotification : public PopNotification
|
||||
@ -492,7 +619,7 @@ private:
|
||||
void render_close_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_eject_button(ImGuiWrapper& imgui,
|
||||
void render_eject_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
|
||||
@ -555,13 +682,15 @@ private:
|
||||
// If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed.
|
||||
bool has_slicing_error_notification();
|
||||
|
||||
// set by init(), until false notifications are only added not updated and frame is not requested after push
|
||||
bool m_initialized{ false };
|
||||
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
|
||||
wxEvtHandler* m_evt_handler;
|
||||
// Cache of IDs to identify and reuse ImGUI windows.
|
||||
NotificationIDProvider m_id_provider;
|
||||
std::deque<std::unique_ptr<PopNotification>> m_pop_notifications;
|
||||
// delayed waiting notifications, first is remaining time
|
||||
std::deque<DelayedNotification> m_waiting_notifications;
|
||||
std::vector<DelayedNotification> m_waiting_notifications;
|
||||
//timestamps used for slicing finished - notification could be gone so it needs to be stored here
|
||||
std::unordered_set<int> m_used_timestamps;
|
||||
// True if G-code preview is active. False if the Plater is active.
|
||||
@ -576,7 +705,7 @@ private:
|
||||
NotificationType::PlaterWarning,
|
||||
NotificationType::ProgressBar,
|
||||
NotificationType::PrintHostUpload,
|
||||
NotificationType::SimplifySuggestion
|
||||
NotificationType::ObjectWarning
|
||||
};
|
||||
//prepared (basic) notifications
|
||||
static const NotificationData basic_notifications[];
|
||||
|
@ -1208,6 +1208,8 @@ void Sidebar::update_sliced_info_sizer()
|
||||
wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time));
|
||||
p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _L("Estimated printing time") + ":");
|
||||
|
||||
p->plater->get_notification_manager()->set_slicing_complete_print_time(_utf8("Estimated printing time: ") + boost::nowide::narrow(t_est), p->plater->is_sidebar_collapsed());
|
||||
|
||||
// Hide non-SLA sliced info parameters
|
||||
p->sliced_info->SetTextAndShow(siFilament_m, "N/A");
|
||||
p->sliced_info->SetTextAndShow(siFilament_mm3, "N/A");
|
||||
@ -1296,10 +1298,7 @@ void Sidebar::update_sliced_info_sizer()
|
||||
new_label += format_wxstr("\n - %1%", _L("normal mode"));
|
||||
info_text += format_wxstr("\n%1%", short_time(ps.estimated_normal_print_time));
|
||||
|
||||
// uncomment next line to not disappear slicing finished notif when colapsing sidebar before time estimate
|
||||
//if (p->plater->is_sidebar_collapsed())
|
||||
p->plater->get_notification_manager()->set_slicing_complete_large(p->plater->is_sidebar_collapsed());
|
||||
p->plater->get_notification_manager()->set_slicing_complete_print_time("Estimated printing time: " + ps.estimated_normal_print_time);
|
||||
p->plater->get_notification_manager()->set_slicing_complete_print_time(_utf8("Estimated printing time: ") + ps.estimated_normal_print_time, p->plater->is_sidebar_collapsed());
|
||||
|
||||
}
|
||||
if (ps.estimated_silent_print_time != "N/A") {
|
||||
@ -1502,7 +1501,7 @@ struct Plater::priv
|
||||
GLToolbar view_toolbar;
|
||||
GLToolbar collapse_toolbar;
|
||||
Preview *preview;
|
||||
NotificationManager* notification_manager { nullptr };
|
||||
std::shared_ptr<NotificationManager> notification_manager;
|
||||
|
||||
ProjectDirtyStateManager dirty_state;
|
||||
|
||||
@ -1523,10 +1522,10 @@ struct Plater::priv
|
||||
public:
|
||||
Jobs(priv *_m) : m(_m)
|
||||
{
|
||||
m_arrange_id = add_job(std::make_unique<ArrangeJob>(m->statusbar(), m->q));
|
||||
m_fill_bed_id = add_job(std::make_unique<FillBedJob>(m->statusbar(), m->q));
|
||||
m_rotoptimize_id = add_job(std::make_unique<RotoptimizeJob>(m->statusbar(), m->q));
|
||||
m_sla_import_id = add_job(std::make_unique<SLAImportJob>(m->statusbar(), m->q));
|
||||
m_arrange_id = add_job(std::make_unique<ArrangeJob>(m->notification_manager, m->q));
|
||||
m_fill_bed_id = add_job(std::make_unique<FillBedJob>(m->notification_manager, m->q));
|
||||
m_rotoptimize_id = add_job(std::make_unique<RotoptimizeJob>(m->notification_manager, m->q));
|
||||
m_sla_import_id = add_job(std::make_unique<SLAImportJob>(m->notification_manager, m->q));
|
||||
}
|
||||
|
||||
void arrange()
|
||||
@ -1637,7 +1636,7 @@ struct Plater::priv
|
||||
void apply_free_camera_correction(bool apply = true);
|
||||
void update_ui_from_settings();
|
||||
void update_main_toolbar_tooltips();
|
||||
std::shared_ptr<ProgressStatusBar> statusbar();
|
||||
// std::shared_ptr<ProgressStatusBar> statusbar();
|
||||
std::string get_config(const std::string &key) const;
|
||||
BoundingBoxf bed_shape_bb() const;
|
||||
BoundingBox scaled_bed_shape_bb() const;
|
||||
@ -1739,6 +1738,7 @@ struct Plater::priv
|
||||
void add_warning(const Slic3r::PrintStateBase::Warning &warning, size_t oid);
|
||||
// Update notification manager with the current state of warnings produced by the background process (slicing).
|
||||
void actualize_slicing_warnings(const PrintBase &print);
|
||||
void actualize_object_warnings(const PrintBase& print);
|
||||
// Displays dialog window with list of warnings.
|
||||
// Returns true if user clicks OK.
|
||||
// Returns true if current_warnings vector is empty without showning the dialog
|
||||
@ -1790,6 +1790,8 @@ struct Plater::priv
|
||||
// extension should contain the leading dot, i.e.: ".3mf"
|
||||
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
|
||||
void set_project_filename(const wxString& filename);
|
||||
// Call after plater and Canvas#D is initialized
|
||||
void init_notification_manager();
|
||||
|
||||
// Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it.
|
||||
mutable bool ready_to_slice = { false };
|
||||
@ -1799,6 +1801,7 @@ struct Plater::priv
|
||||
std::string last_output_dir_path;
|
||||
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
|
||||
bool process_completed_with_error { false };
|
||||
|
||||
private:
|
||||
bool layers_height_allowed() const;
|
||||
|
||||
@ -1847,6 +1850,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
"support_material_contact_distance", "support_material_bottom_contact_distance", "raft_layers"
|
||||
}))
|
||||
, sidebar(new Sidebar(q))
|
||||
, notification_manager(std::make_shared<NotificationManager>(q))
|
||||
, m_ui_jobs(this)
|
||||
, delayed_scene_refresh(false)
|
||||
, view_toolbar(GLToolbar::Radio, "View")
|
||||
@ -2014,7 +2018,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
});
|
||||
#endif /* _WIN32 */
|
||||
|
||||
notification_manager = new NotificationManager(this->q);
|
||||
//notification_manager = new NotificationManager(this->q);
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); });
|
||||
this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); });
|
||||
@ -2024,12 +2029,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
this->show_action_buttons(this->ready_to_slice);
|
||||
notification_manager->close_notification_of_type(NotificationType::ExportFinished);
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
format(_L("Successfully unmounted. The device %s(%s) can now be safely removed from the computer."), evt.data.first.name, evt.data.first.path)
|
||||
);
|
||||
} else {
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::ErrorNotification,
|
||||
NotificationManager::NotificationLevel::ErrorNotificationLevel,
|
||||
format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path)
|
||||
);
|
||||
}
|
||||
@ -2076,8 +2081,7 @@ Plater::priv::~priv()
|
||||
{
|
||||
if (config != nullptr)
|
||||
delete config;
|
||||
if (notification_manager != nullptr)
|
||||
delete notification_manager;
|
||||
notification_manager->deactivate_loaded_hints();
|
||||
}
|
||||
|
||||
void Plater::priv::update(unsigned int flags)
|
||||
@ -2152,6 +2156,8 @@ void Plater::priv::collapse_sidebar(bool collapse)
|
||||
new_tooltip += " [Shift+Tab]";
|
||||
int id = collapse_toolbar.get_item_id("collapse_sidebar");
|
||||
collapse_toolbar.set_tooltip(id, new_tooltip);
|
||||
|
||||
notification_manager->set_sidebar_collapsed(collapse);
|
||||
}
|
||||
|
||||
|
||||
@ -2179,10 +2185,11 @@ void Plater::priv::update_main_toolbar_tooltips()
|
||||
view3D->get_canvas3d()->update_tooltip_for_settings_item_in_main_toolbar();
|
||||
}
|
||||
|
||||
std::shared_ptr<ProgressStatusBar> Plater::priv::statusbar()
|
||||
{
|
||||
return main_frame->m_statusbar;
|
||||
}
|
||||
//std::shared_ptr<ProgressStatusBar> Plater::priv::statusbar()
|
||||
//{
|
||||
// return nullptr;
|
||||
// return main_frame->m_statusbar;
|
||||
//}
|
||||
|
||||
std::string Plater::priv::get_config(const std::string &key) const
|
||||
{
|
||||
@ -2327,7 +2334,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
for (std::string& name : names)
|
||||
notif_text += "\n - " + name;
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotification, notif_text);
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel, notif_text);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2469,7 +2476,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
if (load_model) {
|
||||
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().make_preferred().string());
|
||||
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
|
||||
statusbar()->set_status_text(_L("Loaded"));
|
||||
// statusbar()->set_status_text(_L("Loaded"));
|
||||
}
|
||||
|
||||
// automatic selection of added objects
|
||||
@ -2882,7 +2889,7 @@ void Plater::priv::split_object()
|
||||
// If we splited object which is contain some parts/modifiers then all non-solid parts (modifiers) were deleted
|
||||
if (current_model_object->volumes.size() > 1 && current_model_object->volumes.size() != new_objects.size())
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
_u8L("All non-solid parts (modifiers) were deleted"));
|
||||
|
||||
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
||||
@ -2958,7 +2965,7 @@ void Plater::priv::process_validation_warning(const std::string& warning) const
|
||||
|
||||
notification_manager->push_notification(
|
||||
NotificationType::ValidateWarning,
|
||||
NotificationManager::NotificationLevel::WarningNotification,
|
||||
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||
_u8L("WARNING:") + "\n" + text, hypertext, action_fn
|
||||
);
|
||||
}
|
||||
@ -3004,6 +3011,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
// In SLA mode, we need to reload the 3D scene every time to show the support structures.
|
||||
if (printer_technology == ptSLA || (printer_technology == ptFFF && config->opt_bool("wipe_tower")))
|
||||
return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE;
|
||||
|
||||
notification_manager->set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! background_process.empty()) {
|
||||
@ -3051,6 +3060,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
//actualizate warnings
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED) {
|
||||
actualize_slicing_warnings(*this->background_process.current_print());
|
||||
actualize_object_warnings(*this->background_process.current_print());
|
||||
show_warning_dialog = false;
|
||||
process_completed_with_error = false;
|
||||
}
|
||||
@ -3075,9 +3085,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
else
|
||||
{
|
||||
// Background data is valid.
|
||||
if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
|
||||
(return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
|
||||
this->statusbar()->set_status_text(_L("Ready to slice"));
|
||||
// if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
|
||||
// (return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
|
||||
// this->statusbar()->set_status_text(_L("Ready to slice"));
|
||||
|
||||
sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export));
|
||||
sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send));
|
||||
@ -3114,10 +3124,10 @@ bool Plater::priv::restart_background_process(unsigned int state)
|
||||
(state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) {
|
||||
// The print is valid and it can be started.
|
||||
if (this->background_process.start()) {
|
||||
this->statusbar()->set_cancel_callback([this]() {
|
||||
this->statusbar()->set_status_text(_L("Cancelling"));
|
||||
this->background_process.stop();
|
||||
});
|
||||
// this->statusbar()->set_cancel_callback([this]() {
|
||||
// this->statusbar()->set_status_text(_L("Cancelling"));
|
||||
// this->background_process.stop();
|
||||
// });
|
||||
if (!show_warning_dialog)
|
||||
on_slicing_began();
|
||||
return true;
|
||||
@ -3715,10 +3725,7 @@ void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_i
|
||||
manager.open_gizmo(GLGizmosManager::EType::Simplify);
|
||||
return true;
|
||||
};
|
||||
notification_manager->push_notification(
|
||||
NotificationType::SimplifySuggestion,
|
||||
NotificationManager::NotificationLevel::WarningNotification,
|
||||
text.str(), hypertext, open_simplify);
|
||||
notification_manager->push_object_warning_notification(text.str(), model.objects[object_id]->id(), hypertext, open_simplify);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3882,9 +3889,9 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||
return;
|
||||
}
|
||||
|
||||
this->statusbar()->set_progress(evt.status.percent);
|
||||
this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…"));
|
||||
//notification_manager->set_progress_bar_percentage("Slicing progress", (float)evt.status.percent / 100.0f);
|
||||
// this->statusbar()->set_progress(evt.status.percent);
|
||||
// this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…"));
|
||||
notification_manager->set_slicing_progress_percentage(evt.status.text, (float)evt.status.percent / 100.0f);
|
||||
}
|
||||
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
|
||||
switch (this->printer_technology) {
|
||||
@ -3935,7 +3942,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||
|
||||
void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
|
||||
{
|
||||
notification_manager->push_slicing_complete_notification(evt.GetInt(), is_sidebar_collapsed());
|
||||
switch (this->printer_technology) {
|
||||
case ptFFF:
|
||||
this->update_fff_scene();
|
||||
@ -3958,8 +3964,8 @@ void Plater::priv::on_export_began(wxCommandEvent& evt)
|
||||
void Plater::priv::on_slicing_began()
|
||||
{
|
||||
clear_warnings();
|
||||
notification_manager->close_notification_of_type(NotificationType::SlicingComplete);
|
||||
notification_manager->close_notification_of_type(NotificationType::SignDetected);
|
||||
notification_manager->close_notification_of_type(NotificationType::ExportFinished);
|
||||
}
|
||||
void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
|
||||
{
|
||||
@ -3983,6 +3989,16 @@ void Plater::priv::actualize_slicing_warnings(const PrintBase &print)
|
||||
notification_manager->remove_slicing_warnings_of_released_objects(ids);
|
||||
notification_manager->set_all_slicing_warnings_gray(true);
|
||||
}
|
||||
void Plater::priv::actualize_object_warnings(const PrintBase& print)
|
||||
{
|
||||
std::vector<ObjectID> ids;
|
||||
for (const ModelObject* object : print.model().objects )
|
||||
{
|
||||
ids.push_back(object->id());
|
||||
}
|
||||
std::sort(ids.begin(), ids.end());
|
||||
notification_manager->remove_object_warnings_of_released_objects(ids);
|
||||
}
|
||||
void Plater::priv::clear_warnings()
|
||||
{
|
||||
notification_manager->close_slicing_errors_and_warnings();
|
||||
@ -4014,8 +4030,9 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
// At this point of time the thread should be either finished or canceled,
|
||||
// so the following call just confirms, that the produced data were consumed.
|
||||
this->background_process.stop();
|
||||
this->statusbar()->reset_cancel_callback();
|
||||
this->statusbar()->stop_busy();
|
||||
// this->statusbar()->reset_cancel_callback();
|
||||
// this->statusbar()->stop_busy();
|
||||
notification_manager->set_slicing_progress_export_possible();
|
||||
|
||||
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
||||
this->background_process.reset_export();
|
||||
@ -4032,7 +4049,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
show_error(q, message.first, message.second);
|
||||
} else
|
||||
notification_manager->push_slicing_error_notification(message.first);
|
||||
this->statusbar()->set_status_text(from_u8(message.first));
|
||||
// this->statusbar()->set_status_text(from_u8(message.first));
|
||||
if (evt.invalidate_plater())
|
||||
{
|
||||
const wxString invalid_str = _L("Invalid data");
|
||||
@ -4042,8 +4059,10 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
}
|
||||
has_error = true;
|
||||
}
|
||||
if (evt.cancelled())
|
||||
this->statusbar()->set_status_text(_L("Cancelled"));
|
||||
if (evt.cancelled()) {
|
||||
// this->statusbar()->set_status_text(_L("Cancelled"));
|
||||
this->notification_manager->set_slicing_progress_percentage(_utf8("Slicing Cancelled."), -1);
|
||||
}
|
||||
|
||||
this->sidebar->show_sliced_info_sizer(evt.success());
|
||||
|
||||
@ -4247,6 +4266,20 @@ void Plater::priv::set_project_filename(const wxString& filename)
|
||||
wxGetApp().mainframe->add_to_recent_projects(filename);
|
||||
}
|
||||
|
||||
void Plater::priv::init_notification_manager()
|
||||
{
|
||||
if (!notification_manager)
|
||||
return;
|
||||
notification_manager->init();
|
||||
|
||||
auto cancel_callback = [this]() {
|
||||
this->background_process.stop();
|
||||
};
|
||||
notification_manager->init_slicing_progress_notification(cancel_callback);
|
||||
notification_manager->set_fff(printer_technology == ptFFF);
|
||||
notification_manager->init_progress_indicator();
|
||||
}
|
||||
|
||||
void Plater::priv::set_current_canvas_as_dirty()
|
||||
{
|
||||
if (current_panel == view3D)
|
||||
@ -5685,7 +5718,7 @@ void Plater::export_stl(bool extended, bool selection_only)
|
||||
}
|
||||
|
||||
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
|
||||
p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path));
|
||||
// p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path));
|
||||
}
|
||||
|
||||
void Plater::export_amf()
|
||||
@ -5702,10 +5735,10 @@ void Plater::export_amf()
|
||||
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
|
||||
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
|
||||
// Success
|
||||
p->statusbar()->set_status_text(format_wxstr(_L("AMF file exported to %s"), path));
|
||||
// p->statusbar()->set_status_text(format_wxstr(_L("AMF file exported to %s"), path));
|
||||
} else {
|
||||
// Failure
|
||||
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting AMF file %s"), path));
|
||||
// p->statusbar()->set_status_text(format_wxstr(_L("Error exporting AMF file %s"), path));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5744,12 +5777,12 @@ bool Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||
bool ret = Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data);
|
||||
if (ret) {
|
||||
// Success
|
||||
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
|
||||
// p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
|
||||
p->set_project_filename(path);
|
||||
}
|
||||
else {
|
||||
// Failure
|
||||
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
|
||||
// p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -6323,6 +6356,8 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
|
||||
|
||||
p->sidebar->get_searcher().set_printer_technology(printer_technology);
|
||||
|
||||
p->notification_manager->set_fff(printer_technology == ptFFF);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -6343,7 +6378,7 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
||||
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
|
||||
get_notification_manager()->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
_u8L("Custom supports, seams and multimaterial painting were "
|
||||
"removed after repairing the mesh."));
|
||||
// _u8L("Undo the repair"),
|
||||
@ -6356,7 +6391,7 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
||||
// else
|
||||
// notification_manager->push_notification(
|
||||
// NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
// NotificationManager::NotificationLevel::RegularNotification,
|
||||
// NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
// _u8L("Cannot undo to before the mesh repair!"));
|
||||
// return true;
|
||||
// });
|
||||
@ -6622,14 +6657,14 @@ Mouse3DController& Plater::get_mouse3d_controller()
|
||||
return p->mouse3d_controller;
|
||||
}
|
||||
|
||||
const NotificationManager* Plater::get_notification_manager() const
|
||||
std::shared_ptr<NotificationManager> Plater::get_notification_manager()
|
||||
{
|
||||
return p->notification_manager;
|
||||
}
|
||||
|
||||
NotificationManager* Plater::get_notification_manager()
|
||||
void Plater::init_notification_manager()
|
||||
{
|
||||
return p->notification_manager;
|
||||
p->init_notification_manager();
|
||||
}
|
||||
|
||||
bool Plater::can_delete() const { return p->can_delete(); }
|
||||
|
@ -359,11 +359,11 @@ public:
|
||||
void set_bed_shape() const;
|
||||
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
|
||||
|
||||
const NotificationManager* get_notification_manager() const;
|
||||
NotificationManager* get_notification_manager();
|
||||
std::shared_ptr<NotificationManager> get_notification_manager();
|
||||
void init_notification_manager();
|
||||
|
||||
void bring_instance_forward();
|
||||
|
||||
|
||||
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
||||
class SuppressSnapshots
|
||||
{
|
||||
|
@ -194,3 +194,4 @@ void ProgressStatusBar::hide_cancel_button()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace Slic3r {
|
||||
* of the Slicer main window. It consists of a message area to the left and a
|
||||
* progress indication area to the right with an optional cancel button.
|
||||
*/
|
||||
|
||||
class ProgressStatusBar : public ProgressIndicator
|
||||
{
|
||||
wxStatusBar *self; // we cheat! It should be the base class but: perl!
|
||||
|
@ -36,7 +36,15 @@ plan tests => 8;
|
||||
$layer_infill{$self->Z} = 1;
|
||||
}
|
||||
}
|
||||
$layers{$args->{Z}} = 1 if $cmd eq 'G1' && $info->{dist_Z} > 0;
|
||||
# Previously, all G-code commands had a fixed number of decimal points with means with redundant zeros after decimal points.
|
||||
# We changed this behavior and got rid of these redundant padding zeros, which caused this test to fail
|
||||
# because the position in Z-axis is compared as a string, and previously, G-code contained the following two commands:
|
||||
# "G1 Z5 F5000 ; lift nozzle"
|
||||
# "G1 Z5.000 F7800.000"
|
||||
# That has a different Z-axis position from the view of string comparisons of floating-point numbers.
|
||||
# To correct the computation of the number of printed layers, even in the case of string comparisons of floating-point numbers,
|
||||
# we filtered out the G-code command with the commend 'lift nozzle'.
|
||||
$layers{$args->{Z}} = 1 if $cmd eq 'G1' && $info->{dist_Z} && index($info->{comment}, 'lift nozzle') == -1;
|
||||
});
|
||||
|
||||
my $layers_with_perimeters = scalar(keys %layer_infill);
|
||||
|
@ -33,7 +33,10 @@ sub buffer {
|
||||
$gcodegen = Slic3r::GCode->new;
|
||||
$gcodegen->apply_print_config($print_config);
|
||||
$gcodegen->set_layer_count(10);
|
||||
$gcodegen->set_extruders([ 0 ]);
|
||||
|
||||
my $extruders_ref = shift;
|
||||
$extruders_ref = [ 0 ] if !defined $extruders_ref;
|
||||
$gcodegen->set_extruders($extruders_ref);
|
||||
return Slic3r::GCode::CoolingBuffer->new($gcodegen);
|
||||
}
|
||||
|
||||
@ -131,8 +134,8 @@ $config->set('disable_fan_first_layers', [ 0 ]);
|
||||
'cooling' => [ 1 , 0 ],
|
||||
'fan_below_layer_time' => [ $print_time2 + 1, $print_time2 + 1 ],
|
||||
'slowdown_below_layer_time' => [ $print_time2 + 2, $print_time2 + 2 ]
|
||||
});
|
||||
$buffer->gcodegen->set_extruders([ 0, 1 ]);
|
||||
},
|
||||
[ 0, 1]);
|
||||
my $gcode = $buffer->process_layer($gcode1 . "T1\nG1 X0 E1 F3000\n", 0);
|
||||
like $gcode, qr/^M106/, 'fan is activated for the 1st tool';
|
||||
like $gcode, qr/.*M107/, 'fan is disabled for the 2nd tool';
|
||||
|
@ -78,13 +78,13 @@ SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
|
||||
}
|
||||
}
|
||||
WHEN("set_speed is called to set speed to 1") {
|
||||
THEN("Output string is G1 F1.000") {
|
||||
REQUIRE_THAT(writer.set_speed(1.0), Catch::Equals("G1 F1.000\n"));
|
||||
THEN("Output string is G1 F1") {
|
||||
REQUIRE_THAT(writer.set_speed(1.0), Catch::Equals("G1 F1\n"));
|
||||
}
|
||||
}
|
||||
WHEN("set_speed is called to set speed to 203.200022") {
|
||||
THEN("Output string is G1 F203.200") {
|
||||
REQUIRE_THAT(writer.set_speed(203.200022), Catch::Equals("G1 F203.200\n"));
|
||||
THEN("Output string is G1 F203.2") {
|
||||
REQUIRE_THAT(writer.set_speed(203.200022), Catch::Equals("G1 F203.2\n"));
|
||||
}
|
||||
}
|
||||
WHEN("set_speed is called to set speed to 203.200522") {
|
||||
|
@ -99,7 +99,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
||||
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
|
||||
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
|
||||
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, { clip });
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
|
||||
THEN("intersection_pl - result is not empty") {
|
||||
REQUIRE(result.size() == 1);
|
||||
}
|
||||
@ -117,7 +117,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
||||
GIVEN("Clipper bug #126") {
|
||||
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
|
||||
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, { clip });
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
|
||||
THEN("intersection_pl - result is not empty") {
|
||||
REQUIRE(result.size() == 1);
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
CoolingBuffer(GCode* gcode)
|
||||
%code{% RETVAL = new CoolingBuffer(*gcode); %};
|
||||
~CoolingBuffer();
|
||||
Ref<GCode> gcodegen();
|
||||
std::string process_layer(std::string gcode, size_t layer_id)
|
||||
%code{% RETVAL = THIS->process_layer(std::move(gcode), layer_id, true); %};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user