123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705 |
- #include <catch2/catch.hpp>
- #include "test_data.hpp"
- #include <libslic3r/Fill/Fill.hpp>
- #include <libslic3r/Print.hpp>
- #include <libslic3r/Geometry.hpp>
- #include <libslic3r/Flow.hpp>
- #include <libslic3r/ClipperUtils.hpp>
- #include <libslic3r/SVG.hpp>
- using namespace Slic3r;
- using namespace Slic3r::Geometry;
- using namespace Slic3r::Test;
- bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle = 0, double density = 1.0);
- Polylines test(const ExPolygon& poly, Fill &filler, const FillParams ¶ms){
- Surface surface{ Slic3r::Surface((stPosTop | stDensSolid), poly) };
- return filler.fill_surface(&surface, params);
- }
- TEST_CASE("Fill: Pattern Path Length") {
- Fill* filler {Slic3r::Fill::new_from_type("rectilinear")};
- filler->angle = -(PI) / 2.0;
- FillParams params{};
- params.dont_adjust = true;
- params.density = 0.1;
- filler->set_bounding_box(BoundingBox(Point(0, 0), Point::new_scale(Point(100, 100))));
- filler->init_spacing(5, params);
-
- SECTION("Square") {
- Points test_set{};
- test_set.reserve(4);
- Points points {Point{0,0}, Point{100,0}, Point{100,100}, Point{0,100}};
- for (size_t i = 0; i < 4; ++i) {
- std::transform(points.cbegin()+i, points.cend(), std::back_inserter(test_set), [] (const Point& a) -> Point { return Point::new_scale(a); } );
- std::transform(points.cbegin(), points.cbegin()+i, std::back_inserter(test_set), [] (const Point& a) -> Point { return Point::new_scale(a); } );
- Slic3r::ExPolygon expoly{};
- expoly.contour = Slic3r::Polygon{ test_set };
- Polylines paths {test(expoly, *filler, params)};
- REQUIRE(paths.size() == 1);
-
-
-
- REQUIRE(std::abs(paths[0].length() - static_cast<double>(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0);
- test_set.clear();
- }
- }
- SECTION("Diamond with endpoints on grid") {
- Points points {Point{0,0}, Point{100,0}, Point{150,50}, Point{100,100}, Point{0,100}, Point{-50,50}};
- Points test_set{};
- test_set.reserve(6);
- std::transform(points.cbegin(), points.cend(), std::back_inserter(test_set), [] (const Point& a) -> Point { return Point::new_scale(a); } );
- Slic3r::ExPolygon expoly;
- expoly.contour = Slic3r::Polygon(test_set);
- Polylines paths {test(expoly, *filler, params)};
- REQUIRE(paths.size() == 1);
- }
- SECTION("Square with hole") {
- Points square { Point{0,0}, Point{100,0}, Point{100,100}, Point{0,100}};
- Points hole {Point{25,25}, Point{75,25}, Point{75,75}, Point{25,75} };
- std::reverse(hole.begin(), hole.end());
- Points test_hole{};
- Points test_square{};
- std::transform(square.cbegin(), square.cend(), std::back_inserter(test_square), [] (const Point& a) -> Point { return Point::new_scale(a); } );
- std::transform(hole.cbegin(), hole.cend(), std::back_inserter(test_hole), [] (const Point& a) -> Point { return Point::new_scale(a); } );
- for (double angle : {-(PI/2.0), -(PI/4.0), -(PI), PI/2.0, PI}) {
- for (double spacing : {25.0, 5.0, 7.5, 8.5}) {
- FillParams params_local = params;
- params_local.density = filler->get_spacing() / spacing;
- filler->angle = angle;
- Slic3r::ExPolygon e{};
- e.contour = Slic3r::Polygon(test_square);
- e.holes = Slic3r::Polygons(Slic3r::Polygon(test_hole));
- Polylines paths {test(e, *filler, params_local)};
-
-
-
-
-
-
-
-
-
-
- REQUIRE(paths.size() >= 1);
- REQUIRE(paths.size() <= 3);
-
- REQUIRE(diff_pl(paths, offset(e, (float)(+SCALED_EPSILON * 10))).size() == 0);
- }
- }
- }
- SECTION("Regression: Missing infill segments in some rare circumstances") {
- FillParams params_local = params;
- params_local.density = 1;
- params_local.dont_adjust = false;
- Fill* filler_local = { Slic3r::Fill::new_from_type("rectilinear") };
- filler_local->angle = (PI/4.0);
- filler_local->set_bounding_box(BoundingBox(Point(0, 0), Point(2512749, 2512749)));
- filler_local->init_spacing(0.654498, params_local);
-
- filler_local->layer_id = 66;
- filler_local->z = 20.15;
- Points points {Point{25771516,14142125},Point{14142138,25771515},Point{2512749,14142131},Point{14142125,2512749}};
- Slic3r::ExPolygon expoly{};
- expoly.contour = Slic3r::Polygon(points);
- Polylines paths {test(expoly, *filler_local, params_local)};
- REQUIRE(paths.size() == 1);
-
-
-
- REQUIRE(std::abs(paths[0].length() - static_cast<double>(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0);
- }
- SECTION("Rotated Square") {
- Points square { Point::new_scale(0,0), Point::new_scale(50,0), Point::new_scale(50,50), Point::new_scale(0,50)};
- ExPolygon expolygon{};
- expolygon.contour = Slic3r::Polygon(square);
- auto filler {Slic3r::Fill::new_from_type("rectilinear")};
- filler->bounding_box = expolygon.contour.bounding_box();
- filler->angle = 0.F;
-
- Surface surface {(stPosTop|stDensSolid), expolygon};
- Flow flow {0.69f, 0.4f, 0.50f};
- params.density = 1.0;
- filler->init_spacing(flow.spacing(), params);
- for (auto angle : { 0.0, 45.0}) {
- surface.expolygon.rotate(angle, Point{0,0});
- Polylines paths = filler->fill_surface(&surface, params);
- REQUIRE(paths.size() == 1);
- }
- }
- SECTION("Solid surface fill") {
- Points points {
- Point::new_scale(6883102, 9598327.01296997),
- Point::new_scale(6883102, 20327272.01297),
- Point::new_scale(3116896, 20327272.01297),
- Point::new_scale(3116896, 9598327.01296997)
- };
- Slic3r::ExPolygon expolygon{};
- expolygon.contour = Slic3r::Polygon{ points };
-
- REQUIRE(test_if_solid_surface_filled(expolygon, 0.55) == true);
- for (size_t i = 0; i <= 20; ++i)
- {
- expolygon.scale(1.05);
-
-
- }
- }
- SECTION("Solid surface fill") {
- Points points {
- Point{59515297,5422499},Point{59531249,5578697},Point{59695801,6123186},
- Point{59965713,6630228},Point{60328214,7070685},Point{60773285,7434379},
- Point{61274561,7702115},Point{61819378,7866770},Point{62390306,7924789},
- Point{62958700,7866744},Point{63503012,7702244},Point{64007365,7434357},
- Point{64449960,7070398},Point{64809327,6634999},Point{65082143,6123325},
- Point{65245005,5584454},Point{65266967,5422499},Point{66267307,5422499},
- Point{66269190,8310081},Point{66275379,17810072},Point{66277259,20697500},
- Point{65267237,20697500},Point{65245004,20533538},Point{65082082,19994444},
- Point{64811462,19488579},Point{64450624,19048208},Point{64012101,18686514},
- Point{63503122,18415781},Point{62959151,18251378},Point{62453416,18198442},
- Point{62390147,18197355},Point{62200087,18200576},Point{61813519,18252990},
- Point{61274433,18415918},Point{60768598,18686517},Point{60327567,19047892},
- Point{59963609,19493297},Point{59695865,19994587},Point{59531222,20539379},
- Point{59515153,20697500},Point{58502480,20697500},Point{58502480,5422499}
- };
- Slic3r::ExPolygon expolygon;
- expolygon.contour = Slic3r::Polygon{ points };
-
- REQUIRE(test_if_solid_surface_filled(expolygon, 0.55) == true);
- REQUIRE(test_if_solid_surface_filled(expolygon, 0.55, PI/2.0) == true);
- }
- SECTION("Solid surface fill") {
- Points points {
- Point::new_scale(0,0),Point::new_scale(98,0),Point::new_scale(98,10), Point::new_scale(0,10)
- };
- Slic3r::ExPolygon expolygon{};
- expolygon.contour = Slic3r::Polygon{ points };
-
- REQUIRE(test_if_solid_surface_filled(expolygon, 0.5, 45.0, 0.99) == true);
- }
- }
- class ExtrusionGetVolume : public ExtrusionVisitor {
- double volume = 0;
- public:
- ExtrusionGetVolume() {}
- void use(ExtrusionPath &path) override {
- volume += unscaled(path.length()) * path.mm3_per_mm; }
- void use(ExtrusionPath3D &path3D) override { volume += unscaled(path3D.length()) * path3D.mm3_per_mm; }
- void use(ExtrusionMultiPath &multipath) override { for (ExtrusionPath path : multipath.paths) path.visit(*this); }
- void use(ExtrusionMultiPath3D &multipath) override { for (ExtrusionPath path : multipath.paths) path.visit(*this); }
- void use(ExtrusionLoop &loop) override { for (ExtrusionPath path : loop.paths) path.visit(*this); }
- void use(ExtrusionEntityCollection &collection) override { for (ExtrusionEntity *entity : collection.entities) entity->visit(*this); }
- double get(ExtrusionEntityCollection &coll) {
- for (ExtrusionEntity *entity : coll.entities) entity->visit(*this);
- return volume;
- }
- };
- #include "libslic3r/GCodeReader.hpp"
- TEST_CASE("Fill: extrude gcode and check it")
- {
- SECTION("simple square") {
- Model model{};
- TriangleMesh sample_mesh = make_cube(5, 5, 0.2);
- double volume = (5 * 5 * 0.2);
- sample_mesh.repair();
- DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
- config.set_key_value("perimeters", new ConfigOptionInt(1));
- config.set_key_value("top_solid_layers", new ConfigOptionInt(1));
- config.set_key_value("bottom_solid_layers", new ConfigOptionInt(1));
- config.set_key_value("enforce_full_fill_volume", new ConfigOptionBool(true));
- config.set_key_value("infill_overlap", new ConfigOptionFloatOrPercent(0.1, true));
- config.set_key_value("skirts", new ConfigOptionInt(0));
- config.set_key_value("layer_height", new ConfigOptionFloat(0.2));
- config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, false));
- config.set_key_value("extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("external_perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("solid_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("top_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- auto event_counter{ 0U };
- std::string stage;
- Print print{};
- Slic3r::Test::init_print(print, { sample_mesh }, model, &config);
- print.process();
- std::string gcode_filepath{ "" };
- Slic3r::Test::gcode(gcode_filepath, print);
-
- std::string gcode_from_file = read_to_string(gcode_filepath);
-
- GCodeReader parser;
- double volume_extruded = 0;
- int idx = 0;
- double volume_perimeter_extruded = 0;
- double volume_infill_extruded = 0;
-
- parser.parse_buffer(gcode_from_file,
- [&](GCodeReader& reader, const GCodeReader::GCodeLine& line)
- {
- if (line.cmd_is("G1"))
- {
- if (line.dist_E(reader) > 0 && line.dist_XY(reader) > 0) {
-
- volume_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
-
- if (idx<4)volume_perimeter_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
- else volume_infill_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
- idx++;
- }
- }
- });
- double perimeterRoundGapRemove = unscaled(print.objects()[0]->layers()[0]->lslices[0].contour.length()) * 0.1*0.1 * (2 - (PI / 2));
- double perimeterRoundGapAdd = unscaled(print.objects()[0]->layers()[0]->lslices[0].contour.length()) * 0.1*0.1 * ((PI / 2));
-
-
-
-
-
-
-
- double volumeExtrPerimeter = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->perimeters);
- double volumeExtrInfill = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->fills);
- double volumeInfill = 0;
- for (const ExPolygon & p : print.objects()[0]->layers()[0]->regions()[0]->fill_no_overlap_expolygons) {
- volumeInfill += unscaled(unscaled(p.area()));
- }
- volumeInfill *= 0.2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- REQUIRE(abs(volumeInfill - volumeExtrInfill) < EPSILON);
- REQUIRE(abs(volumeInfill - volume_infill_extruded) < 0.01);
- REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volumeExtrPerimeter) < 0.01);
- REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volume_perimeter_extruded) < 0.1);
- clean_file(gcode_filepath, "gcode");
- }
- SECTION("simple disk") {
- Model model{};
- TriangleMesh sample_mesh = make_cylinder(5, 0.2);
- double volume = (PI * 25 * 0.2);
- sample_mesh.repair();
- DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
- config.set_key_value("perimeters", new ConfigOptionInt(1));
- config.set_key_value("top_solid_layers", new ConfigOptionInt(1));
- config.set_key_value("bottom_solid_layers", new ConfigOptionInt(1));
- config.set_key_value("enforce_full_fill_volume", new ConfigOptionBool(true));
- config.set_key_value("infill_overlap", new ConfigOptionFloatOrPercent(0.1, true));
- config.set_key_value("skirts", new ConfigOptionInt(0));
- config.set_key_value("layer_height", new ConfigOptionFloat(0.2));
- config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, false));
- config.set_key_value("extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("external_perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("solid_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- config.set_key_value("top_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
- auto event_counter{ 0U };
- std::string stage;
- Print print{};
- Slic3r::Test::init_print(print, { sample_mesh }, model, &config);
- print.process();
- std::string gcode_filepath{ "" };
- Slic3r::Test::gcode(gcode_filepath, print);
-
- std::string gcode_from_file = read_to_string(gcode_filepath);
-
- GCodeReader parser;
- double volume_extruded = 0;
- int idx = 0;
- double volume_perimeter_extruded = 0;
- double volume_infill_extruded = 0;
-
- parser.parse_buffer(gcode_from_file,
- [&](GCodeReader& reader, const GCodeReader::GCodeLine& line)
- {
- if (line.cmd_is("G1"))
- {
- if (line.dist_E(reader) > 0 && line.dist_XY(reader) > 0) {
-
- volume_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
-
- if (idx<36)volume_perimeter_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
- else volume_infill_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
- idx++;
- }
- }
- });
- double perimeterRoundGapRemove = unscaled(print.objects()[0]->layers()[0]->lslices[0].contour.length()) * 0.1*0.1 * (2 - (PI / 2));
- double perimeterRoundGapAdd = unscaled(print.objects()[0]->layers()[0]->lslices[0].contour.length()) * 0.1*0.1 * ((PI / 2));
- double volumeExtrPerimeter = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->perimeters);
- double volumeExtrInfill = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->fills);
- double volumeInfill = 0;
- for (const ExPolygon & p : print.objects()[0]->layers()[0]->regions()[0]->fill_no_overlap_expolygons) {
- volumeInfill += unscaled(unscaled(p.area()));
- }
- volumeInfill *= 0.2;
- std::cout << "volumeRealr=" << (volume_perimeter_extruded + volume_infill_extruded) << " volumeRealPerimeter= " << volume_perimeter_extruded << " and volumeRealInfill=" << volume_infill_extruded << " mm3." << "\n";
- std::cout << "volumeExtr=" << (volumeExtrPerimeter + volumeExtrInfill) << " volumeExtrPerimeter= " << volumeExtrPerimeter << " and volumeExtrInfill=" << volumeExtrInfill << " mm3." << "\n";
- std::cout << "volumePerimeter= " << (volume - volumeInfill) << " volumePerimeter(wo/bits)= " << (volume - volumeInfill - perimeterRoundGapRemove) << " and volumeInfill=" << volumeInfill << " mm3." << "\n";
- REQUIRE(abs(volumeInfill - volumeExtrInfill) < EPSILON);
- REQUIRE(abs(volumeInfill - volume_infill_extruded) < 0.01);
- REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volumeExtrPerimeter) < EPSILON);
- REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volume_perimeter_extruded) < 0.1);
- clean_file(gcode_filepath, "gcode");
- }
- }
- bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_width, double angle, double density) {
- auto* filler {Slic3r::Fill::new_from_type("concentricgapfill")};
- filler->bounding_box = expolygon.contour.bounding_box();
- filler->angle = angle;
- FillParams params;
- params.dont_adjust = false;
- Surface surface((stPosBottom | stDensSolid), expolygon);
-
- Flow flow(flow_width, 0.4, flow_width);
- params.density = density;
- filler->init_spacing(flow.spacing(), params);
- Polylines paths {filler->fill_surface(&surface, params)};
-
- Polygons grown_paths;
- grown_paths.reserve(paths.size());
- std::for_each(paths.begin(), paths.end(), [filler, &grown_paths] (const Slic3r::Polyline& p) {
- polygons_append(grown_paths, offset(p, scale_(filler->get_spacing() / 2.0)));
- });
-
- ExPolygons uncovered = diff_ex(expolygon, grown_paths, true);
-
- const auto scaled_flow_width { std::pow(scale_(flow_width), 2) };
- auto iter {std::remove_if(uncovered.begin(), uncovered.end(), [scaled_flow_width] (const ExPolygon& poly) {
- return poly.area() > scaled_flow_width;
- }) };
- uncovered.erase(iter, uncovered.end());
- double uncovered_area = 0;
- for (ExPolygon &p : uncovered) uncovered_area += unscaled(unscaled(p.area()));
- std::cout << "uncovered size =" << uncovered_area << " / "<< unscaled(unscaled(expolygon.area()))<<"\n";
- return uncovered.size() == 0;
- }
|