Cut: Process cut in respect to the selected parts

This commit is contained in:
YuSanka 2023-02-28 17:38:30 +01:00 committed by Lukas Matena
parent e5b66f125f
commit 1a7f46001b
6 changed files with 145 additions and 86 deletions

View File

@ -1353,6 +1353,7 @@ void ModelObject::synchronize_model_after_cut()
if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id)) if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id))
obj->cut_id.copy(this->cut_id); obj->cut_id.copy(this->cut_id);
} }
this->invalidate_cut();
} }
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
@ -1378,7 +1379,7 @@ void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
void ModelObject::clone_for_cut(ModelObject** obj) void ModelObject::clone_for_cut(ModelObject** obj)
{ {
(*obj) = ModelObject::new_clone(*this); (*obj) = ModelObject::new_clone(*this);
(*obj)->set_model(nullptr); (*obj)->set_model(this->get_model());
(*obj)->sla_support_points.clear(); (*obj)->sla_support_points.clear();
(*obj)->sla_drain_holes.clear(); (*obj)->sla_drain_holes.clear();
(*obj)->sla_points_status = sla::PointsStatus::NoPoints; (*obj)->sla_points_status = sla::PointsStatus::NoPoints;
@ -1461,7 +1462,7 @@ static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelV
void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace) std::vector<ModelObject*>& dowels)
{ {
assert(volume->cut_info.is_connector); assert(volume->cut_info.is_connector);
volume->cut_info.set_processed(); volume->cut_info.set_processed();
@ -1497,9 +1498,6 @@ void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d&
vol->set_rotation(Vec3d::Zero()); vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0); 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); dowels.push_back(dowel);
} }
@ -1566,7 +1564,7 @@ void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& ins
lower_mesh = TriangleMesh(lower_its); lower_mesh = TriangleMesh(lower_its);
} }
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, 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) ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
{ {
// Perform cut // Perform cut
TriangleMesh upper_mesh, lower_mesh; TriangleMesh upper_mesh, lower_mesh;
@ -1583,31 +1581,12 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d&
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix); add_cut_volume(upper_mesh, upper, volume, cut_matrix);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty())
add_cut_volume(lower_mesh, lower, volume, cut_matrix); 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) void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
{ bool place_on_cut/* = false*/, bool flip/* = false*/)
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; using namespace Geometry;
@ -1615,15 +1594,8 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan
for (size_t i = 0; i < object->instances.size(); ++i) { for (size_t i = 0; i < object->instances.size(); ++i) {
auto& obj_instance = object->instances[i]; auto& obj_instance = object->instances[i];
const Vec3d offset = obj_instance->get_offset();
const double rot_z = obj_instance->get_rotation().z(); const double rot_z = obj_instance->get_rotation().z();
obj_instance->set_transformation(Transformation());
const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() :
rotation_transform(obj_instance->get_rotation()) * local_displace;
obj_instance->set_offset(offset + displace);
Vec3d rotation = Vec3d::Zero(); Vec3d rotation = Vec3d::Zero();
if (!flip && !place_on_cut) { if (!flip && !place_on_cut) {
if ( i != src_instance_idx) if ( i != src_instance_idx)
@ -1680,10 +1652,6 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
const Transformation cut_transformation = Transformation(cut_matrix); 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()); const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_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();
for (ModelVolume* volume : volumes) { for (ModelVolume* volume : volumes) {
volume->reset_extra_facets(); volume->reset_extra_facets();
@ -1691,10 +1659,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
if (volume->cut_info.is_processed) if (volume->cut_info.is_processed)
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
else else
process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace); process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels);
} }
else if (!volume->mesh().empty()) else if (!volume->mesh().empty())
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace); process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower);
} }
// Post-process cut parts // Post-process cut parts
@ -1707,31 +1675,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
} }
else { else {
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
invalidate_translations(upper, instances[instance]);
reset_instance_transformation(upper, instance, cut_matrix, reset_instance_transformation(upper, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper), attributes.has(ModelObjectCutAttribute::FlipUpper));
local_displace);
res.push_back(upper); res.push_back(upper);
} }
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
invalidate_translations(lower, instances[instance]);
reset_instance_transformation(lower, instance, cut_matrix, reset_instance_transformation(lower, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower); res.push_back(lower);
} }
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) { for (auto dowel : dowels) {
invalidate_translations(dowel, instances[instance]); reset_instance_transformation(dowel, instance, Transform3d::Identity());
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name; dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel); res.push_back(dowel);
} }

View File

@ -472,13 +472,17 @@ public:
void clone_for_cut(ModelObject **obj); void clone_for_cut(ModelObject **obj);
void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace); std::vector<ModelObject*>& dowels);
void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh); ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh);
void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace); ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
bool place_on_cut = false, bool flip = false);
ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes); ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs*new_objects); void split(ModelObjectPtrs*new_objects);
void merge(); void merge();

