Prusa 2.7.2

This commit is contained in:
sunsets
2024-03-27 14:38:03 +08:00
parent 63daf0c087
commit 2387bc9cdb
203 changed files with 6053 additions and 15634 deletions

View File

@@ -2,6 +2,8 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests.cpp
test_line.cpp
test_point.cpp
test_3mf.cpp
test_aabbindirect.cpp
test_kdtreeindirect.cpp

View File

@@ -478,7 +478,7 @@ TEST_CASE("Arachne - #8849 - Missing part of model", "[ArachneMissingPart8849]")
export_perimeters_to_svg(debug_out_path("arachne-missing-part-8849.svg"), polygons, perimeters, union_ex(wall_tool_paths.getInnerContour()));
#endif
int64_t total_extrusion_length = 0;
[[maybe_unused]] int64_t total_extrusion_length = 0;
for (Arachne::VariableWidthLines &perimeter : perimeters)
for (Arachne::ExtrusionLine &extrusion_line : perimeter)
total_extrusion_length += extrusion_line.getLength();

View File

@@ -280,7 +280,6 @@ TEST_CASE("least squares arc fitting, interpolating end points", "[ArcWelder]")
REQUIRE(new_center_opt);
if (new_center_opt) {
Vec2d new_center = *new_center_opt;
double new_radius = (new_center - start_pos).norm();
double total_deviation = 0;
double new_total_deviation = 0;
for (const Vec2d &s : samples) {

View File

@@ -11,6 +11,242 @@
using namespace Slic3r;
TEST_CASE("Dynamic config serialization - tests ConfigBase", "[Config]"){
DynamicPrintConfig config;
INFO("Serialize float");
config.set_key_value("layer_height", new ConfigOptionFloat(0.3));
CHECK(config.opt_serialize("layer_height") == "0.3");
INFO("Serialize int");
config.set_key_value("perimeters", new ConfigOptionInt(2));
CHECK(config.opt_serialize("perimeters") == "2");
INFO("Serialize float or percent");
config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(30, true));
CHECK(config.opt_serialize("first_layer_height") == "30%");
INFO("Serialize bool");
config.set_key_value("use_relative_e_distances", new ConfigOptionBool(true));
CHECK(config.opt_serialize("use_relative_e_distances") == "1");
INFO("Serialize enum");
config.set_key_value("gcode_flavor", new ConfigOptionEnum<GCodeFlavor>(gcfTeacup));
CHECK(config.opt_serialize("gcode_flavor") == "teacup");
INFO("Serialize string");
config.set_key_value("extrusion_axis", new ConfigOptionString("A"));
CHECK(config.opt_serialize("extrusion_axis") == "A");
INFO("Serialize string with newline");
config.set_key_value("notes", new ConfigOptionString("foo\nbar"));
CHECK(config.opt_serialize("notes") == "foo\\nbar");
config.set_deserialize_strict("notes", "bar\\nbaz");
INFO("Deserialize string with newline");
CHECK(config.opt_string("notes") == "bar\nbaz");
INFO("Serialize points");
config.set_key_value("extruder_offset", new ConfigOptionPoints({{10, 20}, {30, 45}}));
CHECK(config.opt_serialize("extruder_offset") == "10x20,30x45");
INFO("Deserialize points");
config.set_deserialize_strict("extruder_offset", "20x10");
CHECK(config.option<ConfigOptionPoints>("extruder_offset")->values == std::vector{Vec2d{20, 10}});
INFO("Serialize floats");
config.set_key_value("nozzle_diameter", new ConfigOptionFloats({0.2, 3}));
CHECK(config.opt_serialize("nozzle_diameter") == "0.2,3");
INFO("Deserialize floats");
config.set_deserialize_strict("nozzle_diameter", "0.1,0.4");
CHECK_THAT(config.option<ConfigOptionFloats>("nozzle_diameter")->values, Catch::Matchers::Approx(std::vector{0.1, 0.4}));
INFO("Deserialize floats from one value");
config.set_deserialize_strict("nozzle_diameter", "3");
CHECK_THAT(config.option<ConfigOptionFloats>("nozzle_diameter")->values, Catch::Matchers::Approx(std::vector{3.0}));
INFO("Serialize ints");
config.set_key_value("temperature", new ConfigOptionInts({180, 210}));
CHECK(config.opt_serialize("temperature") == "180,210");
INFO("Deserialize ints");
config.set_deserialize_strict("temperature", "195,220");
CHECK(config.option<ConfigOptionInts>("temperature")->values == std::vector{195,220});
INFO("Serialize bools");
config.set_key_value("wipe", new ConfigOptionBools({true, false}));
CHECK(config.opt_serialize("wipe") == "1,0");
INFO("Deserialize bools");
config.set_deserialize_strict("wipe", "0,1,1");
CHECK(config.option<ConfigOptionBools>("wipe")->values == std::vector<unsigned char>{false, true, true});
INFO("Deserialize bools from empty stirng");
config.set_deserialize_strict("wipe", "");
CHECK(config.option<ConfigOptionBools>("wipe")->values == std::vector<unsigned char>{});
INFO("Deserialize bools from value");
config.set_deserialize_strict({{"wipe", 1}});
CHECK(config.option<ConfigOptionBools>("wipe")->values == std::vector<unsigned char>{true});
INFO("Serialize strings");
config.set_key_value("post_process", new ConfigOptionStrings({"foo", "bar"}));
CHECK(config.opt_serialize("post_process") == "foo;bar");
INFO("Deserialize strings");
config.set_deserialize_strict("post_process", "bar;baz");
CHECK(config.option<ConfigOptionStrings>("post_process")->values == std::vector<std::string>{"bar", "baz"});
}
TEST_CASE("Get keys", "[Config]"){
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
CHECK(!config.keys().empty());
}
TEST_CASE("Set not already set option", "[Config]") {
DynamicPrintConfig config;
config.set_deserialize_strict("filament_diameter", "3");
}
TEST_CASE("Config apply dynamic to static", "[Config]") {
DynamicPrintConfig config;
config.set_deserialize_strict("perimeters", "2");
// This trick is taken directly from perl.
StaticPrintConfig* config2 = static_cast<GCodeConfig*>(new FullPrintConfig());
config2->apply(config, true);
CHECK(config2->opt_int("perimeters") == 2);
delete config2;
}
TEST_CASE("Config apply static to dynamic", "[Config]") {
// This trick is taken directly from perl.
StaticPrintConfig* config = static_cast<GCodeConfig*>(new FullPrintConfig());
DynamicPrintConfig config2;
config2.apply(*config, true);
delete config;
CHECK(
config2.opt_int("perimeters") ==
DynamicPrintConfig::full_print_config().opt_int("perimeters")
);
}
TEST_CASE("Config apply dynamic to dynamic", "[Config]") {
DynamicPrintConfig config;
config.set_key_value("extruder_offset", new ConfigOptionPoints({{0, 0}, {20, 0}, {0, 20}}));
DynamicPrintConfig config2;
config2.apply(config, true);
CHECK(
config2.option<ConfigOptionPoints>("extruder_offset")->values ==
std::vector<Vec2d>{{0, 0}, {20, 0}, {0, 20}}
);
}
TEST_CASE("Get abs value on percent", "[Config]") {
StaticPrintConfig* config = static_cast<GCodeConfig*>(new FullPrintConfig());
config->set_deserialize_strict("solid_infill_speed", "60");
config->set_deserialize_strict("top_solid_infill_speed", "10%");
CHECK(config->get_abs_value("top_solid_infill_speed") == 6);
delete config;
}
TEST_CASE("No interference between DynamicConfig objects", "[Config]") {
DynamicPrintConfig config;
config.set_key_value("fill_pattern", new ConfigOptionString("line"));
DynamicPrintConfig config2;
config2.set_key_value("fill_pattern", new ConfigOptionString("hilbertcurve"));
CHECK(config.opt_string("fill_pattern") == "line");
}
TEST_CASE("Normalize fdm extruder", "[Config]") {
DynamicPrintConfig config;
config.set("extruder", 2, true);
config.set("perimeter_extruder", 3, true);
config.normalize_fdm();
INFO("Extruder option is removed after normalize().");
CHECK(!config.has("extruder"));
INFO("Undefined extruder is populated with default extruder.");
CHECK(config.opt_int("infill_extruder") == 2);
INFO("Defined extruder is not overwritten by default extruder.");
CHECK(config.opt_int("perimeter_extruder") == 3);
}
TEST_CASE("Normalize fdm infill extruder", "[Config]") {
DynamicPrintConfig config;
config.set("infill_extruder", 2, true);
config.normalize_fdm();
INFO("Undefined solid infill extruder is populated with infill extruder.");
CHECK(config.opt_int("solid_infill_extruder") == 2);
}
TEST_CASE("Normalize fdm retract layer change", "[Config]") {
DynamicPrintConfig config;
config.set("spiral_vase", true, true);
config.set_key_value("retract_layer_change", new ConfigOptionBools({true, false}));
config.normalize_fdm();
CHECK(config.option<ConfigOptionBools>("retract_layer_change")->values == std::vector<unsigned char>{0, 0});
}
TEST_CASE("Can read ini with invalid items", "[Config]") {
std::string path = std::string(TEST_DATA_DIR) + "/test_config/bad_config_options.ini";
DynamicPrintConfig config;
config.load(path, ForwardCompatibilitySubstitutionRule::Disable);
//Did not crash.
}
struct SerializationTestData {
std::string name;
std::vector<std::string> values;
std::string serialized;
};
TEST_CASE("Config serialization of multiple values", "[Config]"){
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
std::vector<SerializationTestData> test_data{
{
"empty",
{},
""
},
{
"single empty",
{""},
"\"\""
},
{
"single noempty, simple",
{"RGB"},
"RGB"
},
{
"multiple noempty, simple",
{"ABC", "DEF", "09182745@!#$*(&"},
"ABC;DEF;09182745@!#$*(&"
},
{
"multiple, simple, some empty",
{"ABC", "DEF", "", "09182745@!#$*(&", ""},
"ABC;DEF;;09182745@!#$*(&;"
},
{
"complex",
{"some \"quoted\" notes", "yet\n some notes", "whatever \n notes", ""},
"\"some \\\"quoted\\\" notes\";\"yet\\n some notes\";\"whatever \\n notes\";"
}
};
for (const SerializationTestData& data : test_data) {
config.set_key_value("filament_notes", new ConfigOptionStrings(data.values));
CHECK(config.opt_serialize("filament_notes") == data.serialized);
config.set_deserialize_strict("filament_notes", "");
CHECK(config.option<ConfigOptionStrings>("filament_notes")->values == std::vector<std::string>{});
config.set_deserialize_strict("filament_notes", data.serialized);
CHECK(config.option<ConfigOptionStrings>("filament_notes")->values == data.values);
}
}
SCENARIO("Generic config validation performs as expected.", "[Config]") {
GIVEN("A config generated from default options") {
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();

View File

@@ -0,0 +1,59 @@
/**
* Ported from xs/t/10_line.t
*/
#include <catch2/catch.hpp>
#include <libslic3r/Line.hpp>
#include "test_utils.hpp"
using namespace Slic3r;
TEST_CASE("Line can be translated", "[Line]") {
Line line{{100, 100}, {200, 100}};
line.translate(10, -5);
CHECK(Points{line.a, line.b} == Points{{110, 95}, {210, 95}});
}
TEST_CASE("Check if lines are parallel", "[Line]") {
CHECK(Line{{0, 0}, {100, 0}}.parallel_to(Line{{200, 200}, {0, 200}}));
}
TEST_CASE("Parallel lines under angles", "[Line]") {
auto base_angle = GENERATE(0, M_PI/3, M_PI/2, M_PI);
Line line{{0, 0}, {100, 0}};
line.rotate(base_angle, {0, 0});
Line clone{line};
INFO("Line is parallel to self");
CHECK(line.parallel_to(clone));
clone.reverse();
INFO("Line is parallel to self + PI");
CHECK(line.parallel_to(clone));
INFO("Line is parallel to its direction");
CHECK(line.parallel_to(line.direction()));
INFO("Line is parallel to its direction + PI");
line.parallel_to(line.direction() + M_PI);
INFO("line is parallel to its direction - PI")
line.parallel_to(line.direction() - M_PI);
SECTION("Line is parallel within epsilon") {
clone = line;
clone.rotate(EPSILON/2, {0, 0});
CHECK(line.parallel_to(clone));
clone = line;
clone.rotate(-EPSILON/2, {0, 0});
CHECK(line.parallel_to(clone));
}
}
TEST_CASE("Intersection infinite", "[Line]") {
const Line a{{100, 0}, {200, 0}};
const Line b{{300, 300}, {300, 100}};
Point r;
a.intersection_infinite(b, &r);
CHECK(r == Point{300, 0});
}

View File

@@ -0,0 +1,46 @@
/**
* Ported from xs/t/03_point.t
* - it used to check ccw() but it does not exist anymore
* and cross product uses doubles
*/
#include <catch2/catch.hpp>
#include <libslic3r/Point.hpp>
#include "test_utils.hpp"
using namespace Slic3r;
TEST_CASE("Nearest point", "[Point]") {
const Point point{10, 15};
const Point point2{30, 15};
const Point nearest{nearest_point({point2, Point{100, 200}}, point).first};
CHECK(nearest == point2);
}
TEST_CASE("Distance to line", "[Point]") {
const Line line{{0, 0}, {100, 0}};
CHECK(line.distance_to(Point{0, 0}) == Approx(0));
CHECK(line.distance_to(Point{100, 0}) == Approx(0));
CHECK(line.distance_to(Point{50, 0}) == Approx(0));
CHECK(line.distance_to(Point{150, 0}) == Approx(50));
CHECK(line.distance_to(Point{0, 50}) == Approx(50));
CHECK(line.distance_to(Point{50, 50}) == Approx(50));
CHECK(line.perp_distance_to(Point{50, 50}) == Approx(50));
CHECK(line.perp_distance_to(Point{150, 50}) == Approx(50));
}
TEST_CASE("Distance to diagonal line", "[Point]") {
const Line line{{50, 50}, {125, -25}};
CHECK(std::abs(line.distance_to(Point{100, 0})) == Approx(0));
}
TEST_CASE("Perp distance to line does not overflow", "[Point]") {
const Line line{
{18335846, 18335845},
{18335846, 1664160},
};
CHECK(line.distance_to(Point{1664161, 18335848}) == Approx(16671685));
}

View File

@@ -5,6 +5,73 @@
using namespace Slic3r;
struct PolylineTestCase {
Polyline polyline{
{100, 100},
{200, 100},
{200, 200}
};
};
TEST_CASE_METHOD(PolylineTestCase, "Lines can be retrieved", "[Polyline]") {
CHECK(polyline.lines() == Lines{
{{100, 100}, {200, 100}},
{{200, 100}, {200, 200}},
});
}
TEST_CASE_METHOD(PolylineTestCase, "Clip", "[Polyline]") {
const double len = polyline.length();
polyline.clip_end(len/3);
CHECK(std::abs(polyline.length() - 2.0/3.0*len) < 1);
}
TEST_CASE_METHOD(PolylineTestCase, "Append", "[Polyline]") {
Polyline tested_polyline{polyline};
tested_polyline.append(tested_polyline);
Points expected{polyline.points};
expected.insert(expected.end(), polyline.points.begin(), polyline.points.end());
CHECK(tested_polyline.points == expected);
}
TEST_CASE_METHOD(PolylineTestCase, "Extend end", "[Polyline]") {
CHECK(polyline.length() == 100*2);
polyline.extend_end(50);
CHECK(polyline.length() == 100*2 + 50);
}
TEST_CASE_METHOD(PolylineTestCase, "Extend start", "[Polyline]") {
CHECK(polyline.length() == 100*2);
polyline.extend_start(50);
CHECK(polyline.length() == 100*2 + 50);
}
TEST_CASE_METHOD(PolylineTestCase, "Split", "[Polyline]") {
Polyline p1;
Polyline p2;
const Point point{150, 100};
polyline.split_at(point, &p1, &p2);
CHECK(p1.size() == 2);
CHECK(p2.size() == 3);
CHECK(p1.last_point() == point);
CHECK(p2.first_point() == point);
}
TEST_CASE_METHOD(PolylineTestCase, "Split at first point", "[Polyline]") {
Polyline to_split{
polyline.points[0],
polyline.points[1],
polyline.points[2],
polyline.points[0]
};
Polyline p1;
Polyline p2;
to_split.split_at(to_split.first_point(), &p1, &p2);
CHECK(p1.size() == 1);
CHECK(p2.size() == 4);
}
SCENARIO("Simplify polyne, template", "[Polyline]")
{
Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} };
@@ -43,4 +110,23 @@ SCENARIO("Simplify polyline", "[Polyline]")
}
}
}
GIVEN("polyline 3") {
auto polyline = Polyline{ {0,0}, {100,0}, {50,10} };
WHEN("simplified with Douglas-Peucker") {
polyline.simplify(25.);
THEN("not simplified") {
REQUIRE(polyline == Polyline{ {0,0}, {100, 0}, {50,10} });
}
}
}
GIVEN("polyline 4") {
auto polyline = Polyline{ {0,0}, {20,0}, {50,0}, {80,0}, {100,0} };
WHEN("simplified with Douglas-Peucker") {
polyline.simplify(2.);
THEN("not simplified") {
REQUIRE(polyline == Polyline{ {0,0}, {100,0} });
}
}
}
}

