Implementing a new switch for the shape of support towers:

expanded to a grid (the old way) vs.
snug (like the upstream Slic3r, Cura or Ideamaker).

Snug supports suffered from the degeneracies when merging overhang islands
over a large number of layers when projecting the support towers down.
We borrowed the idea & a bit of code from Cura by simplifying the support
polygons by closing the concave cracks, see the smooth_outward() function
and the MutablePolygon class.

Fixes Support problems with models with hole in the walls. #555
Fixes Support in the Air #740
Fixes [Bug] Supports generated beyond bed edges (X<0 and X>250) and where none are needed. #902
Fixes Unable to remove support material/can't change support "inflation distance" #2708
Fixes FR: support inflation and support conform to boundary #4783
Fixes Support blocker not working on this model #1346
Fixes Unnecessary support material #1993
Fixes support blocker enforcer issue #6240
This commit is contained in:
Vojtech Bubnik 2021-03-23 11:06:31 +01:00
parent 00295919bf
commit af9c7c967f
10 changed files with 208 additions and 151 deletions

View File

@ -427,7 +427,7 @@ const std::vector<std::string>& Preset::print_options()
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance", "support_material_contact_distance", "support_material_bottom_contact_distance",

View File

@ -2419,6 +2419,20 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60)); def->set_default_value(new ConfigOptionFloat(60));
def = this->add("support_material_style", coEnum);
def->label = L("Style");
def->category = L("Support material");
def->tooltip = L("Style and shape of the support towers. Projecting the supports into a regular grid "
"will create more stable supports, while snug support towers will save material and reduce "
"object scarring.");
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values();
def->enum_values.push_back("grid");
def->enum_values.push_back("snug");
def->enum_labels.push_back(L("Grid"));
def->enum_labels.push_back(L("Snug"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
def = this->add("support_material_synchronize_layers", coBool); def = this->add("support_material_synchronize_layers", coBool);
def->label = L("Synchronize with object layers"); def->label = L("Synchronize with object layers");
def->category = L("Support material"); def->category = L("Support material");

View File

@ -65,6 +65,10 @@ enum SupportMaterialPattern {
smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpRectilinear, smpRectilinearGrid, smpHoneycomb,
}; };
enum SupportMaterialStyle {
smsGrid, smsSnug,
};
enum SupportMaterialInterfacePattern { enum SupportMaterialInterfacePattern {
smipAuto, smipRectilinear, smipConcentric, smipAuto, smipRectilinear, smipConcentric,
}; };
@ -211,6 +215,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPa
return keys_map; return keys_map;
} }
template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialStyle>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["grid"] = smsGrid;
keys_map["snug"] = smsSnug;
}
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialInterfacePattern>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialInterfacePattern>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
@ -519,6 +532,7 @@ public:
// Spacing between support material lines (the hatching distance). // Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing; ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed; ConfigOptionFloat support_material_speed;
ConfigOptionEnum<SupportMaterialStyle> support_material_style;
ConfigOptionBool support_material_synchronize_layers; ConfigOptionBool support_material_synchronize_layers;
// Overhang angle threshold. // Overhang angle threshold.
ConfigOptionInt support_material_threshold; ConfigOptionInt support_material_threshold;
@ -570,6 +584,7 @@ protected:
OPT_PTR(support_material_interface_pattern); OPT_PTR(support_material_interface_pattern);
OPT_PTR(support_material_spacing); OPT_PTR(support_material_spacing);
OPT_PTR(support_material_speed); OPT_PTR(support_material_speed);
OPT_PTR(support_material_style);
OPT_PTR(support_material_synchronize_layers); OPT_PTR(support_material_synchronize_layers);
OPT_PTR(support_material_xy_spacing); OPT_PTR(support_material_xy_spacing);
OPT_PTR(support_material_threshold); OPT_PTR(support_material_threshold);

View File

@ -588,6 +588,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_material_interface_extruder" || opt_key == "support_material_interface_extruder"
|| opt_key == "support_material_interface_spacing" || opt_key == "support_material_interface_spacing"
|| opt_key == "support_material_pattern" || opt_key == "support_material_pattern"
|| opt_key == "support_material_style"
|| opt_key == "support_material_xy_spacing" || opt_key == "support_material_xy_spacing"
|| opt_key == "support_material_spacing" || opt_key == "support_material_spacing"
|| opt_key == "support_material_synchronize_layers" || opt_key == "support_material_synchronize_layers"

View File

