Smooth infill (ironing)

- new fill option : smooth (aka ironing)
 - needed a more powerful function from fill.cpp (implemented in FillBase)
 - ensure the no_sort mean really no sort (changes in gcode.cpp and ExtrusionEntityCollection)
This commit is contained in:
Merill 2018-04-02 18:09:24 +02:00
parent 2749c63b10
commit 0544793128
12 changed files with 336 additions and 33 deletions

View File

@ -74,6 +74,8 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/Fill/FillRectilinear2.hpp ${LIBDIR}/libslic3r/Fill/FillRectilinear2.hpp
${LIBDIR}/libslic3r/Fill/FillRectilinear3.cpp ${LIBDIR}/libslic3r/Fill/FillRectilinear3.cpp
${LIBDIR}/libslic3r/Fill/FillRectilinear3.hpp ${LIBDIR}/libslic3r/Fill/FillRectilinear3.hpp
${LIBDIR}/libslic3r/Fill/FillSmooth.cpp
${LIBDIR}/libslic3r/Fill/FillSmooth.hpp
${LIBDIR}/libslic3r/Flow.cpp ${LIBDIR}/libslic3r/Flow.cpp
${LIBDIR}/libslic3r/Flow.hpp ${LIBDIR}/libslic3r/Flow.hpp
${LIBDIR}/libslic3r/Format/3mf.cpp ${LIBDIR}/libslic3r/Format/3mf.cpp

View File

@ -207,6 +207,45 @@ ExtrusionEntityCollection::flatten() const
return coll; return coll;
} }
/* Returns a vector of chained (new) pointers to all non-collection items contained in this one */
void
ExtrusionEntityCollection::flattenIfSortable(ExtrusionEntityCollection* retval) const
{
if (no_sort){
ExtrusionEntityCollection *unsortable = new ExtrusionEntityCollection(*this);
retval->append(*unsortable);
unsortable->entities.clear();
for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
if ((*it)->is_collection()) {
ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it);
collection->flattenIfSortable(unsortable);
}
else {
unsortable->append(**it);
}
}
}
else{
for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
if ((*it)->is_collection()) {
ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it);
retval->append(collection->flattenIfSortable().entities);
}
else {
retval->append(**it);
}
}
}
}
ExtrusionEntityCollection
ExtrusionEntityCollection::flattenIfSortable() const
{
ExtrusionEntityCollection coll;
this->flattenIfSortable(&coll);
return coll;
}
double double
ExtrusionEntityCollection::min_mm3_per_mm() const ExtrusionEntityCollection::min_mm3_per_mm() const
{ {

View File

@ -77,7 +77,9 @@ public:
{ Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
size_t items_count() const; size_t items_count() const;
void flatten(ExtrusionEntityCollection* retval) const; void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const; ExtrusionEntityCollection flatten() const;
void flattenIfSortable(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flattenIfSortable() const;
double min_mm3_per_mm() const; double min_mm3_per_mm() const;
// Following methods shall never be called on an ExtrusionEntityCollection. // Following methods shall never be called on an ExtrusionEntityCollection.

View File

@ -228,9 +228,6 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
params.density = 0.01 * density; params.density = 0.01 * density;
// params.dont_adjust = true; // params.dont_adjust = true;
params.dont_adjust = false; params.dont_adjust = false;
Polylines polylines = f->fill_surface(&surface, params);
if (polylines.empty())
continue;
// calculate actual flow from spacing (which might have been adjusted by the infill // calculate actual flow from spacing (which might have been adjusted by the infill
// pattern generator) // pattern generator)
@ -244,22 +241,10 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
float flow_percent = 1; float flow_percent = 1;
if(surface.is_overBridge()){ if(surface.is_overBridge()){
flow_percent = layerm.region()->config.over_bridge_flow_ratio; params.flow_mult = layerm.region()->config.over_bridge_flow_ratio;
} }
// Save into layer. f->fill_surface_extrusion(&surface, params, flow, out);
auto *eec = new ExtrusionEntityCollection();
out.entities.push_back(eec);
// Only concentric fills are not sorted.
eec->no_sort = f->no_sort();
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines),
is_bridge ?
erBridgeInfill :
(surface.is_solid() ?
((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
erInternalInfill),
flow.mm3_per_mm()*flow_percent, flow.width*flow_percent, flow.height);
} }
// add thin fill regions // add thin fill regions

View File

@ -3,6 +3,7 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
#include "../ExtrusionEntityCollection.hpp"
#include "FillBase.hpp" #include "FillBase.hpp"
#include "FillConcentric.hpp" #include "FillConcentric.hpp"
@ -13,6 +14,7 @@
#include "FillRectilinear.hpp" #include "FillRectilinear.hpp"
#include "FillRectilinear2.hpp" #include "FillRectilinear2.hpp"
#include "FillRectilinear3.hpp" #include "FillRectilinear3.hpp"
#include "FillSmooth.hpp"
namespace Slic3r { namespace Slic3r {
@ -34,6 +36,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipArchimedeanChords: return new FillArchimedeanChords(); case ipArchimedeanChords: return new FillArchimedeanChords();
case ipHilbertCurve: return new FillHilbertCurve(); case ipHilbertCurve: return new FillHilbertCurve();
case ipOctagramSpiral: return new FillOctagramSpiral(); case ipOctagramSpiral: return new FillOctagramSpiral();
case ipSmooth: return new FillSmooth();
default: CONFESS("unknown type"); return nullptr; default: CONFESS("unknown type"); return nullptr;
} }
} }
@ -130,4 +133,25 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
return std::pair<float, Point>(out_angle, out_shift); return std::pair<float, Point>(out_angle, out_shift);
} }
void Fill::fill_surface_extrusion(const Surface *surface, const FillParams &params, const Flow &flow, ExtrusionEntityCollection &out ){
Polylines polylines = this->fill_surface(surface, params);
if (polylines.empty())
return;
// Save into layer.
auto *eec = new ExtrusionEntityCollection();
out.entities.push_back(eec);
// Only concentric fills are not sorted.
eec->no_sort = this->no_sort();
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines),
flow.bridge ?
erBridgeInfill :
(surface->is_solid() ?
((surface->is_top()) ? erTopSolidInfill : erSolidInfill) :
erInternalInfill),
flow.mm3_per_mm() * params.flow_mult, flow.width * params.flow_mult, flow.height);
}
} // namespace Slic3r } // namespace Slic3r

