From 518cd4ce270b6a96514e80dcf798e61c3011f6fd Mon Sep 17 00:00:00 2001 From: "zhimin.zeng" Date: Mon, 20 Mar 2023 17:14:41 +0800 Subject: [PATCH] ENH: recover CutToParts for cut Change-Id: I5d7fb089c5d2ea9d36fce3d70ec4da896d601940 (cherry picked from commit e0232b21ebf5a37bedee1c213d1c21890634c563) --- src/libslic3r/Model.cpp | 305 +++++++++---------- src/libslic3r/Model.hpp | 11 +- src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp | 21 +- src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp | 1 + 4 files changed, 174 insertions(+), 164 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index a9df179dca..f1031ee126 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -916,6 +916,25 @@ bool Model::is_mm_painted() const return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); }); } + +static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART) +{ + if (mesh.empty()) + return; + + mesh.transform(cut_matrix); + ModelVolume* vol = object->add_volume(mesh); + vol->set_type(type); + + vol->name = src_volume->name + suffix; + // Don't copy the config's ID. + vol->config.assign_config(src_volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != src_volume->config.id()); + vol->set_material(src_volume->material_id(), *src_volume->material()); + vol->cut_info = src_volume->cut_info; +} + ModelObject::~ModelObject() { this->clear_volumes(); @@ -1706,7 +1725,9 @@ void ModelObject::synchronize_model_after_cut() void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) { // we don't save cut information, if result will not contains all parts of initial object - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower)) + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || + !attributes.has(ModelObjectCutAttribute::KeepLower) || + attributes.has(ModelObjectCutAttribute::InvalidateCutInfo)) return; if (cut_id.id().invalid()) @@ -1768,6 +1789,8 @@ Transform3d ModelObject::calculate_cut_plane_inverse_matrix(const std::array &dowels, @@ -1780,38 +1803,53 @@ void ModelObject::process_connector_cut( // ! Don't apply instance transformation for the conntectors. // This transformation is already there - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume *vol = upper->add_volume(*volume); - vol->set_transformation(volume_matrix); - vol->apply_tolerance(); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - ModelVolume *vol = lower->add_volume(*volume); - vol->set_transformation(volume_matrix); - - if (volume->cut_info.connector_type == CutConnectorType::Dowel) + if (volume->cut_info.connector_type != CutConnectorType::Dowel) { + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume *vol = upper->add_volume(*volume); + vol->set_transformation(volume_matrix); vol->apply_tolerance(); - else + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume *vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); // 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 (volume->cut_info.connector_type == CutConnectorType::Dowel && attributes.has(ModelObjectCutAttribute::CreateDowels)) { - ModelObject *dowel{nullptr}; - // Clone the object to duplicate instances, materials etc. - clone_for_cut(&dowel); + else { + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject *dowel{nullptr}; + // Clone the object to duplicate instances, materials etc. + clone_for_cut(&dowel); - // add one more solid part same as connector if this connector is a dowel - ModelVolume *vol = dowel->add_volume(*volume); - vol->set_type(ModelVolumeType::MODEL_PART); + // add one more solid part same as connector if this connector is a dowel + ModelVolume *vol = dowel->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); + // 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)); + // 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)); - dowels.push_back(dowel); + dowels.push_back(dowel); + } + + // Cut the dowel + volume->apply_tolerance(); + + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // add small Z offset to better preview + upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast()); + lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast()); + + // Add cut parts to the related objects + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); + add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); } } @@ -1829,6 +1867,11 @@ void ModelObject::process_modifier_cut( // to the modifier volume transformation to preserve their shape properly. volume->set_transformation(Geometry::Transformation(volume_matrix)); + if (attributes.has(ModelObjectCutAttribute::CutToParts)) { + upper->add_volume(*volume); + return; + } + // Some logic for the negative volumes/connectors. Add only needed modifiers auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; @@ -1838,52 +1881,59 @@ void ModelObject::process_modifier_cut( lower->add_volume(*volume); } +void ModelObject::process_volume_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & cut_matrix, + ModelObjectCutAttributes attributes, + TriangleMesh & upper_mesh, + TriangleMesh & lower_mesh) +{ + const auto volume_matrix = volume->get_matrix(); + + using namespace Geometry; + + const Geometry::Transformation cut_transformation = Geometry::Transformation(cut_matrix); + const Transform3d invert_cut_matrix = cut_transformation.get_matrix(true, false, true, true).inverse() + * translation_transform(-1 * cut_transformation.get_offset()); + + // 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(invert_cut_matrix * instance_matrix * volume_matrix, true); + + indexed_triangle_set upper_its, lower_its; + cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper_mesh = TriangleMesh(upper_its); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower_mesh = TriangleMesh(lower_its); +} + void ModelObject::process_solid_part_cut(ModelVolume * volume, const Transform3d & instance_matrix, + const Transform3d & cut_matrix, const std::array &plane_points, ModelObjectCutAttributes attributes, ModelObject * upper, ModelObject * lower, Vec3d & local_displace) { - // 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(instance_matrix * volume->get_matrix(), true); - volume->reset_mesh(); - // Reset volume transformation except for offset - const Vec3d offset = volume->get_offset(); - volume->set_transformation(Geometry::Transformation()); - volume->set_offset(offset); - // Perform cut TriangleMesh upper_mesh, lower_mesh; - { - indexed_triangle_set upper_its, lower_its; - cut_mesh(mesh.its, plane_points, &upper_its, &lower_its); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) upper_mesh = TriangleMesh(upper_its); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower_mesh = TriangleMesh(lower_its); + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // Add required cut parts to the objects + if (attributes.has(ModelObjectCutAttribute::CutToParts)) { + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); + add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + return; } - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { - ModelVolume *vol = upper->add_volume(upper_mesh); - vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_upper"; // BBS - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - vol->cut_info = volume->cut_info; - } + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + add_cut_volume(upper_mesh, upper, volume, cut_matrix); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - ModelVolume *vol = lower->add_volume(lower_mesh); - vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_lower"; // BBS - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - vol->cut_info = volume->cut_info; + add_cut_volume(lower_mesh, lower, volume, cut_matrix); // Compute the displacement (in instance coordinates) to be applied to place the upper parts // The upper part displacement is set to half of the lower part bounding box @@ -1957,29 +2007,13 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi // apply cut attributes for object apply_cut_attributes(attributes); - // Clone the object to duplicate instances, materials etc. - bool keep_upper = attributes.has(ModelObjectCutAttribute::KeepUpper); - bool keep_lower = attributes.has(ModelObjectCutAttribute::KeepLower); - ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr; - ModelObject* lower = keep_lower ? ModelObject::new_clone(*this) : nullptr; + ModelObject* upper{ nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + clone_for_cut(&upper); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - upper->set_model(nullptr); - upper->sla_support_points.clear(); - upper->sla_drain_holes.clear(); - upper->sla_points_status = sla::PointsStatus::NoPoints; - upper->clear_volumes(); - upper->input_file.clear(); - } - - if (keep_lower && lower != upper) { - lower->set_model(nullptr); - lower->sla_support_points.clear(); - lower->sla_drain_holes.clear(); - lower->sla_points_status = sla::PointsStatus::NoPoints; - lower->clear_volumes(); - lower->input_file.clear(); - } + ModelObject* lower{ nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::CutToParts)) + clone_for_cut(&lower); // Because transformations are going to be applied to meshes directly, // we reset transformation of all instances and volumes, @@ -1999,12 +2033,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi for (Vec3d& point : plane_points) { point -= instances[instance]->get_offset(); } + Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); + Transform3d cut_matrix = inverse_cut_matrix.inverse(); std::vector dowels; // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); Vec3d local_dowels_displace = Vec3d::Zero(); - + for (ModelVolume *volume : volumes) { const auto volume_matrix = volume->get_matrix(); @@ -2016,96 +2052,52 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi if (volume->cut_info.is_processed) { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. - Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); + //Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); } else { - process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace); + process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace); } } else if (! volume->mesh().empty()) { - process_solid_part_cut(volume, instance_matrix, plane_points, attributes, upper, lower, local_displace); + process_solid_part_cut(volume, instance_matrix, cut_matrix, plane_points, attributes, upper, lower, local_displace); } } ModelObjectPtrs res; - - Transform3d cut_matrix = calculate_cut_plane_inverse_matrix(plane_points).inverse(); - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - invalidate_translations(upper, instances[instance]); - - reset_instance_transformation(upper, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - attributes.has(ModelObjectCutAttribute::FlipUpper), - local_displace); - + + if (attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) { + reset_instance_transformation(upper, instance, cut_matrix); res.push_back(upper); } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - invalidate_translations(lower, instances[instance]); + else { + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { + invalidate_translations(upper, instances[instance]); - reset_instance_transformation(lower, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); + reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace); - res.push_back(lower); - } + res.push_back(upper); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { + invalidate_translations(lower, instances[instance]); - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { - auto invalidate_translations = [](ModelObject *object, const ModelInstance *src_instance) { - if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) { - object->center_around_origin(); - object->translate_instances(-object->origin_translation); - object->origin_translation = Vec3d::Zero(); - } else { - object->invalidate_bounding_box(); - object->center_around_origin(); + reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); + + res.push_back(lower); + } + + if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { + for (auto dowel : dowels) { + invalidate_translations(dowel, instances[instance]); + + reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); + + local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + res.push_back(dowel); } - }; - - auto reset_instance_transformation = [](ModelObject *object, size_t src_instance_idx, const Transform3d &cut_matrix, - bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero()) { - using namespace Geometry; - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < object->instances.size(); ++i) { - auto & obj_instance = object->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - const double rot_z = obj_instance->get_rotation().z(); - - obj_instance->set_transformation(Transformation()); - - const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() : rotation_transform(obj_instance->get_rotation()) * local_displace; - obj_instance->set_offset(offset + displace); - - Vec3d rotation = Vec3d::Zero(); - if (!flip && !place_on_cut) { - if (i != src_instance_idx) rotation[Z] = rot_z; - } else { - Transform3d rotation_matrix = Transform3d::Identity(); - if (flip) - rotation_matrix = rotation_transform(PI * Vec3d::UnitX()); - - if (place_on_cut) - rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_matrix(true, false, true, true).inverse(); - - if (i != src_instance_idx) - rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix; - - rotation = Transformation(rotation_matrix).get_rotation(); - } - - obj_instance->set_rotation(rotation); - } - }; - - for (auto dowel : dowels) { - invalidate_translations(dowel, instances[instance]); - - reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); - - local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); - dowel->name += "-Dowel-" + dowel->volumes[0]->name; - res.push_back(dowel); } } @@ -2552,7 +2544,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume // %print_volume.min.x() %print_volume.min.y() %print_volume.min.z()%print_volume.max.x() %print_volume.max.y() %print_volume.max.z(); for (ModelInstance* model_instance : this->instances) { unsigned int inside_outside = 0; - for (const ModelVolume* vol : this->volumes) + for (const ModelVolume *vol : this->volumes) { if (vol->is_model_part()) { //BBS: add bounding box empty check logic, for some volume is empty before split(it will be removed after split to object) BoundingBoxf3 bb = vol->get_convex_hull().bounding_box(); @@ -2578,6 +2570,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume // Volume colliding with the build volume. inside_outside |= INSIDE | OUTSIDE; } + } model_instance->print_volume_state = inside_outside == (INSIDE | OUTSIDE) ? ModelInstancePVS_Partly_Outside : inside_outside == INSIDE ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 9188017012..ede417fe4e 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -316,7 +316,7 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels }; +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, CutToParts, InvalidateCutInfo }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); @@ -475,6 +475,8 @@ public: void clone_for_cut(ModelObject **obj); Transform3d calculate_cut_plane_inverse_matrix(const std::array &plane_points); void process_connector_cut(ModelVolume *volume, + const Transform3d & instance_matrix, + const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject *upper, ModelObject *lower, std::vector &dowels, @@ -485,8 +487,15 @@ public: ModelObjectCutAttributes attributes, ModelObject * upper, ModelObject * lower); + void process_volume_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & cut_matrix, + ModelObjectCutAttributes attributes, + TriangleMesh & upper_mesh, + TriangleMesh & lower_mesh); void process_solid_part_cut(ModelVolume * volume, const Transform3d & instance_matrix, + const Transform3d & cut_matrix, const std::array &plane_points, ModelObjectCutAttributes attributes, ModelObject * upper, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp index ef33bd5d36..295d81a851 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp @@ -113,6 +113,7 @@ GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& ic , m_last_active_id(0) , m_keep_upper(true) , m_keep_lower(true) + , m_cut_to_parts(false) , m_do_segment(false) , m_segment_smoothing_alpha(0.5) , m_segment_number(5) @@ -350,6 +351,7 @@ void GLGizmoAdvancedCut::reset_all() m_keep_upper = true; m_keep_lower = true; + m_cut_to_parts = false; m_place_on_cut_upper = true; m_place_on_cut_lower = false; m_rotate_upper = false; @@ -653,11 +655,13 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection) wxGetApp().plater()->cut(object_idx, instance_idx, get_plane_points_world_coord(), only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_cut_to_parts, ModelObjectCutAttribute::CutToParts) | only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | - only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) | + only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo)); } } } @@ -1393,7 +1397,7 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b ImGui::PopStyleVar(1); m_imgui->disabled_end(); - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_cut_to_parts); if (m_imgui->button(_L("Add/Edit connectors"))) set_connectors_editing(true); m_imgui->disabled_end(); @@ -1406,22 +1410,22 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b label_width = width; } - auto render_part_action_line = [this, label_width](const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { - CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; - + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + const bool has_connectors = !connectors.empty(); + auto render_part_action_line = [this, label_width, &connectors](const wxString &label, const wxString &suffix, bool &keep_part, bool &place_on_cut_part, bool &rotate_part) { bool keep = true; ImGui::AlignTextToFramePadding(); m_imgui->text(label); ImGui::SameLine(label_width); - m_imgui->disabled_begin(!connectors.empty()); + m_imgui->disabled_begin(!connectors.empty() || m_cut_to_parts); m_imgui->bbl_checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); m_imgui->disabled_end(); ImGui::SameLine(); - m_imgui->disabled_begin(!keep_part); + m_imgui->disabled_begin(!keep_part || m_cut_to_parts); if (m_imgui->bbl_checkbox(_L("Place on cut") + suffix, place_on_cut_part)) rotate_part = false; ImGui::SameLine(); @@ -1434,6 +1438,9 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b render_part_action_line( _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); render_part_action_line( _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); + m_imgui->disabled_begin(has_connectors); + m_imgui->bbl_checkbox(_L("Cut to parts"), m_cut_to_parts); + m_imgui->disabled_end(); #if 0 // Auto segment input diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp index 45fe4419a1..a2ea836e0e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp @@ -48,6 +48,7 @@ private: bool m_keep_upper; bool m_keep_lower; + bool m_cut_to_parts; bool m_place_on_cut_upper{true}; bool m_place_on_cut_lower{false}; bool m_rotate_upper{false};