mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-01 06:16:58 +00:00
Fixes from Full spectrum port
https://github.com/OrcaSlicer/OrcaSlicer/pull/14383
This commit is contained in:
124
tests/libslic3r/test_triangle_selector.cpp
Normal file
124
tests/libslic3r/test_triangle_selector.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "libslic3r/TriangleSelector.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
// A sphere gives well over ExtruderMax original facets, so every extruder state can be assigned
|
||||
// to a facet of its own without any splitting getting in the way.
|
||||
static TriangleMesh test_mesh() { return make_sphere(5., 2 * PI / 24); }
|
||||
|
||||
// Read the nibble_idx-th 4-bit group of a serialized bitstream, least significant bit first.
|
||||
static int nibble_at(const std::vector<bool> &bitstream, size_t nibble_idx)
|
||||
{
|
||||
int n = 0;
|
||||
for (size_t bit = 0; bit < 4; ++bit)
|
||||
n |= int(bitstream[nibble_idx * 4 + bit]) << bit;
|
||||
return n;
|
||||
}
|
||||
|
||||
TEST_CASE("Every extruder state survives a serialize/deserialize round trip", "[TriangleSelector]")
|
||||
{
|
||||
const TriangleMesh mesh = test_mesh();
|
||||
const int max_state = int(EnforcerBlockerType::ExtruderMax);
|
||||
REQUIRE(int(mesh.its.indices.size()) >= max_state);
|
||||
|
||||
TriangleSelector selector(mesh);
|
||||
for (int state = 1; state <= max_state; ++state)
|
||||
selector.set_facet(state - 1, EnforcerBlockerType(state));
|
||||
|
||||
TriangleSelector restored(mesh);
|
||||
restored.deserialize(selector.serialize());
|
||||
|
||||
for (int state = 1; state <= max_state; ++state) {
|
||||
INFO("Extruder " << state);
|
||||
REQUIRE(restored.has_facets(EnforcerBlockerType(state)));
|
||||
REQUIRE(restored.num_facets(EnforcerBlockerType(state)) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Serialized data reports the extruder states it uses", "[TriangleSelector]")
|
||||
{
|
||||
const TriangleMesh mesh = test_mesh();
|
||||
TriangleSelector selector(mesh);
|
||||
selector.set_facet(0, EnforcerBlockerType::Extruder16);
|
||||
selector.set_facet(1, EnforcerBlockerType::Extruder32);
|
||||
|
||||
const TriangleSelector::TriangleSplittingData data = selector.serialize();
|
||||
|
||||
REQUIRE(data.used_states.size() == size_t(EnforcerBlockerType::ExtruderMax) + 1);
|
||||
REQUIRE(data.used_states[size_t(EnforcerBlockerType::Extruder16)]);
|
||||
REQUIRE(data.used_states[size_t(EnforcerBlockerType::Extruder32)]);
|
||||
REQUIRE_FALSE(data.used_states[size_t(EnforcerBlockerType::Extruder17)]);
|
||||
|
||||
SECTION("used_states recomputed from the bitstream agrees") {
|
||||
TriangleSelector::TriangleSplittingData recomputed = data;
|
||||
recomputed.reset_used_states();
|
||||
recomputed.update_used_states(0);
|
||||
REQUIRE(recomputed.used_states == data.used_states);
|
||||
}
|
||||
|
||||
SECTION("has_facets on the raw data agrees") {
|
||||
REQUIRE(TriangleSelector::has_facets(data, EnforcerBlockerType::Extruder32));
|
||||
REQUIRE_FALSE(TriangleSelector::has_facets(data, EnforcerBlockerType::Extruder17));
|
||||
}
|
||||
}
|
||||
|
||||
// States 3..17 must keep the pre-existing encoding ("11" prefix plus one nibble of state-3) so
|
||||
// projects written by older builds stay readable and newly written ones stay readable by them.
|
||||
TEST_CASE("Extruder states up to 17 keep the single-nibble encoding", "[TriangleSelector]")
|
||||
{
|
||||
const int state = GENERATE(3, 8, 16, 17);
|
||||
|
||||
TriangleSelector selector(test_mesh());
|
||||
selector.set_facet(0, EnforcerBlockerType(state));
|
||||
const std::vector<bool> bitstream = selector.serialize().bitstream;
|
||||
|
||||
INFO("Extruder " << state);
|
||||
// Two nibbles: the "11"-prefixed leaf code, then the state itself.
|
||||
REQUIRE(bitstream.size() == 8);
|
||||
REQUIRE(nibble_at(bitstream, 0) == 0b1100);
|
||||
REQUIRE(nibble_at(bitstream, 1) == state - 3);
|
||||
}
|
||||
|
||||
// States 18 and above set the state nibble to 0b1111 and carry (state-18) in one more nibble.
|
||||
TEST_CASE("Extruder states above 17 are encoded in a second nibble", "[TriangleSelector]")
|
||||
{
|
||||
const int state = GENERATE(18, 25, 32);
|
||||
|
||||
TriangleSelector selector(test_mesh());
|
||||
selector.set_facet(0, EnforcerBlockerType(state));
|
||||
const std::vector<bool> bitstream = selector.serialize().bitstream;
|
||||
|
||||
INFO("Extruder " << state);
|
||||
REQUIRE(bitstream.size() == 12);
|
||||
REQUIRE(nibble_at(bitstream, 0) == 0b1100);
|
||||
REQUIRE(nibble_at(bitstream, 1) == 0b1111);
|
||||
REQUIRE(nibble_at(bitstream, 2) == state - 18);
|
||||
}
|
||||
|
||||
// Model.cpp writes these hex strings into the 3MF for colored mesh imports; the selector must
|
||||
// decode exactly the states that table assigns to them.
|
||||
TEST_CASE("Extruder states match the CONST_FILAMENTS hex encoding", "[TriangleSelector]")
|
||||
{
|
||||
struct Case { const char *hex; int state; };
|
||||
const auto c = GENERATE(values<Case>({
|
||||
{"8", 2}, {"0C", 3}, {"DC", 16}, {"EC", 17}, {"0FC", 18}, {"EFC", 32},
|
||||
}));
|
||||
|
||||
// get_triangle_as_string emits the nibbles most significant first, so read the hex backwards.
|
||||
std::vector<bool> bitstream;
|
||||
for (auto it = std::string(c.hex).rbegin(); it != std::string(c.hex).rend(); ++it) {
|
||||
const int nibble = *it >= 'A' ? (*it - 'A' + 10) : (*it - '0');
|
||||
for (int bit = 0; bit < 4; ++bit)
|
||||
bitstream.push_back((nibble >> bit) & 1);
|
||||
}
|
||||
|
||||
TriangleSelector::TriangleSplittingData data;
|
||||
data.triangles_to_split.emplace_back(0, 0);
|
||||
data.bitstream = bitstream;
|
||||
|
||||
INFO("Hex " << c.hex << " -> extruder " << c.state);
|
||||
REQUIRE(TriangleSelector::has_facets(data, EnforcerBlockerType(c.state)));
|
||||
}
|
||||
Reference in New Issue
Block a user