View File

@ -9,6 +9,7 @@
#include "../libslic3r.h" #include "../libslic3r.h"
#include "../BoundingBox.hpp" #include "../BoundingBox.hpp"
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
#include "../Flow.hpp"
namespace Slic3r { namespace Slic3r {
@ -20,12 +21,16 @@ struct FillParams
memset(this, 0, sizeof(FillParams)); memset(this, 0, sizeof(FillParams));
// Adjustment does not work. // Adjustment does not work.
dont_adjust = true; dont_adjust = true;
flow_mult = 1.f;
} }
bool full_infill() const { return density > 0.9999f; } bool full_infill() const { return density > 0.9999f; }
// Fill density, fraction in <0, 1> // Fill density, fraction in <0, 1>
float density; float density;
// Fill extruding flow multiplier, fraction in <0, 1>. Used by FillSmooth
float flow_mult;
// Don't connect the fill lines around the inner perimeter. // Don't connect the fill lines around the inner perimeter.
bool dont_connect; bool dont_connect;
@ -75,6 +80,9 @@ public:
// Do not sort the fill lines to optimize the print head path? // Do not sort the fill lines to optimize the print head path?
virtual bool no_sort() const { return false; } virtual bool no_sort() const { return false; }
// This method have to fill the ExtrusionEntityCollection. It call fill_surface by default
virtual void fill_surface_extrusion(const Surface *surface, const FillParams &params, const Flow &flow, ExtrusionEntityCollection &out );
// Perform the fill. // Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams &params); virtual Polylines fill_surface(const Surface *surface, const FillParams &params);

View File

