Don't remap paint unless it's the final cut

This commit is contained in:
Noisyfox
2026-05-05 17:08:53 +08:00
parent 4b489339c6
commit 01e0013b16
4 changed files with 27 additions and 26 deletions

View File

@@ -14,9 +14,9 @@ namespace Slic3r {
using namespace Geometry; using namespace Geometry;
// Saved painting data for remapping after mesh cutting. // Saved painting data for remapping after mesh change.
struct SavedPainting { struct SavedPainting {
indexed_triangle_set its; // Original mesh transformed to cut space indexed_triangle_set its; // Original mesh
TriangleSelector::TriangleSplittingData supported; TriangleSelector::TriangleSplittingData supported;
TriangleSelector::TriangleSplittingData seam; TriangleSelector::TriangleSplittingData seam;
TriangleSelector::TriangleSplittingData mmu; TriangleSelector::TriangleSplittingData mmu;
@@ -24,21 +24,24 @@ struct SavedPainting {
}; };
// Remap painting data from saved source to a cut result mesh, and set on a volume. // Remap painting data from saved source to a cut result mesh, and set on a volume.
static void remap_and_set_painting(ModelVolume* vol, const indexed_triangle_set& cut_its, static void remap_and_set_painting(ModelVolume* vol, const SavedPainting* saved)
const SavedPainting& saved)
{ {
if (!saved) {
return;
}
auto remap_one = [&](const TriangleSelector::TriangleSplittingData& src_data, auto remap_one = [&](const TriangleSelector::TriangleSplittingData& src_data,
FacetsAnnotation& target_facets) { FacetsAnnotation& target_facets) {
if (src_data.bitstream.empty()) if (src_data.bitstream.empty())
return; return;
auto result = TriangleSelector::remap_painting(saved.its, src_data, cut_its); auto result = TriangleSelector::remap_painting(saved->its, src_data, vol->mesh().its);
if (!result.bitstream.empty()) if (!result.bitstream.empty())
target_facets.set_data(result); target_facets.set_data(result);
}; };
remap_one(saved.supported, vol->supported_facets); remap_one(saved->supported, vol->supported_facets);
remap_one(saved.seam, vol->seam_facets); remap_one(saved->seam, vol->seam_facets);
remap_one(saved.mmu, vol->mmu_segmentation_facets); remap_one(saved->mmu, vol->mmu_segmentation_facets);
remap_one(saved.fuzzy, vol->fuzzy_skin_facets); remap_one(saved->fuzzy, vol->fuzzy_skin_facets);
} }
static void apply_tolerance(ModelVolume* vol) static void apply_tolerance(ModelVolume* vol)
@@ -217,28 +220,26 @@ static void process_solid_part_cut(ModelVolume* volume, const Transform3d& insta
// Add required cut parts to the objects // Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); if (!upper_mesh.empty()) {
if (saved_painting && !upper_mesh.empty()) add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
remap_and_set_painting(upper->volumes.back(), upper_mesh.its, *saved_painting); remap_and_set_painting(upper->volumes.back(), saved_painting);
}
if (!lower_mesh.empty()) { if (!lower_mesh.empty()) {
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
if (saved_painting)
remap_and_set_painting(upper->volumes.back(), lower_mesh.its, *saved_painting);
upper->volumes.back()->cut_info.is_from_upper = false; upper->volumes.back()->cut_info.is_from_upper = false;
remap_and_set_painting(upper->volumes.back(), saved_painting);
} }
return; return;
} }
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix); add_cut_volume(upper_mesh, upper, volume, cut_matrix);
if (saved_painting && !upper_mesh.empty()) remap_and_set_painting(upper->volumes.back(), saved_painting);
remap_and_set_painting(upper->volumes.back(), upper_mesh.its, *saved_painting);
} }
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);
if (saved_painting) remap_and_set_painting(lower->volumes.back(), saved_painting);
remap_and_set_painting(lower->volumes.back(), lower_mesh.its, *saved_painting);
} }
} }
@@ -363,21 +364,19 @@ const ModelObjectPtrs& Cut::perform_with_plane()
// Save painting data before reset_extra_facets() discards it. // Save painting data before reset_extra_facets() discards it.
// Only for model parts that will be cut (not modifiers/connectors). // Only for model parts that will be cut (not modifiers/connectors).
std::optional<SavedPainting> saved_painting; std::optional<SavedPainting> saved_painting;
if (volume->is_model_part() && !volume->mesh().empty()) { if (m_attributes.has(ModelObjectCutAttribute::KeepPaint) && volume->is_any_painted() && volume->is_model_part() && !volume->mesh().empty()) {
SavedPainting sp;
// Get mesh in cut space (same transform as process_volume_cut applies) // Get mesh in cut space (same transform as process_volume_cut applies)
TriangleMesh mesh(volume->mesh()); TriangleMesh mesh(volume->mesh());
const auto volume_matrix = volume->get_matrix(); const auto volume_matrix = volume->get_matrix();
mesh.transform(inverse_cut_matrix * instance_matrix * volume_matrix, true); mesh.transform(inverse_cut_matrix * instance_matrix * volume_matrix, true);
mesh.transform(m_cut_matrix); mesh.transform(m_cut_matrix);
SavedPainting sp;
sp.its = std::move(mesh.its); sp.its = std::move(mesh.its);
sp.supported = volume->supported_facets.get_data(); sp.supported = volume->supported_facets.get_data();
sp.seam = volume->seam_facets.get_data(); sp.seam = volume->seam_facets.get_data();
sp.mmu = volume->mmu_segmentation_facets.get_data(); sp.mmu = volume->mmu_segmentation_facets.get_data();
sp.fuzzy = volume->fuzzy_skin_facets.get_data(); sp.fuzzy = volume->fuzzy_skin_facets.get_data();
if (!sp.supported.bitstream.empty() || !sp.seam.bitstream.empty() || saved_painting = std::move(sp);
!sp.mmu.bitstream.empty() || !sp.fuzzy.bitstream.empty())
saved_painting = std::move(sp);
} }
volume->reset_extra_facets(); volume->reset_extra_facets();

View File

@@ -11,7 +11,7 @@ namespace Slic3r {
using ModelObjectPtrs = std::vector<ModelObject*>; using ModelObjectPtrs = std::vector<ModelObject*>;
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo }; enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo, KeepPaint };
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>; using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);

View File

@@ -1005,6 +1005,7 @@ public:
bool is_seam_painted() const { return !this->seam_facets.empty(); } bool is_seam_painted() const { return !this->seam_facets.empty(); }
bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); } bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); }
bool is_fuzzy_skin_painted() const { return !this->fuzzy_skin_facets.empty(); } bool is_fuzzy_skin_painted() const { return !this->fuzzy_skin_facets.empty(); }
bool is_any_painted() const { return is_fdm_support_painted() || is_seam_painted() || is_mm_painted() || is_fuzzy_skin_painted(); }
// Orca: Implement prusa's filament shrink compensation approach // Orca: Implement prusa's filament shrink compensation approach
// Returns 0-based indices of extruders painted by multi-material painting gizmo. // Returns 0-based indices of extruders painted by multi-material painting gizmo.

View File

@@ -3329,7 +3329,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) |
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) |
only_if(dowels_count > 0, ModelObjectCutAttribute::CreateDowels) | only_if(dowels_count > 0, ModelObjectCutAttribute::CreateDowels) |
only_if(!has_connectors && !cut_with_groove && cut_mo->cut_id.id().invalid(), ModelObjectCutAttribute::InvalidateCutInfo); only_if(!has_connectors && !cut_with_groove && cut_mo->cut_id.id().invalid(), ModelObjectCutAttribute::InvalidateCutInfo) |
ModelObjectCutAttribute::KeepPaint;
// update cut_id for the cut object in respect to the attributes // update cut_id for the cut object in respect to the attributes
update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); update_object_cut_id(cut_mo->cut_id, attributes, dowels_count);