From 3b20d03e1d8a29d42fd777fe87c070b78ecfe82b Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Mon, 26 Nov 2018 17:08:36 +0100
Subject: [PATCH] Cut: Preserve Z-rotation, fix upper part mesh position,
 perform custom placing instead of arrange()

---
 src/libslic3r/Model.cpp   | 92 ++++++++++++++++++++++++++-------------
 src/slic3r/GUI/Plater.cpp |  2 -
 2 files changed, 62 insertions(+), 32 deletions(-)

diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 415fd5225..bd300f74d 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -975,12 +975,6 @@ bool ModelObject::needed_repair() const
     return false;
 }
 
-template<class T> static void cut_reset_transform(T *thing) {
-    const Vec3d offset = thing->get_offset();
-    thing->set_transformation(Geometry::Transformation());
-    thing->set_offset(offset);
-}
-
 ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
 {
     // Clone the object to duplicate instances, materials etc.
@@ -1001,24 +995,25 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
         lower->input_file = "";
     }
 
-    const auto instance_matrix = instances[instance]->get_matrix(true);
-
     // Because transformations are going to be applied to meshes directly,
     // we reset transformation of all instances and volumes,
-    // except for translation, which is preserved in the transformation matrix
-    // and not applied to the mesh transform.
-    // TODO: Do the same for Z-rotation as well?
+    // except for translation and Z-rotation on instances, which are preserved
+    // in the transformation matrix and not applied to the mesh transform.
 
-    if (keep_upper) {
-        for (auto *instance : upper->instances) { cut_reset_transform(instance); }
-    }
-    if (keep_lower) {
-        for (auto *instance : lower->instances) { cut_reset_transform(instance); }
-    }
+    // const auto instance_matrix = instances[instance]->get_matrix(true);
+    const auto instance_matrix = Geometry::assemble_transform(
+        Vec3d::Zero(),  // don't apply offset
+        instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 0.0)),   // don't apply Z-rotation
+        instances[instance]->get_scaling_factor(),
+        instances[instance]->get_mirror()
+    );
+
+    // Lower part per-instance bounding boxes
+    std::vector<BoundingBoxf3> lower_bboxes { instances.size() };
 
     for (ModelVolume *volume : volumes) {
         if (! volume->is_model_part()) {
-            // don't cut modifiers
+            // Don't cut modifiers
             if (keep_upper) { upper->add_volume(*volume); }
             if (keep_lower) { lower->add_volume(*volume); }
         } else {
@@ -1027,13 +1022,23 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
             // Transform the mesh by the object transformation matrix
             const auto volume_tr = instance_matrix * volume->get_matrix(true);
             volume->mesh.transform(volume_tr);
-            cut_reset_transform(volume);
 
             // Transform z from world to object
-            const auto local_z = volume_tr * Vec3d(0.0, 0.0, z);
+            const double local_z = (volume_tr * Vec3d(0.0, 0.0, z))(2);
 
+            // Perform cut
             TriangleMeshSlicer tms(&volume->mesh);
-            tms.cut(local_z(2), &upper_mesh, &lower_mesh);
+            tms.cut(local_z, &upper_mesh, &lower_mesh);
+
+            // Move the upper mesh to down to zero in Z
+            if (keep_upper) {
+                upper_mesh.translate(0.0, 0.0, -local_z);
+            }
+
+            // Reset volume transformation except for offset
+            const Vec3d offset = volume->get_offset();
+            volume->set_transformation(Geometry::Transformation());
+            volume->set_offset(offset);
 
             if (keep_upper) {
                 upper_mesh.repair();
@@ -1055,16 +1060,15 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
                 vol->name           = volume->name;
                 vol->config         = volume->config;
                 vol->set_material(volume->material_id(), *volume->material());
-            }
-        }
-    }
 
-    if (keep_lower && rotate_lower) {
-        for (auto *instance : lower->instances) {
-            Geometry::Transformation tr;
-            tr.set_offset(instance->get_offset());
-            tr.set_rotation({Geometry::deg2rad(180.0), 0.0, 0.0});
-            instance->set_transformation(tr);
+                // Compute the lower part instances' bounding boxes to figure out where to place
+                // the upper part
+                if (keep_upper) {
+                    for (size_t i = 0; i < instances.size(); i++) {
+                        lower_bboxes[i].merge(instances[i]->transform_mesh_bounding_box(lower_mesh, true));
+                    }
+                }
+            }
         }
     }
 
@@ -1073,11 +1077,39 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
     if (keep_upper && upper->volumes.size() > 0) {
         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 &instance = upper->instances[i];
+            const Vec3d offset = instance->get_offset();
+            const double rot_z = instance->get_rotation()(2);
+            const Vec3d displace = lower_bboxes[i].size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
+
+            instance->set_transformation(Geometry::Transformation());
+            instance->set_offset(offset + displace);
+            instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
+        }
+
         res.push_back(upper);
     }
     if (keep_lower && lower->volumes.size() > 0) {
         lower->invalidate_bounding_box();
         lower->center_around_origin();
+
+        // Reset instance transformation except offset and Z-rotation
+        for (auto *instance : lower->instances) {
+            const Vec3d offset = instance->get_offset();
+            const double rot_z = instance->get_rotation()(2);
+            instance->set_transformation(Geometry::Transformation());
+
+            if (rotate_lower) {
+                instance->set_rotation({Geometry::deg2rad(180.0), 0.0, 0.0});
+            }
+
+            instance->set_offset(offset);
+            instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
+        }
+
         res.push_back(lower);
     }
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 5e79958fa..4afc3a26c 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -2483,8 +2483,6 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
 
     remove(obj_idx);
     p->load_model_objects(new_objects);
-
-    p->arrange();
 }
 
 void Plater::export_gcode(fs::path output_path)