Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/libslic3r/ClipperUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,12 @@ Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Pol
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset)
{
Slic3r::ExPolygons clip_temp;
clip_temp.push_back(clip);
return diff_ex(subject, clip_temp, do_safety_offset);
}

Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Expand All @@ -817,6 +823,8 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset);}
// May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative).
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type)
{ return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No, fill_type); }
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/ClipperUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surf
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
Expand Down Expand Up @@ -498,6 +499,7 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ static std::vector<std::string> s_Preset_print_options {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"ensure_vertical_shell_thickness", "extra_perimeters", "extra_perimeters_on_overhangs",
"make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size",
"avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position", "staggered_inner_seams", "seam_gap_distance",
"external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/Print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posC
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
void apply_conical_overhang();
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data(
const std::vector<std::pair<const Surface*, float>>& surfaces_w_bottom_z) const;
FillLightning::GeneratorPtr prepare_lightning_infill_data();
Expand Down
29 changes: 29 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,35 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(100., true));

def = this->add("make_overhang_printable", coBool);
def->label = L("Make overhangs printable");
def->category = L("Quality");
def->tooltip = L("Modify the geometry to print overhangs without support material.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));

def = this->add("make_overhang_printable_angle", coFloat);
def->label = L("Make overhangs printable - Maximum angle");
def->category = L("Quality");
def->tooltip = L("Maximum angle of overhangs to allow after making more steep overhangs printable."
"90° will not change the model at all and allow any overhang, while 0 will "
"replace all overhangs with conical material.");
def->sidetext = u8"°"; // degrees, don't need translation
def->mode = comAdvanced;
def->min = 0.;
def->max = 90.;
def->set_default_value(new ConfigOptionFloat(55.));

def = this->add("make_overhang_printable_hole_size", coFloat);
def->label = L("Make overhangs printable - Hole area");
def->category = L("Quality");
def->tooltip = L("Maximum area of a hole in the base of the model before it's filled by conical material. "
"A value of 0 will fill all the holes in the model base.");
def->sidetext = u8"mm²"; // square millimeters, don't need translation
def->mode = comAdvanced;
def->min = 0.;
def->set_default_value(new ConfigOptionFloat(0.));

// Maximum extruder temperature, bumped to 1500 to support printing of glass.
const int max_temp = 1500;
def = this->add("avoid_crossing_curled_overhangs", coBool);
Expand Down
4 changes: 3 additions & 1 deletion src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, thick_bridges))
((ConfigOptionFloat, xy_size_compensation))
((ConfigOptionBool, wipe_into_objects))

((ConfigOptionFloat, make_overhang_printable_angle))
((ConfigOptionFloat, make_overhang_printable_hole_size))
((ConfigOptionBool, interlocking_beam))
((ConfigOptionFloat, interlocking_beam_width))
((ConfigOptionFloat, interlocking_orientation))
Expand Down Expand Up @@ -763,6 +764,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, top_solid_min_thickness))
((ConfigOptionFloatOrPercent, top_solid_infill_speed))
((ConfigOptionBool, wipe_into_infill))
((ConfigOptionBool, make_overhang_printable))
// Single perimeter.
((ConfigOptionEnum<TopOnePerimeterType>, top_one_perimeter_type))
((ConfigOptionBool, only_one_perimeter_first_layer))
Expand Down
3 changes: 3 additions & 0 deletions src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,9 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "raft_contact_distance"
|| opt_key == "slice_closing_radius"
|| opt_key == "slicing_mode"
|| opt_key == "make_overhang_printable"
|| opt_key == "make_overhang_printable_angle"
|| opt_key == "make_overhang_printable_hole_size"
|| opt_key == "interlocking_beam"
|| opt_key == "interlocking_orientation"
|| opt_key == "interlocking_beam_layer_count"
Expand Down
120 changes: 120 additions & 0 deletions src/libslic3r/PrintObjectSlice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,8 @@ void PrintObject::slice_volumes()
m_layers.back()->upper_layer = nullptr;
m_print->throw_if_canceled();

this->apply_conical_overhang();

// Is any ModelVolume multi-material painted?
if (m_print->config().nozzle_diameter.size() > 1 && this->model_object()->is_mm_painted()) {
// If XY Size compensation is also enabled, notify the user that XY Size compensation
Expand Down Expand Up @@ -1011,6 +1013,124 @@ void PrintObject::slice_volumes()
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
}

