From e6b3f6ccef662428a3c909c4f3769111333067f3 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sat, 9 May 2026 21:47:40 +0800 Subject: [PATCH] Add option to keep existing painting while mapping new paintings to it --- src/libslic3r/Model.cpp | 10 +++++++--- src/libslic3r/Model.hpp | 4 ++-- src/libslic3r/TriangleSelector.cpp | 7 ++++++- src/libslic3r/TriangleSelector.hpp | 4 +++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 586043bf4b..000ab997a9 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1995,7 +1995,7 @@ std::optional ModelVolume::save_painting() cons return {}; } -void ModelVolume::restore_painting(const std::optional& saved) +void ModelVolume::restore_painting(const std::optional& saved, const bool keep_existing_paint) { if (!saved) { return; @@ -2005,9 +2005,13 @@ void ModelVolume::restore_painting(const std::optionalmesh.its, src_data, mesh().its, Geometry::translation_transform(mesh().get_init_shift())); + auto result = + TriangleSelector::remap_painting(saved->mesh.its, src_data, mesh().its, Geometry::translation_transform(mesh().get_init_shift()), + keep_existing_paint ? + std::optional>{std::ref(target_facets.get_data())} : + std::optional>{}); if (!result.bitstream.empty()) - target_facets.set_data(result); + target_facets.set_data(std::move(result)); }; remap_one(saved->supported, supported_facets); remap_one(saved->seam, seam_facets); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index fbe8da7b6a..d8697adb41 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -731,7 +731,7 @@ public: void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } } void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } const TriangleSelector::TriangleSplittingData &get_data() const noexcept { return m_data; } - void set_data(const TriangleSelector::TriangleSplittingData &data) { m_data = data; this->touch(); } + void set_data(TriangleSelector::TriangleSplittingData &&data) { m_data = std::move(data); this->touch(); } bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; // BBS @@ -884,7 +884,7 @@ public: std::optional save_painting() const; // Remap painting data from previous saved source to this mesh - void restore_painting(const std::optional& saved); + void restore_painting(const std::optional& saved, bool keep_existing_paint = false); // BBS: quick access for volume extruders, 1 based mutable std::vector mmuseg_extruders; diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 19f638a160..0fb1423df4 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -2407,7 +2407,8 @@ TriangleSelector::TriangleSplittingData TriangleSelector::remap_painting( const indexed_triangle_set& source_its, const TriangleSplittingData& source_painting, const indexed_triangle_set& target_its, - const Transform3d& target_transform) + const Transform3d& target_transform, + const std::optional>& existing_painting) { TriangleSelector::TriangleSplittingData result; if (source_painting.bitstream.empty()) @@ -2477,6 +2478,10 @@ TriangleSelector::TriangleSplittingData TriangleSelector::remap_painting( // 4. For each painted face, we find the nearest target face, and apply the TriangleCursor to paint it TriangleSelector target_selector(target_mesh); + if (existing_painting) { + // Restore existing painting first, if given + target_selector.deserialize(existing_painting->get(), false); + } for (auto tri_ref : painted_triangles) { const Triangle& tri = tri_ref.get(); const Vec3f& pv0 = source_selector.m_vertices[tri.verts_idxs[0]].v; diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 412c9fb9b1..09b606b5d2 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -383,11 +383,13 @@ public: // Remap painting data from source mesh to target mesh using spatial mapping. // `target_transform` should transform the target mesh into source's coordinate space. + // If `existing_painting` is present, the result will be a combine of `existing_painting` and remapped `source_painting`. static TriangleSplittingData remap_painting( const indexed_triangle_set& source_its, const TriangleSplittingData& source_painting, const indexed_triangle_set& target_its, - const Transform3d& target_transform); + const Transform3d& target_transform, + const std::optional>& existing_painting); protected: // Triangle and info about how it's split.