mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Make Overhang Printable (#1615)
* First working conical overhang impl * Add settings for Make overhang printable * Added an Advanced setting to the Quality menu for Make overhang printable * Allow toggling the feature and adjusting the angle threshold * Retain recesses in the bottom of the model when "Make Overhangs Printable" is turned on * Add settings for Make overhang printable hole size * Allow disabling conical overhang per modifier * Should check upper layer instead * Skip unnecessary layers * Apply conical overhang before any size compensation which fixs the `lslices` not being updated issue and also the elephant foot compensation * 1. Fix an issue that changing make_overhang_printable related parameters won't clear caches properly. 2. Tweak the logic of make_overhang_printable parameter so that it works for per region/object/print level. Remove make_overhang_printable_disable parameter. * Change default make_overhang_printable_angle to 55 * One more missing commit * 1. Skip checking default_region_config()->make_overhang_printable 2. Handle make_overhang_printable_angle and make_overhang_printable_hole_size value change --------- Co-authored-by: David Bern <odie5533@users.noreply.github.com> Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
		
							parent
							
								
									ab6649da8a
								
							
						
					
					
						commit
						b571318be8
					
				
					 11 changed files with 164 additions and 5 deletions
				
			
		|  | @ -787,7 +787,11 @@ Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons& poly1, const Slic3r::ExPol | |||
|     for (const ExPolygon& expoly : poly2) | ||||
|         expolys.push_back(expoly); | ||||
|     return union_ex(expolys); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
|  |  | |||
|  | @ -529,6 +529,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons& poly1, const Slic3r::ExPol | |||
| ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject); | ||||
| ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject); | ||||
| 
 | ||||
| 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); | ||||
|  |  | |||
|  | @ -756,7 +756,8 @@ static std::vector<std::string> s_Preset_print_options { | |||
|      "bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration", | ||||
|      "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim",  | ||||
|      "tree_support_brim_width", "gcode_comments", "gcode_label_objects", | ||||
|      "initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max" | ||||
|      "initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max", | ||||
|      "make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size"  | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -521,6 +521,7 @@ private: | |||
|     // 
 | ||||
|     // object id
 | ||||
|     size_t               m_id; | ||||
|     void apply_conical_overhang(); | ||||
| 
 | ||||
|  public: | ||||
|     //BBS: When printing multi-material objects, this settings will make slicer to clip the overlapping object parts one by the other.
 | ||||
|  |  | |||
|  | @ -2371,6 +2371,35 @@ void PrintConfigDef::init_fff_params() | |||
|     def->mode = comAdvanced; | ||||
|     def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[0]}_{print_time}.gcode")); | ||||
| 
 | ||||
|     def = this->add("make_overhang_printable", coBool); | ||||
|     def->label = L("Make overhang 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 overhang 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 = 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("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 = L("mm²"); | ||||
|     def->mode = comAdvanced; | ||||
|     def->min = 0.; | ||||
|     def->set_default_value(new ConfigOptionFloat(0.)); | ||||
| 
 | ||||
|     def = this->add("detect_overhang_wall", coBool); | ||||
|     def->label = L("Detect overhang wall"); | ||||
|     def->category = L("Quality"); | ||||
|  |  | |||
|  | @ -713,6 +713,10 @@ PRINT_CONFIG_CLASS_DEFINE( | |||
|     ((ConfigOptionInt,                wall_distribution_count)) | ||||
|     ((ConfigOptionPercent,            min_feature_size)) | ||||
|     ((ConfigOptionPercent,            min_bead_width)) | ||||
| 
 | ||||
|     // Orca
 | ||||
|     ((ConfigOptionFloat,              make_overhang_printable_angle)) | ||||
|     ((ConfigOptionFloat,              make_overhang_printable_hole_size)) | ||||
| ) | ||||
| 
 | ||||
| // This object is mapped to Perl as Slic3r::Config::PrintRegion.
 | ||||
|  | @ -791,6 +795,8 @@ PRINT_CONFIG_CLASS_DEFINE( | |||
|     ((ConfigOptionFloatOrPercent,       infill_anchor)) | ||||
|     ((ConfigOptionFloatOrPercent,       infill_anchor_max)) | ||||
| 
 | ||||
|     // Orca
 | ||||
|     ((ConfigOptionBool,               make_overhang_printable)) | ||||
| ) | ||||
| 
 | ||||
| PRINT_CONFIG_CLASS_DEFINE( | ||||
|  |  | |||
|  | @ -731,7 +731,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" | ||||
|  |  | |||
|  | @ -777,6 +777,7 @@ void PrintObject::slice() | |||
|     m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile)); | ||||
|     this->slice_volumes(); | ||||
|     m_print->throw_if_canceled(); | ||||
| 
 | ||||
|     int firstLayerReplacedBy = 0; | ||||
| 
 | ||||
| #if 1 | ||||
|  | @ -1028,6 +1029,8 @@ void PrintObject::slice_volumes() | |||
|         apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); }); | ||||
|     } | ||||
| 
 | ||||
|     this->apply_conical_overhang(); | ||||
|     m_print->throw_if_canceled(); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin"; | ||||
|     { | ||||
|  | @ -1166,6 +1169,107 @@ 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); | ||||
| 
 | ||||
|         // 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]->slices.set(union_ex(layer_polygons, p), stInternal); | ||||
|         } | ||||
|         //layer->export_region_slices_to_svg_debug("layer_after_conical_overhang");
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| //BBS: this function is used to offset contour and holes of expolygons seperately by different value
 | ||||
| ExPolygons PrintObject::_shrink_contour_holes(double contour_delta, double hole_delta, const ExPolygons& polys) const | ||||
| { | ||||
|  |  | |||
|  | @ -702,6 +702,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co | |||
|     if(gcflavor == gcfKlipper) | ||||
|         toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable")); | ||||
| 
 | ||||
|     // SoftFever
 | ||||
|     bool have_make_overhang_printable = config->opt_bool("make_overhang_printable"); | ||||
|     toggle_line("make_overhang_printable_angle", have_make_overhang_printable); | ||||
|     toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable); | ||||
| 
 | ||||
|     toggle_line("exclude_object", gcflavor == gcfKlipper); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -75,7 +75,8 @@ std::map<std::string, std::vector<SimpleSettingData>>  SettingsFactory::OBJECT_C | |||
|                     //{"initial_layer_print_height", "",2},
 | ||||
|                     {"seam_position", "",2}, | ||||
|                     {"slice_closing_radius", "",3}, {"resolution", "",4}, | ||||
|                     {"xy_hole_compensation", "",5}, {"xy_contour_compensation", "",6}, {"elefant_foot_compensation", "",7} | ||||
|                     {"xy_hole_compensation", "",5}, {"xy_contour_compensation", "",6}, {"elefant_foot_compensation", "",7}, | ||||
|                     {"make_overhang_printable_angle","", 8},{"make_overhang_printable_hole_size","",9} | ||||
|                     }}, | ||||
|     { L("Support"), {{"brim_type", "",1},{"brim_width", "",2},{"brim_object_gap", "",3}, | ||||
|                     {"enable_support", "",4},{"support_type", "",5},{"support_threshold_angle", "",6},{"support_on_build_plate_only", "",7}, | ||||
|  | @ -91,7 +92,7 @@ std::map<std::string, std::vector<SimpleSettingData>>  SettingsFactory::OBJECT_C | |||
| 
 | ||||
| std::map<std::string, std::vector<SimpleSettingData>>  SettingsFactory::PART_CATEGORY_SETTINGS= | ||||
| { | ||||
|     { L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"bridge_flow", "",11},{"bridge_density", "", 1} | ||||
|     { L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1} | ||||
|                     }}, | ||||
|     { L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1}, | ||||
|                     {"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1}, | ||||
|  |  | |||
|  | @ -1888,6 +1888,9 @@ void TabPrint::build() | |||
|         optgroup->append_single_option_line("only_one_wall_top"); | ||||
|         optgroup->append_single_option_line("only_one_wall_first_layer"); | ||||
|         optgroup->append_single_option_line("detect_overhang_wall"); | ||||
|         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->append_single_option_line("reduce_crossing_wall"); | ||||
|         optgroup->append_single_option_line("max_travel_detour_distance"); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Noisyfox
						Noisyfox