update test

This commit is contained in:
QIDI TECH
2025-02-10 15:31:36 +08:00
parent 7529de7fe1
commit 748e5f2db2
76 changed files with 9796 additions and 99 deletions

View File

@@ -20,6 +20,7 @@ add_executable(${_TEST_NAME}_tests
test_seam_aligned.cpp
test_seam_rear.cpp
test_seam_random.cpp
test_seam_scarf.cpp
benchmark_seams.cpp
test_gcodefindreplace.cpp
test_gcodewriter.cpp
@@ -38,7 +39,7 @@ add_executable(${_TEST_NAME}_tests
test_thin_walls.cpp
test_trianglemesh.cpp
)
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
target_link_libraries(${_TEST_NAME}_tests test_common slic3r-arrange-wrapper)
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
target_compile_definitions(${_TEST_NAME}_tests PUBLIC CATCH_CONFIG_ENABLE_BENCHMARKING)

View File

@@ -105,13 +105,14 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
using namespace Slic3r;
Placer placer;
placer.init(print->objects(), params, [](){});
std::vector<std::pair<const Layer*, const ExtrusionLoop*>> loops;
std::vector<std::tuple<const Layer*, const ExtrusionLoop*, const PrintRegion *>> loops;
const PrintObject* object{print->objects().front()};
for (const Layer* layer :object->layers()) {
for (const LayerSlice& lslice : layer->lslices_ex) {
for (const LayerIsland &island : lslice.islands) {
const LayerRegion &layer_region = *layer->get_region(island.perimeters.region());
const PrintRegion &region = print->get_print_region(layer_region.region().print_region_id());
for (uint32_t perimeter_id : island.perimeters) {
const auto *entity_collection{static_cast<const ExtrusionEntityCollection*>(layer_region.perimeters().entities[perimeter_id])};
if (entity_collection != nullptr) {
@@ -120,7 +121,7 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
if (loop == nullptr) {
continue;
}
loops.emplace_back(layer, loop);
loops.emplace_back(layer, loop, &region);
}
}
}
@@ -129,8 +130,8 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
}
BENCHMARK_ADVANCED("Place seam benchy")(Catch::Benchmark::Chronometer meter) {
meter.measure([&] {
for (const auto &[layer, loop] : loops) {
placer.place_seam(layer, *loop, {0, 0});
for (const auto &[layer, loop, region] : loops) {
placer.place_seam(layer, region, *loop, false, {0, 0});
}
});
};

View File

@@ -7,6 +7,8 @@
#include "libslic3r/Format/OBJ.hpp"
#include "libslic3r/Format/STL.hpp"
#include <arrange-wrapper/ModelArrange.hpp>
#include <cstdlib>
#include <string>
@@ -14,7 +16,6 @@
#include <boost/nowide/fstream.hpp>
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include <libslic3r/ModelArrange.hpp>
using namespace std;
@@ -254,7 +255,7 @@ void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r
double distance = min_object_distance(config);
arr2::ArrangeSettings arrange_settings{};
arrange_settings.set_distance_from_objects(distance);
arr2::ArrangeBed bed{arr2::to_arrange_bed(get_bed_shape(config))};
arr2::ArrangeBed bed{arr2::to_arrange_bed(get_bed_shape(config), Vec2crd{0, 0})};
if (duplicate_count > 1) {
duplicate(model, duplicate_count, bed, arrange_settings);
}

View File

@@ -165,7 +165,8 @@ inline std::unique_ptr<Print> process_3mf(const std::filesystem::path &path) {
Model model;
ConfigSubstitutionContext context{ForwardCompatibilitySubstitutionRule::Disable};
load_3mf(path.string().c_str(), config, context, &model, false);
boost::optional<Semver> version;
load_3mf(path.string().c_str(), config, context, &model, false, version);
Slic3r::Test::init_print(std::vector<TriangleMesh>{}, *print, model, config);
print->process();

View File

@@ -6,7 +6,6 @@
#include "libslic3r/GCode.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/ModelArrange.hpp"
#include "test_data.hpp"
using namespace Slic3r;

View File

@@ -2,7 +2,7 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/Model.hpp"
#include "libslic3r/ModelArrange.hpp"
#include <arrange-wrapper/ModelArrange.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/filesystem.hpp>
@@ -43,7 +43,7 @@ SCENARIO("Model construction", "[Model]") {
}
model_object->add_instance();
arrange_objects(model,
arr2::to_arrange_bed(get_bed_shape(config)),
arr2::to_arrange_bed(get_bed_shape(config), Point::new_scale(10, 10)),
arr2::ArrangeSettings{}.set_distance_from_objects(
min_object_distance(config)));

View File

@@ -48,6 +48,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]")
//B56
ExPolygons fill_expolygons_no_overlap;
Flow flow(1., 1., 1.);
PerimeterRegions perimeter_regions;
PerimeterGenerator::Parameters perimeter_generator_params(
1., // layer height
-1, // layer ID
@@ -55,6 +56,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]")
static_cast<const PrintRegionConfig&>(config),
static_cast<const PrintObjectConfig&>(config),
static_cast<const PrintConfig&>(config),
perimeter_regions,
false); // spiral_vase
Polygons lower_layer_polygons_cache;
Polygons upper_layer_polygons_cache;

View File

@@ -143,3 +143,30 @@ TEST_CASE("Calculate overhangs", "[Seams][SeamGeometry]") {
0.0, M_PI / 4.0, M_PI / 4.0, 0.0
}));
}
const Linesf lines{to_unscaled_linesf({ExPolygon{
scaled(Vec2d{0.0, 0.0}),
scaled(Vec2d{1.0, 0.0}),
scaled(Vec2d{1.0, 1.0}),
scaled(Vec2d{0.0, 1.0})
}})};
TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
{0.5, 0.0}, 0, lines, 3.9, Seams::Geometry::Direction1D::forward
)};
REQUIRE(result);
const auto &[point, line_index] = *result;
CHECK((scaled(point) - Point::new_scale(0.4, 0.0)).norm() < scaled(EPSILON));
CHECK(line_index == 0);
}
TEST_CASE("Offset along loop lines backward", "[Seams][SeamGeometry]") {
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
{1.0, 0.5}, 1, lines, 1.8, Seams::Geometry::Direction1D::backward
)};
REQUIRE(result);
const auto &[point, line_index] = *result;
CHECK((scaled(point) - Point::new_scale(0.0, 0.3)).norm() < scaled(EPSILON));
CHECK(line_index == 3);
}

View File

@@ -124,44 +124,48 @@ constexpr const char *to_string(Perimeters::AngleType angle_type) {
throw std::runtime_error("Unreachable");
}
void serialize_shell(std::ostream &output, const Shells::Shell<Perimeters::Perimeter> &shell) {
void serialize_shells(std::ostream &output, const Shells::Shells<> &shells) {
output << "x,y,z,point_type,point_classification,angle_type,layer_index,"
"point_index,distance,distance_to_previous,is_degenerate"
"point_index,distance,distance_to_previous,is_degenerate,shell_index"
<< std::endl;
for (std::size_t perimeter_index{0}; perimeter_index < shell.size(); ++perimeter_index) {
const Shells::Slice<> &slice{shell[perimeter_index]};
const Perimeters::Perimeter &perimeter{slice.boundary};
const std::vector<Vec2d> &points{perimeter.positions};
for (std::size_t shell_index{0}; shell_index < shells.size(); ++shell_index) {
const Shells::Shell<> &shell{shells[shell_index]};
for (std::size_t perimeter_index{0}; perimeter_index < shell.size(); ++perimeter_index) {
const Shells::Slice<> &slice{shell[perimeter_index]};
const Perimeters::Perimeter &perimeter{slice.boundary};
const std::vector<Vec2d> &points{perimeter.positions};
double total_distance{0.0};
for (std::size_t point_index{0}; point_index < perimeter.point_types.size(); ++point_index) {
const Vec3d point{to_3d(points[point_index], perimeter.slice_z)};
const Perimeters::PointType point_type{perimeter.point_types[point_index]};
const Perimeters::PointClassification point_classification{
perimeter.point_classifications[point_index]};
const Perimeters::AngleType angle_type{perimeter.angle_types[point_index]};
const std::size_t layer_index{slice.layer_index};
const std::size_t previous_index{point_index == 0 ? points.size() - 1 : point_index - 1};
const double distance_to_previous{(points[point_index] - points[previous_index]).norm()};
total_distance += point_index == 0 ? 0.0 : distance_to_previous;
const double distance{total_distance};
const bool is_degenerate{perimeter.is_degenerate};
double total_distance{0.0};
for (std::size_t point_index{0}; point_index < perimeter.point_types.size(); ++point_index) {
const Vec3d point{to_3d(points[point_index], perimeter.slice_z)};
const Perimeters::PointType point_type{perimeter.point_types[point_index]};
const Perimeters::PointClassification point_classification{
perimeter.point_classifications[point_index]};
const Perimeters::AngleType angle_type{perimeter.angle_types[point_index]};
const std::size_t layer_index{slice.layer_index};
const std::size_t previous_index{point_index == 0 ? points.size() - 1 : point_index - 1};
const double distance_to_previous{(points[point_index] - points[previous_index]).norm()};
total_distance += point_index == 0 ? 0.0 : distance_to_previous;
const double distance{total_distance};
const bool is_degenerate{perimeter.is_degenerate};
// clang-format off
output
<< point.x() << ","
<< point.y() << ","
<< point.z() << ","
<< to_string(point_type) << ","
<< to_string(point_classification) << ","
<< to_string(angle_type) << ","
<< layer_index << ","
<< point_index << ","
<< distance << ","
<< distance_to_previous << ","
<< is_degenerate << std::endl;
// clang-format on
// clang-format off
output
<< point.x() << ","
<< point.y() << ","
<< point.z() << ","
<< to_string(point_type) << ","
<< to_string(point_classification) << ","
<< to_string(angle_type) << ","
<< layer_index << ","
<< point_index << ","
<< distance << ","
<< distance_to_previous << ","
<< is_degenerate << ","
<< shell_index << std::endl;
// clang-format on
}
}
}
}
@@ -175,6 +179,6 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Create perimeters", "[Seams][SeamPerimeter
if constexpr (debug_files) {
std::ofstream csv{"perimeters.csv"};
serialize_shell(csv, shells[0]);
serialize_shells(csv, shells);
}
}

View File

@@ -0,0 +1,320 @@
#include <catch2/catch.hpp>
#include <libslic3r/GCode/SeamScarf.hpp>
#include <libslic3r/GCode/SmoothPath.hpp>
using namespace Slic3r;
using Seams::Scarf::Impl::PathPoint;
TEST_CASE("Get path point", "[Seams][Scarf]") {
using Seams::Scarf::Impl::get_path_point;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(0, 1),
Point::new_scale(0, 2),
Point::new_scale(0, 3),
Point::new_scale(0, 4),
};
const ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
{{points[2], points[3], points[4]}, {}},
};
const std::size_t global_index{5}; // Index if paths are flattened.
const Point point{Point::new_scale(0, 3.5)};
const PathPoint path_point{get_path_point(paths, point, global_index)};
CHECK(path_point.path_index == 2);
CHECK(path_point.previous_point_on_path_index == 1);
CHECK(path_point.point == point);
}
TEST_CASE("Split path", "[Seams][Scarf]") {
using Seams::Scarf::Impl::split_path;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(1, 0),
Point::new_scale(2, 0),
};
const auto split_point{Point::new_scale(1.5, 0)};
const ExtrusionPath path{Polyline{points}, {}};
const auto[path_before, path_after]{split_path(
path, split_point, 1
)};
REQUIRE(path_before.size() == 3);
CHECK(path_before.first_point() == points.front());
CHECK(path_before.last_point() == split_point);
REQUIRE(path_after.size() == 2);
CHECK(path_after.first_point() == split_point);
CHECK(path_after.last_point() == points.back());
}
TEST_CASE("Split paths", "[Seams][Scarf]") {
using Seams::Scarf::Impl::split_paths;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(0, 1),
Point::new_scale(0, 2),
};
ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
};
const auto split_point{Point::new_scale(0, 1.5)};
PathPoint path_point{};
path_point.point = split_point;
path_point.previous_point_on_path_index = 0;
path_point.path_index = 1;
const ExtrusionPaths result{split_paths(std::move(paths), path_point)};
REQUIRE(result.size() == 3);
CHECK(result[1].last_point() == split_point);
CHECK(result[2].first_point() == split_point);
}
TEST_CASE("Get length", "[Seams][Scarf]") {
using Seams::Scarf::Impl::get_length;
using Seams::Scarf::Impl::convert_to_smooth;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(0, 1),
Point::new_scale(0, 2.2),
};
ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
};
CHECK(get_length(convert_to_smooth(paths)) == scaled(2.2));
}
TEST_CASE("Linspace", "[Seams][Scarf]") {
using Seams::Scarf::Impl::linspace;
const auto from{Point::new_scale(1, 0)};
const auto to{Point::new_scale(3, 0)};
Points points{linspace(from, to, 3)};
REQUIRE(points.size() == 3);
CHECK(points[1] == Point::new_scale(2, 0));
points = linspace(from, to, 5);
REQUIRE(points.size() == 5);
CHECK(points[1] == Point::new_scale(1.5, 0));
CHECK(points[2] == Point::new_scale(2.0, 0));
CHECK(points[3] == Point::new_scale(2.5, 0));
}
TEST_CASE("Ensure max distance", "[Seams][Scarf]") {
using Seams::Scarf::Impl::ensure_max_distance;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(0, 1),
};
Points result{ensure_max_distance(points, scaled(0.5))};
REQUIRE(result.size() == 3);
CHECK(result[1] == Point::new_scale(0, 0.5));
result = ensure_max_distance(points, scaled(0.49));
REQUIRE(result.size() == 4);
}
TEST_CASE("Lineary increase extrusion height", "[Seams][Scarf]") {
using Seams::Scarf::Impl::lineary_increase_extrusion_height;
using GCode::SmoothPath, GCode::SmoothPathElement;
SmoothPath path{
{{}, {{Point::new_scale(0, 0)}, {Point::new_scale(1, 0)}}},
{{}, {{Point::new_scale(1, 0)}, {Point::new_scale(2, 0)}}},
};
SmoothPath result{lineary_increase_extrusion_height(std::move(path), 0.5)};
CHECK(result[0].path[0].height_fraction == Approx(0.5));
CHECK(result[0].path[0].e_fraction == Approx(0.0));
CHECK(result[0].path[1].height_fraction == Approx(0.75));
CHECK(result[0].path[1].e_fraction == Approx(0.5));
CHECK(result[1].path[0].height_fraction == Approx(0.75));
CHECK(result[1].path[0].e_fraction == Approx(0.5));
CHECK(result[1].path[1].height_fraction == Approx(1.0));
CHECK(result[1].path[1].e_fraction == Approx(1.0));
}
TEST_CASE("Lineary reduce extrusion amount", "[Seams][Scarf]") {
using Seams::Scarf::Impl::lineary_readuce_extrusion_amount;
using GCode::SmoothPath, GCode::SmoothPathElement;
SmoothPath path{
{{}, {{Point::new_scale(0, 0)}, {Point::new_scale(1, 0)}}},
{{}, {{Point::new_scale(1, 0)}, {Point::new_scale(2, 0)}}},
};
SmoothPath result{lineary_readuce_extrusion_amount(std::move(path))};
CHECK(result[0].path[0].e_fraction == Approx(1.0));
CHECK(result[0].path[1].e_fraction == Approx(0.5));
CHECK(result[1].path[0].e_fraction == Approx(0.5));
CHECK(result[1].path[1].e_fraction == Approx(0.0));
}
TEST_CASE("Elevate scarf", "[Seams][Scarf]") {
using Seams::Scarf::Impl::elevate_scarf;
using Seams::Scarf::Impl::convert_to_smooth;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(1, 0),
Point::new_scale(2, 0),
Point::new_scale(3, 0),
};
const ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
{{points[2], points[3]}, {}},
};
const GCode::SmoothPath result{elevate_scarf(
paths,
1,
convert_to_smooth,
0.5
)};
REQUIRE(result.size() == 3);
REQUIRE(result[0].path.size() == 2);
CHECK(result[0].path[0].e_fraction == Approx(0.0));
CHECK(result[0].path[0].height_fraction == Approx(0.5));
CHECK(result[0].path[1].e_fraction == Approx(1.0));
CHECK(result[0].path[1].height_fraction == Approx(1.0));
REQUIRE(result[1].path.size() == 2);
CHECK(result[1].path[0].e_fraction == Approx(1.0));
CHECK(result[1].path[0].height_fraction == Approx(1.0));
CHECK(result[1].path[1].e_fraction == Approx(1.0));
CHECK(result[1].path[1].height_fraction == Approx(1.0));
REQUIRE(result[2].path.size() == 2);
CHECK(result[2].path[0].e_fraction == Approx(1.0));
CHECK(result[2].path[0].height_fraction == Approx(1.0));
CHECK(result[2].path[1].e_fraction == Approx(0.0));
CHECK(result[2].path[1].height_fraction == Approx(1.0));
}
TEST_CASE("Get point offset from the path end", "[Seams][Scarf]") {
using Seams::Scarf::Impl::get_point_offset_from_end;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(1, 0),
Point::new_scale(2, 0),
Point::new_scale(3, 0),
};
const ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
{{points[2], points[3]}, {}},
};
std::optional<PathPoint> result{get_point_offset_from_end(paths, scaled(1.6))};
REQUIRE(result);
CHECK(result->point == Point::new_scale(1.4, 0));
CHECK(result->previous_point_on_path_index == 0);
CHECK(result->path_index == 1);
}
TEST_CASE("Find point on path from the path end", "[Seams][Scarf]") {
using Seams::Scarf::Impl::get_point_offset_from_end;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(1, 0),
Point::new_scale(2, 0),
Point::new_scale(3, 0),
Point::new_scale(4, 0),
};
const ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
{{points[2], points[3], points[4]}, {}},
};
const auto point{Point::new_scale(3.4, 0)};
std::optional<PathPoint> result{Seams::Scarf::Impl::find_path_point_from_end(paths, point, scaled(1e-2))};
REQUIRE(result);
CHECK(result->point == point);
CHECK(result->previous_point_on_path_index == 1);
CHECK(result->path_index == 2);
}
TEST_CASE("Add scarf seam", "[Seams][Scarf]") {
using Seams::Scarf::add_scarf_seam;
using Seams::Scarf::Impl::convert_to_smooth;
using Seams::Scarf::Impl::get_length;
using Seams::Scarf::Scarf;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(1, 0),
Point::new_scale(1, 1),
Point::new_scale(0, 1),
Point::new_scale(0, 0),
};
const ExtrusionPaths paths{
{Polyline{points}, {}},
};
Scarf scarf{};
scarf.start_point = Point::new_scale(0.5, 0);
scarf.end_point = Point::new_scale(1, 0.5);
scarf.start_height = 0.2;
scarf.max_segment_length = 0.1;
scarf.end_point_previous_index = 1;
scarf.entire_loop = false;
const auto [path, wipe_offset]{add_scarf_seam(ExtrusionPaths{paths}, scarf, convert_to_smooth, false)};
REQUIRE(path.size() == 4);
CHECK(wipe_offset == 1);
REQUIRE(path.back().path.size() >= 1.0 / scarf.max_segment_length);
CHECK(path.back().path.back().point == scarf.end_point);
CHECK(path.back().path.front().point == scarf.start_point);
CHECK(path.back().path.back().e_fraction == Approx(0));
REQUIRE(path.front().path.size() >= 1.0 / scarf.max_segment_length);
CHECK(path.front().path.back().point == scarf.end_point);
CHECK(path.front().path.front().point == scarf.start_point);
CHECK(path.front().path.front().e_fraction == Approx(0));
CHECK(path.front().path.front().height_fraction == Approx(scarf.start_height));
CHECK(path.front().path[5].point == points[1]);
CHECK(path.front().path[5].e_fraction == Approx(0.5));
CHECK(path.front().path[5].height_fraction == Approx(0.6));
CHECK(path.back().path[5].e_fraction == Approx(0.5));
CHECK(path.back().path[5].height_fraction == Approx(1.0));
scarf.entire_loop = true;
const auto [loop_path, _]{add_scarf_seam(ExtrusionPaths{paths}, scarf, convert_to_smooth, false)};
CHECK(get_length(loop_path) == scaled(8.0));
REQUIRE(!loop_path.empty());
REQUIRE(!loop_path.front().path.empty());
CHECK(loop_path.front().path.front().point == scarf.end_point);
CHECK(loop_path.front().path.front().e_fraction == Approx(0));
REQUIRE(!loop_path.back().path.empty());
CHECK(loop_path.back().path.back().point == scarf.end_point);
CHECK(loop_path.front().path.at(20).e_fraction == Approx(0.5));
CHECK(loop_path.front().path.at(20).point == Point::new_scale(0, 0.5));
}

View File

@@ -10,8 +10,6 @@
using namespace Slic3r;
using namespace Slic3r::Seams;
constexpr bool debug_files{false};
struct ProjectionFixture
{
Polygon extrusion_path{