mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-01 00:18:44 +03:00
update src and test
This commit is contained in:
@@ -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++)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
5
tests/data/sla_islands/SPE-2674.svg
Normal file
5
tests/data/sla_islands/SPE-2674.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 22 KiB |
11
tests/data/sla_islands/SPE-2674_2.svg
Normal file
11
tests/data/sla_islands/SPE-2674_2.svg
Normal 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 |
1176
tests/data/sla_islands/SPE-2709.svg
Normal file
1176
tests/data/sla_islands/SPE-2709.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 189 KiB |
11
tests/data/sla_islands/lm_issue.svg
Normal file
11
tests/data/sla_islands/lm_issue.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 50 KiB |
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "test_data.hpp"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <libslic3r/BridgeDetector.hpp>
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "test_data.hpp"
|
||||
#include "libslic3r/ClipperZUtils.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <numeric>
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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]};
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -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]") {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -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 == "");
|
||||
}
|
||||
}
|
||||
83
tests/fff_print/test_infill_above_bridges.cpp
Normal file
83
tests/fff_print/test_infill_above_bridges.cpp
Normal 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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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{}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/TriangleMeshSlicer.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
|
||||
@@ -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]")
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <libslic3r/libslic3r.h>
|
||||
#include <libslic3r/AnyPtr.hpp>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Arachne/WallToolPaths.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "libslic3r/Color.hpp"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/SLA/Hollowing.hpp"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <random>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/JumpPointSearch.hpp"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@@ -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.);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <libslic3r/MultipleBeds.hpp>
|
||||
#include <numeric>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/MutablePolygon.hpp"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
#include "libslic3r/MutablePriorityQueue.hpp"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <libslic3r/Optimize/BruteforceOptimizer.hpp>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Polyline.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <igl/qslim.h>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
|
||||
@@ -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\\"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <string_view>
|
||||
|
||||
#include "libslic3r/StaticMap.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Time.hpp"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
|
||||
59
tests/sla_print/sla_lineUtils_tests.cpp
Normal file
59
tests/sla_print/sla_lineUtils_tests.cpp
Normal 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)));
|
||||
}
|
||||
50
tests/sla_print/sla_parabola_tests.cpp
Normal file
50
tests/sla_print/sla_parabola_tests.cpp
Normal 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 ¶bola)
|
||||
{
|
||||
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 ¶bola, 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user