View File

@@ -6,7 +6,7 @@ using namespace Slic3r;
using namespace SupportSpotsGenerator;
TEST_CASE("Numerical integral calculation compared with exact solution.", "[SupportSpotsGenerator]") {
namespace Rectangle {
const float width = 10;
const float height = 20;
const Polygon polygon = {
@@ -15,13 +15,35 @@ TEST_CASE("Numerical integral calculation compared with exact solution.", "[Supp
scaled(Vec2f{width / 2, height / 2}),
scaled(Vec2f{-width / 2, height / 2})
};
}
const Integrals integrals{{polygon}};
CHECK(integrals.area == Approx(width * height));
TEST_CASE("Numerical integral over polygon calculation compared with exact solution.", "[SupportSpotsGenerator]") {
const Integrals integrals{Rectangle::polygon};
CHECK(integrals.area == Approx(Rectangle::width * Rectangle::height));
CHECK(integrals.x_i.x() == Approx(0));
CHECK(integrals.x_i.y() == Approx(0));
CHECK(integrals.x_i_squared.x() == Approx(std::pow(width, 3) * height / 12));
CHECK(integrals.x_i_squared.y() == Approx(width * std::pow(height, 3) / 12));
CHECK(integrals.x_i_squared.x() == Approx(std::pow(Rectangle::width, 3) * Rectangle::height / 12));
CHECK(integrals.x_i_squared.y() == Approx(Rectangle::width * std::pow(Rectangle::height, 3) / 12));
}
TEST_CASE("Integrals over multiple polygons", "[SupportSpotsGenerator]") {
const Integrals integrals{{Rectangle::polygon, Rectangle::polygon}};
CHECK(integrals.area == Approx(2 * Rectangle::width * Rectangle::height));
}
TEST_CASE("Numerical integral over line calculation compared with exact solution.", "[SupportSpotsGenerator]") {
const float length = 10;
const float width = 20;
const Polyline polyline{scaled(Vec2f{-length/2.0f, 0.0f}), scaled(Vec2f{length/2.0f, 0.0f})};
const Integrals integrals{{polyline}, {width}};
CHECK(integrals.area == Approx(length * width));
CHECK(integrals.x_i.x() == Approx(0));
CHECK(integrals.x_i.y() == Approx(0));
CHECK(integrals.x_i_squared.x() == Approx(std::pow(length, 3) * width / 12));
CHECK(integrals.x_i_squared.y() == Approx(length * std::pow(width, 3) / 12));
}
TEST_CASE("Moment values and ratio check.", "[SupportSpotsGenerator]") {
@@ -37,7 +59,7 @@ TEST_CASE("Moment values and ratio check.", "[SupportSpotsGenerator]") {
scaled(Vec2f{0, height})
};
const Integrals integrals{{polygon}};
const Integrals integrals{polygon};
const Vec2f x_axis{1, 0};
const float x_axis_moment = compute_second_moment(integrals, x_axis);
@@ -69,7 +91,7 @@ TEST_CASE("Moments calculation for rotated axis.", "[SupportSpotsGenerator]") {
scaled(Vec2f{77.56229640885199, 189.33057746591336})
};
Integrals integrals{{polygon}};
Integrals integrals{polygon};
// Meassured counterclockwise from (1, 0)
const float angle = 1.432f;
@@ -130,7 +152,7 @@ TEST_CASE_METHOD(ObjectPartFixture, "Constructing ObjectPart using extrusion col
std::nullopt
};
Integrals expected{{expected_polygon}};
Integrals expected{expected_polygon};
CHECK(part.connected_to_bed == true);
Vec3f volume_centroid{part.volume_centroid_accumulator / part.volume};

View File

@@ -62,7 +62,7 @@ TEST_CASE("Voronoi missing edges - points 12067", "[Voronoi]")
// Construction of the Voronoi Diagram.
VD vd;
construct_voronoi(pts.begin(), pts.end(), &vd);
vd.construct_voronoi(pts.begin(), pts.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-pts.svg").c_str(),
@@ -190,7 +190,7 @@ TEST_CASE("Voronoi missing edges - Alessandro gapfill 12707", "[Voronoi]")
Lines lines = to_lines(poly);
VD vd;
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-lines.svg").c_str(),
@@ -298,7 +298,7 @@ TEST_CASE("Voronoi weirdness", "[Voronoi]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-weirdness.svg").c_str(),
@@ -322,7 +322,7 @@ TEST_CASE("Voronoi division by zero 12903", "[Voronoi]")
}
VD vd;
construct_voronoi(pts.begin(), pts.end(), &vd);
vd.construct_voronoi(pts.begin(), pts.end());
#ifdef VORONOI_DEBUG_OUT
// Scale the voronoi vertices and input points, so that the dump_voronoi_to_svg will display them correctly.
@@ -1319,7 +1319,7 @@ TEST_CASE("Voronoi NaN coordinates 12139", "[Voronoi][!hide][!mayfail]")
#endif
VD vd;
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
for (const auto& edge : vd.edges())
if (edge.is_finite()) {
@@ -1360,7 +1360,7 @@ TEST_CASE("Voronoi offset", "[VoronoiOffset]")
VD vd;
Lines lines = to_lines(poly_with_hole);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
for (const OffsetTest &ot : {
OffsetTest { scale_(0.2), 1, 1 },
@@ -1426,7 +1426,7 @@ TEST_CASE("Voronoi offset 2", "[VoronoiOffset]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
for (const OffsetTest &ot : {
OffsetTest { scale_(0.2), 2, 2 },
@@ -1496,7 +1496,7 @@ TEST_CASE("Voronoi offset 3", "[VoronoiOffset]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
for (const OffsetTest &ot : {
OffsetTest { scale_(0.2), 2, 2 },
@@ -1747,7 +1747,7 @@ TEST_CASE("Voronoi offset with edge collapse", "[VoronoiOffset4]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
for (const OffsetTest &ot : {
OffsetTest { scale_(0.2), 2, 2 },
@@ -1858,7 +1858,7 @@ TEST_CASE("Voronoi offset 5", "[VoronoiOffset5]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
for (const OffsetTest &ot : {
OffsetTest { scale_(2.8), 1, 1 },
@@ -1916,7 +1916,7 @@ TEST_CASE("Voronoi skeleton", "[VoronoiSkeleton]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
Slic3r::Voronoi::annotate_inside_outside(vd, lines);
static constexpr double threshold_alpha = M_PI / 12.; // 30 degrees
std::vector<Vec2d> skeleton_edges = Slic3r::Voronoi::skeleton_edges_rough(vd, lines, threshold_alpha);
@@ -1966,7 +1966,7 @@ TEST_CASE("Voronoi missing vertex 1", "[VoronoiMissingVertex1]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-missing-vertex1-out.svg").c_str(), vd, Points(), lines);
#endif
@@ -2006,7 +2006,7 @@ TEST_CASE("Voronoi missing vertex 2", "[VoronoiMissingVertex2]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-missing-vertex2-out.svg").c_str(), vd, Points(), lines);
#endif
@@ -2047,7 +2047,7 @@ TEST_CASE("Voronoi missing vertex 3", "[VoronoiMissingVertex3]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-missing-vertex3-out.svg").c_str(), vd, Points(), lines);
#endif
@@ -2091,8 +2091,8 @@ TEST_CASE("Voronoi missing vertex 4", "[VoronoiMissingVertex4]")
Geometry::VoronoiDiagram vd_2;
Lines lines_1 = to_lines(polygon_1);
Lines lines_2 = to_lines(polygon_2);
construct_voronoi(lines_1.begin(), lines_1.end(), &vd_1);
construct_voronoi(lines_2.begin(), lines_2.end(), &vd_2);
vd_1.construct_voronoi(lines_1.begin(), lines_1.end());
vd_2.construct_voronoi(lines_2.begin(), lines_2.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-missing-vertex4-1-out.svg").c_str(), vd_1, Points(), lines_1);
dump_voronoi_to_svg(debug_out_path("voronoi-missing-vertex4-2-out.svg").c_str(), vd_2, Points(), lines_2);
@@ -2124,7 +2124,7 @@ TEST_CASE("Duplicate Voronoi vertices", "[Voronoi]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-duplicate-vertices-out.svg").c_str(), vd, Points(), lines);
#endif
@@ -2164,7 +2164,7 @@ TEST_CASE("Intersecting Voronoi edges", "[Voronoi]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-intersecting-edges-out.svg").c_str(), vd, Points(), lines);
#endif
@@ -2226,10 +2226,66 @@ TEST_CASE("Non-planar voronoi diagram", "[VoronoiNonPlanar]")
VD vd;
Lines lines = to_lines(poly);
construct_voronoi(lines.begin(), lines.end(), &vd);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("voronoi-non-planar-out.svg").c_str(), vd, Points(), lines);
#endif
// REQUIRE(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(vd));
}
// This case is extracted from SPE-1729, where several ExPolygon with very thin lines
// and holes formed by very close (1-5nm) vertices that are on the edge of our resolution.
// Those thin lines and holes are both unprintable and cause the Voronoi diagram to be invalid.
TEST_CASE("Invalid Voronoi diagram - Thin lines - SPE-1729", "[InvalidVoronoiDiagramThinLinesSPE1729]")
{
Polygon contour = {
{32247689, -2405501},
{32247733, -2308514},
{32247692, -2405496},
{50484384, 332941},
{50374839, 1052546},
{32938040, -1637993},
{32938024, -1673788},
{32942107, 7220481},
{32252205, 7447599},
{32252476, 8037808},
{32555965, 8277599},
{17729260, 8904718},
{17729236, 8853233},
{17729259, 8904722},
{17039259, 8935481},
{17033440, -3880421},
{17204385, -3852156},
{17723645, -3441873},
{17723762, -3187210},
{17728957, 8240730},
{17728945, 8213866},
{31716233, 7614090},
{20801623, -1009882},
{21253963, -1580792},
{32252082, 7157187},
{32248022, -1673787},
{24245653, -2925506},
{18449246, -3809095},
{18728385, -4449246}
};
Polygon hole = {
{32247789, -2181284},
{32247870, -2003865},
{32247872, -2003866},
{32247752, -2267007}
};
Polygons polygons = {contour, hole};
VD vd;
Lines lines = to_lines(polygons);
vd.construct_voronoi(lines.begin(), lines.end());
#ifdef VORONOI_DEBUG_OUT
dump_voronoi_to_svg(debug_out_path("invalid-voronoi-diagram-thin-lines.svg").c_str(), vd, Points(), lines);
#endif
// REQUIRE(vd.is_valid());
}