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

@@ -1,7 +1,9 @@
# TODO Add individual tests as executables in separate directories
# add_subirectory(<testcase>)
find_package(Catch2 2.9 REQUIRED)
find_package(Catch2 3.8 REQUIRED)
slic3r_remap_configs(Catch2::Catch2 RelWithDebInfo Release)
slic3r_remap_configs(Catch2::Catch2WithMain RelWithDebInfo Release)
include(Catch)
@@ -13,7 +15,7 @@ set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.")
add_library(test_common INTERFACE)
target_include_directories(test_common INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
target_link_libraries(test_common INTERFACE Catch2::Catch2)
target_link_libraries(test_common INTERFACE Catch2::Catch2WithMain)
if (APPLE)
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)

View File

@@ -1,4 +1,7 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_approx.hpp>
#include "test_utils.hpp"
#include <libslic3r/Execution/ExecutionSeq.hpp>
@@ -36,6 +39,8 @@
#include <random>
using namespace Catch;
template<class ArrItem = Slic3r::arr2::ArrangeItem>
static std::vector<ArrItem> qidi_parts(double infl = 0.) {
using namespace Slic3r;

View File

@@ -1,4 +1,11 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include <catch2/generators/catch_generators_all.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_approx.hpp>
#include "test_utils.hpp"
#include <arrange-wrapper/Arrange.hpp>
@@ -11,6 +18,8 @@
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/Format/3mf.hpp"
using namespace Catch;
static Slic3r::Model get_example_model_with_20mm_cube()
{
using namespace Slic3r;

View File

@@ -4,46 +4,53 @@
#define CATCH_CONFIG_EXTERNAL_INTERFACES
#define CATCH_CONFIG_MAIN
// #define CATCH_CONFIG_DEFAULT_REPORTER "verboseconsole"
#include <catch2/catch.hpp>
#include <catch2/reporters/catch_reporter_streaming_base.hpp>
#include <catch2/catch_all.hpp>
namespace Catch {
struct VerboseConsoleReporter : public ConsoleReporter {
struct VerboseConsoleReporter : public StreamingReporterBase {
double duration = 0.;
using ConsoleReporter::ConsoleReporter;
using StreamingReporterBase::StreamingReporterBase;
static std::string getDescription() {
return "Verbose Console Reporter";
}
void testCaseStarting(TestCaseInfo const& _testInfo) override
{
Colour::use(Colour::Cyan);
stream << "Testing ";
Colour::use(Colour::None);
stream << _testInfo.name << std::endl;
ConsoleReporter::testCaseStarting(_testInfo);
//Colour::use(Colour::Cyan);
m_stream << "Testing ";
//Colour::use(Colour::None);
m_stream << _testInfo.name << std::endl;
StreamingReporterBase::testCaseStarting(_testInfo);
}
void sectionStarting(const SectionInfo &_sectionInfo) override
{
if (_sectionInfo.name != currentTestCaseInfo->name)
stream << _sectionInfo.name << std::endl;
m_stream << _sectionInfo.name << std::endl;
ConsoleReporter::sectionStarting(_sectionInfo);
StreamingReporterBase::sectionStarting(_sectionInfo);
}
void sectionEnded(const SectionStats &_sectionStats) override {
duration += _sectionStats.durationInSeconds;
ConsoleReporter::sectionEnded(_sectionStats);
StreamingReporterBase::sectionEnded(_sectionStats);
}
void testCaseEnded(TestCaseStats const& stats) override
{
if (stats.totals.assertions.allOk()) {
Colour::use(Colour::BrightGreen);
stream << "Passed";
Colour::use(Colour::None);
stream << " in " << duration << " [seconds]\n" << std::endl;
//Colour::use(Colour::BrightGreen);
m_stream << "Passed";
//Colour::use(Colour::None);
m_stream << " in " << duration << " [seconds]\n" << std::endl;
}
duration = 0.;
ConsoleReporter::testCaseEnded(stats);
StreamingReporterBase::testCaseEnded(stats);
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg height="20.860270" width="22.311550" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<marker id="endArrow" markerHeight="8" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="1" refY="5" viewBox="0 0 10 10">
<polyline fill="darkblue" points="0,0 10,5 0,10 1,5" />
</marker>
<rect fill='white' stroke='none' x='0' y='0' width='22.311550' height='20.860270'/>
<path d="M 1716256 -11015182 1795391 -11035439 1568498 -10955916 1631011 -10984708 1564236 -10960836 1564236 -10960837 1674546 -11011825 1791112 -11041943 z " style="fill: none; stroke: black; stroke-width: 0.000010; fill-type: evenodd" fill-opacity="0.000000" />
<path d="M 11.5202 10.5927 12.3116 10.7952 10.0426 10 10.6677 10.2879 10 10.0492 10 10.0492 11.1031 10.5591 12.2688 10.8603 z " style="fill: lightgray; stroke: black; stroke-width: 0.000000; fill-type: evenodd" fill-opacity="1.000000" />
<path d="M 10 10.0492 12.0868 10.7952 11.5202 10.5927 12.3116 10.7952 z " style="fill: gray; stroke: black; stroke-width: 0.000000; fill-type: evenodd" fill-opacity="1.000000" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 189 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 50 KiB

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"

View File

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

View File

@@ -1,5 +1,6 @@
#include <algorithm>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
#include <libslic3r/TriangleMesh.hpp>
@@ -7,6 +8,7 @@
#include <libslic3r/AABBTreeLines.hpp>
using namespace Slic3r;
using namespace Catch;
TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndirect]")
{

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
#include <random>
@@ -12,6 +13,7 @@
#include <libslic3r/libslic3r.h>
using namespace Slic3r;
using namespace Catch;
TEST_CASE("arc basics", "[ArcWelder]") {
using namespace Slic3r::Geometry;

View File

@@ -1,4 +1,8 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <algorithm>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/AStar.hpp"
@@ -6,6 +10,7 @@
#include "libslic3r/PointGrid.hpp"
using namespace Slic3r;
using namespace Catch;
TEST_CASE("Testing basic invariants of AStar", "[AStar]") {
struct DummyTracer {

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <iostream>
#include <boost/filesystem.hpp>
@@ -8,6 +9,7 @@
#include "libslic3r/SVG.hpp"
using namespace Slic3r;
using namespace Catch;
// #define TESTS_EXPORT_SVGS

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <numeric>
#include <iostream>
@@ -9,6 +10,7 @@
#include "libslic3r/SVG.hpp"
using namespace Slic3r;
using namespace Catch;
SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
// CCW oriented contour

View File

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

View File

@@ -1,4 +1,6 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintConfig.hpp"

View File

@@ -1,10 +1,13 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
#include <libslic3r/Geometry/Curves.hpp>
#include <libslic3r/Utils.hpp>
#include <libslic3r/SVG.hpp>
using namespace Catch;
TEST_CASE("Curves: cubic b spline fit test", "[Curves]") {
using namespace Slic3r;
using namespace Slic3r::Geometry;

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/CutSurface.hpp>
#include <libslic3r/TriangleMesh.hpp> // its_make_cube + its_merge

View File

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

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/Emboss.hpp>
#include <libslic3r/SVG.hpp> // only debug visualization

View File

@@ -1,10 +1,12 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/Point.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/ExPolygon.hpp"
using namespace Slic3r;
using namespace Catch;
static inline bool points_close(const Point &p1, const Point &p2)
{

View File

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

View File

@@ -1,6 +1,6 @@
#include <iostream>
#include <fstream>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/SLA/Hollowing.hpp"

View File

@@ -1,6 +1,7 @@
#include <iostream>
#include <fstream>
#include <catch2/catch.hpp>
#include <random>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"

View File

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

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <algorithm>
#include "libslic3r/KDTreeIndirect.hpp"
#include "libslic3r/Execution/ExecutionSeq.hpp"

View File

@@ -2,11 +2,13 @@
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/SVG.hpp"
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <libslic3r/LayerRegion.hpp>
using namespace Slic3r;
using namespace Slic3r::Algorithm;
using namespace Catch;
constexpr bool export_svgs = false;

View File

@@ -1,8 +1,10 @@
/**
* Ported from xs/t/10_line.t
*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch.hpp>
#include <libslic3r/Line.hpp>
#include <libslic3r/Line.hpp>
#include "test_utils.hpp"
@@ -37,7 +39,7 @@ TEST_CASE("Parallel lines under angles", "[Line]") {
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")
INFO("line is parallel to its direction - PI");
line.parallel_to(line.direction() - M_PI);
SECTION("Line is parallel within epsilon") {

View File

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

View File

@@ -1,10 +1,12 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/MeshBoolean.hpp>
using namespace Slic3r;
using namespace Catch;
TEST_CASE("CGAL and TriangleMesh conversions", "[MeshBoolean]") {
TriangleMesh sphere = make_sphere(1.);

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <queue>
#include <random>
#include <algorithm>
#include "libslic3r/MutablePriorityQueue.hpp"

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <test_utils.hpp>
#include <libslic3r/Optimize/BruteforceOptimizer.hpp>

View File

@@ -1,9 +1,11 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/PlaceholderParser.hpp"
#include "libslic3r/PrintConfig.hpp"
using namespace Slic3r;
using namespace Catch;
SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
PlaceholderParser parser;

View File

@@ -2,7 +2,7 @@
#define NOMINMAX
#endif
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>

View File

@@ -5,11 +5,13 @@
* and cross product uses doubles
*/
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
#include <libslic3r/Point.hpp>
#include "test_utils.hpp"
using namespace Slic3r;
using namespace Catch;
TEST_CASE("Nearest point", "[Point]") {
const Point point{10, 15};

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <igl/qslim.h>
#include <test_utils.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/Algorithm/RegionExpansion.hpp>
@@ -8,6 +9,7 @@
#include <libslic3r/SVG.cpp>
using namespace Slic3r;
using namespace Catch;
//#define DEBUG_TEMP_DIR "d:\\temp\\"

View File

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

View File

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

View File

@@ -1,9 +1,12 @@
#include "libslic3r/Point.hpp"
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <libslic3r/SupportSpotsGenerator.hpp>
using namespace Slic3r;
using namespace SupportSpotsGenerator;
using namespace Catch;
namespace Rectangle {
const float width = 10;

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/Triangulation.hpp>
#include <libslic3r/SVG.hpp> // only debug visualization

View File

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

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/Polyline.hpp>
@@ -6,6 +6,7 @@
#include <libslic3r/Geometry/VoronoiOffset.hpp>
#include <numeric>
#include <random>
//#define VORONOI_DEBUG_OUT

View File

@@ -4,6 +4,10 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp
sla_test_utils.hpp sla_test_utils.cpp
sla_supptgen_tests.cpp
sla_raycast_tests.cpp
sla_parabola_tests.cpp
sla_voronoi_graph_tests.cpp
sla_vectorUtils_tests.cpp
sla_lineUtils_tests.cpp
sla_supptreeutils_tests.cpp
sla_archive_readwrite_tests.cpp
sla_zcorrection_tests.cpp)
@@ -18,3 +22,11 @@ endif()
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})
if (WIN32)
# Adds a post-build copy of libgmp-10.dll
add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_PREFIX_PATH}/bin/libgmp-10.dll"
$<TARGET_FILE_DIR:${_TEST_NAME}_tests>)
endif()

View File

@@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <test_utils.hpp>
#include "libslic3r/SLAPrint.hpp"
@@ -6,6 +6,7 @@
#include "libslic3r/Format/SLAArchiveFormatRegistry.hpp"
#include "libslic3r/Format/SLAArchiveWriter.hpp"
#include "libslic3r/Format/SLAArchiveReader.hpp"
#include "libslic3r/FileReader.hpp"
#include <boost/filesystem.hpp>
@@ -20,7 +21,7 @@ TEST_CASE("Archive export test", "[sla_archives]") {
SLAPrint print;
SLAFullPrintConfig fullcfg;
auto m = Model::read_from_file(TEST_DATA_DIR PATH_SEPARATOR + std::string(pname) + ".obj", nullptr);
auto m = FileReader::load_model(TEST_DATA_DIR PATH_SEPARATOR + std::string(pname) + ".obj");
fullcfg.printer_technology.setInt(ptSLA); // FIXME this should be ensured
fullcfg.set("sla_archive_format", entry.id);

View File

@@ -0,0 +1,59 @@
#include <catch2/catch_test_macros.hpp>
#include <libslic3r/SLA/SupportIslands/LineUtils.hpp>
using namespace Slic3r;
using namespace Slic3r::sla;
TEST_CASE("Intersection point", "[Utils], [LineUtils]")
{
Point a1(0, 0);
Point b1(3, 6);
Line l1(a1, b1);
auto intersection = LineUtils::intersection(l1, Line(Point(0, 4),
Point(5, 4)));
CHECK(intersection.has_value());
Point i_point = intersection->cast<coord_t>();
CHECK(PointUtils::is_equal(i_point, Point(2, 4)));
// same line
auto bad_intersection = LineUtils::intersection(l1, l1);
CHECK(!bad_intersection.has_value());
// oposit direction
bad_intersection = LineUtils::intersection(l1, Line(b1, a1));
CHECK(!bad_intersection.has_value());
// parallel line
bad_intersection = LineUtils::intersection(l1, Line(a1 + Point(0, 1),
b1 + Point(0, 1)));
CHECK(!bad_intersection.has_value());
// out of line segment, but ray has intersection
Line l2(Point(0, 8), Point(6, 8));
intersection = LineUtils::intersection(l1, l2);
auto intersection2 = LineUtils::intersection(l2, l1);
CHECK(intersection.has_value());
CHECK(intersection2.has_value());
i_point = intersection->cast<coord_t>();
CHECK(PointUtils::is_equal(i_point, Point(4, 8)));
CHECK(PointUtils::is_equal(i_point, intersection2->cast<coord_t>()));
Line l3(Point(-2, -2), Point(1, -2));
intersection = LineUtils::intersection(l1, l3);
intersection2 = LineUtils::intersection(l3, l1);
CHECK(intersection.has_value());
CHECK(intersection2.has_value());
i_point = intersection->cast<coord_t>();
CHECK(PointUtils::is_equal(i_point, Point(-1, -2)));
CHECK(PointUtils::is_equal(i_point, intersection2->cast<coord_t>()));
}
TEST_CASE("Point belongs to line", "[Utils], [LineUtils]")
{
Line l(Point(10, 10), Point(50, 30));
CHECK(LineUtils::belongs(l, Point(30, 20)));
CHECK(!LineUtils::belongs(l, Point(30, 30)));
CHECK(LineUtils::belongs(l, Point(30, 30), 10.));
CHECK(!LineUtils::belongs(l, Point(30, 10)));
CHECK(!LineUtils::belongs(l, Point(70, 40)));
}

View File

@@ -0,0 +1,50 @@
#include "sla_test_utils.hpp"
#include <libslic3r/SLA/SupportIslands/ParabolaUtils.hpp>
using namespace Slic3r;
using namespace Slic3r::sla;
void parabola_check_length(const ParabolaSegment &parabola)
{
auto diffPoint = parabola.to - parabola.from;
double min = sqrt(diffPoint.x() * diffPoint.x() +
diffPoint.y() * diffPoint.y());
double max = static_cast<double>(diffPoint.x()) + diffPoint.y();
double len = ParabolaUtils::length(parabola);
double len2 = ParabolaUtils::length_by_sampling(parabola, 1.);
CHECK(fabs(len2 - len) < 1.);
CHECK(len >= min);
CHECK(len <= max);
}
// after generalization put to ParabolaUtils
double getParabolaY(const Parabola &parabola, double x)
{
double f = ParabolaUtils::focal_length(parabola);
Vec2d perp = parabola.directrix.normal().cast<double>();
// work only for test cases
if (perp.y() > 0.) perp *= -1.;
perp.normalize();
Vec2d v = parabola.focus.cast<double>() + perp * f;
return 1 / (4 * f) * (x - v.x()) * (x - v.x()) + v.y();
}
TEST_CASE("Parabola length", "[SupGen][Voronoi][Parabola]")
{
using namespace Slic3r::sla;
double scale = 1e6;
// U shape parabola
Parabola parabola_x2(Line({-1. * scale, -.25 * scale},
{1. * scale, -.25 * scale}),
Point(0. * scale, .25 * scale));
double from_x = 1 * scale;
double to_x = 3 * scale;
Point from(from_x, getParabolaY(parabola_x2, from_x));
Point to(to_x, getParabolaY(parabola_x2, to_x));
ParabolaSegment parabola_segment(parabola_x2, from, to);
parabola_check_length(parabola_segment);
}

View File

@@ -31,52 +31,6 @@ const char *const SUPPORT_TEST_MODELS[] = {
} // namespace
TEST_CASE("Support point generator should be deterministic if seeded",
"[SLASupportGeneration], [SLAPointGen]") {
TriangleMesh mesh = load_model("A_upsidedown.obj");
AABBMesh emesh{mesh};
sla::SupportTreeConfig supportcfg;
sla::SupportPointGenerator::Config autogencfg;
autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm);
sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}};
auto bb = mesh.bounding_box();
double zmin = bb.min.z();
double zmax = bb.max.z();
double gnd = zmin - supportcfg.object_elevation_mm;
auto layer_h = 0.05f;
auto slicegrid = grid(float(gnd), float(zmax), layer_h);
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, slicegrid, CLOSING_RADIUS);
point_gen.seed(0);
point_gen.execute(slices, slicegrid);
auto get_chksum = [](const std::vector<sla::SupportPoint> &pts){
int64_t chksum = 0;
for (auto &pt : pts) {
auto p = scaled(pt.pos);
chksum += p.x() + p.y() + p.z();
}
return chksum;
};
int64_t checksum = get_chksum(point_gen.output());
size_t ptnum = point_gen.output().size();
REQUIRE(point_gen.output().size() > 0);
for (int i = 0; i < 20; ++i) {
point_gen.output().clear();
point_gen.seed(0);
point_gen.execute(slices, slicegrid);
REQUIRE(point_gen.output().size() == ptnum);
REQUIRE(checksum == get_chksum(point_gen.output()));
}
}
TEST_CASE("Flat pad geometry is valid", "[SLASupportGeneration]") {
sla::PadConfig padcfg;

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
#include <libslic3r/AABBMesh.hpp>
@@ -7,6 +8,7 @@
#include "sla_test_utils.hpp"
using namespace Slic3r;
using namespace Catch;
// First do a simple test of the hole raycaster.
TEST_CASE("Raycaster - find intersections of a line and cylinder")

View File

@@ -1,20 +1,32 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <test_utils.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/SLA/SpatIndex.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/TriangleMeshSlicer.hpp>
#include <libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp>
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp>
#include <libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp>
#include <libslic3r/SLA/SupportIslands/PolygonUtils.hpp>
#include "nanosvg/nanosvg.h" // load SVG file
#include "sla_test_utils.hpp"
namespace Slic3r { namespace sla {
using namespace Slic3r;
using namespace Slic3r::sla;
//#define STORE_SAMPLE_INTO_SVG_FILES "C:/data/temp/test_islands/sample_"
//#define STORE_ISLAND_ISSUES "C:/data/temp/issues/"
TEST_CASE("Overhanging point should be supported", "[SupGen]") {
// Pyramid with 45 deg slope
TriangleMesh mesh = make_pyramid(10.f, 10.f);
mesh.rotate_y(float(PI));
mesh.WriteOBJFile("Pyramid.obj");
//mesh.WriteOBJFile("Pyramid.obj");
sla::SupportPoints pts = calc_support_pts(mesh);
@@ -54,18 +66,11 @@ double min_point_distance(const sla::SupportPoints &pts)
TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") {
double width = 10., depth = 10., height = 1.;
TriangleMesh mesh = make_cube(width, depth, height);
TriangleMesh mesh = make_cube(width, depth, height);
mesh.translate(0., 0., 5.); // lift up
mesh.WriteOBJFile("Cuboid.obj");
sla::SupportPointGenerator::Config cfg;
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
double mm2 = width * depth;
// mesh.WriteOBJFile("Cuboid.obj");
sla::SupportPoints pts = calc_support_pts(mesh);
REQUIRE(!pts.empty());
REQUIRE(pts.size() * cfg.support_force() > mm2 * cfg.tear_pressure());
REQUIRE(min_point_distance(pts) >= cfg.minimal_distance);
}
template<class M> auto&& center_around_bb(M &&mesh)
@@ -84,8 +89,7 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") {
mesh.translate(0., 0., height);
mesh.WriteOBJFile("Prism.obj");
sla::SupportPointGenerator::Config cfg;
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
sla::SupportPoints pts = calc_support_pts(mesh);
Linef3 overh{ {0.f, -depth / 2.f, 0.f}, {0.f, depth / 2.f, 0.f}};
@@ -97,9 +101,8 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") {
return line_alg::distance_to(overh, Vec3d{pt.pos.cast<double>()}) < 1.;
});
REQUIRE(overh_pts.size() * cfg.support_force() > overh.length() * cfg.tear_pressure());
double ddiff = min_point_distance(pts) - cfg.minimal_distance;
REQUIRE(ddiff > - 0.1 * cfg.minimal_distance);
//double ddiff = min_point_distance(pts) - cfg.minimal_distance;
//REQUIRE(ddiff > - 0.1 * cfg.minimal_distance);
}
TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowed]") {
@@ -114,9 +117,9 @@ TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowe
Vec3f mv = bb.center().cast<float>() - Vec3f{0.f, 0.f, 0.5f * h};
mesh.translate(-mv);
sla::SupportPointGenerator::Config cfg;
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON);
sla::SupportPoints pts = calc_support_pts(mesh);
//sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON);
REQUIRE(!pts.empty());
}
@@ -132,11 +135,497 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]")
mesh.WriteOBJFile("parallel_plates.obj");
sla::SupportPointGenerator::Config cfg;
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON);
sla::SupportPoints pts = calc_support_pts(mesh);
//sla::remove_bottom_points(pts, mesh.bounding_box().min.z() + EPSILON);
REQUIRE(!pts.empty());
}
}} // namespace Slic3r::sla
Slic3r::Polygon create_cross_roads(double size, double width)
{
auto r1 = PolygonUtils::create_rect(5.3 * size, width);
r1.rotate(3.14/4);
r1.translate(2 * size, width / 2);
auto r2 = PolygonUtils::create_rect(6.1 * size, 3 / 4. * width);
r2.rotate(-3.14 / 5);
r2.translate(3 * size, width / 2);
auto r3 = PolygonUtils::create_rect(7.9 * size, 4 / 5. * width);
r3.translate(2*size, width/2);
auto r4 = PolygonUtils::create_rect(5 / 6. * width, 5.7 * size);
r4.translate(-size,3*size);
Polygons rr = union_(Polygons({r1, r2, r3, r4}));
return rr.front();
}
ExPolygon create_trinagle_with_hole(double size)
{
auto hole = PolygonUtils::create_equilateral_triangle(size / 3);
hole.reverse();
hole.rotate(3.14 / 4);
return ExPolygon(PolygonUtils::create_equilateral_triangle(size), hole);
}
ExPolygon create_square_with_hole(double size, double hole_size)
{
assert(sqrt(hole_size *hole_size / 2) < size);
auto hole = PolygonUtils::create_square(hole_size);
hole.rotate(M_PI / 4.); // 45
hole.reverse();
return ExPolygon(PolygonUtils::create_square(size), hole);
}
ExPolygon create_square_with_4holes(double size, double hole_size) {
auto hole = PolygonUtils::create_square(hole_size);
hole.reverse();
double size_4 = size / 4;
auto h1 = hole;
h1.translate(size_4, size_4);
auto h2 = hole;
h2.translate(-size_4, size_4);
auto h3 = hole;
h3.translate(size_4, -size_4);
auto h4 = hole;
h4.translate(-size_4, -size_4);
ExPolygon result(PolygonUtils::create_square(size));
result.holes = Polygons({h1, h2, h3, h4});
return result;
}
// boudary of circle
ExPolygon create_disc(double radius, double width, size_t count_line_segments)
{
double width_2 = width / 2;
auto hole = PolygonUtils::create_circle(radius - width_2,
count_line_segments);
hole.reverse();
return ExPolygon(PolygonUtils::create_circle(radius + width_2,
count_line_segments),
hole);
}
Slic3r::Polygon create_V_shape(double height, double line_width, double angle = M_PI/4) {
double angle_2 = angle / 2;
auto left_side = PolygonUtils::create_rect(line_width, height);
auto right_side = left_side;
right_side.rotate(-angle_2);
double small_move = cos(angle_2) * line_width / 2;
double side_move = sin(angle_2) * height / 2 + small_move;
right_side.translate(side_move,0);
left_side.rotate(angle_2);
left_side.translate(-side_move, 0);
auto bottom = PolygonUtils::create_rect(4 * small_move, line_width);
bottom.translate(0., -cos(angle_2) * height / 2 + line_width/2);
Polygons polygons = union_(Polygons({left_side, right_side, bottom}));
return polygons.front();
}
ExPolygon create_tiny_wide_test_1(double wide, double tiny)
{
double hole_size = wide;
double width = 2 * wide + hole_size;
double height = wide + hole_size + tiny;
auto outline = PolygonUtils::create_rect(width, height);
auto hole = PolygonUtils::create_rect(hole_size, hole_size);
hole.reverse();
int hole_move_y = height/2 - (hole_size/2 + tiny);
hole.translate(0, hole_move_y);
ExPolygon result(outline);
result.holes = {hole};
return result;
}
ExPolygon create_tiny_wide_test_2(double wide, double tiny)
{
double hole_size = wide;
double width = (3 + 1) * wide + 3 * hole_size;
double height = 2*wide + 2*tiny + 3*hole_size;
auto outline = PolygonUtils::create_rect( width, height);
auto hole = PolygonUtils::create_rect(hole_size, hole_size);
hole.reverse();
auto hole2 = hole;// copy
auto hole3 = hole; // copy
auto hole4 = hole; // copy
int hole_move_x = wide + hole_size;
int hole_move_y = wide + hole_size;
hole.translate(hole_move_x, hole_move_y);
hole2.translate(-hole_move_x, hole_move_y);
hole3.translate(hole_move_x, -hole_move_y);
hole4.translate(-hole_move_x, -hole_move_y);
auto hole5 = PolygonUtils::create_circle(hole_size / 2, 16);
hole5.reverse();
auto hole6 = hole5; // copy
hole5.translate(0, hole_move_y);
hole6.translate(0, -hole_move_y);
auto hole7 = PolygonUtils::create_equilateral_triangle(hole_size);
hole7.reverse();
auto hole8 = PolygonUtils::create_circle(hole_size/2, 7, Point(hole_move_x,0));
hole8.reverse();
auto hole9 = PolygonUtils::create_circle(hole_size/2, 5, Point(-hole_move_x,0));
hole9.reverse();
ExPolygon result(outline);
result.holes = {hole, hole2, hole3, hole4, hole5, hole6, hole7, hole8, hole9};
return result;
}
ExPolygon create_tiny_between_holes(double wide, double tiny)
{
double hole_size = wide;
double width = 2 * wide + 2*hole_size + tiny;
double height = 2 * wide + hole_size;
auto outline = PolygonUtils::create_rect(width, height);
auto holeL = PolygonUtils::create_rect(hole_size, hole_size);
holeL.reverse();
auto holeR = holeL;
int hole_move_x = (hole_size + tiny)/2;
holeL.translate(-hole_move_x, 0);
holeR.translate(hole_move_x, 0);
ExPolygon result(outline);
result.holes = {holeL, holeR};
return result;
}
// stress test for longest path
// needs reshape
ExPolygon create_mountains(double size) {
return ExPolygon({{0., 0.},
{size, 0.},
{5 * size / 6, size},
{4 * size / 6, size / 6},
{3 * size / 7, 2 * size},
{2 * size / 7, size / 6},
{size / 7, size}});
}
/// Neighbor points create trouble for voronoi - test of neccessary offseting(closing) of contour
ExPolygon create_cylinder_bottom_slice() {
indexed_triangle_set its_cylinder = its_make_cylinder(6.6551999999999998, 11.800000000000001);
MeshSlicingParams param;
Polygons polygons = slice_mesh(its_cylinder, 0.0125000002, param);
return ExPolygon{polygons.front()};
}
ExPolygon load_frog(){
TriangleMesh mesh = load_model("frog_legs.obj");
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, {0.1f});
return slices.front()[1];
}
ExPolygon load_svg(const std::string& svg_filepath) {
struct NSVGimage *image = nsvgParseFromFile(svg_filepath.c_str(), "px", 96);
ScopeGuard sg_image([&image] { nsvgDelete(image); });
auto to_polygon = [](NSVGpath *path) {
Polygon r;
r.points.reserve(path->npts);
for (int i = 0; i < path->npts; i++)
r.points.push_back(Point(path->pts[2 * i], path->pts[2 * i + 1]));
return r;
};
for (NSVGshape *shape_ptr = image->shapes; shape_ptr != NULL; shape_ptr = shape_ptr->next) {
const NSVGshape &shape = *shape_ptr;
if (!(shape.flags & NSVG_FLAGS_VISIBLE)) continue; // is visible
if (shape.fill.type != NSVG_PAINT_NONE) continue; // is not used fill
if (shape.stroke.type == NSVG_PAINT_NONE) continue; // exist stroke
//if (shape.strokeWidth < 1e-5f) continue; // is visible stroke width
//if (shape.stroke.color != 4278190261) continue; // is red
ExPolygon result;
for (NSVGpath *path = shape.paths; path != NULL; path = path->next) {
// Path order is reverse to path in file
if (path->next == NULL) // last path is contour
result.contour = to_polygon(path);
else
result.holes.push_back(to_polygon(path));
}
return result;
}
REQUIRE(false);
return {};
}
ExPolygons createTestIslands(double size)
{
std::string dir = std::string(TEST_DATA_DIR PATH_SEPARATOR) + "sla_islands/";
bool useFrogLeg = false;
// need post reorganization of longest path
ExPolygons result = {
// one support point
ExPolygon(PolygonUtils::create_equilateral_triangle(size)),
ExPolygon(PolygonUtils::create_square(size)),
ExPolygon(PolygonUtils::create_rect(size / 2, size)),
ExPolygon(PolygonUtils::create_isosceles_triangle(size / 2, 3 * size / 2)), // small sharp triangle
ExPolygon(PolygonUtils::create_circle(size / 2, 10)),
create_square_with_4holes(size, size / 4),
create_disc(size/4, size / 4, 10),
ExPolygon(create_V_shape(2*size/3, size / 4)),
// two support points
ExPolygon(PolygonUtils::create_isosceles_triangle(size / 2, 3 * size)), // small sharp triangle
ExPolygon(PolygonUtils::create_rect(size / 2, 3 * size)),
ExPolygon(create_V_shape(1.5*size, size/3)),
// tiny line support points
ExPolygon(PolygonUtils::create_rect(size / 2, 10 * size)), // long line
ExPolygon(create_V_shape(size*4, size / 3)),
ExPolygon(create_cross_roads(size, size / 3)),
create_disc(3*size, size / 4, 30),
create_disc(2*size, size, 12), // 3 points
create_square_with_4holes(5 * size, 5 * size / 2 - size / 3),
// Tiny and wide part together with holes
ExPolygon(PolygonUtils::create_isosceles_triangle(5. * size, 40. * size)),
create_tiny_wide_test_1(3 * size, 2 / 3. * size),
create_tiny_wide_test_2(3 * size, 2 / 3. * size),
create_tiny_between_holes(3 * size, 2 / 3. * size),
ExPolygon(PolygonUtils::create_equilateral_triangle(scale_(18.6))),
create_cylinder_bottom_slice(),
load_svg(dir + "lm_issue.svg"), // change from thick to thin and vice versa on circle
load_svg(dir + "SPE-2674.svg"), // center of longest path lay inside of the VD node
load_svg(dir + "SPE-2674_2.svg"), // missing Voronoi vertex even after the rotation of input.
// still problem
// three support points
ExPolygon(PolygonUtils::create_equilateral_triangle(3 * size)),
ExPolygon(PolygonUtils::create_circle(size, 20)),
create_mountains(size),
create_trinagle_with_hole(size),
create_square_with_hole(size, size / 2),
create_square_with_hole(size, size / 3)
};
if (useFrogLeg)
result.push_back(load_frog());
return result;
}
Points createNet(const BoundingBox& bounding_box, double distance)
{
Point size = bounding_box.size();
double distance_2 = distance / 2;
int cols1 = static_cast<int>(floor(size.x() / distance))+1;
int cols2 = static_cast<int>(floor((size.x() - distance_2) / distance))+1;
// equilateral triangle height with side distance
double h = sqrt(distance * distance - distance_2 * distance_2);
int rows = static_cast<int>(floor(size.y() / h)) +1;
int rows_2 = rows / 2;
size_t count_points = rows_2 * (cols1 + static_cast<size_t>(cols2));
if (rows % 2 == 1) count_points += cols2;
Points result;
result.reserve(count_points);
bool isOdd = true;
Point offset = bounding_box.min;
double x_max = offset.x() + static_cast<double>(size.x());
double y_max = offset.y() + static_cast<double>(size.y());
for (double y = offset.y(); y <= y_max; y += h) {
double x_offset = offset.x();
if (isOdd) x_offset += distance_2;
isOdd = !isOdd;
for (double x = x_offset; x <= x_max; x += distance) {
result.emplace_back(x, y);
}
}
assert(result.size() == count_points);
return result;
}
// create uniform triangle net and return points laying inside island
Points rasterize(const ExPolygon &island, double distance) {
BoundingBox bb;
for (const Point &pt : island.contour.points) bb.merge(pt);
Points fullNet = createNet(bb, distance);
Points result;
result.reserve(fullNet.size());
std::copy_if(fullNet.begin(), fullNet.end(), std::back_inserter(result),
[&island](const Point &p) { return island.contains(p); });
return result;
}
SupportIslandPoints test_island_sampling(const ExPolygon & island,
const SampleConfig &config)
{
auto points = uniform_support_island(island, {}, config);
Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer
bool is_island_supported = true; // Check rasterized island points that exist support point in max_distance
double max_distance = config.thick_inner_max_distance;
std::vector<double> point_distances(chck_points.size(), {max_distance + 1});
for (size_t index = 0; index < chck_points.size(); ++index) {
const Point &chck_point = chck_points[index];
double &min_distance = point_distances[index];
bool exist_close_support_point = false;
for (const auto &island_point : points) {
const Point& p = island_point->point;
Point abs_diff(fabs(p.x() - chck_point.x()),
fabs(p.y() - chck_point.y()));
if (abs_diff.x() < min_distance && abs_diff.y() < min_distance) {
double distance = sqrt((double) abs_diff.x() * abs_diff.x() +
(double) abs_diff.y() * abs_diff.y());
if (min_distance > distance) {
min_distance = distance;
exist_close_support_point = true;
}
}
}
if (!exist_close_support_point) is_island_supported = false;
}
bool is_all_points_inside_island = true;
for (const auto &point : points)
if (!island.contains(point->point))
is_all_points_inside_island = false;
#ifdef STORE_ISLAND_ISSUES
if (!is_island_supported || !is_all_points_inside_island) { // visualize
static int counter = 0;
BoundingBox bb;
for (const Point &pt : island.contour.points) bb.merge(pt);
SVG svg(STORE_ISLAND_ISSUES + std::string("Error") + std::to_string(++counter) + ".svg", bb);
svg.draw(island, "blue", 0.5f);
for (auto& p : points)
svg.draw(p->point, island.contains(p->point) ? "lightgreen" : "red", config.head_radius);
for (size_t index = 0; index < chck_points.size(); ++index) {
const Point &chck_point = chck_points[index];
double distance = point_distances[index];
bool isOk = distance < max_distance;
std::string color = (isOk) ? "gray" : "red";
svg.draw(chck_point, color, config.head_radius / 4);
}
}
#endif // STORE_ISLAND_ISSUES
CHECK(!points.empty());
CHECK(is_all_points_inside_island);
// CHECK(is_island_supported); // TODO: skip special cases with one point and 2 points
return points;
}
SampleConfig create_sample_config(double size) {
float head_diameter = .4f;
return SampleConfigFactory::create(head_diameter);
//coord_t max_distance = 3 * size + 0.1;
//SampleConfig cfg;
//cfg.head_radius = size / 4;
//cfg.minimal_distance_from_outline = cfg.head_radius;
//cfg.maximal_distance_from_outline = max_distance/4;
//cfg.max_length_for_one_support_point = 2*size;
//cfg.max_length_for_two_support_points = 4*size;
//cfg.thin_max_width = size;
//cfg.thick_min_width = cfg.thin_max_width;
//cfg.thick_outline_max_distance = max_distance;
//cfg.minimal_move = static_cast<coord_t>(size/30);
//cfg.count_iteration = 100;
//cfg.max_align_distance = 0;
//return cfg;
}
#ifdef STORE_SAMPLE_INTO_SVG_FILES
namespace {
void store_sample(const SupportIslandPoints &samples, const ExPolygon &island) {
static int counter = 0;
BoundingBox bb(island.contour.points);
SVG svg((STORE_SAMPLE_INTO_SVG_FILES + std::to_string(counter++) + ".svg").c_str(), bb);
double mm = scale_(1);
svg.draw(island, "lightgray");
for (const auto &s : samples)
svg.draw(s->point, "blue", 0.2*mm);
// draw resolution
Point p(bb.min.x() + 1e6, bb.max.y() - 2e6);
svg.draw_text(p, (std::to_string(samples.size()) + " samples").c_str(), "black");
svg.draw_text(p - Point(0., 1.8e6), "Scale 1 cm ", "black");
Point start = p - Point(0., 2.3e6);
svg.draw(Line(start + Point(0., 5e5), start + Point(10*mm, 5e5)), "black", 2e5);
svg.draw(Line(start + Point(0., -5e5), start + Point(10*mm, -5e5)), "black", 2e5);
svg.draw(Line(start + Point(10*mm, 5e5), start + Point(10*mm, -5e5)), "black", 2e5);
for (int i=0; i<10;i+=2)
svg.draw(Line(start + Point(i*mm, 0.), start + Point((i+1)*mm, 0.)), "black", 1e6);
}
} // namespace
#endif // STORE_SAMPLE_INTO_SVG_FILES
/// <summary>
/// Check for correct sampling of island
/// </summary>
TEST_CASE("Uniform sample test islands", "[SupGen], [VoronoiSkeleton]")
{
//set_logging_level(5);
float head_diameter = .4f;
SampleConfig cfg = SampleConfigFactory::create(head_diameter);
//cfg.path = "C:/data/temp/islands/<<order>>.svg";
ExPolygons islands = createTestIslands(7 * scale_(head_diameter));
for (ExPolygon &island : islands) {
// information for debug which island cause problem
[[maybe_unused]] size_t debug_index = &island - &islands.front();
SupportIslandPoints points = test_island_sampling(island, cfg);
#ifdef STORE_SAMPLE_INTO_SVG_FILES
store_sample(points, island);
#endif // STORE_SAMPLE_INTO_SVG_FILES
double angle = 3.14 / 3; // cca 60 degree
island.rotate(angle);
SupportIslandPoints pointsR = test_island_sampling(island, cfg);
#ifdef STORE_SAMPLE_INTO_SVG_FILES
store_sample(pointsR, island);
#endif // STORE_SAMPLE_INTO_SVG_FILES
// points count should be the same
//CHECK(points.size() == pointsR.size())
}
}
TEST_CASE("Sample island with config", "[SupportIsland]") {
// set_logging_level(5);
SampleConfig cfg{
/*thin_max_distance*/ 5832568,
/*thick_inner_max_distance*/ 7290710,
/*thick_outline_max_distance*/ 5468032,
/*head_radius*/ 250000,
/*minimal_distance_from_outline*/ 250000,
/*maximal_distance_from_outline*/ 1944189,
/*max_length_for_one_support_point*/ 1869413,
/*max_length_for_two_support_points*/ 7290710,
/*max_length_ratio_for_two_support_points*/ 0.250000000f,
/*thin_max_width*/ 4673532,
/*thick_min_width*/ 4019237,
/*min_part_length*/ 5832568,
/*minimal_move*/ 100000,
/*count_iteration*/ 30,
/*max_align_distance*/ 3645355,
/*simplification_tolerance*/ 50000.000000000007
//*path*/, "C:/data/temp/islands/<<order>>.svg" // need define OPTION_TO_STORE_ISLAND in SampleConfig.hpp
};
std::string dir = std::string(TEST_DATA_DIR PATH_SEPARATOR) + "sla_islands/";
ExPolygon island = load_svg(dir + "SPE-2709.svg"); // Bad field creation
SupportIslandPoints points = test_island_sampling(island, cfg);
// in time of write poins.size() == 39
CHECK(points.size() > 22); // not only thin parts
}
TEST_CASE("Disable visualization", "[hide]")
{
CHECK(true);
#ifdef STORE_SAMPLE_INTO_SVG_FILES
CHECK(false);
#endif // STORE_SAMPLE_INTO_SVG_FILES
#ifdef STORE_ISLAND_ISSUES
CHECK(false);
#endif // STORE_ISLAND_ISSUES
#ifdef USE_ISLAND_GUI_FOR_SETTINGS
CHECK(false);
#endif // USE_ISLAND_GUI_FOR_SETTINGS
CHECK(is_uniform_support_island_visualization_disabled());
}

