From 33e1108f65ff3dc893ea73e56774ba3d5cf031c1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 28 Mar 2018 17:05:31 +0200 Subject: [PATCH] Background processing in C++, WIP. --- lib/Slic3r/GUI/Plater.pm | 8 +- xs/src/libslic3r/GCode.cpp | 40 +++++- xs/src/libslic3r/GCode.hpp | 3 +- xs/src/libslic3r/Layer.cpp | 9 +- xs/src/libslic3r/LayerRegion.cpp | 9 +- xs/src/libslic3r/Print.cpp | 47 +++++--- xs/src/libslic3r/Print.hpp | 78 ++++++++---- xs/src/libslic3r/PrintObject.cpp | 114 ++++++++++++------ xs/src/libslic3r/PrintRegion.cpp | 3 +- xs/src/libslic3r/TriangleMesh.cpp | 37 +++--- xs/src/libslic3r/TriangleMesh.hpp | 11 +- xs/src/slic3r/GUI/3DScene.cpp | 17 ++- .../slic3r/GUI/BackgroundSlicingProcess.cpp | 30 +++-- .../slic3r/GUI/BackgroundSlicingProcess.hpp | 2 +- xs/xsp/GUI_BackgroundSlicingProcess.xsp | 3 +- xs/xsp/Print.xsp | 6 +- xs/xsp/TriangleMesh.xsp | 2 +- 17 files changed, 279 insertions(+), 140 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b6450fbdd..ae31ca764 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1162,7 +1162,7 @@ sub stop_background_process { $self->statusbar->SetCancelCallback(undef); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); - # Stop the background task. + # Stop the background task, wait until the thread goes into the "Idle" state. $self->{background_slicing_process}->stop; # Update the UI with the slicing results. $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; @@ -1275,7 +1275,11 @@ sub on_update_print_preview { # This gets called also if we don't have threads. sub on_process_completed { my ($self, $result) = @_; - + + # Stop the background task, wait until the thread goes into the "Idle" state. + # 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. + $self->{background_slicing_process}->stop; $self->statusbar->SetCancelCallback(undef); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 8ad479532..9c6143fd6 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -365,13 +365,21 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); - this->m_placeholder_parser_failed_templates.clear(); - this->_do_export(*print, file, preview_data); - fflush(file); - if (ferror(file)) { + try { + this->m_placeholder_parser_failed_templates.clear(); + this->_do_export(*print, file, preview_data); + fflush(file); + if (ferror(file)) { + fclose(file); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + } + } catch (std::exception &ex) { + // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown. + // Close and remove the file. fclose(file); boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + throw; } fclose(file); if (! this->m_placeholder_parser_failed_templates.empty()) { @@ -403,6 +411,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { PROFILE_FUNC(); + print.set_started(psGCodeExport); + // resets time estimator m_time_estimator.reset(); m_time_estimator.set_dialect(print.config.gcode_flavor); @@ -444,6 +454,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) std::sort(zs.begin(), zs.end()); m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin()); } + print.throw_if_canceled(); m_enable_cooling_markers = true; this->apply_print_config(print.config); @@ -475,6 +486,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) for (auto layer : object->support_layers) mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); } + print.throw_if_canceled(); // filter out 0-width segments mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); if (! mm3_per_mm.empty()) { @@ -489,6 +501,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value); } } + print.throw_if_canceled(); m_cooling_buffer = make_unique(*this); if (print.config.spiral_vase.value) @@ -513,6 +526,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (! lines.empty()) _write(file, "\n"); } + print.throw_if_canceled(); + // Write some terse information on the slicing parameters. const PrintObject *first_object = print.objects.front(); const double layer_height = first_object->config.layer_height.value; @@ -530,6 +545,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); _write_format(file, "\n"); } + print.throw_if_canceled(); // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser; @@ -566,6 +582,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); m_cooling_buffer->set_current_extruder(initial_extruder_id); @@ -611,7 +628,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); } this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); - + print.throw_if_canceled(); + // Set other general things. _write(file, this->preamble()); @@ -629,6 +647,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } //FIXME Mege the islands in parallel. m_avoid_crossing_perimeters.init_external_mp(union_ex(islands)); + print.throw_if_canceled(); } // Calculate wiping points if needed @@ -659,6 +678,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) ); #endif } + print.throw_if_canceled(); } // Set initial extruder only after custom start G-code. @@ -686,6 +706,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); this->set_origin(unscale(copy.x), unscale(copy.y)); if (finished_objects > 0) { // Move to the origin position for the copy we're going to print. @@ -716,6 +737,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) std::vector lrs; lrs.emplace_back(std::move(ltp)); this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object._shifted_copies.data()); + print.throw_if_canceled(); } if (m_pressure_equalizer) _write(file, m_pressure_equalizer->process("", true)); @@ -761,6 +783,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. _write(file, "M1 S10\n"); } + print.throw_if_canceled(); } // Extrude the layers. for (auto &layer : layers_to_print) { @@ -768,6 +791,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); + print.throw_if_canceled(); } if (m_pressure_equalizer) _write(file, m_pressure_equalizer->process("", true)); @@ -799,6 +823,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id())); _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% _write(file, m_writer.postamble()); + print.throw_if_canceled(); // calculates estimated printing time m_time_estimator.calculate_time(); @@ -839,10 +864,13 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (!full_config.empty()) _write(file, full_config); } + print.throw_if_canceled(); // starts analizer calculations if (preview_data != nullptr) m_analyzer.calc_gcode_preview_data(*preview_data); + + print.set_done(psGCodeExport); } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 968cd14de..5c3a6ffb3 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -136,7 +136,8 @@ public: {} ~GCode() {} - // throws std::runtime_exception + // throws std::runtime_exception on error, + // throws CanceledException through print->throw_if_canceled(). void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 652bcdaa0..3dc2335d7 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -150,13 +150,12 @@ void Layer::make_fills() #ifdef SLIC3R_DEBUG printf("Making fills for layer " PRINTF_ZU "\n", this->id()); #endif - for (LayerRegionPtrs::iterator it_layerm = regions.begin(); it_layerm != regions.end(); ++ it_layerm) { - LayerRegion &layerm = *(*it_layerm); - layerm.fills.clear(); - make_fill(layerm, layerm.fills); + for (LayerRegion *layerm : regions) { + layerm->fills.clear(); + make_fill(*layerm, layerm->fills); #ifndef NDEBUG for (size_t i = 0; i < layerm.fills.entities.size(); ++ i) - assert(dynamic_cast(layerm.fills.entities[i]) != NULL); + assert(dynamic_cast(layerm->fills.entities[i]) != NULL); #endif } } diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 68e17407e..68d1c5829 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -51,8 +51,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() } } -void -LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) +void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) { this->perimeters.clear(); this->thin_fills.clear(); @@ -340,8 +339,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } -void -LayerRegion::prepare_fill_surfaces() +void LayerRegion::prepare_fill_surfaces() { #ifdef SLIC3R_DEBUG_SLICE_PROCESSING export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial"); @@ -382,8 +380,7 @@ LayerRegion::prepare_fill_surfaces() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } -double -LayerRegion::infill_area_threshold() const +double LayerRegion::infill_area_threshold() const { double ss = this->flow(frSolidInfill).scaled_spacing(); return ss*ss; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a74afdaec..a5e826161 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -83,7 +83,7 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore = { + static std::unordered_set steps_gcode = { "avoid_crossing_perimeters", "bed_shape", "bed_temperature", @@ -159,13 +159,18 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore = {}; + std::vector steps; std::vector osteps; bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { - if (steps_ignore.find(opt_key) != steps_ignore.end()) { + if (steps_gcode.find(opt_key) != steps_gcode.end()) { // These options only affect G-code export or they are just notes without influence on the generated G-code, // so there is nothing to invalidate. + steps.emplace_back(psGCodeExport); + } else if (steps_ignore.find(opt_key) != steps_ignore.end()) { + // These steps have no influence on the G-code whatsoever. Just ignore them. } else if ( opt_key == "skirts" || opt_key == "skirt_height" @@ -226,22 +231,22 @@ bool Print::invalidate_state_by_config_options(const std::vectorstate.invalidate(step); + bool invalidated = m_state.invalidate(step); // Propagate to dependent steps. //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? if (step == psSkirt) - invalidated |= this->state.invalidate(psBrim); + invalidated |= m_state.invalidate(psBrim); return invalidated; } // returns true if an object step is done on all objects // and there's at least one object -bool Print::step_done(PrintObjectStep step) const +bool Print::is_step_done(PrintObjectStep step) const { if (this->objects.empty()) return false; for (const PrintObject *object : this->objects) - if (!object->state.is_done(step)) + if (!object->m_state.is_done(step)) return false; return true; } @@ -801,37 +806,42 @@ void Print::process() BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; for (PrintObject *obj : this->objects) obj->make_perimeters(); + this->throw_if_canceled(); this->set_status(70, "Infilling layers"); for (PrintObject *obj : this->objects) obj->infill(); + this->throw_if_canceled(); for (PrintObject *obj : this->objects) obj->generate_support_material(); - if (! this->state.is_done(psSkirt)) { - this->state.set_started(psSkirt); + this->throw_if_canceled(); + if (! m_state.is_done(psSkirt)) { + this->set_started(psSkirt); this->skirt.clear(); if (this->has_skirt()) { this->set_status(88, "Generating skirt"); this->_make_skirt(); } - this->state.set_done(psSkirt); + m_state.set_done(psSkirt); } - if (! this->state.is_done(psBrim)) { - this->state.set_started(psBrim); + this->throw_if_canceled(); + if (! m_state.is_done(psBrim)) { + this->set_started(psBrim); this->brim.clear(); if (this->config.brim_width > 0) { this->set_status(88, "Generating brim"); this->_make_brim(); } - this->state.set_done(psBrim); + m_state.set_done(psBrim); } - if (! this->state.is_done(psWipeTower)) { - this->state.set_started(psWipeTower); + this->throw_if_canceled(); + if (! m_state.is_done(psWipeTower)) { + this->set_started(psWipeTower); this->_clear_wipe_tower(); if (this->has_wipe_tower()) { //this->set_status(95, "Generating wipe tower"); this->_make_wipe_tower(); } - this->state.set_done(psWipeTower); + m_state.set_done(psWipeTower); } BOOST_LOG_TRIVIAL(info) << "Slicing process finished."; } @@ -883,6 +893,7 @@ void Print::_make_skirt() // Collect points from all layers contained in skirt height. Points points; for (const PrintObject *object : this->objects) { + this->throw_if_canceled(); Points object_points; // Get object layers up to skirt_height_z. for (const Layer *layer : object->layers) { @@ -912,6 +923,7 @@ void Print::_make_skirt() // At least three points required for a convex hull. return; + this->throw_if_canceled(); Polygon convex_hull = Slic3r::Geometry::convex_hull(points); // Skirt may be printed on several layers, having distinct layer heights, @@ -946,6 +958,7 @@ void Print::_make_skirt() // Loop while we have less skirts than required or any extruder hasn't reached the min length if any. std::vector extruded_length(extruders.size(), 0.); for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { + this->throw_if_canceled(); // Offset the skirt outside. distance += coord_t(scale_(spacing)); // Generate the skirt centerline. @@ -994,6 +1007,7 @@ void Print::_make_brim() Flow flow = this->brim_flow(); Polygons islands; for (PrintObject *object : this->objects) { + this->throw_if_canceled(); Polygons object_islands; for (ExPolygon &expoly : object->layers.front()->slices.expolygons) object_islands.push_back(expoly.contour); @@ -1009,6 +1023,7 @@ void Print::_make_brim() Polygons loops; size_t num_loops = size_t(floor(this->config.brim_width.value / flow.width)); for (size_t i = 0; i < num_loops; ++ i) { + this->throw_if_canceled(); islands = offset(islands, float(flow.scaled_spacing()), jtSquare); for (Polygon &poly : islands) { // poly.simplify(SCALED_RESOLUTION); @@ -1088,6 +1103,7 @@ void Print::_make_wipe_tower() } } } + this->throw_if_canceled(); // Initialize the wipe tower. WipeTowerPrusaMM wipe_tower( @@ -1119,6 +1135,7 @@ void Print::_make_wipe_tower() // Set current_extruder_id to the last extruder primed. unsigned int current_extruder_id = m_tool_ordering.all_extruders().back(); for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) { + this->throw_if_canceled(); if (! layer_tools.has_wipe_tower) // This is a support only layer, or the wipe tower does not reach to this height. continue; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 341e48e29..1162c9b0f 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -18,56 +18,71 @@ #include "GCode/WipeTower.hpp" #include "tbb/atomic.h" +// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros. +#ifndef NOMINMAX + #define NOMINMAX +#endif +#include "tbb/mutex.h" namespace Slic3r { class Print; class PrintObject; class ModelObject; +class GCode; class GCodePreviewData; // Print step IDs for keeping track of the print state. enum PrintStep { - psSkirt, psBrim, psWipeTower, psCount, + psSkirt, psBrim, psWipeTower, psGCodeExport, psCount, }; enum PrintObjectStep { posSlice, posPerimeters, posPrepareInfill, posInfill, posSupportMaterial, posCount, }; +class CanceledException : public std::exception { +public: + const char* what() const throw() { return "Background processing has been canceled"; } +}; + // To be instantiated over PrintStep or PrintObjectStep enums. template class PrintState { public: - PrintState() { memset(state, 0, sizeof(state)); } + PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i] = INVALID; } enum State { INVALID, STARTED, DONE, }; - State state[COUNT]; - bool is_started(StepType step) const { return this->state[step] == STARTED; } - bool is_done(StepType step) const { return this->state[step] == DONE; } - void set_started(StepType step) { this->state[step] = STARTED; } - void set_done(StepType step) { this->state[step] = DONE; } + bool is_done(StepType step) const { return m_state[step] == DONE; } + // set_started() will lock the provided mutex before setting the state. + // This is necessary to block until the Print::apply_config() updates its state, which may + // influence the processing step being entered. + void set_started(StepType step, tbb::mutex &mtx) { mtx.lock(); m_state[step] = STARTED; mtx.unlock(); } + void set_done(StepType step) { m_state[step] = DONE; } bool invalidate(StepType step) { - bool invalidated = this->state[step] != INVALID; - this->state[step] = INVALID; + bool invalidated = m_state[step] != INVALID; + m_state[step] = INVALID; return invalidated; } bool invalidate_all() { bool invalidated = false; for (size_t i = 0; i < COUNT; ++ i) - if (this->state[i] != INVALID) { + if (m_state[i] != INVALID) { invalidated = true; + m_state[i] = INVALID; break; } - memset(state, 0, sizeof(state)); return invalidated; } + +private: + std::atomic m_state[COUNT]; }; // A PrintRegion object represents a group of volumes to print @@ -131,7 +146,6 @@ public: LayerPtrs layers; SupportLayerPtrs support_layers; - PrintState state; Print* print() { return this->_print; } const Print* print() const { return this->_print; } @@ -174,7 +188,8 @@ public: // methods for handling state bool invalidate_state_by_config_options(const std::vector &opt_keys); bool invalidate_step(PrintObjectStep step); - bool invalidate_all_steps() { return this->state.invalidate_all(); } + bool invalidate_all_steps() { return m_state.invalidate_all(); } + bool is_step_done(PrintObjectStep step) const { return m_state.is_done(step); } // To be used over the layer_height_profile of both the PrintObject and ModelObject // to initialize the height profile with the height ranges. @@ -218,11 +233,18 @@ private: ModelObject* _model_object; Points _copies; // Slic3r::Point objects in scaled G-code coordinates + PrintState m_state; + // Mutex used for synchronization of the worker thread with the UI thread: + // The mutex will be used to guard the worker thread against entering a stage + // while the data influencing the stage is modified. + tbb::mutex m_mutex; + // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} + void set_started(PrintObjectStep step) { m_state.set_started(step, m_mutex); } std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; @@ -242,7 +264,6 @@ public: std::string estimated_print_time; double total_used_filament, total_extruded_volume, total_cost, total_weight; std::map filament_stats; - PrintState state; // ordered collections of extrusion paths to build skirt loops and brim ExtrusionEntityCollection skirt, brim; @@ -266,9 +287,10 @@ public: // methods for handling state bool invalidate_step(PrintStep step); - bool invalidate_all_steps() { return this->state.invalidate_all(); } - bool step_done(PrintObjectStep step) const; - + bool invalidate_all_steps() { return m_state.invalidate_all(); } + bool is_step_done(PrintStep step) const { return m_state.is_done(step); } + bool is_step_done(PrintObjectStep step) const; + void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig config); bool has_infinite_skirt() const; @@ -308,7 +330,7 @@ public: typedef std::function status_callback_type; // Default status console print out in the form of percent => message. void set_status_default() { m_status_callback = nullptr; } - // No status output or callback whatsoever. + // No status output or callback whatsoever, useful mostly for automatic tests. void set_status_silent() { m_status_callback = [](int, const std::string&){}; } // Register a custom status callback. void set_status_callback(status_callback_type cb) { m_status_callback = cb; } @@ -323,7 +345,12 @@ public: void restart() { m_canceled = false; } // Has the calculation been canceled? bool canceled() { return m_canceled; } - + void throw_if_canceled() { if (m_canceled) throw CanceledException(); } + +protected: + void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } + void set_done(PrintStep step) { m_state.set_done(step); } + private: bool invalidate_state_by_config_options(const std::vector &opt_keys); PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); @@ -333,9 +360,18 @@ private: void _clear_wipe_tower(); void _make_wipe_tower(); + PrintState m_state; + // Mutex used for synchronization of the worker thread with the UI thread: + // The mutex will be used to guard the worker thread against entering a stage + // while the data influencing the stage is modified. + tbb::mutex m_mutex; // Has the calculation been canceled? - tbb::atomic m_canceled; - status_callback_type m_status_callback; + tbb::atomic m_canceled; + // Callback to be evoked regularly to update state of the UI thread. + status_callback_type m_status_callback; + + // To allow GCode to set the Print's GCodeExport step status. + friend class GCode; }; #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index e64c12969..ff1e983fd 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -31,8 +31,6 @@ #include #endif -#define PARALLEL_FOR_CANCEL do { if (this->print()->canceled()) return; } while (0) - namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : @@ -117,14 +115,16 @@ bool PrintObject::reload_model_instances() // this should be idempotent void PrintObject::slice() { - if (this->state.is_done(posSlice)) + if (m_state.is_done(posSlice)) return; - this->state.set_started(posSlice); + this->set_started(posSlice); this->_print->set_status(10, "Processing triangulated mesh"); this->_slice(); + this->_print->throw_if_canceled(); // Fix the model. //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. std::string warning = this->_fix_slicing_errors(); + this->_print->throw_if_canceled(); if (! warning.empty()) BOOST_LOG_TRIVIAL(info) << warning; // Simplify slices if required. @@ -132,7 +132,7 @@ void PrintObject::slice() this->_simplify_slices(scale_(this->_print->config.resolution)); if (this->layers.empty()) throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); - this->state.set_done(posSlice); + m_state.set_done(posSlice); } // 1) Merges typed region slices into stInternal type. @@ -143,19 +143,21 @@ void PrintObject::make_perimeters() // prerequisites this->slice(); - if (this->state.is_done(posPerimeters)) + if (m_state.is_done(posPerimeters)) return; - this->state.set_started(posPerimeters); + this->set_started(posPerimeters); this->_print->set_status(20, "Generating perimeters"); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; // merge slices if they were split into types if (this->typed_slices) { - FOREACH_LAYER(this, layer_it) + FOREACH_LAYER(this, layer_it) { (*layer_it)->merge_slices(); + this->_print->throw_if_canceled(); + } this->typed_slices = false; - this->state.invalidate(posPrepareInfill); + m_state.invalidate(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing @@ -168,8 +170,7 @@ void PrintObject::make_perimeters() FOREACH_REGION(this->_print, region_it) { size_t region_id = region_it - this->_print->regions.begin(); const PrintRegion ®ion = **region_it; - - + if (!region.config.extra_perimeters || region.config.perimeters == 0 || region.config.fill_density == 0 @@ -181,7 +182,7 @@ void PrintObject::make_perimeters() tbb::blocked_range(0, this->layers.size() - 1), [this, ®ion, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); LayerRegion &layerm = *this->layers[layer_idx]->regions[region_id]; const LayerRegion &upper_layerm = *this->layers[layer_idx+1]->regions[region_id]; const Polygons upper_layerm_polygons = upper_layerm.slices; @@ -230,6 +231,7 @@ void PrintObject::make_perimeters() } } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; } @@ -238,11 +240,12 @@ void PrintObject::make_perimeters() tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); this->layers[layer_idx]->make_perimeters(); } } ); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; /* @@ -252,15 +255,15 @@ void PrintObject::make_perimeters() ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); */ - this->state.set_done(posPerimeters); + m_state.set_done(posPerimeters); } void PrintObject::prepare_infill() { - if (this->state.is_done(posPrepareInfill)) + if (m_state.is_done(posPrepareInfill)) return; - this->state.set_started(posPrepareInfill); + this->set_started(posPrepareInfill); this->_print->set_status(30, "Preparing infill"); // This will assign a type (top/bottom/internal) to $layerm->slices. @@ -268,14 +271,17 @@ void PrintObject::prepare_infill() // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); + this->_print->throw_if_canceled(); // Decide what surfaces are to be filled. // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; for (auto *layer : this->layers) - for (auto *region : layer->regions) + for (auto *region : layer->regions) { region->prepare_fill_surfaces(); + this->_print->throw_if_canceled(); + } // this will detect bridges and reverse bridges // and rearrange top/bottom/internal surfaces @@ -287,9 +293,11 @@ void PrintObject::prepare_infill() // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. this->process_external_surfaces(); + this->_print->throw_if_canceled(); // Add solid fills to ensure the shell vertical thickness. this->discover_vertical_shells(); + this->_print->throw_if_canceled(); // Debugging output. #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -309,6 +317,7 @@ void PrintObject::prepare_infill() // to close these surfaces reliably. //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? this->discover_horizontal_shells(); + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { @@ -327,6 +336,7 @@ void PrintObject::prepare_infill() // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. this->clip_fill_surfaces(); + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { @@ -341,9 +351,11 @@ void PrintObject::prepare_infill() // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); + this->_print->throw_if_canceled(); // combine fill surfaces to honor the "infill every N layers" option this->combine_infill(); + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { @@ -359,7 +371,7 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - this->state.set_done(posPrepareInfill); + m_state.set_done(posPrepareInfill); } void PrintObject::infill() @@ -367,36 +379,38 @@ void PrintObject::infill() // prerequisites this->prepare_infill(); - if (! this->state.is_done(posInfill)) { - this->state.set_started(posInfill); + if (! m_state.is_done(posInfill)) { + this->set_started(posInfill); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); this->layers[layer_idx]->make_fills(); } } ); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; /* we could free memory now, but this would make this step not idempotent ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; */ - this->state.set_done(posInfill); + m_state.set_done(posInfill); } } void PrintObject::generate_support_material() { - if (! this->state.is_done(posSupportMaterial)) { - this->state.set_started(posSupportMaterial); + if (! m_state.is_done(posSupportMaterial)) { + this->set_started(posSupportMaterial); this->clear_support_layers(); if ((this->config.support_material || this->config.raft_layers > 0) && this->layers.size() > 1) { this->_print->set_status(85, "Generating support material"); this->_generate_support_material(); + this->_print->throw_if_canceled(); } - this->state.set_done(posSupportMaterial); + m_state.set_done(posSupportMaterial); } } @@ -545,7 +559,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorstate.invalidate(step); + bool invalidated = m_state.invalidate(step); // propagate to dependent steps if (step == posPerimeters) { @@ -624,7 +638,7 @@ void PrintObject::detect_surfaces_type() (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) ? stBottom : stBottomBridge; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; Layer *layer = this->layers[idx_layer]; LayerRegion *layerm = layer->get_region(idx_region); @@ -749,6 +763,7 @@ void PrintObject::detect_surfaces_type() } } ); // for each layer of a region + this->_print->throw_if_canceled(); if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces @@ -762,7 +777,7 @@ void PrintObject::detect_surfaces_type() tbb::blocked_range(0, this->layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -770,6 +785,7 @@ void PrintObject::detect_surfaces_type() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } // for each layer of a region }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end"; } // for each this->print->region_count @@ -789,12 +805,13 @@ void PrintObject::process_external_surfaces() tbb::blocked_range(0, this->layers.size()), [this, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << this->layers[layer_idx]->print_z; this->layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : this->layers[layer_idx - 1]); } } ); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end"; } } @@ -838,7 +855,7 @@ void PrintObject::discover_vertical_shells() const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const size_t num_regions = this->_print->regions.size(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); const Layer &layer = *this->layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; // Simulate single set of perimeters over all merged regions. @@ -893,6 +910,7 @@ void PrintObject::discover_vertical_shells() cache.holes = union_(cache.holes, false); } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } @@ -921,7 +939,7 @@ void PrintObject::discover_vertical_shells() [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); Layer &layer = *this->layers[idx_layer]; LayerRegion &layerm = *layer.regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; @@ -939,6 +957,7 @@ void PrintObject::discover_vertical_shells() } } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom"; } @@ -950,7 +969,7 @@ void PrintObject::discover_vertical_shells() // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { PROFILE_BLOCK(discover_vertical_shells_region_layer); - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; @@ -1163,7 +1182,8 @@ void PrintObject::discover_vertical_shells() layerm->fill_surfaces.append(new_internal_void, stInternalVoid); layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); } // for each layer - }); + }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1303,6 +1323,7 @@ void PrintObject::bridge_over_infill() layerm->export_region_slices_to_svg_debug("7_bridge_over_infill"); layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + this->_print->throw_if_canceled(); } } } @@ -1411,9 +1432,11 @@ void PrintObject::_slice() for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end"; } @@ -1422,6 +1445,7 @@ void PrintObject::_slice() for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); + this->_print->throw_if_canceled(); // loop through the other regions and 'steal' the slices belonging to this one BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { @@ -1443,6 +1467,7 @@ void PrintObject::_slice() layerm->slices.append(std::move(my_parts), stInternal); } } + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end"; } } @@ -1459,6 +1484,7 @@ void PrintObject::_slice() if (! this->layers.empty()) this->layers.back()->upper_layer = nullptr; } + this->_print->throw_if_canceled(); end: ; @@ -1467,7 +1493,7 @@ end: tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); Layer *layer = this->layers[layer_id]; // Apply size compensation and perform clipping of multi-part objects. float delta = float(scale_(this->config.xy_size_compensation.value)); @@ -1503,6 +1529,7 @@ end: layer->make_slices(); } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; } @@ -1519,6 +1546,7 @@ std::vector PrintObject::_slice_region(size_t region_id, const std:: ModelVolume *volume = this->model_object()->volumes[volume_id]; if (volume->modifier == modifier) mesh.merge(volume->mesh); + this->_print->throw_if_canceled(); } if (mesh.stl.stats.number_of_facets > 0) { // transform mesh @@ -1528,8 +1556,12 @@ std::vector PrintObject::_slice_region(size_t region_id, const std:: // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z)); // perform actual slicing - TriangleMeshSlicer mslicer(&mesh); - mslicer.slice(z, &layers); + TriangleMeshSlicer mslicer; + Print *print = this->print(); + auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); + mslicer.init(&mesh, callback); + mslicer.slice(z, &layers, callback); + this->_print->throw_if_canceled(); } } } @@ -1551,7 +1583,7 @@ std::string PrintObject::_fix_slicing_errors() tbb::blocked_range(0, buggy_layers.size()), [this, &buggy_layers](const tbb::blocked_range& range) { for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); size_t idx_layer = buggy_layers[buggy_layer_idx]; Layer *layer = this->layers[idx_layer]; assert(layer->slicing_errors); @@ -1602,6 +1634,7 @@ std::string PrintObject::_fix_slicing_errors() layer->make_slices(); } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom @@ -1628,7 +1661,7 @@ void PrintObject::_simplify_slices(double distance) tbb::blocked_range(0, this->layers.size()), [this, distance](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); Layer *layer = this->layers[layer_idx]; for (size_t region_idx = 0; region_idx < layer->regions.size(); ++ region_idx) layer->regions[region_idx]->slices.simplify(distance); @@ -1721,6 +1754,7 @@ void PrintObject::clip_fill_surfaces() layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); #endif } + this->_print->throw_if_canceled(); } } @@ -1730,6 +1764,7 @@ void PrintObject::discover_horizontal_shells() for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { for (int i = 0; i < int(this->layers.size()); ++ i) { + this->_print->throw_if_canceled(); LayerRegion *layerm = this->layers[i]->regions[region_id]; PrintRegionConfig ®ion_config = layerm->region()->config; if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && @@ -1746,6 +1781,7 @@ void PrintObject::discover_horizontal_shells() continue; for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { + this->_print->throw_if_canceled(); SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge; // Find slices of current type for current layer. // Use slices instead of fill_surfaces, because they also include the perimeter area, @@ -1932,6 +1968,7 @@ void PrintObject::combine_infill() double current_height = 0.; size_t num_layers = 0; for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { + this->_print->throw_if_canceled(); const Layer *layer = this->layers[layer_idx]; if (layer->id() == 0) // Skip first print layer (which may not be first layer in array because of raft). @@ -1954,6 +1991,7 @@ void PrintObject::combine_infill() // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { + this->_print->throw_if_canceled(); size_t num_layers = combine[layer_idx]; if (num_layers <= 1) continue; diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp index 4874c71bc..2fcf69166 100644 --- a/xs/src/libslic3r/PrintRegion.cpp +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -2,8 +2,7 @@ namespace Slic3r { -Flow -PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const +Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const { ConfigOptionFloatOrPercent config_width; if (width != -1) { diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index aecb39662..17b3d98f9 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -606,11 +606,11 @@ TriangleMesh::require_shared_vertices() BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } - -TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : - mesh(_mesh) +void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel) { + mesh = _mesh; _mesh->require_shared_vertices(); + throw_on_cancel(); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); // Scale the copied vertices. @@ -650,6 +650,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : e2f.face_edge = - e2f.face_edge; } } + throw_on_cancel(); std::sort(edges_map.begin(), edges_map.end()); // Assign a unique common edge id to touching triangle edges. @@ -689,11 +690,12 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : edge_j.face = -1; } ++ num_edges; + if ((i & 0x0ffff) == 0) + throw_on_cancel(); } } -void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -730,13 +732,17 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la boost::mutex lines_mutex; tbb::parallel_for( tbb::blocked_range(0,this->mesh->stl.stats.number_of_facets), - [&lines, &lines_mutex, &z, this](const tbb::blocked_range& range) { - for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) + [&lines, &lines_mutex, &z, throw_on_cancel, this](const tbb::blocked_range& range) { + for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) { + if ((facet_idx & 0x0ffff) == 0) + throw_on_cancel(); this->_slice_do(facet_idx, &lines, &lines_mutex, z); + } } ); } - + throw_on_cancel(); + // v_scaled_shared could be freed here // build loops @@ -744,9 +750,12 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&lines, &layers, this](const tbb::blocked_range& range) { - for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) + [&lines, &layers, throw_on_cancel, this](const tbb::blocked_range& range) { + for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) { + if ((line_idx & 0x0ffff) == 0) + throw_on_cancel(); this->make_loops(lines[line_idx], &(*layers)[line_idx]); + } } ); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice finished"; @@ -823,21 +832,21 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector &z, std::vector* layers) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; - this->slice(z, &layers_p); + this->slice(z, &layers_p, throw_on_cancel); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start"; layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&layers_p, layers, this](const tbb::blocked_range& range) { + [&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif + throw_on_cancel(); this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]); } }); diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index c700784a5..55900656b 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include +#include #include #include #include "BoundingBox.hpp" @@ -130,9 +131,13 @@ typedef std::vector IntersectionLinePtrs; class TriangleMeshSlicer { public: - TriangleMeshSlicer(TriangleMesh* _mesh); - void slice(const std::vector &z, std::vector* layers) const; - void slice(const std::vector &z, std::vector* layers) const; + typedef std::function throw_on_cancel_callback_type; + TriangleMeshSlicer() : mesh(nullptr) {} + // Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices() + TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, throw_on_cancel_callback_type()); } + void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx, const float min_z, const float max_z, IntersectionLine *line_out) const; void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index f6be96a78..03154c47e 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -647,14 +647,10 @@ std::vector GLVolumeCollection::get_current_print_zs() const for (GLVolume *vol : this->volumes) { for (coordf_t z : vol->print_zs) - { - double round_z = (double)round(z * 100000.0f) / 100000.0f; - if (std::find(print_zs.begin(), print_zs.end(), round_z) == print_zs.end()) - print_zs.push_back(round_z); - } + print_zs.emplace_back((double)round(z * 100000.0f) / 100000.0f); } - std::sort(print_zs.begin(), print_zs.end()); + sort_remove_duplicates(print_zs); return print_zs; } @@ -1671,6 +1667,9 @@ void _3DScene::_load_print_toolpaths( const std::vector &tool_colors, bool use_VBOs) { + // The skirt and brim steps should be marked as done, so their paths are valid. + assert(print->is_step_done(psSkirt) && print->is_step_done(psBrim)); + if (!print->has_skirt() && print->config.brim_width.value == 0) return; @@ -1762,9 +1761,9 @@ void _3DScene::_load_print_object_toolpaths( std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); // Maximum size of an allocation block: 32MB / sizeof(float) - ctxt.has_perimeters = print_object->state.is_done(posPerimeters); - ctxt.has_infill = print_object->state.is_done(posInfill); - ctxt.has_support = print_object->state.is_done(posSupportMaterial); + ctxt.has_perimeters = print_object->is_step_done(posPerimeters); + ctxt.has_infill = print_object->is_step_done(posInfill); + ctxt.has_support = print_object->is_step_done(posSupportMaterial); ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 15da2eb0c..d774ef65c 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -24,7 +24,7 @@ void BackgroundSlicingProcess::thread_proc() lck.unlock(); m_condition.notify_one(); for (;;) { - assert(m_state == STATE_IDLE); + assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED); // Wait until a new task is ready to be executed, or this thread should be finished. lck.lock(); m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; }); @@ -38,10 +38,13 @@ void BackgroundSlicingProcess::thread_proc() try { assert(m_print != nullptr); m_print->process(); - if (m_print->canceled()) - return; - wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); - m_print->export_gcode(m_output_path, m_gcode_preview_data); + if (! m_print->canceled()) { + wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); + m_print->export_gcode(m_output_path, m_gcode_preview_data); + } + } catch (CanceledException &ex) { + // Canceled, this is all right. + assert(m_print->canceled()); } catch (std::exception &ex) { error = ex.what(); } catch (...) { @@ -53,6 +56,7 @@ void BackgroundSlicingProcess::thread_proc() evt.SetString(error); evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); wxQueueEvent(GUI::g_wxPlater, evt.Clone()); + m_print->restart(); lck.unlock(); // Let the UI thread wake up if it is waiting for the background task to finish. m_condition.notify_one(); @@ -112,21 +116,23 @@ bool BackgroundSlicingProcess::stop() if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { m_print->cancel(); // Wait until the background processing stops by being canceled. - lck.unlock(); m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); + // In the "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; + } else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) { + // In the "Finished" or "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; } return true; } // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. -bool BackgroundSlicingProcess::apply_config(DynamicPrintConfig *config) +bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config) { - /* - // apply new config - my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); - */ - return true; + this->stop(); + bool invalidated = this->m_print->apply_config(config); + return invalidated; } }; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 9dcce0af1..358e00ca9 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -39,7 +39,7 @@ public: // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. - bool apply_config(DynamicPrintConfig *config); + bool apply_config(const DynamicPrintConfig &config); enum State { // m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet). diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp index d12c7f8e3..cef9f6d58 100644 --- a/xs/xsp/GUI_BackgroundSlicingProcess.xsp +++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp @@ -17,7 +17,8 @@ bool start(); bool stop(); - bool apply_config(DynamicPrintConfig *config); + bool apply_config(DynamicPrintConfig *config) + %code%{ RETVAL = THIS->apply_config(*config); %}; bool running(); }; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index a9744c312..16e2d84aa 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -74,7 +74,7 @@ _constant() Ref get_support_layer(int idx); bool step_done(PrintObjectStep step) - %code%{ RETVAL = THIS->state.is_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) %code%{ @@ -123,9 +123,9 @@ _constant() %code%{ RETVAL = THIS->regions.size(); %}; bool step_done(PrintStep step) - %code%{ RETVAL = THIS->state.is_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; bool object_step_done(PrintObjectStep step) - %code%{ RETVAL = THIS->step_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; SV* filament_stats() %code%{ diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index d4578303b..690d87120 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -185,7 +185,7 @@ TriangleMesh::slice(z) std::vector layers; TriangleMeshSlicer mslicer(THIS); - mslicer.slice(z_f, &layers); + mslicer.slice(z_f, &layers, [](){}); AV* layers_av = newAV(); size_t len = layers.size();