PRUSA 2.7.0

This commit is contained in:
sunsets
2023-12-27 18:02:35 +08:00
parent b33112327f
commit 0a3c63dcb1
488 changed files with 92371 additions and 29443 deletions

View File

@@ -14,7 +14,7 @@
using namespace Slic3r;
std::unique_ptr<CoolingBuffer> make_cooling_buffer(
GCode &gcode,
GCodeGenerator &gcode,
const DynamicPrintConfig &config = DynamicPrintConfig{},
const std::vector<unsigned int> &extruder_ids = { 0 })
{
@@ -65,7 +65,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
const double print_time = 100. / (3000. / 60.);
//FIXME slowdown_below_layer_time is rounded down significantly from 1.8s to 1s.
config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 0.999) } } });
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config);
std::string gcode = buffer->process_layer("G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1", 0, true);
bool speed_not_altered = gcode.find("F3000") != gcode.npos;
@@ -83,7 +83,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
// Print time of gcode.
const double print_time = 50. / (2500. / 60.) + 100. / (3000. / 60.) + 4. / (400. / 60.);
config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 1.001) } } });
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config);
std::string gcode = buffer->process_layer(gcode_src, 0, true);
THEN("speed is altered when elapsed time is lower than slowdown threshold") {
@@ -106,7 +106,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
{ "fan_below_layer_time" , int(print_time1 * 0.88) },
{ "slowdown_below_layer_time" , int(print_time1 * 0.99) }
});
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config);
std::string gcode = buffer->process_layer(gcode1, 0, true);
bool fan_not_activated = gcode.find("M106") == gcode.npos;
@@ -119,7 +119,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
{ "fan_below_layer_time", { int(print_time2 + 1.), int(print_time2 + 1.) } },
{ "slowdown_below_layer_time", { int(print_time2 + 2.), int(print_time2 + 2.) } }
});
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config, { 0, 1 });
std::string gcode = buffer->process_layer(gcode1 + "T1\nG1 X0 E1 F3000\n", 0, true);
THEN("fan is activated for the 1st tool") {
@@ -134,7 +134,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
WHEN("G-code block 2") {
THEN("slowdown is computed on all objects printing at the same Z") {
config.set_deserialize_strict({ { "slowdown_below_layer_time", int(print_time2 * 0.99) } });
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config);
std::string gcode = buffer->process_layer(gcode2, 0, true);
bool ok = gcode.find("F3000") != gcode.npos;
@@ -145,7 +145,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
{ "fan_below_layer_time", int(print_time2 * 0.65) },
{ "slowdown_below_layer_time", int(print_time2 * 0.7) }
});
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config);
// use an elapsed time which is < the threshold but greater than it when summed twice
std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true);
@@ -158,7 +158,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
{ "fan_below_layer_time", int(print_time2 + 1) },
{ "slowdown_below_layer_time", int(print_time2 + 1) }
});
GCode gcodegen;
GCodeGenerator gcodegen;
auto buffer = make_cooling_buffer(gcodegen, config);
// use an elapsed time which is < the threshold but greater than it when summed twice
std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true);

File diff suppressed because one or more lines are too long

View File

