Skip to content

Commit

Permalink
Fix skirt/Brim (slic3r#4669)
Browse files Browse the repository at this point in the history
* Start implementing skirt/brim tests.

* Move skirt_height_z (representing the highest skirt height in actual Z coordinates) to Print and prime it with make_brim so it can be used in PrintGCode.

Updated test to correctly verify skirt is generated for one and multiple objects.

* Fallout from the config refactor; use correct accessors.

* Fix bug where brim was not generated (inverted logic)

* Finish porting tests, left one intentional failure as support material is not generated yet (segfaults).
  • Loading branch information
lordofhyphens authored and alranel committed Jan 6, 2019
1 parent b3cac9d commit dabb5f3
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 50 deletions.
190 changes: 148 additions & 42 deletions src/test/libslic3r/test_skirt_brim.cpp
Original file line number Diff line number Diff line change
@@ -1,88 +1,156 @@
#include <catch.hpp>
#include "test_data.hpp" // get access to init_print, etc
#include "GCodeReader.hpp"
#include "Config.hpp"
#include "Geometry.hpp"
#include <regex>

using namespace Slic3r::Test;
using namespace Slic3r;

SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
GIVEN("Configuration with a skirt height of 2") {
auto config {Config::new_from_defaults()};
config->set("skirts", 1);
config->set("skirt_height", 2);
config->set("perimeters", 0);
config->set("support_material_speed", 99);

// avoid altering speeds unexpectedly
config->set("cooling", 0);
config->set("first_layer_speed", "100%");
/// Helper method to find the tool used for the brim (always the first extrusion)
int get_brim_tool(std::stringstream& gcode, Slic3r::GCodeReader& parser) {
int brim_tool = -1;
std::regex tool_regex("^T(\\d+)");
std::smatch m;
int tool = -1;

WHEN("multiple objects are printed") {
auto gcode {std::stringstream("")};
Slic3r::Model model;
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, model, config)};
std::map<double, bool> layers_with_skirt;
Slic3r::Test::gcode(gcode, print);
auto parser {Slic3r::GCodeReader()};
parser.parse_stream(gcode, [&layers_with_skirt, &config] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
if (self.Z > 0) {
if (line.extruding() && line.new_F() == config->getFloat("support_material_speed") * 60.0) {
layers_with_skirt[self.Z] = 1;
}
parser.parse_stream(gcode, [&tool, &brim_tool, &m, tool_regex] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
// if the command is a T command, set the the current tool
if (std::regex_match(line.cmd, m, tool_regex)) {
tool = std::stoi(m[1].str());
} else if (line.cmd == "G1" && line.extruding() && line.dist_XY() > 0 && brim_tool < 0) {
brim_tool = tool;
}
});
});

THEN("skirt_height is honored") {
REQUIRE(layers_with_skirt.size() == (size_t)config->getInt("skirt_height"));
}
}
return brim_tool;
}


TEST_CASE("Skirt height is honored") {
auto config {Config::new_from_defaults()};
config->set("skirts", 1);
config->set("skirt_height", 5);
config->set("perimeters", 0);
config->set("support_material_speed", 99);

// avoid altering speeds unexpectedly
config->set("cooling", false);
config->set("first_layer_speed", "100%");
auto support_speed = config->get<ConfigOptionFloat>("support_material_speed") * MM_PER_MIN;

std::map<double, bool> layers_with_skirt;
auto gcode {std::stringstream("")};
gcode.clear();
auto parser {Slic3r::GCodeReader()};
Slic3r::Model model;

SECTION("printing a single object") {
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
Slic3r::Test::gcode(gcode, print);
}

SECTION("printing multiple objects") {
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, model, config)};
Slic3r::Test::gcode(gcode, print);
}
parser.parse_stream(gcode, [&layers_with_skirt, &support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
if (line.extruding() && self.F == Approx(support_speed)) {
layers_with_skirt[self.Z] = 1;
}
});

REQUIRE(layers_with_skirt.size() == (size_t)config->getInt("skirt_height"));
}

SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
auto parser {Slic3r::GCodeReader()};
Slic3r::Model model;
auto gcode {std::stringstream("")};
gcode.clear();
GIVEN("A default configuration") {
auto config {Config::new_from_defaults()};
config->set("support_material_speed", 99);
config->set("first_layer_height", 0.3);
config->set("gcode_comments", true);

// avoid altering speeds unexpectedly
config->set("cooling", 0);
config->set("cooling", false);
config->set("first_layer_speed", "100%");
// remove noise from top/solid layers
config->set("top_solid_layers", 0);
config->set("bottom_solid_layers", 0);
config->set("bottom_solid_layers", 1);

WHEN("Brim width is set to 5") {
config->set("perimeters", 0);
config->set("skirts", 0);
config->set("brim_width", 5);
THEN("Brim is generated") {
REQUIRE(false);
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
Slic3r::Test::gcode(gcode, print);
bool brim_generated = false;
auto support_speed = config->get<ConfigOptionFloat>("support_material_speed") * MM_PER_MIN;
parser.parse_stream(gcode, [&brim_generated, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
if (self.Z == Approx(0.3) || line.new_Z() == Approx(0.3)) {
if (line.extruding() && self.F == Approx(support_speed)) {
brim_generated = true;
}
}
});
REQUIRE(brim_generated);
}
}

WHEN("Skirt area is smaller than the brim") {
config->set("skirts", 1);
config->set("brim_width", 10);
THEN("GCode generates successfully.") {
REQUIRE(false);
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
THEN("Gcode generates") {
Slic3r::Test::gcode(gcode, print);
auto exported {gcode.str()};
REQUIRE(exported.size() > 0);
}
}

WHEN("Skirt height is 0 and skirts > 0") {
config->set("skirts", 2);
config->set("skirt_height", 0);

THEN("GCode generates successfully.") {
REQUIRE(false);
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
THEN("Gcode generates") {
Slic3r::Test::gcode(gcode, print);
auto exported {gcode.str()};
REQUIRE(exported.size() > 0);
}
}

WHEN("Perimeter extruder = 2 and support extruders = 3") {
config->set("skirts", 0);
config->set("brim_width", 5);
config->set("perimeter_extruder", 2);
config->set("support_material_extruder", 3);
THEN("Brim is printed with the extruder used for the perimeters of first object") {
REQUIRE(false);
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
Slic3r::Test::gcode(gcode, print);
int tool = get_brim_tool(gcode, parser);
REQUIRE(tool == config->getInt("perimeter_extruder") - 1);
}
}
WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
config->set("skirts", 0);
config->set("brim_width", 5);
config->set("perimeter_extruder", 2);
config->set("support_material_extruder", 3);
config->set("raft_layers", 1);
THEN("brim is printed with same extruder as skirt") {
REQUIRE(false);
auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
Slic3r::Test::gcode(gcode, print);
int tool = get_brim_tool(gcode, parser);
REQUIRE(tool == config->getInt("support_material_extruder") - 1);
}
}

Expand All @@ -94,17 +162,55 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
config->set("support_material_speed", 99);
config->set("perimeter_extruder", 1);
config->set("support_material_extruder", 2);
config->set("cooling", 0); // to prevent speeds to be altered
config->set("infill_extruder", 3); // ensure that a tool command gets emitted.
config->set("cooling", false); // to prevent speeds to be altered
config->set("first_layer_speed", "100%"); // to prevent speeds to be altered

Slic3r::Model model;
auto print {Slic3r::Test::init_print({TestMesh::overhang}, model, config)};
print->process();
config->set("support_material", true); // to prevent speeds to be altered

// config->set("support_material", true); // to prevent speeds to be altered

THEN("skirt length is large enough to contain object with support") {
REQUIRE(false);
CHECK(config->getBool("support_material")); // test is not valid if support material is off
double skirt_length = 0.0;
Points extrusion_points;
int tool = -1;
std::regex tool_regex("^T(\\d+)");
std::smatch m;

auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
Slic3r::Test::gcode(gcode, print);

auto support_speed = config->get<ConfigOptionFloat>("support_material_speed") * MM_PER_MIN;
parser.parse_stream(gcode, [tool_regex, &m, config, &extrusion_points, &tool, &skirt_length, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
std::cerr << line.cmd << "\n";
if (std::regex_match(line.cmd, m, tool_regex)) {
tool = std::stoi(m[1].str());
} else if (self.Z == Approx(config->get<ConfigOptionFloat>("first_layer_height").value)) {
// on first layer
if (line.extruding() && line.dist_XY() > 0) {
auto speed = ( self.F > 0 ? self.F : line.new_F());
std::cerr << "Tool " << tool << "\n";
if (speed == Approx(support_speed) && tool == config->getInt("perimeter_extruder") - 1) {
// Skirt uses first material extruder, support material speed.
skirt_length += line.dist_XY();
} else {
extrusion_points.push_back(Slic3r::Point::new_scale(line.new_X(), line.new_Y()));
}
}
}

if (self.Z == Approx(0.3) || line.new_Z() == Approx(0.3)) {
if (line.extruding() && self.F == Approx(support_speed)) {
}
}
});
auto convex_hull = Slic3r::Geometry::convex_hull(extrusion_points);
auto hull_perimeter = unscale(convex_hull.split_at_first_point().length());
REQUIRE(skirt_length > hull_perimeter);
}
}
WHEN("Large minimum skirt length is used.") {
Expand Down
2 changes: 2 additions & 0 deletions src/test/test_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Slic3r { namespace Test {

constexpr double MM_PER_MIN = 60.0;

/// Enumeration of test meshes
enum class TestMesh {
A,
Expand Down
12 changes: 6 additions & 6 deletions xs/src/libslic3r/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,32 +172,32 @@ Print::make_skirt()
// include the thickest object first. It is just guaranteed that a skirt is
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
double skirt_height_z {-1.0};
this->skirt_height_z = -1.0;
for (const auto* object : this->objects) {
const size_t skirt_height {
this->has_infinite_skirt()
? object->layer_count()
: std::min(size_t(this->config.skirt_height()), object->layer_count())
};
const Layer* highest_layer { object->get_layer(skirt_height - 1) };
skirt_height_z = std::max(skirt_height_z, highest_layer->print_z);
this->skirt_height_z = std::max(skirt_height_z, highest_layer->print_z);
}

// collect points from all layers contained in skirt height
Points points;
for (auto* object : this->objects) {
Points object_points;

// get object layers up to skirt_height_z
// get object layers up to this->skirt_height_z
for (const auto* layer : object->layers) {
if (layer->print_z > skirt_height_z) break;
if (layer->print_z > this->skirt_height_z) break;
for (const ExPolygon ex : layer->slices)
append_to(object_points, static_cast<Points>(ex));
}

// get support layers up to skirt_height_z
// get support layers up to this->skirt_height_z
for (const auto* layer : object->support_layers) {
if (layer->print_z > skirt_height_z) break;
if (layer->print_z > this->skirt_height_z) break;
for (auto* ee : layer->support_fills)
append_to(object_points, ee->as_polyline().points);
for (auto* ee : layer->support_interface_fills)
Expand Down
1 change: 1 addition & 0 deletions xs/src/libslic3r/Print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ class Print

// ordered collections of extrusion paths to build skirt loops and brim
ExtrusionEntityCollection skirt, brim;
double skirt_height_z {-1.0};

Print();
~Print();
Expand Down
7 changes: 5 additions & 2 deletions xs/src/libslic3r/PrintGCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,11 @@ PrintGCode::process_layer(size_t idx, const Layer* layer, const Points& copies)

// extrude skirt along raft layers and normal obj layers
// (not along interlaced support material layers)

if (layer->id() < static_cast<size_t>(obj.config.raft_layers)
|| ((_print.has_infinite_skirt() || _skirt_done.size() == 0 || (_skirt_done.rbegin())->first < _print.config.skirt_height)
|| ((_print.has_infinite_skirt()
|| _skirt_done.size() == 0
|| (_skirt_done.rbegin())->first < scale_(_print.skirt_height_z))
&& _skirt_done.count(scale_(layer->print_z)) == 0
&& typeid(layer) != typeid(SupportLayer*)) ) {

Expand Down Expand Up @@ -547,7 +550,7 @@ PrintGCode::process_layer(size_t idx, const Layer* layer, const Points& copies)
}

// extrude brim
if (this->_brim_done) {
if (!this->_brim_done) {
gcode += _gcodegen.set_extruder(_print.brim_extruder() - 1);
_gcodegen.set_origin(Pointf(0,0));
_gcodegen.avoid_crossing_perimeters.use_external_mp = true;
Expand Down

0 comments on commit dabb5f3

Please sign in to comment.