diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index 8ed68ca72fa..f8b14837737 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -3,7 +3,6 @@ #include "Geometry.hpp" #include "libslic3r.h" #include "Model.hpp" -#include "ModelTextureDataRemap.hpp" #include "TriangleMeshSlicer.hpp" #include "TriangleSelector.hpp" #include "ObjectID.hpp" @@ -46,59 +45,10 @@ static void apply_tolerance(ModelVolume* vol) vol->set_offset(vol->get_offset() + rot_norm * z_offset); } -static void apply_cut_texture_data(ModelVolume *volume, const SimplifyTextureDataSnapshot *source_snapshot, const Transform3d &cut_matrix) -{ - if (volume == nullptr || source_snapshot == nullptr || - (source_snapshot->source == SimplifyColorSource::None && !source_snapshot->region_painting_present) || - volume->mesh().empty()) - return; - - SimplifyTextureDataSnapshot snapshot = *source_snapshot; - transform_simplify_texture_data_snapshot(snapshot, cut_matrix); - transform_simplify_texture_data_snapshot(snapshot, translation_transform(-volume->get_offset())); - - SimplifyTextureDataResult result = remap_simplify_texture_data(snapshot, volume->mesh().its); - apply_simplify_texture_data_result(*volume, std::move(result)); -} - -static bool volume_has_remappable_color_data(const ModelVolume &volume) -{ - const indexed_triangle_set &its = volume.mesh().its; - if (its.vertices.empty() || its.indices.empty()) - return false; - - if (model_volume_region_painting_needs_remap(volume)) - return true; - - if (!volume.texture_mapping_color_facets.empty()) - return true; - - const bool has_valid_uvs = volume.imported_texture_uv_valid.size() == its.indices.size() && - volume.imported_texture_uvs_per_face.size() >= its.indices.size() * 6; - const bool has_rgba_texture = volume.imported_texture_width > 0 && volume.imported_texture_height > 0 && - volume.imported_texture_rgba.size() >= - size_t(volume.imported_texture_width) * size_t(volume.imported_texture_height) * 4; - const bool has_raw_texture = volume.imported_texture_width > 0 && volume.imported_texture_height > 0 && - volume.imported_texture_raw_channels > 0 && - volume.imported_texture_raw_filament_offsets.size() >= - size_t(volume.imported_texture_width) * size_t(volume.imported_texture_height) * - size_t(volume.imported_texture_raw_channels); - if (has_valid_uvs && (has_rgba_texture || has_raw_texture)) - return true; - - return volume.imported_vertex_colors_rgba.size() == its.vertices.size(); -} - -static ModelVolume* add_cut_volume(TriangleMesh &mesh, - ModelObject *object, - const ModelVolume *src_volume, - const Transform3d &cut_matrix, - const std::string &suffix = {}, - ModelVolumeType type = ModelVolumeType::MODEL_PART, - const SimplifyTextureDataSnapshot *source_snapshot = nullptr) +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 nullptr; + return; mesh.transform(cut_matrix); ModelVolume* vol = object->add_volume(mesh); @@ -111,31 +61,20 @@ static ModelVolume* add_cut_volume(TriangleMesh &mesh, 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; - apply_cut_texture_data(vol, source_snapshot, cut_matrix); - return vol; } -static void process_volume_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh, - SimplifyTextureDataSnapshot *source_snapshot = nullptr, - const SimplifyTextureDataSnapshot *volume_snapshot = nullptr) +static void process_volume_cut( const 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()); - const Transform3d source_to_cut_matrix = invert_cut_matrix * instance_matrix * volume_matrix; - - if (source_snapshot != nullptr) { - *source_snapshot = volume_snapshot != nullptr ? *volume_snapshot : snapshot_simplify_texture_data(*volume); - transform_simplify_texture_data_snapshot(*source_snapshot, source_to_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(source_to_cut_matrix, true); + 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); @@ -147,8 +86,7 @@ static void process_volume_cut( ModelVolume* volume, const Transform3d& instance static void process_connector_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels, - const SimplifyTextureDataSnapshot *volume_snapshot = nullptr) + std::vector& dowels) { assert(volume->cut_info.is_connector); volume->cut_info.set_processed(); @@ -205,16 +143,15 @@ static void process_connector_cut( ModelVolume* volume, const Transform3d& inst // Perform cut TriangleMesh upper_mesh, lower_mesh; - SimplifyTextureDataSnapshot source_snapshot; - process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh, &source_snapshot, volume_snapshot); + 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(), &source_snapshot); - add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type(), &source_snapshot); + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); + add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); } } @@ -241,31 +178,29 @@ static void process_modifier_cut(ModelVolume* volume, const Transform3d& instanc 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, - const SimplifyTextureDataSnapshot *volume_snapshot = nullptr) +static void process_solid_part_cut(const ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) { // Perform cut TriangleMesh upper_mesh, lower_mesh; - SimplifyTextureDataSnapshot source_snapshot; - process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh, &source_snapshot, volume_snapshot); + 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", ModelVolumeType::MODEL_PART, &source_snapshot); + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); if (!lower_mesh.empty()) { - add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B", ModelVolumeType::MODEL_PART, &source_snapshot); + 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, {}, ModelVolumeType::MODEL_PART, &source_snapshot); + 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, {}, ModelVolumeType::MODEL_PART, &source_snapshot); + add_cut_volume(lower_mesh, lower, volume, cut_matrix); } static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, @@ -344,9 +279,22 @@ void Cut::post_process(ModelObject* upper, ModelObject* lower, ModelObjectPtrs& } -void Cut::finalize(const ModelObjectPtrs& objects) +void Cut::finalize(const ModelObjectPtrs& objects, const std::vector>& saved_paintings) { - //clear model from temporarry objects + // Paint volumes + for (const auto& saved_painting : saved_paintings) { + if (saved_painting) { + for (const auto object : objects) { + for (const auto volume : object->volumes) { + if (volume->is_model_part() && !volume->is_cut_connector()) { + volume->restore_painting(saved_painting, true); + } + } + } + } + } + + //clear model from temporary objects m_model.clear_objects(); // add to model result objects @@ -385,18 +333,27 @@ const ModelObjectPtrs& Cut::perform_with_plane() 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()); + std::vector> saved_paintings; for (ModelVolume* volume : mo->volumes) { - SimplifyTextureDataSnapshot volume_snapshot = snapshot_simplify_texture_data(*volume); + // Save painting data before reset_extra_facets() discards it. + if (m_attributes.has(ModelObjectCutAttribute::KeepPaint)) { + saved_paintings.emplace_back(volume->save_painting()); + if (saved_paintings.back()) { + // Transform mesh to cut space (same transform as process_volume_cut applies) + saved_paintings.back()->mesh.transform(instance_matrix * volume->get_matrix(), true); + } + } + 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, &volume_snapshot); + 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, &volume_snapshot); + process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower); } // Post-process cut parts @@ -442,9 +399,9 @@ const ModelObjectPtrs& Cut::perform_with_plane() } } - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + finalize(cut_object_ptrs, saved_paintings); - finalize(cut_object_ptrs); + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; return m_model.objects; } @@ -472,25 +429,10 @@ static void distribute_modifiers_from_object(ModelObject* from_obj, const int in static void merge_solid_parts_inside_object(ModelObjectPtrs& objects) { for (ModelObject* mo : objects) { - bool has_remappable_color_data = false; - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector() && volume_has_remappable_color_data(*mv)) { - has_remappable_color_data = true; - break; - } - } - if (has_remappable_color_data) { - mo->sort_volumes(true); - continue; - } - TriangleMesh mesh; - const ModelVolume *merged_volume_source = nullptr; // Merge all SolidPart but not Connectors for (const ModelVolume* mv : mo->volumes) { if (mv->is_model_part() && !mv->is_cut_connector()) { - if (merged_volume_source == nullptr) - merged_volume_source = mv; TriangleMesh m = mv->mesh(); m.transform(mv->get_matrix()); mesh.merge(m); @@ -499,14 +441,6 @@ static void merge_solid_parts_inside_object(ModelObjectPtrs& objects) if (!mesh.empty()) { ModelVolume* new_volume = mo->add_volume(mesh); new_volume->name = mo->name; - if (merged_volume_source != nullptr) { - new_volume->config.assign_config(merged_volume_source->config); - if (ModelMaterial *material = merged_volume_source->material()) - new_volume->set_material(merged_volume_source->material_id(), *material); - else - new_volume->set_material_id(merged_volume_source->material_id()); - new_volume->cut_info = merged_volume_source->cut_info; - } // Delete all merged SolidPart but not Connectors for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { const ModelVolume* mv = mo->volumes[i]; @@ -520,7 +454,7 @@ static void merge_solid_parts_inside_object(ModelObjectPtrs& objects) } -const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowels_count) +const ModelObjectPtrs& Cut::perform_by_contour(const ModelObject* src_object, std::vector parts, int dowels_count) { ModelObject* cut_mo = m_model.objects.front(); @@ -535,6 +469,19 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe lower->name = lower->name + "_B"; } + // Save painting data so we later can remap it. + std::vector> saved_paintings; + if (m_attributes.has(ModelObjectCutAttribute::KeepPaint)) { + const auto instance_matrix = src_object->instances[m_instance]->get_transformation().get_matrix_no_offset(); + for (const auto volume : src_object->volumes) { + saved_paintings.emplace_back(volume->save_painting()); + if (saved_paintings.back()) { + // Transform mesh to cut space (same transform as process_volume_cut applies) + saved_paintings.back()->mesh.transform(instance_matrix * volume->get_matrix(), true); + } + } + } + const size_t cut_parts_cnt = parts.size(); bool has_modifiers = false; @@ -564,7 +511,7 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe merge_solid_parts_inside_object(cut_object_ptrs); // replace initial objects in model with cut object - finalize(cut_object_ptrs); + finalize(cut_object_ptrs, saved_paintings); } else if (volumes.size() > cut_parts_cnt) { // Means that object is cut with connectors @@ -595,7 +542,7 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe merge_solid_parts_inside_object(cut_object_ptrs); // replace initial objects in model with cut object - finalize(cut_object_ptrs); + finalize(cut_object_ptrs, saved_paintings); // Add Dowel-connectors as separate objects to model if (cut_connectors_obj.size() >= 3) @@ -607,7 +554,12 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe } -const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts/* = false*/) +const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, + const Transform3d& rotation_m, + const int groove_count, + const float groove_gap, + const float m_radius, + bool keep_as_parts /* = false*/) { ModelObject* cut_mo = m_model.objects.front(); @@ -621,6 +573,20 @@ const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Tran upper->name = upper->name + "_A"; lower->name = lower->name + "_B"; } + + // Save painting data so we later can remap it. + std::vector> saved_paintings; + if (m_attributes.has(ModelObjectCutAttribute::KeepPaint)) { + const auto instance_matrix = cut_mo->instances[m_instance]->get_transformation().get_matrix_no_offset(); + for (const auto volume : cut_mo->volumes) { + saved_paintings.emplace_back(volume->save_painting()); + if (saved_paintings.back()) { + // Transform mesh to cut space (same transform as process_volume_cut applies) + saved_paintings.back()->mesh.transform(instance_matrix * volume->get_matrix(), true); + } + } + } + const double groove_half_depth = 0.5 * double(groove.depth); Model tmp_model_for_cut = Model(); @@ -654,61 +620,108 @@ const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Tran 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 by upper plane (+Z) { + 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 by lower plane (-Z) { + 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 + // Compute same slot outer width used in preview plane + const float groove_width = calculate_groove_width(groove, m_radius); - const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle)); + ModelObject* groove_object{nullptr}; - // 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)); + // multiple cuts + for (int i = 0; i < groove_count; i++) { + bool is_first_groove = i == 0; + bool is_last_groove = i == groove_count - 1; - cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + // Calculate the x-axis offset for this dovetail + float groove_offset_factor_start = -.5 * ((groove_count - 1)); + float groove_offset_factor = groove_offset_factor_start + i; + + float offset_x = groove_offset_factor * (groove_gap + groove_width); + + + tmp_object->clone_for_cut(&groove_object); + for (ModelVolume* volume : tmp_object->volumes) { + ModelVolume* new_vol = groove_object->add_volume(*volume); + new_vol->reset_from_upper(); + } + + // isolate area of current groove + if (!is_first_groove) { + float left_cut_position = (-groove_gap / 2.f) - (groove_width / 2.f) + offset_x; + + const Transform3d cut_matrix_left = translation_transform(rotation_m * (left_cut_position * Vec3d::UnitX())) * + m_cut_matrix * rotation_transform(Vec3d(0, M_PI / 2.0, 0)); + + cut(groove_object, cut_matrix_left, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + if (!is_last_groove) { + float right_cut_position = (groove_gap / 2.f) + (groove_width / 2.f) + offset_x; + + const Transform3d cut_matrix_right = translation_transform(rotation_m * (right_cut_position * Vec3d::UnitX())) * + m_cut_matrix * rotation_transform(Vec3d(0, M_PI / 2.0, 0)); + cut(groove_object, cut_matrix_right, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + } + + const Transform3d groove_translation = translation_transform(rotation_m * (offset_x * Vec3d::UnitX())); + // 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 = groove_translation * translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * + m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + + cut(groove_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 = groove_translation * translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * + m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + + cut(groove_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 = groove_translation * translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * + m_cut_matrix; + cut(groove_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 = groove_translation * translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * + m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + cut(groove_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + const Transform3d cut_matrix_angle2_tolerance = groove_translation * translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * + m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + cut(groove_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + groove_object->clear_volumes(); } - // 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) { @@ -747,9 +760,24 @@ const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Tran merge_solid_parts_inside_object(cut_object_ptrs); } - finalize(cut_object_ptrs); + finalize(cut_object_ptrs, saved_paintings); return m_model.objects; } +float Cut::calculate_groove_width (const Cut::Groove& groove, const float m_radius) +{ + // Compute same slot outer width used in preview plane + const double flap_width = is_approx(groove.flaps_angle, 0.f) ? groove.depth : groove.depth / sin(groove.flaps_angle); + const double total_flap_width = 2.0 * flap_width * cos(groove.flaps_angle); + const double slot_neck_half_width = 0.5f * (groove.width); + const double slot_mouth_half_width = 0.5 * (groove.width + total_flap_width); + const double plane_half_height = 0.5f* (1.5f * (1.5f *m_radius)); + const double flap_taper_offset = plane_half_height * tan(groove.angle); + const double slot_outer_x_max = std::max(slot_mouth_half_width + flap_taper_offset, slot_neck_half_width + flap_taper_offset); + + return float(2.0 * slot_outer_x_max); +} + } // namespace Slic3r +