diff --git a/resources/images/back_up_ts_bk.svg b/resources/images/back_up_ts_bk.svg new file mode 100644 index 0000000000..75a6cbe0db --- /dev/null +++ b/resources/images/back_up_ts_bk.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/backup_current_use1.svg b/resources/images/backup_current_use1.svg new file mode 100644 index 0000000000..c37817cfae --- /dev/null +++ b/resources/images/backup_current_use1.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/backup_current_use2.svg b/resources/images/backup_current_use2.svg new file mode 100644 index 0000000000..30dfcdf41c --- /dev/null +++ b/resources/images/backup_current_use2.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/backup_tips_img.svg b/resources/images/backup_tips_img.svg new file mode 100644 index 0000000000..0640f07390 --- /dev/null +++ b/resources/images/backup_tips_img.svg @@ -0,0 +1,8 @@ + + + + Layer 1 + + + + \ No newline at end of file diff --git a/resources/images/bar_publish.svg b/resources/images/bar_publish.svg new file mode 100644 index 0000000000..51e5cb2d24 --- /dev/null +++ b/resources/images/bar_publish.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/bbl_cali_lines.svg b/resources/images/bbl_cali_lines.svg new file mode 100644 index 0000000000..a125cadd0b --- /dev/null +++ b/resources/images/bbl_cali_lines.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/block_notification_close.svg b/resources/images/block_notification_close.svg new file mode 100644 index 0000000000..a55fe49f7c --- /dev/null +++ b/resources/images/block_notification_close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/block_notification_close_hover.svg b/resources/images/block_notification_close_hover.svg new file mode 100644 index 0000000000..afb1dc2a7d --- /dev/null +++ b/resources/images/block_notification_close_hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/block_notification_error.svg b/resources/images/block_notification_error.svg new file mode 100644 index 0000000000..902027fae7 --- /dev/null +++ b/resources/images/block_notification_error.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/go_last_plate.svg b/resources/images/go_last_plate.svg new file mode 100644 index 0000000000..b5192f2774 --- /dev/null +++ b/resources/images/go_last_plate.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/images/go_next_plate.svg b/resources/images/go_next_plate.svg new file mode 100644 index 0000000000..cf7ee8a4cb --- /dev/null +++ b/resources/images/go_next_plate.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/images/link_more_error_close.svg b/resources/images/link_more_error_close.svg new file mode 100644 index 0000000000..4fb473c659 --- /dev/null +++ b/resources/images/link_more_error_close.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/link_more_error_open.svg b/resources/images/link_more_error_open.svg new file mode 100644 index 0000000000..06abce1fb4 --- /dev/null +++ b/resources/images/link_more_error_open.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/model_time.svg b/resources/images/model_time.svg new file mode 100644 index 0000000000..0808f4545d --- /dev/null +++ b/resources/images/model_time.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/model_weight.svg b/resources/images/model_weight.svg new file mode 100644 index 0000000000..31417e6569 --- /dev/null +++ b/resources/images/model_weight.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/monitir_err_close.svg b/resources/images/monitir_err_close.svg new file mode 100644 index 0000000000..745e129dac --- /dev/null +++ b/resources/images/monitir_err_close.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/monitir_err_open.svg b/resources/images/monitir_err_open.svg new file mode 100644 index 0000000000..f396d2a8f7 --- /dev/null +++ b/resources/images/monitir_err_open.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/plate_name_edit.svg b/resources/images/plate_name_edit.svg new file mode 100644 index 0000000000..c9266a127f --- /dev/null +++ b/resources/images/plate_name_edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/plate_name_edit_dark.svg b/resources/images/plate_name_edit_dark.svg new file mode 100644 index 0000000000..c9266a127f --- /dev/null +++ b/resources/images/plate_name_edit_dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/plate_name_edit_hover.svg b/resources/images/plate_name_edit_hover.svg new file mode 100644 index 0000000000..9359cd74b5 --- /dev/null +++ b/resources/images/plate_name_edit_hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/plate_name_edit_hover_dark.svg b/resources/images/plate_name_edit_hover_dark.svg new file mode 100644 index 0000000000..a9840d5ca9 --- /dev/null +++ b/resources/images/plate_name_edit_hover_dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/toolbar_meshboolean.svg b/resources/images/toolbar_meshboolean.svg new file mode 100644 index 0000000000..ecfc8349d3 --- /dev/null +++ b/resources/images/toolbar_meshboolean.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resources/images/toolbar_meshboolean_dark.svg b/resources/images/toolbar_meshboolean_dark.svg new file mode 100644 index 0000000000..7643f71734 --- /dev/null +++ b/resources/images/toolbar_meshboolean_dark.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resources/images/transparent.svg b/resources/images/transparent.svg new file mode 100644 index 0000000000..ee71b59f7b --- /dev/null +++ b/resources/images/transparent.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/transparent_ams_item.svg b/resources/images/transparent_ams_item.svg new file mode 100644 index 0000000000..6991cfa546 --- /dev/null +++ b/resources/images/transparent_ams_item.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/images/transparent_ams_lib.svg b/resources/images/transparent_ams_lib.svg new file mode 100644 index 0000000000..fa2a0b65f2 --- /dev/null +++ b/resources/images/transparent_ams_lib.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/transparent_color_picker.svg b/resources/images/transparent_color_picker.svg new file mode 100644 index 0000000000..5a41befa3f --- /dev/null +++ b/resources/images/transparent_color_picker.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/transparent_mapping_item.svg b/resources/images/transparent_mapping_item.svg new file mode 100644 index 0000000000..f1e577439a --- /dev/null +++ b/resources/images/transparent_mapping_item.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/transparent_material_item.svg b/resources/images/transparent_material_item.svg new file mode 100644 index 0000000000..9436fd87a9 --- /dev/null +++ b/resources/images/transparent_material_item.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/ts_bitmap_cube.svg b/resources/images/ts_bitmap_cube.svg new file mode 100644 index 0000000000..9a8403aff1 --- /dev/null +++ b/resources/images/ts_bitmap_cube.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/ts_custom_color_picker.svg b/resources/images/ts_custom_color_picker.svg new file mode 100644 index 0000000000..90b418a863 --- /dev/null +++ b/resources/images/ts_custom_color_picker.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index c1fd07e3ca..72f58a1355 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -341,8 +341,9 @@ const float bed3d_ax3s_default_tip_length = 5.0f; int CLI::run(int argc, char **argv) { // Mark the main thread for the debugger and for runtime checks. - set_current_thread_name("bambustu_main"); - + set_current_thread_name("orcaslicer_main"); + // Save the thread ID of the main thread. + save_main_thread_id(); #ifdef __WXGTK__ // On Linux, wxGTK has no support for Wayland, and the app crashes on // startup if gtk3 is used. This env var has to be set explicitly to @@ -2993,7 +2994,7 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) #if defined(_MSC_VER) || defined(__MINGW32__) extern "C" { - __declspec(dllexport) int __stdcall bambustu_main(int argc, wchar_t **argv) + __declspec(dllexport) int __stdcall orcaslicer_main(int argc, wchar_t **argv) { // Convert wchar_t arguments to UTF8. std::vector argv_narrow; diff --git a/src/BambuStudio_app_msvc.cpp b/src/BambuStudio_app_msvc.cpp index 1537f5cb68..b0509d1a9f 100644 --- a/src/BambuStudio_app_msvc.cpp +++ b/src/BambuStudio_app_msvc.cpp @@ -204,7 +204,7 @@ bool OpenGLVersionCheck::message_pump_exit = false; extern "C" { typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv); - Slic3rMainFunc bambustu_main = nullptr; + Slic3rMainFunc orcaslicer_main = nullptr; } extern "C" { @@ -292,19 +292,19 @@ int wmain(int argc, wchar_t **argv) } // resolve function address here - bambustu_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r, + orcaslicer_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r, #ifdef _WIN64 // there is just a single calling conversion, therefore no mangling of the function name. - "bambustu_main" + "orcaslicer_main" #else // stdcall calling convention declaration "_bambustu_main@8" #endif ); - if (bambustu_main == nullptr) { - printf("could not locate the function bambustu_main in OrcaSlicer.dll\n"); + if (orcaslicer_main == nullptr) { + printf("could not locate the function orcaslicer_main in OrcaSlicer.dll\n"); return -1; } // argc minus the trailing nullptr of the argv - return bambustu_main((int)argv_extended.size() - 1, argv_extended.data()); + return orcaslicer_main((int)argv_extended.size() - 1, argv_extended.data()); } } diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index cdb6275cec..1774eb28ac 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -193,6 +193,10 @@ namespace ImGui const wchar_t CollapseBtn = 0x0831; const wchar_t RevertBtn = 0x0832; + const wchar_t CloseBlockNotifButton = 0x0833; + const wchar_t CloseBlockNotifHoverButton = 0x0834; + const wchar_t BlockNotifErrorIcon = 0x0835; + // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 0c5ea4a606..5c838b3673 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -461,6 +461,11 @@ public: const PrintObjects& objects() const { return m_objects; } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. + const SLAPrintObject* get_print_object_by_model_object_id(ObjectID object_id) const { + auto it = std::find_if(m_objects.begin(), m_objects.end(), + [object_id](const SLAPrintObject* obj) { return obj->model_object()->id() == object_id; }); + return (it == m_objects.end()) ? nullptr : *it; + } const SLAPrintObject* get_object(ObjectID object_id) const { auto it = std::find_if(m_objects.begin(), m_objects.end(), [object_id](const SLAPrintObject *obj) { return obj->id() == object_id; }); diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index fc1a43a27d..b1568646f6 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -137,6 +137,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoSeam.hpp GUI/Gizmos/GLGizmoText.cpp GUI/Gizmos/GLGizmoText.hpp + GUI/Gizmos/GLGizmoMeshBoolean.cpp + GUI/Gizmos/GLGizmoMeshBoolean.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/Gizmos/GizmoObjectManipulation.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6198bf31db..06fec07eea 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -71,30 +71,34 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char // BBS std::vector> get_extruders_colors() { - unsigned char rgb_color[3] = {}; - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + unsigned char rgba_color[4] = {}; + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); std::vector> colors_out(colors.size()); - for (const std::string& color : colors) { - Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); - size_t color_idx = &color - &colors.front(); - colors_out[color_idx] = { float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f }; + for (const std::string &color : colors) { + Slic3r::GUI::BitmapCache::parse_color4(color, rgba_color); + size_t color_idx = &color - &colors.front(); + colors_out[color_idx] = { + float(rgba_color[0]) / 255.f, + float(rgba_color[1]) / 255.f, + float(rgba_color[2]) / 255.f, + float(rgba_color[3]) / 255.f, + }; } return colors_out; } - -std::array adjust_color_for_rendering(const std::array& colors) +float FullyTransparentMaterialThreshold = 0.1f; +float FullTransparentModdifiedToFixAlpha = 0.3f; +std::array adjust_color_for_rendering(const std::array &colors) { - if ((colors[0] < 0.1) && (colors[1] < 0.1) && (colors[2] < 0.1)) - { - std::array new_color; - new_color[0] = 0.1; - new_color[1] = 0.1; - new_color[2] = 0.1; - new_color[3] = colors[3]; - return new_color; - } - + if (colors[3] < FullyTransparentMaterialThreshold) { // completely transparent + std::array new_color; + new_color[0] = 1; + new_color[1] = 1; + new_color[2] = 1; + new_color[3] = FullTransparentModdifiedToFixAlpha; + return new_color; + } return colors; } @@ -514,8 +518,13 @@ void GLVolume::set_render_color() } } - if (force_transparent) - render_color[3] = color[3]; + if (force_transparent) { + if (color[3] < FullyTransparentMaterialThreshold) { + render_color[3] = FullTransparentModdifiedToFixAlpha; + } else { + render_color[3] = color[3]; + } + } //BBS set unprintable color if (!printable) { @@ -1017,12 +1026,21 @@ void GLWipeTowerVolume::render(bool with_outline) const } this->iva_per_colors[i].render(); } - + glsafe(::glPopMatrix()); if (this->is_left_handed()) glFrontFace(GL_CCW); } +bool GLWipeTowerVolume::IsTransparent() { + for (size_t i = 0; i < m_colors.size(); i++) { + if (m_colors[i][3] < 1.0f) { + return true; + } + } + return false; +} + std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -1211,8 +1229,12 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i) { GLVolume* volume = volumes[i]; bool is_transparent = (volume->render_color[3] < 1.0f); - if (((type == GLVolumeCollection::ERenderType::Opaque && !is_transparent) || - (type == GLVolumeCollection::ERenderType::Transparent && is_transparent) || + auto tempGlwipeTowerVolume = dynamic_cast(volume); + if (tempGlwipeTowerVolume) { + is_transparent = tempGlwipeTowerVolume->IsTransparent(); + } + if (((type == GLVolumeCollection::ERenderType::Opaque && !is_transparent) || + (type == GLVolumeCollection::ERenderType::Transparent && is_transparent) || type == GLVolumeCollection::ERenderType::All) && (! filter_func || filter_func(*volume))) list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0))); @@ -1236,8 +1258,17 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo return list; } +int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_support) const +{ + const DynamicPrintConfig& glb_cfg = GUI::wxGetApp().preset_bundle->prints.get_edited_preset().config; + enable_support = glb_cfg.opt_bool("enable_support"); + int support_threshold_angle = glb_cfg.opt_int("support_threshold_angle"); + return support_threshold_angle ; +} + //BBS: add outline drawing logic -void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func, bool with_outline) const +void GLVolumeCollection::render( + GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d &view_matrix, std::function filter_func, bool with_outline) const { GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func); if (to_render.empty()) @@ -1305,10 +1336,16 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab //use -1 ad a invalid type shader->set_uniform("print_volume.type", -1); } + + bool enable_support; + int support_threshold_angle = get_selection_support_threshold_angle(enable_support); + + float normal_z = -::cos(Geometry::deg2rad((float) support_threshold_angle)); + shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); - shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); + shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.volume_world_normal_matrix", static_cast(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast())); - shader->set_uniform("slope.normal_z", m_slope.normal_z); + shader->set_uniform("slope.normal_z", normal_z); #if ENABLE_ENVIRONMENT_MAP unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id(); @@ -1496,34 +1533,35 @@ void GLVolumeCollection::reset_outside_state() } } -void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config) +void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha) { static const float inv_255 = 1.0f / 255.0f; struct Color { std::string text; - unsigned char rgb[3]; + unsigned char rgba[4]; Color() : text("") { - rgb[0] = 255; - rgb[1] = 255; - rgb[2] = 255; + rgba[0] = 255; + rgba[1] = 255; + rgba[2] = 255; + rgba[3] = 255; } - void set(const std::string& text, unsigned char* rgb) + void set(const std::string& text, unsigned char* rgba) { this->text = text; - ::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char)); + ::memcpy((void*)this->rgba, (const void*)rgba, 4 * sizeof(unsigned char)); } }; if (config == nullptr) return; - unsigned char rgb[3]; + unsigned char rgba[4]; std::vector colors; if (static_cast(config->opt_int("printer_technology")) == ptSLA) @@ -1531,9 +1569,9 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con const std::string& txt_color = config->opt_string("material_colour").empty() ? print_config_def.get("material_colour")->get_default_value()->value : config->opt_string("material_colour"); - if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) { + if (Slic3r::GUI::BitmapCache::parse_color4(txt_color, rgba)) { colors.resize(1); - colors[0].set(txt_color, rgb); + colors[0].set(txt_color, rgba); } } else @@ -1549,8 +1587,8 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con for (unsigned int i = 0; i < colors_count; ++i) { const std::string& txt_color = config->opt_string("filament_colour", i); - if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) - colors[i].set(txt_color, rgb); + if (Slic3r::GUI::BitmapCache::parse_color4(txt_color, rgba)) + colors[i].set(txt_color, rgba); } } @@ -1564,8 +1602,14 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con const Color& color = colors[extruder_id]; if (!color.text.empty()) { - for (int i = 0; i < 3; ++i) { - volume->color[i] = (float)color.rgb[i] * inv_255; + for (int i = 0; i < 4; ++i) { + if (is_update_alpha == false) { + if (i < 3) { + volume->color[i] = (float) color.rgba[i] * inv_255; + } + continue; + } + volume->color[i] = (float) color.rgba[i] * inv_255; } } } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index c61a4eb284..dcabb59946 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -31,7 +31,9 @@ #define glcheck() #endif // HAS_GLSAFE extern std::vector> get_extruders_colors(); -extern std::array adjust_color_for_rendering(const std::array& colors); +extern float FullyTransparentMaterialThreshold; +extern float FullTransparentModdifiedToFixAlpha; +extern std::array adjust_color_for_rendering(const std::array &colors); namespace Slic3r { @@ -558,6 +560,7 @@ public: virtual void render(bool with_outline = false) const; std::vector iva_per_colors; + bool IsTransparent(); private: std::vector> m_colors; @@ -604,7 +607,8 @@ private: struct Slope { // toggle for slope rendering - bool active{ false }; + bool active{ false };//local active + bool isGlobalActive{false}; float normal_z; }; @@ -656,9 +660,14 @@ public: GLVolume* new_toolpath_volume(const std::array& rgba, size_t reserve_vbo_floats = 0); GLVolume* new_nontoolpath_volume(const std::array& rgba, size_t reserve_vbo_floats = 0); + int get_selection_support_threshold_angle(bool&) const; // Render the volumes by OpenGL. //BBS: add outline drawing logic - void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func = std::function(), bool with_outline = true) const; + void render(ERenderType type, + bool disable_cullface, + const Transform3d & view_matrix, + std::function filter_func = std::function(), + bool with_outline = true) const; // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects @@ -678,8 +687,10 @@ public: void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; } void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; } + bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; } bool is_slope_active() const { return m_slope.active; } void set_slope_active(bool active) { m_slope.active = active; } + void set_slope_GlobalActive(bool active) { m_slope.isGlobalActive = active; } float get_slope_normal_z() const { return m_slope.normal_z; } void set_slope_normal_z(float normal_z) { m_slope.normal_z = normal_z; } @@ -691,7 +702,7 @@ public: bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const; void reset_outside_state(); - void update_colors_by_extruder(const DynamicPrintConfig* config); + void update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha = true); // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index f771b4d789..4bafdc7bf7 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -46,9 +46,10 @@ bool SlicingProcessCompletedEvent::critical_error() const } catch (const Slic3r::SlicingError &) { // Exception derived from SlicingError is non-critical. return false; - } catch (...) { - } - return true; + } catch (const Slic3r::SlicingErrors &) { + return false; + } catch (...) {} + return true; } bool SlicingProcessCompletedEvent::invalidate_plater() const @@ -69,7 +70,7 @@ bool SlicingProcessCompletedEvent::invalidate_plater() const return false; } -std::pair SlicingProcessCompletedEvent::format_error_message() const +std::pair> SlicingProcessCompletedEvent::format_error_message() const { std::string error; size_t monospace = 0; @@ -88,12 +89,20 @@ std::pair SlicingProcessCompletedEvent::format_error_messag } catch (SlicingError &ex) { error = ex.what(); monospace = ex.objectId(); + } catch (SlicingErrors &exs) { + std::vector ids; + for (auto &ex : exs.errors_) { + error = ex.what(); + monospace = ex.objectId(); + ids.push_back(monospace); + } + return std::make_pair(std::move(error), ids); } catch (std::exception &ex) { - error = ex.what(); - } catch (...) { - error = "Unknown C++ exception."; - } - return std::make_pair(std::move(error), monospace); + error = ex.what(); + } catch (...) { + error = "Unknown C++ exception."; + } + return std::make_pair(std::move(error), std::vector{monospace}); } BackgroundSlicingProcess::BackgroundSlicingProcess() @@ -308,6 +317,8 @@ void BackgroundSlicingProcess::thread_proc() break; // Process the background slicing task. m_state = STATE_RUNNING; + //BBS: internal cancel + m_internal_cancelled = false; lck.unlock(); std::exception_ptr exception; #ifdef _WIN32 @@ -328,6 +339,10 @@ void BackgroundSlicingProcess::thread_proc() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": send SlicingProcessCompletedEvent to main, status %1%")%evt.status(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); } + else { + //BBS: internal cancel + m_internal_cancelled = true; + } m_print->restart(); lck.unlock(); // Let the UI thread wake up if it is waiting for the background task to finish. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 90eebbbba8..b5b8ffcd7a 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -60,7 +60,7 @@ public: void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); } // Produce a human readable message to be displayed by a notification or a message box. // 2nd parameter defines whether the output should be displayed with a monospace font. - std::pair format_error_message() const; + std::pair> format_error_message() const; private: StatusType m_status; @@ -185,6 +185,7 @@ public: //BBS: improve the finished logic, also judge the m_gcode_result //bool finished() const { return m_print->finished(); } bool finished() const { return m_print->finished() && !m_gcode_result->moves.empty(); } + bool is_internal_cancelled() { return m_internal_cancelled; } //BBS: add Plater to friend class //need to call stop_internal in ui thread @@ -275,6 +276,7 @@ private: //BBS: partplate related GUI::PartPlate* m_current_plate; PrinterTechnology m_printer_tech = ptUnknown; + bool m_internal_cancelled = false; PrintState m_step_state; bool set_step_started(BackgroundSlicingProcessStep step); diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 6c7cd77384..f1076069d0 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -375,7 +375,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ std::vector data(n_pixels * 4, 0); // BBS: support resize by fill border - if (scale_in_center > 0) { + if (scale_in_center > 0 && scale_in_center < svg_scale) { int w = (int)(image->width * scale_in_center); int h = (int)(image->height * scale_in_center); ::nsvgRasterize(rast, image, 0, 0, scale_in_center, data.data() + int(height - h) / 2 * width * 4 + int(width - w) / 2 * 4, w, h, width * 4); @@ -433,6 +433,14 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out) { + if (scolor.size() == 9) { + unsigned char rgba[4]; + parse_color4(scolor, rgba); + rgb_out[0] = rgba[0]; + rgb_out[1] = rgba[1]; + rgb_out[2] = rgba[2]; + return true; + } rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; if (scolor.size() != 7 || scolor.front() != '#') return false; @@ -444,6 +452,23 @@ bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out) return false; rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); } + + return true; +} + +bool BitmapCache::parse_color4(const std::string& scolor, unsigned char* rgba_out) +{ + rgba_out[0] = rgba_out[1] = rgba_out[2] = 0; rgba_out[3] = 255; + if ((scolor.size() != 7 && scolor.size() != 9) || scolor.front() != '#') + return false; + const char* c = scolor.data() + 1; + for (size_t i = 0; i < scolor.size() / 2; ++i) { + int digit1 = hex_digit_to_int(*c++); + int digit2 = hex_digit_to_int(*c++); + if (digit1 == -1 || digit2 == -1) + return false; + rgba_out[i] = (unsigned char)(digit1 * 16 + digit2); + } return true; } diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 6d63b17e36..4803fa961d 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -48,6 +48,7 @@ public: wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } static bool parse_color(const std::string& scolor, unsigned char* rgb_out); + static bool parse_color4(const std::string& scolor, unsigned char* rgba_out); private: std::map m_map; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 2a3427ddfd..d66c136c82 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -835,10 +835,12 @@ public: bool can_export_toolpaths() const; std::vector get_plater_extruder(); + const float get_max_print_height() const { return m_max_print_height; } const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; } const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; } const BoundingBoxf3& get_shell_bounding_box() const { return m_shell_bounding_box; } const std::vector& get_layers_zs() const { return m_layers.get_zs(); } + const std::array &get_layers_z_range() const { return m_layers_z_range; } const SequentialView& get_sequential_view() const { return m_sequential_view; } void update_sequential_view_current(unsigned int first, unsigned int last); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 486d390025..1f1b60e8fc 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -748,7 +748,7 @@ void GLCanvas3D::Labels::render(const std::vector& sorted_ } // updates print order strings - if (sorted_instances.size() > 1) { + if (sorted_instances.size() > 0) { for (size_t i = 0; i < sorted_instances.size(); ++i) { size_t id = sorted_instances[i]->id().id; std::vector::iterator it = std::find_if(owners.begin(), owners.end(), [id](const Owner& owner) { @@ -1296,6 +1296,15 @@ void GLCanvas3D::on_change_color_mode(bool is_dark, bool reinit) { m_gizmos.set_icon_dirty(); } } + if (m_canvas_type == CanvasAssembleView) { + m_gizmos.on_change_color_mode(is_dark); + if (reinit) { + // reset svg + m_gizmos.switch_gizmos_icon_filename(); + // set dirty to re-generate icon texture + m_gizmos.set_icon_dirty(); + } + } } void GLCanvas3D::set_as_dirty() @@ -1614,6 +1623,8 @@ void GLCanvas3D::enable_main_toolbar(bool enable) void GLCanvas3D::reset_select_plate_toolbar_selection() { if (m_sel_plate_toolbar.m_all_plates_stats_item) m_sel_plate_toolbar.m_all_plates_stats_item->selected = false; + if (wxGetApp().mainframe) + wxGetApp().mainframe->update_slice_print_status(MainFrame::eEventSliceUpdate, true, true); } void GLCanvas3D::enable_select_plate_toolbar(bool enable) @@ -1887,32 +1898,30 @@ void GLCanvas3D::render(bool only_init) _render_selection(); if (!no_partplate) _render_bed(!camera.is_looking_downward(), show_axes); - //BBS: add outline logic + if (!no_partplate) //BBS: add outline logic + _render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id, true); _render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running()); - if (!no_partplate) - _render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id); } /* preview render */ else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) { - //BBS: add outline logic _render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running()); - //BBS: GUI refactor: add canvas size as parameters - _render_gcode(cnv_size.get_width(), cnv_size.get_height()); _render_sla_slices(); _render_selection(); _render_bed(!camera.is_looking_downward(), show_axes); _render_platelist(!camera.is_looking_downward(), only_current, true, hover_id); + // BBS: GUI refactor: add canvas size as parameters + _render_gcode(cnv_size.get_width(), cnv_size.get_height()); } /* assemble render*/ else if (m_canvas_type == ECanvasType::CanvasAssembleView) { //BBS: add outline logic _render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running()); //_render_bed(!camera.is_looking_downward(), show_axes); - //BBS: add outline logic - _render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running()); _render_plane(); //BBS: add outline logic insteadof selection under assemble view //_render_selection(); + // BBS: add outline logic + _render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running()); } _render_sequential_clearance(); @@ -2377,9 +2386,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re deleted_wipe_towers.emplace_back(volume, volume_id); delete volume; } - - // BBS - m_explosion_ratio = 1.0; } else { // This GLVolume will be reused. @@ -2635,7 +2641,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const float margin = 15.f; BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_id)->get_bounding_box(); + coordf_t plate_bbox_x_max_local_coord = plate_bbox.max(0) - plate_origin(0); coordf_t plate_bbox_y_max_local_coord = plate_bbox.max(1) - plate_origin(1); + if (x + margin + wipe_tower_size(0) > plate_bbox_x_max_local_coord) { + x = plate_bbox_x_max_local_coord - wipe_tower_size(0) - margin; + ConfigOptionFloat wt_x_opt(x); + dynamic_cast(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_id, 0); + } if (y + margin + wipe_tower_size(1) > plate_bbox_y_max_local_coord) { y = plate_bbox_y_max_local_coord - wipe_tower_size(1) - margin; ConfigOptionFloat wt_y_opt(y); @@ -2673,6 +2685,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re //BBS:exclude the assmble view if (m_canvas_type != ECanvasType::CanvasAssembleView) { + _set_warning_notification_if_needed(EWarning::GCodeConflict); // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) { ModelInstanceEPrintVolumeState state; @@ -2758,6 +2771,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co if (wxGetApp().is_editor()) { //BBS: always load shell at preview, do this in load_shells //m_gcode_viewer.update_shells_color_by_extruder(m_config); + _set_warning_notification_if_needed(EWarning::ToolHeightOutside); _set_warning_notification_if_needed(EWarning::ToolpathOutside); _set_warning_notification_if_needed(EWarning::GCodeConflict); } @@ -2902,6 +2916,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) // BBS //m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state(); + _update_imgui_select_plate_toolbar(); bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera()); m_dirty |= mouse3d_controller_applied; m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this); @@ -3051,7 +3066,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #else /* __APPLE__ */ case WXK_CONTROL_Y: #endif /* __APPLE__ */ - if (m_canvas_type == CanvasView3D) { + if (m_canvas_type == CanvasView3D || m_canvas_type == CanvasAssembleView) { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); } break; @@ -3062,7 +3077,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case WXK_CONTROL_Z: #endif /* __APPLE__ */ // only support redu/undo in CanvasView3D - if (m_canvas_type == CanvasView3D) { + if (m_canvas_type == CanvasView3D || m_canvas_type == CanvasAssembleView) { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); } break; @@ -3477,7 +3492,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; - else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { + else if (keyCode == WXK_TAB && evt.ShiftDown() && !evt.ControlDown() && !wxGetApp().is_gcode_viewer()) { + // Collapse side-panel with Shift+Tab + post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR)); + } else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { auto _do_rotate = [this](double angle_z_rad) { m_selection.start_dragging(); m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); @@ -3769,7 +3787,7 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt) float z = 0; const Vec3d &p2 = _mouse_to_3d({p.x, p.y}, &z); const Vec3d &p1 = _mouse_to_3d({p.x - d.x, p.y - d.y}, &z); - camera.set_target(camera.get_target() + p2 - p1); + camera.set_target(camera.get_target() + p1 - p2); } else if (evt.GetEventType() == wxEVT_GESTURE_ZOOM) { static float zoom_start = 1; if (evt.IsGestureStart()) @@ -4151,7 +4169,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // if dragging over blank area with left button, rotate if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) { const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); - if (this->m_canvas_type == ECanvasType::CanvasAssembleView) { + if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports || + m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) { //BBS rotate around target Camera& camera = wxGetApp().plater()->get_camera(); Vec3d rotate_target = Vec3d::Zero(); @@ -4181,13 +4200,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) camera.recover_from_free_camera(); //BBS modify rotation - if (m_gizmos.get_current_type() == GLGizmosManager::FdmSupports - || m_gizmos.get_current_type() == GLGizmosManager::Seam - || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) { - //camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target); - camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target); - } - else if (evt.ControlDown() || evt.CmdDown()) { + //if (m_gizmos.get_current_type() == GLGizmosManager::FdmSupports + // || m_gizmos.get_current_type() == GLGizmosManager::Seam + // || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) { + // //camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target); + // //camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target); + //} + //else + if (evt.ControlDown() || evt.CmdDown()) { if ((m_rotation_center.x() == 0.f) && (m_rotation_center.y() == 0.f) && (m_rotation_center.z() == 0.f)) { auto canvas_w = float(get_canvas_size().get_width()); auto canvas_h = float(get_canvas_size().get_height()); @@ -4280,7 +4300,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) wxGetApp().plater()->select_plate_by_hover_id(hover_idx); //wxGetApp().plater()->get_partplate_list().select_plate_view(); //deselect all the objects - if (m_hover_volume_idxs.empty()) + if (m_gizmos.get_current_type() != GLGizmosManager::MeshBoolean && m_hover_volume_idxs.empty()) deselect_all(); } else if (evt.RightUp() && !is_layers_editing_enabled()) { @@ -5269,13 +5289,13 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa if (imgui->checkbox(_L("Enable rotation"), settings.enable_rotation)) { settings_out.enable_rotation = settings.enable_rotation; - appcfg->set("orient", rot_key, settings_out.enable_rotation); + appcfg->set("orient", rot_key, settings_out.enable_rotation ? "1" : "0"); settings_changed = true; } if (imgui->checkbox(_L("Optimize support interface area"), settings.min_area)) { settings_out.min_area = settings.min_area; - appcfg->set("orient", key_min_area, settings_out.min_area); + appcfg->set("orient", key_min_area, settings_out.min_area ? "1" : "0"); settings_changed = true; } @@ -5292,8 +5312,8 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa settings_out = OrientSettings{}; settings_out.overhang_angle = 60.f; appcfg->set("orient", angle_key, std::to_string(settings_out.overhang_angle)); - appcfg->set("orient", rot_key, settings_out.enable_rotation ); - appcfg->set("orient", key_min_area, settings_out.min_area); + appcfg->set("orient", rot_key, settings_out.enable_rotation ? "1" : "0"); + appcfg->set("orient", key_min_area, settings_out.min_area? "1" : "0"); settings_changed = true; } @@ -5644,7 +5664,8 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const curr_color[2] = vol->color[2]; curr_color[3] = vol->color[3]; - shader->set_uniform("uniform_color", curr_color); + std::array new_color = adjust_color_for_rendering(curr_color); + shader->set_uniform("uniform_color", new_color); shader->set_uniform("volume_world_matrix", vol->world_matrix()); //BBS set all volume to orange //shader->set_uniform("uniform_color", orange); @@ -5683,6 +5704,8 @@ void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, uns return; bool multisample = OpenGLManager::can_multisample(); + if (for_picking) + multisample = false; //if (!multisample) // glsafe(::glEnable(GL_MULTISAMPLE)); @@ -5789,6 +5812,8 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, return; bool multisample = OpenGLManager::can_multisample(); + if (for_picking) + multisample = false; //if (!multisample) // glsafe(::glEnable(GL_MULTISAMPLE)); @@ -6068,6 +6093,8 @@ bool GLCanvas3D::_init_main_toolbar() wxGetApp().plater()->orient(); //BBS do not show orient menu //_render_orient_menu(left, right, bottom, top); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("auto_orient", std::to_string(++auto_orient_count)); } }; if (!m_main_toolbar.add_item(item)) @@ -6664,9 +6691,9 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom) //m_bed.render_for_picking(*this, bottom, scale_factor); } -void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id) const +void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) const { - wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id); + wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id, render_cali); } void GLCanvas3D::_render_plates_for_picking() const @@ -6739,7 +6766,10 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with else { m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); } - m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); + if (m_canvas_type == CanvasAssembleView) + m_volumes.set_show_sinking_contours(false); + else + m_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances()); GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); ECanvasType canvas_type = this->m_canvas_type; @@ -7056,8 +7086,9 @@ void GLCanvas3D::_render_overlays() if (m_layers_editing.last_object_id >= 0 && m_layers_editing.object_max_z() > 0.0f) m_layers_editing.render_overlay(*this); - const ConfigOptionEnum* opt = dynamic_cast*>(m_config->option>("print_sequence")); - bool sequential_print = opt != nullptr && (opt->value == PrintSequence::ByObject); + auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); + auto curr_print_seq = curr_plate->get_real_print_seq(); + bool sequential_print = (curr_print_seq == PrintSequence::ByObject); std::vector sorted_instances; if (sequential_print) { const Print* print = fff_print(); @@ -7230,9 +7261,6 @@ void GLCanvas3D::_render_gizmos_overlay() const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale()); m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment #endif /* __WXMSW__ */ - if (m_canvas_type == CanvasAssembleView) - return; - m_gizmos.render_overlay(); if (m_gizmo_highlighter.m_render_arrow) @@ -7394,7 +7422,6 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, scroll_col); ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_active); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_hover); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 10.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); @@ -7410,7 +7437,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() imgui.begin(_L("Select Plate"), ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse); ImGui::SetWindowFontScale(1.2f); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * f_scale); ImVec2 size = ImVec2(button_width, button_height); // Size of the image we want to make visible ImVec4 bg_col = ImVec4(128.0f, 128.0f, 128.0f, 0.0f); @@ -7435,7 +7462,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() } else { ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_hover); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_hover); } } @@ -7477,6 +7504,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImVec2 size = ImVec2(button_width, button_height * all_plates_stats_item->percent / 100.0f); ImVec2 rect_start_pos = ImVec2(start_pos.x, start_pos.y + size.y); ImVec2 rect_end_pos = ImVec2(start_pos.x + button_width, start_pos.y + button_height); + ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, rect_end_pos, IM_COL32(0, 0, 0, 10)); ImGui::GetForegroundDrawList()->AddRectFilled(rect_start_pos, rect_end_pos, IM_COL32(0, 0, 0, 80)); } else if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED) { @@ -7485,6 +7513,11 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(40, 1, 1, 64)); ImGui::GetForegroundDrawList()->AddRect(start_pos, end_pos, IM_COL32(208, 27, 27, 255), 0.0f, 0, 1.0f); } + else if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICED) { + ImVec2 size = ImVec2(button_width, button_height); + ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y); + ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(0, 0, 0, 10)); + } // draw text GImGui->FontSize = 15.0f; @@ -7508,16 +7541,31 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImVec2 uv0 = ImVec2(0.0f, 1.0f); // UV coordinates for lower-left ImVec2 uv1 = ImVec2(1.0f, 0.0f); // UV coordinates in our texture + auto button_pos = ImGui::GetCursorPos(); + ImGui::SetCursorPos(button_pos + margin); + + ImGui::Image(item->texture_id, size, uv0, uv1, tint_col); + + ImGui::SetCursorPos(button_pos); + + // invisible button + auto button_size = size + margin + margin + ImVec2(2 * frame_padding, 2 * frame_padding); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f * f_scale); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); if (item->selected) { - ImGui::PushStyleColor(ImGuiCol_Button, button_active); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_active); + ImGui::PushStyleColor(ImGuiCol_Border, button_active); } else { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f)); + if (ImGui::IsMouseHoveringRect(button_pos, button_pos + button_size)) { + ImGui::PushStyleColor(ImGuiCol_Border, button_hover); + } + else { + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(.0f, .0f, .0f, .0f)); + } } - - if (ImGui::ImageButton2(item->texture_id, size, uv0, uv1, frame_padding, bg_col, tint_col, margin)) { + if(ImGui::Button("##invisible_button", button_size)){ if (m_process && !m_process->running()) { all_plates_stats_item->selected = false; item->selected = true; @@ -7527,8 +7575,8 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() wxQueueEvent(wxGetApp().plater(), evt); } } - - ImGui::PopStyleColor(2); + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(); ImVec2 start_pos = ImVec2(button_start_pos.x + frame_padding + margin.x, button_start_pos.y + frame_padding + margin.y); if (item->slice_state == IMToolbarItem::SliceState::UNSLICED) { @@ -7539,12 +7587,17 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImVec2 size = ImVec2(button_width, button_height * item->percent / 100.0f); ImVec2 rect_start_pos = ImVec2(start_pos.x, start_pos.y + size.y); ImVec2 rect_end_pos = ImVec2(start_pos.x + button_width, start_pos.y + button_height); + ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, rect_end_pos, IM_COL32(0, 0, 0, 10)); ImGui::GetForegroundDrawList()->AddRectFilled(rect_start_pos, rect_end_pos, IM_COL32(0, 0, 0, 80)); } else if (item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED) { ImVec2 size = ImVec2(button_width, button_height); ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y); ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(40, 1, 1, 64)); ImGui::GetForegroundDrawList()->AddRect(start_pos, end_pos, IM_COL32(208, 27, 27, 255), 0.0f, 0, 1.0f); + } else if (item->slice_state == IMToolbarItem::SliceState::SLICED) { + ImVec2 size = ImVec2(button_width, button_height); + ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y); + ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(0, 0, 0, 10)); } // draw text @@ -7554,7 +7607,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImGui::PopID(); } ImGui::SetWindowFontScale(1.0f); - ImGui::PopStyleColor(9); + ImGui::PopStyleColor(8); ImGui::PopStyleVar(5); if (ImGui::IsWindowHovered() || is_hovered) { @@ -7645,6 +7698,8 @@ void GLCanvas3D::_render_return_toolbar() const if (ImGui::ImageTextButton(real_size,_utf8(L("return")).c_str(), m_return_toolbar.get_return_texture_id(), button_icon_size, uv0, uv1, -1, bg_col, tint_col, margin)) { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); + const_cast(&m_gizmos)->reset_all_states(); + wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager().reset_all_states(); } ImGui::PopStyleColor(5); ImGui::PopStyleVar(1); @@ -7846,6 +7901,7 @@ void GLCanvas3D::_render_paint_toolbar() const ImGui::BBLRenderArrow(draw_list, right_arrow_button.GetCenter() - ImVec2(draw_list->_Data->FontSize, draw_list->_Data->FontSize) * 0.5f, arrow_color, ImGuiDir_Right, 2.0f); } + m_paint_toolbar_width = (ImGui::GetWindowWidth() + 50.0f * em_unit * f_scale); imgui.end(); ImGui::PopStyleVar(3); ImGui::PopStyleColor(); @@ -7858,6 +7914,10 @@ void GLCanvas3D::_render_assemble_control() const GLVolume::explosion_ratio = m_explosion_ratio = 1.0; return; } + if (m_gizmos.get_current_type() == GLGizmosManager::EType::MmuSegmentation) { + m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(0.0, true); + return; + } ImGuiWrapper* imgui = wxGetApp().imgui(); @@ -8845,15 +8905,22 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) { _set_current(); bool show = false; - if (!m_volumes.empty()) + if (!m_volumes.empty()) { show = _is_any_volume_outside(); - else { + show &= m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.m_conflict_result.has_value(); + } else { if (wxGetApp().is_editor()) { - if (current_printer_technology() != ptSLA) - if (warning == EWarning::ToolpathOutside) - show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed(); - else if (warning==EWarning::GCodeConflict) + if (current_printer_technology() != ptSLA) { + unsigned int max_z_layer = m_gcode_viewer.get_layers_z_range().back(); + if (warning == EWarning::ToolHeightOutside) // check if max z_layer height exceed max print height + show = m_gcode_viewer.has_data() && (m_gcode_viewer.get_layers_zs()[max_z_layer] - m_gcode_viewer.get_max_print_height() >= 1e-6); + else if (warning == EWarning::ToolpathOutside) { // check if max x,y coords exceed bed area + show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed() && + (m_gcode_viewer.get_max_print_height() -m_gcode_viewer.get_layers_zs()[max_z_layer] >= 1e-6); + } + else if (warning == EWarning::GCodeConflict) show = m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.m_conflict_result.has_value(); + } } } @@ -8887,12 +8954,17 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) enum ErrorType{ PLATER_WARNING, PLATER_ERROR, + SLICING_SERIOUS_WARNING, SLICING_ERROR }; std::string text; ErrorType error = ErrorType::PLATER_WARNING; + const ModelObject* conflictObj=nullptr; switch (warning) { case EWarning::GCodeConflict: { + static std::string prevConflictText; + text = prevConflictText; + error = ErrorType::SLICING_SERIOUS_WARNING; if (!m_gcode_viewer.m_conflict_result) { break; } std::string objName1 = m_gcode_viewer.m_conflict_result.value()._objName1; std::string objName2 = m_gcode_viewer.m_conflict_result.value()._objName2; @@ -8901,10 +8973,13 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) text = (boost::format(_u8L("Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please separate the conflicted objects farther (%s <-> %s).")) % layer % height % objName1 % objName2) .str(); - error = ErrorType::SLICING_ERROR; + prevConflictText = text; + const PrintObject *obj2 = reinterpret_cast(m_gcode_viewer.m_conflict_result.value()._obj2); + conflictObj = obj2->model_object(); break; } case EWarning::ObjectOutside: text = _u8L("An object is layed over the boundary of plate."); break; + case EWarning::ToolHeightOutside: text = _u8L("A G-code path goes beyond the max print height."); error = ErrorType::SLICING_ERROR; break; case EWarning::ToolpathOutside: text = _u8L("A G-code path goes beyond the boundary of plate."); error = ErrorType::SLICING_ERROR; break; // BBS: remove _u8L() for SLA case EWarning::SlaSupportsOutside: text = ("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break; @@ -8920,25 +8995,6 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) return; auto& notification_manager = *wxGetApp().plater()->get_notification_manager(); - if (warning == EWarning::GCodeConflict && m_gcode_viewer.m_conflict_result) { - const PrintObject *obj2 = reinterpret_cast(m_gcode_viewer.m_conflict_result.value()._obj2); - auto mo = obj2->model_object(); - ObjectID id = mo->id(); - auto action_fn = [id](wxEvtHandler *) { - auto &objects = wxGetApp().model().objects; - auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end(); - if (iter != objects.end()) { - wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); - wxGetApp().obj_list()->select_items({{*iter, nullptr}}); - } - return false; - }; - auto hypertext = _u8L("Jump to"); - hypertext += std::string(" [") + mo->name + "]"; - notification_manager.push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::ErrorNotificationLevel, _u8L("ERROR:") + "\n" + text, - hypertext, action_fn); - return; - } switch (error) { case PLATER_WARNING: @@ -8953,9 +9009,15 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) else notification_manager.close_plater_error_notification(text); break; + case SLICING_SERIOUS_WARNING: + if (state) + notification_manager.push_slicing_serious_warning_notification(text, {conflictObj}); + else + notification_manager.close_slicing_serious_warning_notification(text); + break; case SLICING_ERROR: if (state) - notification_manager.push_slicing_error_notification(text, nullptr); + notification_manager.push_slicing_error_notification(text, {conflictObj}); else notification_manager.close_slicing_error_notification(text); break; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8beb998384..1f2b090f39 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -373,7 +373,8 @@ class GLCanvas3D SlaSupportsOutside, SomethingNotShown, ObjectClashed, - GCodeConflict + GCodeConflict, + ToolHeightOutside }; class RenderStats @@ -439,6 +440,8 @@ class GLCanvas3D bool is_enabled() const { return m_enabled; } void use(bool use) { m_volumes.set_slope_active(m_enabled ? use : false); } bool is_used() const { return m_volumes.is_slope_active(); } + void globalUse(bool use) { m_volumes.set_slope_GlobalActive(m_enabled ? use : false); } + bool is_GlobalUsed() const { return m_volumes.is_slope_GlobalActive(); } void set_normal_angle(float angle_in_deg) const { m_volumes.set_slope_normal_z(-::cos(Geometry::deg2rad(90.0f - angle_in_deg))); } @@ -515,6 +518,7 @@ private: mutable IMToolbar m_sel_plate_toolbar; mutable GLToolbar m_assemble_view_toolbar; mutable IMReturnToolbar m_return_toolbar; + mutable float m_paint_toolbar_width; //BBS: add canvas type for assemble view usage ECanvasType m_canvas_type; @@ -619,6 +623,15 @@ private: ArrangeSettings &get_arrange_settings() { return get_arrange_settings(this); } + + //BBS:record key botton frequency + int auto_orient_count = 0; + int auto_arrange_count = 0; + int split_to_objects_count = 0; + int split_to_part_count = 0; + int custom_height_count = 0; + int custom_painting_count = 0; + public: OrientSettings& get_orient_settings() { @@ -707,6 +720,7 @@ public: bool init(); void post_event(wxEvent &&event); + void reset_explosion_ratio() { m_explosion_ratio = 1.0; } void on_change_color_mode(bool is_dark, bool reinit = true); const bool get_dark_mode_status() { return m_is_dark; } void set_as_dirty(); @@ -716,6 +730,7 @@ public: const GLVolumeCollection& get_volumes() const { return m_volumes; } void reset_volumes(); ModelInstanceEPrintVolumeState check_volumes_outside_state() const; + bool is_all_plates_selected() { return m_sel_plate_toolbar.m_all_plates_stats_item && m_sel_plate_toolbar.m_all_plates_stats_item->selected; } const float get_scale() const; //BBS @@ -813,6 +828,7 @@ public: float get_main_toolbar_width() { return m_main_toolbar.get_width();} float get_assemble_view_toolbar_width() { return m_assemble_view_toolbar.get_width(); } float get_assemble_view_toolbar_height() { return m_assemble_view_toolbar.get_height(); } + float get_assembly_paint_toolbar_width() { return m_paint_toolbar_width; } float get_separator_toolbar_width() { return m_separator_toolbar.get_width(); } float get_separator_toolbar_height() { return m_separator_toolbar.get_height(); } float get_collapse_toolbar_width(); @@ -992,6 +1008,9 @@ public: bool are_labels_shown() const { return m_labels.is_shown(); } void show_labels(bool show) { m_labels.show(show); } + bool is_overhang_shown() const { return m_slope.is_GlobalUsed(); } + void show_overhang(bool show) { m_slope.globalUse(show); } + bool is_using_slope() const { return m_slope.is_used(); } void use_slope(bool use) { m_slope.use(use); } void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); } @@ -1093,7 +1112,7 @@ private: void _render_bed(bool bottom, bool show_axes); void _render_bed_for_picking(bool bottom); //BBS: add part plate related logic - void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1) const; + void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false) const; void _render_plates_for_picking() const; //BBS: add outline drawing logic void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f55b3ccfce..7c72c79d16 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3545,6 +3545,18 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio } } +void ObjectList::add_objects_to_list(std::vector obj_idxs, bool call_selection_changed, bool notify_partplate, bool do_info_update) +{ +#ifdef __WXOSX__ + AssociateModel(nullptr); +#endif + for (const size_t idx : obj_idxs) { + add_object_to_list(idx, call_selection_changed, notify_partplate, do_info_update); + } +#ifdef __WXOSX__ + AssociateModel(m_objects_model); +#endif +} void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed, bool notify_partplate, bool do_info_update) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 5793b59d3c..bafa0b5ba3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -325,6 +325,8 @@ public: void part_selection_changed(); // Add object to the list + // @param do_info_update: [Arthur] this function becomes slow as more functions are added, but I only need a fast version in FillBedJob, and I don't care about any info updates, so I pass a do_info_update param to skip all the uneccessary steps. + void add_objects_to_list(std::vector obj_idxs, bool call_selection_changed = true, bool notify_partplate = true, bool do_info_update = true); void add_object_to_list(size_t obj_idx, bool call_selection_changed = true, bool notify_partplate = true, bool do_info_update = true); // Add object's volumes to the list // Return selected items, if add_to_selection is defined diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp index dbc8281bf6..c851535d61 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp @@ -1297,7 +1297,7 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; unsigned int current_active_id = ImGui::GetActiveID(); - Vec3d rotation = {Geometry::rad2deg(m_rotation(0)), Geometry::rad2deg(m_rotation(1)), Geometry::rad2deg(m_rotation(2))}; + Vec3d rotation = {Geometry::rad2deg(m_buffered_rotation(0)), Geometry::rad2deg(m_buffered_rotation(1)), Geometry::rad2deg(m_buffered_rotation(2))}; char buf[3][64]; float buf_size[3]; float vec_max = 0, unit_size = 0; @@ -1450,6 +1450,10 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b m_imgui->disabled_begin(has_connectors); m_imgui->bbl_checkbox(_L("Cut to parts"), m_cut_to_parts); + if (m_cut_to_parts) { + m_keep_upper = true; + m_keep_lower = true; + } m_imgui->disabled_end(); #if 0 @@ -1585,7 +1589,9 @@ void GLGizmoAdvancedCut::render_connectors_input_window(float x, float y, float if (render_combo(_u8L("Style"), connector_styles, m_connector_style)) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); m_imgui->disabled_end(); + ImGuiWrapper::pop_combo_style(); + ImGuiWrapper::push_combo_style(m_parent.get_scale()); if (render_combo(_u8L("Shape"), connector_shapes, m_connector_shape_id)) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); ImGuiWrapper::pop_combo_style(); @@ -1728,11 +1734,13 @@ bool GLGizmoAdvancedCut::render_combo(const std::string &label, const std::vecto bool GLGizmoAdvancedCut::render_slider_double_input(const std::string &label, float &value_in, float &tolerance_in) { + // -------- [ ] -------- [ ] + // slider_with + item_in_gap + first_input_width + item_out_gap + slider_with + item_in_gap + second_input_width double slider_with = 0.24 * m_editing_window_width; // m_control_width * 0.35; double item_in_gap = 0.01 * m_editing_window_width; - double item_out_gap = 0.02 * m_editing_window_width; - double first_input_width = 0.33 * m_editing_window_width; - double second_input_width = 0.15 * m_editing_window_width; + double item_out_gap = 0.01 * m_editing_window_width; + double first_input_width = 0.29 * m_editing_window_width; + double second_input_width = 0.29 * m_editing_window_width; ImGui::AlignTextToFramePadding(); m_imgui->text(label); @@ -1770,18 +1778,21 @@ bool GLGizmoAdvancedCut::render_slider_double_input(const std::string &label, fl ImGui::SameLine(left_width); ImGui::PushItemWidth(slider_with); - float old_tolerance, tolerance = old_tolerance = tolerance_in * 100.f; - std::string format_t = tolerance_in < 0.f ? " " : "%.f %%"; + float tolerance = tolerance_in; + if (m_imperial_units) + tolerance *= float(units_mm_to_in); + float old_tolerance = tolerance; + //std::string format_t = tolerance_in < 0.f ? " " : "%.f %%"; float min_tolerance = tolerance_in < 0.f ? UndefMinVal : 0.f; - m_imgui->bbl_slider_float_style(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, 20.f, format_t.c_str(), 1.f, true, _L("Tolerance")); + m_imgui->bbl_slider_float_style(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, 2.f, format.c_str(), 1.f, true, _L("Tolerance")); left_width += (slider_with + item_in_gap); ImGui::SameLine(left_width); ImGui::PushItemWidth(second_input_width); - ImGui::BBLDragFloat(("##tolerance_input_" + label).c_str(), &tolerance, 0.05f, min_tolerance, 20.f, format_t.c_str()); + ImGui::BBLDragFloat(("##tolerance_input_" + label).c_str(), &tolerance, 0.05f, min_tolerance, 2.f, format.c_str()); - tolerance_in = tolerance * 0.01f; + tolerance_in = tolerance * float(m_imperial_units ? units_in_to_mm : 1.0); return !is_approx(old_val, value) || !is_approx(old_tolerance, tolerance); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index ba8fd5280b..c725809bba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -191,6 +191,9 @@ public: virtual std::string get_tooltip() const { return ""; } + int get_count() { return ++count; } + std::string get_gizmo_name() { return on_get_name(); } + protected: float last_input_window_width = 0; virtual bool on_init() = 0; @@ -229,6 +232,7 @@ private: // Flag for dirty visible state of Gizmo // When True then need new rendering bool m_dirty; + int count = 0; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 06793ce7c0..c095c0915e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -186,7 +186,7 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const shader->set_uniform("volume_world_matrix", trafo_matrix); shader->set_uniform("volume_mirrored", is_left_handed); shader->set_uniform("slope.actived", m_parent.is_using_slope()); - shader->set_uniform("slope.volume_world_normal_matrix", static_cast(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast())); + shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix); shader->set_uniform("slope.normal_z", normal_z); m_triangle_selectors[mesh_id]->render(m_imgui); @@ -290,7 +290,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 619cebb798..4ad8136bba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -141,6 +141,7 @@ void GLGizmoFlatten::update_planes() // Following constants are used for discarding too small polygons. const float minimal_area = 5.f; // in square mm (world coordinates) const float minimal_side = 1.f; // mm + const float minimal_angle = 1.f; // degree, initial value was 10, but cause bugs // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. @@ -151,9 +152,9 @@ void GLGizmoFlatten::update_planes() std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; + int facet_idx = 0; while (1) { // Find next unvisited triangle: - int facet_idx = 0; for (; facet_idx < num_of_facets; ++ facet_idx) if (!facet_visited[facet_idx]) { facet_queue[facet_queue_cnt ++] = facet_idx; @@ -235,7 +236,7 @@ void GLGizmoFlatten::update_planes() discard = true; else { // We also check the inner angles and discard polygons with angles smaller than the following threshold - const double angle_threshold = ::cos(10.0 * (double)PI / 180.0); + const double angle_threshold = ::cos(minimal_angle * (double)PI / 180.0); for (unsigned int i = 0; i < polygon.size(); ++i) { const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1]; @@ -250,7 +251,8 @@ void GLGizmoFlatten::update_planes() } if (discard) { - m_planes.erase(m_planes.begin() + (polygon_id--)); + m_planes[polygon_id--] = std::move(m_planes.back()); + m_planes.pop_back(); continue; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp new file mode 100644 index 0000000000..10c2e32ce2 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -0,0 +1,466 @@ +#include "GLGizmoMeshBoolean.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "libslic3r/MeshBoolean.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/NotificationManager.hpp" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include +namespace Slic3r { +namespace GUI { + +static const std::string warning_text = _u8L("Unable to perform boolean operation on selected parts"); + +GLGizmoMeshBoolean::GLGizmoMeshBoolean(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) +{ +} + +GLGizmoMeshBoolean::~GLGizmoMeshBoolean() +{ +} + +bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (action == SLAGizmoEventType::LeftDown) { + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo == nullptr) + return true; + const ModelInstance* mi = mo->instances[m_parent.get_selection().get_instance_idx()]; + std::vector trafo_matrices; + for (const ModelVolume* mv : mo->volumes) { + //if (mv->is_model_part()) { + trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + //} + } + + const Camera& camera = wxGetApp().plater()->get_camera(); + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + Vec3f closest_normal = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + int closest_hit_mesh_id = -1; + + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, + m_c->object_clipper()->get_clipping_plane(), &facet)) { + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position() - trafo_matrices[mesh_id] * hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + closest_normal = normal; + } + } + } + + if (closest_hit == Vec3f::Zero() && closest_normal == Vec3f::Zero()) + return true; + + if (get_selecting_state() == MeshBooleanSelectingState::SelectTool) { + m_tool.trafo = trafo_matrices[closest_hit_mesh_id]; + m_tool.volume_idx = closest_hit_mesh_id; + set_tool_volume(mo->volumes[closest_hit_mesh_id]); + return true; + } + if (get_selecting_state() == MeshBooleanSelectingState::SelectSource) { + m_src.trafo = trafo_matrices[closest_hit_mesh_id]; + m_src.volume_idx = closest_hit_mesh_id; + set_src_volume(mo->volumes[closest_hit_mesh_id]); + m_selecting_state = MeshBooleanSelectingState::SelectTool; + return true; + } + } + return true; +} + +bool GLGizmoMeshBoolean::on_init() +{ + m_shortcut_key = WXK_CONTROL_B; + return true; +} + +std::string GLGizmoMeshBoolean::on_get_name() const +{ + return _u8L("Mesh Boolean"); +} + +bool GLGizmoMeshBoolean::on_is_activable() const +{ + return m_parent.get_selection().is_single_full_instance() && m_parent.get_selection().get_volume_idxs().size() > 1; +} + +void GLGizmoMeshBoolean::on_render() +{ + if (m_parent.get_selection().get_object_idx() < 0) + return; + static ModelObject* last_mo = nullptr; + ModelObject* curr_mo = m_parent.get_selection().get_model()->objects[m_parent.get_selection().get_object_idx()]; + if (last_mo != curr_mo) { + last_mo = curr_mo; + m_src.reset(); + m_tool.reset(); + m_operation_mode = MeshBooleanOperation::Union; + m_selecting_state = MeshBooleanSelectingState::SelectSource; + return; + } + + BoundingBoxf3 src_bb; + BoundingBoxf3 tool_bb; + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[m_parent.get_selection().get_instance_idx()]; + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int i : idxs) { + const GLVolume* volume = selection.get_volume(i); + if(volume->volume_idx() == m_src.volume_idx) { + src_bb = volume->transformed_convex_hull_bounding_box(); + } + if (volume->volume_idx() == m_tool.volume_idx) { + tool_bb = volume->transformed_convex_hull_bounding_box(); + } + } + + float src_color[3] = { 1.0f, 1.0f, 1.0f }; + float tool_color[3] = { 0.0f, 174.0f / 255.0f, 66.0f / 255.0f }; + m_parent.get_selection().render_bounding_box(src_bb, src_color, m_parent.get_scale()); + m_parent.get_selection().render_bounding_box(tool_bb, tool_color, m_parent.get_scale()); +} + +void GLGizmoMeshBoolean::on_set_state() +{ + if (m_state == EState::On) { + m_src.reset(); + m_tool.reset(); + bool m_diff_delete_input = false; + bool m_inter_delete_input = false; + m_operation_mode = MeshBooleanOperation::Union; + m_selecting_state = MeshBooleanSelectingState::SelectSource; + } + else if (m_state == EState::Off) { + m_src.reset(); + m_tool.reset(); + bool m_diff_delete_input = false; + bool m_inter_delete_input = false; + m_operation_mode = MeshBooleanOperation::Undef; + m_selecting_state = MeshBooleanSelectingState::Undef; + wxGetApp().notification_manager()->close_plater_warning_notification(warning_text); + } +} + +CommonGizmosDataID GLGizmoMeshBoolean::on_get_requirements() const +{ + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::ObjectClipper)); +} + +void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_limit) +{ + y = std::min(y, bottom_limit - ImGui::GetWindowHeight()); + + static float last_y = 0.0f; + static float last_w = 0.0f; + + const float currt_scale = m_parent.get_scale(); + ImGuiWrapper::push_toolbar_style(currt_scale); + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); + GizmoImguiBegin("MeshBoolean", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + + const int max_tab_length = 2 * ImGui::GetStyle().FramePadding.x + std::max(ImGui::CalcTextSize(_L("Union").c_str()).x, + std::max(ImGui::CalcTextSize(_L("Difference").c_str()).x, ImGui::CalcTextSize(_L("Intersection").c_str()).x)); + const int max_cap_length = ImGui::GetStyle().WindowPadding.x + ImGui::GetStyle().ItemSpacing.x + std::max(ImGui::CalcTextSize(_L("Source Volume").c_str()).x, ImGui::CalcTextSize(_L("Tool Volume").c_str()).x); + const int select_btn_length = 2 * ImGui::GetStyle().FramePadding.x + std::max(ImGui::CalcTextSize(("1 " + _L("selected")).c_str()).x, ImGui::CalcTextSize(_L("Select").c_str()).x); + + auto selectable = [this](const wxString& label, bool selected, const ImVec2& size_arg) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0,0 }); + + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImVec2 label_size = ImGui::CalcTextSize(label.c_str(), NULL, true); + ImVec2 pos = window->DC.CursorPos; + ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + ImGui::GetStyle().FramePadding.x * 2.0f, label_size.y + ImGui::GetStyle().FramePadding.y * 2.0f); + bool hovered = ImGui::IsMouseHoveringRect(pos, pos + size); + + if (selected || hovered) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f }); + } + else { + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f }); + } + + bool res = ImGui::Button(label.c_str(), size_arg); + + if (selected || hovered) { + ImGui::PopStyleColor(4); + } + else { + ImGui::PopStyleColor(2); + } + + ImGui::PopStyleVar(1); + return res; + }; + + auto operate_button = [this](const wxString &label, bool enable) { + if (!enable) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + if (m_is_dark_mode) { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(39.0f / 255.0f, 39.0f / 255.0f, 39.0f / 255.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(108.0f / 255.0f, 108.0f / 255.0f, 108.0f / 255.0f, 1.0f)); + } + else { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(163.0f / 255.0f, 163.0f / 255.0f, 163.0f / 255.0f, 1.0f)); + } + } + + bool res = m_imgui->button(label.c_str()); + + if (!enable) { + ImGui::PopItemFlag(); + ImGui::PopStyleColor(2); + } + return res; + }; + + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0); + if (selectable(_L("Union").c_str(), m_operation_mode == MeshBooleanOperation::Union, ImVec2(max_tab_length, 0.0f))) { + m_operation_mode = MeshBooleanOperation::Union; + } + ImGui::SameLine(0, 0); + if (selectable(_L("Difference").c_str(), m_operation_mode == MeshBooleanOperation::Difference, ImVec2(max_tab_length, 0.0f))) { + m_operation_mode = MeshBooleanOperation::Difference; + } + ImGui::SameLine(0, 0); + if (selectable(_L("Intersection").c_str(), m_operation_mode == MeshBooleanOperation::Intersection, ImVec2(max_tab_length, 0.0f))) { + m_operation_mode = MeshBooleanOperation::Intersection; + } + ImGui::PopStyleVar(); + + ImGui::AlignTextToFramePadding(); + std::string cap_str1 = m_operation_mode != MeshBooleanOperation::Difference ? _u8L("Part 1") : _u8L("Subtract from"); + m_imgui->text(cap_str1); + ImGui::SameLine(max_cap_length); + wxString select_src_str = m_src.mv ? "1 " + _L("selected") : _L("Select"); + select_src_str << "##select_source_volume"; + ImGui::PushItemWidth(select_btn_length); + if (selectable(select_src_str.c_str(), m_selecting_state == MeshBooleanSelectingState::SelectSource, ImVec2(select_btn_length, 0))) + m_selecting_state = MeshBooleanSelectingState::SelectSource; + ImGui::PopItemWidth(); + if (m_src.mv) { + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_src.mv->name); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Button)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button)); + ImGui::PushStyleColor(ImGuiCol_Border, { 0, 0, 0, 0 }); + if (ImGui::Button((into_u8(ImGui::TextSearchCloseIcon) + "##src").c_str(), {18, 18})) + { + m_src.reset(); + } + ImGui::PopStyleColor(5); + } + + ImGui::AlignTextToFramePadding(); + std::string cap_str2 = m_operation_mode != MeshBooleanOperation::Difference ? _u8L("Part 2") : _u8L("Subtract with"); + m_imgui->text(cap_str2); + ImGui::SameLine(max_cap_length); + wxString select_tool_str = m_tool.mv ? "1 " + _L("selected") : _L("Select"); + select_tool_str << "##select_tool_volume"; + ImGui::PushItemWidth(select_btn_length); + if (selectable(select_tool_str.c_str(), m_selecting_state == MeshBooleanSelectingState::SelectTool, ImVec2(select_btn_length, 0))) + m_selecting_state = MeshBooleanSelectingState::SelectTool; + ImGui::PopItemWidth(); + if (m_tool.mv) { + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_tool.mv->name); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Button)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button)); + ImGui::PushStyleColor(ImGuiCol_Border, { 0, 0, 0, 0 }); + if (ImGui::Button((into_u8(ImGui::TextSearchCloseIcon) + "tool").c_str(), {18, 18})) + { + m_tool.reset(); + } + ImGui::PopStyleColor(5); + } + + bool enable_button = m_src.mv && m_tool.mv; + if (m_operation_mode == MeshBooleanOperation::Union) + { + if (operate_button(_L("Union") + "##btn", enable_button)) { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(true, *temp_mesh_resuls.begin()); + wxGetApp().notification_manager()->close_plater_warning_notification(warning_text); + } + else { + wxGetApp().notification_manager()->push_plater_warning_notification(warning_text); + } + } + } + else if (m_operation_mode == MeshBooleanOperation::Difference) { + m_imgui->bbl_checkbox(_L("Delete input"), m_diff_delete_input); + if (operate_button(_L("Difference") + "##btn", enable_button)) { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin()); + wxGetApp().notification_manager()->close_plater_warning_notification(warning_text); + } + else { + wxGetApp().notification_manager()->push_plater_warning_notification(warning_text); + } + } + } + else if (m_operation_mode == MeshBooleanOperation::Intersection){ + m_imgui->bbl_checkbox(_L("Delete input"), m_inter_delete_input); + if (operate_button(_L("Intersection") + "##btn", enable_button)) { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin()); + wxGetApp().notification_manager()->close_plater_warning_notification(warning_text); + } + else { + wxGetApp().notification_manager()->push_plater_warning_notification(warning_text); + } + } + } + + float win_w = ImGui::GetWindowWidth(); + if (last_w != win_w || last_y != y) { + // ask canvas for another frame to render the window in the correct position + m_imgui->set_requires_extra_frame(); + m_parent.set_as_dirty(); + m_parent.request_extra_frame(); + if (last_w != win_w) + last_w = win_w; + if (last_y != y) + last_y = y; + } + + GizmoImguiEnd(); + ImGuiWrapper::pop_toolbar_style(); +} + +void GLGizmoMeshBoolean::on_load(cereal::BinaryInputArchive &ar) +{ + ar(m_enable, m_operation_mode, m_selecting_state, m_diff_delete_input, m_inter_delete_input, m_src, m_tool); + ModelObject *curr_model_object = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; + m_src.mv = curr_model_object == nullptr ? nullptr : m_src.volume_idx < 0 ? nullptr : curr_model_object->volumes[m_src.volume_idx]; + m_tool.mv = curr_model_object == nullptr ? nullptr : m_tool.volume_idx < 0 ? nullptr : curr_model_object->volumes[m_tool.volume_idx]; +} + +void GLGizmoMeshBoolean::on_save(cereal::BinaryOutputArchive &ar) const +{ + ar(m_enable, m_operation_mode, m_selecting_state, m_diff_delete_input, m_inter_delete_input, m_src, m_tool); +} + +void GLGizmoMeshBoolean::generate_new_volume(bool delete_input, const TriangleMesh& mesh_result) { + + wxGetApp().plater()->take_snapshot("Mesh Boolean"); + + ModelObject* curr_model_object = m_c->selection_info()->model_object(); + + // generate new volume + ModelVolume* new_volume = curr_model_object->add_volume(std::move(mesh_result)); + + // assign to new_volume from old_volume + ModelVolume* old_volume = m_src.mv; + std::string suffix; + switch (m_operation_mode) + { + case MeshBooleanOperation::Union: + suffix = "union"; + break; + case MeshBooleanOperation::Difference: + suffix = "difference"; + break; + case MeshBooleanOperation::Intersection: + suffix = "intersection"; + break; + } + new_volume->name = old_volume->name + " - " + suffix; + new_volume->set_new_unique_id(); + new_volume->config.apply(old_volume->config); + new_volume->set_type(old_volume->type()); + new_volume->set_material_id(old_volume->material_id()); + new_volume->set_transformation(old_volume->get_transformation()); + //Vec3d translate_z = { 0,0, (new_volume->source.mesh_offset - old_volume->source.mesh_offset).z() }; + //new_volume->translate(new_volume->get_transformation().get_matrix(true) * translate_z); + //new_volume->supported_facets.assign(old_volume->supported_facets); + //new_volume->seam_facets.assign(old_volume->seam_facets); + //new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); + + // delete old_volume + std::swap(curr_model_object->volumes[m_src.volume_idx], curr_model_object->volumes.back()); + curr_model_object->delete_volume(curr_model_object->volumes.size() - 1); + + if (delete_input) { + std::vector items; + auto obj_idx = m_parent.get_selection().get_object_idx(); + items.emplace_back(ItemType::itVolume, obj_idx, m_tool.volume_idx); + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + + //bool sinking = curr_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD; + //if (!sinking) + // curr_model_object->ensure_on_bed(); + //curr_model_object->sort_volumes(true); + + wxGetApp().plater()->update(); + wxGetApp().obj_list()->select_item([this, new_volume]() { + wxDataViewItem sel_item; + + wxDataViewItemArray items = wxGetApp().obj_list()->reorder_volumes_and_get_selection(m_parent.get_selection().get_object_idx(), [new_volume](const ModelVolume* volume) { return volume == new_volume; }); + if (!items.IsEmpty()) + sel_item = items.front(); + + return sel_item; + }); + + m_src.reset(); + m_tool.reset(); + m_selecting_state = MeshBooleanSelectingState::SelectSource; +} + + +}} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp new file mode 100644 index 0000000000..42fa97eede --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp @@ -0,0 +1,88 @@ +#ifndef slic3r_GLGizmoMeshBoolean_hpp_ +#define slic3r_GLGizmoMeshBoolean_hpp_ + +#include "GLGizmoBase.hpp" +#include "GLGizmosCommon.hpp" +#include "libslic3r/Model.hpp" + +namespace Slic3r { + +namespace GUI { + +enum class MeshBooleanSelectingState { + Undef, + SelectSource, + SelectTool, + +}; +enum class MeshBooleanOperation{ + Undef, + Union, + Difference, + Intersection, +}; +struct VolumeInfo { + ModelVolume* mv{ nullptr }; + int volume_idx{-1}; + Transform3d trafo; + void reset() { + mv = nullptr; + volume_idx = -1; + trafo = Transform3d::Identity(); + }; + template + void serialize(Archive& ar) { + ar(volume_idx, trafo); + } +}; +class GLGizmoMeshBoolean : public GLGizmoBase +{ +public: + GLGizmoMeshBoolean(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + ~GLGizmoMeshBoolean(); + + void set_enable(bool enable) { m_enable = enable; } + bool get_enable() { return m_enable; } + MeshBooleanSelectingState get_selecting_state() { return m_selecting_state; } + void set_src_volume(ModelVolume* mv) { + m_src.mv = mv; + if (m_src.mv == m_tool.mv) + m_tool.reset(); + } + void set_tool_volume(ModelVolume* mv) { + m_tool.mv = mv; + if (m_tool.mv == m_src.mv) + m_src.reset(); + } + + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + +protected: + virtual bool on_init() override; + virtual std::string on_get_name() const override; + virtual bool on_is_activable() const override; + virtual void on_render() override; + virtual void on_render_for_picking() override {} + virtual void on_set_state() override; + virtual CommonGizmosDataID on_get_requirements() const override; + virtual void on_render_input_window(float x, float y, float bottom_limit); + + void on_load(cereal::BinaryInputArchive &ar) override; + void on_save(cereal::BinaryOutputArchive &ar) const override; + +private: + bool m_enable{ false }; + MeshBooleanOperation m_operation_mode; + MeshBooleanSelectingState m_selecting_state; + bool m_diff_delete_input = false; + bool m_inter_delete_input = false; + VolumeInfo m_src; + VolumeInfo m_tool; + + void generate_new_volume(bool delete_input, const TriangleMesh& mesh_result); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoMeshBoolean_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 278d0d3758..8ad89a9f2d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -212,7 +212,14 @@ void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const ++mesh_id; - const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + Transform3d trafo_matrix; + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix(); + trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + } + else { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix()* mv->get_matrix(); + } bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; if (is_left_handed) @@ -459,14 +466,14 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip; if (m_selected_extruder_idx != extruder_idx) flags |= ImGuiColorEditFlags_NoBorder; #ifdef __APPLE__ - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0); bool color_picked = ImGui::ColorButton(color_label.c_str(), color_vec, flags, button_size); ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); #else - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 2.0); bool color_picked = ImGui::ColorButton(color_label.c_str(), color_vec, flags, button_size); @@ -512,7 +519,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 8d00510c9c..6b421b1531 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -85,10 +85,15 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const continue; ++mesh_id; - - const Transform3d trafo_matrix = - mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * - mv->get_matrix(); + + Transform3d trafo_matrix; + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix(); + trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + } + else { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix()* mv->get_matrix(); + } bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; if (is_left_handed) @@ -124,7 +129,16 @@ void GLGizmoPainterBase::render_cursor() const std::vector trafo_matrices; for (const ModelVolume* mv : mo->volumes) { if (mv->is_model_part()) - trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + { + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + Transform3d temp = mi->get_assemble_transformation().get_matrix() * mv->get_matrix(); + temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + trafo_matrices.emplace_back(temp); + } + else { + trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + } + } } // Raycast and return if there's no hit. update_raycast_cache(m_parent.get_local_mouse_position(), camera, trafo_matrices); @@ -230,17 +244,35 @@ void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) co const BoundingBoxf3 box = bounding_box(); Vec3d hit_world = trafo * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)); float max_z = (float)box.max.z(); + float min_z = (float)box.min.z(); - float cursor_z = std::clamp((float)hit_world.z(), 0.f, max_z); - std::array zs = { cursor_z, std::clamp(cursor_z + m_cursor_height, 0.f, max_z) }; - for (int i = 0; i < zs.size(); i++) { - update_contours(zs[i], max_z); + float cursor_z = std::clamp((float)hit_world.z(), min_z, max_z); + std::array zs = { cursor_z, std::clamp(cursor_z + m_cursor_height, min_z, max_z) }; + + const Selection& selection = m_parent.get_selection(); + const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; + const ModelInstance* mi = model_object->instances[selection.get_instance_idx()]; + for (const ModelVolume* mv : model_object->volumes) { + TriangleMesh vol_mesh = mv->mesh(); + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + Transform3d temp = mi->get_assemble_transformation().get_matrix() * mv->get_matrix(); + temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + vol_mesh.transform(temp); + } + else { + vol_mesh.transform(mi->get_transformation().get_matrix() * mv->get_matrix()); + } + + for (int i = 0; i < zs.size(); i++) { + update_contours(vol_mesh, zs[i], max_z, min_z); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); + glsafe(::glLineWidth(2.0f)); + m_cut_contours.contours.render(); + glsafe(::glPopMatrix()); + } - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); } } @@ -257,7 +289,7 @@ BoundingBoxf3 GLGizmoPainterBase::bounding_box() const return ret; } -void GLGizmoPainterBase::update_contours(float cursor_z, float max_z) const +void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const { const Selection& selection = m_parent.get_selection(); const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); @@ -266,33 +298,32 @@ void GLGizmoPainterBase::update_contours(float cursor_z, float max_z) const const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; const int instance_idx = selection.get_instance_idx(); - if (0.0 < cursor_z && cursor_z < max_z) { - if (m_cut_contours.cut_z != cursor_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) { - m_cut_contours.cut_z = cursor_z; + if (min_z < cursor_z && cursor_z < max_z) { + if (m_cut_contours.cut_z != cursor_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) { + m_cut_contours.cut_z = cursor_z; - if (m_cut_contours.object_id != model_object->id()) - m_cut_contours.mesh = model_object->raw_mesh(); + m_cut_contours.mesh = vol_mesh; - m_cut_contours.position = box.center(); - m_cut_contours.shift = Vec3d::Zero(); - m_cut_contours.object_id = model_object->id(); - m_cut_contours.instance_idx = instance_idx; - m_cut_contours.contours.reset(); + m_cut_contours.position = box.center(); + m_cut_contours.shift = Vec3d::Zero(); + m_cut_contours.object_id = model_object->id(); + m_cut_contours.instance_idx = instance_idx; + m_cut_contours.contours.reset(); - MeshSlicingParams slicing_params; - slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix(); - const Polygons polys = slice_mesh(m_cut_contours.mesh.its, cursor_z, slicing_params); - if (!polys.empty()) { - m_cut_contours.contours.init_from(polys, static_cast(cursor_z)); - m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); + MeshSlicingParams slicing_params; + slicing_params.trafo = Transform3d::Identity().matrix(); + const Polygons polys = slice_mesh(m_cut_contours.mesh.its, cursor_z, slicing_params); + if (!polys.empty()) { + m_cut_contours.contours.init_from(polys, static_cast(cursor_z)); + m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); + } + } + else if (box.center() != m_cut_contours.position) { + m_cut_contours.shift = box.center() - m_cut_contours.position; } } - else if (box.center() != m_cut_contours.position) { - m_cut_contours.shift = box.center() - m_cut_contours.position; - } - } - else - m_cut_contours.contours.reset(); + else + m_cut_contours.contours.reset(); } bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const @@ -451,8 +482,12 @@ std::vector GLGizmoPainterBase::get_pr const Selection& selection = m_parent.get_selection(); const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - const Transform3d instance_trafo = mi->get_transformation().get_matrix(); - const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); + const Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix() : + mi->get_transformation().get_matrix(); + const Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix(true) : + mi->get_transformation().get_matrix(true); for (int mesh_idx = 0; mesh_idx < part_volumes.size(); mesh_idx++) { if (mesh_idx == m_rr.mesh_id) @@ -518,8 +553,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Selection &selection = m_parent.get_selection(); const ModelObject *mo = m_c->selection_info()->model_object(); const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true); - const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix(); + const Transform3d trafo_matrix_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true) : + mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true); + const Transform3d trafo_matrix = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix() : + mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix(); m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); @@ -581,8 +620,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Selection &selection = m_parent.get_selection(); const ModelObject *mo = m_c->selection_info()->model_object(); const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - const Transform3d instance_trafo = mi->get_transformation().get_matrix(); - const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); + Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix() : + mi->get_transformation().get_matrix(); + Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix(true) : + mi->get_transformation().get_matrix(true); std::vector part_volumes; // Precalculate transformations of individual meshes. @@ -590,7 +633,14 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous std::vector trafo_matrices_not_translate; for (const ModelVolume *mv : mo->volumes) if (mv->is_model_part()) { - trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + Transform3d temp = instance_trafo * mv->get_matrix(); + temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + trafo_matrices.emplace_back(temp); + } + else { + trafo_matrices.emplace_back(instance_trafo* mv->get_matrix()); + } trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true)); part_volumes.push_back(mv); } @@ -713,15 +763,26 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Selection &selection = m_parent.get_selection(); const ModelObject *mo = m_c->selection_info()->model_object(); const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - const Transform3d instance_trafo = mi->get_transformation().get_matrix(); - const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); + const Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix() : + mi->get_transformation().get_matrix(); + const Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + mi->get_assemble_transformation().get_matrix(true) : + mi->get_transformation().get_matrix(true); // Precalculate transformations of individual meshes. std::vector trafo_matrices; std::vector trafo_matrices_not_translate; for (const ModelVolume *mv : mo->volumes) if (mv->is_model_part()) { - trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + Transform3d temp = instance_trafo * mv->get_matrix(); + temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + trafo_matrices.emplace_back(temp); + } + else { + trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + } trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true)); } @@ -811,7 +872,8 @@ void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position, hit, normal, m_c->object_clipper()->get_clipping_plane(), - &facet)) + &facet, + m_parent.get_canvas_type() != GLCanvas3D::CanvasAssembleView)) { // In case this hit is clipped, skip it. if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index aa4554413c..db56d58656 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -368,7 +368,7 @@ private: mutable CutContours m_cut_contours; BoundingBoxf3 bounding_box() const; - void update_contours(float cursor_z, float max_z) const; + void update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const; protected: void on_set_state() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 3ea292e942..6729de6140 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -241,7 +241,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 5c2096cffb..7b7cc2065d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -273,8 +273,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.81f, 0.81f, 0.81f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.81f, 0.81f, 0.81f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.81f, 0.81f, 0.81f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); if (m_imgui->bbl_sliderin("##ReductionLevel", &reduction, 0, 4, reduce_captions[reduction].c_str())) { if (reduction < 0) reduction = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index b249b06cd8..907848e50a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -26,6 +26,7 @@ namespace Slic3r { namespace GUI { +static const double PI = 3.141592653589793238; static const wxColour FONT_TEXTURE_BG = wxColour(0, 0, 0, 0); static const wxColour FONT_TEXTURE_FG = *wxWHITE; static const int FONT_SIZE = 12; @@ -560,7 +561,7 @@ void GLGizmoText::push_button_style(bool pressed) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 150 / 255.f, 136 / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 174 / 255.f, 66 / 255.f, 1.f)); } else { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(45.f / 255.f, 45.f / 255.f, 49.f / 255.f, 1.f)); @@ -574,7 +575,7 @@ void GLGizmoText::push_button_style(bool pressed) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 150 / 255.f, 136 / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 174 / 255.f, 66 / 255.f, 1.f)); } else { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.f, 1.f, 1.f, 1.f)); @@ -595,10 +596,10 @@ void GLGizmoText::push_combo_style(const float scale) { ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG_DARK); - ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.59f, 0.53f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.59f, 0.53f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG_DARK); ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f }); } @@ -606,10 +607,10 @@ void GLGizmoText::push_combo_style(const float scale) { ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG); - ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.59f, 0.53f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.59f, 0.53f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG); ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f }); } @@ -1071,6 +1072,11 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) Vec3d rotation_axis; Matrix3d rotation_matrix; Geometry::rotation_from_two_vectors(m_cut_plane_dir, Vec3d::UnitZ(), rotation_axis, phi, &rotation_matrix); + if (abs(phi - PI) < 1e-6) { + Transform3d transform = Transform3d::Identity(); + transform.rotate(Eigen::AngleAxisd(phi, m_mouse_normal_world)); + rotation_matrix = transform.matrix().block<3, 3>(0, 0); + } Transform3d transfo1; transfo1.setIdentity(); @@ -1312,7 +1318,7 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) } TriangleMesh mesh = slice_meshs; - std::vector mesh_values(m_position_points.size(), 1'000'000'000); + std::vector mesh_values(m_position_points.size(), 1e9); m_normal_points.resize(m_position_points.size()); auto point_in_triangle_delete_area = [](const Vec3d &point, const Vec3d &point0, const Vec3d &point1, const Vec3d &point2) { Vec3d p0_p = point - point0; @@ -1383,7 +1389,6 @@ TriangleMesh GLGizmoText::get_text_mesh(const char* text_str, const Vec3d &posit new_text_dir.normalize(); Geometry::rotation_from_two_vectors(old_text_dir, new_text_dir, rotation_axis, phi, &rotation_matrix); - static double const PI = 3.141592653589793238; if (abs(phi - PI) < EPSILON) rotation_axis = normal; @@ -1466,6 +1471,9 @@ void GLGizmoText::generate_text_volume(bool is_temp) mesh.merge(sub_mesh); } + if (mesh.empty()) + return; + Plater *plater = wxGetApp().plater(); if (!plater) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 524264cd3d..4e29d10afb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -145,6 +145,11 @@ void InstancesHider::on_update() const ModelObject* mo = get_pool()->selection_info()->model_object(); int active_inst = get_pool()->selection_info()->get_active_instance(); GLCanvas3D* canvas = get_pool()->get_canvas(); + double z_min; + if (canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView) + z_min = std::numeric_limits::max(); + else + z_min = -SINKING_Z_THRESHOLD; if (mo && active_inst != -1) { canvas->toggle_model_objects_visibility(false); @@ -152,7 +157,7 @@ void InstancesHider::on_update() canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); canvas->set_use_clipping_planes(true); // Some objects may be sinking, do not show whatever is below the bed. - canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), z_min)); canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), std::numeric_limits::max())); @@ -164,7 +169,7 @@ void InstancesHider::on_update() m_clippers.clear(); for (const TriangleMesh* mesh : meshes) { m_clippers.emplace_back(new MeshClipper); - m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), z_min)); m_clippers.back()->set_mesh(*mesh); } m_old_meshes = meshes; @@ -404,18 +409,30 @@ void ObjectClipper::render_cut() const return; const SelectionInfo* sel_info = get_pool()->selection_info(); const ModelObject* mo = sel_info->model_object(); - Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + Geometry::Transformation inst_trafo; + bool is_assem_cnv = get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView; + inst_trafo = is_assem_cnv ? + mo->instances[sel_info->get_active_instance()]->get_assemble_transformation() : + mo->instances[sel_info->get_active_instance()]->get_transformation(); + auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly(); size_t clipper_id = 0; for (const ModelVolume* mv : mo->volumes) { Geometry::Transformation vol_trafo = mv->get_transformation(); Geometry::Transformation trafo = inst_trafo * vol_trafo; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - + if (is_assem_cnv) { + trafo.set_offset(trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0) + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0)); + } + else { + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + } auto& clipper = m_clippers[clipper_id]; clipper->set_plane(*m_clp); clipper->set_transformation(trafo); - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + if (is_assem_cnv) + clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), std::numeric_limits::max())); + else + clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); glsafe(::glPushMatrix()); // BBS glsafe(::glColor3f(0.25f, 0.25f, 0.25f)); @@ -438,14 +455,27 @@ void ObjectClipper::set_position(double pos, bool keep_normal) // camera_dir(2) = 0; Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : /*-camera_dir;*/ -wxGetApp().plater()->get_camera().get_dir_forward(); - const Vec3d& center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift); + Vec3d center; + + if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + const SelectionInfo* sel_info = get_pool()->selection_info(); + auto trafo = mo->instances[sel_info->get_active_instance()]->get_assemble_transformation(); + auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly(); + center = trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0); + } + else { + center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift); + } + float dist = normal.dot(center); if (pos < 0.) pos = m_clp_ratio; m_clp_ratio = pos; - m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2*m_active_inst_bb_radius))); + + m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2 * m_active_inst_bb_radius))); + get_pool()->get_canvas()->set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 7575d1dd7d..87c9a00c9e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -23,6 +23,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" #include "slic3r/GUI/Gizmos/GLGizmoText.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp" #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -54,9 +55,16 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) std::vector GLGizmosManager::get_selectable_idxs() const { std::vector out; - for (size_t i=0; iis_selectable()) - out.push_back(i); + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + for (size_t i = 0; i < m_gizmos.size(); ++i) + if (m_gizmos[i]->get_sprite_id() == (unsigned int)MmuSegmentation) + out.push_back(i); + } + else { + for (size_t i = 0; i < m_gizmos.size(); ++i) + if (m_gizmos[i]->is_selectable()) + out.push_back(i); + } return out; } @@ -78,6 +86,8 @@ size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const #if BBS_TOOLBAR_ON_TOP //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();; float top_x = std::max(m_parent.get_main_toolbar_width() + border, 0.5f * (cnv_w - width + m_parent.get_main_toolbar_width() + m_parent.get_collapse_toolbar_width() - m_parent.get_assemble_view_toolbar_width()) + border); + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) + top_x = 0.5f * cnv_w + 0.5f * (m_parent.get_assembly_paint_toolbar_width()); float top_y = 0; float stride_x = m_layout.scaled_stride_x(); @@ -161,7 +171,11 @@ void GLGizmosManager::switch_gizmos_icon_filename() case(EType::MmuSegmentation): gizmo->set_icon_filename(m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg"); break; + case(EType::MeshBoolean): + gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"); + break; } + } } @@ -191,6 +205,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg", EType::Scale, &m_object_manipulation)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg", EType::Flatten)); m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut)); + m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg", EType::MeshBoolean)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg", EType::FdmSupports)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text)); @@ -629,6 +644,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Cut) return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == MeshBoolean) + return dynamic_cast(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -875,7 +892,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || - m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut) + m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut || m_current == MeshBoolean) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; @@ -965,6 +982,17 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) update_on_off_state(mouse_pos); update_data(); m_parent.set_as_dirty(); + try { + if ((int)m_hover >= 0 && (int)m_hover < m_gizmos.size()) { + std::string name = get_name_from_gizmo_etype(m_hover); + int count = m_gizmos[m_hover]->get_count(); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) { + agent->track_update_property(name, std::to_string(count)); + } + } + } + catch (...) {} } } else if (evt.MiddleDown()) { @@ -1372,17 +1400,23 @@ void GLGizmosManager::do_render_overlay() const float width = get_scaled_total_width(); float zoomed_border = m_layout.scaled_border() * inv_zoom; - //BBS: GUI refactor: GLToolbar&&Gizmo adjust + float zoomed_top_x; + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + zoomed_top_x = 0.5f * m_parent.get_assembly_paint_toolbar_width() * inv_zoom; + } + else { + //BBS: GUI refactor: GLToolbar&&Gizmo adjust #if BBS_TOOLBAR_ON_TOP - float main_toolbar_width = (float)m_parent.get_main_toolbar_width(); - float assemble_view_width = (float)m_parent.get_assemble_view_toolbar_width(); - float collapse_width = (float)m_parent.get_collapse_toolbar_width(); - //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - //float zoomed_top_x = 0.5f *(cnv_w + main_toolbar_width - 2 * space_width - width) * inv_zoom; + float main_toolbar_width = (float)m_parent.get_main_toolbar_width(); + float assemble_view_width = (float)m_parent.get_assemble_view_toolbar_width(); + float collapse_width = (float)m_parent.get_collapse_toolbar_width(); + //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); + //float zoomed_top_x = 0.5f *(cnv_w + main_toolbar_width - 2 * space_width - width) * inv_zoom; - float main_toolbar_left = std::max(-0.5f * cnv_w, -0.5f * (main_toolbar_width + get_scaled_total_width() + assemble_view_width - collapse_width)) * inv_zoom; - //float zoomed_top_x = 0.5f *(main_toolbar_width + collapse_width - width - assemble_view_width) * inv_zoom; - float zoomed_top_x = main_toolbar_left + (main_toolbar_width) *inv_zoom; + float main_toolbar_left = std::max(-0.5f * cnv_w, -0.5f * (main_toolbar_width + get_scaled_total_width() + assemble_view_width - collapse_width)) * inv_zoom; + //float zoomed_top_x = 0.5f *(main_toolbar_width + collapse_width - width - assemble_view_width) * inv_zoom; + zoomed_top_x = main_toolbar_left + (main_toolbar_width)*inv_zoom; + } float zoomed_top_y = 0.5f * cnv_h * inv_zoom; #else //float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom; @@ -1653,5 +1687,28 @@ int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const return m_gizmos[type]->get_shortcut_key(); } +std::string get_name_from_gizmo_etype(GLGizmosManager::EType type) +{ + switch (type) { + case GLGizmosManager::EType::Move: + return "Move"; + case GLGizmosManager::EType::Rotate: + return "Rotate"; + case GLGizmosManager::EType::Scale: + return "Scale"; + case GLGizmosManager::EType::Flatten: + return "Flatten"; + case GLGizmosManager::EType::FdmSupports: + return "FdmSupports"; + case GLGizmosManager::EType::Seam: + return "Seam"; + case GLGizmosManager::EType::Text: + return "Text"; + default: + return ""; + } + return ""; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 4d13c3d07f..fefb85b6b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -70,6 +70,7 @@ public: Scale, Flatten, Cut, + MeshBoolean, FdmSupports, Seam, // BBS @@ -327,7 +328,7 @@ private: bool grabber_contains_mouse() const; }; - +std::string get_name_from_gizmo_etype(GLGizmosManager::EType type); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 7c7b158d08..2cdd8aac36 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -452,6 +452,7 @@ void GizmoObjectManipulation::set_uniform_scaling(const bool new_value) // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); // Is the angle close to a multiple of 90 degrees? + if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) { // Cannot apply scaling in the world coordinate system. // BBS: remove tilt prompt dialog diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 6fd3a54b8e..cd9a617f4b 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -241,7 +241,7 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3 bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, - size_t* facet_idx) const + size_t* facet_idx, bool sinking_limit) const { Vec3d point; Vec3d direction; @@ -258,8 +258,8 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& // Also, remove anything below the bed (sinking objects). for (i=0; i= SINKING_Z_THRESHOLD && - (! clipping_plane || ! clipping_plane->is_point_clipped(transformed_hit))) + if (transformed_hit.z() >= (sinking_limit ? SINKING_Z_THRESHOLD : -std::numeric_limits::max()) && + (!clipping_plane || !clipping_plane->is_point_clipped(transformed_hit))) break; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 02c597da3c..123b8a7852 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -150,7 +150,8 @@ public: Vec3f& position, // where to save the positibon of the hit (mesh coords) Vec3f& normal, // normal of the triangle that was hit const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) - size_t* facet_idx = nullptr // index of the facet hit + size_t* facet_idx = nullptr, // index of the facet hit + bool sinking_limit = true ) const; // Given a vector of points in woorld coordinates, this returns vector diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 9588a293e6..2e5686c3ca 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -306,6 +306,96 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init if (fading_pop) ImGui::PopStyleColor(3); } + +void NotificationManager::PopNotification::bbl_render_block_notification(GLCanvas3D &canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin) +{ + if (m_state == EState::Unknown) + init(); + + if (m_state == EState::Hidden) { + m_top_y = initial_y - GAP_WIDTH; + return; + } + + if (m_state == EState::ClosePending || m_state == EState::Finished) + { + m_state = EState::Finished; + return; + } + + Size cnv_size = canvas.get_canvas_size(); + ImGuiWrapper& imgui = *wxGetApp().imgui(); + float right_gap = right_margin + (move_from_overlay ? overlay_width + m_line_height * 5 : 0); + bool fading_pop = false; + + if (m_line_height != ImGui::CalcTextSize("A").y) + init(); + + set_next_window_size(imgui); + + // top y of window + m_top_y = initial_y + m_window_height; + + ImVec2 win_pos(1.0f * (float) cnv_size.get_width() - right_gap, 1.0f * (float) cnv_size.get_height() - m_top_y); + imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); + imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); + + // color change based on fading out + if (m_state == EState::FadingOut) { + push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity); + push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity); + push_style_color(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered), true, m_current_fade_opacity); + fading_pop = true; + } + + bool bgrnd_color_pop = push_background_color(); + + // name of window indentifies window - has to be unique string + if (m_id == 0) + m_id = m_id_provider.allocate_id(); + std::string name = "!!Ntfctn" + std::to_string(m_id); + + use_bbl_theme(); + if (m_data.level == NotificationLevel::SeriousWarningNotificationLevel) + { + push_style_color(ImGuiCol_Border, {245.f / 255.f, 155 / 255.f, 22 / 255.f, 1}, true, m_current_fade_opacity); + push_style_color(ImGuiCol_WindowBg, {245.f / 255.f, 155 / 255.f, 22 / 255.f, 1}, true, m_current_fade_opacity); + } + if (m_data.level == NotificationLevel::ErrorNotificationLevel) { + push_style_color(ImGuiCol_Border, {225.f / 255.f, 71 / 255.f, 71 / 255.f, 1}, true, m_current_fade_opacity); + push_style_color(ImGuiCol_WindowBg, {225.f / 255.f, 71 / 255.f, 71 / 255.f, 1}, true, m_current_fade_opacity); + } + push_style_color(ImGuiCol_Text, { 1,1,1,1 }, true, m_current_fade_opacity); + + if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + ImVec2 win_size = ImGui::GetWindowSize(); + ImVec2 win_pos = ImGui::GetWindowPos(); + if (ImGui::IsMouseHoveringRect(win_pos, win_pos + win_size)) { + set_hovered(); + } + + //render left icon + bbl_render_block_notif_left_sign(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + bbl_render_block_notif_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + bbl_render_block_notif_buttons(imgui, win_size, win_pos); + //render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + //m_minimize_b_visible = false; + //if (m_multiline && m_lines_count > 3) + // render_minimize_button(imgui, win_pos.x, win_pos.y); + } + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + imgui.end(); + + ImGui::PopStyleColor(3); + restore_default_theme(); + + if (bgrnd_color_pop) + ImGui::PopStyleColor(); + + if (fading_pop) + ImGui::PopStyleColor(3); +} + bool NotificationManager::PopNotification::push_background_color() { if (m_is_gray) { @@ -345,6 +435,10 @@ void NotificationManager::PopNotification::count_spaces() // m_left_indentation = picture_width + m_line_height / 2; //} m_window_width_offset = m_left_indentation + m_line_height * 3.f; + if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::SeriousWarningNotificationLevel) { + m_left_indentation = 32 + m_line_height; + m_window_width_offset = 90.f; + } m_window_width = m_line_height * 25; } @@ -464,6 +558,68 @@ void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& im m_window_height += 1 * m_line_height; // top and bottom } +void NotificationManager::PopNotification::bbl_render_block_notif_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + float x_offset = m_left_indentation; + int last_end = 0; + float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height : (m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2 : m_line_height / 2)); + float shift_y = m_line_height; + std::string line; + + for (size_t i = 0; i < (m_multiline ? m_endlines.size() : std::min(m_endlines.size(), (size_t)2)); i++) { + assert(m_endlines.size() > i && m_text1.size() >= m_endlines[i]); + line.clear(); + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) { + if (i == 1 && m_endlines.size() > 2 && !m_multiline) { + // second line with "more" hypertext + line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); + while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) { + line = line.substr(0, line.length() - 1); + } + line += ".."; + } + else { + // regural line + line = m_text1.substr(last_end, m_endlines[i] - last_end); + } + last_end = m_endlines[i]; + if (m_text1.size() > m_endlines[i]) + last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); + + if (pos_start != string::npos && pos_end != string::npos && m_endlines[i] - line.length() >= pos_start && m_endlines[i] <= pos_end) { + push_style_color(ImGuiCol_Text, m_ErrorColor, m_state == EState::FadingOut, m_current_fade_opacity); + imgui.text(line.c_str()); + ImGui::PopStyleColor(); + } + else { + imgui.text(line.c_str()); + } + } + } + //hyperlink text + if (!m_multiline && m_lines_count > 2) { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true); + } + else if (!m_hypertext.empty()) { + const ImVec2 button_size = {52, 28}; + ImVec2 cursor_pos = { win_size_x - m_line_height * 6.0f, win_size_y / 2 - button_size.y / 2 }; + ImGui::SetCursorPos(cursor_pos); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 2); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.f, 1.f, 1.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.f, 1.f, 1.f, 0.f)); + //if (imgui.button(_L("Jump"), button_size.x, button_size.y)) { if (on_text_click()) { close(); } } + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(2); + render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext); + } + + // text2 (text after hypertext) is not rendered for regular notifications + // its rendering is in HintNotification::render_text +} + void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { float x_offset = m_left_indentation; @@ -539,9 +695,18 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, ImGui::PopStyleColor(3); //hover color - ImVec4 HyperColor = m_HyperTextColor; - if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)) - HyperColor.y += 0.1f; + ImVec4 HyperColor = m_HyperTextColor;//ImVec4(150.f / 255.f, 100.f / 255.f, 0.f / 255.f, 1) + if (m_data.level == NotificationLevel::SeriousWarningNotificationLevel) + HyperColor = ImVec4(0.f, 0.f, 0.f, 0.4f); + if (m_data.level == NotificationLevel::ErrorNotificationLevel) + HyperColor = ImVec4(135.f / 255.f, 43 / 255.f, 43 / 255.f, 1); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)) + { + HyperColor.y += 0.1f; + if (m_data.level == NotificationLevel::SeriousWarningNotificationLevel || m_data.level == NotificationLevel::SeriousWarningNotificationLevel) + HyperColor.x += 0.2f; + } + //text push_style_color(ImGuiCol_Text, HyperColor, m_state == EState::FadingOut, m_current_fade_opacity); @@ -607,6 +772,64 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img ImGui::PopStyleColor(5); } +void NotificationManager::PopNotification::bbl_render_block_notif_buttons(ImGuiWrapper& imgui, ImVec2 win_size, ImVec2 win_pos) +{ + // close button + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + std::wstring button_text; + button_text = ImGui::CloseBlockNotifButton; + ImVec2 button_pic_size = ImGui::CalcTextSize(into_u8(button_text).c_str()); + ImVec2 button_size(button_pic_size.x * 2.f, button_pic_size.y * 2.f); + + ImVec2 cursor_pos = { win_size.x - m_line_height * 2.0f, win_size.y / 2 - button_size.y / 2 }; + ImGui::SetCursorPos(cursor_pos); + //if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y + win_size.y / 2 - button_pic_size.y), + // ImVec2(win_pos.x - win_size.x / 20.f, win_pos.y + win_size.y / 2 + button_pic_size.y), + // true)) + if(ImGui::IsMouseHoveringRect(win_pos + cursor_pos, win_pos + cursor_pos + button_size)) + { + button_text = ImGui::CloseBlockNotifHoverButton; + } + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + close(); + } + ImGui::PopStyleColor(5); + + + //// minimize button + //ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + //ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + //push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity); + //push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + //push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ////button - if part if treggered + //std::wstring button_text; + //button_text = m_is_dark ? ImGui::MinimalizeDarkButton : ImGui::MinimalizeButton; + //if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1), + // ImVec2(win_pos_x, win_pos_y + m_window_height), + // true)) + //{ + // button_text = m_is_dark ? ImGui::MinimalizeHoverDarkButton : ImGui::MinimalizeHoverButton; + //} + //ImVec2 button_pic_size = ImGui::CalcTextSize(into_u8(button_text).c_str()); + //ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + //ImGui::SetCursorPosX(m_window_width - m_line_height * 1.8f); + //ImGui::SetCursorPosY(m_window_height - button_size.y - 5); + //if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + //{ + // m_multiline = false; + //} + + //ImGui::PopStyleColor(5); + //m_minimize_b_visible = true; +} + //void NotificationManager::PopNotification::render_multiline(ImGuiWrapper &imgui, const float win_pos_x, const float win_pos_y) //{ // if (m_data.type == NotificationType::BBLObjectInfo) @@ -615,6 +838,19 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img // } //} +void NotificationManager::PopNotification::bbl_render_block_notif_left_sign(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + auto window = ImGui::GetCurrentWindow(); + //window->DrawList->AddImage(user_texture_id, bb.Min + padding + margin, bb.Max - padding - margin, uv0, uv1, ImGui::GetColorU32(tint_col)); + + std::wstring text; + text = ImGui::BlockNotifErrorIcon; + ImGui::SetCursorPosX(m_line_height / 3); + ImGui::SetCursorPosY(m_window_height / 2 - m_line_height); + imgui.text(text.c_str()); + +} + void NotificationManager::PopNotification::bbl_render_left_sign(ImGuiWrapper &imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { ImDrawList *draw_list = ImGui::GetWindowDrawList(); @@ -1630,19 +1866,39 @@ void NotificationManager::push_validate_error_notification(StringObjectException set_slicing_progress_hidden(); } -void NotificationManager::push_slicing_error_notification(const std::string &text, ModelObject const *obj) +void NotificationManager::push_slicing_error_notification(const std::string &text, std::vector objs) { - auto callback = obj ? [id = obj->id()](wxEvtHandler *) { + std::vector ids; + for (auto optr : objs) { + if (optr) + ids.push_back(optr->id()); + } + auto callback = !objs.empty() ? [ids](wxEvtHandler *) { auto & objects = wxGetApp().model().objects; - auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }); - if (iter != objects.end()) { - wxGetApp().obj_list()->select_items({{*iter, nullptr}}); + std::vector ovs; + for (auto id : ids) { + auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }); + if (iter != objects.end()) { ovs.push_back({*iter, nullptr}); } + } + if (!ovs.empty()) { wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); + wxGetApp().obj_list()->select_items(ovs); } return false; } : std::function(); auto link = callback ? _u8L("Jump to") : ""; - if (obj) link += std::string(" [") + obj->name + "]"; + if (!objs.empty()) { + link += " ["; + for (auto obj : objs) { + if (obj) + link += obj->name + ", "; + } + if (!objs.empty()) { + link.pop_back(); + link.pop_back(); + } + link += "] "; + } set_all_slicing_errors_gray(false); push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("Error:") + "\n" + text, link, callback }, 0); set_slicing_progress_hidden(); @@ -1653,8 +1909,8 @@ void NotificationManager::push_slicing_warning_notification(const std::string& t auto & objects = wxGetApp().model().objects; auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }); if (iter != objects.end()) { - wxGetApp().obj_list()->select_items({{*iter, nullptr}}); wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); + wxGetApp().obj_list()->select_items({{*iter, nullptr}}); } return false; } : std::function(); @@ -1869,6 +2125,53 @@ void NotificationManager::upload_job_notification_show_error(int id, const std:: } } +void NotificationManager::push_slicing_serious_warning_notification(const std::string &text, std::vector objs) +{ + std::vector ids; + for (auto optr : objs) { + if (optr) ids.push_back(optr->id()); + } + auto callback = !objs.empty() ? + [ids](wxEvtHandler *) { + auto & objects = wxGetApp().model().objects; + std::vector ovs; + for (auto id : ids) { + auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }); + if (iter != objects.end()) { ovs.push_back({*iter, nullptr}); } + } + if (!ovs.empty()) { + wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); + wxGetApp().obj_list()->select_items(ovs); + } + return false; + } : + std::function(); + auto link = callback ? _u8L("Jump to") : ""; + if (!objs.empty()) { + link += " ["; + for (auto obj : objs) { + if (obj) link += obj->name + ", "; + } + if (!objs.empty()) { + link.pop_back(); + link.pop_back(); + } + link += "] "; + } + set_all_slicing_warnings_gray(false); + push_notification_data({NotificationType::SlicingSeriousWarning, NotificationLevel::SeriousWarningNotificationLevel, 0, _u8L("Serious warning:") + "\n" + text, link, + callback}, + 0); + set_slicing_progress_hidden(); +} + +void NotificationManager::close_slicing_serious_warning_notification(const std::string &text) +{ + for (std::unique_ptr ¬ification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingSeriousWarning && notification->compare_text(_u8L("Serious warning:") + "\n" + text)) { notification->close(); } + } +} + void NotificationManager::init_slicing_progress_notification(std::function cancel_callback) { for (std::unique_ptr& notification : m_pop_notifications) { @@ -2114,12 +2417,13 @@ bool NotificationManager::push_notification_data(std::unique_ptrget_type() == NotificationType::SlicingWarning) { m_pop_notifications.back()->append(notification->get_data().ori_text); - } - else - m_pop_notifications.back()->update(notification->get_data()); + } else { + m_pop_notifications.back()->update(notification->get_data()); + } } } else { m_pop_notifications.emplace_back(std::move(notification)); + retval = true; } if (!m_initialized) @@ -2129,7 +2433,10 @@ bool NotificationManager::push_notification_data(std::unique_ptr notification, std::function condition_callback, int64_t initial_delay, int64_t delay_interval) +void NotificationManager::push_delayed_notification_data(std::unique_ptr notification, + std::function condition_callback, + int64_t initial_delay, + int64_t delay_interval) { if (initial_delay == 0 && condition_callback()) { if( push_notification_data(std::move(notification), 0)) @@ -2155,13 +2462,20 @@ void NotificationManager::render_notifications(GLCanvas3D &canvas, float overlay { sort_notifications(); - float last_y = bottom_margin * m_scale; + float bottom_up_last_y = bottom_margin * m_scale; for (const auto& notification : m_pop_notifications) { - if (notification->get_state() != PopNotification::EState::Hidden) { - notification->render(canvas, last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale); - if (notification->get_state() != PopNotification::EState::Finished) - last_y = notification->get_top() + GAP_WIDTH; + if (notification->get_data().level == NotificationLevel::ErrorNotificationLevel || notification->get_data().level == NotificationLevel::SeriousWarningNotificationLevel) { + notification->bbl_render_block_notification(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale); + if (notification->get_state() != PopNotification::EState::Finished) + bottom_up_last_y = notification->get_top() + GAP_WIDTH; + } + else { + if (notification->get_state() != PopNotification::EState::Hidden) { + notification->render(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale); + if (notification->get_state() != PopNotification::EState::Finished) + bottom_up_last_y = notification->get_top() + GAP_WIDTH; + } } } m_last_render = GLCanvas3D::timestamp_now(); @@ -2295,6 +2609,8 @@ void NotificationManager::set_in_preview(bool preview) notification->hide(preview); if (m_in_preview && notification->get_type() == NotificationType::DidYouKnowHint) notification->close(); + if (notification->get_type() == NotificationType::ValidateWarning) + notification->hide(preview); } } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 9016a3df26..7127ce30e1 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -73,6 +73,8 @@ enum class NotificationType // Slicing error produced by BackgroundSlicingProcess::validate() or by the BackgroundSlicingProcess background // thread thowing a SlicingError exception. SlicingError, + //Gcode conflict generates slicing severe warning + SlicingSeriousWarning, // Slicing warnings, issued by the slicing process. // Slicing warnings are registered for a particular Print milestone or a PrintObject and its milestone. SlicingWarning, @@ -162,6 +164,8 @@ public: ImportantNotificationLevel, // Warning, no fade-out. WarningNotificationLevel, + // Serious warning, bold reminder + SeriousWarningNotificationLevel, // Error, no fade-out. Top most position. ErrorNotificationLevel, }; @@ -191,8 +195,10 @@ public: void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage); void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host); void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host); + void push_slicing_serious_warning_notification(const std::string &text, std::vector objs); + void close_slicing_serious_warning_notification(const std::string &text); // Creates Slicing Error notification with a custom text and no fade out. - void push_slicing_error_notification(const std::string &text, ModelObject const *obj); + void push_slicing_error_notification(const std::string &text, std::vector objs); // Creates Slicing Warning notification with a custom text and no fade out. void push_slicing_warning_notification(const std::string &text, bool gray, ModelObject const *obj, ObjectID oid, int warning_step, int warning_msg_id); // marks slicing errors as gray @@ -375,6 +381,7 @@ private: PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin); + virtual void bbl_render_block_notification(GLCanvas3D &canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin); // close will dissapear notification on next render virtual void close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);} // data from newer notification of same type @@ -384,6 +391,8 @@ private: void reinit() { m_state = EState::Unknown; } // returns top after movement float get_top() const { return m_top_y; } + // returns bottom after movement + float get_bottom() const { return m_bottom_y; } //returns top in actual frame float get_current_top() const { return m_top_y; } const NotificationType get_type() const { return m_data.type; } @@ -420,6 +429,13 @@ private: const float text_x, const float text_y, const std::string text, bool more = false); + virtual void bbl_render_block_notif_text(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + virtual void bbl_render_block_notif_buttons(ImGuiWrapper& imgui, + ImVec2 win_size, + ImVec2 win_pos); + virtual void bbl_render_block_notif_left_sign(ImGuiWrapper &imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); // Left sign could be error or warning sign virtual void bbl_render_left_sign(ImGuiWrapper &imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); @@ -511,6 +527,8 @@ private: float m_window_width { 450.0f }; //Distance from bottom of notifications to top of this notification float m_top_y { 0.0f }; + //Distance from top of block notifications to bottom of this notification + float m_bottom_y { 0.0f }; // Height of text - Used as basic scaling unit! float m_line_height; // endlines for text1, hypertext excluded diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 95599ecb5f..cf758aae25 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -38,7 +38,7 @@ #include "format.hpp" #include "slic3r/GUI/GUI.hpp" #include - +#include using boost::optional; namespace fs = boost::filesystem; @@ -577,7 +577,7 @@ void PartPlate::render_logo_texture(GLTexture &logo_texture, const GeometryBuffe } } -void PartPlate::render_logo(bool bottom) const +void PartPlate::render_logo(bool bottom, bool render_cali) const { if (!m_partplate_list->render_bedtype_logo) { // render third-party printer texture logo @@ -656,6 +656,7 @@ void PartPlate::render_logo(bool bottom) const } m_partplate_list->load_bedtype_textures(); + m_partplate_list->load_cali_textures(); // btDefault should be skipped auto curr_bed_type = get_bed_type(); @@ -665,6 +666,7 @@ void PartPlate::render_logo(bool bottom) const curr_bed_type = proj_cfg.opt_enum(std::string("curr_bed_type")); } int bed_type_idx = (int)curr_bed_type; + // render bed textures for (auto &part : m_partplate_list->bed_texture_info[bed_type_idx].parts) { if (part.texture) { if (part.buffer && part.buffer->get_vertices_count() > 0 @@ -681,6 +683,24 @@ void PartPlate::render_logo(bool bottom) const } } } + + // render cali texture + if (render_cali) { + for (auto& part : m_partplate_list->cali_texture_info.parts) { + if (part.texture) { + if (part.buffer && part.buffer->get_vertices_count() > 0) { + if (part.offset.x() != m_origin.x() || part.offset.y() != m_origin.y()) { + part.offset = Vec2d(m_origin.x(), m_origin.y()); + part.update_buffer(); + } + render_logo_texture(*(part.texture), + *(part.buffer), + bottom, + part.vbo_id); + } + } + } + } } void PartPlate::render_exclude_area(bool force_default_color) const { @@ -1311,6 +1331,14 @@ std::vector PartPlate::get_extruders(bool conside_custom_gcode) const plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end()); } + // layer range + for (auto layer_range : mo->layer_config_ranges) { + if (layer_range.second.has("extruder")) { + if (auto id = layer_range.second.option("extruder")->getInt(); id > 0) + plate_extruders.push_back(id); + } + } + bool obj_support = false; const ConfigOption* obj_support_opt = mo->config.option("enable_support"); const ConfigOption *obj_raft_opt = mo->config.option("raft_layers"); @@ -1365,6 +1393,142 @@ std::vector PartPlate::get_extruders(bool conside_custom_gcode) const return plate_extruders; } +std::vector PartPlate::get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig& full_config) const +{ + std::vector plate_extruders; + + // if 3mf file + int glb_support_intf_extr = full_config.opt_int("support_interface_filament"); + int glb_support_extr = full_config.opt_int("support_filament"); + bool glb_support = full_config.opt_bool("enable_support"); + glb_support |= full_config.opt_int("raft_layers") > 0; + + for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) + { + int obj_id = it->first; + int instance_id = it->second; + + if ((obj_id >= 0) && (obj_id < m_model->objects.size())) + { + ModelObject* object = m_model->objects[obj_id]; + ModelInstance* instance = object->instances[instance_id]; + + if (!instance->printable) + continue; + + for (ModelVolume* mv : object->volumes) { + std::vector volume_extruders = mv->get_extruders(); + plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end()); + } + + // layer range + for (auto layer_range : object->layer_config_ranges) { + if (layer_range.second.has("extruder")) { + if (auto id = layer_range.second.option("extruder")->getInt(); id > 0) + plate_extruders.push_back(id); + } + } + + bool obj_support = false; + const ConfigOption* obj_support_opt = object->config.option("enable_support"); + const ConfigOption *obj_raft_opt = object->config.option("raft_layers"); + if (obj_support_opt != nullptr || obj_raft_opt != nullptr) { + if (obj_support_opt != nullptr) + obj_support = obj_support_opt->getBool(); + if (obj_raft_opt != nullptr) + obj_support |= obj_raft_opt->getInt() > 0; + } + else + obj_support = glb_support; + + if (!obj_support) + continue; + + int obj_support_intf_extr = 0; + const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament"); + if (support_intf_extr_opt != nullptr) + obj_support_intf_extr = support_intf_extr_opt->getInt(); + if (obj_support_intf_extr != 0) + plate_extruders.push_back(obj_support_intf_extr); + else if (glb_support_intf_extr != 0) + plate_extruders.push_back(glb_support_intf_extr); + + int obj_support_extr = 0; + const ConfigOption* support_extr_opt = object->config.option("support_filament"); + if (support_extr_opt != nullptr) + obj_support_extr = support_extr_opt->getInt(); + if (obj_support_extr != 0) + plate_extruders.push_back(obj_support_extr); + else if (glb_support_extr != 0) + plate_extruders.push_back(glb_support_extr); + } + } + + if (conside_custom_gcode) { + //BBS + int nums_extruders = 0; + if (const ConfigOptionStrings *color_option = dynamic_cast(full_config.option("filament_colour"))) { + nums_extruders = color_option->values.size(); + if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { + for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { + if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders) + plate_extruders.push_back(item.extruder); + } + } + } + } + + std::sort(plate_extruders.begin(), plate_extruders.end()); + auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end()); + plate_extruders.resize(std::distance(plate_extruders.begin(), it_end)); + return plate_extruders; +} + +std::vector PartPlate::get_extruders_without_support(bool conside_custom_gcode) const +{ + std::vector plate_extruders; + // if gcode.3mf file + if (m_model->objects.empty()) { + for (int i = 0; i < slice_filaments_info.size(); i++) { + plate_extruders.push_back(slice_filaments_info[i].id + 1); + } + return plate_extruders; + } + + // if 3mf file + const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + + for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) { + if (!contain_instance_totally(obj_idx, 0)) + continue; + + ModelObject* mo = m_model->objects[obj_idx]; + for (ModelVolume* mv : mo->volumes) { + std::vector volume_extruders = mv->get_extruders(); + plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end()); + } + } + + if (conside_custom_gcode) { + //BBS + int nums_extruders = 0; + if (const ConfigOptionStrings* color_option = dynamic_cast(wxGetApp().preset_bundle->project_config.option("filament_colour"))) { + nums_extruders = color_option->values.size(); + if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { + for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { + if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders) + plate_extruders.push_back(item.extruder); + } + } + } + } + + std::sort(plate_extruders.begin(), plate_extruders.end()); + auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end()); + plate_extruders.resize(std::distance(plate_extruders.begin(), it_end)); + return plate_extruders; +} + std::vector PartPlate::get_used_extruders() { std::vector used_extruders; @@ -1916,6 +2080,47 @@ void PartPlate::translate_all_instance(Vec3d position) return; } +void PartPlate::duplicate_all_instance(unsigned int dup_count, bool need_skip, std::map& skip_objects) +{ + std::set> old_obj_list = obj_to_instance_set; + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id %1%, dup_count %2%") % m_plate_index % dup_count; + for (std::set>::iterator it = old_obj_list.begin(); it != old_obj_list.end(); ++it) + { + int obj_id = it->first; + int instance_id = it->second; + + if ((obj_id >= 0) && (obj_id < m_model->objects.size())) + { + ModelObject* object = m_model->objects[obj_id]; + ModelInstance* instance = object->instances[instance_id]; + + if (need_skip) + { + if (skip_objects.find(instance->loaded_id) != skip_objects.end()) + { + instance->printable = false; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": skipped object, loaded_id %1%, name %2%, set to unprintable, no need to duplicate") % instance->loaded_id % object->name; + continue; + } + } + for (size_t index = 0; index < dup_count; index ++) + { + ModelObject* newObj = m_model->add_object(*object); + newObj->name = object->name +"_"+ std::to_string(index+1); + int new_obj_id = m_model->objects.size() - 1; + for ( size_t new_instance_id = 0; new_instance_id < object->instances.size(); new_instance_id++ ) + { + obj_to_instance_set.emplace(std::pair(new_obj_id, new_instance_id)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": duplicate object into plate: index_pair [%1%,%2%], obj_id %3%") % new_obj_id % new_instance_id % newObj->id().id; + } + } + } + } + + return; +} + + //update instance exclude state void PartPlate::update_instance_exclude_status(int obj_id, int instance_id, BoundingBoxf3* bounding_box) @@ -2012,6 +2217,28 @@ bool PartPlate::has_printable_instances() return result; } +bool PartPlate::is_all_instances_unprintable() +{ + bool result = true; + + for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { + int obj_id = it->first; + int instance_id = it->second; + + if (obj_id >= m_model->objects.size()) continue; + + ModelObject * object = m_model->objects[obj_id]; + ModelInstance *instance = object->instances[instance_id]; + + if ((instance->printable)) { + result = false; + break; + } + } + + return result; +} + //move instances to left or right PartPlate void PartPlate::move_instances_to(PartPlate& left_plate, PartPlate& right_plate, BoundingBoxf3* bounding_box) { @@ -2301,7 +2528,7 @@ bool PartPlate::intersects(const BoundingBoxf3& bb) const return print_volume.intersects(bb); } -void PartPlate::render(bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id) +void PartPlate::render(bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id, bool render_cali) { glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); @@ -2318,7 +2545,10 @@ void PartPlate::render(bool bottom, bool only_body, bool force_background_color, render_grid(bottom); if (!bottom && m_selected && !force_background_color) { - render_logo(bottom); + if (m_partplate_list) + render_logo(bottom, m_partplate_list->render_cali_logo && render_cali); + else + render_logo(bottom); } render_height_limit(mode); @@ -2838,6 +3068,7 @@ void PartPlateList::release_icon_textures() } //reset PartPlateList::is_load_bedtype_textures = false; + PartPlateList::is_load_cali_texture = false; for (int i = 0; i < btCount; i++) { for (auto& part: bed_texture_info[i].parts) { if (part.texture) { @@ -3944,8 +4175,7 @@ bool PartPlateList::preprocess_nonprefered_areas(arrangement::ArrangePolygons& r bool added = false; std::vector nonprefered_regions; - nonprefered_regions.emplace_back(Vec2d{ 45,0 }, Vec2d{ 225,25 }); // extrusion calibration region - nonprefered_regions.emplace_back(Vec2d{ 25,0 }, Vec2d{ 50,60 }); // hand-eye calibration region + nonprefered_regions.emplace_back(Vec2d{ 18,0 }, Vec2d{ 240,15 }); // new extrusion & hand-eye calibration region //has exclude areas PartPlate* plate = m_plate_list[0]; @@ -4102,7 +4332,7 @@ void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arr /*rendering related functions*/ //render -void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id) +void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) { const std::lock_guard local_lock(m_plates_mutex); std::vector::iterator it = m_plate_list.begin(); @@ -4127,15 +4357,15 @@ void PartPlateList::render(bool bottom, bool only_current, bool only_body, int h if (current_index == m_current_plate) { PartPlate::HeightLimitMode height_mode = (only_current)?PartPlate::HEIGHT_LIMIT_NONE:m_height_limit_mode; if (plate_hover_index == current_index) - (*it)->render(bottom, only_body, false, height_mode, plate_hover_action); + (*it)->render(bottom, only_body, false, height_mode, plate_hover_action, render_cali); else - (*it)->render(bottom, only_body, false, height_mode, -1); + (*it)->render(bottom, only_body, false, height_mode, -1, render_cali); } else { if (plate_hover_index == current_index) - (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action); + (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action, render_cali); else - (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1); + (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali); } } } @@ -4323,14 +4553,23 @@ bool PartPlateList::is_all_slice_results_valid() const //check whether all plates's slice result valid for print bool PartPlateList::is_all_slice_results_ready_for_print() const { - for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i) - { - if (!m_plate_list[i]->is_slice_result_ready_for_print() - && m_plate_list[i]->has_printable_instances() - ) - return false; - } - return true; + bool res = false; + + for (unsigned int i = 0; i < (unsigned int) m_plate_list.size(); ++i) { + if (!m_plate_list[i]->empty()) { + if (m_plate_list[i]->is_all_instances_unprintable()) { + continue; + } + if (!m_plate_list[i]->is_slice_result_ready_for_print()) { + return false; + } + } + if (m_plate_list[i]->is_slice_result_ready_for_print()) { + res = true; + } + } + + return res; } @@ -4564,6 +4803,7 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w plate_data_item->gcode_prediction = std::to_string( (int) m_plate_list[i]->get_slice_result()->print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time); plate_data_item->toolpath_outside = m_plate_list[i]->m_gcode_result->toolpath_outside; + plate_data_item->is_label_object_enabled = m_plate_list[i]->m_gcode_result->label_object_enabled; Print *print = nullptr; m_plate_list[i]->get_print((PrintBase **) &print, nullptr, nullptr); if (print) { @@ -4612,8 +4852,8 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate index %1% seems invalid, skip it")% plate_data_list[i]->plate_index; } - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5%") - %i %plate_data_list[i]->gcode_file %plate_data_list[i]->is_sliced_valid %plate_data_list[i]->toolpath_outside %plate_data_list[i]->is_support_used; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5% is_label_object_enabled %6%") + %i %plate_data_list[i]->gcode_file %plate_data_list[i]->is_sliced_valid %plate_data_list[i]->toolpath_outside %plate_data_list[i]->is_support_used %plate_data_list[i]->is_label_object_enabled; //load object and instance from 3mf //just test for file correct or not, we will rebuild later /*for (std::vector>::iterator it = plate_data_list[i]->objects_and_instances.begin(); it != plate_data_list[i]->objects_and_instances.end(); ++it) @@ -4634,6 +4874,7 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list) } ps.total_used_filament *= 1000; //koef gcode_result->toolpath_outside = plate_data_list[i]->toolpath_outside; + gcode_result->label_object_enabled = plate_data_list[i]->is_label_object_enabled; m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info; gcode_result->warnings = plate_data_list[i]->warnings; if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) { @@ -4722,6 +4963,7 @@ void PartPlateList::print() const } bool PartPlateList::is_load_bedtype_textures = false; +bool PartPlateList::is_load_cali_texture = false; void PartPlateList::BedTextureInfo::TexturePart::update_buffer() { @@ -4812,5 +5054,39 @@ void PartPlateList::load_bedtype_textures() PartPlateList::is_load_bedtype_textures = true; } +void PartPlateList::init_cali_texture_info() +{ + BedTextureInfo::TexturePart cali_line(18, 2, 224, 16, "bbl_cali_lines.svg"); + cali_texture_info.parts.push_back(cali_line); + + for (int j = 0; j < cali_texture_info.parts.size(); j++) { + cali_texture_info.parts[j].update_buffer(); + } +} + +void PartPlateList::load_cali_textures() +{ + if (PartPlateList::is_load_cali_texture) return; + + init_cali_texture_info(); + GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); + GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048; + for (int i = 0; i < (unsigned int)btCount; ++i) { + for (int j = 0; j < cali_texture_info.parts.size(); j++) { + std::string filename = resources_dir() + "/images/" + cali_texture_info.parts[j].filename; + if (boost::filesystem::exists(filename)) { + PartPlateList::cali_texture_info.parts[j].texture = new GLTexture(); + if (!PartPlateList::cali_texture_info.parts[j].texture->load_from_svg_file(filename, true, true, true, logo_tex_size)) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename; + } + } + else { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename; + } + } + } + PartPlateList::is_load_cali_texture = true; +} + }//end namespace GUI }//end namespace slic3r diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 9c2617f0e4..cea321358d 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -170,10 +170,8 @@ private: void calc_vertex_for_number(int index, bool one_number, GeometryBuffer &buffer); void calc_vertex_for_icons(int index, GeometryBuffer &buffer); void calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer); - //void calc_vertex_for_name_tex(GeometryBuffer &buffer); - void render_background(bool force_default_color = false) const; - void render_logo(bool bottom) const; + void render_logo(bool bottom, bool render_cali = true) const; void render_logo_texture(GLTexture& logo_texture, const GeometryBuffer& logo_buffer, bool bottom, unsigned int vbo_id) const; void render_exclude_area(bool force_default_color) const; //void render_background_for_picking(const float* render_color) const; @@ -288,6 +286,8 @@ public: Vec3d get_origin() { return m_origin; } Vec3d estimate_wipe_tower_size(const double w, const double wipe_volume) const; std::vector get_extruders(bool conside_custom_gcode = false) const; + std::vector get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig& full_config) const; + std::vector get_extruders_without_support(bool conside_custom_gcode = false) const; std::vector get_used_extruders(); /* instance related operations*/ @@ -315,6 +315,9 @@ public: //translate instance on the plate void translate_all_instance(Vec3d position); + //duplicate all instance for count + void duplicate_all_instance(unsigned int dup_count, bool need_skip, std::map& skip_objects); + //update instance exclude state void update_instance_exclude_status(int obj_id, int instance_id, BoundingBoxf3* bounding_box = nullptr); @@ -328,6 +331,7 @@ public: //whether it is has printable instances bool has_printable_instances(); + bool is_all_instances_unprintable(); //move instances to left or right PartPlate void move_instances_to(PartPlate& left_plate, PartPlate& right_plate, BoundingBoxf3* bounding_box = nullptr); @@ -340,7 +344,7 @@ public: bool contains(const BoundingBoxf3& bb) const; bool intersects(const BoundingBoxf3& bb) const; - void render(bool bottom, bool only_body = false, bool force_background_color = false, HeightLimitMode mode = HEIGHT_LIMIT_NONE, int hover_id = -1); + void render(bool bottom, bool only_body = false, bool force_background_color = false, HeightLimitMode mode = HEIGHT_LIMIT_NONE, int hover_id = -1, bool render_cali = false); void render_for_picking() const { on_render_for_picking(); } void set_selected(); void set_unselected(); @@ -400,7 +404,7 @@ public: { bool result = m_slice_result_valid; if (result) - result = m_gcode_result ? (!m_gcode_result->toolpath_outside && !m_gcode_result->conflict_result.has_value()) : false; + result = m_gcode_result ? (!m_gcode_result->toolpath_outside) : false;// && !m_gcode_result->conflict_result.has_value() gcode conflict can also print return result; } @@ -524,6 +528,7 @@ class PartPlateList : public ObjectBase // set render option bool render_bedtype_logo = true; bool render_plate_settings = true; + bool render_cali_logo = true; bool m_is_dark = false; @@ -587,6 +592,7 @@ public: static const unsigned int MAX_PLATES_COUNT = MAX_PLATE_COUNT; static GLTexture bed_textures[(unsigned int)btCount]; static bool is_load_bedtype_textures; + static bool is_load_cali_texture; PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF); PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF); @@ -732,9 +738,10 @@ public: /*rendering related functions*/ void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; } - void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1); + void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false); void render_for_picking_pass(); void set_render_option(bool bedtype_texture, bool plate_settings); + void set_render_cali(bool value = true) { render_cali_logo = value; } BoundingBoxf3& get_bounding_box() { return m_bounding_box; } //int select_plate_by_hover_id(int hover_id); int select_plate_by_obj(int obj_index, int instance_index); @@ -792,7 +799,12 @@ public: void init_bed_type_info(); void load_bedtype_textures(); + void show_cali_texture(bool show = true); + void init_cali_texture_info(); + void load_cali_textures(); + BedTextureInfo bed_texture_info[btCount]; + BedTextureInfo cali_texture_info; }; } // namespace GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3dcdb87cd4..6d64666327 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -60,6 +60,10 @@ #include "libslic3r/PresetBundle.hpp" #include "libslic3r/ClipperUtils.hpp" +// For stl export +#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp" +#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp" + #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -169,6 +173,8 @@ wxDEFINE_EVENT(EVT_INSTALL_PLUGIN_HINT, wxCommandEvent); wxDEFINE_EVENT(EVT_PREVIEW_ONLY_MODE_HINT, wxCommandEvent); //BBS: change light/dark mode wxDEFINE_EVENT(EVT_GLCANVAS_COLOR_MODE_CHANGED, SimpleEvent); +//BBS: print +wxDEFINE_EVENT(EVT_PRINT_FROM_SDCARD_VIEW, SimpleEvent); bool Plater::has_illegal_filename_characters(const wxString& wxs_name) @@ -528,8 +534,8 @@ Sidebar::Sidebar(Plater *parent) p->m_text_printer_settings = new Label(p->m_panel_printer_title, _L("Printer"), LB_PROPAGATE_MOUSE_EVENT); p->m_printer_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { - auto wizard_t = new ConfigWizard(wxGetApp().mainframe); - wizard_t->run(ConfigWizard::RR_USER, ConfigWizard::SP_CUSTOM); + //auto wizard_t = new ConfigWizard(wxGetApp().mainframe); + //wizard_t->run(ConfigWizard::RR_USER, ConfigWizard::SP_CUSTOM); }); @@ -619,11 +625,8 @@ Sidebar::Sidebar(Plater *parent) m_bed_type_list = new ComboBox(p->m_panel_printer_content, wxID_ANY, wxString(""), wxDefaultPosition, {-1, FromDIP(30)}, 0, nullptr, wxCB_READONLY); const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type"); if (bed_type_def && bed_type_def->enum_keys_map) { - for (auto item : *bed_type_def->enum_keys_map) { - if (item.first == "Default Plate") - continue; - - m_bed_type_list->AppendString(_L(item.first)); + for (auto item : bed_type_def->enum_labels) { + m_bed_type_list->AppendString(_L(item)); } } @@ -1628,6 +1631,10 @@ bool Sidebar::show_object_list(bool show) const { if (!p->m_object_list->Show(show)) return false; + if (!show) + p->object_layers->Show(false); + else + p->m_object_list->part_selection_changed(); p->scrolled->Layout(); return true; } @@ -1731,6 +1738,7 @@ struct Plater::priv bool m_slice_all{false}; bool m_is_slicing {false}; bool m_is_publishing {false}; + int m_is_RightClickInLeftUI{-1}; int m_cur_slice_plate; //BBS: m_slice_all in .gcode.3mf file case, set true when slice all bool m_slice_all_only_has_gcode{ false }; @@ -1906,6 +1914,12 @@ struct Plater::priv bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); } void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); } + bool is_view3D_overhang_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_overhang_shown(); } + void show_view3D_overhang(bool show) + { + if (current_panel == view3D) view3D->get_canvas3d()->show_overhang(show); + } + bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); } void collapse_sidebar(bool collapse); @@ -2115,6 +2129,7 @@ struct Plater::priv //void show_action_buttons(const bool is_ready_to_slice) const; bool show_publish_dlg(bool show = true); void update_publish_dialog_status(wxString &msg, int percent = -1); + void on_action_print_plate_from_sdcard(SimpleEvent&); // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, @@ -2227,6 +2242,9 @@ private: std::vector> current_warnings; bool show_warning_dialog { false }; + //record print preset + void record_start_print_preset(std::string action); + }; const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf)", std::regex::icase); @@ -2499,6 +2517,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) assemble_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); assemble_canvas->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); }); assemble_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); + assemble_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); + assemble_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); } if (wxGetApp().is_editor()) { @@ -2512,6 +2532,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->Bind(EVT_GLTOOLBAR_SLICE_PLATE, &priv::on_action_slice_plate, this); q->Bind(EVT_GLTOOLBAR_SLICE_ALL, &priv::on_action_slice_all, this); q->Bind(EVT_GLTOOLBAR_PRINT_PLATE, &priv::on_action_print_plate, this); + q->Bind(EVT_PRINT_FROM_SDCARD_VIEW, &priv::on_action_print_plate_from_sdcard, this); q->Bind(EVT_GLTOOLBAR_SELECT_SLICED_PLATE, &priv::on_action_select_sliced_plate, this); q->Bind(EVT_GLTOOLBAR_PRINT_ALL, &priv::on_action_print_all, this); q->Bind(EVT_GLTOOLBAR_EXPORT_GCODE, &priv::on_action_export_gcode, this); @@ -3701,12 +3722,13 @@ std::vector Plater::priv::load_files(const std::vector& input_ // update printable state for new volumes on canvas3D wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs); - Selection& selection = view3D->get_canvas3d()->get_selection(); - selection.clear(); - for (size_t idx : obj_idxs) { - selection.add_object((unsigned int)idx, false); + if (!load_config) { + Selection& selection = view3D->get_canvas3d()->get_selection(); + selection.clear(); + for (size_t idx : obj_idxs) { + selection.add_object((unsigned int)idx, false); + } } - // BBS: update object list selection this->sidebar->obj_list()->update_selections(); @@ -3776,17 +3798,15 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode const double max_ratio = std::max(ratio(0), ratio(1)); if (max_ratio > 10000) { MessageDialog dlg(q, _L("Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?"), _L("Object too large"), - wxICON_QUESTION | wxYES_NO); + wxICON_QUESTION | wxYES); int answer = dlg.ShowModal(); - if (answer == wxID_YES) { - // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, - // so scale down the mesh - object->scale_mesh_after_creation(1. / max_ratio); - object->origin_translation = Vec3d::Zero(); - object->center_around_origin(); - scaled_down = true; - break; - } + // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, + // so scale down the mesh + object->scale_mesh_after_creation(1. / max_ratio); + object->origin_translation = Vec3d::Zero(); + object->center_around_origin(); + scaled_down = true; + break; } else if (max_ratio > 10) { MessageDialog dlg(q, _L("Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?"), _L("Object too large"), @@ -3836,8 +3856,14 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode for (auto& instance : new_instances) { auto offset = instance->get_offset(); auto start_point = this->bed.build_volume().bounding_volume2d().center(); - auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({ start_point(0),start_point(1) }); - Vec3d displacement = { empty_cell.x(),empty_cell.y(),offset(2)}; + bool plate_empty = partplate_list.get_curr_plate()->empty(); + Vec3d displacement; + if (plate_empty) + displacement = {start_point(0), start_point(1), offset(2)}; + else { + auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({start_point(0), start_point(1)}); + displacement = {empty_cell.x(), empty_cell.y(), offset(2)}; + } instance->set_offset(displacement); } #endif @@ -3852,9 +3878,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode //} notification_manager->close_notification_of_type(NotificationType::UpdatedItemsInfo); - for (const size_t idx : obj_idxs) { - wxGetApp().obj_list()->add_object_to_list(idx); - } + wxGetApp().obj_list()->add_objects_to_list(obj_idxs); update(); // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), @@ -4166,6 +4190,7 @@ void Plater::priv::reset(bool apply_presets_change) if (view3D->is_layers_editing_enabled()) view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); + view3D->get_canvas3d()->reset_all_gizmos(); reset_gcode_toolpaths(); //BBS: update gcode to current partplate's @@ -4185,6 +4210,7 @@ void Plater::priv::reset(bool apply_presets_change) // Stop and reset the Print content. this->background_process.reset(); model.clear_objects(); + assemble_view->get_canvas3d()->reset_explosion_ratio(); update(); //BBS @@ -4505,13 +4531,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) { - // The background processing was killed and it will not be restarted. - // Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages. - SlicingProcessCompletedEvent evt(EVT_PROCESS_COMPLETED, 0, - SlicingProcessCompletedEvent::Cancelled, nullptr); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%, post an EVT_PROCESS_COMPLETED to main, status %2%")%__LINE__ %evt.status(); - wxQueueEvent(q, evt.Clone()); - } + // The background processing was killed and it will not be restarted. + // Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages. + SlicingProcessCompletedEvent evt(EVT_PROCESS_COMPLETED, 0, + SlicingProcessCompletedEvent::Cancelled, nullptr); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%, post an EVT_PROCESS_COMPLETED to main, status %2%")%__LINE__ %evt.status(); + wxQueueEvent(q, evt.Clone()); + } if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0) { @@ -4636,7 +4662,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; - show_warning_dialog = false; + show_warning_dialog = true; if (! output_path.empty()) { background_process.schedule_export(output_path.string(), output_path_on_removable_media); notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0); @@ -4668,7 +4694,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; - show_warning_dialog = false; + show_warning_dialog = true; if (! output_path.empty()) { background_process.schedule_export(output_path.string(), output_path_on_removable_media); notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0); @@ -5103,7 +5129,7 @@ void Plater::priv::reload_from_disk() if (has_source && old_volume->source.object_idx < int(new_model.objects.size())) { const ModelObject *obj = new_model.objects[old_volume->source.object_idx]; if (old_volume->source.volume_idx < int(obj->volumes.size())) { - if (obj->volumes[old_volume->source.volume_idx]->name == old_volume->name) { + if (obj->volumes[old_volume->source.volume_idx]->source.input_file == old_volume->source.input_file) { new_volume_idx = old_volume->source.volume_idx; new_object_idx = old_volume->source.object_idx; match_found = true; @@ -5574,15 +5600,16 @@ void Plater::priv::on_select_bed_type(wxCommandEvent &evt) { ComboBox* combo = static_cast(evt.GetEventObject()); int selection = combo->GetSelection(); - wxString bed_type_name = combo->GetString(selection); + std::string bed_type_name = print_config_def.get("curr_bed_type")->enum_values[selection]; + PresetBundle& preset_bundle = *wxGetApp().preset_bundle; DynamicPrintConfig& proj_config = wxGetApp().preset_bundle->project_config; const t_config_enum_values* keys_map = print_config_def.get("curr_bed_type")->enum_keys_map; if (keys_map) { BedType new_bed_type = btCount; for (auto item : *keys_map) { - if (_L(item.first) == bed_type_name) { + if (item.first == bed_type_name) { new_bed_type = (BedType)item.second; break; } @@ -5924,21 +5951,24 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) // This bool stops showing export finished notification even when process_completed_with_error is false bool has_error = false; if (evt.error()) { - std::pair message = evt.format_error_message(); + auto message = evt.format_error_message(); if (evt.critical_error()) { if (q->m_tracking_popup_menu) { // We don't want to pop-up a message box when tracking a pop-up menu. // We postpone the error message instead. q->m_tracking_popup_menu_error_message = message.first; } else { - show_error(q, message.first, message.second != 0); + show_error(q, message.first, message.second.size() != 0 && message.second[0] != 0); notification_manager->set_slicing_progress_hidden(); } } else { - ModelObject const *model_object = nullptr; - const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(message.second)); - if (print_object) model_object = print_object->model_object(); - notification_manager->push_slicing_error_notification(message.first, model_object); + std::vector ptrs; + for (auto oid : message.second) + { + const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(oid)); + if (print_object) { ptrs.push_back(print_object->model_object()); } + } + notification_manager->push_slicing_error_notification(message.first, ptrs); } if (evt.invalidate_plater()) { @@ -6219,6 +6249,18 @@ void Plater::priv::on_action_print_plate(SimpleEvent&) m_select_machine_dlg->ShowModal(); } +void Plater::priv::on_action_print_plate_from_sdcard(SimpleEvent&) +{ + if (q != nullptr) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print plate event\n"; + } + + //BBS + if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q); + m_select_machine_dlg->prepare(0); + m_select_machine_dlg->ShowModal(); +} + void Plater::priv::on_action_send_to_printer(bool isall) { @@ -7278,18 +7320,19 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle"); } } - const GLGizmosManager& gizmos = view3D->get_canvas3d()->get_gizmos_manager(); + const GLGizmosManager& gizmos = get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager(); if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator) this->undo_redo_stack().clear(); - this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), partplate_list, snapshot_data); + this->undo_redo_stack().take_snapshot(snapshot_name, model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : view3D->get_canvas3d()->get_selection(), gizmos, partplate_list, snapshot_data); if (snapshot_type == UndoRedo::SnapshotType::LeavingGizmoWithAction) { // Filter all but the last UndoRedo::SnapshotType::GizmoAction in a row between the last UndoRedo::SnapshotType::EnteringGizmo and UndoRedo::SnapshotType::LeavingGizmoWithAction. // The remaining snapshot will be renamed to a more generic name, // depending on what gizmo is being left. - assert(gizmos.get_current() != nullptr); - std::string new_name = gizmos.get_current()->get_action_snapshot_name(); - this->undo_redo_stack().reduce_noisy_snapshots(new_name); + if (gizmos.get_current() != nullptr) { + std::string new_name = gizmos.get_current()->get_action_snapshot_name(); + this->undo_redo_stack().reduce_noisy_snapshots(new_name); + } } else if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator) { // Reset the "dirty project" flag. m_undo_redo_stack_main.mark_current_as_saved(); @@ -7311,6 +7354,13 @@ void Plater::priv::undo() // BBS: undo-redo until modify record while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); if (it_current == snapshots.begin()) return; + if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction && + it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::EnteringGizmo && + it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoNoAction && + it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoWithAction) + return; + } this->undo_redo_to(it_current); } @@ -7413,8 +7463,8 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator const UndoRedo::Snapshot snapshot_copy = *it_snapshot; // Do the jump in time. if (it_snapshot->timestamp < this->undo_redo_stack().active_snapshot_time() ? - this->undo_redo_stack().undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) : - this->undo_redo_stack().redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) { + this->undo_redo_stack().undo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) : + this->undo_redo_stack().redo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) { if (printer_technology_changed) { // Switch to the other printer technology. Switch to the last printer active for that particular technology. AppConfig *app_config = wxGetApp().app_config; @@ -7491,7 +7541,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */) { - this->view3D->get_canvas3d()->get_selection().clear(); + get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear(); // Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies). this->update((unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE | (unsigned int)UpdateParams::POSTPONE_VALIDATION_ERROR_MESSAGE); // Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot. @@ -7500,8 +7550,12 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo // triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size. this->undo_redo_stack().release_least_recently_used(); //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) - this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances); - this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot); + get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + assemble_view->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances) : + this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances); + get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + assemble_view->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot) : + this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot); wxGetApp().obj_list()->update_after_undo_redo(); @@ -7653,6 +7707,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_ //get_partplate_list().update_slice_context_to_current_plate(p->background_process); //p->preview->update_gcode_result(p->partplate_list.get_current_slice_result()); reset(transfer_preset_changes); + reset_project_dirty_after_save(); reset_project_dirty_initial_presets(); wxGetApp().update_saved_preset_from_current_preset(); update_project_dirty_from_presets(); @@ -7777,6 +7832,9 @@ void Plater::load_project(wxString const& filename2, p->camera.requires_zoom_to_plate = REQUIRES_ZOOM_TO_ALL_PLATE; wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); } + else { + p->partplate_list.select_plate_view(); + } if (previous_gcode) collapse_sidebar(false); @@ -7814,7 +7872,7 @@ int Plater::save_project(bool saveAs) return wxID_CANCEL; //BBS export 3mf without gcode - if (export_3mf(into_path(filename), SaveStrategy::SplitModel) < 0) { + if (export_3mf(into_path(filename), SaveStrategy::SplitModel | SaveStrategy::ShareMesh) < 0) { MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs open the project file."), _L("Save project"), wxOK | wxICON_WARNING).ShowModal(); return wxID_CANCEL; @@ -7828,7 +7886,7 @@ int Plater::save_project(bool saveAs) up_to_date(true, true); wxGetApp().update_saved_preset_from_current_preset(); - p->dirty_state.reset_after_save(); + reset_project_dirty_after_save(); return wxID_YES; } @@ -7839,9 +7897,9 @@ void Plater::import_model_id(const std::string& download_info) std::string filename; std::string download_origin_url = wxGetApp().url_decode(download_info); - fs::path download_path = fs::path(download_origin_url); - download_url = download_origin_url; - filename = download_path.filename().string(); + fs::path download_path = fs::path(download_origin_url); + download_url = download_origin_url; + filename = download_path.filename().string(); bool download_ok = false; @@ -7937,7 +7995,8 @@ void Plater::import_model_id(const std::string& download_info) } //target_path /= (boost::format("%1%_%2%.3mf") % filename % unique).str(); - target_path /= filename.c_str(); + target_path /= fs::path(wxString(filename).wc_str()); + fs::path tmp_path = target_path; tmp_path += format(".%1%", ".download"); @@ -8048,11 +8107,11 @@ void Plater::add_model(bool imperial_units/* = false*/, std::string fname/* = " std::vector paths; if(fname.empty()){ - wxArrayString input_files; + wxArrayString input_files; wxGetApp().import_model(this, input_files); if (input_files.empty()) return; - + for (const auto &file : input_files) paths.emplace_back(into_path(file)); } @@ -8659,6 +8718,16 @@ void Plater::load_gcode(const wxString& filename) } *current_result = std::move(processor.extract_result()); //current_result->filename = filename; + + BedType bed_type = current_result->bed_type; + if (bed_type != BedType::btCount) { + DynamicPrintConfig &proj_config = wxGetApp().preset_bundle->project_config; + proj_config.set_key_value("curr_bed_type", new ConfigOptionEnum(bed_type)); + on_bed_type_change(bed_type); + } + + current_print.apply(this->model(), wxGetApp().preset_bundle->full_config()); + current_print.set_gcode_file_ready(); // show results @@ -9402,6 +9471,9 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); } bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); } void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); } +bool Plater::is_view3D_overhang_shown() const { return p->is_view3D_overhang_shown(); } +void Plater::show_view3D_overhang(bool show) { p->show_view3D_overhang(show); } + bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); } void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); } @@ -9446,8 +9518,12 @@ int GUI::Plater::close_with_confirm(std::function second_check) wxGetApp().app_config->set("save_project_choise", result == wxID_YES ? "yes" : "no"); if (result == wxID_YES) { result = save_project(); - if (result == wxID_CANCEL) - return result; + if (result == wxID_CANCEL) { + if (choise.empty()) + return result; + else + result = wxID_NO; + } } } @@ -9938,38 +10014,92 @@ void Plater::export_core_3mf() export_3mf(path_u8, SaveStrategy::Silence); } +// Following lambda generates a combined mesh for export with normals pointing outwards. +TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, std::function notify_func) +{ + TriangleMesh mesh; + + std::vector csgmesh; + csgmesh.reserve(2 * mo.volumes.size()); + bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh), + csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits); + + if (csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }) == csgmesh.end()) { + try { + MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) }); + mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr); + } + catch (...) {} +#if 0 + // if mcut fails, try again with CGAL + if (mesh.empty()) { + try { + auto meshPtr = csg::perform_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }); + mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*meshPtr); + } + catch (...) {} + } +#endif + } + + if (mesh.empty()) { + if (notify_func) + notify_func(_u8L("Unable to perform boolean operation on model meshes. " + "Only positive parts will be exported.")); + + for (const ModelVolume* v : mo.volumes) + if (v->is_model_part()) { + TriangleMesh vol_mesh(v->mesh()); + vol_mesh.transform(v->get_matrix(), true); + mesh.merge(vol_mesh); + } + } + + if (instance_id == -1) { + TriangleMesh vols_mesh(mesh); + mesh = TriangleMesh(); + for (const ModelInstance* i : mo.instances) { + TriangleMesh m = vols_mesh; + m.transform(i->get_matrix(), true); + mesh.merge(m); + } + } + else if (0 <= instance_id && instance_id < int(mo.instances.size())) + mesh.transform(mo.instances[instance_id]->get_matrix(), true); + return mesh; +} + +// BBS export with/without boolean, however, stil merge mesh +#define EXPORT_WITH_BOOLEAN 0 void Plater::export_stl(bool extended, bool selection_only) { if (p->model.objects.empty()) { return; } - wxBusyCursor wait; - - const auto &selection = p->get_selection(); - - // BBS support mulity objects - // const auto obj_idx = selection.get_object_idx(); - // if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) - // return; - - if (selection_only && selection.is_wipe_tower()) - return; - - //BBS - if (selection_only) { - // only support selection single full object and mulitiple full object - if (!selection.is_single_full_object() && !selection.is_multiple_full_object()) - return; - } - wxString path = p->get_export_file(FT_STL); if (path.empty()) { return; } const std::string path_u8 = into_u8(path); + wxBusyCursor wait; + const auto& selection = p->get_selection(); + const auto obj_idx = selection.get_object_idx(); + +#if EXPORT_WITH_BOOLEAN + if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) + return; +#else + // BBS support selecting multiple objects + if (selection_only && selection.is_wipe_tower()) return; + + // BBS + if (selection_only) { + // only support selection single full object and mulitiple full object + if (!selection.is_single_full_object() && !selection.is_multiple_full_object()) return; + } // Following lambda generates a combined mesh for export with normals pointing outwards. - auto mesh_to_export = [](const ModelObject& mo, int instance_id) { + auto mesh_to_export_fff_no_boolean = [](const ModelObject &mo, int instance_id) { TriangleMesh mesh; - for (const ModelVolume* v : mo.volumes) + for (const ModelVolume *v : mo.volumes) if (v->is_model_part()) { TriangleMesh vol_mesh(v->mesh()); vol_mesh.transform(v->get_matrix(), true); @@ -9978,125 +10108,72 @@ void Plater::export_stl(bool extended, bool selection_only) if (instance_id == -1) { TriangleMesh vols_mesh(mesh); mesh = TriangleMesh(); - for (const ModelInstance* i : mo.instances) { + for (const ModelInstance *i : mo.instances) { TriangleMesh m = vols_mesh; m.transform(i->get_matrix(), true); mesh.merge(m); } - } - else if (0 <= instance_id && instance_id < int(mo.instances.size())) + } else if (0 <= instance_id && instance_id < int(mo.instances.size())) mesh.transform(mo.instances[instance_id]->get_matrix(), true); return mesh; }; +#endif + auto mesh_to_export_sla = [&, this](const ModelObject& mo, int instance_id) { + TriangleMesh mesh; - TriangleMesh mesh; - if (p->printer_technology == ptFFF) { - if (selection_only) { - if (selection.is_single_full_object()) { - const auto obj_idx = selection.get_object_idx(); - const ModelObject* model_object = p->model.objects[obj_idx]; - if (selection.get_mode() == Selection::Instance) - { - mesh = std::move(mesh_to_export(*model_object, ( model_object->instances.size() > 1) ? -1 : selection.get_instance_idx())); - } - else - { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - mesh = model_object->volumes[volume->volume_idx()]->mesh(); - mesh.transform(volume->get_volume_transformation().get_matrix(), true); - } + const SLAPrintObject *object = this->p->sla_print.get_print_object_by_model_object_id(mo.id()); - if (model_object->instances.size() == 1) - mesh.translate(-model_object->origin_translation.cast()); - } - else if (selection.is_multiple_full_object()) { - const std::set>& instances_idxs = p->get_selection().get_selected_object_instances(); - for (const std::pair& i : instances_idxs) - { - ModelObject* object = p->model.objects[i.first]; - mesh.merge(mesh_to_export(*object, i.second)); - } - } - } + if (auto m = object->get_mesh_to_print(); m.empty()) + mesh = combine_mesh_fff(mo, instance_id, [this](const std::string& msg) {return get_notification_manager()->push_plater_error_notification(msg); }); else { - for (const ModelObject *o : p->model.objects) - mesh.merge(mesh_to_export(*o, -1)); - } - } - else - { - // This is SLA mode, all objects have only one volume. - // However, we must have a look at the backend to load - // hollowed mesh and/or supports - const auto obj_idx = selection.get_object_idx(); - const PrintObjects& objects = p->sla_print.objects(); - for (const SLAPrintObject* object : objects) - { - const ModelObject* model_object = object->model_object(); - if (selection_only) { - if (model_object->id() != p->model.objects[obj_idx]->id()) - continue; - } - Transform3d mesh_trafo_inv = object->trafo().inverse(); - bool is_left_handed = object->is_left_handed(); + const Transform3d mesh_trafo_inv = object->trafo().inverse(); + const bool is_left_handed = object->is_left_handed(); - TriangleMesh pad_mesh; - bool has_pad_mesh = extended && object->has_mesh(slaposPad); - if (has_pad_mesh) - { - pad_mesh = object->get_mesh(slaposPad); - pad_mesh.transform(mesh_trafo_inv); - } + auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{}; + pad_mesh.transform(mesh_trafo_inv); + + auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{}; + supports_mesh.transform(mesh_trafo_inv); - TriangleMesh supports_mesh; - bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree); - if (has_supports_mesh) - { - supports_mesh = object->get_mesh(slaposSupportTree); - supports_mesh.transform(mesh_trafo_inv); - } const std::vector& obj_instances = object->instances(); - for (const SLAPrintObject::Instance& obj_instance : obj_instances) - { - auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), - [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); - assert(it != model_object->instances.end()); + for (const SLAPrintObject::Instance& obj_instance : obj_instances) { + auto it = std::find_if(object->model_object()->instances.begin(), object->model_object()->instances.end(), + [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); + assert(it != object->model_object()->instances.end()); - if (it != model_object->instances.end()) - { - bool one_inst_only = selection_only && ! selection.is_single_full_object(); + if (it != object->model_object()->instances.end()) { + const bool one_inst_only = selection_only && ! selection.is_single_full_object(); - int instance_idx = it - model_object->instances.begin(); + const int instance_idx = it - object->model_object()->instances.begin(); const Transform3d& inst_transform = one_inst_only - ? Transform3d::Identity() - : object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); + ? Transform3d::Identity() + : object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); TriangleMesh inst_mesh; - if (has_pad_mesh) - { + if (!pad_mesh.empty()) { TriangleMesh inst_pad_mesh = pad_mesh; inst_pad_mesh.transform(inst_transform, is_left_handed); inst_mesh.merge(inst_pad_mesh); } - if (has_supports_mesh) - { + if (!supports_mesh.empty()) { TriangleMesh inst_supports_mesh = supports_mesh; inst_supports_mesh.transform(inst_transform, is_left_handed); inst_mesh.merge(inst_supports_mesh); } - TriangleMesh inst_object_mesh = object->get_mesh_to_slice(); + TriangleMesh inst_object_mesh = object->get_mesh_to_print(); + inst_object_mesh.transform(mesh_trafo_inv); inst_object_mesh.transform(inst_transform, is_left_handed); inst_mesh.merge(inst_object_mesh); - // ensure that the instance lays on the bed - inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min[2]); + // ensure that the instance lays on the bed + inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z()); - // merge instance with global mesh + // merge instance with global mesh mesh.merge(inst_mesh); if (one_inst_only) @@ -10104,6 +10181,50 @@ void Plater::export_stl(bool extended, bool selection_only) } } } + + return mesh; + }; + + std::function + mesh_to_export; + + if (p->printer_technology == ptFFF) +#if EXPORT_WITH_BOOLEAN + mesh_to_export = [this](const ModelObject& mo, int instance_id) {return Plater::combine_mesh_fff(mo, instance_id, + [this](const std::string& msg) {return get_notification_manager()->push_plater_error_notification(msg); }); }; +#else + mesh_to_export = mesh_to_export_fff_no_boolean; +#endif + else + mesh_to_export = mesh_to_export_sla; + + TriangleMesh mesh; + if (selection_only) { + if (selection.is_single_full_object()) { + const auto obj_idx = selection.get_object_idx(); + const ModelObject* model_object = p->model.objects[obj_idx]; + if (selection.get_mode() == Selection::Instance) + mesh = mesh_to_export(*model_object, (model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()); + else { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + mesh = model_object->volumes[volume->volume_idx()]->mesh(); + mesh.transform(volume->get_volume_transformation().get_matrix(), true); + } + + if (model_object->instances.size() == 1) mesh.translate(-model_object->origin_translation.cast()); + } + else if (selection.is_multiple_full_object()) { + const std::set>& instances_idxs = p->get_selection().get_selected_object_instances(); + for (const std::pair& i : instances_idxs) { + ModelObject* object = p->model.objects[i.first]; + mesh.merge(mesh_to_export(*object, i.second)); + } + } + } + else { + for (const ModelObject* o : p->model.objects) { + mesh.merge(mesh_to_export(*o, -1)); + } } Slic3r::store_stl(path_u8.c_str(), &mesh, true); @@ -11038,7 +11159,7 @@ std::vector Plater::get_colors_for_color_print(const GCodeProcessor return colors; } - + wxString Plater::get_project_filename(const wxString& extension) const { return p->get_project_filename(extension); @@ -11469,7 +11590,8 @@ int Plater::select_plate(int plate_index, bool need_slice) { if (need_slice) { //from preview's thumbnail if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) || (gcode_result->moves.empty())){ - //part_plate->update_slice_result_valid_state(false); + if (invalidated & PrintBase::APPLY_STATUS_INVALIDATED) + part_plate->update_slice_result_valid_state(false); p->process_completed_with_error = -1; p->m_slice_all = false; reset_gcode_toolpaths(); @@ -11897,11 +12019,23 @@ void Plater::show_object_info() { NotificationManager *notify_manager = get_notification_manager(); const Selection& selection = get_selection(); + int selCount = selection.get_volume_idxs().size(); ModelObjectPtrs objects = model().objects; int obj_idx = selection.get_object_idx(); std::string info_text; - if (objects.empty() || (obj_idx < 0) || (obj_idx >= objects.size()) || + if (selCount > 1 && !selection.is_single_full_object()) { + notify_manager->bbl_close_objectsinfo_notification(); + if (selection.get_mode() == Selection::EMode::Volume) { + info_text += (boost::format(_utf8(L("Number of currently selected parts: %1%\n"))) % selCount).str(); + } else if (selection.get_mode() == Selection::EMode::Instance) { + int content_count = selection.get_content().size(); + info_text += (boost::format(_utf8(L("Number of currently selected objects: %1%\n"))) % content_count).str(); + } + notify_manager->bbl_show_objectsinfo_notification(info_text, false, !(p->current_panel == p->view3D)); + return; + } + else if (objects.empty() || (obj_idx < 0) || (obj_idx >= objects.size()) || objects[obj_idx]->volumes.empty() ||// hack to avoid crash when deleting the last object on the bed (selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) || !(selection.is_single_full_instance() || selection.is_single_volume())) @@ -11939,12 +12073,12 @@ void Plater::show_object_info() size = vol->get_convex_hull().transformed_bounding_box(t).size(); } else { - int obj_idx, vol_idx; - wxGetApp().obj_list()->get_selected_item_indexes(obj_idx, vol_idx); - if (obj_idx < 0) { - //corner case when merge/split/remove - return; - } + //int obj_idx, vol_idx; + //wxGetApp().obj_list()->get_selected_item_indexes(obj_idx, vol_idx); + //if (obj_idx < 0) { + // //corner case when merge/split/remove + // return; + //} info_text += (boost::format(_utf8(L("Object name: %1%\n"))) % model_object->name).str(); face_count = static_cast(model_object->facets_count()); size = model_object->instance_convex_hull_bounding_box(inst_idx).size(); @@ -12277,7 +12411,8 @@ wxMenu* Plater::default_menu() { return p->menus.default_menu(); wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); } wxMenu* Plater::layer_menu() { return p->menus.layer_menu(); } wxMenu* Plater::multi_selection_menu() { return p->menus.multi_selection_menu(); } - +int Plater::GetPlateIndexByRightMenuInLeftUI() { return p->m_is_RightClickInLeftUI; } +void Plater::SetPlateIndexByRightMenuInLeftUI(int index) { p->m_is_RightClickInLeftUI = index; } SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() : m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled()) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a1ba2e827b..0796b69a32 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -288,6 +288,9 @@ public: bool are_view3D_labels_shown() const; void show_view3D_labels(bool show); + bool is_view3D_overhang_shown() const; + void show_view3D_overhang(bool show); + bool is_sidebar_collapsed() const; void collapse_sidebar(bool show); @@ -332,6 +335,7 @@ public: void export_gcode_3mf(bool export_all = false); void send_gcode_finish(wxString name); void export_core_3mf(); + static TriangleMesh combine_mesh_fff(const ModelObject& mo, int instance_id, std::function notify_func = {}); void export_stl(bool extended = false, bool selection_only = false); //BBS: remove amf //void export_amf(); @@ -686,7 +690,8 @@ public: wxMenu* instance_menu(); wxMenu* layer_menu(); wxMenu* multi_selection_menu(); - + int GetPlateIndexByRightMenuInLeftUI(); + void SetPlateIndexByRightMenuInLeftUI(int); static bool has_illegal_filename_characters(const wxString& name); static bool has_illegal_filename_characters(const std::string& name); static void show_illegal_characters_warning(wxWindow* parent); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index d82834e2c8..c9e8fb9b4c 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1961,7 +1961,9 @@ void Selection::update_type() unsigned int sels_cntr = 0; for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) { - const ModelObject* model_object = m_model->objects[it->first]; + bool is_wipe_tower = it->first >= 1000; + int actual_obj_id = is_wipe_tower ? it->first - 1000 : it->first; + const ModelObject *model_object = m_model->objects[actual_obj_id]; unsigned int volumes_count = (unsigned int)model_object->volumes.size(); unsigned int instances_count = (unsigned int)model_object->instances.size(); sels_cntr += volumes_count * instances_count; @@ -2710,9 +2712,14 @@ void Selection::paste_objects_from_clipboard() displacement = {empty_cell.x() + point_offset.x(), empty_cell.y() + point_offset.y(), start_offset(2)}; } - for (ModelInstance* inst : dst_object->instances) + for (ModelInstance* inst : dst_object->instances) { inst->set_offset(displacement); + //BBS init asssmble transformation + Geometry::Transformation t = inst->get_transformation(); + inst->set_assemble_transformation(t); + } + object_idxs.push_back(m_model->objects.size() - 1); #ifdef _DEBUG check_model_ids_validity(*m_model); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 5c1669b1b9..bfee49bd2b 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -362,6 +362,11 @@ public: bool requires_local_axes() const; + void render_bounding_box(const BoundingBoxf3& box, float* color, float scale) { + m_scale_factor = scale; + render_bounding_box(box, color); + } + //BBS void cut_to_clipboard(); void copy_to_clipboard();