<commit_before>#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;

/// 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;

    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;
                }
        });

    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", false);
        config->set("first_layer_speed", "100%");
        // remove noise from top/solid layers
        config->set("top_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") {
                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);
            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);

            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") {
                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") {
                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);
            }
        }

        WHEN("Object is plated with overhang support and a brim") {
            config->set("layer_height", 0.4);
            config->set("first_layer_height", 0.4);
            config->set("skirts", 1);
            config->set("skirt_distance", 0);
            config->set("support_material_speed", 99);
            config->set("perimeter_extruder", 1);
            config->set("support_material_extruder", 2);
            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

            THEN("skirt length is large enough to contain object with support") {
                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;<commit_msg>add some little tests to brim ears<commit_after>#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;

/// 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;

    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;
                }
        });

    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", false);
        config->set("first_layer_speed", "100%");
        // remove noise from top/solid layers
        config->set("top_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") {
                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);
            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);

            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") {
                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") {
                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);
            }
        }
        WHEN("brim width to 1 with layer_width of 0.5") {
            config->set("skirts", 0);
            config->set("first_layer_extrusion_width", 0.5);
            config->set("brim_width", 1);
            config->set("brim_ears", false);
			
            THEN("2 brim lines") {
                Slic3r::Model model;
                auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
                print->process();
                REQUIRE(print->brim.size() == 2);
            }
        }

        WHEN("brim ears on a square") {
            config->set("skirts", 0);
            config->set("first_layer_extrusion_width", 0.5);
            config->set("brim_width", 1);
            config->set("brim_ears", true);
            config->set("brim_ears_max_angle", 91);
			
            Slic3r::Model model;
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            print->process();

            THEN("Four brim ears") {
                REQUIRE(print->brim.size() == 4);
            }
        }


        WHEN("brim ears on a square but with a too small max angle") {
            config->set("skirts", 0);
            config->set("first_layer_extrusion_width", 0.5);
            config->set("brim_width", 1);
            config->set("brim_ears", true);
            config->set("brim_ears_max_angle", 89);
			
            THEN("no brim") {
                Slic3r::Model model;
                auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
                print->process();
                REQUIRE(print->brim.size() == 0);
            }
        }

        WHEN("Object is plated with overhang support and a brim") {
            config->set("layer_height", 0.4);
            config->set("first_layer_height", 0.4);
            config->set("skirts", 1);
            config->set("skirt_distance", 0);
            config->set("support_material_speed", 99);
            config->set("perimeter_extruder", 1);
            config->set("support_material_extruder", 2);
            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

            THEN("skirt length is large enough to contain object with support") {
                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;
{'fraction_non_alphanumeric': 0.1438211477526112, 'fraction_numerical': 0.02047881704025349, 'mean_word_length': 2.229064039408867, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 135, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4027096', 'n_tokens_mistral': 5546, 'n_tokens_neox': 5163, 'n_words': 1078}