@ -6,6 +6,7 @@
#include "Fill/FillBase.hpp" #include "Fill/FillBase.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "Point.hpp" #include "Point.hpp"
#include "MutablePolygon.hpp"
#include <cmath> #include <cmath>
#include <memory> #include <memory>
@ -667,12 +668,14 @@ Polygons collect_slices_outer(const Layer &layer)
struct SupportGridParams { struct SupportGridParams {
SupportGridParams(const PrintObjectConfig &object_config, const Flow &support_material_flow) : SupportGridParams(const PrintObjectConfig &object_config, const Flow &support_material_flow) :
style(object_config.support_material_style.value),
grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()), grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()),
support_angle(Geometry::deg2rad(object_config.support_material_angle.value)), support_angle(Geometry::deg2rad(object_config.support_material_angle.value)),
extrusion_width(support_material_flow.spacing()), extrusion_width(support_material_flow.spacing()),
expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)), expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)),
expansion_to_propagate(-3) {} expansion_to_propagate(-3) {}
SupportMaterialStyle style;
double grid_resolution; double grid_resolution;
double support_angle; double support_angle;
double extrusion_width; double extrusion_width;
@ -689,9 +692,14 @@ public:
// Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
const Polygons *trimming_polygons, const Polygons *trimming_polygons,
const SupportGridParams &params) : const SupportGridParams &params) :
m_style(params.style),
m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons), m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons),
m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle) m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle)
{ {
switch (m_style) {
case smsGrid:
{
// Prepare the grid data, it will be reused when extracting support structures.
if (m_support_angle != 0.) { if (m_support_angle != 0.) {
// Create a copy of the rotated contours. // Create a copy of the rotated contours.
m_support_polygons_rotated = *support_polygons; m_support_polygons_rotated = *support_polygons;
@ -709,7 +717,7 @@ public:
// Align the bounding box with the sparse support grid. // Align the bounding box with the sparse support grid.
bbox.align_to_grid(grid_resolution); bbox.align_to_grid(grid_resolution);
#ifdef SUPPORT_USE_AGG_RASTERIZER #ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox; m_bbox = bbox;
// Oversample the grid to avoid leaking of supports through or around the object walls. // Oversample the grid to avoid leaking of supports through or around the object walls.
int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100))); int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
@ -733,31 +741,39 @@ public:
dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size), dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size),
grid_blocks, oversampling); grid_blocks, oversampling);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
{ {
static int irun; static int irun;
Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4); Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4);
} }
#endif // SLIC3R_DEBUG #endif // SLIC3R_DEBUG
#else // SUPPORT_USE_AGG_RASTERIZER #else // SUPPORT_USE_AGG_RASTERIZER
// Create an EdgeGrid, initialize it with projection, initialize signed distance field. // Create an EdgeGrid, initialize it with projection, initialize signed distance field.
m_grid.set_bbox(bbox); m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution); m_grid.create(*m_support_polygons, grid_resolution);
#if 0 #if 0
if (m_grid.has_intersecting_edges()) { if (m_grid.has_intersecting_edges()) {
// EdgeGrid fails to produce valid signed distance function for self-intersecting polygons. // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
m_support_polygons_rotated = simplify_polygons(*m_support_polygons); m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
m_support_polygons = &m_support_polygons_rotated; m_support_polygons = &m_support_polygons_rotated;
m_grid.set_bbox(bbox); m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution); m_grid.create(*m_support_polygons, grid_resolution);
// assert(! m_grid.has_intersecting_edges()); // assert(! m_grid.has_intersecting_edges());
printf("SupportGridPattern: fixing polygons with intersection %s\n", printf("SupportGridPattern: fixing polygons with intersection %s\n",
m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED"); m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
} }
#endif #endif
m_grid.calculate_sdf(); m_grid.calculate_sdf();
#endif // SUPPORT_USE_AGG_RASTERIZER #endif // SUPPORT_USE_AGG_RASTERIZER
break;
}
case smsSnug:
default:
// nothing to prepare
break;
}
} }
// Extract polygons from the grid, offsetted by offset_in_grid, // Extract polygons from the grid, offsetted by offset_in_grid,
@ -770,21 +786,21 @@ public:
#endif #endif
) )
{ {
#ifdef SUPPORT_USE_AGG_RASTERIZER switch (m_style) {
case smsGrid:
{
#ifdef SUPPORT_USE_AGG_RASTERIZER
Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes); Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes);
#else // SUPPORT_USE_AGG_RASTERIZER #else // SUPPORT_USE_AGG_RASTERIZER
// Generate islands, so each island may be tested for overlap with island_samples. // Generate islands, so each island may be tested for overlap with island_samples.
assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes); Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
#endif // SUPPORT_USE_AGG_RASTERIZER #endif // SUPPORT_USE_AGG_RASTERIZER
ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false); ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
// Extract polygons, which contain some of the island_samples. // Extract polygons, which contain some of the island_samples.
Polygons out; Polygons out;
#if 0
out = to_polygons(std::move(islands));
#else
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
// polygons if ever these polygons get split into parts by the trimming polygons. // polygons if ever these polygons get split into parts by the trimming polygons.
@ -835,9 +851,8 @@ public:
} }
} }
} }
#endif
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
BoundingBox bbox = get_extents(*m_trimming_polygons); BoundingBox bbox = get_extents(*m_trimming_polygons);
if (! islands.empty()) if (! islands.empty())
bbox.merge(get_extents(islands)); bbox.merge(get_extents(islands));
@ -856,12 +871,18 @@ public:
for (const Point &pt : samples) for (const Point &pt : samples)
svg.draw(pt, "black", coord_t(scale_(0.15))); svg.draw(pt, "black", coord_t(scale_(0.15)));
svg.Close(); svg.Close();
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
if (m_support_angle != 0.) if (m_support_angle != 0.)
polygons_rotate(out, m_support_angle); polygons_rotate(out, m_support_angle);
return out; return out;
} }
case smsSnug:
// Just close the gaps.
float thr = scaled<float>(0.5);
return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr);
}
}
#if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER) #if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER)
void serialize(const std::string &path) void serialize(const std::string &path)
@ -1096,6 +1117,7 @@ private:
return pts; return pts;
} }
SupportMaterialStyle m_style;
const Polygons *m_support_polygons; const Polygons *m_support_polygons;
const Polygons *m_trimming_polygons; const Polygons *m_trimming_polygons;
Polygons m_support_polygons_rotated; Polygons m_support_polygons_rotated;
@ -1525,7 +1547,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
ClipperLib::jtRound, ClipperLib::jtRound,
// round mitter limit // round mitter limit
scale_(0.05)), scale_(0.05)),
slices_margin_cached); slices_margin.polygons);
} }
#else #else
diff_polygons = diff(diff_polygons, slices_margin.polygons); diff_polygons = diff(diff_polygons, slices_margin.polygons);