@ -0,0 +1,160 @@
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#include "../ExtrusionEntityCollection.hpp"
#include "../Surface.hpp"
#include <cmath>
#include <algorithm>
#include <iostream>
#include "FillSmooth.hpp"
namespace Slic3r {
Polylines FillSmooth::fill_surface(const Surface *surface, const FillParams &params)
{
//ERROR: you shouldn't call that. Default to the rectilinear one.
printf("FillSmooth::fill_surface() : you call the wrong method (fill_surface instead of fill_surface_extrusion).\n");
Polylines polylines_out;
return polylines_out;
}
void FillSmooth::fill_surface_extrusion(const Surface *surface, const FillParams &params, const Flow &flow, ExtrusionEntityCollection &out )
{
coordf_t init_spacing = this->spacing;
printf("FillSmooth::fill_surface() : you call the right method (fill_surface instead of fill_surface_extrusion).\n");
//second pass with half layer width
FillParams params1 = params;
FillParams params2 = params;
FillParams params3 = params;
params1.density *= percentWidth[0];
params2.density *= percentWidth[1];
params3.density *= percentWidth[2];
// Polylines polylines_layer1;
// Polylines polylines_layer2;
// Polylines polylines_layer3;
//a small under-overlap to prevent over-extrudion on thin surfaces (i.e. remove the overlap)
Surface surfaceNoOverlap(*surface);
//remove the overlap (prevent over-extruding)
ExPolygons offsetPloys = offset2_ex(surfaceNoOverlap.expolygon, -scale_(this->overlap)*0.9, 0);
if(offsetPloys.size() == 1) surfaceNoOverlap.expolygon = offsetPloys[0];
//TODO: recursive if multiple polys instead of failing
//if (! fill_surface_by_lines(&surfaceNoOverlap/* */, params1, anglePass[0], 0.f, polylines_layer1) ||
// (nbPass>1 && !fill_surface_by_lines(surface, params2, anglePass[1], 0.f, polylines_layer2)) ||
// (nbPass>2 && !fill_surface_by_lines(surface, params3, anglePass[2], 0.f, polylines_layer3))) {
// printf("FillSmooth::fill_surface() failed to fill a region.\n");
//}
/*
std::cout<<"polylines_layer1 size ="<<polylines_layer1.size()<<" \n";
std::cout<<"polylines_layer2 size ="<<polylines_layer2.size()<<" \n";
std::cout<<"polylines_layer3 size ="<<polylines_layer3.size()<<" \n";*/
//if (polylines_layer1.empty() && polylines_layer2.empty() && polylines_layer3.empty())
// return;
//create root node
ExtrusionEntityCollection *eecroot = new ExtrusionEntityCollection();
//you don't want to sort the extrusions: big infill first, small second
eecroot->no_sort = true;
ExtrusionEntityCollection *eec;
// first infill
std::unique_ptr<Fill> f1 = std::unique_ptr<Fill>(Fill::new_from_type(fillPattern[0]));
f1->bounding_box = this->bounding_box;
f1->spacing = init_spacing;
f1->layer_id = this->layer_id;
f1->z = this->z;
f1->angle = anglePass[0];
// Maximum length of the perimeter segment linking two infill lines.
f1->link_max_length = this->link_max_length;
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f1->loop_clipping = this->loop_clipping;
Polylines polylines_layer1 = f1->fill_surface(surface, params1);
if (!polylines_layer1.empty()){
eec = new ExtrusionEntityCollection();
eecroot->entities.push_back(eec);
eec->no_sort = false; //can be sorted inside the pass
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines_layer1),
flow.bridge ? erBridgeInfill : rolePass[0],
flow.mm3_per_mm()*percentFlow[0], (float)flow.width*percentFlow[0], (float)flow.height*0.9);
//reduced flow width & height for a better view (it's only a gui thing)
}
//second infill
if (nbPass > 1){
std::unique_ptr<Fill> f2 = std::unique_ptr<Fill>(Fill::new_from_type(fillPattern[1]));
f2->bounding_box = this->bounding_box;
f2->spacing = init_spacing;
f2->layer_id = this->layer_id;
f2->z = this->z;
f2->angle = anglePass[1];
// Maximum length of the perimeter segment linking two infill lines.
f2->link_max_length = this->link_max_length;
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f2->loop_clipping = this->loop_clipping;
Polylines polylines_layer2 = f2->fill_surface(&surfaceNoOverlap, params2);
if (fillPattern[2] == InfillPattern::ipRectilinear && polylines_layer2[0].points.size() > 3){
polylines_layer2[0].points.erase(polylines_layer2[0].points.begin());
polylines_layer2[polylines_layer2.size() - 1].points.pop_back();
}
if (!polylines_layer2.empty()){
// Save into layer smoothing path.
eec = new ExtrusionEntityCollection();
eecroot->entities.push_back(eec);
eec->no_sort = false;
// print thin
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines_layer2),
rolePass[1],
flow.mm3_per_mm()*percentFlow[1], (float)flow.width*(percentFlow[1] < 0.1 ? 0.1 : percentFlow[1]), (float)flow.height);
}
}
// third infill
if (nbPass > 2){
std::unique_ptr<Fill> f3 = std::unique_ptr<Fill>(Fill::new_from_type(fillPattern[0]));
f3->bounding_box = this->bounding_box;
f3->spacing = init_spacing;
f3->layer_id = this->layer_id;
f3->z = this->z;
f3->angle = anglePass[2];
// Maximum length of the perimeter segment linking two infill lines.
f3->link_max_length = this->link_max_length;
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f3->loop_clipping = this->loop_clipping;
Polylines polylines_layer3 = f3->fill_surface(&surfaceNoOverlap, params1);
//remove some points that are not inside the area
if (fillPattern[2] == InfillPattern::ipRectilinear && polylines_layer3[0].points.size() > 3){
polylines_layer3[0].points.erase(polylines_layer3[0].points.begin());
polylines_layer3[polylines_layer3.size() - 1].points.pop_back();
}
if (!polylines_layer3.empty()){
// Save into layer smoothing path. layer 3
eec = new ExtrusionEntityCollection();
eecroot->entities.push_back(eec);
eec->no_sort = false;
// print thin
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines_layer3),
rolePass[2], //slow (if last)
flow.mm3_per_mm()*percentFlow[2], (float)flow.width*(percentFlow[2] < 0.1 ? 0.1 : percentFlow[2]), (float)flow.height);
}
}
if (!eecroot->entities.empty())
out.entities.push_back(eecroot);
}
} // namespace Slic3r

