diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 36034d904..b18756a5e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -713,7 +713,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t ModelVolume* v = new ModelVolume(this, other); if (type != ModelVolumeType::INVALID && v->type() != type) v->set_type(type); - v->source.is_connector = other.source.is_connector; + v->cut_info = other.cut_info; this->volumes.push_back(v); // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. // v->center_geometry_after_creation(); @@ -1380,10 +1380,9 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) { - // discard old connector markers vor volumes - for (ModelVolume* volume : volumes) { - volume->source.is_connector = false; - } + // discard old connector markers for volumes + for (ModelVolume* volume : volumes) + volume->cut_info.discard(); if (cut_connectors.empty()) return; @@ -1404,8 +1403,8 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr Vec3d::Ones() )); + new_volume->cut_info = { true, connector.radius_tolerance, connector.height_tolerance }; new_volume->name = name + "-" + std::to_string(++connector_id); - new_volume->source.is_connector = true; } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1481,27 +1480,47 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const dowels->input_file.clear(); } + using namespace Geometry; + // Because transformations are going to be applied to meshes directly, // we reset transformation of all instances and volumes, // except for translation and Z-rotation on instances, which are preserved // in the transformation matrix and not applied to the mesh transform. // const auto instance_matrix = instances[instance]->get_matrix(true); - const auto instance_matrix = Geometry::assemble_transform( + const auto instance_matrix = assemble_transform( Vec3d::Zero(), // don't apply offset instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), instances[instance]->get_scaling_factor(), instances[instance]->get_mirror() ); - const auto cut_matrix = Geometry::assemble_transform(-cut_center); - - const auto invert_cut_matrix = Geometry::assemble_transform(cut_center, cut_rotation); + const auto cut_matrix = rotation_transform(cut_rotation).inverse() * assemble_transform(-cut_center); + const auto invert_cut_matrix = assemble_transform(cut_center, cut_rotation); // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); Vec3d local_dowels_displace = Vec3d::Zero(); + Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); + + auto apply_tolerance = [](ModelVolume * vol) + { + Vec3d sf = vol->get_scaling_factor(); +/* + // correct Z offset in respect to the new size + Vec3d pos = vol->get_offset(); + pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance; + vol->set_offset(pos); +*/ + // make a "hole" wider + sf[X] *= (1 + vol->cut_info.radius_tolerance); + sf[Y] *= (1 + vol->cut_info.radius_tolerance); + // make a "hole" dipper + sf[Z] *= (1 + vol->cut_info.height_tolerance); + vol->set_scaling_factor(sf); + }; + for (ModelVolume* volume : volumes) { const auto volume_matrix = volume->get_matrix(); @@ -1510,43 +1529,33 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const volume->mmu_segmentation_facets.reset(); if (!volume->is_model_part()) { - if (volume->source.is_connector) { + if (volume->cut_info.is_connector) { + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - - Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper) ? - Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix : - instance_matrix * volume_matrix; - volume->set_transformation(m); - ModelVolume* vol = upper->add_volume(*volume); - // make a "hole" dipper - vol->set_scaling_factor(Z, 1.1 * vol->get_scaling_factor(Z)); + vol->set_transformation(volume_matrix); + apply_tolerance(vol); } if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - - Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? - Geometry::rotation_transform(Geometry::deg2rad(180.0) * Vec3d::UnitX()) * Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix : - instance_matrix * volume_matrix; - volume->set_transformation(m); ModelVolume* vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) - // make a "hole" dipper - vol->set_scaling_factor(Z, 1.2 * vol->get_scaling_factor(Z)); + apply_tolerance(vol); else // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { // add one more solid part same as connector if this connector is a dowel - // But discard rotation and Z-offset for this volume - volume->set_rotation(Vec3d::Zero()); - Vec3d offset = volume->get_offset(); - offset[Z] = 0.0; - volume->set_offset(offset); - ModelVolume* vol = dowels->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + // Compute the displacement (in instance coordinates) to be applied to place the dowels local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); } @@ -1556,19 +1565,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // to the modifier volume transformation to preserve their shape properly. volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); + // #ysFIXME - add logic for the negative volumes/connectors if (attributes.has(ModelObjectCutAttribute::KeepUpper)) upper->add_volume(*volume); if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower->add_volume(*volume); } } - else if (!volume->mesh().empty() -// && !volume->source.is_connector // we don't allow to cut a connectors - ) { + else if (!volume->mesh().empty()) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); - mesh.transform(Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix, true); + mesh.transform(cut_matrix * instance_matrix* volume_matrix, true); volume->reset_mesh(); // Reset volume transformation except for offset @@ -1588,8 +1596,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const } if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { - if (!attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) - upper_mesh.transform(invert_cut_matrix); + upper_mesh.transform(invert_cut_matrix); ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; @@ -1600,10 +1607,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const vol->set_material(volume->material_id(), *volume->material()); } if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) - lower_mesh.transform(Geometry::assemble_transform(Vec3d::Zero(), Geometry::deg2rad(180.0)*Vec3d::UnitX())); - else - lower_mesh.transform(invert_cut_matrix); + lower_mesh.transform(invert_cut_matrix); ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; @@ -1643,7 +1647,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const obj_instance->set_transformation(Geometry::Transformation()); obj_instance->set_offset(offset + displace); - obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipUpper) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z)); + + Vec3d rotation = Vec3d::Zero(); + if (attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) { + Transform3d trafo = rotation_transform(cut_rotation).inverse(); + if (i != instance) + trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; + rotation = Transformation(trafo).get_rotation(); + } + else if (attributes.has(ModelObjectCutAttribute::FlipUpper)) { + rotation = rotate_z180; + if (i != instance) + rotation[Z] = rot_z; + } + else if (i != instance) + rotation[Z] = rot_z/* * Vec3d::UnitZ()*/; + obj_instance->set_rotation(rotation); } res.push_back(upper); @@ -1666,7 +1685,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const const double rot_z = obj_instance->get_rotation().z(); obj_instance->set_transformation(Geometry::Transformation()); obj_instance->set_offset(offset); - obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z)); + + Vec3d rotation = Vec3d::Zero(); + if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) { + Transform3d trafo = rotation_transform(rotate_z180) * rotation_transform(cut_rotation).inverse(); + if (i != instance) + trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; + rotation = Transformation(trafo).get_rotation(); + } + else if (attributes.has(ModelObjectCutAttribute::FlipLower)) { + rotation = rotate_z180; + if (i != instance) + rotation[Z] = rot_z; + } + else if (i != instance) + rotation[Z] = rot_z; + obj_instance->set_rotation(rotation); } res.push_back(lower); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 6afa2efe8..3242dd255 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -225,18 +225,20 @@ struct CutConnector Vec3d rotation; float radius; float height; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] bool failed = false; CutConnector() - : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f) + : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} - CutConnector(Vec3d p, Vec3d rot, float r, float h, bool fl = false) - : pos(p), rotation(rot), radius(r), height(h), failed(fl) + CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, bool fl = false) + : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), failed(fl) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.failed) {} + CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.failed) {} bool operator==(const CutConnector& sp) const; @@ -251,7 +253,7 @@ struct CutConnector */ template inline void serialize(Archive& ar) { - ar(pos, rotation, radius, height, failed); + ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, failed); } static constexpr size_t steps = 32; @@ -696,16 +698,25 @@ public: bool is_converted_from_meters{ false }; bool is_from_builtin_objects{ false }; - bool is_connector{ false }; - template void serialize(Archive& ar) { //FIXME Vojtech: Serialize / deserialize only if the Source is set. // likely testing input_file or object_idx would be sufficient. - ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects, is_connector); + ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects); } }; Source source; + // struct used by cut command + // It contains information about connetors + struct CutInfo + { + bool is_connector {false}; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] + + void discard() { is_connector = false; } + } cut_info; + // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 3b5af8cd9..23ee84a5c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2499,6 +2499,7 @@ void ObjectList::part_selection_changed() bool enable_manipulation {true}; bool disable_ss_manipulation {false}; + bool disable_ununiform_scale {false}; const auto item = GetSelection(); @@ -2543,6 +2544,7 @@ void ObjectList::part_selection_changed() disable_ss_manipulation = true; break; } + disable_ununiform_scale = !cut_objects.empty(); } } } @@ -2649,8 +2651,11 @@ void ObjectList::part_selection_changed() if (disable_ss_manipulation) wxGetApp().obj_manipul()->DisableScale(); - else + else { wxGetApp().obj_manipul()->Enable(enable_manipulation); + if (disable_ununiform_scale) + wxGetApp().obj_manipul()->DisableUnuniformScale(); + } } if (update_and_show_settings) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index b3f71687b..c89ee949a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -580,7 +580,11 @@ void ObjectManipulation::Enable(const bool enadle) { for (auto editor : m_editors) editor->Enable(enadle); - for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt }) + for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt +#if ENABLE_WORLD_COORDINATE + ,m_reset_skew_button +#endif // ENABLE_WORLD_COORDINATE + }) win->Enable(enadle); } @@ -588,10 +592,19 @@ void ObjectManipulation::DisableScale() { for (auto editor : m_editors) editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true); - for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_lock_bnt }) + for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_lock_bnt +#if ENABLE_WORLD_COORDINATE + ,m_reset_skew_button +#endif // ENABLE_WORLD_COORDINATE + }) win->Enable(false); } +void ObjectManipulation::DisableUnuniformScale() +{ + m_lock_bnt->disable(); +} + void ObjectManipulation::update_ui_from_settings() { if (m_imperial_units != (wxGetApp().app_config->get("use_inches") == "1")) { diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 936ac99d8..2a4b3e46a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -201,6 +201,7 @@ public: void Enable(const bool enadle = true); void Disable() { Enable(false); } void DisableScale(); + void DisableUnuniformScale(); void update_ui_from_settings(); bool use_colors() { return m_use_colors; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5fd0b28d4..417cf6037 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -430,12 +430,12 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i return old_val != value; } -bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double& value_in) +bool GLGizmoCut3D::render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in) { ImGui::AlignTextToFramePadding(); m_imgui->text(label); ImGui::SameLine(m_label_width); - ImGui::PushItemWidth(m_control_width); + ImGui::PushItemWidth(m_control_width * 0.85f); float value = (float)value_in; if (m_imperial_units) @@ -443,15 +443,25 @@ bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double& float old_val = value; const BoundingBoxf3 bbox = bounding_box(); - const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); - - m_imgui->slider_float(("##" + label).c_str(), &value, 1.0f, mean_size); - - ImGui::SameLine(); - m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); + float min_size = 1.f; + if (m_imperial_units) { + mean_size *= ObjectManipulation::mm_to_in; + min_size *= ObjectManipulation::mm_to_in; + } + std::string format = m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); + m_imgui->slider_float(("##" + label).c_str(), &value, min_size, mean_size, format.c_str()); value_in = (double)(value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0)); - return old_val != value; + + ImGui::SameLine(m_label_width + m_control_width + 3); + ImGui::PushItemWidth(m_control_width * 0.3f); + + float old_tolerance, tolerance = old_tolerance = (float)tolerance_in; + m_imgui->slider_float(("##tolerance_" + label).c_str(), &tolerance, 1.f, 20.f, "%.f %%", 1.f, true, _L("Tolerance")); + tolerance_in = (int)tolerance; + + return old_val != value || old_tolerance != tolerance; } void GLGizmoCut3D::render_move_center_input(int axis) @@ -1253,11 +1263,11 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_imgui->button(" " + _L("Reset") + " ##connectors")) reset_connectors(); m_imgui->disabled_end(); - +/* m_imgui->text(_L("Mode")); render_connect_mode_radio_button(CutConnectorMode::Auto); render_connect_mode_radio_button(CutConnectorMode::Manual); - +*/ m_imgui->text(_L("Type")); render_connect_type_radio_button(CutConnectorType::Plug); render_connect_type_radio_button(CutConnectorType::Dowel); @@ -1267,12 +1277,16 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) update_connector_shape(); - if (render_slicer_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) - for (auto& connector : connectors) + if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + for (auto& connector : connectors) { connector.height = float(m_connector_depth_ratio); - if (render_slicer_double_input(_u8L("Size"), m_connector_size)) - for (auto& connector : connectors) + connector.height_tolerance = 0.01f * m_connector_depth_ratio; + } + if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) + for (auto& connector : connectors) { connector.radius = float(m_connector_size * 0.5); + connector.radius_tolerance = 0.01f * m_connector_size_tolerance; + } m_imgui->disabled_end(); @@ -1710,7 +1724,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi //std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), + float(m_connector_size * 0.5), float(m_connector_depth_ratio), + float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance)); update_model_object(); m_selected.push_back(false); assert(m_selected.size() == connectors.size()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index b562396c1..91f8749e7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -81,6 +81,9 @@ class GLGizmoCut3D : public GLGizmoBase double m_connector_depth_ratio{ 3.0 }; double m_connector_size{ 2.5 }; + int m_connector_depth_ratio_tolerance{ 10 }; + int m_connector_size_tolerance{ 0 }; + float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; bool m_imperial_units{ false }; @@ -174,7 +177,7 @@ private: void set_center(const Vec3d& center); bool render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); bool render_double_input(const std::string& label, double& value_in); - bool render_slicer_double_input(const std::string& label, double& value_in); + bool render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in); void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label);