View File

@@ -1,4 +1,5 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
#include <unordered_set>
@@ -7,6 +8,8 @@
#include "libslic3r/SLA/SupportTreeUtils.hpp"
#include "libslic3r/SLA/SupportTreeUtilsLegacy.hpp"
using Catch::Approx;
// Test pair hash for 'nums' random number pairs.
template <class I, class II> void test_pairhash()
{

View File

@@ -128,29 +128,27 @@ void test_supports(const std::string &obj_filename,
// TODO: do the cgal hole cutting...
// Create the support point generator
sla::SupportPointGenerator::Config autogencfg;
autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm);
sla::SupportPointGenerator point_gen{sm.emesh, autogencfg, [] {}, [](int) {}};
point_gen.seed(0); // Make the test repeatable
point_gen.execute(out.model_slices, out.slicegrid);
sla::SupportPointGeneratorConfig autogencfg;
sla::SupportPointGeneratorData gen_data = sla::prepare_generator_data(std::move(out.model_slices), out.slicegrid);
sla::LayerSupportPoints layer_support_points = sla::generate_support_points(gen_data, autogencfg);
double allowed_move = (out.slicegrid[1] - out.slicegrid[0]) + std::numeric_limits<float>::epsilon();
// Get the calculated support points.
sm.pts = point_gen.output();
sm.pts = sla::move_on_mesh_surface(layer_support_points, sm.emesh, allowed_move);
out.model_slices = std::move(gen_data.slices); // return ownership
int validityflags = ASSUME_NO_REPAIR;
// If there is no elevation, support points shall be removed from the
// bottom of the object.
if (std::abs(supportcfg.object_elevation_mm) < EPSILON) {
sla::remove_bottom_points(sm.pts, zmin + supportcfg.base_height_mm);
} else {
// Should be support points at least on the bottom of the model
REQUIRE_FALSE(sm.pts.empty());
//if (std::abs(supportcfg.object_elevation_mm) < EPSILON) {
// sla::remove_bottom_points(sm.pts, zmin + supportcfg.base_height_mm);
//} else {
// // Should be support points at least on the bottom of the model
// REQUIRE_FALSE(sm.pts.empty());
// Also the support mesh should not be empty.
validityflags |= ASSUME_NO_EMPTY;
}
// // Also the support mesh should not be empty.
// validityflags |= ASSUME_NO_EMPTY;
//}
// Generate the actual support tree
sla::SupportTreeBuilder treebuilder;
@@ -465,7 +463,7 @@ double predict_error(const ExPolygon &p, const sla::PixelDim &pd)
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg)
const sla::SupportPointGeneratorConfig &cfg)
{
// Prepare the slice grid and the slices
auto bb = cast<float>(mesh.bounding_box());
@@ -473,12 +471,10 @@ sla::SupportPoints calc_support_pts(
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, heights, CLOSING_RADIUS);
// Prepare the support point calculator
sla::SupportPointGeneratorData gen_data = sla::prepare_generator_data(std::move(slices), heights);
sla::LayerSupportPoints layer_support_points = sla::generate_support_points(gen_data, cfg);
AABBMesh emesh{mesh};
sla::SupportPointGenerator spgen{emesh, cfg, []{}, [](int){}};
// Calculate the support points
spgen.seed(0);
spgen.execute(slices, heights);
return spgen.output();
double allowed_move = (heights[1] - heights[0]) + std::numeric_limits<float>::epsilon();
// Get the calculated support points.
return sla::move_on_mesh_surface(layer_support_points, emesh, allowed_move);
}

View File

@@ -1,7 +1,8 @@
#ifndef SLA_TEST_UTILS_HPP
#define SLA_TEST_UTILS_HPP
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <test_utils.hpp>
// Debug
@@ -22,6 +23,7 @@
#include "libslic3r/SVG.hpp"
using namespace Slic3r;
using Catch::Approx;
enum e_validity {
ASSUME_NO_EMPTY = 1,
@@ -134,6 +136,6 @@ double predict_error(const ExPolygon &p, const sla::PixelDim &pd);
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg = {});
const sla::SupportPointGeneratorConfig &cfg = {});
#endif // SLA_TEST_UTILS_HPP

Some files were not shown because too many files have changed in this diff Show More