View File

@ -0,0 +1,58 @@
#ifndef slic3r_FillSmooth_hpp_
#define slic3r_FillSmooth_hpp_
#include "../libslic3r.h"
#include "FillBase.hpp"
namespace Slic3r {
class FillSmooth : public Fill
{
public:
FillSmooth() {
nbPass = 2;
anglePass[0] = float(M_PI/4);
anglePass[1] = -float(M_PI/4);
anglePass[2] = 0;
fillPattern[0] = InfillPattern::ipRectilinear;
fillPattern[1] = InfillPattern::ipRectilinear;
fillPattern[2] = InfillPattern::ipRectilinear;
rolePass[0] = erSolidInfill;
rolePass[1] = erTopSolidInfill;
rolePass[2] = erSolidInfill;
percentWidth[0] = 0.9;
percentWidth[1] = 2;
percentWidth[2] = 1.0;
percentFlow[0] = 0.7/percentWidth[0]; //* 1.25 => 0.9
percentFlow[1] = 0.3/percentWidth[1]; //*1.25 => 0.35
percentFlow[2] = 0.0/percentWidth[2];
// percentWidth[0] = 2.0;
// percentWidth[1] = 0.6;
// percentWidth[2] = 1.0;
// percentFlow[0] = 0.65;
// percentFlow[1] = 0.0;
// percentFlow[2] = 0.0;
// (extrusion mult => 0.9 + 0.15*2 = 1.2)
double extrusionMult = 1.0;
percentFlow[0] *= extrusionMult;
percentFlow[1] *= extrusionMult;
percentFlow[2] *= extrusionMult;
}
virtual Fill* clone() const { return new FillSmooth(*this); }
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
virtual void fill_surface_extrusion(const Surface *surface, const FillParams &params, const Flow &flow, ExtrusionEntityCollection &out );
protected:
int nbPass=2;
double percentWidth[3];
double percentFlow[3];
float anglePass[3];
ExtrusionRole rolePass[3];
InfillPattern fillPattern[3];
};
} // namespace Slic3r
#endif // slic3r_FillSmooth_hpp_

View File