void PrintObject::apply_conical_overhang() {
BOOST_LOG_TRIVIAL(info) << "Make overhang printable...";

if (m_layers.empty()) {
return;
}

const double conical_overhang_angle = this->config().make_overhang_printable_angle;
if (conical_overhang_angle == 90.0) {
return;
}
const double angle_radians = conical_overhang_angle * M_PI / 180.;
const double max_hole_area = this->config().make_overhang_printable_hole_size; // in MM^2
const double tan_angle = tan(angle_radians); // the XY-component of the angle
BOOST_LOG_TRIVIAL(info) << "angle " << angle_radians << " maxHoleArea " << max_hole_area << " tan_angle "
<< tan_angle;
const coordf_t layer_thickness = m_config.layer_height.value;
const coordf_t max_dist_from_lower_layer = tan_angle * layer_thickness; // max dist which can be bridged, in MM
BOOST_LOG_TRIVIAL(info) << "layer_thickness " << layer_thickness << " max_dist_from_lower_layer "
<< max_dist_from_lower_layer;

// Pre-scale config
const coordf_t scaled_max_dist_from_lower_layer = -float(scale_(max_dist_from_lower_layer));
const coordf_t scaled_max_hole_area = float(scale_(scale_(max_hole_area)));


for (auto i = m_layers.rbegin() + 1; i != m_layers.rend(); ++i) {
m_print->throw_if_canceled();
Layer *layer = *i;
Layer *upper_layer = layer->upper_layer;

if (upper_layer->empty()) {
continue;
}

// Skip if entire layer has this disabled
if (std::all_of(layer->m_regions.begin(), layer->m_regions.end(),
[](const LayerRegion *r) { return r->slices().empty() || !r->region().config().make_overhang_printable; })) {
continue;
}

//layer->export_region_slices_to_svg_debug("layer_before_conical_overhang");
//upper_layer->export_region_slices_to_svg_debug("upper_layer_before_conical_overhang");


// Merge the upper layer because we want to offset the entire layer uniformly, otherwise
// the model could break at the region boundary.
auto upper_poly = upper_layer->merged(float(SCALED_EPSILON));
upper_poly = union_ex(upper_poly);

// Merge layer for the same reason
auto current_poly = layer->merged(float(SCALED_EPSILON));
current_poly = union_ex(current_poly);

// Avoid closing up of recessed holes in the base of a model.
// Detects when a hole is completely covered by the layer above and removes the hole from the layer above before
// adding it in.
// This should have no effect any time a hole in a layer interacts with any polygon in the layer above
if (scaled_max_hole_area > 0.0) {

// Now go through all the holes in the current layer and check if they intersect anything in the layer above
// If not, then they're the top of a hole and should be cut from the layer above before the union
for (auto layer_polygon : current_poly) {
for (auto hole : layer_polygon.holes) {
if (std::abs(hole.area()) < scaled_max_hole_area) {
ExPolygon hole_poly(hole);
auto hole_with_above = intersection_ex(upper_poly, hole_poly);
if (!hole_with_above.empty()) {
// The hole had some intersection with the above layer, check if it's a complete overlap
auto hole_difference = xor_ex(hole_with_above, hole_poly);
if (hole_difference.empty()) {
// The layer above completely cover it, remove it from the layer above
upper_poly = diff_ex(upper_poly, hole_poly);
}
}
}
}
}
}


// Now offset the upper layer to be added into current layer
upper_poly = offset_ex(upper_poly, scaled_max_dist_from_lower_layer);

for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
// export_to_svg(debug_out_path("Surface-obj-%d-layer-%d-region-%d.svg", id().id, layer->id(), region_id).c_str(),
// layer->m_regions[region_id]->slices.surfaces);

// Disable on given region
if (!upper_layer->m_regions[region_id]->region().config().make_overhang_printable) {
continue;
}

// Calculate the scaled upper poly that belongs to current region
auto p = union_ex(intersection_ex(upper_layer->m_regions[region_id]->slices().surfaces, upper_poly));

// Remove all islands that have already been fully covered by current layer
p.erase(std::remove_if(p.begin(), p.end(), [&current_poly](const ExPolygon& ex) {
return diff_ex(ex, current_poly).empty();
}), p.end());

// And now union it with current region
ExPolygons layer_polygons = to_expolygons(layer->m_regions[region_id]->slices().surfaces);
layer->m_regions[region_id]->m_slices.set(union_ex(layer_polygons, p), stInternal);

// Then remove it from all other regions, to avoid overlapping regions
for (size_t other_region = 0; other_region < this->num_printing_regions(); ++other_region) {
if (other_region == region_id) {
continue;
}
ExPolygons s = to_expolygons(layer->m_regions[other_region]->slices().surfaces);
layer->m_regions[other_region]->m_slices.set(diff_ex(s, p, ApplySafetyOffset::Yes), stInternal);
}
}
//layer->export_region_slices_to_svg_debug("layer_after_conical_overhang");
}
}

std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
{
auto it_volume = this->model_object()->volumes.begin();
Expand Down
4 changes: 4 additions & 0 deletions src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("min_bead_width", have_arachne);
toggle_field("thin_walls", !have_arachne);

bool have_make_overhang_printable = config->opt_bool("make_overhang_printable");
toggle_field("make_overhang_printable_angle", have_make_overhang_printable);
toggle_field("make_overhang_printable_hole_size", have_make_overhang_printable);

toggle_field("scarf_seam_placement", !has_spiral_vase);
const auto scarf_seam_placement{config->opt_enum<ScarfSeamPlacement>("scarf_seam_placement")};
const bool uses_scarf_seam{!has_spiral_vase && scarf_seam_placement != ScarfSeamPlacement::nowhere};
Expand Down
6 changes: 5 additions & 1 deletion src/slic3r/GUI/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1708,7 +1708,11 @@ void TabPrint::build()
optgroup->append_single_option_line("gcode_resolution");
optgroup->append_single_option_line("arc_fitting");
optgroup->append_single_option_line("xy_size_compensation");
optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487");

optgroup = page->new_optgroup(L("Overhangs"));
optgroup->append_single_option_line("make_overhang_printable");
optgroup->append_single_option_line("make_overhang_printable_angle");
optgroup->append_single_option_line("make_overhang_printable_hole_size");

optgroup = page->new_optgroup(L("Arachne perimeter generator"));
optgroup->append_single_option_line("wall_transition_angle");
Expand Down