ENH: recover CutToParts for cut

Change-Id: I5d7fb089c5d2ea9d36fce3d70ec4da896d601940
(cherry picked from commit e0232b21ebf5a37bedee1c213d1c21890634c563)
This commit is contained in:
zhimin.zeng 2023-03-20 17:14:41 +08:00 committed by Lane.Wei
parent b1041a5de8
commit 518cd4ce27
4 changed files with 174 additions and 164 deletions

View file

@ -916,6 +916,25 @@ bool Model::is_mm_painted() const
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); });
}
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART)
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume* vol = object->add_volume(mesh);
vol->set_type(type);
vol->name = src_volume->name + suffix;
// Don't copy the config's ID.
vol->config.assign_config(src_volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != src_volume->config.id());
vol->set_material(src_volume->material_id(), *src_volume->material());
vol->cut_info = src_volume->cut_info;
}
ModelObject::~ModelObject()
{
this->clear_volumes();
@ -1706,7 +1725,9 @@ void ModelObject::synchronize_model_after_cut()
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
{
// we don't save cut information, if result will not contains all parts of initial object
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower))
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) ||
!attributes.has(ModelObjectCutAttribute::KeepLower) ||
attributes.has(ModelObjectCutAttribute::InvalidateCutInfo))
return;
if (cut_id.id().invalid())
@ -1768,6 +1789,8 @@ Transform3d ModelObject::calculate_cut_plane_inverse_matrix(const std::array<Vec
void ModelObject::process_connector_cut(
ModelVolume *volume,
const Transform3d & instance_matrix,
const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject *upper, ModelObject *lower,
std::vector<ModelObject *> &dowels,
@ -1780,38 +1803,53 @@ void ModelObject::process_connector_cut(
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume *vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
vol->apply_tolerance();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume *vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
if (volume->cut_info.connector_type == CutConnectorType::Dowel)
if (volume->cut_info.connector_type != CutConnectorType::Dowel) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume *vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
vol->apply_tolerance();
else
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume *vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
}
if (volume->cut_info.connector_type == CutConnectorType::Dowel && attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject *dowel{nullptr};
// Clone the object to duplicate instances, materials etc.
clone_for_cut(&dowel);
else {
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject *dowel{nullptr};
// Clone the object to duplicate instances, materials etc.
clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume *vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// add one more solid part same as connector if this connector is a dowel
ModelVolume *vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
dowels.push_back(dowel);
dowels.push_back(dowel);
}
// Cut the dowel
volume->apply_tolerance();
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// add small Z offset to better preview
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
// Add cut parts to the related objects
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
}
}
@ -1829,6 +1867,11 @@ void ModelObject::process_modifier_cut(
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(volume_matrix));
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
upper->add_volume(*volume);
return;
}
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
@ -1838,52 +1881,59 @@ void ModelObject::process_modifier_cut(
lower->add_volume(*volume);
}
void ModelObject::process_volume_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
ModelObjectCutAttributes attributes,
TriangleMesh & upper_mesh,
TriangleMesh & lower_mesh)
{
const auto volume_matrix = volume->get_matrix();
using namespace Geometry;
const Geometry::Transformation cut_transformation = Geometry::Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_matrix(true, false, true, true).inverse()
* translation_transform(-1 * cut_transformation.get_offset());
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
void ModelObject::process_solid_part_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
const std::array<Vec3d, 4> &plane_points,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower,
Vec3d & local_displace)
{
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume->get_matrix(), true);
volume->reset_mesh();
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, plane_points, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower_mesh = TriangleMesh(lower_its);
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
return;
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) {
ModelVolume *vol = upper->add_volume(upper_mesh);
vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_upper"; // BBS
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
vol->cut_info = volume->cut_info;
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
ModelVolume *vol = lower->add_volume(lower_mesh);
vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_lower"; // BBS
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
vol->cut_info = volume->cut_info;
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
// The upper part displacement is set to half of the lower part bounding box
@ -1957,29 +2007,13 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
// apply cut attributes for object
apply_cut_attributes(attributes);
// Clone the object to duplicate instances, materials etc.
bool keep_upper = attributes.has(ModelObjectCutAttribute::KeepUpper);
bool keep_lower = attributes.has(ModelObjectCutAttribute::KeepLower);
ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr;
ModelObject* lower = keep_lower ? ModelObject::new_clone(*this) : nullptr;
ModelObject* upper{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
clone_for_cut(&upper);
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
upper->set_model(nullptr);
upper->sla_support_points.clear();
upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes();
upper->input_file.clear();
}
if (keep_lower && lower != upper) {
lower->set_model(nullptr);
lower->sla_support_points.clear();
lower->sla_drain_holes.clear();
lower->sla_points_status = sla::PointsStatus::NoPoints;
lower->clear_volumes();
lower->input_file.clear();
}
ModelObject* lower{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::CutToParts))
clone_for_cut(&lower);
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
@ -1999,12 +2033,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
for (Vec3d& point : plane_points) {
point -= instances[instance]->get_offset();
}
Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
Transform3d cut_matrix = inverse_cut_matrix.inverse();
std::vector<ModelObject *> dowels;
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
for (ModelVolume *volume : volumes) {
const auto volume_matrix = volume->get_matrix();
@ -2016,96 +2052,52 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
if (volume->cut_info.is_processed) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
//Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
}
else {
process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace);
}
}
else if (! volume->mesh().empty()) {
process_solid_part_cut(volume, instance_matrix, plane_points, attributes, upper, lower, local_displace);
process_solid_part_cut(volume, instance_matrix, cut_matrix, plane_points, attributes, upper, lower, local_displace);
}
}
ModelObjectPtrs res;
Transform3d cut_matrix = calculate_cut_plane_inverse_matrix(plane_points).inverse();
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
invalidate_translations(upper, instances[instance]);
reset_instance_transformation(upper, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper),
local_displace);
if (attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) {
reset_instance_transformation(upper, instance, cut_matrix);
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
invalidate_translations(lower, instances[instance]);
else {
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
invalidate_translations(upper, instances[instance]);
reset_instance_transformation(lower, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace);
res.push_back(lower);
}
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
invalidate_translations(lower, instances[instance]);
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
auto invalidate_translations = [](ModelObject *object, const ModelInstance *src_instance) {
if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) {
object->center_around_origin();
object->translate_instances(-object->origin_translation);
object->origin_translation = Vec3d::Zero();
} else {
object->invalidate_bounding_box();
object->center_around_origin();
reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
invalidate_translations(dowel, instances[instance]);
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel);
}
};
auto reset_instance_transformation = [](ModelObject *object, size_t src_instance_idx, const Transform3d &cut_matrix,
bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero()) {
using namespace Geometry;
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < object->instances.size(); ++i) {
auto & obj_instance = object->instances[i];
const Vec3d offset = obj_instance->get_offset();
const double rot_z = obj_instance->get_rotation().z();
obj_instance->set_transformation(Transformation());
const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() : rotation_transform(obj_instance->get_rotation()) * local_displace;
obj_instance->set_offset(offset + displace);
Vec3d rotation = Vec3d::Zero();
if (!flip && !place_on_cut) {
if (i != src_instance_idx) rotation[Z] = rot_z;
} else {
Transform3d rotation_matrix = Transform3d::Identity();
if (flip)
rotation_matrix = rotation_transform(PI * Vec3d::UnitX());
if (place_on_cut)
rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_matrix(true, false, true, true).inverse();
if (i != src_instance_idx)
rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix;
rotation = Transformation(rotation_matrix).get_rotation();
}
obj_instance->set_rotation(rotation);
}
};
for (auto dowel : dowels) {
invalidate_translations(dowel, instances[instance]);
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel);
}
}
@ -2552,7 +2544,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume
// %print_volume.min.x() %print_volume.min.y() %print_volume.min.z()%print_volume.max.x() %print_volume.max.y() %print_volume.max.z();
for (ModelInstance* model_instance : this->instances) {
unsigned int inside_outside = 0;
for (const ModelVolume* vol : this->volumes)
for (const ModelVolume *vol : this->volumes) {
if (vol->is_model_part()) {
//BBS: add bounding box empty check logic, for some volume is empty before split(it will be removed after split to object)
BoundingBoxf3 bb = vol->get_convex_hull().bounding_box();
@ -2578,6 +2570,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume
// Volume colliding with the build volume.
inside_outside |= INSIDE | OUTSIDE;
}
}
model_instance->print_volume_state =
inside_outside == (INSIDE | OUTSIDE) ? ModelInstancePVS_Partly_Outside :
inside_outside == INSIDE ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside;