diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 5db6940ad9..2aa9b4839e 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -194,6 +194,8 @@ set(SLIC3R_SOURCES GCode/AvoidCrossingPerimeters.hpp GCode/Travels.cpp GCode/Travels.hpp + GCode/LayerChanges.cpp + GCode/LayerChanges.hpp GCode.cpp GCode.hpp GCodeReader.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0e18895cdf..35092335f3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -35,6 +35,7 @@ #include "GCode/WipeTower.hpp" #include "GCode/WipeTowerIntegration.hpp" #include "GCode/Travels.hpp" +#include "GCode/LayerChanges.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" @@ -2649,54 +2650,6 @@ std::string GCodeGenerator::preamble() return gcode; } -namespace GCode::Impl { -Polygon generate_regular_polygon( - const Point& centroid, - const Point& start_point, - const unsigned points_count -) { - Points points; - points.reserve(points_count); - const double part_angle{2*M_PI / points_count}; - for (unsigned i = 0; i < points_count; ++i) { - const double current_angle{i * part_angle}; - points.emplace_back(scaled(std::cos(current_angle)), scaled(std::sin(current_angle))); - } - - Polygon regular_polygon{points}; - const Vec2d current_vector{unscaled(regular_polygon.points.front())}; - const Vec2d expected_vector{unscaled(start_point) - unscaled(centroid)}; - - const double current_scale = current_vector.norm(); - const double expected_scale = expected_vector.norm(); - regular_polygon.scale(expected_scale / current_scale); - - regular_polygon.rotate(angle(current_vector, expected_vector)); - - regular_polygon.translate(centroid); - - return regular_polygon; -} - -Bed::Bed(const std::vector& shape, const double padding): - inner_offset(get_inner_offset(shape, padding)), - centroid(unscaled(inner_offset.centroid())) -{} - -bool Bed::contains_within_padding(const Vec2d& point) const { - return inner_offset.contains(scaled(point)); -} - -Polygon Bed::get_inner_offset(const std::vector& shape, const double padding) { - Points shape_scaled; - shape_scaled.reserve(shape.size()); - using std::begin, std::end, std::back_inserter, std::transform; - transform(begin(shape), end(shape), back_inserter(shape_scaled), [](const Vec2d& point){ - return scaled(point); - }); - return shrink({Polygon{shape_scaled}}, scaled(padding)).front(); -} -} std::optional GCodeGenerator::get_helical_layer_change_gcode( const coordf_t previous_layer_z, @@ -2713,7 +2666,7 @@ std::optional GCodeGenerator::get_helical_layer_change_gcode( const Point n_gon_start_point{this->last_pos()}; - static GCode::Impl::Bed bed{ + static GCode::Impl::LayerChanges::Bed bed{ this->m_config.bed_shape.values, circle_radius }; @@ -2729,7 +2682,7 @@ std::optional GCodeGenerator::get_helical_layer_change_gcode( }) }; - const Polygon n_gon{GCode::Impl::generate_regular_polygon( + const Polygon n_gon{GCode::Impl::LayerChanges::generate_regular_polygon( n_gon_centeroid, n_gon_start_point, n_gon_points_count @@ -3413,8 +3366,6 @@ std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, st const Point start_point = this->last_pos(); - using namespace GCode::Impl; - // check whether a straight travel move would need retraction bool could_be_wipe_disabled {false}; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d75a86d443..b266b8a156 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -89,43 +89,6 @@ struct LayerResult { static LayerResult make_nop_layer_result() { return {"", std::numeric_limits::max(), false, false, true}; } }; -namespace GCode::Impl { - -/** - * Generates a regular polygon - all angles are the same (e.g. typical hexagon). - * - * @param centroid Central point. - * @param start_point The polygon point are ordered. This is the first point. - * @param points_count Amount of nodes of the polygon (e.g. 6 for haxagon). - * - * Distance between centroid and start point sets the scale of the polygon. - */ -Polygon generate_regular_polygon( - const Point& centroid, - const Point& start_point, - const unsigned points_count -); - -class Bed { - private: - Polygon inner_offset; - static Polygon get_inner_offset(const std::vector& shape, const double padding); - - public: - /** - * Bed shape with inner padding. - */ - Bed(const std::vector& shape, const double padding); - - Vec2d centroid; - - /** - * Returns true if the point is within the bed shape including inner padding. - */ - bool contains_within_padding(const Vec2d& point) const; -}; -} - class GCodeGenerator { public: diff --git a/src/libslic3r/GCode/LayerChanges.cpp b/src/libslic3r/GCode/LayerChanges.cpp new file mode 100644 index 0000000000..826c01347a --- /dev/null +++ b/src/libslic3r/GCode/LayerChanges.cpp @@ -0,0 +1,48 @@ +#include "LayerChanges.hpp" +#include "libslic3r/ClipperUtils.hpp" + +namespace Slic3r::GCode::Impl::LayerChanges { + +Polygon generate_regular_polygon( + const Point ¢roid, const Point &start_point, const unsigned points_count +) { + Points points; + points.reserve(points_count); + const double part_angle{2 * M_PI / points_count}; + for (unsigned i = 0; i < points_count; ++i) { + const double current_angle{i * part_angle}; + points.emplace_back(scaled(std::cos(current_angle)), scaled(std::sin(current_angle))); + } + + Polygon regular_polygon{points}; + const Vec2d current_vector{unscaled(regular_polygon.points.front())}; + const Vec2d expected_vector{unscaled(start_point) - unscaled(centroid)}; + + const double current_scale = current_vector.norm(); + const double expected_scale = expected_vector.norm(); + regular_polygon.scale(expected_scale / current_scale); + + regular_polygon.rotate(angle(current_vector, expected_vector)); + + regular_polygon.translate(centroid); + + return regular_polygon; +} + +Bed::Bed(const std::vector &shape, const double padding) + : inner_offset(get_inner_offset(shape, padding)), centroid(unscaled(inner_offset.centroid())) {} + +bool Bed::contains_within_padding(const Vec2d &point) const { + return inner_offset.contains(scaled(point)); +} + +Polygon Bed::get_inner_offset(const std::vector &shape, const double padding) { + Points shape_scaled; + shape_scaled.reserve(shape.size()); + using std::begin, std::end, std::back_inserter, std::transform; + transform(begin(shape), end(shape), back_inserter(shape_scaled), [](const Vec2d &point) { + return scaled(point); + }); + return shrink({Polygon{shape_scaled}}, scaled(padding)).front(); +} +} // namespace Slic3r::GCode::Impl::LayerChanges diff --git a/src/libslic3r/GCode/LayerChanges.hpp b/src/libslic3r/GCode/LayerChanges.hpp new file mode 100644 index 0000000000..5eb178e3f1 --- /dev/null +++ b/src/libslic3r/GCode/LayerChanges.hpp @@ -0,0 +1,52 @@ +/** + * @file + * @brief Utility functions for layer change gcode generation. + */ + +#ifndef slic3r_GCode_LayerChanges_hpp_ +#define slic3r_GCode_LayerChanges_hpp_ + +#include "libslic3r/Point.hpp" +#include "libslic3r/Polygon.hpp" + +namespace Slic3r::GCode::Impl::LayerChanges { +/** + * Generates a regular polygon - all angles are the same (e.g. typical hexagon). + * + * @param centroid Central point. + * @param start_point The polygon point are ordered. This is the first point. + * @param points_count Amount of nodes of the polygon (e.g. 6 for haxagon). + * + * Distance between centroid and start point sets the scale of the polygon. + */ +Polygon generate_regular_polygon( + const Point ¢roid, const Point &start_point, const unsigned points_count +); + +/** + * @brief A representation of the bed shape with inner padding. + * + * Its purpose is to facilitate the bed boundary checking. + */ +class Bed +{ +private: + Polygon inner_offset; + static Polygon get_inner_offset(const std::vector &shape, const double padding); + +public: + /** + * Bed shape with inner padding. + */ + Bed(const std::vector &shape, const double padding); + + Vec2d centroid; + + /** + * Returns true if the point is within the bed shape including inner padding. + */ + bool contains_within_padding(const Vec2d &point) const; +}; +} // namespace Slic3r::GCode::Impl::LayerChanges + +#endif // slic3r_GCode_LayerChanges_hpp_ diff --git a/tests/fff_print/CMakeLists.txt b/tests/fff_print/CMakeLists.txt index 45efe83341..ed128bf2e0 100644 --- a/tests/fff_print/CMakeLists.txt +++ b/tests/fff_print/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${_TEST_NAME}_tests test_gaps.cpp test_gcode.cpp test_gcode_travels.cpp + test_gcode_layer_changes.cpp test_gcodefindreplace.cpp test_gcodewriter.cpp test_model.cpp diff --git a/tests/fff_print/test_gcode.cpp b/tests/fff_print/test_gcode.cpp index 11dce2e90f..3ec1758b4e 100644 --- a/tests/fff_print/test_gcode.cpp +++ b/tests/fff_print/test_gcode.cpp @@ -5,7 +5,6 @@ #include "libslic3r/GCode.hpp" using namespace Slic3r; -using namespace Slic3r::GCode::Impl; SCENARIO("Origin manipulation", "[GCode]") { Slic3r::GCodeGenerator gcodegen; @@ -21,53 +20,3 @@ SCENARIO("Origin manipulation", "[GCode]") { } } } - -TEST_CASE("Generate regular polygon", "[GCode]") { - const unsigned points_count{32}; - const Point centroid{scaled(Vec2d{5, -2})}; - const Polygon result{generate_regular_polygon(centroid, scaled(Vec2d{0, 0}), points_count)}; - const Point oposite_point{centroid * 2}; - - REQUIRE(result.size() == 32); - CHECK(result[16].x() == Approx(oposite_point.x())); - CHECK(result[16].y() == Approx(oposite_point.y())); - - std::vector angles; - angles.reserve(points_count); - for (unsigned index = 0; index < points_count; index++) { - const unsigned previous_index{index == 0 ? points_count - 1 : index - 1}; - const unsigned next_index{index == points_count - 1 ? 0 : index + 1}; - - const Point previous_point = result.points[previous_index]; - const Point current_point = result.points[index]; - const Point next_point = result.points[next_index]; - - angles.emplace_back(angle(Vec2crd{previous_point - current_point}, Vec2crd{next_point - current_point})); - } - - std::vector expected; - angles.reserve(points_count); - std::generate_n(std::back_inserter(expected), points_count, [&](){ - return angles.front(); - }); - - CHECK_THAT(angles, Catch::Matchers::Approx(expected)); -} - -TEST_CASE("Square bed with padding", "[GCode]") { - const Bed bed{ - { - Vec2d{0, 0}, - Vec2d{100, 0}, - Vec2d{100, 100}, - Vec2d{0, 100} - }, - 10.0 - }; - - CHECK(bed.centroid.x() == 50); - CHECK(bed.centroid.y() == 50); - CHECK(bed.contains_within_padding(Vec2d{10, 10})); - CHECK_FALSE(bed.contains_within_padding(Vec2d{9, 10})); - -} diff --git a/tests/fff_print/test_gcode_layer_changes.cpp b/tests/fff_print/test_gcode_layer_changes.cpp new file mode 100644 index 0000000000..6621d38cbe --- /dev/null +++ b/tests/fff_print/test_gcode_layer_changes.cpp @@ -0,0 +1,55 @@ +#include +#include + +using namespace Slic3r; +using namespace Slic3r::GCode::Impl::LayerChanges; + +TEST_CASE("Generate regular polygon", "[GCode]") { + const unsigned points_count{32}; + const Point centroid{scaled(Vec2d{5, -2})}; + const Polygon result{generate_regular_polygon(centroid, scaled(Vec2d{0, 0}), points_count)}; + const Point oposite_point{centroid * 2}; + + REQUIRE(result.size() == 32); + CHECK(result[16].x() == Approx(oposite_point.x())); + CHECK(result[16].y() == Approx(oposite_point.y())); + + std::vector angles; + angles.reserve(points_count); + for (unsigned index = 0; index < points_count; index++) { + const unsigned previous_index{index == 0 ? points_count - 1 : index - 1}; + const unsigned next_index{index == points_count - 1 ? 0 : index + 1}; + + const Point previous_point = result.points[previous_index]; + const Point current_point = result.points[index]; + const Point next_point = result.points[next_index]; + + angles.emplace_back(angle(Vec2crd{previous_point - current_point}, Vec2crd{next_point - current_point})); + } + + std::vector expected; + angles.reserve(points_count); + std::generate_n(std::back_inserter(expected), points_count, [&](){ + return angles.front(); + }); + + CHECK_THAT(angles, Catch::Matchers::Approx(expected)); +} + +TEST_CASE("Square bed with padding", "[GCode]") { + const Bed bed{ + { + Vec2d{0, 0}, + Vec2d{100, 0}, + Vec2d{100, 100}, + Vec2d{0, 100} + }, + 10.0 + }; + + CHECK(bed.centroid.x() == 50); + CHECK(bed.centroid.y() == 50); + CHECK(bed.contains_within_padding(Vec2d{10, 10})); + CHECK_FALSE(bed.contains_within_padding(Vec2d{9, 10})); + +}