View File

@ -892,6 +892,7 @@ void GLGizmoCut3D::on_set_state()
// initiate archived values // initiate archived values
m_ar_plane_center = m_plane_center; m_ar_plane_center = m_plane_center;
m_start_dragging_m = m_rotation_m; m_start_dragging_m = m_rotation_m;
reset_cut_by_contours();
m_parent.request_extra_frame(); m_parent.request_extra_frame();
} }
@ -1116,6 +1117,8 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data)
projection = m_snap_step * std::round(projection / m_snap_step); projection = m_snap_step * std::round(projection / m_snap_step);
const Vec3d shift = starting_vec * projection; const Vec3d shift = starting_vec * projection;
if (shift != Vec3d::Zero())
reset_cut_by_contours();
// move cut plane center // move cut plane center
set_center(m_plane_center + shift, true); set_center(m_plane_center + shift, true);
@ -1153,6 +1156,9 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
if (m_hover_id == X) if (m_hover_id == X)
theta += 0.5 * PI; theta += 0.5 * PI;
if (!is_approx(theta, 0.0))
reset_cut_by_contours();
Vec3d rotation = Vec3d::Zero(); Vec3d rotation = Vec3d::Zero();
rotation[m_hover_id] = theta; rotation[m_hover_id] = theta;
@ -1187,14 +1193,10 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data)
{ {
if (m_hover_id < 0) if (m_hover_id < 0)
return; return;
if (m_hover_id == Z || m_hover_id == CutPlane) { if (m_hover_id == Z || m_hover_id == CutPlane)
dragging_grabber_z(data); dragging_grabber_z(data);
reset_cut_by_contours(); else if (m_hover_id == X || m_hover_id == Y)
}
else if (m_hover_id == X || m_hover_id == Y) {
dragging_grabber_xy(data); dragging_grabber_xy(data);
reset_cut_by_contours();
}
else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual)
dragging_connector(data); dragging_connector(data);
} }
@ -1301,6 +1303,7 @@ void GLGizmoCut3D::update_bb()
m_bounding_box = box; m_bounding_box = box;
invalidate_cut_plane(); invalidate_cut_plane();
reset_cut_by_contours();
m_max_pos = box.max; m_max_pos = box.max;
m_min_pos = box.min; m_min_pos = box.min;
@ -1402,11 +1405,14 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal)
// FIXME: Cache the transforms. // FIXME: Cache the transforms.
const Vec3d inst_offset = model_object->instances[0]->get_offset(); const Vec3d inst_offset = model_object->instances[instance_idx]->get_offset();
const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset);
const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05;
for (size_t id=0; id<parts.size(); ++id) { for (size_t id=0; id<parts.size(); ++id) {
if (normal && camera.get_dir_forward().dot(*normal) < 0 && parts[id].selected) if (normal && (( is_looking_forward && parts[id].selected) ||
(!is_looking_forward && !parts[id].selected) ) )
continue; continue;
const Vec3d volume_offset = model_object->volumes[id]->get_offset(); const Vec3d volume_offset = model_object->volumes[id]->get_offset();
shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset));
@ -1419,7 +1425,6 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal)
} }
} }
void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
{ {
// FIXME: Cache the transforms. // FIXME: Cache the transforms.
@ -1430,7 +1435,7 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
for (size_t id=0; id<parts.size(); ++id) { for (size_t id=0; id<parts.size(); ++id) {
const Vec3d volume_offset = model_object->volumes[id]->get_offset(); const Vec3d volume_offset = model_object->volumes[id]->get_offset();
Transform3d tr = model_object->instances.front()->get_matrix() * model_object->volumes[id]->get_matrix(); Transform3d tr = model_object->instances[instance_idx]->get_matrix() * model_object->volumes[id]->get_matrix();
if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) {
parts[id].selected = ! parts[id].selected; parts[id].selected = ! parts[id].selected;
return; return;
@ -1438,6 +1443,12 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
} }
} }
void GLGizmoCut3D::PartSelection::turn_over_selection()
{
for (Part& part : parts)
part.selected = !part.selected;
}
void GLGizmoCut3D::on_render() void GLGizmoCut3D::on_render()
{ {
if (m_state == On) { if (m_state == On) {
@ -1708,12 +1719,14 @@ void GLGizmoCut3D::flip_cut_plane()
m_start_dragging_m = m_rotation_m; m_start_dragging_m = m_rotation_m;
update_clipper(); update_clipper();
m_part_selection.turn_over_selection();
} }
GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal) GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, int instance_idx_in, const Vec3d& center, const Vec3d& normal)
{ {
model_object = mo; // FIXME: Ownership. model_object = mo; // FIXME: Ownership.
instance_idx = instance_idx_in;
const ModelVolumePtrs& volumes = mo->volumes; const ModelVolumePtrs& volumes = mo->volumes;
@ -1730,7 +1743,7 @@ GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center,
parts.back().glmodel.init_from(volume->mesh()); parts.back().glmodel.init_from(volume->mesh());
// Now check whether this part is below or above the plane. // Now check whether this part is below or above the plane.
Transform3d tr = (model_object->instances.front()->get_matrix() * volume->get_matrix()).inverse(); Transform3d tr = (model_object->instances[instance_idx]->get_matrix() * volume->get_matrix()).inverse();
Vec3f pos = (tr * center).cast<float>(); Vec3f pos = (tr * center).cast<float>();
Vec3f norm = (tr.linear().inverse().transpose() * normal).cast<float>(); Vec3f norm = (tr.linear().inverse().transpose() * normal).cast<float>();
for (const Vec3f& v : volume->mesh().its.vertices) { for (const Vec3f& v : volume->mesh().its.vertices) {
@ -1749,7 +1762,10 @@ GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center,
void GLGizmoCut3D::reset_cut_by_contours() void GLGizmoCut3D::reset_cut_by_contours()
{ {
m_part_selection = PartSelection(); m_part_selection = PartSelection();
m_parent.toggle_model_objects_visibility(true);
const Selection& selection = m_parent.get_selection();
const ModelObjectPtrs& model_objects = selection.get_model()->objects;
m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx());
} }
void GLGizmoCut3D::process_contours() void GLGizmoCut3D::process_contours()
@ -1763,14 +1779,14 @@ void GLGizmoCut3D::process_contours()
const int instance_idx = selection.get_instance_idx(); const int instance_idx = selection.get_instance_idx();
const int object_idx = selection.get_object_idx(); const int object_idx = selection.get_object_idx();
ModelObjectPtrs moptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), m_cut_part_ptrs.clear();
m_cut_part_ptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection),
ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepUpper |
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepLower |
ModelObjectCutAttribute::KeepAsParts | ModelObjectCutAttribute::KeepAsParts);
ModelObjectCutAttribute::InvalidateCutInfo); assert(m_cut_part_ptrs.size() == 1);
assert(moptrs.size() == 1); m_part_selection = PartSelection(m_cut_part_ptrs.front(), instance_idx, m_plane_center, m_cut_normal);
m_part_selection = PartSelection(moptrs.front(), m_plane_center, m_cut_normal);
m_parent.toggle_model_objects_visibility(false); m_parent.toggle_model_objects_visibility(false);
} }
@ -1919,7 +1935,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
ImGuiWrapper::text(_L("Cut result") + ": "); ImGuiWrapper::text(_L("Cut result") + ": ");
add_vertical_scaled_interval(0.5f); add_vertical_scaled_interval(0.5f);
m_imgui->disabled_begin(has_connectors || m_keep_as_parts); m_imgui->disabled_begin(has_connectors || m_keep_as_parts || m_part_selection.valid);
render_part_name("A", m_keep_upper, m_imgui->to_ImU32(UPPER_PART_COLOR)); render_part_name("A", m_keep_upper, m_imgui->to_ImU32(UPPER_PART_COLOR));
ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x); ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x);
render_part_name("B", m_keep_lower, m_imgui->to_ImU32(LOWER_PART_COLOR)); render_part_name("B", m_keep_lower, m_imgui->to_ImU32(LOWER_PART_COLOR));
@ -1938,6 +1954,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
m_imgui->disabled_begin(has_connectors); m_imgui->disabled_begin(has_connectors);
ImGuiWrapper::text(_L("Cut into") + ":"); ImGuiWrapper::text(_L("Cut into") + ":");
if (m_part_selection.valid)
m_keep_as_parts = false;
add_horizontal_scaled_interval(1.2f); add_horizontal_scaled_interval(1.2f);
// TRN CutGizmo: RadioButton Cut into ... // TRN CutGizmo: RadioButton Cut into ...
if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts)) if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts))
@ -2322,21 +2341,88 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
{ {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane"));
const bool cut_by_contour = m_part_selection.valid && !m_cut_part_ptrs.empty();
ModelObject* cut_mo = cut_by_contour ? m_cut_part_ptrs.front() : nullptr;
if (cut_mo)
cut_mo->cut_connectors = mo->cut_connectors;
bool create_dowels_as_separate_object = false; bool create_dowels_as_separate_object = false;
const bool has_connectors = !mo->cut_connectors.empty(); const bool has_connectors = !mo->cut_connectors.empty();
// update connectors pos as offset of its center before cut performing // update connectors pos as offset of its center before cut performing
apply_connectors_in_model(mo, create_dowels_as_separate_object); apply_connectors_in_model(cut_mo ? cut_mo : mo , create_dowels_as_separate_object);
plater->cut(object_idx, instance_idx, get_cut_matrix(selection), wxBusyCursor wait;
only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | const Transform3d cut_matrix = get_cut_matrix(selection);
only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) |
only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | ModelObjectCutAttributes attributes = only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(has_connectors ? false : m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) |
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) |
only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) | only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) |
only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo)); only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) |
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) |
only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) |
only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo);
ModelObjectPtrs cut_object_ptrs;
if (cut_by_contour) {
// apply cut attributes for object
cut_mo->apply_cut_attributes(ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper |
only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels));
// 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);
auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower, bool invalidate_cut = true) {
if (!upper->volumes.empty()) {
ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper);
if (invalidate_cut)
upper->invalidate_cut();
cut_objects.push_back(upper);
}
if (!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);
if (invalidate_cut)
lower->invalidate_cut();
cut_objects.push_back(lower);
}
};
const size_t cut_parts_cnt = m_part_selection.parts.size();
for (size_t id = 0; id < cut_parts_cnt; ++id)
(m_part_selection.parts[id].selected ? upper : lower)->add_volume(*(cut_mo->volumes[id]));
ModelVolumePtrs& volumes = cut_mo->volumes;
if (volumes.size() == cut_parts_cnt)
add_cut_objects(cut_object_ptrs, upper, lower);
else if (volumes.size() > cut_parts_cnt) {
for (size_t id = 0; id < cut_parts_cnt; id++)
delete *(volumes.begin() + id);
volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt);
const auto cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes);
assert(create_dowels_as_separate_object ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2);
for (const ModelVolume* volume : cut_connectors_obj[0]->volumes)
upper->add_volume(*volume, volume->type());
for (const ModelVolume* volume : cut_connectors_obj[1]->volumes)
lower->add_volume(*volume, volume->type());
add_cut_objects(cut_object_ptrs, upper, lower, false);
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]);
}
}
else
cut_object_ptrs = mo->cut(instance_idx, cut_matrix, attributes);
plater->cut(object_idx, cut_object_ptrs);
} }
} }

