mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-24 02:17:57 +00:00
fix: slice the same model to the same lightning infill every time (#15311)
This commit is contained in:
@@ -351,19 +351,23 @@ void Node::convertToPolylines(Polylines &output, const coord_t line_overlap) con
|
||||
{
|
||||
Polylines result;
|
||||
result.emplace_back();
|
||||
convertToPolylines(0, result);
|
||||
// Orca: the layers are filled in parallel, so they would consume a shared generator in a
|
||||
// different order every run, and a model would not slice the same way twice. Each tree seeds
|
||||
// its own from where it is rooted; one constant seed would start them all on the same pick.
|
||||
std::mt19937_64 rng { uint64_t(PointHash{}(m_p)) };
|
||||
convertToPolylines(0, result, rng);
|
||||
removeJunctionOverlap(result, line_overlap);
|
||||
append(output, std::move(result));
|
||||
}
|
||||
|
||||
void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const
|
||||
void Node::convertToPolylines(size_t long_line_idx, Polylines &output, std::mt19937_64 &rng) const
|
||||
{
|
||||
if (m_children.empty()) {
|
||||
output[long_line_idx].points.push_back(m_p);
|
||||
return;
|
||||
}
|
||||
size_t first_child_idx = rand() % m_children.size();
|
||||
m_children[first_child_idx]->convertToPolylines(long_line_idx, output);
|
||||
const size_t first_child_idx = rng() % m_children.size();
|
||||
m_children[first_child_idx]->convertToPolylines(long_line_idx, output, rng);
|
||||
output[long_line_idx].points.push_back(m_p);
|
||||
|
||||
for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) {
|
||||
@@ -371,7 +375,7 @@ void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const
|
||||
const Node& child = *m_children[child_idx];
|
||||
output.emplace_back();
|
||||
size_t child_line_idx = output.size() - 1;
|
||||
child.convertToPolylines(child_line_idx, output);
|
||||
child.convertToPolylines(child_line_idx, output, rng);
|
||||
output[child_line_idx].points.emplace_back(m_p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "../../EdgeGrid.hpp"
|
||||
@@ -259,8 +260,9 @@ protected:
|
||||
*
|
||||
* \param long_line a reference to a polyline in \p output which to continue building on in the recursion
|
||||
* \param output all branches in this tree connected into polylines
|
||||
* \param rng the generator the junctions draw from, carried through the recursion
|
||||
*/
|
||||
void convertToPolylines(size_t long_line_idx, Polylines &output) const;
|
||||
void convertToPolylines(size_t long_line_idx, Polylines &output, std::mt19937_64 &rng) const;
|
||||
|
||||
void removeJunctionOverlap(Polylines &polylines, coord_t line_overlap) const;
|
||||
|
||||
|
||||
@@ -755,6 +755,9 @@ struct SparseInfillShape {
|
||||
size_t sharp_turns { 0 };
|
||||
size_t path_count { 0 };
|
||||
double length { 0. };
|
||||
// Digest of every point in the order it is printed. The counts above all survive the same
|
||||
// extrusions being joined into different polylines, so only this tells two such fills apart.
|
||||
uint64_t sequence { 14695981039346656037ull };
|
||||
};
|
||||
|
||||
static SparseInfillShape sparse_infill_shape(const Print &print)
|
||||
@@ -767,6 +770,9 @@ static SparseInfillShape sparse_infill_shape(const Print &print)
|
||||
const Points3 &pts = path.polyline.points;
|
||||
++shape.path_count;
|
||||
shape.point_count += pts.size();
|
||||
for (const auto &pt : pts)
|
||||
for (const coord_t coordinate : {pt.x(), pt.y(), pt.z()})
|
||||
shape.sequence = (shape.sequence ^ uint64_t(coordinate)) * 1099511628211ull;
|
||||
for (size_t i = 1; i < pts.size(); ++i)
|
||||
shape.length += (pts[i] - pts[i - 1]).head<2>().cast<double>().norm();
|
||||
for (size_t i = 1; i + 1 < pts.size(); ++i) {
|
||||
@@ -793,6 +799,33 @@ static SparseInfillShape sparse_infill_shape(const Print &print)
|
||||
return shape;
|
||||
}
|
||||
|
||||
TEST_CASE("Lightning infill slices the same model the same way twice", "[Fill][Regression]")
|
||||
{
|
||||
// Slicing twice in one process catches a generator that carries state from one slice to the
|
||||
// next, or whose result depends on how the parallel layer fill interleaves.
|
||||
auto shape = [] {
|
||||
Print print;
|
||||
Slic3r::Test::init_and_process_print({Slic3r::Test::cube(20)}, print,
|
||||
{{"sparse_infill_pattern", "lightning"},
|
||||
{"sparse_infill_density", "50%"},
|
||||
{"layer_height", 0.2}});
|
||||
return sparse_infill_shape(print);
|
||||
};
|
||||
|
||||
const SparseInfillShape first = shape();
|
||||
const SparseInfillShape second = shape();
|
||||
|
||||
REQUIRE(first.path_count > 0);
|
||||
REQUIRE(second.path_count == first.path_count);
|
||||
REQUIRE(second.point_count == first.point_count);
|
||||
REQUIRE(second.sharp_turns == first.sharp_turns);
|
||||
// No tolerance: the same extrusions in the same order add up to the very same number.
|
||||
REQUIRE_THAT(second.length, Catch::Matchers::WithinAbs(first.length, 0.));
|
||||
// All of the above agree when the same branches are joined into different polylines, so the
|
||||
// point sequence is what actually decides whether the two slices produced the same infill.
|
||||
REQUIRE(second.sequence == first.sequence);
|
||||
}
|
||||
|
||||
TEST_CASE("Lightning infill rounds the turns of its branches with the smooth factor", "[Fill]")
|
||||
{
|
||||
auto shape_for = [](const std::string &smooth_factor) {
|
||||
|
||||
Reference in New Issue
Block a user