update src and test

This commit is contained in:
QIDI TECH
2025-03-22 09:44:19 +08:00
parent b15deeb656
commit 7e7d699e43
151 changed files with 36981 additions and 1531 deletions

View File

@@ -14,6 +14,7 @@ add_executable(${_TEST_NAME}_tests
test_gaps.cpp
test_gcode.cpp
test_gcode_travels.cpp
test_infill_above_bridges.cpp
test_seam_perimeters.cpp
test_seam_shells.cpp
test_seam_geometry.cpp

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/benchmark/catch_benchmark_all.hpp>
#include "test_data.hpp"
#include "libslic3r/GCode/SeamGeometry.hpp"

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "test_data.hpp"

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/BridgeDetector.hpp>
#include <libslic3r/Geometry.hpp>

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <sstream>
#include <fstream>
@@ -7,6 +8,7 @@
using namespace Slic3r;
using namespace Test;
using namespace Catch;
constexpr bool debug_files{false};
@@ -168,7 +170,7 @@ TEST_CASE_METHOD(CancelObjectFixture, "Single extruder", "[CancelObject]") {
}
TEST_CASE_METHOD(CancelObjectFixture, "Sequential print", "[CancelObject]") {
config.set_deserialize_strict({{"complete_objects", 1}});
config.set_deserialize_strict({{"complete_objects", 1} });
Print print;
print.apply(two_cubes, config);

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "test_data.hpp"
#include "libslic3r/ClipperZUtils.hpp"

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>
#include <sstream>

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <exception>
#include <numeric>

View File

@@ -1,3 +1,4 @@
#include <catch2/catch_test_macros.hpp>
#include "test_data.hpp"
#include "libslic3r/TriangleMesh.hpp"
@@ -399,7 +400,6 @@ bool contains_regex(const std::string &data, const std::string &pattern)
} } // namespace Slic3r::Test
#include <catch2/catch.hpp>
SCENARIO("init_print functionality", "[test_data]") {
GIVEN("A default config") {

View File

@@ -14,7 +14,7 @@
#include "libslic3r/GCode/SeamPlacer.hpp"
#include "libslic3r/GCode/SeamAligned.hpp"
#include <filesystem>
#include <boost/filesystem.hpp>
#include <unordered_map>
namespace Slic3r { namespace Test {
@@ -159,7 +159,7 @@ std::string slice(
bool contains(const std::string &data, const std::string &pattern);
bool contains_regex(const std::string &data, const std::string &pattern);
inline std::unique_ptr<Print> process_3mf(const std::filesystem::path &path) {
inline std::unique_ptr<Print> process_3mf(const boost::filesystem::path &path) {
DynamicPrintConfig config;
auto print{std::make_unique<Print>()};
Model model;
@@ -176,7 +176,7 @@ inline std::unique_ptr<Print> process_3mf(const std::filesystem::path &path) {
static std::map<std::string, std::unique_ptr<Print>> prints_3mfs;
// Lazy getter, to avoid processing the 3mf multiple times, it already takes ages.
inline Print *get_print(const std::filesystem::path &file_path) {
inline Print *get_print(const boost::filesystem::path &file_path) {
if (!prints_3mfs.count(file_path.string())) {
prints_3mfs[file_path.string()] = process_3mf(file_path.string());
}
@@ -204,8 +204,8 @@ inline void serialize_seam(std::ostream &output, const std::vector<std::vector<S
struct SeamsFixture
{
const std::filesystem::path file_3mf{
std::filesystem::path{TEST_DATA_DIR} / std::filesystem::path{"seam_test_object.3mf"}};
const boost::filesystem::path file_3mf{
boost::filesystem::path{TEST_DATA_DIR} / boost::filesystem::path{"seam_test_object.3mf"}};
const Print *print{Test::get_print(file_3mf)};
const PrintObject *print_object{print->objects()[0]};

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <cstdlib>
@@ -11,6 +12,7 @@
#include "test_data.hpp"
using namespace Slic3r;
using namespace Catch;
static inline Slic3r::Point random_point(float LO=-50, float HI=50)
{

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>
#include <sstream>

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <numeric>
#include <sstream>
@@ -12,6 +13,7 @@
using namespace Slic3r::Test;
using namespace Slic3r;
using namespace Catch;
SCENARIO("Extrusion width specifics", "[Flow]") {

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <memory>
#include <regex>
@@ -10,6 +11,7 @@
using namespace Slic3r;
using namespace Test;
using namespace Catch;
constexpr bool debug_files = false;
@@ -61,7 +63,7 @@ TEST_CASE("Wiping speeds", "[GCode]") {
INFO("Wipe moves don\'t retract faster than configured speed");
CHECK(retract_speed < expected_retract_speed);
}
INFO("No wiping after layer change")
INFO("No wiping after layer change");
CHECK(!wiping_on_new_layer);
}
@@ -115,12 +117,12 @@ std::optional<double> parse_axis(const std::string& line, const std::string& axi
* - no travel moves go outside skirt
* - temperatures are set correctly
*/
TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
TEST_CASE("Extrusion, travels, temperatures", "[GCode]") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "gcode_comments", 1 },
{ "complete_objects", 1 },
{ "extrusion_axis", 'A' },
{ "extrusion_axis", "A" },
{ "start_gcode", "" }, // prevent any default extra Z move
{ "layer_height", 0.4 },
{ "first_layer_height", 0.4 },
@@ -170,6 +172,11 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
}
});
// Remove last travel_moves returning to origin
if (travel_moves.back().x() == 0 && travel_moves.back().y() == 0) {
travel_moves.pop_back();
}
const unsigned layer_count = 20 / 0.4;
INFO("Complete_objects generates the correct number of Z moves.");
CHECK(z_moves.size() == layer_count * 2);
@@ -188,20 +195,30 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
TEST_CASE("Used filament", "[GCode]") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "retract_length", "1000000" },
DynamicPrintConfig config1 = Slic3r::DynamicPrintConfig::full_print_config();
config1.set_deserialize_strict({
{ "retract_length", "0" },
{ "use_relative_e_distances", 1 },
{ "layer_gcode", "G92 E0\n" },
});
GCodeReader parser;
Print print;
Model model;
Test::init_print({TestMesh::cube_20x20x20}, print, model, config);
Test::gcode(print);
Print print1;
Model model1;
Test::init_print({TestMesh::cube_20x20x20}, print1, model1, config1);
Test::gcode(print1);
DynamicPrintConfig config2 = Slic3r::DynamicPrintConfig::full_print_config();
config2.set_deserialize_strict({
{ "retract_length", "999" },
{ "use_relative_e_distances", 1 },
{ "layer_gcode", "G92 E0\n" },
});
Print print2;
Model model2;
Test::init_print({TestMesh::cube_20x20x20}, print2, model2, config2);
Test::gcode(print2);
INFO("Final retraction is not considered in total used filament");
CHECK(print.print_statistics().total_used_filament > 0);
CHECK(print1.print_statistics().total_used_filament == print2.print_statistics().total_used_filament);
}
void check_m73s(Print& print){

View File

@@ -1,4 +1,6 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/GCode/Travels.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/GCode.hpp>
@@ -6,8 +8,9 @@
using namespace Slic3r;
using namespace Slic3r::GCode::Impl::Travels;
using namespace Catch;
struct ApproxEqualsPoints : public Catch::MatcherBase<Points> {
struct ApproxEqualsPoints : public Catch::Matchers::MatcherBase<Points> {
ApproxEqualsPoints(const Points& expected, unsigned tolerance): expected(expected), tolerance(tolerance) {}
bool match(const Points& points) const override {
if (points.size() != expected.size()) {

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <memory>

View File

@@ -1,10 +1,15 @@
#include <catch2/catch.hpp>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <memory>
#include "libslic3r/GCode/GCodeWriter.hpp"
#include "libslic3r/GCodeReader.hpp"
using namespace Slic3r;
using Catch::Approx;
SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
@@ -12,23 +17,523 @@ SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
GCodeWriter writer;
WHEN("set_speed is called to set speed to 99999.123") {
THEN("Output string is G1 F99999.123") {
REQUIRE_THAT(writer.set_speed(99999.123), Catch::Equals("G1 F99999.123\n"));
REQUIRE_THAT(writer.set_speed(99999.123), Catch::Matchers::Equals("G1 F99999.123\n"));
}
}
WHEN("set_speed is called to set speed to 1") {
THEN("Output string is G1 F1") {
REQUIRE_THAT(writer.set_speed(1.0), Catch::Equals("G1 F1\n"));
REQUIRE_THAT(writer.set_speed(1.0), Catch::Matchers::Equals("G1 F1\n"));
}
}
WHEN("set_speed is called to set speed to 203.200022") {
THEN("Output string is G1 F203.2") {
REQUIRE_THAT(writer.set_speed(203.200022), Catch::Equals("G1 F203.2\n"));
REQUIRE_THAT(writer.set_speed(203.200022), Catch::Matchers::Equals("G1 F203.2\n"));
}
}
WHEN("set_speed is called to set speed to 203.200522") {
THEN("Output string is G1 F203.201") {
REQUIRE_THAT(writer.set_speed(203.200522), Catch::Equals("G1 F203.201\n"));
REQUIRE_THAT(writer.set_speed(203.200522), Catch::Matchers::Equals("G1 F203.201\n"));
}
}
}
}
void check_gcode_feedrate(const std::string& gcode, const GCodeConfig& config, double expected_speed) {
GCodeReader parser;
parser.parse_buffer(gcode, [&] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
const double travel_speed = config.opt_float("travel_speed");
const double feedrate = line.has_f() ? line.f() : self.f();
CHECK(feedrate == Approx(expected_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
if (line.dist_Z(self) != 0) {
// lift move or lift + change layer
const double travel_speed_z = config.opt_float("travel_speed_z");
if (travel_speed_z) {
Vec3d move{line.dist_X(self), line.dist_Y(self), line.dist_Z(self)};
double move_u_z = move.z() / move.norm();
double travel_speed_ = std::abs(travel_speed_z / move_u_z);
INFO("move Z feedrate Z component is less than or equal to travel_speed_z");
CHECK(feedrate * std::abs(move_u_z) <= Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
if (travel_speed_ < travel_speed) {
INFO("move Z at travel speed Z");
CHECK(feedrate == Approx(travel_speed_ * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
INFO("move Z feedrate Z component is equal to travel_speed_z");
CHECK(feedrate * std::abs(move_u_z) == Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
} else {
INFO("move Z at travel speed");
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
}
} else {
INFO("move Z at travel speed");
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
}
} else if (not line.extruding(self)) {
// normal move
INFO("move XY at travel speed");
CHECK(feedrate == Approx(travel_speed * 60));
}
});
}
SCENARIO("travel_speed_z is zero should use travel_speed.", "[GCodeWriter]") {
GIVEN("GCodeWriter instance") {
GCodeWriter writer;
WHEN("travel_speed_z is set to 0") {
writer.config.travel_speed.value = 1000;
writer.config.travel_speed_z.value = 0;
THEN("XYZ move feed rate should be equal to travel_speed") {
const Vec3d move{10, 10, 10};
const double speed = writer.config.travel_speed.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
}
}
}
SCENARIO("travel_speed_z is respected in Z speed component.", "[GCodeWriter]") {
GIVEN("GCodeWriter instance") {
GCodeWriter writer;
WHEN("travel_speed_z is set to 10") {
writer.config.travel_speed.value = 1000;
writer.config.travel_speed_z.value = 10;
THEN("Z move feed rate should be equal to travel_speed_z") {
const Vec3d move{0, 0, 10};
const double speed = writer.config.travel_speed_z.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-Z move feed rate should be equal to travel_speed_z") {
const Vec3d move{0, 0, -10};
const double speed = writer.config.travel_speed_z.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("XY move feed rate should be equal to travel_speed") {
const Vec3d move{10, 10, 0};
const double speed = writer.config.travel_speed.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-XY move feed rate should be equal to travel_speed") {
const Vec3d move{-10, 10, 0};
const double speed = writer.config.travel_speed.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("X-Y move feed rate should be equal to travel_speed") {
const Vec3d move{10, -10, 0};
const double speed = writer.config.travel_speed.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-X-Y move feed rate should be equal to travel_speed") {
const Vec3d move{-10, -10, 0};
const double speed = writer.config.travel_speed.value;
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("XZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{10, 0, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
Vec3d p1 = writer.get_position();
Vec3d p2 = p1 + move;
std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-XZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{-10, 0, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("X-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{10, 0, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-X-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{-10, 0, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("YZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{0, 10, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-YZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{0, -10, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("Y-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{0, 10, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-Y-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{0, -10, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("XYZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{10, 10, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-XYZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{-10, 10, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("X-YZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{10, -10, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-X-YZ move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{-10, -10, 10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("XY-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{10, 10, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-XY-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{-10, 10, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("X-Y-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{10, -10, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
THEN("-X-Y-Z move feed rate Z component should be equal to travel_speed_z") {
const Vec3d move{-10, -10, -10};
const Vec3d move_u = move / move.norm();
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
const Vec3d p1 = writer.get_position();
const Vec3d p2 = p1 + move;
const std::string result = writer.travel_to_xyz(p2);
check_gcode_feedrate(result, writer.config, speed);
}
}
}
}
TEST_CASE("GCodeWriter emits G1 code correctly according to XYZF_EXPORT_DIGITS", "[GCodeWriter]") {
GCodeWriter writer;
SECTION("Check quantize") {
CHECK(GCodeFormatter::quantize(1.0,0) == 1.);
CHECK(GCodeFormatter::quantize(0.0,0) == 0.);
CHECK(GCodeFormatter::quantize(0.1,0) == 0);
CHECK(GCodeFormatter::quantize(1.0,1) == 1.);
CHECK(GCodeFormatter::quantize(0.0,1) == 0.);
CHECK(GCodeFormatter::quantize(0.1,1) == Approx(0.1));
CHECK(GCodeFormatter::quantize(0.01,1) == 0.);
CHECK(GCodeFormatter::quantize(1.0,2) == 1.);
CHECK(GCodeFormatter::quantize(0.0,2) == 0.);
CHECK(GCodeFormatter::quantize(0.1,2) == Approx(0.1));
CHECK(GCodeFormatter::quantize(0.01,2) == Approx(0.01));
CHECK(GCodeFormatter::quantize(0.001,2) == 0.);
CHECK(GCodeFormatter::quantize(1.0,3) == 1.);
CHECK(GCodeFormatter::quantize(0.0,3) == 0.);
CHECK(GCodeFormatter::quantize(0.1,3) == Approx(0.1));
CHECK(GCodeFormatter::quantize(0.01,3) == Approx(0.01));
CHECK(GCodeFormatter::quantize(0.001,3) == Approx(0.001));
CHECK(GCodeFormatter::quantize(0.0001,3) == 0.);
CHECK(GCodeFormatter::quantize(1.0,4) == 1.);
CHECK(GCodeFormatter::quantize(0.0,4) == 0.);
CHECK(GCodeFormatter::quantize(0.1,4) == Approx(0.1));
CHECK(GCodeFormatter::quantize(0.01,4) == Approx(0.01));
CHECK(GCodeFormatter::quantize(0.001,4) == Approx(0.001));
CHECK(GCodeFormatter::quantize(0.0001,4) == Approx(0.0001));
CHECK(GCodeFormatter::quantize(0.00001,4) == 0.);
CHECK(GCodeFormatter::quantize(1.0,5) == 1.);
CHECK(GCodeFormatter::quantize(0.0,5) == 0.);
CHECK(GCodeFormatter::quantize(0.1,5) == Approx(0.1));
CHECK(GCodeFormatter::quantize(0.01,5) == Approx(0.01));
CHECK(GCodeFormatter::quantize(0.001,5) == Approx(0.001));
CHECK(GCodeFormatter::quantize(0.0001,5) == Approx(0.0001));
CHECK(GCodeFormatter::quantize(0.00001,5) == Approx(0.00001));
CHECK(GCodeFormatter::quantize(0.000001,5) == 0.);
CHECK(GCodeFormatter::quantize(1.0,6) == 1.);
CHECK(GCodeFormatter::quantize(0.0,6) == 0.);
CHECK(GCodeFormatter::quantize(0.1,6) == Approx(0.1));
CHECK(GCodeFormatter::quantize(0.01,6) == Approx(0.01));
CHECK(GCodeFormatter::quantize(0.001,6) == Approx(0.001));
CHECK(GCodeFormatter::quantize(0.0001,6) == Approx(0.0001));
CHECK(GCodeFormatter::quantize(0.00001,6) == Approx(0.00001));
CHECK(GCodeFormatter::quantize(0.000001,6) == Approx(0.000001));
CHECK(GCodeFormatter::quantize(0.0000001,6) == 0.);
}
SECTION("Check pow_10") {
// IEEE 754 floating point numbers can represent these numbers EXACTLY.
CHECK(GCodeFormatter::pow_10[0] == 1.);
CHECK(GCodeFormatter::pow_10[1] == 10.);
CHECK(GCodeFormatter::pow_10[2] == 100.);
CHECK(GCodeFormatter::pow_10[3] == 1000.);
CHECK(GCodeFormatter::pow_10[4] == 10000.);
CHECK(GCodeFormatter::pow_10[5] == 100000.);
CHECK(GCodeFormatter::pow_10[6] == 1000000.);
CHECK(GCodeFormatter::pow_10[7] == 10000000.);
CHECK(GCodeFormatter::pow_10[8] == 100000000.);
CHECK(GCodeFormatter::pow_10[9] == 1000000000.);
}
SECTION("Check pow_10_inv") {
// IEEE 754 floating point numbers can NOT represent these numbers exactly.
CHECK(GCodeFormatter::pow_10_inv[0] == 1.);
CHECK(GCodeFormatter::pow_10_inv[1] == 0.1);
CHECK(GCodeFormatter::pow_10_inv[2] == 0.01);
CHECK(GCodeFormatter::pow_10_inv[3] == 0.001);
CHECK(GCodeFormatter::pow_10_inv[4] == 0.0001);
CHECK(GCodeFormatter::pow_10_inv[5] == 0.00001);
CHECK(GCodeFormatter::pow_10_inv[6] == 0.000001);
CHECK(GCodeFormatter::pow_10_inv[7] == 0.0000001);
CHECK(GCodeFormatter::pow_10_inv[8] == 0.00000001);
CHECK(GCodeFormatter::pow_10_inv[9] == 0.000000001);
}
SECTION("travel_to_z Emit G1 code for very significant movement") {
double z1 = 10.0;
std::string result1{ writer.travel_to_z(z1) };
CHECK(result1 == "G1 Z10 F7800\n");
double z2 = z1 * 2;
std::string result2{ writer.travel_to_z(z2) };
CHECK(result2 == "G1 Z20 F7800\n");
}
SECTION("travel_to_z Emit G1 code for significant movement") {
double z1 = 10.0;
std::string result1{ writer.travel_to_z(z1) };
CHECK(result1 == "G1 Z10 F7800\n");
// This should test with XYZ_EPSILON exactly,
// but IEEE 754 floating point numbers cannot pass the test.
double z2 = z1 + GCodeFormatter::XYZ_EPSILON * 1.001;
std::string result2{ writer.travel_to_z(z2) };
std::ostringstream oss;
oss << "G1 Z"
<< GCodeFormatter::quantize_xyzf(z2)
<< " F7800\n";
CHECK(result2 == oss.str());
}
SECTION("travel_to_z Do not emit G1 code for insignificant movement") {
double z1 = 10.0;
std::string result1{ writer.travel_to_z(z1) };
CHECK(result1 == "G1 Z10 F7800\n");
// Movement smaller than XYZ_EPSILON
double z2 = z1 + (GCodeFormatter::XYZ_EPSILON * 0.999);
std::string result2{ writer.travel_to_z(z2) };
CHECK(result2 == "");
double z3 = z1 + (GCodeFormatter::XYZ_EPSILON * 0.1);
std::string result3{ writer.travel_to_z(z3) };
CHECK(result3 == "");
}
SECTION("travel_to_xyz Emit G1 code for very significant movement") {
Vec3d v1{10.0, 10.0, 10.0};
std::string result1{ writer.travel_to_xyz(v1) };
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
Vec3d v2 = v1 * 2;
std::string result2{ writer.travel_to_xyz(v2) };
CHECK(result2 == "G1 X20 Y20 Z20 F7800\n");
}
SECTION("travel_to_xyz Emit G1 code for significant XYZ movement") {
Vec3d v1{10.0, 10.0, 10.0};
std::string result1{ writer.travel_to_xyz(v1) };
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
Vec3d v2 = v1;
// This should test with XYZ_EPSILON exactly,
// but IEEE 754 floating point numbers cannot pass the test.
v2.array() += GCodeFormatter::XYZ_EPSILON * 1.001;
std::string result2{ writer.travel_to_xyz(v2) };
std::ostringstream oss;
oss << "G1 X"
<< GCodeFormatter::quantize_xyzf(v2.x())
<< " Y"
<< GCodeFormatter::quantize_xyzf(v2.y())
<< " Z"
<< GCodeFormatter::quantize_xyzf(v2.z())
<< " F7800\n";
CHECK(result2 == oss.str());
}
SECTION("travel_to_xyz Emit G1 code for significant X movement") {
Vec3d v1{10.0, 10.0, 10.0};
std::string result1{ writer.travel_to_xyz(v1) };
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
Vec3d v2 = v1;
// This should test with XYZ_EPSILON exactly,
// but IEEE 754 floating point numbers cannot pass the test.
v2.x() += GCodeFormatter::XYZ_EPSILON * 1.001;
std::string result2{ writer.travel_to_xyz(v2) };
std::ostringstream oss;
// Only X needs to be emitted in this case,
// but this is how the code currently works.
oss << "G1 X"
<< GCodeFormatter::quantize_xyzf(v2.x())
<< " Y"
<< GCodeFormatter::quantize_xyzf(v2.y())
<< " F7800\n";
CHECK(result2 == oss.str());
}
SECTION("travel_to_xyz Emit G1 code for significant Y movement") {
Vec3d v1{10.0, 10.0, 10.0};
std::string result1{ writer.travel_to_xyz(v1) };
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
Vec3d v2 = v1;
// This should test with XYZ_EPSILON exactly,
// but IEEE 754 floating point numbers cannot pass the test.
v2.y() += GCodeFormatter::XYZ_EPSILON * 1.001;
std::string result2{ writer.travel_to_xyz(v2) };
std::ostringstream oss;
// Only Y needs to be emitted in this case,
// but this is how the code currently works.
oss << "G1 X"
<< GCodeFormatter::quantize_xyzf(v2.x())
<< " Y"
<< GCodeFormatter::quantize_xyzf(v2.y())
<< " F7800\n";
CHECK(result2 == oss.str());
}
SECTION("travel_to_xyz Emit G1 code for significant Z movement") {
Vec3d v1{10.0, 10.0, 10.0};
std::string result1{ writer.travel_to_xyz(v1) };
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
Vec3d v2 = v1;
// This should test with XYZ_EPSILON exactly,
// but IEEE 754 floating point numbers cannot pass the test.
v2.z() += GCodeFormatter::XYZ_EPSILON * 1.001;
std::string result2{ writer.travel_to_xyz(v2) };
std::ostringstream oss;
oss << "G1 Z"
<< GCodeFormatter::quantize_xyzf(v2.z())
<< " F7800\n";
CHECK(result2 == oss.str());
}
SECTION("travel_to_xyz Do not emit G1 code for insignificant movement") {
Vec3d v1{10.0, 10.0, 10.0};
std::string result1{ writer.travel_to_xyz(v1) };
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
// Movement smaller than XYZ_EPSILON
Vec3d v2 = v1;
v2.array() += GCodeFormatter::XYZ_EPSILON * 0.999;
std::string result2{ writer.travel_to_xyz(v2) };
CHECK(result2 == "");
Vec3d v3 = v1;
v3.array() += GCodeFormatter::XYZ_EPSILON * 0.1;
std::string result3{ writer.travel_to_xyz(v3) };
CHECK(result3 == "");
}
}

View File

@@ -0,0 +1,83 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/InfillAboveBridges.hpp>
using namespace Slic3r;
using Catch::Approx;
const ExPolygon square{
Point::new_scale(0, 0),
Point::new_scale(10, 0),
Point::new_scale(10, 10),
Point::new_scale(0, 10)
};
ExPolygon translate(const ExPolygon &polygon, const Point &offset) {
ExPolygons result{polygon};
translate(result, offset);
return result.front();
}
constexpr bool debug_files{false};
void draw_surfaces(const PrepareInfill::SurfaceRefsByRegion &surfaces, std::string_view file_name) {
using PrepareInfill::SurfaceCollectionRef;
SurfaceCollection to_display;
for (const SurfaceCollectionRef &surface_collection : surfaces) {
to_display.append(surface_collection.get());
}
to_display.export_to_svg(file_name.data(), false);
}
TEST_CASE("Separate infill above bridges", "[PrepareInfill]") {
ExPolygons layer_0_region_0_bridge{
square
};
ExPolygons layer_0_region_0_internal{
translate(square, Point::new_scale(10, 0))
};
ExPolygons layer_0_region_1_internal{
translate(square, Point::new_scale(0, 10))
};
ExPolygons layer_0_region_1_bridge{
translate(square, Point::new_scale(10, 10))
};
SurfaceCollection layer_0_region_0;
layer_0_region_0.append(layer_0_region_0_bridge, stBottomBridge);
layer_0_region_0.append(layer_0_region_0_internal, stInternal);
SurfaceCollection layer_0_region_1;
layer_0_region_1.append(layer_0_region_1_bridge, stBottomBridge);
layer_0_region_1.append(layer_0_region_1_internal, stInternal);
PrepareInfill::SurfaceRefsByRegion layer_0{layer_0_region_0, layer_0_region_1};
ExPolygons layer_1_region_0_solid{
translate(square, Point::new_scale(5, 5))
};
SurfaceCollection layer_1_region_0;
layer_1_region_0.append(layer_1_region_0_solid, stInternalSolid);
PrepareInfill::SurfaceRefsByRegion layer_1{layer_1_region_0};
if constexpr (debug_files) {
draw_surfaces(layer_0, "layer_0.svg");
}
PrepareInfill::separate_infill_above_bridges({layer_0, layer_1}, 0);
if constexpr (debug_files) {
draw_surfaces(layer_1, "layer_1.svg");
}
const Surfaces &result{layer_1.front().get().surfaces};
REQUIRE(result.size() == 4);
const double expected_area{scale_(5.0) * scale_(5.0)};
CHECK(result[0].expolygon.contour.area() == Approx(expected_area));
CHECK(result[0].surface_type == stInternalSolid);
CHECK(result[1].expolygon.contour.area() == Approx(expected_area));
CHECK(result[1].surface_type == stInternalSolid);
CHECK(result[2].expolygon.contour.area() == Approx(expected_area));
CHECK(result[2].surface_type == stSolidOverBridge);
CHECK(result[3].expolygon.contour.area() == Approx(expected_area));
CHECK(result[3].surface_type == stSolidOverBridge);
}

View File

@@ -2,11 +2,13 @@
* Ported from t/layers.t
*/
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "test_data.hpp"
using namespace Slic3r;
using namespace Slic3r::Test;
using namespace Catch;
void check_layers(const DynamicPrintConfig& config) {
GCodeReader parser;
@@ -27,10 +29,10 @@ void check_layers(const DynamicPrintConfig& config) {
const double layer_height = config.opt_float("layer_height");
INFO("Correct first layer height.");
CHECK(z.at(0) == Approx(first_layer_height + z_offset));
INFO("Correct second layer height")
INFO("Correct second layer height");
CHECK(z.at(1) == Approx(first_layer_height + layer_height + z_offset));
INFO("Correct layer height")
INFO("Correct layer height");
for (const double increment : tcb::span{increments}.subspan(1)) {
CHECK(increment == Approx(layer_height));
}

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Model.hpp"

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>
#include <sstream>

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>
#include <sstream>

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Print.hpp"

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/GCodeReader.hpp"
@@ -10,6 +11,7 @@
using namespace Slic3r;
using namespace Slic3r::Test;
using namespace Catch;
boost::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
boost::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Print.hpp"
@@ -8,6 +9,7 @@
using namespace Slic3r;
using namespace Slic3r::Test;
using namespace Catch;
SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
GIVEN("20mm cube and default initial config, initial layer height of 2mm") {

View File

@@ -2,10 +2,12 @@
* Ported from t/retraction.t
*/
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/GCodeReader.hpp>
#include <libslic3r/Config.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/GCode/GCodeWriter.hpp"
#include "libslic3r/Config.hpp"
#include "test_data.hpp"
#include <regex>
@@ -13,6 +15,7 @@
using namespace Slic3r;
using namespace Test;
using namespace Catch;
constexpr bool debug_files {false};
@@ -59,6 +62,10 @@ void check_gcode(std::initializer_list<TestMesh> meshes, const DynamicPrintConfi
const double retract_restart_extra = config.option<ConfigOptionFloats>("retract_restart_extra")->get_at(tool);
const double retract_restart_extra_toolchange = config.option<ConfigOptionFloats>("retract_restart_extra_toolchange")->get_at(tool);
const double travel_speed = config.opt_float("travel_speed");
const double feedrate = line.has_f() ? line.f() : self.f();
if (line.dist_Z(self) != 0) {
// lift move or lift + change layer
const double retract_lift = config.option<ConfigOptionFloats>("retract_lift")->get_at(tool);
@@ -77,7 +84,7 @@ void check_gcode(std::initializer_list<TestMesh> meshes, const DynamicPrintConfi
lift_dist = line.dist_Z(self);
}
if (line.dist_Z(self) < 0) {
INFO("Must be lifted before going down.")
INFO("Must be lifted before going down.");
CHECK(lifted);
INFO("Going down by the same amount of the lift or by the amount needed to get to next layer");
CHECK((
@@ -87,9 +94,26 @@ void check_gcode(std::initializer_list<TestMesh> meshes, const DynamicPrintConfi
lift_dist = 0;
lifted = false;
}
const double feedrate = line.has_f() ? line.f() : self.f();
INFO("move Z at travel speed");
CHECK(feedrate == Approx(config.opt_float("travel_speed") * 60));
const double travel_speed_z = config.opt_float("travel_speed_z");
if (travel_speed_z) {
Vec3d move{line.dist_X(self), line.dist_Y(self), line.dist_Z(self)};
const double move_u_z = move.z() / move.norm();
const double travel_speed_ = std::abs(travel_speed_z / move_u_z);
INFO("move Z feedrate Z component is less than or equal to travel_speed_z");
CHECK(feedrate * std::abs(move_u_z) <= Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
if (travel_speed_ < travel_speed) {
INFO("move Z at travel speed Z");
CHECK(feedrate == Approx(travel_speed_ * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
INFO("move Z feedrate Z component is equal to travel_speed_z");
CHECK(feedrate * std::abs(move_u_z) == Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
} else {
INFO("move Z at travel speed");
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
}
} else {
INFO("move Z at travel speed");
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
}
}
if (line.retracting(self)) {
retracted[tool] = true;
@@ -135,11 +159,6 @@ void test_slicing(std::initializer_list<TestMesh> meshes, DynamicPrintConfig& co
check_gcode(meshes, config, duplicate);
}
SECTION("Negative restart extra length") {
config.set_deserialize_strict({{ "retract_restart_extra", "-1" }});
check_gcode(meshes, config, duplicate);
}
SECTION("Retract_lift") {
config.set_deserialize_strict({{ "retract_lift", "1,2" }});
check_gcode(meshes, config, duplicate);
@@ -147,7 +166,7 @@ void test_slicing(std::initializer_list<TestMesh> meshes, DynamicPrintConfig& co
}
TEST_CASE("Slicing with retraction and lifing", "[retraction]") {
TEST_CASE("Slicing with retraction and lifting", "[retraction]") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "nozzle_diameter", "0.6,0.6,0.6,0.6" },
@@ -176,6 +195,37 @@ TEST_CASE("Slicing with retraction and lifing", "[retraction]") {
}
}
TEST_CASE("Slicing with retraction and lifting with travel_speed_z=10", "[retraction]") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "nozzle_diameter", "0.6,0.6,0.6,0.6" },
{ "first_layer_height", config.opt_float("layer_height") },
{ "first_layer_speed", "100%" },
{ "start_gcode", "" }, // To avoid dealing with the nozzle lift in start G-code
{ "retract_length", "1.5" },
{ "retract_before_travel", "3" },
{ "retract_layer_change", "1" },
{ "only_retract_when_crossing_perimeters", 0 },
{ "travel_speed", "600" },
{ "travel_speed_z", "10" },
});
SECTION("Standard run") {
test_slicing({TestMesh::cube_20x20x20}, config);
}
SECTION("With duplicate cube") {
test_slicing({TestMesh::cube_20x20x20}, config, 2);
}
SECTION("Dual extruder with multiple skirt layers") {
config.set_deserialize_strict({
{"infill_extruder", 2},
{"skirts", 4},
{"skirt_height", 3},
});
test_slicing({TestMesh::cube_20x20x20}, config);
}
}
TEST_CASE("Z moves", "[retraction]") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();

View File

@@ -1,11 +1,13 @@
#include <libslic3r/Point.hpp>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/GCode/SeamAligned.hpp>
#include "test_data.hpp"
#include <fstream>
using namespace Slic3r;
using namespace Slic3r::Seams;
using namespace Catch;
constexpr bool debug_files{false};

View File

@@ -1,9 +1,12 @@
#include <libslic3r/Point.hpp>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/GCode/SeamGeometry.hpp>
#include <libslic3r/Geometry.hpp>
using namespace Slic3r;
using namespace Catch;
TEST_CASE("Lists mapping", "[Seams][SeamGeometry]") {
// clang-format off
@@ -123,50 +126,3 @@ TEST_CASE("Vertex angle is rotation agnostic", "[Seams][SeamGeometry]") {
std::vector<double> rotated_angles = Seams::Geometry::get_vertex_angles(points, 0.1);
CHECK(rotated_angles[1] == Approx(angles[1]));
}
TEST_CASE("Calculate overhangs", "[Seams][SeamGeometry]") {
const ExPolygon square{
scaled(Vec2d{0.0, 0.0}),
scaled(Vec2d{1.0, 0.0}),
scaled(Vec2d{1.0, 1.0}),
scaled(Vec2d{0.0, 1.0})
};
const std::vector<Vec2d> points{Seams::Geometry::unscaled(square.contour.points)};
ExPolygon previous_layer{square};
previous_layer.translate(scaled(Vec2d{-0.5, 0}));
AABBTreeLines::LinesDistancer<Linef> previous_layer_distancer{
to_unscaled_linesf({previous_layer})};
const std::vector<double> overhangs{
Seams::Geometry::get_overhangs(points, previous_layer_distancer, 0.5)};
REQUIRE(overhangs.size() == points.size());
CHECK_THAT(overhangs, Catch::Matchers::Approx(std::vector<double>{
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

@@ -2,7 +2,8 @@
#include "libslic3r/GCode/SeamPerimeters.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Point.hpp"
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/GCode/SeamGeometry.hpp>
#include <libslic3r/Geometry.hpp>
#include <fstream>
@@ -11,26 +12,29 @@
using namespace Slic3r;
using namespace Slic3r::Seams;
using namespace Catch;
constexpr bool debug_files{false};
const ExPolygon square{
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("Oversample painted", "[Seams][SeamPerimeters]") {
Perimeters::PerimeterPoints square(4);
square[0].position = Vec2d{0.0, 0.0};
square[1].position = Vec2d{1.0, 0.0};
square[2].position = Vec2d{1.0, 1.0};
square[3].position = Vec2d{0.0, 1.0};
auto is_painted{[](const Vec3f &position, float radius) {
return (position - Vec3f{0.5, 0.0, 1.0}).norm() < radius;
}};
std::vector<Vec2d> points{Perimeters::Impl::oversample_painted(
Seams::Geometry::unscaled(square.contour.points), is_painted, 1.0, 0.2
Perimeters::PerimeterPoints points{Perimeters::Impl::oversample_painted(
square, is_painted, 1.0, 0.2
)};
REQUIRE(points.size() == 8);
CHECK((points[1] - Vec2d{0.2, 0.0}).norm() == Approx(0.0));
CHECK((points[1].position - Vec2d{0.2, 0.0}).norm() == Approx(0.0));
points = Perimeters::Impl::oversample_painted(
Seams::Geometry::unscaled(square.contour.points), is_painted, 1.0, 0.199
square, is_painted, 1.0, 0.199
);
CHECK(points.size() == 9);
}
@@ -39,24 +43,35 @@ TEST_CASE("Remove redundant points", "[Seams][SeamPerimeters]") {
using Perimeters::PointType;
using Perimeters::PointClassification;
std::vector<Vec2d> points{{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0},
{3.0, 1.0}, {3.0, 2.0}, {0.0, 2.0}};
std::vector<PointType> point_types{PointType::common,
PointType::enforcer, // Should keep this.
PointType::enforcer, // Should keep this.
PointType::blocker,
PointType::blocker, // Should remove this.
PointType::blocker, PointType::common};
Perimeters::PerimeterPoints points(9);
points[0].position = {0.0, 0.0};
points[0].type = PointType::common;
points[1].position = {1.0, 0.0};
points[1].type = PointType::enforcer; // Should keep
points[2].position = {2.0, 0.0};
points[2].type = PointType::enforcer; // Should keep
points[3].position = {3.0, 0.0};
points[3].type = PointType::blocker;
points[4].position = {3.0, 1.0};
points[4].type = PointType::blocker; // Should remove
points[5].position = {3.0, 1.1};
points[5].type = PointType::blocker;
points[6].position = {3.0, 1.2};
points[6].type = PointType::blocker;
points[6].classification = PointClassification::overhang; // Should keep
points[7].position = {3.0, 2.0};
points[7].type = PointType::blocker;
points[8].position = {0.0, 2.0};
points[8].type = PointType::common;
const auto [resulting_points, resulting_point_types]{
Perimeters::Impl::remove_redundant_points(points, point_types, 0.1)};
Perimeters::PerimeterPoints result{
Perimeters::Impl::remove_redundant_points(points, 0.1)};
REQUIRE(resulting_points.size() == 6);
REQUIRE(resulting_point_types.size() == 6);
CHECK((resulting_points[3] - Vec2d{3.0, 0.0}).norm() == Approx(0.0));
CHECK((resulting_points[4] - Vec2d{3.0, 2.0}).norm() == Approx(0.0));
CHECK(resulting_point_types[3] == PointType::blocker);
CHECK(resulting_point_types[4] == PointType::blocker);
REQUIRE(result.size() == 8);
CHECK((result[3].position - Vec2d{3.0, 0.0}).norm() == Approx(0.0));
CHECK((result[4].position - Vec2d{3.0, 1.1}).norm() == Approx(0.0));
CHECK(result[3].type == PointType::blocker);
CHECK(result[4].type == PointType::blocker);
}
TEST_CASE("Perimeter constructs KD trees", "[Seams][SeamPerimeters]") {
@@ -89,8 +104,6 @@ TEST_CASE("Perimeter constructs KD trees", "[Seams][SeamPerimeters]") {
CHECK(perimeter.common_points.embedded_points);
}
using std::filesystem::path;
constexpr const char *to_string(Perimeters::PointType point_type) {
using Perimeters::PointType;
@@ -182,3 +195,57 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Create perimeters", "[Seams][SeamPerimeter
serialize_shells(csv, shells);
}
}
using Dir = Seams::Geometry::Direction1D;
Perimeters::Perimeter get_perimeter(){
Perimeters::Perimeter perimeter;
perimeter.positions = {
Vec2d{0.0, 0.0},
Vec2d{1.0, 0.0},
Vec2d{1.0, 1.0},
Vec2d{0.0, 1.0}
};
return perimeter;
}
TEST_CASE("Offset along perimeter forward", "[Seams][SeamPerimeters]") {
const std::optional<Perimeters::PointOnPerimeter> result{Perimeters::offset_along_perimeter(
{0, 1, {0.5, 0.0}}, get_perimeter(), 3.9, Dir::forward,
[](const Perimeters::Perimeter &, const std::size_t) { return false; }
)};
REQUIRE(result);
const auto &[previous_index, next_index, point] = *result;
CHECK((scaled(point) - Point::new_scale(0.4, 0.0)).norm() < scaled(EPSILON));
CHECK(previous_index == 0);
CHECK(next_index == 1);
}
TEST_CASE("Offset along perimeter backward", "[Seams][SeamPerimeters]") {
const std::optional<Perimeters::PointOnPerimeter> result{Perimeters::offset_along_perimeter(
{1, 2, {1.0, 0.5}}, get_perimeter(), 1.8, Dir::backward,
[](const Perimeters::Perimeter &, const std::size_t) { return false; }
)};
REQUIRE(result);
const auto &[previous_index, next_index, point] = *result;
CHECK((scaled(point) - Point::new_scale(0.0, 0.3)).norm() < scaled(EPSILON));
CHECK(previous_index == 3);
CHECK(next_index == 0);
}
TEST_CASE("Offset along perimeter forward respects stop condition", "[Seams][SeamPerimeters]") {
Perimeters::Perimeter perimeter{get_perimeter()};
perimeter.point_types = std::vector<Perimeters::PointType>(perimeter.positions.size(), Perimeters::PointType::common);
perimeter.point_types[2] = Perimeters::PointType::blocker;
const std::optional<Perimeters::PointOnPerimeter> result{Perimeters::offset_along_perimeter(
{0, 1, {0.5, 0.0}}, perimeter, 3.9, Dir::forward,
[](const Perimeters::Perimeter &perimeter, const std::size_t index) {
return perimeter.point_types[index] == Perimeters::PointType::blocker;
}
)};
REQUIRE(result);
const auto &[previous_index, next_index, point] = *result;
CHECK((scaled(point) - Point::new_scale(1.0, 0.0)).norm() < scaled(EPSILON));
CHECK(previous_index == 1);
CHECK(next_index == 1);
}

View File

@@ -1,5 +1,5 @@
#include <libslic3r/Point.hpp>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/GCode/SeamRandom.hpp>
#include "test_data.hpp"
#include <fstream>
@@ -32,7 +32,7 @@ Perimeters::Perimeter get_perimeter() {
}
} // namespace RandomTest
double get_chi2_uniform(const std::vector<double> &data, double min, double max, const std::size_t bin_count) {
double get_chi2_uniform(const std::vector<double> &data, const double min, const 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};
@@ -62,7 +62,7 @@ TEST_CASE("Random is uniform", "[Seams][SeamRandom]") {
return choice->position.x();
});
const std::size_t degrees_of_freedom{10};
const double critical{18.307}; // dof 10, significance 0.05
const double critical{29.588}; // dof 10, significance 0.001
CHECK(get_chi2_uniform(x_positions, 0.0, 1.0, degrees_of_freedom + 1) < critical);
}

View File

@@ -1,5 +1,5 @@
#include <libslic3r/Point.hpp>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/GCode/SeamRear.hpp>
#include "test_data.hpp"
#include <fstream>

View File

@@ -1,9 +1,11 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/GCode/SeamScarf.hpp>
#include <libslic3r/GCode/SmoothPath.hpp>
using namespace Slic3r;
using Seams::Scarf::Impl::PathPoint;
using namespace Catch;
TEST_CASE("Get path point", "[Seams][Scarf]") {
using Seams::Scarf::Impl::get_path_point;

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <filesystem>
#include <fstream>
#include "libslic3r/ClipperUtils.hpp"
@@ -9,6 +10,7 @@
using namespace Slic3r;
using namespace Slic3r::Seams;
using namespace Catch;
struct ProjectionFixture
{
@@ -21,7 +23,12 @@ struct ProjectionFixture
double extrusion_width{0.2};
ProjectionFixture() {
extrusions.emplace_back(Polygon{extrusion_path}, extrusion_path.bounding_box(), extrusion_width, island_boundary);
extrusions.emplace_back(
Polygon{extrusion_path},
extrusion_path.bounding_box(),
extrusion_width, island_boundary,
Seams::Geometry::Overhangs{}
);
}
};

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/GCodeReader.hpp"

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Config.hpp"
@@ -10,6 +11,7 @@
using namespace Slic3r::Test;
using namespace Slic3r;
using namespace Catch;
/// Helper method to find the tool used for the brim (always the first extrusion)
static int get_brim_tool(const std::string &gcode)

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Layer.hpp"

View File

@@ -1,7 +1,8 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>
#include <sstream>
#include <algorithm>
#include "test_data.hpp" // get access to init_print, etc

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"