diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 3b99ccd82..6b8a38f5c 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1477,6 +1477,7 @@ namespace Slic3r { stl_get_size(&stl); volume->mesh.repair(); + volume->center_geometry(); volume->calculate_convex_hull(); // apply volume's name and config data diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 900397f70..81ed392c2 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -819,14 +819,27 @@ TriangleMesh ModelObject::full_raw_mesh() const BoundingBoxf3 ModelObject::raw_bounding_box() const { BoundingBoxf3 bb; +#if ENABLE_GENERIC_SUBPARTS_PLACEMENT + if (this->instances.empty()) + throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); + + const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true); +#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT for (const ModelVolume *v : this->volumes) if (v->is_model_part()) { +#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT if (this->instances.empty()) throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); +#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT TriangleMesh vol_mesh(v->mesh); +#if ENABLE_GENERIC_SUBPARTS_PLACEMENT + vol_mesh.transform(inst_matrix * v->get_matrix()); + bb.merge(vol_mesh.bounding_box()); +#else vol_mesh.transform(v->get_matrix()); bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true)); +#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT } return bb; } @@ -835,13 +848,21 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const { BoundingBoxf3 bb; +#if ENABLE_GENERIC_SUBPARTS_PLACEMENT + const Transform3d& inst_matrix = this->instances[instance_idx]->get_transformation().get_matrix(dont_translate); +#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT for (ModelVolume *v : this->volumes) { if (v->is_model_part()) { TriangleMesh mesh(v->mesh); +#if ENABLE_GENERIC_SUBPARTS_PLACEMENT + mesh.transform(inst_matrix * v->get_matrix()); + bb.merge(mesh.bounding_box()); +#else mesh.transform(v->get_matrix()); bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate)); +#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT } } return bb; diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index bc02d2b58..c2fcb3c3a 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -223,6 +223,9 @@ struct Head { // If there is a pillar connecting to this head, then the id will be set. long pillar_id = -1; + inline void invalidate() { id = -1; } + inline bool is_valid() const { return id >= 0; } + Head(double r_big_mm, double r_small_mm, double length_mm, @@ -739,8 +742,10 @@ public: for(auto& head : heads()) { if(m_ctl.stopcondition()) break; - auto&& m = mesh(head.mesh); - meshcache.merge(m); + if(head.is_valid()) { + auto&& m = mesh(head.mesh); + meshcache.merge(m); + } } for(auto& stick : pillars()) { @@ -1533,8 +1538,19 @@ bool SLASupportTree::generate(const PointSet &points, // In this case there is no room for the base pinhead. if(gh < head.fullwidth()) { - base_width = gh - 2 * cfg.head_front_radius_mm - - 2*cfg.head_back_radius_mm + cfg.head_penetration_mm; + double min_l = + 2 * cfg.head_front_radius_mm + + 2 * cfg.head_back_radius_mm - cfg.head_penetration_mm; + + base_width = gh - min_l; + } + + if(base_width < 0) { + // There is really no space for even a reduced size head. We + // have to replace that with a small half sphere that touches + // the model surface. (TODO) + head.invalidate(); + continue; } head.transform(); @@ -1555,6 +1571,7 @@ bool SLASupportTree::generate(const PointSet &points, // This should not happen it is against all assumptions BOOST_LOG_TRIVIAL(warning) << "Ignoring invalid supports connecting to model body"; + head.invalidate(); continue; } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 629407ee2..3d63f781c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,3 +58,5 @@ #define ENABLE_NEW_EULER_ANGLES (1 && ENABLE_1_42_0_ALPHA4) // Added minimum threshold for click and drag movements #define ENABLE_MOVE_MIN_THRESHOLD (1 && ENABLE_1_42_0_ALPHA4) +// Modified initial default placement of generic subparts +#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 2af2dc27d..3dd160432 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -436,7 +436,7 @@ void SpinCtrl::BUILD() { propagate_value(); }), temp->GetId()); - temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); + temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { propagate_value(); }), temp->GetId()); temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) { @@ -472,7 +472,7 @@ void SpinCtrl::propagate_value() { if (tmp_value < 0) on_kill_focus(); - else + else if (boost::any_cast(m_value) != tmp_value) on_change_field(); } diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index bcc94c7ba..3273f84c9 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -325,6 +325,7 @@ public: void set_value(const boost::any& value, bool change_event = false) { m_disable_change_event = !change_event; tmp_value = boost::any_cast(value); + m_value = value; dynamic_cast(window)->SetValue(tmp_value); m_disable_change_event = false; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 27f0d54c3..50cc028a9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4662,11 +4662,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_camera.set_scene_box(scene_bounding_box(), *this); m_camera.set_target(m_camera.get_target(), *this); - // if no object is selected, deactivate active gizmo, if any - // otherwise it will be shown after cleaning the scene (while it is active) if (m_selection.is_empty()) + { + // If no object is selected, deactivate the active gizmo, if any + // Otherwise it may be shown after cleaning the scene (if it was active while the objects were deleted) m_gizmos.reset_all_states(); + // If no object is selected, reset the objects manipulator on the sidebar + // to force a reset of its cache + auto manip = wxGetApp().obj_manipul(); + if (manip != nullptr) + manip->update_settings_value(m_selection); + } + // and force this canvas to be redrawn. m_dirty = true; } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4441c8c8b..54e3e4750 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -760,7 +760,8 @@ Sidebar& GUI_App::sidebar() ObjectManipulation* GUI_App::obj_manipul() { - return sidebar().obj_manipul(); + // If this method is called before plater_ has been initialized, return nullptr (to avoid a crash) + return (plater_ != nullptr) ? sidebar().obj_manipul() : nullptr; } ObjectSettings* GUI_App::obj_settings() diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 77906fe0d..24aa5e45c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -895,9 +895,29 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); new_volume->set_type(static_cast(type)); +#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT new_volume->set_offset(Vec3d(0.0, 0.0, (*m_objects)[obj_idx]->origin_translation(2) - mesh.stl.stats.min(2))); +#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT new_volume->center_geometry(); +#if ENABLE_GENERIC_SUBPARTS_PLACEMENT + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + int instance_idx = selection.get_instance_idx(); + if (instance_idx != -1) + { + const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const Transform3d& inst_m = v->get_instance_transformation().get_matrix(true); + TriangleMesh vol_mesh(mesh); + vol_mesh.transform(inst_m); + Vec3d vol_shift = -vol_mesh.bounding_box().center(); + vol_mesh.translate((float)vol_shift(0), (float)vol_shift(1), (float)vol_shift(2)); + Vec3d world_mesh_bb_size = vol_mesh.bounding_box().size(); + BoundingBoxf3 inst_bb = (*m_objects)[obj_idx]->instance_bounding_box(instance_idx); + Vec3d world_target = Vec3d(inst_bb.max(0), inst_bb.min(1), inst_bb.min(2)) + 0.5 * world_mesh_bb_size; + new_volume->set_offset(inst_m.inverse() * (world_target - v->get_instance_offset())); + } +#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT + new_volume->name = name; // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8a19ccee9..2a6003986 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -428,17 +428,25 @@ void MainFrame::init_menubar() // menubar // assign menubar to frame after appending items, otherwise special items // will not be handled correctly - { - auto menubar = new wxMenuBar(); - menubar->Append(fileMenu, L("&File")); - if (editMenu) menubar->Append(editMenu, L("&Edit")); - menubar->Append(windowMenu, L("&Window")); - if (viewMenu) menubar->Append(viewMenu, L("&View")); - // Add additional menus from C++ - wxGetApp().add_config_menu(menubar); - menubar->Append(helpMenu, L("&Help")); - SetMenuBar(menubar); + auto menubar = new wxMenuBar(); + menubar->Append(fileMenu, L("&File")); + if (editMenu) menubar->Append(editMenu, L("&Edit")); + menubar->Append(windowMenu, L("&Window")); + if (viewMenu) menubar->Append(viewMenu, L("&View")); + // Add additional menus from C++ + wxGetApp().add_config_menu(menubar); + menubar->Append(helpMenu, L("&Help")); + SetMenuBar(menubar); + +#ifdef __APPLE__ + // This fixes a bug (?) on Mac OS where the quit command doesn't emit window close events + wxMenu *apple_menu = menubar->OSXGetAppleMenu(); + if (apple_menu != nullptr) { + apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) { + Close(); + }, wxID_EXIT); } +#endif } // To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 05852097b..d78e9c695 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1132,6 +1132,13 @@ void TabPrint::update() if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) return; // ys_FIXME + //! Temporary workaround for the correct updates of the SpinCtrl (like "perimeters"): + // KillFocus() for the wxSpinCtrl use CallAfter function. So, + // to except the duplicate call of the update() after dialog->ShowModal(), + // let check if this process is already started. + if (is_msg_dlg_already_exist) + return; + Freeze(); double fill_density = m_config->option("fill_density")->value; @@ -1147,6 +1154,7 @@ void TabPrint::update() "- no ensure_vertical_shell_thickness\n" "\nShall I adjust those settings in order to enable Spiral Vase?")); auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO); + is_msg_dlg_already_exist = true; DynamicPrintConfig new_conf = *m_config; if (dialog->ShowModal() == wxID_YES) { new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); @@ -1162,6 +1170,7 @@ void TabPrint::update() } load_config(new_conf); on_value_change("fill_density", fill_density); + is_msg_dlg_already_exist = false; } if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index b1ec505ab..9ec54e6eb 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -287,6 +287,7 @@ protected: class TabPrint : public Tab { + bool is_msg_dlg_already_exist {false}; public: TabPrint(wxNotebook* parent) : Tab(parent, _(L("Print Settings")), "print") {}