From 66e2c3b30a6208ea373fc6878c13c71f6363c7ad Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 26 Sep 2022 17:23:40 +0200 Subject: [PATCH] Cut WIP: Send to the cut() whole cut_matrix instead of cut_plane_pos and rotation angles + Fixed units inside input window + NotificationManager: Added info line for loaded object with cut parts + Next Code refactoring --- src/libslic3r/Model.cpp | 501 +++++++++++++------------ src/libslic3r/Model.hpp | 25 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 92 +++-- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/NotificationManager.cpp | 1 + src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/Plater.hpp | 2 +- 7 files changed, 325 insertions(+), 302 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 82b5fad57..3d245a7b1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1381,11 +1381,13 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn return connector_mesh; } -void ModelObject::apply_cut_connectors(const std::string& name) +void ModelObject::apply_cut_connectors(const std::string& new_name) { if (cut_connectors.empty()) return; + using namespace Geometry; + size_t connector_id = cut_id.connectors_cnt(); for (const CutConnector& connector : cut_connectors) { TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); @@ -1393,15 +1395,11 @@ void ModelObject::apply_cut_connectors(const std::string& name) ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); // Transform the new modifier to be aligned inside the instance - new_volume->set_transformation(Geometry::assemble_transform( - connector.pos, - connector.rotation, - Vec3d(connector.radius, connector.radius, connector.height), - Vec3d::Ones() - )); + new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m * + scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; - new_volume->name = name + "-" + std::to_string(++connector_id); + new_volume->name = new_name + "-" + std::to_string(++connector_id); } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1426,14 +1424,8 @@ void ModelObject::synchronize_model_after_cut() } } -ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) +void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) { - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) - return {}; - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - - // initiate/update cut attributes for object if (cut_id.id().invalid()) cut_id.init(); { @@ -1444,25 +1436,241 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (cut_obj_cnt > 0) cut_id.increase_check_sum(size_t(cut_obj_cnt)); } +} - auto clone_obj = [this](ModelObject** obj) { - (*obj) = ModelObject::new_clone(*this); - (*obj)->set_model(nullptr); - (*obj)->sla_support_points.clear(); - (*obj)->sla_drain_holes.clear(); - (*obj)->sla_points_status = sla::PointsStatus::NoPoints; - (*obj)->clear_volumes(); - (*obj)->input_file.clear(); - }; +void ModelObject::clone_for_cut(ModelObject** obj) +{ + (*obj) = ModelObject::new_clone(*this); + (*obj)->set_model(nullptr); + (*obj)->sla_support_points.clear(); + (*obj)->sla_drain_holes.clear(); + (*obj)->sla_points_status = sla::PointsStatus::NoPoints; + (*obj)->clear_volumes(); + (*obj)->input_file.clear(); +} + +void ModelVolume::apply_tolerance() +{ + if (!cut_info.is_connector) + return; + + Vec3d sf = get_scaling_factor(); +/* + // correct Z offset in respect to the new size + Vec3d pos = vol->get_offset(); + pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance; + vol->set_offset(pos); +*/ + // make a "hole" wider + sf[X] *= 1. + double(cut_info.radius_tolerance); + sf[Y] *= 1. + double(cut_info.radius_tolerance); + + // make a "hole" dipper + sf[Z] *= 1. + double(cut_info.height_tolerance); + + set_scaling_factor(sf); +} + +void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels, Vec3d& local_dowels_displace) +{ + volume->cut_info.discard(); + + const auto volume_matrix = volume->get_matrix(); + + // ! 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) + vol->apply_tolerance(); + else + // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug + vol->set_type(ModelVolumeType::MODEL_PART); + } + if (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); + + // 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); + + // 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); + } +} + +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 = 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(instance_matrix * volume_matrix)); + + // 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 add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix) +{ + if (mesh.empty()) + return; + + mesh.transform(cut_matrix); + ModelVolume* vol = object->add_volume(mesh); + + vol->name = src_volume->name; + // 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()); +} + +void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace) +{ + 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() * assemble_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); + + 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, 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); + } + + // Add required cut parts to the objects + + 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); + + // 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 + // this is done in hope at least a part of the upper part will always be visible and draggable + local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); + } +} + +static void 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(); + } +} + +static void 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; + static Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); + + // 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() : + assemble_transform(Vec3d::Zero(), 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(rotate_z180); + + 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"; + + // apply cut attributes for object + apply_cut_attributes(attributes); // Clone the object to duplicate instances, materials etc. ModelObject* upper{ nullptr }; if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - clone_obj(&upper); + clone_for_cut(&upper); ModelObject* lower{ nullptr }; if (attributes.has(ModelObjectCutAttribute::KeepLower)) - clone_obj(&lower); + clone_for_cut(&lower); std::vector dowels; @@ -1476,260 +1684,67 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // const auto instance_matrix = instances[instance]->get_matrix(true); const auto instance_matrix = assemble_transform( Vec3d::Zero(), // don't apply offset - instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), + instances[instance]->get_rotation(), instances[instance]->get_scaling_factor(), instances[instance]->get_mirror() ); - const auto cut_matrix = rotation_transform(cut_rotation).inverse() * assemble_transform(-cut_center); - const auto invert_cut_matrix = assemble_transform(cut_center, cut_rotation); + const Transformation cut_transformation = Transformation(cut_matrix); + const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1. * cut_transformation.get_offset()); // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); Vec3d local_dowels_displace = Vec3d::Zero(); - Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); - - auto apply_tolerance = [](ModelVolume * vol) - { - Vec3d sf = vol->get_scaling_factor(); -/* - // correct Z offset in respect to the new size - Vec3d pos = vol->get_offset(); - pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance; - vol->set_offset(pos); -*/ - // make a "hole" wider - sf[X] *= (1 + vol->cut_info.radius_tolerance); - sf[Y] *= (1 + vol->cut_info.radius_tolerance); - // make a "hole" dipper - sf[Z] *= (1 + vol->cut_info.height_tolerance); - vol->set_scaling_factor(sf); - }; - for (ModelVolume* volume : volumes) { - const auto volume_matrix = volume->get_matrix(); - volume->supported_facets.reset(); volume->seam_facets.reset(); volume->mmu_segmentation_facets.reset(); if (!volume->is_model_part()) { - if (volume->cut_info.is_connector) { - volume->cut_info.discard(); - - // ! 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); - apply_tolerance(vol); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - ModelVolume* vol = lower->add_volume(*volume); - vol->set_transformation(volume_matrix); - - if (volume->cut_info.connector_type == CutConnectorType::Dowel) - apply_tolerance(vol); - else - // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug - vol->set_type(ModelVolumeType::MODEL_PART); - } - if (volume->cut_info.connector_type == CutConnectorType::Dowel && - attributes.has(ModelObjectCutAttribute::CreateDowels)) { - ModelObject* dowel{ nullptr }; - // Clone the object to duplicate instances, materials etc. - clone_obj(&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); - - // 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); - } - } - else { - // 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(instance_matrix * volume_matrix)); - - // Some logic for the negative volumes/connectors. Add only needed modifiers - auto bb = volume->mesh().transformed_bounding_box(cut_matrix * volume->get_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); - } - } - else if (!volume->mesh().empty()) { - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(cut_matrix * instance_matrix* volume_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, 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); - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { - upper_mesh.transform(invert_cut_matrix); - - ModelVolume* vol = upper->add_volume(upper_mesh); - vol->name = volume->name; - // 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()); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - lower_mesh.transform(invert_cut_matrix); - - ModelVolume* vol = lower->add_volume(lower_mesh); - vol->name = volume->name; - // 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()); - - // 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 - // this is done in hope at least a part of the upper part will always be visible and draggable - local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); - } + if (volume->cut_info.is_connector) + process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace); + else + process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); } + else if (!volume->mesh().empty()) + process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace); } + // Post-process cut parts + ModelObjectPtrs res; if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - upper->center_around_origin(); - upper->translate_instances(-upper->origin_translation); - upper->origin_translation = Vec3d::Zero(); - } - else { - upper->invalidate_bounding_box(); - upper->center_around_origin(); - } - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = upper->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - const double rot_z = obj_instance->get_rotation().z(); - const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace; + invalidate_translations(upper, instances[instance]); - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset + displace); - - Vec3d rotation = Vec3d::Zero(); - if (attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) { - Transform3d trafo = rotation_transform(cut_rotation).inverse(); - if (i != instance) - trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; - rotation = Transformation(trafo).get_rotation(); - } - else if (attributes.has(ModelObjectCutAttribute::FlipUpper)) { - rotation = rotate_z180; - if (i != instance) - rotation[Z] = rot_z; - } - else if (i != instance) - rotation[Z] = rot_z/* * Vec3d::UnitZ()*/; - obj_instance->set_rotation(rotation); - } + reset_instance_transformation(upper, instance, cut_matrix, + attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + attributes.has(ModelObjectCutAttribute::FlipUpper), + local_displace); res.push_back(upper); } + if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - lower->center_around_origin(); - lower->translate_instances(-lower->origin_translation); - lower->origin_translation = Vec3d::Zero(); - } - else { - lower->invalidate_bounding_box(); - lower->center_around_origin(); - } - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = lower->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - const double rot_z = obj_instance->get_rotation().z(); - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset); + invalidate_translations(lower, instances[instance]); - Vec3d rotation = Vec3d::Zero(); - if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) { - Transform3d trafo = rotation_transform(rotate_z180) * rotation_transform(cut_rotation).inverse(); - if (i != instance) - trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; - rotation = Transformation(trafo).get_rotation(); - } - else if (attributes.has(ModelObjectCutAttribute::FlipLower)) { - rotation = rotate_z180; - if (i != instance) - rotation[Z] = rot_z; - } - else if (i != instance) - rotation[Z] = rot_z; - obj_instance->set_rotation(rotation); - } + 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.size() > 0) { for (auto dowel : dowels) { - if (!dowel->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - dowel->center_around_origin(); - dowel->translate_instances(-dowel->origin_translation); - dowel->origin_translation = Vec3d::Zero(); - } - else { - dowel->invalidate_bounding_box(); - dowel->center_around_origin(); - } + invalidate_translations(dowel, instances[instance]); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = dowel->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - Vec3d rotation = Vec3d::Zero(); - if (i != instance) - rotation[Z] = obj_instance->get_rotation().z(); - - const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), rotation) * local_dowels_displace; - - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset + displace); - obj_instance->set_rotation(rotation); - } + 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)); res.push_back(dowel); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 21b052337..6981946fb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -271,7 +271,7 @@ struct CutConnectorAttributes struct CutConnector { Vec3d pos; - Vec3d rotation; + Transform3d rotation_m; float radius; float height; float radius_tolerance;// [0.f : 1.f] @@ -279,22 +279,22 @@ struct CutConnector CutConnectorAttributes attribs; CutConnector() - : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) + : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} - CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) - : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) + : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} bool operator==(const CutConnector& other) const; bool operator!=(const CutConnector& other) const { return !(other == (*this)); } template inline void serialize(Archive& ar) { - ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, attribs); + ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); } }; @@ -445,8 +445,16 @@ public: // invalidate cut state for this and related objects from the whole model void invalidate_cut(); void synchronize_model_after_cut(); - ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); - void split(ModelObjectPtrs* new_objects); + void apply_cut_attributes(ModelObjectCutAttributes attributes); + void clone_for_cut(ModelObject **obj); + void process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels, Vec3d& local_dowels_displace); + void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); + void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace); + 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, // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. @@ -760,6 +768,7 @@ public: bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } t_model_material_id material_id() const { return m_material_id; } + 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 498668896..1260ee3bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -800,10 +800,9 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) { ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, - m_ar_plane_center, m_ar_rotations); + m_ar_plane_center, m_rotation_m); set_center_pos(m_ar_plane_center, true); - m_rotation_m = rotation_transform(m_ar_rotations); force_update_clipper_on_render = true; @@ -814,7 +813,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_selected, // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, - m_ar_plane_center, m_ar_rotations); + m_ar_plane_center, m_start_dragging_m); } std::string GLGizmoCut3D::on_get_name() const @@ -829,7 +828,7 @@ void GLGizmoCut3D::on_set_state() // initiate archived values m_ar_plane_center = m_plane_center; - m_ar_rotations = Transformation(m_rotation_m).get_rotation(); + m_start_dragging_m = m_rotation_m; m_parent.request_extra_frame(); } @@ -1119,8 +1118,6 @@ void GLGizmoCut3D::on_stop_dragging() m_angle_arc.reset(); m_angle = 0.0; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); - m_ar_rotations = Transformation(m_rotation_m).get_rotation(); - m_start_dragging_m = m_rotation_m; } else if (m_hover_id == Z) { @@ -1436,7 +1433,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) void GLGizmoCut3D::render_build_size() { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); + wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); const BoundingBoxf3 tbb = transformed_bounding_box(); Vec3d tbb_sz = tbb.size(); @@ -1710,6 +1707,31 @@ bool GLGizmoCut3D::can_perform_cut() const return tbb.contains(m_plane_center); } +void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object) +{ + if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { + m_selected.clear(); + + for (CutConnector&connector : mo->cut_connectors) { + connector.rotation_m = m_rotation_m; + + if (connector.attribs.type == CutConnectorType::Dowel) { + if (connector.attribs.style == CutConnectorStyle::Prizm) + connector.height *= 2; + create_dowels_as_separate_object = true; + } + else { + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); + rotate_vec3d_around_plane_center(shifted_center); + Vec3d norm = (shifted_center - m_plane_center).normalized(); + connector.pos += norm * 0.5 * double(connector.height); + } + } + mo->apply_cut_connectors(_u8L("Connector")); + } +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { const int instance_idx = selection.get_instance_idx(); @@ -1717,53 +1739,29 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); - // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const GLVolume* first_glvolume = selection.get_first_volume(); - const double object_cut_z = m_plane_center.z() - first_glvolume->get_sla_shift_z(); - - const Vec3d& instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); - - Vec3d cut_center_offset = m_plane_center - instance_offset; - cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); - Plater* plater = wxGetApp().plater(); + ModelObject* mo = plater->model().objects[object_idx]; + if (!mo) + return; + + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. + const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); + const double object_cut_z = m_plane_center.z() - sla_shift_z; + + const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); + Vec3d cut_center_offset = m_plane_center - instance_offset; + cut_center_offset[Z] -= sla_shift_z; - bool create_dowels_as_separate_object = false; if (0.0 < object_cut_z && can_perform_cut()) { - ModelObject* mo = plater->model().objects[object_idx]; - if(!mo) - return; - - Vec3d rotation = Transformation(m_rotation_m).get_rotation(); - + bool create_dowels_as_separate_object = false; const bool has_connectors = !mo->cut_connectors.empty(); { - Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); // update connectors pos as offset of its center before cut performing - if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { - m_selected.clear(); - - for (CutConnector& connector : mo->cut_connectors) { - connector.rotation = rotation; - - if (connector.attribs.type == CutConnectorType::Dowel) { - if (connector.attribs.style == CutConnectorStyle::Prizm) - connector.height *= 2; - create_dowels_as_separate_object = true; - } - else { - // culculate shift of the connector center regarding to the position on the cut plane - Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); - rotate_vec3d_around_plane_center(shifted_center); - Vec3d norm = (shifted_center - m_plane_center).normalized(); - connector.pos += norm * 0.5 * double(connector.height); - } - } - mo->apply_cut_connectors(_u8L("Connector")); - } + apply_connectors_in_model(mo, has_connectors, create_dowels_as_separate_object); } - plater->cut(object_idx, instance_idx, cut_center_offset, rotation, + plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | @@ -1925,7 +1923,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), + connectors.emplace_back(hit, m_rotation_m, float(m_connector_size) * 0.5f, float(m_connector_depth_ratio), float(m_connector_size_tolerance) * 0.01f, float(m_connector_depth_ratio_tolerance) * 0.01f, CutConnectorAttributes( CutConnectorType(m_connector_type), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index fc2347c64..2df337b04 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -25,7 +25,6 @@ class GLGizmoCut3D : public GLGizmoBase // archived values Vec3d m_ar_plane_center { Vec3d::Zero() }; - Vec3d m_ar_rotations { Vec3d::Zero() }; Vec3d m_plane_center{ Vec3d::Zero() }; // data to check position of the cut palne center on gizmo activation @@ -216,6 +215,7 @@ private: void render_connectors(); bool can_perform_cut() const; + void apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object); bool cut_line_processing() const; void discard_cut_line_processing(); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 2e74270da..16433bc79 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1228,6 +1228,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break; case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break; + case InfoItemType::Cut: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break; } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7f351e0b9..4bf441aa1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5967,7 +5967,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCut selection.add_object((unsigned int)(last_id - i), i == 0); } -void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) +void Slic3r::GUI::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]; @@ -5977,7 +5977,7 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& this->suppress_snapshots(); wxBusyCursor wait; - const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes); + const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); 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 7e35e935f..9ee1af272 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -256,7 +256,7 @@ public: void toggle_layers_editing(bool enable); void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes); - void cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); + void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes); void export_gcode(bool prefer_removable); void export_stl_obj(bool extended = false, bool selection_only = false);