View File

@ -139,10 +139,11 @@ class GLGizmoCut3D : public GLGizmoBase
struct PartSelection { struct PartSelection {
PartSelection() = default; PartSelection() = default;
PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal); PartSelection(ModelObject* mo, int instance_idx, const Vec3d& center, const Vec3d& normal);
void render(const Vec3d* normal = nullptr); void render(const Vec3d* normal = nullptr);
void toggle_selection(const Vec2d& mouse_pos); void toggle_selection(const Vec2d& mouse_pos);
void turn_over_selection();
struct Part { struct Part {
GLModel glmodel; GLModel glmodel;
@ -151,11 +152,13 @@ class GLGizmoCut3D : public GLGizmoBase
bool upper; bool upper;
}; };
ModelObject* model_object; // FIXME: Ownership ! ModelObject* model_object; // FIXME: Ownership !
int instance_idx;
std::vector<Part> parts; std::vector<Part> parts;
bool valid = false; bool valid = false;
}; };
PartSelection m_part_selection; PartSelection m_part_selection;
ModelObjectPtrs m_cut_part_ptrs;
bool m_show_shortcuts{ false }; bool m_show_shortcuts{ false };
std::vector<std::pair<wxString, wxString>> m_shortcuts; std::vector<std::pair<wxString, wxString>> m_shortcuts;

View File

@ -6266,7 +6266,11 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat
wxBusyCursor wait; wxBusyCursor wait;
const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); 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)
{
model().delete_object(obj_idx); model().delete_object(obj_idx);
sidebar().obj_list()->delete_object_from_list(obj_idx); sidebar().obj_list()->delete_object_from_list(obj_idx);
@ -6284,6 +6288,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat
size_t last_id = p->model.objects.size() - 1; size_t last_id = p->model.objects.size() - 1;
for (size_t i = 0; i < new_objects.size(); ++i) for (size_t i = 0; i < new_objects.size(); ++i)
selection.add_object((unsigned int)(last_id - i), i == 0); selection.add_object((unsigned int)(last_id - i), i == 0);
arrange();
} }
void Plater::export_gcode(bool prefer_removable) void Plater::export_gcode(bool prefer_removable)

View File

@ -260,6 +260,7 @@ public:
void toggle_layers_editing(bool enable); 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 obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes);
void cut(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
void export_gcode(bool prefer_removable); void export_gcode(bool prefer_removable);
void export_stl_obj(bool extended = false, bool selection_only = false); void export_stl_obj(bool extended = false, bool selection_only = false);