Keep painting after cut (#13472)

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)


This can be enabled in Preference->Developer->Keep painted feature after
mesh change.

<img width="731" height="633" alt="init"
src="https://github.com/user-attachments/assets/8b195486-538e-4eda-9e77-bfdf1a794306"
/>


TODO:
- [ ] Bug fixes
- [ ] Make it faster
- Keep painting after other mesh operations such as reload from
disk/simplify/boolean operation etc:
  - [x] Planar cut
  - [x] Dovetail cut
  - [x] Cut with part assigned to other side
  - [x] Split to parts
  - [x] Split to objects
  - [x] Mesh boolean gizmo
  - [x] Mesh boolean in right click
- [x] Reload from disk/replace stl (won't work well if mesh changed too
much)
  - [x] Fix model
- [x] Simplify/smooth (this two won't work well due to too much mesh
changes)
- [x] Add options in settings since I think this will be experimental
for a long time until being tested by a lot of ppl
This commit is contained in:
Noisyfox
2026-05-17 14:38:37 +08:00
committed by GitHub
18 changed files with 665 additions and 92 deletions

View File

@@ -7627,7 +7627,7 @@ void Plater::priv::split_object(int obj_idx, bool auto_drop /* = true */)
wxBusyCursor wait;
ModelObjectPtrs new_objects;
current_model_object->split(&new_objects);
current_model_object->split(&new_objects, wxGetApp().app_config->get_bool("keep_painting"));
if (new_objects.size() == 1)
// #ysFIXME use notification
Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split."));
@@ -8375,10 +8375,20 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
new_volume->convert_from_imperial_units();
else if (old_volume->source.is_converted_from_meters)
new_volume->convert_from_meters();
new_volume->supported_facets.assign(old_volume->supported_facets);
new_volume->seam_facets.assign(old_volume->seam_facets);
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
new_volume->fuzzy_skin_facets.assign(old_volume->fuzzy_skin_facets);
if (wxGetApp().app_config->get_bool("keep_painting")) {
// Proper paint remapping
auto saved_painting = old_volume->save_painting();
if (saved_painting) {
saved_painting->mesh.transform(Geometry::translation_transform(new_volume->mesh().get_init_shift()));
new_volume->restore_painting(saved_painting);
}
} else {
// Won't work well if mesh changed, but kept for old behavior
new_volume->supported_facets.assign(old_volume->supported_facets);
new_volume->seam_facets.assign(old_volume->seam_facets);
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
new_volume->fuzzy_skin_facets.assign(old_volume->fuzzy_skin_facets);
}
std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
if (!sinking)
@@ -8904,6 +8914,16 @@ void Plater::priv::reload_from_disk()
new_volume->convert_from_imperial_units();
else if (old_volume->source.is_converted_from_meters)
new_volume->convert_from_meters();
// Remap paint
if (wxGetApp().app_config->get_bool("keep_painting")) {
auto saved_painting = old_volume->save_painting();
if (saved_painting) {
saved_painting->mesh.transform(Geometry::translation_transform(new_volume->mesh().get_init_shift()));
new_volume->restore_painting(saved_painting);
}
}
std::swap(old_model_object->volumes[vol_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
if (!sinking) old_model_object->ensure_on_bed();
@@ -15111,7 +15131,7 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
}
if (instance_id == -1) {
TriangleMesh vols_mesh(mesh);
TriangleMesh vols_mesh(std::move(mesh));
mesh = TriangleMesh();
for (const ModelInstance* i : mo.instances) {
TriangleMesh m = vols_mesh;