@@ -21,7 +21,7 @@ static inline Slic3r::Point random_point(float LO=-50, float HI=50)
// build a sample extrusion entity collection with random start and end points.
static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50)
{
ExtrusionPath t { ExtrusionRole::Perimeter, 1.0, 1.0, 1.0 };
ExtrusionPath t{ ExtrusionAttributes{ ExtrusionRole::Perimeter, ExtrusionFlow{ 1.0, 1.0, 1.0 } } };
for (size_t j = 0; j < length; ++ j)
t.polyline.append(random_point(LO, HI));
return t;
@@ -37,9 +37,8 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20
SCENARIO("ExtrusionPath", "[ExtrusionEntity]") {
GIVEN("Simple path") {
Slic3r::ExtrusionPath path{ ExtrusionRole::ExternalPerimeter };
path.polyline = { { 100, 100 }, { 200, 100 }, { 200, 200 } };
path.mm3_per_mm = 1.;
Slic3r::ExtrusionPath path{ { { 100, 100 }, { 200, 100 }, { 200, 200 } },
ExtrusionAttributes{ ExtrusionRole::ExternalPerimeter, ExtrusionFlow{ 1., -1.f, -1.f } } };
THEN("first point") {
REQUIRE(path.first_point() == path.polyline.front());
}
@@ -52,10 +51,7 @@ SCENARIO("ExtrusionPath", "[ExtrusionEntity]") {
static ExtrusionPath new_extrusion_path(const Polyline &polyline, ExtrusionRole role, double mm3_per_mm)
{
ExtrusionPath path(role);
path.polyline = polyline;
path.mm3_per_mm = 1.;
return path;
return { polyline, ExtrusionAttributes{ role, ExtrusionFlow{ mm3_per_mm, -1.f, -1.f } } };
}
SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
@@ -67,6 +63,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), ExtrusionRole::ExternalPerimeter, 1.));
THEN("polygon area") {
REQUIRE(loop.polygon().area() == Approx(square.area()));
REQUIRE(loop.area() == Approx(square.area()));
}
THEN("loop length") {
REQUIRE(loop.length() == Approx(square.length()));
@@ -110,6 +107,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
loop.paths.emplace_back(new_extrusion_path(polyline1, ExtrusionRole::ExternalPerimeter, 1.));
loop.paths.emplace_back(new_extrusion_path(polyline2, ExtrusionRole::OverhangPerimeter, 1.));
THEN("area") {
REQUIRE(loop.area() == Approx(loop.polygon().area()));
}
double tot_len = polyline1.length() + polyline2.length();
THEN("length") {
REQUIRE(loop.length() == Approx(tot_len));
@@ -212,6 +212,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
loop.paths.emplace_back(new_extrusion_path(polyline3, ExtrusionRole::ExternalPerimeter, 1.));
loop.paths.emplace_back(new_extrusion_path(polyline4, ExtrusionRole::OverhangPerimeter, 1.));
double len = loop.length();
THEN("area") {
REQUIRE(loop.area() == Approx(loop.polygon().area()));
}
WHEN("splitting at vertex") {
Point point(4821067, 9321068);
if (! loop.split_at_vertex(point))
@@ -234,6 +237,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
Polyline { { 15896783, 15868739 }, { 24842049, 12117558 }, { 33853238, 15801279 }, { 37591780, 24780128 }, { 37591780, 24844970 },
{ 33853231, 33825297 }, { 24842049, 37509013 }, { 15896798, 33757841 }, { 12211841, 24812544 }, { 15896783, 15868739 } },
ExtrusionRole::ExternalPerimeter, 1.));
THEN("area") {
REQUIRE(loop.area() == Approx(loop.polygon().area()));
}
double len = loop.length();
THEN("split_at() preserves total length") {
loop.split_at({ 15896783, 15868739 }, false, 0);
@@ -378,23 +384,27 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
REQUIRE(chained == test.chained);
ExtrusionEntityCollection unchained_extrusions;
extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained,
ExtrusionRole::InternalInfill, 0., 0.4f, 0.3f);
ExtrusionAttributes{ ExtrusionRole::InternalInfill, ExtrusionFlow{ 0., 0.4f, 0.3f } });
THEN("Chaining works") {
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
REQUIRE(chained_extrusions.entities.size() == test.chained.size());
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point);
REQUIRE(chained_extrusions.size() == test.chained.size());
for (size_t i = 0; i < chained_extrusions.size(); ++ i) {
const Points &p1 = test.chained[i].points;
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
Points p2 = chained_extrusions[i].cast<ExtrusionPath>()->polyline.points;
if (chained_extrusions[i].flipped())
std::reverse(p2.begin(), p2.end());
REQUIRE(p1 == p2);
}
}
THEN("Chaining produces no change with no_sort") {
unchained_extrusions.no_sort = true;
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
REQUIRE(chained_extrusions.entities.size() == test.unchained.size());
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point);
REQUIRE(chained_extrusions.size() == test.unchained.size());
for (size_t i = 0; i < chained_extrusions.size(); ++ i) {
const Points &p1 = test.unchained[i].points;
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
Points p2 = chained_extrusions[i].cast<ExtrusionPath>()->polyline.points;
if (chained_extrusions[i].flipped())
std::reverse(p2.begin(), p2.end());
REQUIRE(p1 == p2);
}
}

View File

