mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-04 04:01:13 +08:00

Previously there was an algirithm that was fitting a curve through the resulting seam points. This worked for smooth models, but was failing for some very basic cases (such as a cylinder). The new algorithm builds on the previously implemented visibility algirithm but does not do the curve fitting anymore. Now the code is more separated for the four seam placement options (rear, random, aligned, nearest). Nearest and random are handled as one would expect. Aligned is handled in a more "brute force" manner (trying multiple seams and picking the best one) and rear smartly switches between two modes (center straight line projection and max y).
99 lines
3.8 KiB
C++
99 lines
3.8 KiB
C++
#include <libslic3r/Point.hpp>
|
|
#include <catch2/catch.hpp>
|
|
#include <libslic3r/GCode/SeamRandom.hpp>
|
|
#include "test_data.hpp"
|
|
#include <fstream>
|
|
|
|
using namespace Slic3r;
|
|
using namespace Slic3r::Seams;
|
|
|
|
constexpr bool debug_files{false};
|
|
|
|
namespace RandomTest {
|
|
Perimeters::Perimeter get_perimeter() {
|
|
const double slice_z{1.0};
|
|
const std::size_t layer_index{};
|
|
std::vector<Vec2d> positions{{0.0, 0.0}, {0.5, 0.0}, {1.0, 0.0}};
|
|
std::vector<double> angles(positions.size(), -M_PI / 2.0);
|
|
std::vector<Perimeters::PointType> point_types(positions.size(), Perimeters::PointType::common);
|
|
std::vector<Perimeters::PointClassification>
|
|
point_classifications{positions.size(), Perimeters::PointClassification::common};
|
|
std::vector<Perimeters::AngleType> angle_type(positions.size(), Perimeters::AngleType::concave);
|
|
|
|
return {
|
|
slice_z,
|
|
layer_index,
|
|
std::move(positions),
|
|
std::move(angles),
|
|
std::move(point_types),
|
|
std::move(point_classifications),
|
|
std::move(angle_type)};
|
|
}
|
|
} // namespace RandomTest
|
|
|
|
double get_chi2_uniform(const std::vector<double> &data, double min, double max, const std::size_t bin_count) {
|
|
std::vector<std::size_t> bins(bin_count);
|
|
const double bin_size{(max - min) / bin_count};
|
|
const double expected_frequncy{static_cast<double>(data.size()) / bin_count};
|
|
|
|
for (const double value : data) {
|
|
auto bin{static_cast<int>(std::floor((value - min) / bin_size))};
|
|
bins[bin]++;
|
|
}
|
|
|
|
return std::accumulate(bins.begin(), bins.end(), 0.0, [&](const double total, const std::size_t count_in_bin){
|
|
return total + std::pow(static_cast<double>(count_in_bin - expected_frequncy), 2.0) / expected_frequncy;
|
|
});
|
|
}
|
|
|
|
TEST_CASE("Random is uniform", "[Seams][SeamRandom]") {
|
|
const int seed{42};
|
|
std::mt19937 random_engine{seed};
|
|
const Random::Impl::Random random{random_engine};
|
|
Perimeters::Perimeter perimeter{RandomTest::get_perimeter()};
|
|
|
|
std::vector<double> x_positions;
|
|
const std::size_t count{1001};
|
|
x_positions.reserve(count);
|
|
std::generate_n(std::back_inserter(x_positions), count, [&]() {
|
|
std::optional<SeamChoice> choice{
|
|
random(perimeter, Perimeters::PointType::common, Perimeters::PointClassification::common)};
|
|
return choice->position.x();
|
|
});
|
|
const std::size_t degrees_of_freedom{10};
|
|
const double critical{18.307}; // dof 10, significance 0.05
|
|
|
|
CHECK(get_chi2_uniform(x_positions, 0.0, 1.0, degrees_of_freedom + 1) < critical);
|
|
}
|
|
|
|
TEST_CASE("Random respects point type", "[Seams][SeamRandom]") {
|
|
const int seed{42};
|
|
std::mt19937 random_engine{seed};
|
|
const Random::Impl::Random random{random_engine};
|
|
Perimeters::Perimeter perimeter{RandomTest::get_perimeter()};
|
|
std::optional<SeamChoice> choice{
|
|
random(perimeter, Perimeters::PointType::common, Perimeters::PointClassification::common)};
|
|
|
|
REQUIRE(choice);
|
|
const std::size_t picked_index{choice->previous_index};
|
|
perimeter.point_types[picked_index] = Perimeters::PointType::blocker;
|
|
choice = random(perimeter, Perimeters::PointType::common, Perimeters::PointClassification::common);
|
|
REQUIRE(choice);
|
|
CHECK(choice->previous_index != picked_index);
|
|
}
|
|
|
|
TEST_CASE_METHOD(Test::SeamsFixture, "Generate random seam", "[Seams][SeamRandom][Integration]") {
|
|
Shells::Shells<> perimeters{
|
|
Seams::Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
|
|
Shells::Shells<> shell_perimeters;
|
|
shell_perimeters.push_back(std::move(perimeters[shell_index]));
|
|
const std::vector<std::vector<SeamPerimeterChoice>> seam{
|
|
Random::get_object_seams(std::move(shell_perimeters), params.random_seed)};
|
|
REQUIRE(seam.size() == 125);
|
|
|
|
if constexpr (debug_files) {
|
|
std::ofstream csv{"random_seam.csv"};
|
|
Test::serialize_seam(csv, seam);
|
|
}
|
|
}
|