@ -1271,8 +1271,9 @@ void GCode::process_layer(
point_inside_surface(i, fill->first_point())) { point_inside_surface(i, fill->first_point())) {
if (islands[i].by_region.empty()) if (islands[i].by_region.empty())
islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].infills.append(fill->entities); //don't do fill->entities because it will discard no_sort
break; islands[i].by_region[region_id].infills.append(fill->flattenIfSortable().entities);
break;
} }
} }
} // for regions } // for regions
@ -2001,14 +2002,34 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
for (const ObjectByExtruder::Island::Region &region : by_region) { for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions[&region - &by_region.front()]->config); m_config.apply(print.regions[&region - &by_region.front()]->config);
ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false);
for (ExtrusionEntity *fill : chained.entities) { gcode += extrude_infill(print, chained);
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill); }
if (eec) { return gcode;
ExtrusionEntityCollection chained2 = eec->chained_path_from(m_last_pos, false); }
for (ExtrusionEntity *ee : chained2.entities)
gcode += this->extrude_entity(*ee, "infill"); //recursive algorithm to explore the collection tree
} else std::string GCode::extrude_infill(const Print &print, const ExtrusionEntityCollection &collection)
gcode += this->extrude_entity(*fill, "infill"); {
std::string gcode;
ExtrusionEntityCollection chained;
if (collection.no_sort) chained = collection;
else chained = collection.chained_path_from(m_last_pos, false);
for (ExtrusionEntity *fill : chained.entities) {
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
if (eec) {
std::cout << "recursive infill with role: " << fill->role() << " : " << (fill->role() == ExtrusionRole::erBridgeInfill) << "\n";
gcode += extrude_infill(print, *eec);
}
else {
std::cout << "extrude infill with role: " << fill->role() << " : " << (fill->role() == ExtrusionRole::erBridgeInfill) << "\n";
if (/*print.config.disable_fan_top_layers.value && */fill->role() == ExtrusionRole::erTopSolidInfill){ //TODO: add && params.get(disable_fan_top_layers)
// gcode += ";_DISABLE_FAN_START\n";
}
gcode += this->extrude_entity(*fill, "infill");
if (/*print.config.disable_fan_top_layers.value && */fill->role() == ExtrusionRole::erTopSolidInfill){ //TODO: add && params.get(disable_fan_top_layers)
// gcode += ";_DISABLE_FAN_END\n";
}
} }
} }
return gcode; return gcode;

View File

@ -221,7 +221,8 @@ protected:
}; };
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid); std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region); std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
std::string extrude_support(const ExtrusionEntityCollection &support_fills); std::string extrude_infill(const Print &print, const ExtrusionEntityCollection &collection); // recursive extrude_infill
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone);

View File

@ -264,11 +264,13 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral"); def->enum_values.push_back("octagramspiral");
def->enum_values.push_back("smooth");
def->enum_labels.push_back("Rectilinear"); def->enum_labels.push_back("Rectilinear");
def->enum_labels.push_back("Concentric"); def->enum_labels.push_back("Concentric");
def->enum_labels.push_back("Hilbert Curve"); def->enum_labels.push_back("Hilbert Curve");
def->enum_labels.push_back("Archimedean Chords"); def->enum_labels.push_back("Archimedean Chords");
def->enum_labels.push_back("Octagram Spiral"); def->enum_labels.push_back("Octagram Spiral");
def->enum_labels.push_back("Ironing");
// solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern. // solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern.
def->aliases.push_back("solid_fill_pattern"); def->aliases.push_back("solid_fill_pattern");
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear); def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);

View File

@ -29,7 +29,7 @@ enum GCodeFlavor {
enum InfillPattern { enum InfillPattern {
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSmooth,
}; };
enum SupportMaterialPattern { enum SupportMaterialPattern {
@ -77,6 +77,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enu
keys_map["hilbertcurve"] = ipHilbertCurve; keys_map["hilbertcurve"] = ipHilbertCurve;
keys_map["archimedeanchords"] = ipArchimedeanChords; keys_map["archimedeanchords"] = ipArchimedeanChords;
keys_map["octagramspiral"] = ipOctagramSpiral; keys_map["octagramspiral"] = ipOctagramSpiral;
keys_map["smooth"] = ipSmooth;
} }
return keys_map; return keys_map;
} }