@@ -5,9 +5,10 @@
#include "libslic3r/GCode.hpp"
using namespace Slic3r;
using namespace Slic3r::GCode::Impl;
SCENARIO("Origin manipulation", "[GCode]") {
Slic3r::GCode gcodegen;
Slic3r::GCodeGenerator gcodegen;
WHEN("set_origin to (10,0)") {
gcodegen.set_origin(Vec2d(10,0));
REQUIRE(gcodegen.origin() == Vec2d(10, 0));
@@ -20,3 +21,220 @@ SCENARIO("Origin manipulation", "[GCode]") {
}
}
}
struct ApproxEqualsPoints : public Catch::MatcherBase<Points> {
ApproxEqualsPoints(const Points& expected, unsigned tolerance): expected(expected), tolerance(tolerance) {}
bool match(const Points& points) const override {
if (points.size() != expected.size()) {
return false;
}
for (auto i = 0u; i < points.size(); ++i) {
const Point& point = points[i];
const Point& expected_point = this->expected[i];
if (
std::abs(point.x() - expected_point.x()) > this->tolerance
|| std::abs(point.y() - expected_point.y()) > this->tolerance
) {
return false;
}
}
return true;
}
std::string describe() const override {
std::stringstream ss;
ss << std::endl;
for (const Point& point : expected) {
ss << "(" << point.x() << ", " << point.y() << ")" << std::endl;
}
ss << "With tolerance: " << this->tolerance;
return "Equals " + ss.str();
}
private:
Points expected;
unsigned tolerance;
};
Points get_points(const std::vector<DistancedPoint>& result) {
Points result_points;
std::transform(
result.begin(),
result.end(),
std::back_inserter(result_points),
[](const DistancedPoint& point){
return point.point;
}
);
return result_points;
}
std::vector<double> get_distances(const std::vector<DistancedPoint>& result) {
std::vector<double> result_distances;
std::transform(
result.begin(),
result.end(),
std::back_inserter(result_distances),
[](const DistancedPoint& point){
return point.distance_from_start;
}
);
return result_distances;
}
TEST_CASE("Place points at distances - expected use", "[GCode]") {
std::vector<Point> line{
scaled(Vec2f{0, 0}),
scaled(Vec2f{1, 0}),
scaled(Vec2f{2, 1}),
scaled(Vec2f{2, 2})
};
std::vector<double> distances{0, 0.2, 0.5, 1 + std::sqrt(2)/2, 1 + std::sqrt(2) + 0.5, 100.0};
std::vector<DistancedPoint> result = slice_xy_path(line, distances);
REQUIRE_THAT(get_points(result), ApproxEqualsPoints(Points{
scaled(Vec2f{0, 0}),
scaled(Vec2f{0.2, 0}),
scaled(Vec2f{0.5, 0}),
scaled(Vec2f{1, 0}),
scaled(Vec2f{1.5, 0.5}),
scaled(Vec2f{2, 1}),
scaled(Vec2f{2, 1.5}),
scaled(Vec2f{2, 2})
}, 5));
REQUIRE_THAT(get_distances(result), Catch::Matchers::Approx(std::vector<double>{
distances[0], distances[1], distances[2], 1, distances[3], 1 + std::sqrt(2), distances[4], 2 + std::sqrt(2)
}));
}
TEST_CASE("Place points at distances - edge case", "[GCode]") {
std::vector<Point> line{
scaled(Vec2f{0, 0}),
scaled(Vec2f{1, 0}),
scaled(Vec2f{2, 0})
};
std::vector<double> distances{0, 1, 1.5, 2};
Points result{get_points(slice_xy_path(line, distances))};
CHECK(result == Points{
scaled(Vec2f{0, 0}),
scaled(Vec2f{1, 0}),
scaled(Vec2f{1.5, 0}),
scaled(Vec2f{2, 0})
});
}
TEST_CASE("Generate elevated travel", "[GCode]") {
std::vector<Point> xy_path{
scaled(Vec2f{0, 0}),
scaled(Vec2f{1, 0}),
};
std::vector<double> ensure_points_at_distances{0.2, 0.5};
Points3 result{generate_elevated_travel(xy_path, ensure_points_at_distances, 2.0, [](double x){return 1 + x;})};
CHECK(result == Points3{
scaled(Vec3f{0, 0, 3.0}),
scaled(Vec3f{0.2, 0, 3.2}),
scaled(Vec3f{0.5, 0, 3.5}),
scaled(Vec3f{1, 0, 4.0})
});
}
TEST_CASE("Get first crossed line distance", "[GCode]") {
// A 2x2 square at 0, 0, with 1x1 square hole in its center.
ExPolygon square_with_hole{
{
scaled(Vec2f{-1, -1}),
scaled(Vec2f{1, -1}),
scaled(Vec2f{1, 1}),
scaled(Vec2f{-1, 1})
},
{
scaled(Vec2f{-0.5, -0.5}),
scaled(Vec2f{0.5, -0.5}),
scaled(Vec2f{0.5, 0.5}),
scaled(Vec2f{-0.5, 0.5})
}
};
// A 2x2 square above the previous square at (0, 3).
ExPolygon square_above{
{
scaled(Vec2f{-1, 2}),
scaled(Vec2f{1, 2}),
scaled(Vec2f{1, 4}),
scaled(Vec2f{-1, 4})
}
};
// Bottom-up travel intersecting the squares.
Lines travel{Polyline{
scaled(Vec2f{0, -2}),
scaled(Vec2f{0, -0.7}),
scaled(Vec2f{0, 0}),
scaled(Vec2f{0, 1}),
scaled(Vec2f{0, 1.3}),
scaled(Vec2f{0, 2.4}),
scaled(Vec2f{0, 4.5}),
scaled(Vec2f{0, 5}),
}.lines()};
// Try different cases by skipping lines in the travel.
AABBTreeLines::LinesDistancer<Linef> distancer = get_expolygons_distancer({square_with_hole, square_above});
CHECK(*get_first_crossed_line_distance(travel, distancer) == Approx(1));
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(1), distancer) == Approx(0.2));
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(2), distancer) == Approx(0.5));
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(3), distancer) == Approx(1.0)); //Edge case
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(4), distancer) == Approx(0.7));
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(5), distancer) == Approx(1.6));
CHECK_FALSE(get_first_crossed_line_distance(tcb::span{travel}.subspan(6), distancer));
}
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<double> 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<double> 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}));
}