View File

@ -278,7 +278,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
bool have_support_material_auto = have_support_material && config->opt_bool("support_material_auto"); bool have_support_material_auto = have_support_material && config->opt_bool("support_material_auto");
bool have_support_interface = config->opt_int("support_material_interface_layers") > 0; bool have_support_interface = config->opt_int("support_material_interface_layers") > 0;
bool have_support_soluble = have_support_material && config->opt_float("support_material_contact_distance") == 0; bool have_support_soluble = have_support_material && config->opt_float("support_material_contact_distance") == 0;
for (auto el : { "support_material_pattern", "support_material_with_sheath", for (auto el : { "support_material_style", "support_material_pattern", "support_material_with_sheath",
"support_material_spacing", "support_material_angle", "support_material_spacing", "support_material_angle",
"support_material_interface_pattern", "support_material_interface_layers", "support_material_interface_pattern", "support_material_interface_layers",
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",

View File

@ -1224,6 +1224,8 @@ boost::any& Choice::get_value()
m_value = static_cast<SupportMaterialPattern>(ret_enum); m_value = static_cast<SupportMaterialPattern>(ret_enum);
else if (m_opt_id.compare("support_material_interface_pattern") == 0) else if (m_opt_id.compare("support_material_interface_pattern") == 0)
m_value = static_cast<SupportMaterialInterfacePattern>(ret_enum); m_value = static_cast<SupportMaterialInterfacePattern>(ret_enum);
else if (m_opt_id.compare("support_material_style") == 0)
m_value = static_cast<SupportMaterialStyle>(ret_enum);
else if (m_opt_id.compare("seam_position") == 0) else if (m_opt_id.compare("seam_position") == 0)
m_value = static_cast<SeamPosition>(ret_enum); m_value = static_cast<SeamPosition>(ret_enum);
else if (m_opt_id.compare("host_type") == 0) else if (m_opt_id.compare("host_type") == 0)

View File

@ -198,6 +198,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value))); config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value)));
else if (opt_key.compare("support_material_interface_pattern") == 0) else if (opt_key.compare("support_material_interface_pattern") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialInterfacePattern>(boost::any_cast<SupportMaterialInterfacePattern>(value))); config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialInterfacePattern>(boost::any_cast<SupportMaterialInterfacePattern>(value)));
else if (opt_key.compare("support_material_style") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialStyle>(boost::any_cast<SupportMaterialStyle>(value)));
else if (opt_key.compare("seam_position") == 0) else if (opt_key.compare("seam_position") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value))); config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
else if (opt_key.compare("host_type") == 0) else if (opt_key.compare("host_type") == 0)

View File

@ -50,7 +50,7 @@ static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF =
{ L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } },
{ L("Infill") , { "fill_density", "fill_pattern" } }, { L("Infill") , { "fill_density", "fill_pattern" } },
{ L("Support material") , { "support_material", "support_material_auto", "support_material_threshold", { L("Support material") , { "support_material", "support_material_auto", "support_material_threshold",
"support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", "support_material_pattern", "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only",
"support_material_spacing" } }, "support_material_spacing" } },
{ L("Wipe options") , { "wipe_into_infill", "wipe_into_objects" } } { L("Wipe options") , { "wipe_into_infill", "wipe_into_objects" } }
}; };

View File

@ -1506,6 +1506,7 @@ void TabPrint::build()
optgroup->append_single_option_line("raft_expansion"); optgroup->append_single_option_line("raft_expansion");
optgroup = page->new_optgroup(L("Options for support material and raft")); optgroup = page->new_optgroup(L("Options for support material and raft"));
optgroup->append_single_option_line("support_material_style", category_path + "style");
optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance"); optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance");
optgroup->append_single_option_line("support_material_bottom_contact_distance", category_path + "contact-z-distance"); optgroup->append_single_option_line("support_material_bottom_contact_distance", category_path + "contact-z-distance");
optgroup->append_single_option_line("support_material_pattern", category_path + "pattern"); optgroup->append_single_option_line("support_material_pattern", category_path + "pattern");