mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-27 11:59:27 +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;
|
Polylines result;
|
||||||
result.emplace_back();
|
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);
|
removeJunctionOverlap(result, line_overlap);
|
||||||
append(output, std::move(result));
|
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()) {
|
if (m_children.empty()) {
|
||||||
output[long_line_idx].points.push_back(m_p);
|
output[long_line_idx].points.push_back(m_p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
size_t first_child_idx = rand() % m_children.size();
|
const size_t first_child_idx = rng() % m_children.size();
|
||||||
m_children[first_child_idx]->convertToPolylines(long_line_idx, output);
|
m_children[first_child_idx]->convertToPolylines(long_line_idx, output, rng);
|
||||||
output[long_line_idx].points.push_back(m_p);
|
output[long_line_idx].points.push_back(m_p);
|
||||||
|
|
||||||
for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) {
|
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];
|
const Node& child = *m_children[child_idx];
|
||||||
output.emplace_back();
|
output.emplace_back();
|
||||||
size_t child_line_idx = output.size() - 1;
|
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);
|
output[child_line_idx].points.emplace_back(m_p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <random>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../../EdgeGrid.hpp"
|
#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 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 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;
|
void removeJunctionOverlap(Polylines &polylines, coord_t line_overlap) const;
|
||||||
|
|
||||||
|
|||||||
@@ -755,6 +755,9 @@ struct SparseInfillShape {
|
|||||||
size_t sharp_turns { 0 };
|
size_t sharp_turns { 0 };
|
||||||
size_t path_count { 0 };
|
size_t path_count { 0 };
|
||||||
double length { 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)
|
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;
|
const Points3 &pts = path.polyline.points;
|
||||||
++shape.path_count;
|
++shape.path_count;
|
||||||
shape.point_count += pts.size();
|
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)
|
for (size_t i = 1; i < pts.size(); ++i)
|
||||||
shape.length += (pts[i] - pts[i - 1]).head<2>().cast<double>().norm();
|
shape.length += (pts[i] - pts[i - 1]).head<2>().cast<double>().norm();
|
||||||
for (size_t i = 1; i + 1 < pts.size(); ++i) {
|
for (size_t i = 1; i + 1 < pts.size(); ++i) {
|
||||||
@@ -793,6 +799,33 @@ static SparseInfillShape sparse_infill_shape(const Print &print)
|
|||||||
return shape;
|
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]")
|
TEST_CASE("Lightning infill rounds the turns of its branches with the smooth factor", "[Fill]")
|
||||||
{
|
{
|
||||||
auto shape_for = [](const std::string &smooth_factor) {
|
auto shape_for = [](const std::string &smooth_factor) {
|
||||||
|
|||||||
Reference in New Issue
Block a user