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

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-03-23 13:41:20 +0300
committerbubnikv <bubnikv@gmail.com>2018-03-23 13:41:20 +0300
commite931f7501001feb8e2af29489e218ecca7080f48 (patch)
tree0f9e937e41d9ae71fcd037d283931f14b180aaa1 /xs
parent86b79f89ad423e2dacaadb7465df51d9f9878862 (diff)
Work in progress: Good bye, Perl Threads!
Diffstat (limited to 'xs')
-rw-r--r--xs/CMakeLists.txt3
-rw-r--r--xs/lib/Slic3r/XS.pm7
-rw-r--r--xs/src/libslic3r/Print.cpp76
-rw-r--r--xs/src/libslic3r/Print.hpp36
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp1
-rw-r--r--xs/src/libslic3r/PrintObject.cpp541
-rw-r--r--xs/src/perlglue.cpp1
-rw-r--r--xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp138
-rw-r--r--xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp82
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp20
-rw-r--r--xs/src/slic3r/GUI/GUI.hpp7
-rw-r--r--xs/t/20_print.t2
-rw-r--r--xs/xsp/GUI.xsp6
-rw-r--r--xs/xsp/GUI_BackgroundSlicingProcess.xsp23
-rw-r--r--xs/xsp/Print.xsp100
-rw-r--r--xs/xsp/my.map2
-rw-r--r--xs/xsp/typemap.xspt2
17 files changed, 712 insertions, 335 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 63b99a835..0a76721a4 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -171,6 +171,8 @@ add_library(libslic3r STATIC
add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/AppConfig.cpp
${LIBDIR}/slic3r/GUI/AppConfig.hpp
+ ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.cpp
+ ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.hpp
${LIBDIR}/slic3r/GUI/3DScene.cpp
${LIBDIR}/slic3r/GUI/3DScene.hpp
${LIBDIR}/slic3r/GUI/GLShader.cpp
@@ -331,6 +333,7 @@ set(XS_XSP_FILES
${XSP_DIR}/Geometry.xsp
${XSP_DIR}/GUI.xsp
${XSP_DIR}/GUI_AppConfig.xsp
+ ${XSP_DIR}/GUI_BackgroundSlicingProcess.xsp
${XSP_DIR}/GUI_3DScene.xsp
${XSP_DIR}/GUI_Preset.xsp
${XSP_DIR}/GUI_Tab.xsp
diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm
index 47a584343..5105ee0ad 100644
--- a/xs/lib/Slic3r/XS.pm
+++ b/xs/lib/Slic3r/XS.pm
@@ -239,23 +239,16 @@ sub new {
);
}
-package Slic3r::GUI::_3DScene::GLShader;
-sub CLONE_SKIP { 1 }
-
package Slic3r::GUI::_3DScene::GLVolume::Collection;
use overload
'@{}' => sub { $_[0]->arrayref },
'fallback' => 1;
-sub CLONE_SKIP { 1 }
-
package Slic3r::GUI::PresetCollection;
use overload
'@{}' => sub { $_[0]->arrayref },
'fallback' => 1;
-sub CLONE_SKIP { 1 }
-
package main;
for my $class (qw(
Slic3r::BridgeDetector
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index a3f5e231f..a74afdaec 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -5,11 +5,13 @@
#include "Flow.hpp"
#include "Geometry.hpp"
#include "SupportMaterial.hpp"
+#include "GCode.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include <algorithm>
#include <unordered_set>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/log/trivial.hpp>
namespace Slic3r {
@@ -793,6 +795,71 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
}
}
+// Slicing process, running at a background thread.
+void Print::process()
+{
+ BOOST_LOG_TRIVIAL(info) << "Staring the slicing process.";
+ for (PrintObject *obj : this->objects)
+ obj->make_perimeters();
+ this->set_status(70, "Infilling layers");
+ for (PrintObject *obj : this->objects)
+ obj->infill();
+ for (PrintObject *obj : this->objects)
+ obj->generate_support_material();
+ if (! this->state.is_done(psSkirt)) {
+ this->state.set_started(psSkirt);
+ this->skirt.clear();
+ if (this->has_skirt()) {
+ this->set_status(88, "Generating skirt");
+ this->_make_skirt();
+ }
+ this->state.set_done(psSkirt);
+ }
+ if (! this->state.is_done(psBrim)) {
+ this->state.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);
+ }
+ if (! this->state.is_done(psWipeTower)) {
+ this->state.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);
+ }
+ BOOST_LOG_TRIVIAL(info) << "Slicing process finished.";
+}
+
+// G-code export process, running at a background thread.
+// The export_gcode may die for various reasons (fails to process output_filename_format,
+// write error into the G-code, cannot execute post-processing scripts).
+// It is up to the caller to show an error message.
+void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
+{
+ // prerequisites
+ this->process();
+
+ // output everything to a G-code file
+ // The following call may die if the output_filename_format template substitution fails.
+ std::string path = this->output_filepath(path_template);
+ std::string message = "Exporting G-code";
+ if (! path.empty()) {
+ message += " to ";
+ message += path;
+ }
+ this->set_status(90, message);
+
+ // The following line may die for multiple reasons.
+ GCode gcode;
+ gcode.do_export(this, path.c_str(), preview_data);
+}
+
void Print::_make_skirt()
{
// First off we need to decide how tall the skirt must be.
@@ -978,10 +1045,6 @@ void Print::_clear_wipe_tower()
void Print::_make_wipe_tower()
{
- this->_clear_wipe_tower();
- if (! this->has_wipe_tower())
- return;
-
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
if (! m_tool_ordering.has_wipe_tower())
@@ -1153,9 +1216,4 @@ std::string Print::output_filepath(const std::string &path)
return path;
}
-void Print::set_status(int percent, const std::string &message)
-{
- printf("Print::status %d => %s\n", percent, message.c_str());
-}
-
}
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index c56e64c6c..dfde2650a 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -5,6 +5,7 @@
#include <set>
#include <vector>
#include <string>
+#include <functional>
#include "BoundingBox.hpp"
#include "Flow.hpp"
#include "PrintConfig.hpp"
@@ -23,6 +24,7 @@ namespace Slic3r {
class Print;
class PrintObject;
class ModelObject;
+class GCodePreviewData;
// Print step IDs for keeping track of the print state.
enum PrintStep {
@@ -190,23 +192,26 @@ public:
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
+private:
+ void slice();
+ void make_perimeters();
+ void prepare_infill();
+ void infill();
+ void generate_support_material();
+
void _slice();
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
- void _prepare_infill();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
- void _make_perimeters();
- void _infill();
void clip_fill_surfaces();
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
-private:
Print* _print;
ModelObject* _model_object;
Points _copies; // Slic3r::Point objects in scaled G-code coordinates
@@ -232,7 +237,6 @@ public:
PrintObjectPtrs objects;
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
- // TODO: status_cb
std::string estimated_print_time;
double total_used_filament, total_extruded_volume, total_cost, total_weight;
std::map<size_t, float> filament_stats;
@@ -283,13 +287,11 @@ public:
bool has_support_material() const;
void auto_assign_extruders(ModelObject* model_object) const;
- void _make_skirt();
- void _make_brim();
+ void process();
+ void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
// Wipe tower support.
bool has_wipe_tower() const;
- void _clear_wipe_tower();
- void _make_wipe_tower();
// Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
// Cache it here, so it does not need to be recalculated during the G-code generation.
ToolOrdering m_tool_ordering;
@@ -301,8 +303,14 @@ public:
std::string output_filename();
std::string output_filepath(const std::string &path);
+ typedef std::function<void(int, const std::string&)> status_callback_type;
+ void set_status_callback(status_callback_type cb) { m_status_callback = cb; }
+ void reset_status_callback() { m_status_callback = nullptr; }
// Calls a registered callback to update the status.
- void set_status(int percent, const std::string &message);
+ void set_status(int percent, const std::string &message) {
+ if (m_status_callback) m_status_callback(percent, message);
+ else printf("%d => %s\n", percent, message.c_str());
+ }
// Cancel the running computation. Stop execution of all the background threads.
void cancel() { m_canceled = true; }
// Cancel the running computation. Stop execution of all the background threads.
@@ -314,8 +322,14 @@ private:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
+ void _make_skirt();
+ void _make_brim();
+ void _clear_wipe_tower();
+ void _make_wipe_tower();
+
// Has the calculation been canceled?
- tbb::atomic<bool> m_canceled;
+ tbb::atomic<bool> m_canceled;
+ status_callback_type m_status_callback;
};
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index d7ee1e591..298fe9207 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -111,6 +111,7 @@ PrintConfigDef::PrintConfigDef()
"with cooling (use a fan) before tweaking this.");
def->cli = "bridge-flow-ratio=f";
def->min = 0;
+ def->max = 2;
def->default_value = new ConfigOptionFloat(1);
def = this->add("bridge_speed", coFloat);
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index c61fc102b..dd2c4e377 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -31,6 +31,8 @@
#include <cassert>
#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) :
@@ -104,6 +106,305 @@ bool PrintObject::reload_model_instances()
return this->set_copies(copies);
}
+// 1) Decides Z positions of the layers,
+// 2) Initializes layers and their regions
+// 3) Slices the object meshes
+// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
+// 5) Applies size compensation (offsets the slices in XY plane)
+// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
+// Resulting expolygons of layer regions are marked as Internal.
+//
+// this should be idempotent
+void PrintObject::slice()
+{
+ if (this->state.is_done(posSlice))
+ return;
+ this->state.set_started(posSlice);
+ this->_print->set_status(10, "Processing triangulated mesh");
+ this->_slice();
+ // 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();
+ if (! warning.empty())
+ BOOST_LOG_TRIVIAL(info) << warning;
+ // Simplify slices if required.
+ if (this->_print->config.resolution)
+ 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);
+}
+
+// 1) Merges typed region slices into stInternal type.
+// 2) Increases an "extra perimeters" counter at region slices where needed.
+// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
+void PrintObject::make_perimeters()
+{
+ // prerequisites
+ this->slice();
+
+ if (this->state.is_done(posPerimeters))
+ return;
+
+ this->state.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)
+ (*layer_it)->merge_slices();
+ this->typed_slices = false;
+ this->state.invalidate(posPrepareInfill);
+ }
+
+ // compare each layer to the one below, and mark those slices needing
+ // one additional inner perimeter, like the top of domed objects-
+
+ // this algorithm makes sure that at least one perimeter is overlapping
+ // but we don't generate any extra perimeter if fill density is zero, as they would be floating
+ // inside the object - infill_only_where_needed should be the method of choice for printing
+ // hollow objects
+ FOREACH_REGION(this->_print, region_it) {
+ size_t region_id = region_it - this->_print->regions.begin();
+ const PrintRegion &region = **region_it;
+
+
+ if (!region.config.extra_perimeters
+ || region.config.perimeters == 0
+ || region.config.fill_density == 0
+ || this->layer_count() < 2)
+ continue;
+
+ BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, this->layers.size() - 1),
+ [this, &region, region_id](const tbb::blocked_range<size_t>& range) {
+ for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ PARALLEL_FOR_CANCEL;
+ 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;
+ // Filter upper layer polygons in intersection_ppl by their bounding boxes?
+ // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
+ const double total_loop_length = total_length(upper_layerm_polygons);
+ const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
+ const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
+ const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
+ const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
+
+ for (Surface &slice : layerm.slices.surfaces) {
+ for (;;) {
+ // compute the total thickness of perimeters
+ const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
+ + (region.config.perimeters-1 + slice.extra_perimeters) * perimeter_spacing;
+ // define a critical area where we don't want the upper slice to fall into
+ // (it should either lay over our perimeters or outside this area)
+ const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5);
+ const Polygons critical_area = diff(
+ offset(slice.expolygon, float(- perimeters_thickness)),
+ offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth))
+ );
+ // check whether a portion of the upper slices falls inside the critical area
+ const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area);
+ // only add an additional loop if at least 30% of the slice loop would benefit from it
+ if (total_length(intersection) <= total_loop_length*0.3)
+ break;
+ /*
+ if (0) {
+ require "Slic3r/SVG.pm";
+ Slic3r::SVG::output(
+ "extra.svg",
+ no_arrows => 1,
+ expolygons => union_ex($critical_area),
+ polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
+ );
+ }
+ */
+ ++ slice.extra_perimeters;
+ }
+ #ifdef DEBUG
+ if (slice.extra_perimeters > 0)
+ printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx);
+ #endif
+ }
+ }
+ });
+ BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end";
+ }
+
+ BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, this->layers.size()),
+ [this](const tbb::blocked_range<size_t>& range) {
+ for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ PARALLEL_FOR_CANCEL;
+ this->layers[layer_idx]->make_perimeters();
+ }
+ }
+ );
+ BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
+
+ /*
+ simplify slices (both layer and region slices),
+ we only need the max resolution for perimeters
+ ### This makes this method not-idempotent, so we keep it disabled for now.
+ ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
+ */
+
+ this->state.set_done(posPerimeters);
+}
+
+void PrintObject::prepare_infill()
+{
+ if (this->state.is_done(posPrepareInfill))
+ return;
+
+ this->state.set_started(posPrepareInfill);
+ this->_print->set_status(30, "Preparing infill");
+
+ // This will assign a type (top/bottom/internal) to $layerm->slices.
+ // Then the classifcation of $layerm->slices is transfered onto
+ // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
+ // by the cummulative area of the previous $layerm->fill_surfaces.
+ this->detect_surfaces_type();
+
+ // 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)
+ region->prepare_fill_surfaces();
+
+ // this will detect bridges and reverse bridges
+ // and rearrange top/bottom/internal surfaces
+ // It produces enlarged overlapping bridging areas.
+ //
+ // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap.
+ // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap.
+ // 3) Clip the internal surfaces by the grown top/bottom surfaces.
+ // 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();
+
+ // Add solid fills to ensure the shell vertical thickness.
+ this->discover_vertical_shells();
+
+ // Debugging output.
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
+ for (const Layer *layer : this->layers) {
+ LayerRegion *layerm = layer->regions[region_id];
+ layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
+ layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final");
+ } // for each layer
+ } // for each region
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ // Detect, which fill surfaces are near external layers.
+ // They will be split in internal and internal-solid surfaces.
+ // The purpose is to add a configurable number of solid layers to support the TOP surfaces
+ // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces
+ // to close these surfaces reliably.
+ //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters?
+ this->discover_horizontal_shells();
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
+ for (const Layer *layer : this->layers) {
+ LayerRegion *layerm = layer->regions[region_id];
+ layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
+ layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final");
+ } // for each layer
+ } // for each region
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ // Only active if config->infill_only_where_needed. This step trims the sparse infill,
+ // so it acts as an internal support. It maintains all other infill types intact.
+ // Here the internal surfaces and perimeters have to be supported by the sparse infill.
+ //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
+ // 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();
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
+ for (const Layer *layer : this->layers) {
+ LayerRegion *layerm = layer->regions[region_id];
+ layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
+ layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final");
+ } // for each layer
+ } // for each region
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ // 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();
+
+ // combine fill surfaces to honor the "infill every N layers" option
+ this->combine_infill();
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
+ for (const Layer *layer : this->layers) {
+ LayerRegion *layerm = layer->regions[region_id];
+ layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
+ layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final");
+ } // for each layer
+ } // for each region
+ for (const Layer *layer : this->layers) {
+ layer->export_region_slices_to_svg_debug("9_prepare_infill-final");
+ layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final");
+ } // for each layer
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ this->state.set_done(posPrepareInfill);
+}
+
+void PrintObject::infill()
+{
+ // prerequisites
+ this->prepare_infill();
+
+ if (! this->state.is_done(posInfill)) {
+ this->state.set_started(posInfill);
+ BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, this->layers.size()),
+ [this](const tbb::blocked_range<size_t>& range) {
+ for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ PARALLEL_FOR_CANCEL;
+ this->layers[layer_idx]->make_fills();
+ }
+ }
+ );
+ 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);
+ }
+}
+
+void PrintObject::generate_support_material()
+{
+ if (! this->state.is_done(posSupportMaterial)) {
+ this->state.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->state.set_done(posSupportMaterial);
+ char stats[128];
+ //FIXME this does not belong here! Why should the status bar be updated with the object weight
+ // at the end of object's support.?
+ sprintf(stats, "Weight: %.1lfg, Cost: %.1lf", this->_print->total_weight, this->_print->total_cost);
+ this->_print->set_status(85, stats);
+ }
+}
+
void PrintObject::clear_layers()
{
for (Layer *l : this->layers)
@@ -282,105 +583,6 @@ bool PrintObject::has_support_material() const
|| this->config.support_material_enforce_layers > 0;
}
-void PrintObject::_prepare_infill()
-{
- // This will assign a type (top/bottom/internal) to $layerm->slices.
- // Then the classifcation of $layerm->slices is transfered onto
- // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
- // by the cummulative area of the previous $layerm->fill_surfaces.
- this->detect_surfaces_type();
-
- // 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)
- region->prepare_fill_surfaces();
-
- // this will detect bridges and reverse bridges
- // and rearrange top/bottom/internal surfaces
- // It produces enlarged overlapping bridging areas.
- //
- // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap.
- // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap.
- // 3) Clip the internal surfaces by the grown top/bottom surfaces.
- // 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();
-
- // Add solid fills to ensure the shell vertical thickness.
- this->discover_vertical_shells();
-
- // Debugging output.
-#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
- for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
- for (const Layer *layer : this->layers) {
- LayerRegion *layerm = layer->regions[region_id];
- layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
- layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final");
- } // for each layer
- } // for each region
-#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
-
- // Detect, which fill surfaces are near external layers.
- // They will be split in internal and internal-solid surfaces.
- // The purpose is to add a configurable number of solid layers to support the TOP surfaces
- // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces
- // to close these surfaces reliably.
- //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters?
- this->discover_horizontal_shells();
-
-#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
- for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
- for (const Layer *layer : this->layers) {
- LayerRegion *layerm = layer->regions[region_id];
- layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
- layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final");
- } // for each layer
- } // for each region
-#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
-
- // Only active if config->infill_only_where_needed. This step trims the sparse infill,
- // so it acts as an internal support. It maintains all other infill types intact.
- // Here the internal surfaces and perimeters have to be supported by the sparse infill.
- //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
- // 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();
-
-#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
- for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
- for (const Layer *layer : this->layers) {
- LayerRegion *layerm = layer->regions[region_id];
- layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
- layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final");
- } // for each layer
- } // for each region
-#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
-
- // 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();
-
- // combine fill surfaces to honor the "infill every N layers" option
- this->combine_infill();
-
-#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
- for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
- for (const Layer *layer : this->layers) {
- LayerRegion *layerm = layer->regions[region_id];
- layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
- layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final");
- } // for each layer
- } // for each region
- for (const Layer *layer : this->layers) {
- layer->export_region_slices_to_svg_debug("9_prepare_infill-final");
- layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final");
- } // for each layer
-#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
-}
-
// This function analyzes slices of a region (SurfaceCollection slices).
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Initially all slices are of type stInternal.
@@ -427,6 +629,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;
// 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);
@@ -564,6 +767,7 @@ void PrintObject::detect_surfaces_type()
tbb::blocked_range<size_t>(0, this->layers.size()),
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
+ PARALLEL_FOR_CANCEL;
LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region);
layerm->slices_to_fill_surfaces_clipped();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@@ -590,6 +794,7 @@ void PrintObject::process_external_surfaces()
tbb::blocked_range<size_t>(0, this->layers.size()),
[this, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ PARALLEL_FOR_CANCEL;
// 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]);
}
@@ -638,6 +843,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;
const Layer &layer = *this->layers[idx_layer];
DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer];
// Simulate single set of perimeters over all merged regions.
@@ -720,6 +926,7 @@ void PrintObject::discover_vertical_shells()
[this, idx_region, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
+ PARALLEL_FOR_CANCEL;
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;
@@ -748,7 +955,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;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static size_t debug_idx = 0;
++ debug_idx;
@@ -1265,6 +1472,7 @@ end:
tbb::blocked_range<size_t>(0, this->layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
+ PARALLEL_FOR_CANCEL;
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));
@@ -1348,6 +1556,7 @@ std::string PrintObject::_fix_slicing_errors()
tbb::blocked_range<size_t>(0, buggy_layers.size()),
[this, &buggy_layers](const tbb::blocked_range<size_t>& range) {
for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) {
+ PARALLEL_FOR_CANCEL;
size_t idx_layer = buggy_layers[buggy_layer_idx];
Layer *layer = this->layers[idx_layer];
assert(layer->slicing_errors);
@@ -1424,6 +1633,7 @@ void PrintObject::_simplify_slices(double distance)
tbb::blocked_range<size_t>(0, this->layers.size()),
[this, distance](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ PARALLEL_FOR_CANCEL;
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);
@@ -1433,137 +1643,6 @@ void PrintObject::_simplify_slices(double distance)
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end";
}
-void PrintObject::_make_perimeters()
-{
- if (this->state.is_done(posPerimeters)) return;
- this->state.set_started(posPerimeters);
-
- BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
-
- // merge slices if they were split into types
- if (this->typed_slices) {
- FOREACH_LAYER(this, layer_it)
- (*layer_it)->merge_slices();
- this->typed_slices = false;
- this->state.invalidate(posPrepareInfill);
- }
-
- // compare each layer to the one below, and mark those slices needing
- // one additional inner perimeter, like the top of domed objects-
-
- // this algorithm makes sure that at least one perimeter is overlapping
- // but we don't generate any extra perimeter if fill density is zero, as they would be floating
- // inside the object - infill_only_where_needed should be the method of choice for printing
- // hollow objects
- FOREACH_REGION(this->_print, region_it) {
- size_t region_id = region_it - this->_print->regions.begin();
- const PrintRegion &region = **region_it;
-
-
- if (!region.config.extra_perimeters
- || region.config.perimeters == 0
- || region.config.fill_density == 0
- || this->layer_count() < 2)
- continue;
-
- BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, this->layers.size() - 1),
- [this, &region, region_id](const tbb::blocked_range<size_t>& range) {
- for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
- 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;
- // Filter upper layer polygons in intersection_ppl by their bounding boxes?
- // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
- const double total_loop_length = total_length(upper_layerm_polygons);
- const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
- const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
- const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
- const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
-
- for (Surface &slice : layerm.slices.surfaces) {
- for (;;) {
- // compute the total thickness of perimeters
- const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
- + (region.config.perimeters-1 + slice.extra_perimeters) * perimeter_spacing;
- // define a critical area where we don't want the upper slice to fall into
- // (it should either lay over our perimeters or outside this area)
- const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5);
- const Polygons critical_area = diff(
- offset(slice.expolygon, float(- perimeters_thickness)),
- offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth))
- );
- // check whether a portion of the upper slices falls inside the critical area
- const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area);
- // only add an additional loop if at least 30% of the slice loop would benefit from it
- if (total_length(intersection) <= total_loop_length*0.3)
- break;
- /*
- if (0) {
- require "Slic3r/SVG.pm";
- Slic3r::SVG::output(
- "extra.svg",
- no_arrows => 1,
- expolygons => union_ex($critical_area),
- polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
- );
- }
- */
- ++ slice.extra_perimeters;
- }
- #ifdef DEBUG
- if (slice.extra_perimeters > 0)
- printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx);
- #endif
- }
- }
- });
- BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end";
- }
-
- BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, this->layers.size()),
- [this](const tbb::blocked_range<size_t>& range) {
- for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
- this->layers[layer_idx]->make_perimeters();
- }
- );
- BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
-
- /*
- simplify slices (both layer and region slices),
- we only need the max resolution for perimeters
- ### This makes this method not-idempotent, so we keep it disabled for now.
- ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
- */
-
- this->state.set_done(posPerimeters);
-}
-
-void PrintObject::_infill()
-{
- if (this->state.is_done(posInfill)) return;
- this->state.set_started(posInfill);
-
- BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, this->layers.size()),
- [this](const tbb::blocked_range<size_t>& range) {
- for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
- this->layers[layer_idx]->make_fills();
- }
- );
- 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);
-}
-
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
// so it acts as an internal support. It maintains all other infill types intact.
// Here the internal surfaces and perimeters have to be supported by the sparse infill.
diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp
index d7c9a590a..58d553966 100644
--- a/xs/src/perlglue.cpp
+++ b/xs/src/perlglue.cpp
@@ -56,6 +56,7 @@ REGISTER_CLASS(SurfaceCollection, "Surface::Collection");
REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2");
REGISTER_CLASS(TriangleMesh, "TriangleMesh");
REGISTER_CLASS(AppConfig, "GUI::AppConfig");
+REGISTER_CLASS(BackgroundSlicingProcess, "GUI::BackgroundSlicingProcess");
REGISTER_CLASS(GLShader, "GUI::_3DScene::GLShader");
REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume");
REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection");
diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp
new file mode 100644
index 000000000..900fdfa43
--- /dev/null
+++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -0,0 +1,138 @@
+#include "BackgroundSlicingProcess.hpp"
+#include "GUI.hpp"
+
+#include "../../libslic3r/Print.hpp"
+
+#include <wx/event.h>
+#include <wx/panel.h>
+
+//#undef NDEBUG
+#include <cassert>
+#include <stdexcept>
+
+namespace Slic3r {
+
+namespace GUI {
+ extern wxPanel *g_wxPlater;
+};
+
+void BackgroundSlicingProcess::thread_proc()
+{
+ std::unique_lock<std::mutex> lck(m_mutex);
+ // Let the caller know we are ready to run the background processing task.
+ m_state = STATE_IDLE;
+ lck.unlock();
+ m_condition.notify_one();
+ for (;;) {
+ assert(m_state == STATE_IDLE);
+ // 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; });
+ if (m_state == STATE_EXIT)
+ // Exiting this thread.
+ break;
+ // Process the background slicing task.
+ m_state = STATE_RUNNING;
+ lck.unlock();
+ std::string error;
+ try {
+ assert(m_print != nullptr);
+ m_print->process();
+ if (m_print->canceled())
+ return;
+ printf("PReparing m_event_sliced_id command\n");
+ wxCommandEvent evt(m_event_sliced_id);
+ printf("Issuing m_event_sliced_id command\n");
+ wxQueueEvent(GUI::g_wxPlater, evt.Clone());
+ GUI::g_wxPlater->ProcessWindowEvent(evt);
+ //GUI::g_wxPlater->ProcessEvent(evt);
+ printf("Done with m_event_sliced_id command\n");
+ m_print->export_gcode(m_output_path, m_gcode_preview_data);
+ } catch (std::exception &ex) {
+ error = ex.what();
+ } catch (...) {
+ error = "Unknown C++ exception.";
+ }
+ lck.lock();
+ m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED;
+ wxCommandEvent evt(m_event_finished_id);
+ evt.SetString(error);
+ evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0));
+ wxQueueEvent(GUI::g_wxPlater, evt.Clone());
+ lck.unlock();
+ // Let the UI thread wake up if it is waiting for the background task to finish.
+ m_condition.notify_one();
+ // Let the UI thread see the result.
+ }
+ m_state = STATE_EXITED;
+ lck.unlock();
+ // End of the background processing thread. The UI thread should join m_thread now.
+}
+
+void BackgroundSlicingProcess::join_background_thread()
+{
+ std::unique_lock<std::mutex> lck(m_mutex);
+ if (m_state == STATE_INITIAL) {
+ // Worker thread has not been started yet.
+ assert(! m_thread.joinable());
+ } else {
+ assert(m_state == STATE_IDLE);
+ assert(m_thread.joinable());
+ // Notify the worker thread to exit.
+ m_state = STATE_EXIT;
+ lck.unlock();
+ m_condition.notify_one();
+ // Wait until the worker thread exits.
+ m_thread.join();
+ }
+}
+
+bool BackgroundSlicingProcess::start()
+{
+ std::unique_lock<std::mutex> lck(m_mutex);
+ if (m_state == STATE_INITIAL) {
+ // The worker thread is not running yet. Start it.
+ assert(! m_thread.joinable());
+ m_thread = std::thread([this]{this->thread_proc();});
+ // Wait until the worker thread is ready to execute the background processing task.
+ m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; });
+ }
+ assert(m_state == STATE_IDLE || this->running());
+ if (this->running())
+ // The background processing thread is already running.
+ return false;
+ if (! this->idle())
+ throw std::runtime_error("Cannot start a background task, the worker thread is not idle.");
+ m_state = STATE_STARTED;
+ lck.unlock();
+ m_condition.notify_one();
+ return true;
+}
+
+bool BackgroundSlicingProcess::stop()
+{
+ std::unique_lock<std::mutex> lck(m_mutex);
+ if (m_state == STATE_INITIAL)
+ return false;
+ assert(this->running());
+ 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; });
+ }
+ 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)
+{
+ /*
+ // apply new config
+ my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config);
+ */
+ return true;
+}
+
+}; // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp
new file mode 100644
index 000000000..988138d2e
--- /dev/null
+++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -0,0 +1,82 @@
+#ifndef slic3r_GUI_BackgroundSlicingProcess_hpp_
+#define slic3r_GUI_BackgroundSlicingProcess_hpp_
+
+#include <string>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+class GCodePreviewData;
+class Print;
+
+// Support for the GUI background processing (Slicing and G-code generation).
+// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits.
+class BackgroundSlicingProcess
+{
+public:
+ BackgroundSlicingProcess() {}
+ ~BackgroundSlicingProcess() { this->stop(); this->join_background_thread(); }
+
+ void set_print(Print *print) { m_print = print; }
+ void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
+ void set_sliced_event(int event_id) { m_event_sliced_id = event_id; }
+ void set_finished_event(int event_id) { m_event_finished_id = event_id; }
+
+ // Start the background processing. Returns false if the background processing was already running.
+ bool start();
+ // Cancel the background processing. Returns false if the background processing was not running.
+ // A stopped background processing may be restarted with start().
+ bool stop();
+
+ // 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);
+
+ 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).
+ STATE_INITIAL = 0,
+ // m_thread is waiting for the task to execute.
+ STATE_IDLE,
+ STATE_STARTED,
+ // m_thread is executing a task.
+ STATE_RUNNING,
+ // m_thread finished executing a task, and it is waiting until the UI thread picks up the results.
+ STATE_FINISHED,
+ // m_thread finished executing a task, the task has been canceled by the UI thread, therefore the UI thread will not be notified.
+ STATE_CANCELED,
+ // m_thread exited the loop and it is going to finish. The UI thread should join on m_thread.
+ STATE_EXIT,
+ STATE_EXITED,
+ };
+ State state() const { return m_state; }
+ bool idle() const { return m_state == STATE_IDLE; }
+ bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
+
+private:
+ void thread_proc();
+ void start_background_thread();
+ void join_background_thread();
+
+ Print *m_print = nullptr;
+ GCodePreviewData *m_gcode_preview_data = nullptr;
+ std::string m_output_path;
+ // Thread, on which the background processing is executed. The thread will always be present
+ // and ready to execute the slicing process.
+ std::thread m_thread;
+ // Mutex and condition variable to synchronize m_thread with the UI thread.
+ std::mutex m_mutex;
+ std::condition_variable m_condition;
+ State m_state = STATE_INITIAL;
+
+ // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue.
+ int m_event_sliced_id = 0;
+ // wxWidgets command ID to be sent to the platter to inform that the task finished.
+ int m_event_finished_id = 0;
+};
+
+}; // namespace Slic3r
+
+#endif /* slic3r_GUI_BackgroundSlicingProcess_hpp_ */
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 0410b7969..270a5d198 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -47,6 +47,8 @@
#include "Preferences.hpp"
#include "PresetBundle.hpp"
+#include "../../libslic3r/Print.hpp"
+
namespace Slic3r { namespace GUI {
#if __APPLE__
@@ -172,6 +174,7 @@ void break_to_debugger()
wxApp *g_wxApp = nullptr;
wxFrame *g_wxMainFrame = nullptr;
wxNotebook *g_wxTabPanel = nullptr;
+wxPanel *g_wxPlater = nullptr;
AppConfig *g_AppConfig = nullptr;
PresetBundle *g_PresetBundle= nullptr;
@@ -197,6 +200,11 @@ void set_tab_panel(wxNotebook *tab_panel)
g_wxTabPanel = tab_panel;
}
+void set_plater(wxPanel *plater)
+{
+ g_wxPlater = plater;
+}
+
void set_app_config(AppConfig *app_config)
{
g_AppConfig = app_config;
@@ -507,6 +515,18 @@ void warning_catcher(wxWindow* parent, wxString message){
msg->ShowModal();
}
+// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID
+// to deliver a progress status message.
+void set_print_callback_event(Print *print, int id)
+{
+ print->set_status_callback([id](int percent, const std::string &message){
+ wxCommandEvent event(id);
+ event.SetInt(percent);
+ event.SetString(message);
+ wxQueueEvent(g_wxMainFrame, event.Clone());
+ });
+}
+
wxApp* get_app(){
return g_wxApp;
}
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 084b6de46..506454e56 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -11,6 +11,7 @@ class wxFrame;
class wxWindow;
class wxMenuBar;
class wxNotebook;
+class wxPanel;
class wxComboCtrl;
class wxString;
class wxArrayString;
@@ -23,6 +24,7 @@ namespace Slic3r {
class PresetBundle;
class PresetCollection;
+class Print;
class AppConfig;
class DynamicPrintConfig;
class TabIface;
@@ -73,6 +75,7 @@ void break_to_debugger();
void set_wxapp(wxApp *app);
void set_main_frame(wxFrame *main_frame);
void set_tab_panel(wxNotebook *tab_panel);
+void set_plater(wxPanel *plater);
void set_app_config(AppConfig *app_config);
void set_preset_bundle(PresetBundle *preset_bundle);
@@ -98,6 +101,10 @@ void show_error(wxWindow* parent, wxString message);
void show_info(wxWindow* parent, wxString message, wxString title);
void warning_catcher(wxWindow* parent, wxString message);
+// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID
+// to deliver a progress status message.
+void set_print_callback_event(Print *print, int id);
+
// load language saved at application config
bool load_language();
// save language at application config
diff --git a/xs/t/20_print.t b/xs/t/20_print.t
index e535cdd8c..0ef194ecf 100644
--- a/xs/t/20_print.t
+++ b/xs/t/20_print.t
@@ -10,8 +10,6 @@ use Test::More tests => 5;
my $print = Slic3r::Print->new;
isa_ok $print, 'Slic3r::Print';
isa_ok $print->config, 'Slic3r::Config::Static::Ref';
- isa_ok $print->default_object_config, 'Slic3r::Config::Static::Ref';
- isa_ok $print->default_region_config, 'Slic3r::Config::Static::Ref';
isa_ok $print->placeholder_parser, 'Slic3r::GCode::PlaceholderParser::Ref';
}
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index d306f12ce..a7f3bd554 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -32,6 +32,9 @@ void set_main_frame(SV *ui)
void set_tab_panel(SV *ui)
%code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %};
+
+void set_plater(SV *ui)
+ %code%{ Slic3r::GUI::set_plater((wxPanel*)wxPli_sv_2_object(aTHX_ ui, "Wx::Panel")); %};
void add_debug_menu(SV *ui, int event_language_change)
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
@@ -65,5 +68,8 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz
(wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
(wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %};
+void set_print_callback_event(Print *print, int id)
+ %code%{ Slic3r::GUI::set_print_callback_event(print, id); %};
+
std::string fold_utf8_to_ascii(const char *src)
%code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %};
diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp
new file mode 100644
index 000000000..d12c7f8e3
--- /dev/null
+++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp
@@ -0,0 +1,23 @@
+
+%module{Slic3r::XS};
+
+%{
+#include <xsinit.h>
+#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
+%}
+
+%name{Slic3r::GUI::BackgroundSlicingProcess} class BackgroundSlicingProcess {
+ BackgroundSlicingProcess();
+ ~BackgroundSlicingProcess();
+
+ void set_print(Print *print);
+ void set_gcode_preview_data(GCodePreviewData *gpd);
+ void set_sliced_event(int event_id);
+ void set_finished_event(int event_id);
+
+ bool start();
+ bool stop();
+ bool apply_config(DynamicPrintConfig *config);
+
+ bool running();
+};
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index cbc04a804..4ad4f74b6 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -27,7 +27,6 @@ _constant()
%}
-
%name{Slic3r::Print::Region} class PrintRegion {
// owned by Print, no constructor/destructor
@@ -39,16 +38,9 @@ _constant()
%code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %};
};
-
%name{Slic3r::Print::Object} class PrintObject {
// owned by Print, no constructor/destructor
- void add_region_volume(int region_id, int volume_id);
- std::vector<int> get_region_volumes(int region_id)
- %code%{
- if (0 <= region_id && region_id < THIS->region_volumes.size())
- RETVAL = THIS->region_volumes[region_id];
- %};
int region_count()
%code%{ RETVAL = THIS->print()->regions.size(); %};
@@ -67,57 +59,22 @@ _constant()
Points _shifted_copies()
%code%{ RETVAL = THIS->_shifted_copies; %};
- void set_shifted_copies(Points value)
- %code%{ THIS->_shifted_copies = value; %};
bool add_copy(Pointf* point)
%code%{ RETVAL = THIS->add_copy(*point); %};
bool delete_last_copy();
- bool delete_all_copies();
- bool set_copies(Points copies);
bool reload_model_instances();
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
- void set_layer_height_profile(std::vector<double> profile)
- %code%{ THIS->layer_height_profile = profile; %};
- size_t total_layer_count();
size_t layer_count();
- void clear_layers();
Ref<Layer> get_layer(int idx);
- Ref<Layer> add_layer(int id, coordf_t height, coordf_t print_z,
- coordf_t slice_z);
size_t support_layer_count();
- void clear_support_layers();
Ref<SupportLayer> get_support_layer(int idx);
bool step_done(PrintObjectStep step)
%code%{ RETVAL = THIS->state.is_done(step); %};
- void set_step_done(PrintObjectStep step)
- %code%{ THIS->state.set_done(step); %};
- void set_step_started(PrintObjectStep step)
- %code%{ THIS->state.set_started(step); %};
-
- void _slice();
- std::string _fix_slicing_errors();
- void _simplify_slices(double distance);
- void _prepare_infill();
- void detect_surfaces_type();
- void process_external_surfaces();
- void _make_perimeters();
- void _infill();
- void _generate_support_material();
-
- std::vector<double> get_layer_height_min_max()
- %code%{
- SlicingParameters slicing_params = THIS->slicing_parameters();
- RETVAL.push_back(slicing_params.min_layer_height);
- RETVAL.push_back(slicing_params.max_layer_height);
- RETVAL.push_back(slicing_params.first_print_layer_height);
- RETVAL.push_back(slicing_params.first_object_layer_height);
- RETVAL.push_back(slicing_params.layer_height);
- %};
void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
%code%{
@@ -129,25 +86,16 @@ _constant()
%};
void reset_layer_height_profile();
-
- int ptr()
- %code%{ RETVAL = (int)(intptr_t)THIS; %};
};
-
%name{Slic3r::Print} class Print {
Print();
~Print();
Ref<StaticPrintConfig> config()
%code%{ RETVAL = &THIS->config; %};
- Ref<StaticPrintConfig> default_object_config()
- %code%{ RETVAL = &THIS->default_object_config; %};
- Ref<StaticPrintConfig> default_region_config()
- %code%{ RETVAL = &THIS->default_region_config; %};
Ref<PlaceholderParser> placeholder_parser()
%code%{ RETVAL = &THIS->placeholder_parser; %};
- // TODO: status_cb
Ref<ExtrusionEntityCollection> skirt()
%code%{ RETVAL = &THIS->skirt; %};
Ref<ExtrusionEntityCollection> brim()
@@ -176,20 +124,7 @@ _constant()
%code%{ RETVAL = THIS->state.is_done(step); %};
bool object_step_done(PrintObjectStep step)
%code%{ RETVAL = THIS->step_done(step); %};
- void set_step_done(PrintStep step)
- %code%{ THIS->state.set_done(step); %};
- void set_step_started(PrintStep step)
- %code%{ THIS->state.set_started(step); %};
- void clear_filament_stats()
- %code%{
- THIS->filament_stats.clear();
- %};
- void set_filament_stats(int extruder_id, float length)
- %code%{
- THIS->filament_stats.insert(std::pair<size_t,float>(extruder_id, 0));
- THIS->filament_stats[extruder_id] += length;
- %};
SV* filament_stats()
%code%{
HV* hv = newHV();
@@ -203,7 +138,6 @@ _constant()
RETVAL = newRV_noinc((SV*)hv);
}
%};
- void _simplify_slices(double distance);
double max_allowed_layer_height() const;
bool has_support_material() const;
void auto_assign_extruders(ModelObject* model_object);
@@ -220,7 +154,6 @@ _constant()
bool apply_config(DynamicPrintConfig* config)
%code%{ RETVAL = THIS->apply_config(*config); %};
bool has_infinite_skirt();
- bool has_skirt();
std::vector<unsigned int> extruders() const;
int validate() %code%{
std::string err = THIS->validate();
@@ -230,16 +163,33 @@ _constant()
%};
Clone<BoundingBox> bounding_box();
Clone<BoundingBox> total_bounding_box();
- double skirt_first_layer_height();
- Clone<Flow> brim_flow();
- Clone<Flow> skirt_flow();
- void _make_skirt();
- void _make_brim();
+ void set_callback_event(int evt) %code%{
+ %};
- bool has_wipe_tower();
- void _clear_wipe_tower();
- void _make_wipe_tower();
+ void process() %code%{
+ try {
+ THIS->process();
+ } catch (std::exception& e) {
+ croak(e.what());
+ }
+ %};
+
+ void export_gcode_with_preview_data(char *path_template, GCodePreviewData *preview_data) %code%{
+ try {
+ THIS->export_gcode(path_template, preview_data);
+ } catch (std::exception& e) {
+ croak(e.what());
+ }
+ %};
+
+ void export_gcode(char *path_template) %code%{
+ try {
+ THIS->export_gcode(path_template, nullptr);
+ } catch (std::exception& e) {
+ croak(e.what());
+ }
+ %};
%{
diff --git a/xs/xsp/my.map b/xs/xsp/my.map
index 87a8d8d86..67693685a 100644
--- a/xs/xsp/my.map
+++ b/xs/xsp/my.map
@@ -217,6 +217,8 @@ Clone<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
AppConfig* O_OBJECT_SLIC3R
Ref<AppConfig> O_OBJECT_SLIC3R_T
+BackgroundSlicingProcess* O_OBJECT_SLIC3R
+Ref<BackgroundSlicingProcess> O_OBJECT_SLIC3R_T
GLShader* O_OBJECT_SLIC3R
Ref<GLShader> O_OBJECT_SLIC3R_T
diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt
index 0214a158d..461a12e74 100644
--- a/xs/xsp/typemap.xspt
+++ b/xs/xsp/typemap.xspt
@@ -196,6 +196,8 @@
%typemap{Clone<ModelInstancePtrs>}{simple};
%typemap{AppConfig*};
%typemap{Ref<AppConfig>}{simple};
+%typemap{BackgroundSlicingProcess*};
+%typemap{Ref<BackgroundSlicingProcess>}{simple};
%typemap{GLShader*};
%typemap{Ref<GLShader>}{simple};
%typemap{GLVolume*};