View File

@@ -2,71 +2,10 @@
#include <memory>
#include "libslic3r/GCodeWriter.hpp"
#include "libslic3r/GCode/GCodeWriter.hpp"
using namespace Slic3r;
SCENARIO("lift() is not ignored after unlift() at normal values of Z", "[GCodeWriter]") {
GIVEN("A config from a file and a single extruder.") {
GCodeWriter writer;
GCodeConfig &config = writer.config;
config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini", ForwardCompatibilitySubstitutionRule::Disable);
std::vector<unsigned int> extruder_ids {0};
writer.set_extruders(extruder_ids);
writer.set_extruder(0);
WHEN("Z is set to 203") {
double trouble_Z = 203;
writer.travel_to_z(trouble_Z);
AND_WHEN("GcodeWriter::Lift() is called") {
REQUIRE(writer.lift().size() > 0);
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
AND_WHEN("GCodeWriter::Unlift() is called") {
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
THEN("GCodeWriter::Lift() emits gcode.") {
REQUIRE(writer.lift().size() > 0);
}
}
}
}
}
WHEN("Z is set to 500003") {
double trouble_Z = 500003;
writer.travel_to_z(trouble_Z);
AND_WHEN("GcodeWriter::Lift() is called") {
REQUIRE(writer.lift().size() > 0);
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
AND_WHEN("GCodeWriter::Unlift() is called") {
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
THEN("GCodeWriter::Lift() emits gcode.") {
REQUIRE(writer.lift().size() > 0);
}
}
}
}
}
WHEN("Z is set to 10.3") {
double trouble_Z = 10.3;
writer.travel_to_z(trouble_Z);
AND_WHEN("GcodeWriter::Lift() is called") {
REQUIRE(writer.lift().size() > 0);
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
AND_WHEN("GCodeWriter::Unlift() is called") {
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
THEN("GCodeWriter::Lift() emits gcode.") {
REQUIRE(writer.lift().size() > 0);
}
}
}
}
}
// The test above will fail for trouble_Z == 9007199254740992, where trouble_Z + 1.5 will be rounded to trouble_Z + 2.0 due to double mantisa overflow.
}
}
SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {