mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-23 20:22:04 +00:00
Support extended triangle paint states up to 32
Extend TriangleSelector serialization to encode leaf facet paint states up to 32 by adding an escaped second nibble (12-bit path: wwwwzzzzxxyy). Implemented a push_nibble helper and an extended-prefix (0b1110) to signal the extra nibble. Updated deserialize, update_used_states and has_facets to decode the new format while preserving legacy behavior for 3..16. Adjusted asserts and state handling accordingly. Added a unit test and include for TriangleSelector to verify round-trip of paint states above 16.
This commit is contained in:
@@ -1691,9 +1691,11 @@ void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, cons
|
||||
|
||||
TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
||||
// Each original triangle of the mesh is assigned a number encoding its state
|
||||
// or how it is split. Each triangle is encoded by 4 bits (xxyy) or 8 bits (zzzzxxyy):
|
||||
// or how it is split. Each triangle is encoded by 4 bits (xxyy), 8 bits
|
||||
// (zzzzxxyy), or 12 bits (wwwwzzzzxxyy):
|
||||
// leaf triangle: xx = EnforcerBlockerType (Only values 0, 1, and 2. Value 3 is used as an indicator for additional 4 bits.), yy = 0
|
||||
// leaf triangle: xx = 0b11, yy = 0b00, zzzz = EnforcerBlockerType (subtracted by 3)
|
||||
// leaf triangle: xx = 0b11, yy = 0b00, zzzz = EnforcerBlockerType (subtracted by 3) for states 3..16
|
||||
// leaf triangle: xx = 0b11, yy = 0b00, zzzz = 0b1110, wwww = EnforcerBlockerType (subtracted by 17) for states 17..32
|
||||
// non-leaf: xx = special side, yy = number of split sides
|
||||
// These are bitwise appended and formed into one 64-bit integer.
|
||||
|
||||
@@ -1736,13 +1738,25 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
||||
data.used_states[n] = true;
|
||||
|
||||
if (n >= 3) {
|
||||
assert(n <= 16);
|
||||
if (n <= 16) {
|
||||
// Store "11" plus 4 bits of (n-3).
|
||||
data.bitstream.insert(data.bitstream.end(), { true, true });
|
||||
n -= 3;
|
||||
assert(n <= int(EnforcerBlockerType::ExtruderMax));
|
||||
|
||||
auto push_nibble = [&data = this->data](int value) {
|
||||
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx)
|
||||
data.bitstream.push_back(n & (uint64_t(0b0001) << bit_idx));
|
||||
data.bitstream.push_back((value & (1 << int(bit_idx))) != 0);
|
||||
};
|
||||
|
||||
// Store "11" plus either one nibble (legacy 3..16) or
|
||||
// an escaped second nibble for 17..32.
|
||||
data.bitstream.insert(data.bitstream.end(), { true, true });
|
||||
if (n <= 16) {
|
||||
push_nibble(n - 3);
|
||||
} else {
|
||||
// 0b1110 marks the extended range 17..32.
|
||||
constexpr int extended_prefix = 0b1110;
|
||||
const int encoded = n - 17;
|
||||
assert(encoded >= 0 && encoded <= 15);
|
||||
push_nibble(extended_prefix);
|
||||
push_nibble(encoded);
|
||||
}
|
||||
} else {
|
||||
// Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams.
|
||||
@@ -1818,8 +1832,19 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data,
|
||||
int num_of_split_sides = code & 0b11;
|
||||
int num_of_children = num_of_split_sides == 0 ? 0 : num_of_split_sides + 1;
|
||||
bool is_split = num_of_children != 0;
|
||||
// Only valid if not is_split. Value of the second nibble was subtracted by 3, so it is added back.
|
||||
auto state = is_split ? EnforcerBlockerType::NONE : EnforcerBlockerType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
||||
// Only valid if not is_split.
|
||||
auto decode_leaf_state = [&next_nibble](int leaf_code) -> EnforcerBlockerType {
|
||||
if ((leaf_code & 0b1100) != 0b1100)
|
||||
return EnforcerBlockerType(leaf_code >> 2);
|
||||
|
||||
const int extended = next_nibble();
|
||||
if (extended == 0b1110)
|
||||
return EnforcerBlockerType(next_nibble() + 17);
|
||||
|
||||
// Legacy path (states 3..16, plus historical fallback for 18).
|
||||
return EnforcerBlockerType(extended + 3);
|
||||
};
|
||||
auto state = is_split ? EnforcerBlockerType::NONE : decode_leaf_state(code);
|
||||
|
||||
// BBS
|
||||
if (state == to_delete_filament)
|
||||
@@ -1916,7 +1941,17 @@ void TriangleSelector::TriangleSplittingData::update_used_states(const size_t bi
|
||||
if (const bool is_split = (code & 0b11) != 0; is_split)
|
||||
continue;
|
||||
|
||||
const uint8_t facet_state = (code & 0b1100) == 0b1100 ? read_next_nibble() + 3 : code >> 2;
|
||||
uint8_t facet_state = 0;
|
||||
if ((code & 0b1100) != 0b1100) {
|
||||
facet_state = code >> 2;
|
||||
} else {
|
||||
const uint8_t extended = read_next_nibble();
|
||||
if (extended == 0b1110)
|
||||
facet_state = read_next_nibble() + 17;
|
||||
else
|
||||
facet_state = extended + 3;
|
||||
}
|
||||
|
||||
assert(facet_state < this->used_states.size());
|
||||
if (facet_state >= this->used_states.size())
|
||||
continue;
|
||||
@@ -1946,9 +1981,17 @@ bool TriangleSelector::has_facets(const TriangleSplittingData &data, const Enfor
|
||||
auto num_children_or_state = [&next_nibble]() -> int {
|
||||
int code = next_nibble();
|
||||
int num_of_split_sides = code & 0b11;
|
||||
return num_of_split_sides == 0 ?
|
||||
((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2) :
|
||||
- num_of_split_sides - 1;
|
||||
if (num_of_split_sides != 0)
|
||||
return - num_of_split_sides - 1;
|
||||
|
||||
if ((code & 0b1100) != 0b1100)
|
||||
return code >> 2;
|
||||
|
||||
const int extended = next_nibble();
|
||||
if (extended == 0b1110)
|
||||
return next_nibble() + 17;
|
||||
|
||||
return extended + 3;
|
||||
};
|
||||
|
||||
int state = num_children_or_state();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "libslic3r/MixedFilament.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/TriangleSelector.hpp"
|
||||
#include "libslic3r/GCode/ToolOrdering.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -126,6 +127,21 @@ TEST_CASE("Mixed filament remap follows stable row ids when same-pair rows reord
|
||||
CHECK(remap[4] == new_second_custom_virtual_id);
|
||||
}
|
||||
|
||||
TEST_CASE("TriangleSelector round-trips paint states above 16", "[MixedFilament][TriangleSelector]")
|
||||
{
|
||||
TriangleMesh mesh = make_cube(20., 20., 20.);
|
||||
|
||||
TriangleSelector source(mesh);
|
||||
source.set_facet(0, EnforcerBlockerType::Extruder32);
|
||||
const TriangleSelector::TriangleSplittingData serialized = source.serialize();
|
||||
|
||||
REQUIRE(TriangleSelector::has_facets(serialized, EnforcerBlockerType::Extruder32));
|
||||
|
||||
TriangleSelector restored(mesh);
|
||||
restored.deserialize(serialized, true, EnforcerBlockerType::ExtruderMax);
|
||||
CHECK(restored.has_facets(EnforcerBlockerType::Extruder32));
|
||||
}
|
||||
|
||||
TEST_CASE("Mixed filament remap keeps later painted colors stable when an earlier mixed row is deleted", "[MixedFilament]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
|
||||
Reference in New Issue
Block a user