diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index a585f544e..ea06b0561 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -214,4 +214,14 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): SLAPrintObject::~SLAPrintObject() {} +TriangleMesh SLAPrintObject::support_mesh() const +{ + return make_cube(10., 10., 10.); +} + +TriangleMesh SLAPrintObject::pad_mesh() const +{ + return make_cube(10., 10., 10.); +} + } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 4e6627eec..668def6a8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -36,7 +36,23 @@ private: // Prevents erroneous use by other classes. public: const ModelObject* model_object() const { return m_model_object; } ModelObject* model_object() { return m_model_object; } + const Transform3d& trafo() const { return m_trafo; } + + struct Instance { + ModelID instance_id; + // Slic3r::Point objects in scaled G-code coordinates + Point shift; + // Rotation along the Z axis, in radians. + float rotation; + }; + const std::vector& instances() const { return m_instances; } + + // Get a support mesh centered around origin in XY, and with zero rotation around Z applied. + // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. TriangleMesh support_mesh() const; + // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied. + // Support mesh is only valid if this->is_step_done(slaposPad) is true. + TriangleMesh pad_mesh() const; // I refuse to grantee copying (Tamas) SLAPrintObject(const SLAPrintObject&) = delete; @@ -54,12 +70,6 @@ protected: { this->m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } - struct Instance { - // Slic3r::Point objects in scaled G-code coordinates - Point shift; - // Rotation along the Z axis, in radians. - float rotation; - }; bool set_instances(const std::vector &instances); // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint. bool invalidate_step(SLAPrintObjectStep step); @@ -143,6 +153,8 @@ public: void render_supports(SLASupportRenderer& renderer); + const PrintObjects& objects() const { return m_objects; } + private: Model m_model; SLAPrinterConfig m_printer_config; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 565ef94fb..e381cb854 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -2,13 +2,14 @@ #include "3DScene.hpp" -#include "../../libslic3r/ExtrusionEntity.hpp" -#include "../../libslic3r/ExtrusionEntityCollection.hpp" -#include "../../libslic3r/Geometry.hpp" -#include "../../libslic3r/GCode/PreviewData.hpp" -#include "../../libslic3r/Print.hpp" -#include "../../libslic3r/Slicing.hpp" -#include "../../slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/ExtrusionEntityCollection.hpp" +#include "libslic3r/Geometry.hpp" +#include "libslic3r/GCode/PreviewData.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Slicing.hpp" +#include "slic3r/GUI/PresetBundle.hpp" #include "GCode/Analyzer.hpp" #include @@ -209,7 +210,9 @@ GLVolume::GLVolume(float r, float g, float b, float a) #endif // ENABLE_MODELVOLUME_TRANSFORM , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) - , composite_id(-1) + , object_id(-1) + , volume_id(-1) + , instance_id(-1) , extruder_id(0) , selected(false) , disabled(false) @@ -755,7 +758,9 @@ std::vector GLVolumeCollection::load_object( // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; + v.object_id = obj_idx; + v.volume_id = volume_idx; + v.instance_id = instance_idx; if (model_volume->is_model_part()) { v.set_convex_hull(model_volume->get_convex_hull()); @@ -780,6 +785,61 @@ std::vector GLVolumeCollection::load_object( return volumes_idx; } +// Load SLA auxiliary GLVolumes (for support trees or pad). +std::vector GLVolumeCollection::load_object_auxiliary( + const ModelObject *model_object, + const SLAPrintObject *print_object, + int obj_idx, + SLAPrintObjectStep milestone, + bool use_VBOs) +{ + std::vector volumes_idx; + // Find the SLAPrintObject's instance to it. + if (print_object->is_step_done(milestone)) { + // Get the support mesh. + TriangleMesh mesh; + switch (milestone) { + case slaposSupportTree: mesh = print_object->support_mesh(); break; + case slaposBasePool: mesh = print_object->pad_mesh(); break; + default: + assert(false); + } + // Convex hull is required for out of print bed detection. + TriangleMesh convex_hull = mesh.convex_hull_3d(); + const std::vector &instances = print_object->instances(); + std::map map_instances; + for (int i = 0; i < (int)model_object->instances.size(); ++ i) + map_instances[model_object->instances[i]->id()] = i; + for (const SLAPrintObject::Instance &instance : instances) { + auto model_instance_it = map_instances.find(instance.instance_id); + assert(model_instance_it != map_instances.end()); + const int instance_idx = model_instance_it->second; + const ModelInstance *model_instance = model_object->instances[instance_idx]; + volumes_idx.push_back(int(this->volumes.size())); + float color[4] { 0.f, 0.f, 1.f, 1.f }; + this->volumes.emplace_back(new GLVolume(color)); + GLVolume &v = *this->volumes.back(); + if (use_VBOs) + v.indexed_vertex_array.load_mesh_full_shading(mesh); + else + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). + v.bounding_box = v.indexed_vertex_array.bounding_box(); + v.indexed_vertex_array.finalize_geometry(use_VBOs); + v.object_id = obj_idx; + v.volume_id = -1; // SLA supports + v.instance_id = instance_idx; + v.set_convex_hull(convex_hull); + v.is_modifier = false; + v.shader_outside_printer_detection_enabled = true; + v.set_instance_transformation(model_instance->get_transformation()); + // Leave the volume transformation at identity. + // v.set_volume_transformation(model_volume->get_transformation()); + } + } + + return volumes_idx; +} int GLVolumeCollection::load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width) @@ -851,7 +911,9 @@ int GLVolumeCollection::load_wipe_tower_preview( // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.composite_id = obj_idx * 1000000; + v.object_id = obj_idx; + v.volume_id = 0; + v.instance_id = 0; v.is_wipe_tower = true; v.shader_outside_printer_detection_enabled = ! size_unknown; return int(this->volumes.size() - 1); @@ -1848,6 +1910,11 @@ void _3DScene::set_print(wxGLCanvas* canvas, Print* print) s_canvas_mgr.set_print(canvas, print); } +void _3DScene::set_SLA_print(wxGLCanvas* canvas, SLAPrint* print) +{ + s_canvas_mgr.set_SLA_print(canvas, print); +} + void _3DScene::set_model(wxGLCanvas* canvas, Model* model) { s_canvas_mgr.set_model(canvas, model); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 87c9df385..e611e7f54 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -1,13 +1,13 @@ #ifndef slic3r_3DScene_hpp_ #define slic3r_3DScene_hpp_ -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Point.hpp" -#include "../../libslic3r/Line.hpp" -#include "../../libslic3r/TriangleMesh.hpp" -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/Model.hpp" -#include "../../slic3r/GUI/GLCanvas3DManager.hpp" +#include "libslic3r/libslic3r.h" +#include "libslic3r/Point.hpp" +#include "libslic3r/Line.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/Model.hpp" +#include "slic3r/GUI/GLCanvas3DManager.hpp" class wxBitmap; class wxWindow; @@ -16,6 +16,9 @@ namespace Slic3r { class Print; class PrintObject; +class SLAPrint; +class SLAPrintObject; +enum SLAPrintObjectStep; class Model; class ModelObject; class GCodePreviewData; @@ -290,8 +293,15 @@ public: float color[4]; // Color used to render this volume. float render_color[4]; - // An ID containing the object ID, volume ID and instance ID. - int composite_id; + // Object ID, which is equal to the index of the respective ModelObject in Model.objects array. + int object_id; + // Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array. + // If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject, + // and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports. + // Volume with a negative volume_id cannot be picked independently, it will pick the associated instance. + int volume_id; + // Instance ID, which is equal to the index of the respective ModelInstance in ModelObject.instances array. + int instance_id; // An ID containing the extruder ID (used to select color). int extruder_id; // Is this object selected? @@ -404,9 +414,9 @@ public: void set_convex_hull(const TriangleMesh& convex_hull); - int object_idx() const { return this->composite_id / 1000000; } - int volume_idx() const { return (this->composite_id / 1000) % 1000; } - int instance_idx() const { return this->composite_id % 1000; } + int object_idx() const { return this->object_id; } + int volume_idx() const { return this->volume_id; } + int instance_idx() const { return this->instance_id; } #if ENABLE_MODELVOLUME_TRANSFORM Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); } @@ -489,6 +499,14 @@ public: const std::string &color_by, bool use_VBOs); + // Load SLA auxiliary GLVolumes (for support trees or pad). + std::vector load_object_auxiliary( + const ModelObject *model_object, + const SLAPrintObject *print_object, + int obj_idx, + SLAPrintObjectStep milestone, + bool use_VBOs); + int load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width); @@ -556,6 +574,7 @@ public: static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); static void set_print(wxGLCanvas* canvas, Print* print); + static void set_SLA_print(wxGLCanvas* canvas, SLAPrint* print); static void set_model(wxGLCanvas* canvas, Model* model); static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index d9a60caa9..d6bb01c87 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -235,6 +235,12 @@ void BackgroundSlicingProcess::stop_internal() m_print->set_cancel_callback([](){}); } +bool BackgroundSlicingProcess::empty() const +{ + assert(m_print != nullptr); + return m_print->empty(); +} + std::string BackgroundSlicingProcess::validate() { assert(m_print != nullptr); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index b008437c0..da3eb776e 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -51,11 +51,15 @@ public: // Useful when the Model or configuration is being changed drastically. bool reset(); - // Validate the print. Returns an empty string if valid, returns an error message if invalid. - std::string validate(); // 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. Print::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config); + // After calling apply, the empty() call will report whether there is anything to slice. + bool empty() const; + // Validate the print. Returns an empty string if valid, returns an error message if invalid. + // Call validate before calling start(). + std::string validate(); + // Set the export path of the G-code. // Once the path is set, the G-code void schedule_export(const std::string &path); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 707359f3e..f9daa36d0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,16 +1,16 @@ #include "GLCanvas3D.hpp" -#include "../../admesh/stl.h" -#include "../../libslic3r/libslic3r.h" -#include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLShader.hpp" -#include "../../slic3r/GUI/GUI.hpp" -#include "../../slic3r/GUI/PresetBundle.hpp" -#include "../../slic3r/GUI/GLGizmo.hpp" -#include "../../libslic3r/ClipperUtils.hpp" -#include "../../libslic3r/PrintConfig.hpp" -#include "../../libslic3r/GCode/PreviewData.hpp" -#include "../../libslic3r/Geometry.hpp" +#include "admesh/stl.h" +#include "libslic3r/libslic3r.h" +#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLShader.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/GLGizmo.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/GCode/PreviewData.hpp" +#include "libslic3r/Geometry.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" @@ -25,7 +25,8 @@ #include // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. -#include "../../libslic3r/Print.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" #include #include @@ -3065,6 +3066,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_toolbar(*this) , m_config(nullptr) , m_print(nullptr) + , m_sla_print(nullptr) , m_model(nullptr) , m_dirty(true) , m_initialized(false) @@ -3247,7 +3249,11 @@ bool GLCanvas3D::move_volume_up(unsigned int id) if ((id > 0) && (id < (unsigned int)m_volumes.volumes.size())) { std::swap(m_volumes.volumes[id - 1], m_volumes.volumes[id]); - std::swap(m_volumes.volumes[id - 1]->composite_id, m_volumes.volumes[id]->composite_id); + GLVolume &v1 = *m_volumes.volumes[id - 1]; + GLVolume &v2 = *m_volumes.volumes[id]; + std::swap(v1.object_id, v2.object_id); + std::swap(v1.volume_id, v2.volume_id); + std::swap(v1.instance_id, v2.instance_id); return true; } @@ -3259,7 +3265,11 @@ bool GLCanvas3D::move_volume_down(unsigned int id) if ((id >= 0) && (id + 1 < (unsigned int)m_volumes.volumes.size())) { std::swap(m_volumes.volumes[id + 1], m_volumes.volumes[id]); - std::swap(m_volumes.volumes[id + 1]->composite_id, m_volumes.volumes[id]->composite_id); + GLVolume &v1 = *m_volumes.volumes[id + 1]; + GLVolume &v2 = *m_volumes.volumes[id]; + std::swap(v1.object_id, v2.object_id); + std::swap(v1.volume_id, v2.volume_id); + std::swap(v1.instance_id, v2.instance_id); return true; } @@ -3276,6 +3286,11 @@ void GLCanvas3D::set_print(Print* print) m_print = print; } +void GLCanvas3D::set_SLA_print(SLAPrint* print) +{ + m_sla_print = print; +} + void GLCanvas3D::set_model(Model* model) { m_model = model; @@ -3631,6 +3646,13 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) return std::vector(); } +std::vector GLCanvas3D::load_support_meshes(const Model& model, int obj_idx) +{ + std::vector volumes = m_volumes.load_object_auxiliary(model.objects[obj_idx], m_sla_print->objects()[obj_idx], obj_idx, slaposSupportTree, m_use_VBOs && m_initialized); + append(volumes, m_volumes.load_object_auxiliary(model.objects[obj_idx], m_sla_print->objects()[obj_idx], obj_idx, slaposBasePool, m_use_VBOs && m_initialized)); + return volumes; +} + int GLCanvas3D::get_first_volume_id(int obj_idx) const { for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) @@ -3682,11 +3704,14 @@ void GLCanvas3D::reload_scene(bool force) m_reload_delayed = false; + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); if (m_regenerate_volumes) { for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) { load_object(*m_model, obj_idx); + if (printer_technology == ptSLA) + load_support_meshes(*m_model, obj_idx); } } @@ -3694,7 +3719,8 @@ void GLCanvas3D::reload_scene(bool force) if (m_regenerate_volumes) { - if (m_config->has("nozzle_diameter") && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f182f2c37..eb45bddb5 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -22,6 +22,7 @@ namespace Slic3r { class GLShader; class ExPolygon; +class SLAPrint; namespace GUI { @@ -447,17 +448,25 @@ public: struct Cache { + // Cache of GLVolume derived transformation matrices, valid during mouse dragging. VolumesCache volumes_data; + // Center of the dragged selection, valid during mouse dragging. Vec3d dragging_center; + // Map from indices of ModelObject instances in Model::objects + // to a set of indices of ModelVolume instances in ModelObject::instances + // Here the index means a position inside the respective std::vector, not ModelID. ObjectIdxsToInstanceIdxsMap content; }; + // Volumes owned by GLCanvas3D. GLVolumePtrs* m_volumes; + // Model, not owned. Model* m_model; bool m_valid; EMode m_mode; EType m_type; + // set of indices to m_volumes IndicesList m_list; Cache m_cache; mutable BoundingBoxf3 m_bounding_box; @@ -692,6 +701,7 @@ private: Selection m_selection; DynamicPrintConfig* m_config; Print* m_print; + SLAPrint* m_sla_print; Model* m_model; bool m_dirty; @@ -745,6 +755,7 @@ public: void set_config(DynamicPrintConfig* config); void set_print(Print* print); + void set_SLA_print(SLAPrint* print); void set_model(Model* model); const Selection& get_selection() const { return m_selection; } @@ -810,6 +821,9 @@ public: std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(const Model& model, int obj_idx); + // Load SLA support tree and SLA pad meshes into the scene, if available at the respective SLAPrintObject instances. + std::vector load_support_meshes(const Model& model, int obj_idx); + int get_first_volume_id(int obj_idx) const; int get_in_object_volume_id(int scene_vol_idx) const; diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp index d8ff798ed..d38b2f193 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -284,6 +284,14 @@ void GLCanvas3DManager::set_print(wxGLCanvas* canvas, Print* print) it->second->set_print(print); } + +void GLCanvas3DManager::set_SLA_print(wxGLCanvas* canvas, SLAPrint* print) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_SLA_print(print); +} + void GLCanvas3DManager::set_model(wxGLCanvas* canvas, Model* model) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 917b4a4e8..232c928f3 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -14,6 +14,7 @@ namespace Slic3r { class DynamicPrintConfig; class Print; +class SLAPrint; class Model; class ExPolygon; typedef std::vector ExPolygons; @@ -95,6 +96,7 @@ public: void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); void set_print(wxGLCanvas* canvas, Print* print); + void set_SLA_print(wxGLCanvas* canvas, SLAPrint* print); void set_model(wxGLCanvas* canvas, Model* model); void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c57ae381f..77b817a3d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -447,7 +447,7 @@ struct Sidebar::priv void Sidebar::priv::show_preset_comboboxes() { - const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA; + const bool showSLA = plater->printer_technology() == ptSLA; wxWindowUpdateLocker noUpdates_scrolled(scrolled); // scrolled->Freeze(); @@ -630,9 +630,8 @@ void Sidebar::update_presets(Preset::Type preset_type) case Preset::TYPE_PRINTER: { - PrinterTechnology printer_technology = preset_bundle.printers.get_edited_preset().printer_technology(); // Update the print choosers to only contain the compatible presets, update the dirty flags. - if (printer_technology == ptFFF) + if (p->plater->printer_technology() == ptFFF) preset_bundle.prints.update_platter_ui(p->combo_print); else preset_bundle.sla_materials.update_platter_ui(p->combo_sla_material); @@ -640,7 +639,7 @@ void Sidebar::update_presets(Preset::Type preset_type) preset_bundle.printers.update_platter_ui(p->combo_printer); // Update the filament choosers to only contain the compatible presets, update the color preview, // update the dirty flags. - if (printer_technology == ptFFF) { + if (p->plater->printer_technology() == ptFFF) { for (size_t i = 0; i < p->combos_filament.size(); ++ i) preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); } @@ -785,7 +784,7 @@ void Sidebar::show_buttons(const bool show) TabPrinter *tab = dynamic_cast(wxGetApp().tab_panel()->GetPage(i)); if (!tab) continue; - if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) { + if (p->plater->printer_technology() == ptFFF) { p->btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty()); } break; @@ -875,10 +874,11 @@ struct Plater::priv // Data Slic3r::DynamicPrintConfig *config; - Slic3r::Print print; - Slic3r::SLAPrint sla_print; - Slic3r::Model model; - Slic3r::GCodePreviewData gcode_preview_data; + Slic3r::Print print; + Slic3r::SLAPrint sla_print; + Slic3r::Model model; + PrinterTechnology printer_technology = ptFFF; + Slic3r::GCodePreviewData gcode_preview_data; // GUI elements wxNotebook *notebook; @@ -921,8 +921,21 @@ struct Plater::priv void split_object(); void split_volume(); void schedule_background_process(); + // Update background processing thread from the current config and Model. + enum UpdateBackgroundProcessReturnState { + // update_background_process() reports, that the Print / SLAPrint was updated in a way, + // that the background process was invalidated and it needs to be re-run. + UPDATE_BACKGROUND_PROCESS_RESTART = 1, + // update_background_process() reports, that the Print / SLAPrint was updated in a way, + // that a scene needs to be refreshed (you should call _3DScene::reload_scene(canvas3D, false)) + UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE = 2, + // update_background_process() reports, that the Print / SLAPrint is invalid, and the error message + // was sent to the status line. + UPDATE_BACKGROUND_PROCESS_INVALID = 4, + }; + // returns bit mask of UpdateBackgroundProcessReturnState + unsigned int update_background_process(); void async_apply_config(); - void start_background_process(); void reload_from_disk(); void export_object_stl(); void fix_through_netfabb(const int obj_idx); @@ -988,7 +1001,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : background_process.set_sliced_event(EVT_SLICING_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED); // Default printer technology for default config. - background_process.select_technology(q->printer_technology()); + background_process.select_technology(this->printer_technology); // Register progress callback from the Print class to the Platter. print.set_status_callback([this](int percent, const std::string &message) { wxCommandEvent event(EVT_PROGRESS_BAR); @@ -1009,6 +1022,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : // XXX: more config from 3D.pm _3DScene::set_model(canvas3D, &model); _3DScene::set_print(canvas3D, &print); + _3DScene::set_SLA_print(canvas3D, &sla_print); _3DScene::set_config(canvas3D, config); _3DScene::enable_gizmos(canvas3D, true); _3DScene::enable_toolbar(canvas3D, true); @@ -1091,6 +1105,11 @@ void Plater::priv::update(bool force_autocenter) model.center_instances_around_point(bed_center); } + if (this->printer_technology == ptSLA) { + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + this->update_background_process(); + } _3DScene::reload_scene(canvas3D, false); preview->reset_gcode_preview_data(); preview->reload_print(); @@ -1610,10 +1629,18 @@ void Plater::priv::schedule_background_process() this->background_process_timer.Start(500, wxTIMER_ONE_SHOT); } -void Plater::priv::async_apply_config() +// Update background processing thread from the current config and Model. +// Returns a bitmask of UpdateBackgroundProcessReturnState. +unsigned int Plater::priv::update_background_process() { + // bitmap of enum UpdateBackgroundProcessReturnState + unsigned int return_state = 0; + + // If the async_apply_config() was not called by the timer, kill the timer, so the async_apply_config() + // will not be called again in vain. + this->background_process_timer.Stop(); + DynamicPrintConfig config = wxGetApp().preset_bundle->full_config(); - auto printer_technology = config.opt_enum("printer_technology"); BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.opt("bed_shape")->values)); BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(config.opt_float("max_print_height")))); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. @@ -1634,40 +1661,55 @@ void Plater::priv::async_apply_config() // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. // Otherwise they will be just refreshed. this->gcode_preview_data.reset(); - if (printer_technology == ptFFF) { + switch (this->printer_technology) { + case ptFFF: if (this->preview != nullptr) + // If the preview is not visible, the following line just invalidates the preview, + // but the G-code paths are calculated first once the preview is made visible. this->preview->reload_print(); // We also need to reload 3D scene because of the wipe tower preview box if (this->config->opt_bool("wipe_tower")) { // std::vector selections = this->collect_selections(); // Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections); - // Slic3r::_3DScene::reload_scene(this->canvas3D, 1); + return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE; } + break; + case ptSLA: + //FIXME as of now the Print::APPLY_STATUS_INVALIDATED is not reliable, and + // currently the scene refresh is expensive and loses selection. + //return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE; + break; } } - if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->get_config("background_processing") == "1" && - this->background_process.start()) - this->statusbar()->set_cancel_callback([this]() { - this->statusbar()->set_status_text(L("Cancelling")); - this->background_process.stop(); - }); + + if (! this->background_process.empty()) { + std::string err = this->background_process.validate(); + if (err.empty()) { + if (invalidated != Print::APPLY_STATUS_UNCHANGED) + return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; + } else { + // The print is not valid. + GUI::show_error(this->q, _(err)); + return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; + } + } + return return_state; } -void Plater::priv::start_background_process() +void Plater::priv::async_apply_config() { - if (this->background_process.running()) - return; - // Don't start process thread if Print is not valid. - std::string err = this->background_process.validate(); - if (! err.empty()) { - this->statusbar()->set_status_text(err); - } else { - // Copy the names of active presets into the placeholder parser. - //FIXME how to generate a file name for the SLA printers? - wxGetApp().preset_bundle->export_selections(this->q->print().placeholder_parser()); - // Start the background process. - this->background_process.start(); - } + // bitmask of UpdateBackgroundProcessReturnState + unsigned int state = this->update_background_process(); + if (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) + _3DScene::reload_scene(canvas3D, false); + if ((state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 && this->get_config("background_processing") == "1") { + // The print is valid and it can be started. + if (this->background_process.start()) + this->statusbar()->set_cancel_callback([this]() { + this->statusbar()->set_status_text(L("Cancelling")); + this->background_process.stop(); + }); + } } void Plater::priv::reload_from_disk() @@ -1716,6 +1758,13 @@ void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) const auto current_id = notebook->GetCurrentPage()->GetId(); if (current_id == canvas3D->GetId()) { if (_3DScene::is_reload_delayed(canvas3D)) { + // Delayed loading of the 3D scene. + if (this->printer_technology == ptSLA) { + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) + this->schedule_background_process(); + } _3DScene::reload_scene(canvas3D, true); } // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) @@ -1811,22 +1860,18 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) //$self->object_list_changed; // refresh preview - if (this->preview != nullptr) - this->preview->reload_print(); - - // TODO: this needs to be implemented somehow - if(q->printer_technology() == PrinterTechnology::ptSLA) { - - class Renderer: public SLASupportRenderer { - public: - void add_pillar(const Mesh&, ClickCb ) override {} - void add_head(const Mesh&, ClickCb) override {} - void add_bridge(const Mesh&, ClickCb) override {} - void add_junction(const Mesh&, ClickCb) override {} - void add_pad(const Mesh&, ClickCb) override {} - } renderer; - - sla_print.render_supports(renderer); + switch (this->printer_technology) { + case ptFFF: + if (this->preview != nullptr) + this->preview->reload_print(); + break; + case ptSLA: + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) + this->schedule_background_process(); + _3DScene::reload_scene(canvas3D, true); + break; } } @@ -2264,21 +2309,24 @@ void Plater::export_3mf() } } - void Plater::reslice() { - // explicitly cancel a previous thread and start a new one. - // Don't reslice if export of G-code or sending to OctoPrint is running. -// if (! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { - // Stop the background processing threads, stop the async update timer. -// this->p->stop_background_process(); - // Rather perform one additional unnecessary update of the print object instead of skipping a pending async update. - this->p->async_apply_config(); - this->p->statusbar()->set_cancel_callback([this]() { - this->p->statusbar()->set_status_text(L("Cancelling")); - this->p->background_process.stop(); - }); - this->p->start_background_process(); + //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. + // bitmask of UpdateBackgroundProcessReturnState + unsigned int state = this->p->update_background_process(); + if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) + _3DScene::reload_scene(this->p->canvas3D, false); + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running()) { + // The print is valid and it can be started. + // Copy the names of active presets into the placeholder parser. + //FIXME how to generate a file name for the SLA printers? + wxGetApp().preset_bundle->export_selections(this->print().placeholder_parser()); + if (this->p->background_process.start()) + this->p->statusbar()->set_cancel_callback([this]() { + this->p->statusbar()->set_status_text(L("Cancelling")); + this->p->background_process.stop(); + }); + } } void Plater::send_gcode() @@ -2317,8 +2365,10 @@ void Plater::on_config_change(const DynamicPrintConfig &config) bool update_scheduled = false; for (auto opt_key : p->config->diff(config)) { p->config->set_key_value(opt_key, config.option(opt_key)->clone()); - if (opt_key == "printer_technology") - p->background_process.select_technology(config.opt_enum(opt_key)); + if (opt_key == "printer_technology") { + p->printer_technology = config.opt_enum(opt_key); + p->background_process.select_technology(this->printer_technology()); + } else if (opt_key == "bed_shape") { if (p->canvas3D) _3DScene::set_bed_shape(p->canvas3D, p->config->option(opt_key)->values); if (p->preview) p->preview->set_bed_shape(p->config->option(opt_key)->values); @@ -2384,7 +2434,7 @@ wxGLCanvas* Plater::canvas3D() PrinterTechnology Plater::printer_technology() const { - return wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + return p->printer_technology; } void Plater::changed_object(int obj_idx)