From 1b451cdf9f8859aff58df54fa89d689aa47518d7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 9 Aug 2023 13:22:11 +0200 Subject: [PATCH] CutGizmo: Big code refactoring. All manipulations related to cut are extracted to CutUtils now --- src/PrusaSlicer.cpp | 6 +- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/CutUtils.cpp | 627 +++++++++++++++++++++++++++ src/libslic3r/CutUtils.hpp | 65 +++ src/libslic3r/Model.cpp | 321 -------------- src/libslic3r/Model.hpp | 20 - src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 424 ++++-------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 16 +- src/slic3r/GUI/Plater.cpp | 15 +- src/slic3r/GUI/Plater.hpp | 3 +- 10 files changed, 781 insertions(+), 718 deletions(-) create mode 100644 src/libslic3r/CutUtils.cpp create mode 100644 src/libslic3r/CutUtils.hpp diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index e600f343ca..480f42811e 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -40,6 +40,7 @@ #include "libslic3r/Geometry.hpp" #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/CutUtils.hpp" #include "libslic3r/ModelArrange.hpp" #include "libslic3r/Platform.hpp" #include "libslic3r/Print.hpp" @@ -437,8 +438,11 @@ int CLI::run(int argc, char **argv) } #else // model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower); - model.objects.front()->cut(0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()), + Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper); + auto cut_objects = cut.perform_with_plane(); + for (ModelObject* obj : cut_objects) + model.add_object(*obj); #endif model.delete_object(size_t(0)); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..a31e4cc7c8 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -201,6 +201,8 @@ set(SLIC3R_SOURCES BlacklistedLibraryCheck.hpp LocalesUtils.cpp LocalesUtils.hpp + CutUtils.cpp + CutUtils.hpp Model.cpp Model.hpp ModelArrange.hpp diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp new file mode 100644 index 0000000000..7d107b1d05 --- /dev/null +++ b/src/libslic3r/CutUtils.cpp @@ -0,0 +1,627 @@ + +#include "CutUtils.hpp" +#include "libslic3r.h" +#include "Model.hpp" +#include "TriangleMeshSlicer.hpp" +#include "TriangleSelector.hpp" + + +namespace Slic3r { + +using namespace Geometry; + +static void apply_tolerance(ModelVolume* vol) +{ + ModelVolume::CutInfo& cut_info = vol->cut_info; + + assert(cut_info.is_connector); + if (!cut_info.is_processed) + return; + + Vec3d sf = vol->get_scaling_factor(); + + // make a "hole" wider + sf[X] += double(cut_info.radius_tolerance); + sf[Y] += double(cut_info.radius_tolerance); + + // make a "hole" dipper + sf[Z] += double(cut_info.height_tolerance); + + vol->set_scaling_factor(sf); + + // correct offset in respect to the new depth + Vec3d rot_norm = rotation_transform(vol->get_rotation()) * Vec3d::UnitZ(); + if (rot_norm.norm() != 0.0) + rot_norm.normalize(); + + double z_offset = 0.5 * static_cast(cut_info.height_tolerance); + if (cut_info.connector_type == CutConnectorType::Plug || + cut_info.connector_type == CutConnectorType::Snap) + z_offset -= 0.05; // add small Z offset to better preview + + vol->set_offset(vol->get_offset() + rot_norm * z_offset); +} + +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; +} + +static void 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(); + + const Transformation cut_transformation = Transformation(cut_matrix); + const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().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); +} + +static void process_connector_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels) +{ + assert(volume->cut_info.is_connector); + volume->cut_info.set_processed(); + + const auto volume_matrix = volume->get_matrix(); + + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there + if (volume->cut_info.connector_type != CutConnectorType::Dowel) { + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume* vol = nullptr; + if (volume->cut_info.connector_type == CutConnectorType::Snap) { + TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); + + vol = upper->add_volume(std::move(mesh)); + vol->set_transformation(volume->get_transformation()); + vol->set_type(ModelVolumeType::NEGATIVE_VOLUME); + + vol->cut_info = volume->cut_info; + vol->name = volume->name; + } + else + vol = upper->add_volume(*volume); + + vol->set_transformation(volume_matrix); + apply_tolerance(vol); + } + 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); + } + } + else { + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject* dowel{ nullptr }; + // Clone the object to duplicate instances, materials etc. + volume->get_object()->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); + + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + + dowels.push_back(dowel); + } + + // Cut the dowel + apply_tolerance(volume); + + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, Transform3d::Identity(), 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()); + } +} + +static void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + const auto volume_matrix = instance_matrix * volume->get_matrix(); + + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + volume->set_transformation(Transformation(volume_matrix)); + + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + 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; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) + lower->add_volume(*volume); +} + +static void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // Add required cut parts to the objects + + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); + add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + upper->volumes.back()->cut_info.is_from_upper = false; + return; + } + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + add_cut_volume(upper_mesh, upper, volume, cut_matrix); + + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) + add_cut_volume(lower_mesh, lower, volume, cut_matrix); +} + +static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, + const Transform3d& cut_matrix = Transform3d::Identity(), + bool place_on_cut = false, bool flip = false) +{ + // 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 double rot_z = obj_instance->get_rotation().z(); + + obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor())); + + 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_rotation_matrix().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); + } +} + + +Cut::Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes/*= ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts*/) + : m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes) +{ + m_model = Model(); + if (object) + m_model.add_object(*object); +} + +const ModelObjectPtrs& Cut::perform_with_plane() +{ + if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) { + m_model.clear_objects(); + return m_model.objects; + } + + ModelObject* mo = m_model.objects.front(); + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) + mo->clone_for_cut(&upper); + + ModelObject* lower{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !m_attributes.has(ModelObjectCutAttribute::KeepAsParts)) + mo->clone_for_cut(&lower); + + std::vector dowels; + + // 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 = mo->instances[m_instance]->get_transformation().get_matrix_no_offset(); + const Transformation cut_transformation = Transformation(m_cut_matrix); + const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset()); + + for (ModelVolume* volume : mo->volumes) { + volume->reset_extra_facets(); + + if (!volume->is_model_part()) { + if (volume->cut_info.is_processed) + process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, m_attributes, upper, lower); + else + process_connector_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower, dowels); + } + else if (!volume->mesh().empty()) + process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower); + } + + // Post-process cut parts + + if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) { + m_model = Model(); + m_model.objects.push_back(upper); + return m_model.objects; + } + + ModelObjectPtrs cut_object_ptrs; + + if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { + reset_instance_transformation(upper, m_instance, m_cut_matrix); + cut_object_ptrs.push_back(upper); + } + else { + // Delete all modifiers which are not intersecting with solid parts bounding box + auto delete_extra_modifiers = [this](ModelObject* mo) { + if (!mo) return; + const BoundingBoxf3 obj_bb = mo->instance_bounding_box(m_instance); + const Transform3d inst_matrix = mo->instances[m_instance]->get_transformation().get_matrix(); + + for (int i = int(mo->volumes.size()) - 1; i >= 0; --i) + if (const ModelVolume* vol = mo->volumes[i]; + !vol->is_model_part() && !vol->is_cut_connector()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + if (!obj_bb.intersects(bb)) + mo->delete_volume(i); + } + }; + + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { + delete_extra_modifiers(upper); + reset_instance_transformation(upper, m_instance, m_cut_matrix, + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(upper); + } + + if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { + delete_extra_modifiers(lower); + reset_instance_transformation(lower, m_instance, m_cut_matrix, + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); + cut_object_ptrs.push_back(lower); + } + + if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { + for (auto dowel : dowels) { + reset_instance_transformation(dowel, m_instance, Transform3d::Identity()); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + cut_object_ptrs.push_back(dowel); + } + } + } + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + + m_model.clear_objects(); + m_model.objects = cut_object_ptrs; + + return m_model.objects; +} + +static void distribute_modifiers_from_object(ModelObject* from_obj, const int instance_idx, ModelObject* to_obj1, ModelObject* to_obj2) +{ + auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3(); + auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3(); + const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix(); + + for (ModelVolume* vol : from_obj->volumes) + if (!vol->is_model_part()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + // Don't add modifiers which are not intersecting with solid parts + if (obj1_bb.intersects(bb)) + to_obj1->add_volume(*vol); + if (obj2_bb.intersects(bb)) + to_obj2->add_volume(*vol); + } +} + +static void merge_solid_parts_inside_object(ModelObjectPtrs& objects) +{ + for (ModelObject* mo : objects) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } + } + } +} + + +const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowels_count) +{ + ModelObject* cut_mo = m_model.objects.front(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower); + + auto add_cut_objects = [this](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { + if (upper && !upper->volumes.empty()) { + reset_instance_transformation(upper, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_objects.push_back(upper); + } + if (lower && !lower->volumes.empty()) { + reset_instance_transformation(lower, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); + cut_objects.push_back(lower); + } + }; + + const size_t cut_parts_cnt = parts.size(); + bool has_modifiers = false; + + // Distribute SolidParts to the Upper/Lower object + for (size_t id = 0; id < cut_parts_cnt; ++id) { + if (parts[id].is_modifier) + has_modifiers = true; // modifiers will be added later to the related parts + else if (ModelObject* obj = (parts[id].selected ? upper : lower)) + obj->add_volume(*(cut_mo->volumes[id])); + } + + if (has_modifiers) { + // Distribute Modifiers to the Upper/Lower object + distribute_modifiers_from_object(cut_mo, m_instance, upper, lower); + } + + ModelObjectPtrs cut_object_ptrs; + + ModelVolumePtrs& volumes = cut_mo->volumes; + if (volumes.size() == cut_parts_cnt) { + // Means that object is cut without connectors + + // Just add Upper and Lower objects to cut_object_ptrs + add_cut_objects(cut_object_ptrs, upper, lower); + } + else if (volumes.size() > cut_parts_cnt) { + // Means that object is cut with connectors + + // All volumes are distributed to Upper / Lower object, + // So we don’t need them anymore + for (size_t id = 0; id < cut_parts_cnt; id++) + delete* (volumes.begin() + id); + volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); + + // Perform cut just to get connectors + Cut cut(cut_mo, m_instance, m_cut_matrix, m_attributes); + const ModelObjectPtrs& cut_connectors_obj = cut.perform_with_plane(); + assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); + + // Connectors from upper object + for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) + upper->add_volume(*volume, volume->type()); + + // Connectors from lower object + for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) + lower->add_volume(*volume, volume->type()); + + // Add Upper and Lower objects to cut_object_ptrs + add_cut_objects(cut_object_ptrs, upper, lower); + + // Add Dowel-connectors as separate objects to cut_object_ptrs + if (cut_connectors_obj.size() >= 3) + for (size_t id = 2; id < cut_connectors_obj.size(); id++) + cut_object_ptrs.push_back(cut_connectors_obj[id]); + } + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + + m_model.clear_objects(); + m_model.objects = cut_object_ptrs; + + return m_model.objects; +} + + +const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts/* = false*/) +{ + ModelObject* cut_mo = m_model.objects.front(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + cut_mo->clone_for_cut(&lower); + + const double groove_half_depth = 0.5 * double(groove.depth); + + Model tmp_model_for_cut = Model(); + + Model tmp_model = Model(); + tmp_model.add_object(*cut_mo); + ModelObject* tmp_object = tmp_model.objects.front(); + + auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { + const auto& volumes = tmp_model_for_cut.objects.front()->volumes; + for (const ModelVolume* volume : volumes) + if (volume->is_model_part()) { + if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || + (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper())) { + ModelVolume* new_vol = object->add_volume(*volume); + new_vol->reset_from_upper(); + } + } + }; + + auto cut = [this, add_volumes_from_cut] + (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) { + Cut cut(object, m_instance, cut_matrix); + + tmp_model_for_cut = Model(); + tmp_model_for_cut.add_object(*cut.perform_with_plane().front()); + assert(!tmp_model_for_cut.objects.empty()); + + object->clear_volumes(); + add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut); + reset_instance_transformation(object, m_instance); + }; + + // cut by upper plane + + const Transform3d cut_matrix_upper = translation_transform(rotation_m * (groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix; + { + cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // cut by lower plane + + const Transform3d cut_matrix_lower = translation_transform(rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix; + { + cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + } + + // cut middle part with 2 angles and add parts to related upper/lower objects + + const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle)); + + // cut by angle1 plane + { + const Transform3d cut_matrix_angle1 = translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + + cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // cut by angle2 plane + { + const Transform3d cut_matrix_angle2 = translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + + cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // apply tolerance to the middle part + { + const double h_groove_shift_tolerance = groove_half_depth - (double)groove.depth_tolerance; + + const Transform3d cut_matrix_lower_tolerance = translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * m_cut_matrix; + cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + + const double h_side_shift_tolerance = h_side_shift - 0.5 * double(groove.width_tolerance); + + const Transform3d cut_matrix_angle1_tolerance = translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + const Transform3d cut_matrix_angle2_tolerance = translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // this part can be added to the upper object now + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + ModelObjectPtrs cut_object_ptrs; + + if (keep_as_parts) { + // add volumes from lower object to the upper, but mark them as a lower + const auto& volumes = lower->volumes; + for (const ModelVolume* volume : volumes) { + ModelVolume* new_vol = upper->add_volume(*volume); + new_vol->cut_info.is_from_upper = false; + } + + // add modifiers + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) + upper->add_volume(*volume); + + cut_object_ptrs.push_back(upper); + + // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks + cut_object_ptrs.push_back(lower); + } + else { + // add modifiers if object has any + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) { + distribute_modifiers_from_object(cut_mo, m_instance, upper, lower); + break; + } + + assert(!upper->volumes.empty() && !lower->volumes.empty()); + + reset_instance_transformation(upper, m_instance, cut_matrix_upper, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(upper); + reset_instance_transformation(lower, m_instance, cut_matrix_lower, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(lower); + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + } + + m_model.clear_objects(); + m_model.objects = cut_object_ptrs; + + return m_model.objects; +} + +} // namespace Slic3r + diff --git a/src/libslic3r/CutUtils.hpp b/src/libslic3r/CutUtils.hpp new file mode 100644 index 0000000000..a31404e898 --- /dev/null +++ b/src/libslic3r/CutUtils.hpp @@ -0,0 +1,65 @@ +#ifndef slic3r_CutUtils_hpp_ +#define slic3r_CutUtils_hpp_ + +#include "libslic3r.h" +#include "enum_bitmask.hpp" +#include "Geometry.hpp" +#include "ObjectID.hpp" +#include "Point.hpp" +#include "Model.hpp" + +#include + +namespace Slic3r { + +using ModelObjectPtrs = std::vector; + +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo }; +using ModelObjectCutAttributes = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); + + +class Cut { + + Model m_model; + int m_instance; + const Transform3d& m_cut_matrix; + ModelObjectCutAttributes m_attributes; + +public: + + Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts ); + ~Cut() { m_model.clear_objects(); } + + struct Groove + { + float depth{ 0.f }; + float width{ 0.f }; + float flaps_angle{ 0.f }; + float angle{ 0.f }; + float depth_init{ 0.f }; + float width_init{ 0.f }; + float flaps_angle_init{ 0.f }; + float angle_init{ 0.f }; + float depth_tolerance{ 0.1f }; + float width_tolerance{ 0.1f }; + }; + + struct Part + { + bool selected; + bool is_modifier; + }; + + const ModelObjectPtrs& perform_with_plane(); + const ModelObjectPtrs& perform_by_contour(std::vector parts, int dowels_count); + const ModelObjectPtrs& perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts = false); + +}; // namespace Cut + +} // namespace Slic3r + +#endif /* slic3r_CutUtils_hpp_ */ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index cfe76f8622..2a8c78729b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1332,327 +1332,6 @@ void ModelVolume::reset_extra_facets() this->mmu_segmentation_facets.reset(); } -void ModelVolume::apply_tolerance() -{ - assert(cut_info.is_connector); - if (!cut_info.is_processed) - return; - - Vec3d sf = get_scaling_factor(); - - // make a "hole" wider - sf[X] += double(cut_info.radius_tolerance); - sf[Y] += double(cut_info.radius_tolerance); - - // make a "hole" dipper - sf[Z] += double(cut_info.height_tolerance); - - set_scaling_factor(sf); - - // correct offset in respect to the new depth - Vec3d rot_norm = Geometry::rotation_transform(get_rotation()) * Vec3d::UnitZ(); - if (rot_norm.norm() != 0.0) - rot_norm.normalize(); - - double z_offset = 0.5 * static_cast(cut_info.height_tolerance); - if (cut_info.connector_type == CutConnectorType::Plug || - cut_info.connector_type == CutConnectorType::Snap) - z_offset -= 0.05; // add small Z offset to better preview - - set_offset(get_offset() + rot_norm * z_offset); -} - -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; -} - -void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels) -{ - assert(volume->cut_info.is_connector); - volume->cut_info.set_processed(); - - const auto volume_matrix = volume->get_matrix(); - - // ! Don't apply instance transformation for the conntectors. - // This transformation is already there - if (volume->cut_info.connector_type != CutConnectorType::Dowel) { - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume* vol = nullptr; - if (volume->cut_info.connector_type == CutConnectorType::Snap) { - TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); - - vol = upper->add_volume(std::move(mesh)); - vol->set_transformation(volume->get_transformation()); - vol->set_type(ModelVolumeType::NEGATIVE_VOLUME); - - vol->cut_info = volume->cut_info; - vol->name = volume->name; - } - else - 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); - // 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); - } - } - 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); - - // But discard rotation and Z-offset for this volume - vol->set_rotation(Vec3d::Zero()); - vol->set_offset(Z, 0.0); - - dowels.push_back(dowel); - } - - // Cut the dowel - volume->apply_tolerance(); - - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - process_volume_cut(volume, Transform3d::Identity(), 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()); - } -} - -void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) -{ - const auto volume_matrix = instance_matrix * volume->get_matrix(); - - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - volume->set_transformation(Geometry::Transformation(volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { - 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; - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_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 Transformation cut_transformation = Transformation(cut_matrix); - const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().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, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) -{ - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); - - // Add required cut parts to the objects - - if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { - add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); - add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); - upper->volumes.back()->cut_info.is_from_upper = false; - return; - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - add_cut_volume(upper_mesh, upper, volume, cut_matrix); - - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) - add_cut_volume(lower_mesh, lower, volume, cut_matrix); -} - -void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, - bool place_on_cut/* = false*/, bool flip/* = false*/) -{ - 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 double rot_z = obj_instance->get_rotation().z(); - - obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor())); - - 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_rotation_matrix().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); - } -} - -ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) -{ - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) - return {}; - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - clone_for_cut(&upper); - - ModelObject* lower{ nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::KeepAsParts)) - clone_for_cut(&lower); - - std::vector dowels; - - 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 = instances[instance]->get_transformation().get_matrix_no_offset(); - const Transformation cut_transformation = Transformation(cut_matrix); - const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset()); - - for (ModelVolume* volume : volumes) { - volume->reset_extra_facets(); - - if (!volume->is_model_part()) { - if (volume->cut_info.is_processed) - process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); - else - process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels); - } - else if (!volume->mesh().empty()) - process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower); - } - - // Post-process cut parts - - ModelObjectPtrs res; - if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) - return res; - - if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { - reset_instance_transformation(upper, instance, cut_matrix); - res.push_back(upper); - } - else { - // Delete all modifiers which are not intersecting with solid parts bounding box - auto delete_extra_modifiers = [instance](ModelObject* mo) { - if (!mo) return; - const BoundingBoxf3 obj_bb = mo->instance_bounding_box(instance); - const Transform3d inst_matrix = mo->instances[instance]->get_transformation().get_matrix(); - - for (int i = int(mo->volumes.size()) - 1; i >= 0; --i) - if (const ModelVolume* vol = mo->volumes[i]; - !vol->is_model_part() && !vol->is_cut_connector()) { - auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); - if (!obj_bb.intersects(bb)) - mo->delete_volume(i); - } - }; - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { - delete_extra_modifiers(upper); - reset_instance_transformation(upper, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - attributes.has(ModelObjectCutAttribute::FlipUpper)); - res.push_back(upper); - } - - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { - delete_extra_modifiers(lower); - reset_instance_transformation(lower, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower)); - res.push_back(lower); - } - - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { - for (auto dowel : dowels) { - reset_instance_transformation(dowel, instance, Transform3d::Identity()); - dowel->name += "-Dowel-" + dowel->volumes[0]->name; - res.push_back(dowel); - } - } - } - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; - - return res; -} /// /// Compare TriangleMeshes by Bounding boxes (mainly for sort) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a14bf37705..221033c521 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -317,10 +317,6 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo }; -using ModelObjectCutAttributes = enum_bitmask; -ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); - // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), // and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. // Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, @@ -468,21 +464,6 @@ public: void delete_connectors(); void clone_for_cut(ModelObject **obj); -private: - void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels); - void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, - 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, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); -public: - static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix = Transform3d::Identity(), - bool place_on_cut = false, bool flip = false); - - ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs*new_objects); void merge(); // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, @@ -850,7 +831,6 @@ public: bool is_the_only_one_part() const; // behave like an object t_model_material_id material_id() const { return m_material_id; } void reset_extra_facets(); - void apply_tolerance(); void set_material_id(t_model_material_id material_id); ModelMaterial* material() const; void set_material(t_model_material_id material_id, const ModelMaterial &material); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 55d57f7178..3dd4fe6edd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -675,7 +675,7 @@ static double get_grabber_mean_size(const BoundingBoxf3& bb) indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() { - const float ghw = 0.5f * m_groove_width; // groove half width + const float ghw = 0.5f * m_groove.width; // groove half width const float cpr = 1.5f * float(m_radius); // cut plane radius const float cpl = 1.5f * cpr; // cut plane length @@ -684,7 +684,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() // We need two upward facing triangles float x = 0.5f * cpr, y = 0.5f * cpl; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); const float extension_x = ghw + proj; // upper cut plane is simple @@ -712,7 +712,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() if (ghw < proj) { - const float cross_pt_y = ghw / tan(m_groove_angle); + const float cross_pt_y = ghw / tan(m_groove.angle); return { { // roof @@ -756,7 +756,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width) { - const float ghw = 0.5f * (m_groove_width + flaps_width); // groove half width + const float ghw = 0.5f * (m_groove.width + flaps_width); // groove half width const float cpr = 1.5f * float(m_radius); // cut plane radius const float cpl = 1.5f * cpr; // cut plane length @@ -765,7 +765,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width // We need two upward facing triangles float x = 0.5f * cpr, y = 0.5f * cpl; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); const float extension_x = ghw + proj; // upper cut plane is trapezium @@ -793,7 +793,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width // upper cut plane is triangle - const float cross_pt_y = ghw / tan(m_groove_angle); + const float cross_pt_y = ghw / tan(m_groove.angle); return { { // roof @@ -814,14 +814,14 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width) { - const float ghw_upper = 0.5f * (m_groove_width); // groove half width - const float ghw_lower = 0.5f * (m_groove_width + flaps_width); // groove half width + const float ghw_upper = 0.5f * (m_groove.width); // groove half width + const float ghw_lower = 0.5f * (m_groove.width + flaps_width); // groove half width const float cpr = 1.5f * float(m_radius); // cut plane radius const float cpl = 1.5f * cpr; // cut plane length const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height - const float ghd = 0.5f * m_groove_depth; // groove half depth + const float ghd = 0.5f * m_groove.depth; // groove half depth // We need two upward facing triangles @@ -829,7 +829,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width float z_upper = ghd; float z_lower = -ghd; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); const float extension_upper_x = ghw_upper + proj; const float extension_lower_x = ghw_lower + proj; @@ -854,12 +854,12 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width }; } - const float cross_pt_upper_y = ghw_upper / tan(m_groove_angle); + const float cross_pt_upper_y = ghw_upper / tan(m_groove.angle); // groove is closed if (ghw_upper < proj && ghw_lower < proj) { - const float cross_pt_lower_y = ghw_lower / tan(m_groove_angle); + const float cross_pt_lower_y = ghw_lower / tan(m_groove.angle); return { { @@ -898,23 +898,23 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() { // values for calculation - const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle)); - const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle); + const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle); - const float groove_half_width_upper = 0.5f * (m_groove_width); - const float groove_half_width_lower = 0.5f * (m_groove_width + flaps_width); + const float groove_half_width_upper = 0.5f * (m_groove.width); + const float groove_half_width_lower = 0.5f * (m_groove.width + flaps_width); const float cut_plane_radius = 1.5f * float(m_radius); const float cut_plane_length = 1.5f * cut_plane_radius; - const float groove_half_depth = 0.5f * m_groove_depth; + const float groove_half_depth = 0.5f * m_groove.depth; const float x = 0.5f * cut_plane_radius; const float y = 0.5f * cut_plane_length; float z_upper = groove_half_depth; float z_lower = -groove_half_depth; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); float ext_upper_x = groove_half_width_upper + proj; // upper_x extension float ext_lower_x = groove_half_width_lower + proj; // lower_x extension @@ -966,7 +966,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_upper -= cut_plane_thiknes; z_lower -= cut_plane_thiknes; if (m_use_TAG_mesh_full) { - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); nar_upper_x += under_x_shift; nar_lower_x += under_x_shift; @@ -1019,12 +1019,12 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() return mesh; } - float cross_pt_upper_y = groove_half_width_upper / tan(m_groove_angle); + float cross_pt_upper_y = groove_half_width_upper / tan(m_groove.angle); // groove is closed if (groove_half_width_upper < proj && groove_half_width_lower < proj) { - float cross_pt_lower_y = groove_half_width_lower / tan(m_groove_angle); + float cross_pt_lower_y = groove_half_width_lower / tan(m_groove.angle); indexed_triangle_set mesh; @@ -1045,7 +1045,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_lower -= cut_plane_thiknes; if (m_use_TAG_mesh_full) { - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); cross_pt_upper_y += cut_plane_thiknes; cross_pt_lower_y += cut_plane_thiknes; @@ -1112,7 +1112,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_upper -= cut_plane_thiknes; z_lower -= cut_plane_thiknes; - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); nar_lower_x += under_x_shift; ext_upper_x += under_x_shift; @@ -1173,10 +1173,10 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade ColorRGBA cp_clr = m_plane.model.get_color(); // values for calculaton - const double groove_half_depth = 0.5 * double(m_groove_depth); + const double groove_half_depth = 0.5 * double(m_groove.depth); - const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle)); - const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle); + const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle); GLModel model; @@ -1454,7 +1454,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() // render CutPlaneYMove grabber - if (m_groove_angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) + if (m_groove.angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) { size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color(); @@ -1526,19 +1526,19 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) if (m_mode != mode) switch_to_mode(mode); else if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - if (!is_approx(m_groove_depth , groove_depth) || - !is_approx(m_groove_width , groove_width) || - !is_approx(m_groove_flaps_angle , groove_flaps_angle) || - !is_approx(m_groove_angle , groove_angle) || - !is_approx(m_groove_depth_tolerance, groove_depth_tolerance) || - !is_approx(m_groove_width_tolerance, groove_width_tolerance) ) + if (!is_approx(m_groove.depth , groove_depth) || + !is_approx(m_groove.width , groove_width) || + !is_approx(m_groove.flaps_angle , groove_flaps_angle) || + !is_approx(m_groove.angle , groove_angle) || + !is_approx(m_groove.depth_tolerance, groove_depth_tolerance) || + !is_approx(m_groove.width_tolerance, groove_width_tolerance) ) { - m_groove_depth = groove_depth; - m_groove_width = groove_width; - m_groove_flaps_angle = groove_flaps_angle; - m_groove_angle = groove_angle; - m_groove_depth_tolerance= groove_depth_tolerance; - m_groove_width_tolerance= groove_width_tolerance; + m_groove.depth = groove_depth; + m_groove.width = groove_width; + m_groove.flaps_angle = groove_flaps_angle; + m_groove.angle = groove_angle; + m_groove.depth_tolerance= groove_depth_tolerance; + m_groove.width_tolerance= groove_width_tolerance; update_plane_model(); } reset_cut_by_contours(); @@ -1551,7 +1551,7 @@ void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const { ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing, m_ar_plane_center, m_start_dragging_m, - m_groove_depth, m_groove_width, m_groove_flaps_angle, m_groove_angle, m_groove_depth_tolerance, m_groove_width_tolerance); + m_groove.depth, m_groove.width, m_groove.flaps_angle, m_groove.angle, m_groove.depth_tolerance, m_groove.width_tolerance); } std::string GLGizmoCut3D::on_get_name() const @@ -1755,7 +1755,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() offset = (size + xy_connection_len) * Vec3d::UnitX(); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); - if (m_groove_angle > 0.0f) { + if (m_groove.angle > 0.0f) { offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones(); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size)); offset = (size + xy_connection_len) * Vec3d::UnitY(); @@ -1996,7 +1996,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=fa bool can_set_center_pos = false; { - double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove_depth) : */0.5; + double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove.depth) : */0.5; if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val) can_set_center_pos = true; else { @@ -2093,10 +2093,10 @@ void GLGizmoCut3D::update_bb() m_snap_fine_out_radius = m_grabber_connection_len * 1.15; // input params for cut with tongue and groove - m_groove_depth = m_groove_depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box))); - m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; - m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f; - m_groove_angle = m_groove_angle_init = 0.f; + m_groove.depth = m_groove.depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box))); + m_groove.width = m_groove.width_init = 4.0f * m_groove.depth; + m_groove.flaps_angle = m_groove.flaps_angle_init = float(PI) / 3.f; + m_groove.angle = m_groove.angle_init = 0.f; m_plane.reset(); m_cone.reset(); m_sphere.reset(); @@ -2188,16 +2188,9 @@ void GLGizmoCut3D::render_clipper_cut() GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) { + Cut cut(mo, instance_idx_in, cut_matrix); m_model = Model(); - m_model.add_object(*mo); - - Model tmp_model = Model(); - tmp_model.objects = m_model.objects.front()->cut(instance_idx_in, cut_matrix, - ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts); - assert(tmp_model.objects.size() == 1); - m_model = tmp_model; + m_model.add_object(*cut.perform_with_plane().front()); m_instance_idx = instance_idx_in; @@ -2270,6 +2263,7 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor m_valid = true; } +// In CutMode::cutTongueAndGroove we use PartSelection just for rendering GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in) { m_instance_idx = instance_idx_in; @@ -2388,6 +2382,16 @@ bool GLGizmoCut3D::PartSelection::is_one_object() const }); } +std::vector GLGizmoCut3D::PartSelection::get_cut_parts() +{ + std::vector parts; + + for (const auto& part : m_parts) + parts.push_back({part.selected, part.is_modifier}); + + return parts; +} + void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) { @@ -2795,11 +2799,12 @@ void GLGizmoCut3D::process_contours() wxBusyCursor wait; if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - Model tmp_model = Model(); - tmp_model.objects = perform_cut_with_groove(model_objects[object_idx], true); - if (!tmp_model.objects.empty()) - m_part_selection = PartSelection(tmp_model.objects.front(), instance_idx); - tmp_model = Model(); + if (has_valid_groove()) { + Cut cut(model_objects[object_idx], instance_idx, get_cut_matrix(selection)); + const ModelObjectPtrs& new_objects = cut.perform_with_groove(m_groove, m_rotation_m, true); + if (!new_objects.empty()) + m_part_selection = PartSelection(new_objects.front(), instance_idx); + } } else { reset_cut_by_contours(); @@ -2989,10 +2994,10 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) else if (mode == CutMode::cutTongueAndGroove) { ImGui::Separator(); ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": "); - render_groove_float_input(m_labels_map["Depth"], m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); - render_groove_float_input(m_labels_map["Width"], m_groove_width, m_groove_width_init, m_groove_width_tolerance); - render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove_flaps_angle, m_groove_flaps_angle_init, 30.f, 120.f); - render_groove_angle_input(m_labels_map["Groove Angle"], m_groove_angle, m_groove_angle_init, 0.f, 15.f); + render_groove_float_input(m_labels_map["Depth"], m_groove.depth, m_groove.depth_init, m_groove.depth_tolerance); + render_groove_float_input(m_labels_map["Width"], m_groove.width, m_groove.width_init, m_groove.width_tolerance); + render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove.flaps_angle, m_groove.flaps_angle_init, 30.f, 120.f); + render_groove_angle_input(m_labels_map["Groove Angle"], m_groove.angle, m_groove.angle_init, 0.f, 15.f); // m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); } @@ -3445,8 +3450,8 @@ bool GLGizmoCut3D::has_valid_groove() const if (CutMode(m_mode) != CutMode::cutTongueAndGroove) return true; - const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); - if (flaps_width > m_groove_width) + const float flaps_width = -2.f * m_groove.depth / tan(m_groove.flaps_angle); + if (flaps_width > m_groove.width) return false; const Selection& selection = m_parent.get_selection(); @@ -3553,280 +3558,6 @@ void synchronize_model_after_cut(Model& model, const CutObjectBase& cut_id) obj->cut_id.copy(cut_id); } -void distribute_modifiers_from_object(ModelObject *from_obj, const int instance_idx, ModelObject *to_obj1, ModelObject *to_obj2) -{ - auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3(); - auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3(); - const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix(); - - for (ModelVolume*vol : from_obj->volumes) - if (!vol->is_model_part()) { - auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); - // Don't add modifiers which are not intersecting with solid parts - if (obj1_bb.intersects(bb)) - to_obj1->add_volume(*vol); - if (obj2_bb.intersects(bb)) - to_obj2->add_volume(*vol); - } -} - -void merge_solid_parts_inside_object(ModelObjectPtrs& objects) -{ - for (ModelObject* mo : objects) { - TriangleMesh mesh; - // Merge all SolidPart but not Connectors - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector()) { - TriangleMesh m = mv->mesh(); - m.transform(mv->get_matrix()); - mesh.merge(m); - } - } - if (!mesh.empty()) { - ModelVolume* new_volume = mo->add_volume(mesh); - new_volume->name = mo->name; - // Delete all merged SolidPart but not Connectors - for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { - const ModelVolume* mv = mo->volumes[i]; - if (mv->is_model_part() && !mv->is_cut_connector()) - mo->delete_volume(i); - } - } - } -} - -ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count) -{ - const Selection& selection = m_parent.get_selection(); - const int instance_idx = selection.get_instance_idx(); - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - if (m_keep_upper) cut_mo->clone_for_cut(&upper); - ModelObject* lower{ nullptr }; - if (m_keep_lower) cut_mo->clone_for_cut(&lower); - - const Transform3d cut_matrix = get_cut_matrix(selection); - - auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { - if (upper && !upper->volumes.empty()) { - ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); - cut_objects.push_back(upper); - } - if (lower && !lower->volumes.empty()) { - ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower); - cut_objects.push_back(lower); - } - }; - - const size_t cut_parts_cnt = m_part_selection.parts().size(); - bool has_modifiers = false; - - // Distribute SolidParts to the Upper/Lower object - for (size_t id = 0; id < cut_parts_cnt; ++id) { - if (m_part_selection.parts()[id].is_modifier) - has_modifiers = true; // modifiers will be added later to the related parts - else if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower)) - obj->add_volume(*(cut_mo->volumes[id])); - } - - if (has_modifiers) { - // Distribute Modifiers to the Upper/Lower object - distribute_modifiers_from_object(cut_mo, instance_idx, upper, lower); - } - - ModelObjectPtrs cut_object_ptrs; - - ModelVolumePtrs& volumes = cut_mo->volumes; - if (volumes.size() == cut_parts_cnt) { - // Means that object is cut without connectors - - // Just add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); - } - else if (volumes.size() > cut_parts_cnt) { - // Means that object is cut with connectors - - // All volumes are distributed to Upper / Lower object, - // So we don’t need them anymore - for (size_t id = 0; id < cut_parts_cnt; id++) - delete *(volumes.begin() + id); - volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); - - // Perform cut just to get connectors - const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); - assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); - - // Connectors from upper object - for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) - upper->add_volume(*volume, volume->type()); - - // Connectors from lower object - for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) - lower->add_volume(*volume, volume->type()); - - // Add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); - - // Add Dowel-connectors as separate objects to cut_object_ptrs - if (cut_connectors_obj.size() >= 3) - for (size_t id = 2; id < cut_connectors_obj.size(); id++) - cut_object_ptrs.push_back(cut_connectors_obj[id]); - } - - // Now merge all model parts together: - merge_solid_parts_inside_object(cut_object_ptrs); - - return cut_object_ptrs; -} - -ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts/* = false*/) -{ - if (!has_valid_groove()) - return {}; - - const Selection& selection = m_parent.get_selection(); - const int instance_idx = selection.get_instance_idx(); - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - cut_mo->clone_for_cut(&upper); - ModelObject* lower{ nullptr }; - cut_mo->clone_for_cut(&lower); - - const double groove_half_depth = 0.5 * double(m_groove_depth); - - const Transform3d cut_matrix = get_cut_matrix(selection); - - Model tmp_model_for_cut = Model(); - - Model tmp_model = Model(); - tmp_model.add_object(*cut_mo); - ModelObject* tmp_object = tmp_model.objects.front(); - - auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { - const auto& volumes = tmp_model_for_cut.objects.front()->volumes; - for (const ModelVolume* volume : volumes) - if (volume->is_model_part()) { - if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || - (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper()) ) { - ModelVolume* new_vol = object->add_volume(*volume); - new_vol->reset_from_upper(); - } - } - }; - - auto cut = [instance_idx, add_volumes_from_cut] - (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) { - Model model = Model(); - model.add_object(*object); - - tmp_model_for_cut = Model(); - tmp_model_for_cut.objects = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - assert(!tmp_model_for_cut.objects.empty()); - - object->clear_volumes(); - add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut); - ModelObject::reset_instance_transformation(object, instance_idx); - }; - - // cut by upper plane - - const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; - { - cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // cut by lower plane - - const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; - { - cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - } - - // cut middle part with 2 angles and add parts to related upper/lower objects - - const double h_side_shift = 0.5 * double(m_groove_width + m_groove_depth / tan(m_groove_flaps_angle)); - - // cut by angle1 plane - { - const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - - cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // cut by angle2 plane - { - const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - - cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // apply tolerance to the middle part - { - const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; - - const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; - cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - - const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); - - const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - - const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // this part can be added to the upper object now - add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - - ModelObjectPtrs cut_object_ptrs; - - if (keep_as_parts) { - // add volumes from lower object to the upper, but mark them as a lower - const auto& volumes = lower->volumes; - for (const ModelVolume* volume : volumes) { - ModelVolume* new_vol = upper->add_volume(*volume); - new_vol->cut_info.is_from_upper = false; - } - - // add modifiers - for (const ModelVolume* volume : cut_mo->volumes) - if (!volume->is_model_part()) - upper->add_volume(*volume); - - cut_object_ptrs.push_back(upper); - - // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks - cut_object_ptrs.push_back(lower); - } - else { - // add modifiers if object has any - for (const ModelVolume* volume : cut_mo->volumes) - if (!volume->is_model_part()) { - distribute_modifiers_from_object(cut_mo, instance_idx, upper, lower); - break; - } - - assert(!upper->volumes.empty() && !lower->volumes.empty()); - - ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix_upper, m_place_on_cut_upper, m_rotate_upper); - cut_object_ptrs.push_back(upper); - ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix_lower, m_place_on_cut_upper, m_rotate_upper); - cut_object_ptrs.push_back(lower); - - // Now merge all model parts together: - merge_solid_parts_inside_object(cut_object_ptrs); - } - - return cut_object_ptrs; -} - void GLGizmoCut3D::perform_cut(const Selection& selection) { if (!can_perform_cut()) @@ -3867,8 +3598,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) wxBusyCursor wait; - const Transform3d cut_matrix = get_cut_matrix(selection); - ModelObjectCutAttributes attributes = only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(has_connectors ? false : m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | @@ -3882,16 +3611,15 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // update cut_id for the cut object in respect to the attributes update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); - Model tmp_model = Model(); - tmp_model.objects = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) : - cut_with_groove ? perform_cut_with_groove(cut_mo) : - cut_mo->cut(instance_idx, cut_matrix, attributes); - + Cut cut(cut_mo, instance_idx, get_cut_matrix(selection), attributes); + const ModelObjectPtrs& new_objects = cut_by_contour ? cut.perform_by_contour(m_part_selection.get_cut_parts(), dowels_count): + cut_with_groove ? cut.perform_with_groove(m_groove, m_rotation_m) : + cut.perform_with_plane(); // save cut_id to post update synchronization const CutObjectBase cut_id = cut_mo->cut_id; // update cut results on plater and in the model - plater->cut(object_idx, tmp_model.objects); + plater->apply_cut_object_to_model(object_idx, new_objects); synchronize_model_after_cut(plater->model(), cut_id); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 11ac1ee7a8..34ba6e9ab1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -7,6 +7,7 @@ #include "slic3r/GUI/I18N.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/CutUtils.hpp" #include "imgui/imgui.h" namespace Slic3r { @@ -118,16 +119,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_rotate_lower{ false }; // Input params for cut with tongue and groove - float m_groove_depth; - float m_groove_width; - float m_groove_flaps_angle; - float m_groove_angle; - float m_groove_depth_init; - float m_groove_width_init; - float m_groove_flaps_angle_init; - float m_groove_angle_init; - float m_groove_depth_tolerance{ 0.1f }; - float m_groove_width_tolerance{ 0.1f }; + Cut::Groove m_groove; bool m_optimaze_groove_rendering{ true }; // Input params for cut with snaps @@ -187,6 +179,8 @@ class GLGizmoCut3D : public GLGizmoBase const std::vector& parts() const { return m_parts; } const std::vector* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); } + std::vector get_cut_parts(); + private: Model m_model; int m_instance_idx; @@ -367,8 +361,6 @@ private: void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0); void render_cut_plane_grabbers(); void render_cut_line(); - ModelObjectPtrs perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count); - ModelObjectPtrs perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts = false); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false); void update_bb(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fcd97cdc15..fb8cfb99e7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6318,20 +6318,7 @@ void Plater::toggle_layers_editing(bool enable) canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting")); } -void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) -{ - wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); - auto* object = p->model.objects[obj_idx]; - - wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - - wxBusyCursor wait; - - const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); - cut(obj_idx, new_objects); -} - -void Plater::cut(size_t obj_idx, const ModelObjectPtrs& new_objects) +void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& new_objects) { model().delete_object(obj_idx); sidebar().obj_list()->delete_object_from_list(obj_idx); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 8eeabae428..bed65d7d13 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -264,8 +264,7 @@ public: void convert_unit(ConversionType conv_type); void toggle_layers_editing(bool enable); - void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes); - void cut(size_t init_obj_idx, const ModelObjectPtrs& cut_objects); + void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects); void export_gcode(bool prefer_removable); void export_stl_obj(bool extended = false, bool selection_only = false);