Skip to content

Commit

Permalink
Porting Orca Make Overhang Printable SoftFever/OrcaSlicer#1615
Browse files Browse the repository at this point in the history
  • Loading branch information
vovodroid committed Jun 12, 2024
1 parent 22da600 commit 09a2eb8
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/libslic3r/ClipperUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,13 @@ 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); }
//Orca
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 @@ -800,6 +807,10 @@ 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); }
//Orca
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 All @@ -815,6 +826,10 @@ Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, const Slic3r::Pol
{ return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ClipperLib::pftNonZero)); }
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject)
{ return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::SurfacesProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftNonZero)); }
//Orca
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) {
return _clipper_ex(ClipperLib::ctXor, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset);
}

template<typename PathsProvider1, typename PathsProvider2>
Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip)
Expand Down
9 changes: 9 additions & 0 deletions src/libslic3r/ClipperUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ 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);
//Orca
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 @@ -483,6 +486,9 @@ 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);
//Orca
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 Expand Up @@ -523,6 +529,9 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject);
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject);
ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject);

//Orca
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);

Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject);

ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes);
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ static std::vector<std::string> s_Preset_print_options {
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"ensure_vertical_shell_thickness",
"extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size",
"seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"solid_fill_pattern",
"internal_first_on_overhangs",
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/Print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posC

std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> m_adaptive_fill_octrees;
FillLightning::GeneratorPtr m_lightning_generator;
//Orca
void apply_conical_overhang();
};


Expand Down
29 changes: 29 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3701,6 +3701,35 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->set_default_value(new ConfigOptionFloatOrPercent(85, true));

def = this->add("make_overhang_printable", coBool);
def->label = L("Make overhang printable");
def->category = L("Advanced");
def->tooltip = L("Modify the geometry to print overhangs without support material. Disabling modifier must include the object full horizontal dimensions.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));

def = this->add("make_overhang_printable_angle", coFloat);
def->label = L("Make overhang printable maximum angle");
def->category = L("Advanced");
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 = L("°");
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 overhang printable hole area");
def->category = L("Advanced");
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 = L("mm²");
def->mode = comAdvanced;
def->min = 0.;
def->set_default_value(new ConfigOptionFloat(0.));

// Declare retract values for filament profile, overriding the printer's extruder profile.
for (const char *opt_key : {
// floats
Expand Down
5 changes: 5 additions & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,9 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, xy_size_compensation))
((ConfigOptionBool, wipe_into_objects))
((ConfigOptionInt, bridge_fan_speed_layers))
// Orca
((ConfigOptionFloat, make_overhang_printable_angle))
((ConfigOptionFloat, make_overhang_printable_hole_size))
)

PRINT_CONFIG_CLASS_DEFINE(
Expand Down Expand Up @@ -676,6 +679,8 @@ PRINT_CONFIG_CLASS_DEFINE(
// SuperSlicer
((ConfigOptionBool, only_one_perimeter_top))
((ConfigOptionFloatOrPercent, min_width_top_surface))
// Orca
((ConfigOptionBool, make_overhang_printable))
)

PRINT_CONFIG_CLASS_DEFINE(
Expand Down
5 changes: 4 additions & 1 deletion src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "raft_layers"
|| opt_key == "raft_contact_distance"
|| opt_key == "slice_closing_radius"
|| opt_key == "slicing_mode") {
|| opt_key == "slicing_mode"
|| opt_key == "make_overhang_printable"
|| opt_key == "make_overhang_printable_angle"
|| opt_key == "make_overhang_printable_hole_size") {
steps.emplace_back(posSlice);
} else if (
opt_key == "elefant_foot_compensation"
Expand Down
105 changes: 105 additions & 0 deletions src/libslic3r/PrintObjectSlice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,9 @@ void PrintObject::slice_volumes()
apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); });
}

//Orca
this->apply_conical_overhang();
m_print->throw_if_canceled();

BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
{
Expand Down Expand Up @@ -842,6 +845,108 @@ void PrintObject::slice_volumes()
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
}

//Orca
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 == nullptr || 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);

// 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) {
// Merge layer for the same reason
auto current_poly = layer->merged(float(SCALED_EPSILON));
current_poly = union_ex(current_poly);

// 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 = intersection_ex(upper_layer->m_regions[region_id]->slices().surfaces, upper_poly);
// And now union it
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);
}
//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 @@ -375,6 +375,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("min_bead_width", have_arachne);
toggle_field("thin_walls", !have_arachne);

//Orca
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);
}

void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)
Expand Down
5 changes: 5 additions & 0 deletions src/slic3r/GUI/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,11 @@ void TabPrint::build()
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", category_path);
optgroup->append_single_option_line("make_overhang_printable_angle", category_path);
optgroup->append_single_option_line("make_overhang_printable_hole_size", category_path);

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

0 comments on commit 09a2eb8

Please sign in to comment.