From 1984df6d498ce6a5224f3f6b9d33780610a6da56 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sun, 22 Mar 2020 15:25:21 +0100 Subject: [PATCH] Many fixes to "no seam" option for external perimeter first. Also some option to only apply to hole (or others) Also some fixes to some loops options in the codepath. also a test to thin walls, suspected a bug but can't find it. --- resources/ui_layout/print.ui | 2 + src/libslic3r/ExtrusionEntity.hpp | 12 ++-- src/libslic3r/GCode.cpp | 97 +++++++++++++++++++++------ src/libslic3r/Layer.cpp | 3 + src/libslic3r/PerimeterGenerator.cpp | 57 +++++++++++++--- src/libslic3r/PrintConfig.cpp | 21 +++++- src/libslic3r/PrintConfig.hpp | 8 ++- src/libslic3r/PrintObject.cpp | 3 + src/slic3r/GUI/ConfigManipulation.cpp | 3 +- src/slic3r/GUI/Preset.cpp | 6 +- src/slic3r/GUI/Tab.cpp | 2 + src/test/libslic3r/test_thin.cpp | 58 ++++++++++++++-- 12 files changed, 229 insertions(+), 43 deletions(-) diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index 445e921b4..820d20527 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -41,6 +41,8 @@ group:Advanced line:External Perimeter setting:external_perimeters_first setting:external_perimeters_vase + setting:external_perimeters_nothole + setting:external_perimeters_hole end_line line:Looping perimeter setting:perimeter_loop diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index ca1ccdf50..02371a549 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -39,12 +39,14 @@ enum ExtrusionRole : uint8_t { // Special flags describing loop enum ExtrusionLoopRole : uint16_t { - elrDefault=0x1, + elrDefault = 0, // doesn't contains more contour: it's the most internal one - elrInternal=0x10, - elrSkirt = 0x100, + elrInternal = 1 << 1, //2 + elrSkirt = 1 << 2, //4 //it's a modifier that indicate that the loop is around a hole, not around the infill - elrHole = 0x1000, + elrHole = 1 << 3, // 16 + //it's a modifier that indicate that the loop should be printed as vase + elrVase = 1 << 4, //32 }; @@ -448,7 +450,7 @@ public: #endif /* NDEBUG */ private: - ExtrusionLoopRole m_loop_role; + ExtrusionLoopRole m_loop_role{ elrDefault }; }; inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5db755afa..575cb42d8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2533,6 +2533,8 @@ std::vector polygon_angles_at_vertices(const Polygon &polygon, const std: //like extrude_loop but with varying z and two full round std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr *lower_layer_edge_grid) { + //don't keep the speed + speed = -1; // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation ExtrusionLoop loop = original_loop; @@ -2561,6 +2563,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s // extrude all loops ccw //no! this was decided in perimeter_generator bool is_hole_loop = loop.loop_role() & ExtrusionLoopRole::elrHole != 0;// loop.make_counter_clockwise(); + bool reverse_turn = loop.polygon().is_clockwise() ^ is_hole_loop; split_at_seam_pos(loop, lower_layer_edge_grid); @@ -2576,9 +2579,9 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s loop.clip_end(clip_length, &paths); if (paths.empty()) return ""; - // apply the small perimeter speed + // apply the small/external? perimeter speed if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1) - speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); + speed = m_config.external_perimeter_speed.get_abs_value(m_config.perimeter_speed); //get extrusion length coordf_t length = 0; @@ -2586,7 +2589,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s //path->simplify(SCALED_RESOLUTION); //not useful, this should have been done before. length += path->length() * SCALING_FACTOR; } - + //all in unscaled coordinates (hence why it's coordf_t and not coord_t) const coordf_t min_height = EXTRUDER_CONFIG(min_layer_height); const coordf_t bot_init_z = - this->m_layer->height; @@ -2594,25 +2597,77 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s const coordf_t init_z = bot_init_z + min_height; //const coordf_t last_z = bot_init_z + this->m_layer->height; + Point inward_point; + //move the seam point inward a little bit + if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { + // detect angle between last and first segment + // the side depends on the original winding order of the polygon (left for contours, right for holes) + //FIXME improve the algorithm in case the loop is tiny. + //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). + Point a = paths.front().polyline.points[1]; // second point + Point b = *(paths.back().polyline.points.end() - 3); // second to last point + if (reverse_turn) { + // swap points + Point c = a; a = b; b = c; + } + + double angle = paths.front().first_point().ccw_angle(a, b)*2 / 3; + + // turn left if contour, turn right if hole + if (reverse_turn) angle *= -1; + + // create the destination point along the first segment and rotate it + // we make sure we don't exceed the segment length because we don't know + // the rotation of the second segment so we might cross the object boundary + Vec2d p1 = paths.front().polyline.points.front().cast(); + Vec2d p2 = paths.front().polyline.points[1].cast(); + Vec2d v = p2 - p1; + double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); + double l2 = v.squaredNorm(); + // Shift by no more than a nozzle diameter. + //FIXME Hiding the seams will not work nicely for very densely discretized contours! + inward_point = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); + inward_point.rotate(angle, paths.front().polyline.points.front()); + } + coordf_t current_pos_in_length = 0; - coordf_t current_z = init_z; + coordf_t current_z = 0; // over init_z coordf_t current_height = min_height; + coordf_t starting_height = min_height; enum Step { INCR = 0, FLAT = 1 }; std::string gcode; for (int step = 0; step < 2; step++) { + current_pos_in_length = 0; + current_z = 0; const coordf_t z_per_length = (step == Step::INCR) ? ((this->m_layer->height - (min_height + min_height)) / length) : 0; const coordf_t height_per_length = (step == Step::INCR) ? ((this->m_layer->height- (min_height + min_height)) / length) : ((-this->m_layer->height + (min_height + min_height)) / length); if (step == Step::FLAT) { - current_z = 0; current_height = this->m_layer->height - min_height; + starting_height = this->m_layer->height - min_height; } Vec3d previous; for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { - + if (path == paths.begin() ){ + if (step == Step::INCR) { + if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { + paths[0].polyline.points.insert(paths[0].polyline.points.begin(), inward_point); + } + this->m_writer.travel_to_z(this->m_layer->print_z + init_z); + } else { + //ensure we're at the right height + this->m_writer.travel_to_z(this->m_layer->print_z); + } + } gcode += this->_before_extrude(*path, description, speed); + if (path == paths.begin() && step == Step::INCR){ + if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { + paths[0].polyline.points.erase(paths[0].polyline.points.begin()); + gcode += m_writer.extrude_to_xy(this->point_to_gcode(paths[0].polyline.points.front()), 0); + } + } // calculate extrusion length per distance unit double e_per_mm_per_height = m_writer.extruder()->e_per_mm3() * path->mm3_per_mm / this->m_layer->height; @@ -2636,9 +2691,10 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s for (int i = 0; i < nb_sections - 1; i++) { Vec3d new_point = last_point + pos_increment; gcode += m_writer.extrude_to_xyz(new_point, - e_per_mm_per_height * line_length * current_height_internal, + e_per_mm_per_height * (line_length / nb_sections) * current_height_internal, description); current_height_internal += height_increment; + last_point = new_point; } //last bit will go to the exact last pos last_point.x() = this->point_to_gcode(line.b).x(); @@ -2646,14 +2702,14 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s last_point.z() = current_z + z_per_length * line_length; gcode += m_writer.extrude_to_xyz( last_point, - e_per_mm_per_height * line_length * current_height_internal, + e_per_mm_per_height * (line_length / nb_sections) * current_height_internal, comment); previous = last_point; //update vars for next line current_pos_in_length += line_length; - current_z = last_point.z(); - current_height += height_per_length * line_length; + current_z = current_pos_in_length * z_per_length;//last_point.z(); + current_height = starting_height + current_pos_in_length * height_per_length; } } gcode += this->_after_extrude(*path); @@ -2696,7 +2752,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). Point a = paths.front().polyline.points[1]; // second point Point b = *(paths.back().polyline.points.end() - 3); // second to last point - if (is_hole_loop) { + if (reverse_turn) { // swap points Point c = a; a = b; b = c; } @@ -2704,7 +2760,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s double angle = paths.front().first_point().ccw_angle(a, b) / 3; // turn left if contour, turn right if hole - if (is_hole_loop) angle *= -1; + if (reverse_turn) angle *= -1; // create the destination point along the first segment and rotate it // we make sure we don't exceed the segment length because we don't know @@ -2716,10 +2772,11 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! - Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); - pt.rotate(angle, paths.front().polyline.points.front()); + inward_point = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); + inward_point.rotate(angle, paths.front().polyline.points.front()); + // generate the travel move - gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel"); + gcode += m_writer.travel_to_xy(this->point_to_gcode(inward_point), "move inwards before travel"); } return gcode; @@ -2895,7 +2952,7 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptrm_config.external_perimeters_vase && !this->m_config.spiral_vase + if (original_loop.role() == ExtrusionRole::erExternalPerimeter && (original_loop.loop_role() & elrVase) != 0 && !this->m_config.spiral_vase //but not for the first layer && this->m_layer->id() > 0 //exclude if min_layer_height * 2 > layer_height (increase from 2 to 3 because it's working but uses in-between) - && this->m_layer->height >= EXTRUDER_CONFIG(min_layer_height) * 2 + && this->m_layer->height >= EXTRUDER_CONFIG(min_layer_height) * 2 - EPSILON ) { - + std::cout << " ok, loop vase @"<< this->m_layer->id()<<", "<< this->m_layer->print_z<<"\n"; return extrude_loop_vase(original_loop, description, speed, lower_layer_edge_grid); } @@ -3040,7 +3097,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // generate the travel move gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel"); } - + return gcode; } diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index b88f47880..970c8f862 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -131,6 +131,9 @@ void Layer::make_perimeters() && config.perimeter_speed == other_config.perimeter_speed // it os mandatory? can't this be set at gcode.cpp? && config.external_perimeter_extrusion_width == other_config.external_perimeter_extrusion_width && config.external_perimeters_first == other_config.external_perimeters_first + && config.external_perimeters_vase == other_config.external_perimeters_vase + && config.external_perimeters_hole == other_config.external_perimeters_hole + && config.external_perimeters_nothole == other_config.external_perimeters_nothole && config.external_perimeter_speed == other_config.external_perimeter_speed && config.extra_perimeters_odd_layers == other_config.extra_perimeters_odd_layers && config.gap_fill == other_config.gap_fill diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index e39c4a720..e33993936 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -588,6 +588,7 @@ void PerimeterGenerator::process() } } // at this point, all loops should be in contours[0] (= contours.front() ) + // collection of loops to add into loops ExtrusionEntityCollection entities; if (config->perimeter_loop.value) { //onlyone_perimter = >fusion all perimeterLoops @@ -614,9 +615,46 @@ void PerimeterGenerator::process() // if brim will be printed, reverse the order of perimeters so that // we continue inwards after having finished the brim // TODO: add test for perimeter order - if (this->config->external_perimeters_first || - (this->layer_id == 0 && this->print_config->brim_width.value > 0)) - entities.reverse(); + if (this->config->external_perimeters_first || + (this->layer_id == 0 && this->print_config->brim_width.value > 0)) { + if (this->config->external_perimeters_nothole.value) { + if (this->config->external_perimeters_hole.value) { + entities.reverse(); + } else { + //reverse only not-hole perimeters + ExtrusionEntityCollection coll2; + for (const auto loop : entities.entities) { + std::cout << loop->is_loop() <<" test " << (((ExtrusionLoop*)loop)->loop_role()) <<" & " << ExtrusionLoopRole::elrHole <<"\n"; + if (loop->is_loop() && !(((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) { + coll2.entities.push_back(loop); + } + } + coll2.reverse(); + for (const auto loop : entities.entities) { + if (!loop->is_loop() || (((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) { + coll2.entities.push_back(loop); + } + } + entities = coll2; + } + } else if (this->config->external_perimeters_hole.value) { + //reverse the hole, and put them in first place. + ExtrusionEntityCollection coll2; + for (const auto loop : entities.entities) { + if (loop->is_loop() && (((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) { + coll2.entities.push_back(loop); + } + } + coll2.reverse(); + for (const auto loop : entities.entities) { + if (!loop->is_loop() || !(((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) { + coll2.entities.push_back(loop); + } + } + entities = coll2; + } + + } // append perimeters for this slice as a collection if (!entities.empty()) this->loops->append(entities); @@ -708,18 +746,21 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( bool is_external = loop.is_external(); ExtrusionRole role; - ExtrusionLoopRole loop_role; + ExtrusionLoopRole loop_role = ExtrusionLoopRole::elrDefault; role = is_external ? erExternalPerimeter : erPerimeter; if (loop.is_internal_contour()) { // Note that we set loop role to ContourInternalPerimeter // also when loop is both internal and external (i.e. // there's only one contour loop). - loop_role = elrInternal; - } else { - loop_role = elrDefault; + loop_role = ExtrusionLoopRole::elrInternal; } if (!loop.is_contour) { - loop_role = (ExtrusionLoopRole)(loop_role | elrHole); + loop_role = (ExtrusionLoopRole)(loop_role | ExtrusionLoopRole::elrHole); + } + if (this->config->external_perimeters_vase.value && this->config->external_perimeters_first.value && is_external) { + if ((loop.is_contour && this->config->external_perimeters_nothole.value) || (!loop.is_contour && this->config->external_perimeters_hole.value)) { + loop_role = (ExtrusionLoopRole)(loop_role | ExtrusionLoopRole::elrVase); + } } // detect overhanging/bridging perimeters diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a07106fba..93994bf6a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -668,6 +668,25 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("external_perimeters_nothole", coBool); + def->label = L("only for outter side"); + def->full_label = L("ext peri first for outter side"); + def->category = OptionCategory::perimeter; + def->tooltip = L("Only do the vase trick on the external side. Useful when the thikness is too low."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("external_perimeters_hole", coBool); + def->label = L("only for inner side"); + def->full_label = L("ext peri first for inner side"); + def->category = OptionCategory::perimeter; + def->tooltip = L("Only do the vase trick on the external side. Useful when you only want to remode seam from screw hole."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + + ConfigOptionBool external_perimeters_nothole; + ConfigOptionBool external_perimeters_hole; + def = this->add("perimeter_loop", coBool); def->label = L(""); def->full_label = L("Perimeters loop"); @@ -679,7 +698,7 @@ void PrintConfigDef::init_fff_params() def = this->add("perimeter_loop_seam", coEnum); def->label = L("Seam position"); - def->full_label = L("Perimeter loop"); + def->full_label = L("Perimeter loop seam"); def->category = OptionCategory::perimeter; def->tooltip = L("Position of perimeters starting points."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8134928c7..d7fd91f98 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -492,7 +492,6 @@ public: ConfigOptionFloat elefant_foot_compensation; ConfigOptionBool exact_last_layer_height; ConfigOptionFloatOrPercent extrusion_width; - ConfigOptionBool external_perimeters_vase; ConfigOptionFloatOrPercent first_layer_height; ConfigOptionBool infill_only_where_needed; // Force the generation of solid shells between adjacent materials/volumes. @@ -546,7 +545,6 @@ protected: OPT_PTR(elefant_foot_compensation); OPT_PTR(exact_last_layer_height); OPT_PTR(extrusion_width); - OPT_PTR(external_perimeters_vase); OPT_PTR(first_layer_height); OPT_PTR(infill_only_where_needed); OPT_PTR(interface_shells); @@ -611,6 +609,9 @@ public: ConfigOptionFloatOrPercent external_perimeter_extrusion_width; ConfigOptionFloatOrPercent external_perimeter_speed; ConfigOptionBool external_perimeters_first; + ConfigOptionBool external_perimeters_vase; + ConfigOptionBool external_perimeters_nothole; + ConfigOptionBool external_perimeters_hole; ConfigOptionBool extra_perimeters; ConfigOptionBool extra_perimeters_odd_layers; ConfigOptionBool only_one_perimeter_top; @@ -683,6 +684,9 @@ protected: OPT_PTR(external_perimeter_extrusion_width); OPT_PTR(external_perimeter_speed); OPT_PTR(external_perimeters_first); + OPT_PTR(external_perimeters_vase); + OPT_PTR(external_perimeters_nothole); + OPT_PTR(external_perimeters_hole); OPT_PTR(extra_perimeters); OPT_PTR(extra_perimeters_odd_layers); OPT_PTR(only_one_perimeter_top); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d29983175..252a9178b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -611,6 +611,9 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectoropt_bool("external_perimeters_first")); for (auto el : { "thin_walls_min_width", "thin_walls_overlap" }) @@ -254,6 +254,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("perimeter_loop_seam", config->opt_bool("perimeter_loop")); + toggle_field("gap_fill_min_area", config->opt_bool("gap_fill")); bool have_infill = config->option("fill_density")->value > 0; // infill_extruder uses the same logic as in Print::extruders() diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 2526d84d8..68533b774 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -403,8 +403,10 @@ const std::vector& Preset::print_options() "thin_walls", "overhangs", "overhangs_width", "seam_position", - "external_perimeters_first", - "external_perimeters_vase", + "external_perimeters_first", + "external_perimeters_vase", + "external_perimeters_nothole", + "external_perimeters_hole", "fill_density" , "fill_pattern" , "fill_top_flow_ratio" diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index e37076667..57e8c3051 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1557,6 +1557,8 @@ void TabPrint::build() line = { _(L("External Perimeter")), "" }; line.append_option(optgroup->get_option("external_perimeters_first")); line.append_option(optgroup->get_option("external_perimeters_vase")); + line.append_option(optgroup->get_option("external_perimeters_nothole")); + line.append_option(optgroup->get_option("external_perimeters_hole")); optgroup->append_line(line); line = { _(L("Looping perimeter")), "" }; line.append_option(optgroup->get_option("perimeter_loop")); diff --git a/src/test/libslic3r/test_thin.cpp b/src/test/libslic3r/test_thin.cpp index ab32db5ff..859ca1092 100644 --- a/src/test/libslic3r/test_thin.cpp +++ b/src/test/libslic3r/test_thin.cpp @@ -6,10 +6,60 @@ #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/MedialAxis.hpp" #include "../../libslic3r/SVG.hpp" +#include "../../libslic3r/GCode.hpp" using namespace Slic3r; using namespace Slic3r::Geometry; +using namespace Slic3r::Test; +class ExtrusionVolumeVisitor : public ExtrusionVisitorConst { + double volume = 0; +public: + virtual void use(const ExtrusionPath &path) override { + for (int i = 0; i < path.polyline.size() - 1; i++) volume += path.polyline.points[i].distance_to(path.polyline.points[i + 1]) * path.mm3_per_mm; + }; + virtual void use(const ExtrusionPath3D &path3D) override { std::cout << "error, not supported"; }; + virtual void use(const ExtrusionMultiPath &multipath) override { + for (const ExtrusionPath &path : multipath.paths) use(path); + } + virtual void use(const ExtrusionMultiPath3D &multipath) override { std::cout << "error, not supported"; }; + virtual void use(const ExtrusionLoop &loop) override { + for (const ExtrusionEntity &path : loop.paths) path.visit(*this); + } + virtual void use(const ExtrusionEntityCollection &collection) override { + for (const ExtrusionEntity *path : collection.entities) path->visit(*this); + } + double compute(const ExtrusionEntity &entity) && { + entity.visit(*this); + return volume; + } +}; + + +SCENARIO("extrude_thinwalls") { + GIVEN("ThickLine") { + ExPolygon expolygon; + expolygon.contour = Slic3r::Polygon{ Points{ + Point::new_scale(-0.5, 0), + Point::new_scale(0.5, 0), + Point::new_scale(0.3, 10), + Point::new_scale(-0.3, 10) } }; + ThickPolylines res; + + MedialAxis{ expolygon, scale_(1.1), scale_(0.5), scale_(0.2) }.build(res); + Flow periflow{ 1.1, 0.2, 0.4 }; + ExtrusionEntityCollection gap_fill = thin_variable_width(res, erGapFill, periflow); + + + //std::string gcode = gcodegen.get_visitor_gcode(); + THEN("analyse extrusion.") { + ExtrusionVolumeVisitor vis; + std::cout << " volume is " << ExtrusionVolumeVisitor{}.compute(gap_fill) << "\n"; + std::cout << " wanted volume is " << ((0.6*0.2 * 10) + (0.2*0.2 * 10)) << "\n"; + REQUIRE(std::abs(ExtrusionVolumeVisitor{}.compute(gap_fill) - ((0.6*0.2 * 10) + (0.2*0.2 * 10)))<0.01); + } + } +} SCENARIO("thin walls: ") { @@ -48,7 +98,7 @@ SCENARIO("thin walls: ") } } - GIVEN("narrow rectangle"){ + GIVEN("narrow rectangle") { ExPolygon expolygon; expolygon.contour = Slic3r::Polygon{ Points{ Point::new_scale(100, 100), @@ -67,15 +117,15 @@ SCENARIO("thin walls: ") Point::new_scale(100, 200) } }; Polylines res2; expolygon.medial_axis(scale_(20), scale_(0.5), &res2); - WHEN("creating the medial axis"){ + WHEN("creating the medial axis") { - THEN("medial axis of a narrow rectangle is a single line"){ + THEN("medial axis of a narrow rectangle is a single line") { REQUIRE(res.size() == 1); THEN("medial axis has reasonable length") { REQUIRE(res[0].length() >= scale_(200 - 100 - (120 - 100)) - SCALED_EPSILON); } } - THEN("medial axis of a narrow rectangle with an extra vertex is still a single line"){ + THEN("medial axis of a narrow rectangle with an extra vertex is still a single line") { REQUIRE(res2.size() == 1); THEN("medial axis of a narrow rectangle with an extra vertex has reasonable length") { REQUIRE(res2[0].length() >= scale_(200 - 100 - (120 - 100)) - SCALED_EPSILON);