Enhancement: Additional controls over bridges (#8263)
* Additional control over bridges * Label updates * Detect and handle layers over external bridges * Label updates * To-Do placeholders * Filter out small external bridges * Apply safety offset for internal bridge polygon intersections * code comments * Increase bridge offsets to 3 perimeters total (1.5 perimeter in each dimension) * Filter out bridges based on perimeter counts to focus bridge on areas where bridge infill is actually generated in the end. * Fixing bugs * Convert tick boxes to drop down menu * Additional geometry checks for second internal bridge to ensure no small polygons are left over. * Minor code refactor for clarity * Further refinements in polygon logic * Polygon logic refinements pt3 * Further union operations to ensure clean geometry * Fix compile error * Clean up constructors * Only create bridges on stInternalSolid areas, not sparse infill. * Refactor internal second bridge logic to stand alone parallel for loop to avoid thread deadlocks * Revert change to only consider stInternalSolid areas for second internal bridge layer. This resulted in partly unsupported solid infill areas above as the remainder was too narrow to generate sparse infill * Updated beta statements and tooltip changes --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
3bbee6cf5f
commit
b4a7721cc0
8 changed files with 339 additions and 22 deletions
|
@ -984,6 +984,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||
params.density = layerm->region().config().bridge_density.get_abs_value(1.0);
|
||||
params.dont_adjust = true;
|
||||
}
|
||||
if(surface_fill.surface.is_internal_bridge()){
|
||||
params.density = f->print_object_config->internal_bridge_density.get_abs_value(1.0);
|
||||
params.dont_adjust = true;
|
||||
}
|
||||
// BBS: make fill
|
||||
f->fill_surface_extrusion(&surface_fill.surface,
|
||||
params,
|
||||
|
|
|
@ -801,7 +801,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"independent_support_layer_height",
|
||||
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
|
||||
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
|
||||
"filename_format", "wall_filament", "support_bottom_z_distance",
|
||||
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
|
||||
"ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width",
|
||||
|
@ -824,7 +824,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
|
||||
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap",
|
||||
"role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "wipe_before_external_loop",
|
||||
"bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration",
|
||||
"bridge_density","internal_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_min_bead_width",
|
||||
|
|
|
@ -285,6 +285,14 @@ static t_config_enum_values s_keys_map_InternalBridgeFilter {
|
|||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InternalBridgeFilter)
|
||||
|
||||
static t_config_enum_values s_keys_map_EnableExtraBridgeLayer {
|
||||
{ "disabled", eblDisabled },
|
||||
{ "external_bridge_only", eblExternalBridgeOnly },
|
||||
{ "internal_bridge_only", eblInternalBridgeOnly },
|
||||
{ "apply_to_all", eblApplyToAll },
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(EnableExtraBridgeLayer)
|
||||
|
||||
// Orca
|
||||
static t_config_enum_values s_keys_map_GapFillTarget {
|
||||
{ "everywhere", gftEverywhere },
|
||||
|
@ -967,16 +975,32 @@ void PrintConfigDef::init_fff_params()
|
|||
def->category = L("Strength");
|
||||
def->tooltip = L("Internal bridging angle override. If left to zero, the bridging angle will be calculated "
|
||||
"automatically. Otherwise the provided angle will be used for internal bridges. "
|
||||
"Use 180°for zero angle.\n\n It is recommended to leave it at 0 unless there is a specific model need not to.");
|
||||
"Use 180°for zero angle.\n\nIt is recommended to leave it at 0 unless there is a specific model need not to.");
|
||||
def->sidetext = L("°");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
def = this->add("bridge_density", coPercent);
|
||||
def->label = L("Bridge density");
|
||||
def->label = L("External bridge density");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Density of external bridges. 100% means solid bridge. Default is 100%.");
|
||||
def->tooltip = L("Controls the density (spacing) of external bridge lines. 100% means solid bridge. Default is 100%.\n\n"
|
||||
"Lower density external bridges can help improve reliability as there is more space for air to circulate "
|
||||
"around the extruded bridge, improving its cooling speed.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 10;
|
||||
def->max = 100;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionPercent(100));
|
||||
|
||||
def = this->add("internal_bridge_density", coPercent);
|
||||
def->label = L("Internal bridge density");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Controls the density (spacing) of internal bridge lines. 100% means solid bridge. Default is 100%.\n\n "
|
||||
"Lower density internal bridges can help reduce top surface pillowing and improve internal bridge reliability as there is more space for "
|
||||
"air to circulate around the extruded bridge, improving its cooling speed. \n\n"
|
||||
"This option works particularly well when combined with the second internal bridge over infill option, "
|
||||
"further improving internal bridging structure before solid infill is extruded.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 10;
|
||||
def->max = 100;
|
||||
|
@ -1432,7 +1456,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("thick_bridges", coBool);
|
||||
def->label = L("Thick bridges");
|
||||
def->label = L("Thick external bridges");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. "
|
||||
"If disabled, bridges look better but are reliable just for shorter bridged distances.");
|
||||
|
@ -1446,23 +1470,53 @@ void PrintConfigDef::init_fff_params()
|
|||
"consider turning it off if you are using large nozzles.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("enable_extra_bridge_layer", coEnum);
|
||||
def->label = L("Extra bridge layers (beta)");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("This option enables the generation of an extra bridge layer over internal and/or external bridges.\n\n"
|
||||
"Extra bridge layers help improve bridge appearance and reliability, as the solid infill is better supported. "
|
||||
"This is especially useful in fast printers, where the bridge and solid infill speeds vary greatly. "
|
||||
"The extra bridge layer results in reduced pillowing on top surfaces, as well as reduced separation of the external bridge layer from its surrounding perimeters.\n\n"
|
||||
"It is generally recommended to set this to at least 'External bridge only', unless specific issues with the sliced model are found.\n\n"
|
||||
"Options:\n"
|
||||
"1. Disabled - does not generate second bridge layers. This is the default and is set for compatibility purposes.\n"
|
||||
"2. External bridge only - generates second bridge layers for external-facing bridges only. Please note that small bridges that are shorter "
|
||||
"or narrower than the set number of perimeters will be skipped as they would not benefit from a second bridge layer. If generated, the second bridge layer will be extruded "
|
||||
"parallel to the first bridge layer to reinforce the bridge strength.\n"
|
||||
"3. Internal bridge only - generates second bridge layers for internal bridges over sparse infill only. Please note that the internal "
|
||||
"bridges count towards the top shell layer count of your model. The second internal bridge layer will be extruded as close to perpendicular to the first as possible. If multiple regions "
|
||||
"in the same island, with varying bridge angles are present, the last region of that island will be selected as the angle reference.\n"
|
||||
"4. Apply to all - generates second bridge layers for both internal and external-facing bridges\n");
|
||||
|
||||
def->enum_keys_map = &ConfigOptionEnum<EnableExtraBridgeLayer>::get_enum_values();
|
||||
def->enum_values.push_back("disabled");
|
||||
def->enum_values.push_back("external_bridge_only");
|
||||
def->enum_values.push_back("internal_bridge_only");
|
||||
def->enum_values.push_back("apply_to_all");
|
||||
def->enum_labels.push_back(L("Disabled"));
|
||||
def->enum_labels.push_back(L("External bridge only"));
|
||||
def->enum_labels.push_back(L("Internal bridge only"));
|
||||
def->enum_labels.push_back(L("Apply to all"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<EnableExtraBridgeLayer>(eblDisabled));
|
||||
|
||||
def = this->add("dont_filter_internal_bridges", coEnum);
|
||||
def->label = L("Filter out small internal bridges (beta)");
|
||||
def->label = L("Filter out small internal bridges");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("This option can help reducing pillowing on top surfaces in heavily slanted or curved models.\n\n"
|
||||
"By default, small internal bridges are filtered out and the internal solid infill is printed directly"
|
||||
" over the sparse infill. This works well in most cases, speeding up printing without too much compromise"
|
||||
" on top surface quality. \n\nHowever, in heavily slanted or curved models especially where too low sparse"
|
||||
" infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
|
||||
"Disabling this option will print internal bridge layer over slightly unsupported internal"
|
||||
" solid infill. The options below control the amount of filtering, i.e. the amount of internal bridges "
|
||||
def->tooltip = L("This option can help reduce pillowing on top surfaces in heavily slanted or curved models.\n\n"
|
||||
"By default, small internal bridges are filtered out and the internal solid infill is printed directly "
|
||||
"over the sparse infill. This works well in most cases, speeding up printing without too much compromise "
|
||||
"on top surface quality. \n\nHowever, in heavily slanted or curved models, especially where too low a sparse "
|
||||
"infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
|
||||
"Enabling limited filtering or no filtering will print internal bridge layer over slightly unsupported internal "
|
||||
"solid infill. The options below control the sensitivity of the filtering, i.e. they control where internal bridges are "
|
||||
"created.\n\n"
|
||||
"Filter - enable this option. This is the default behavior and works well in most cases.\n\n"
|
||||
"Limited filtering - creates internal bridges on heavily slanted surfaces, while avoiding creating "
|
||||
"unnecessary internal bridges. This works well for most difficult models.\n\n"
|
||||
"No filtering - creates internal bridges on every potential internal overhang. This option is useful "
|
||||
"for heavily slanted top surface models. However, in most cases it creates too many unnecessary bridges.");
|
||||
"1. Filter - enables this option. This is the default behavior and works well in most cases.\n\n"
|
||||
"2. Limited filtering - creates internal bridges on heavily slanted surfaces while avoiding unnecessary bridges. "
|
||||
"This works well for most difficult models.\n\n"
|
||||
"3. No filtering - creates internal bridges on every potential internal overhang. This option is useful for "
|
||||
"heavily slanted top surface models; however, in most cases, it creates too many unnecessary bridges.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<InternalBridgeFilter>::get_enum_values();
|
||||
def->enum_values.push_back("disabled");
|
||||
def->enum_values.push_back("limited");
|
||||
|
|
|
@ -187,6 +187,11 @@ enum InternalBridgeFilter {
|
|||
ibfDisabled, ibfLimited, ibfNofilter
|
||||
};
|
||||
|
||||
//Orca
|
||||
enum EnableExtraBridgeLayer {
|
||||
eblDisabled, eblExternalBridgeOnly, eblInternalBridgeOnly, eblApplyToAll
|
||||
};
|
||||
|
||||
//Orca
|
||||
enum GapFillTarget {
|
||||
gftEverywhere, gftTopBottom, gftNowhere
|
||||
|
@ -827,6 +832,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, thick_bridges))
|
||||
((ConfigOptionBool, thick_internal_bridges))
|
||||
((ConfigOptionEnum<InternalBridgeFilter>, dont_filter_internal_bridges))
|
||||
// Orca
|
||||
((ConfigOptionEnum<EnableExtraBridgeLayer>, enable_extra_bridge_layer))
|
||||
((ConfigOptionPercent, internal_bridge_density))
|
||||
// Overhang angle threshold.
|
||||
((ConfigOptionInt, support_threshold_angle))
|
||||
((ConfigOptionFloatOrPercent, support_threshold_overlap))
|
||||
|
|
|
@ -1081,7 +1081,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "bridge_angle"
|
||||
|| opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
|
||||
//BBS
|
||||
|| opt_key == "bridge_density") {
|
||||
|| opt_key == "bridge_density"
|
||||
|| opt_key == "internal_bridge_density") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
} else if (
|
||||
opt_key == "top_surface_pattern"
|
||||
|
@ -1447,7 +1448,111 @@ void PrintObject::detect_surfaces_type()
|
|||
for (size_t i = num_layers; i < m_layers.size(); ++ i)
|
||||
m_layers[i]->m_regions[region_id]->slices.set_type(stInternal);
|
||||
}
|
||||
|
||||
|
||||
// ==================================================================================================
|
||||
// === ORCA: Create a SECOND bridge layer above the first bridge layer. =============================
|
||||
// === ORCA: Surface is flagged as a new surface type called stInternalAfterExternalBridge ==================
|
||||
// === Algorithm only considers stInternal surfaces for re-classification, leaving stTop unaffected =
|
||||
// ==================================================================================================
|
||||
// Only iterate to the second-to-last layer, since we look at layer i+1.
|
||||
if( (this->config().enable_extra_bridge_layer.value == eblApplyToAll) || (this->config().enable_extra_bridge_layer.value == eblExternalBridgeOnly)){
|
||||
const size_t last = (m_layers.empty() ? 0 : m_layers.size() - 1);
|
||||
tbb::parallel_for( tbb::blocked_range<size_t>(0, last), [this, region_id](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||
m_print->throw_if_canceled();
|
||||
|
||||
// Step 1: Find bridge polygons
|
||||
// Current layer (i): Search for stBottomBridge polygons.
|
||||
const Surfaces &bot_surfs = m_layers[i]->m_regions[region_id]->slices.surfaces;
|
||||
// Next layer (i+1): The layer where stInternal polygons may be re-classified.
|
||||
Surfaces &top_surfs = m_layers[i + 1]->m_regions[region_id]->slices.surfaces;
|
||||
|
||||
// Step 2: Collect the bridge polygons in the current layer region
|
||||
Polygons polygons_bridge;
|
||||
for (const Surface &sbot : bot_surfs) {
|
||||
if (sbot.surface_type == stBottomBridge) {
|
||||
polygons_append(polygons_bridge, to_polygons(sbot));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Early termination of loop if no meaningfull bridge found
|
||||
// No bridge polygons found, continue to the next layer
|
||||
if (polygons_bridge.empty())
|
||||
continue;
|
||||
|
||||
// Step 4: Bottom bridge polygons found - scan and create layer+1 bridge polygon
|
||||
Surfaces new_surfaces;
|
||||
new_surfaces.reserve(top_surfs.size());
|
||||
|
||||
//filtering parameters here. Filter bridges that are less than 2x external walls and 2xN internal perimeters wide.
|
||||
LayerRegion *layerm = m_layers[i]->m_regions[region_id];
|
||||
int number_of_internal_walls = std::max(0, layerm->m_region->config().wall_loops - 1); // number of internal walls, clamped to a minimum of 0 as a safety precaution
|
||||
float offset_distance = layerm->flow(frExternalPerimeter).scaled_width() // shrink down by external perimeter width (effectively filtering out 2x external perimeters wide bridges)
|
||||
+ ((layerm->flow(frPerimeter).scaled_width()) * number_of_internal_walls); // shrink down by number of external walls * width of them, effectively filtering out 2x internal perimeter wide bridges
|
||||
// The reason for doing the above filtering is that in pure bridges, the walls are always printed separately as overhang walls. Here we care about the bridge infill which is distinct and is the remainder
|
||||
// of the bridge area minus the perimeter width on both sides of the bridge itself.
|
||||
// This would also skip generation of very short dual bridge layers (that are shorter than N perimeters), but these are unecessary as the bridge distance is
|
||||
// We could reduce this slightly to account for innacurcies in the clipping operation.
|
||||
// TODO: Monitor GitHub issues to check whether second bridge layers are ommited where they should be generated. If yes, reduce the filtering distance
|
||||
|
||||
// For each surface in the layer above
|
||||
for (Surface &s_up : top_surfs) {
|
||||
// Only reclassify stInternal polygons (i.e. what will become later solid and sparse infill)
|
||||
// Leave the rest unaffected
|
||||
if (s_up.surface_type != stInternal) {
|
||||
new_surfaces.push_back(std::move(s_up)); // do not modify them
|
||||
continue; // continue to the next surface
|
||||
}
|
||||
// Identify stInternal polygons that overlap with the bridging polygons on the layer underneath.
|
||||
Polygons p_up = to_polygons(s_up);
|
||||
ExPolygons overlap = intersection_ex(p_up, polygons_bridge , ApplySafetyOffset::Yes);
|
||||
// Filter out the resulting candidate bridges based on size. First perform a shrink operation...
|
||||
// ...followed by an expand operation to bring them back to the original size (positive offset)
|
||||
overlap = offset_ex(shrink_ex(overlap, offset_distance), offset_distance);
|
||||
|
||||
// Now subtract the filtered new bridge layer from the remaining internal surfaces to create the new internal surface
|
||||
ExPolygons remainder = diff_ex(p_up, overlap, ApplySafetyOffset::Yes);
|
||||
|
||||
// Remainder stays as stInternal
|
||||
ExPolygons unified_remainder = union_safety_offset_ex(remainder);
|
||||
for (auto &ex_remainder : unified_remainder) {
|
||||
Surface s(stInternal, ex_remainder);
|
||||
new_surfaces.push_back(std::move(s));
|
||||
}
|
||||
// Overlap portion becomes the new polygon type - stInternalAfterExternalBridge
|
||||
ExPolygons unified_overlap = union_safety_offset_ex(overlap);
|
||||
for (auto &ex_overlap : unified_overlap) {
|
||||
Surface s(stInternalAfterExternalBridge, ex_overlap);
|
||||
new_surfaces.push_back(std::move(s));
|
||||
}
|
||||
}
|
||||
top_surfs = std::move(new_surfaces);
|
||||
}
|
||||
}
|
||||
);
|
||||
// ==============================================================================================================
|
||||
// === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ==============
|
||||
// === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ==========
|
||||
// === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ==========
|
||||
// ==============================================================================================================
|
||||
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
|
||||
tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), [this, region_id](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
|
||||
Surfaces &surfs = m_layers[idx_layer]->m_regions[region_id]->slices.surfaces;
|
||||
for (Surface &s : surfs) {
|
||||
if (s.surface_type == stInternalAfterExternalBridge) {
|
||||
s.surface_type = stBottomBridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
// ==============================================================================================================
|
||||
// === ORCA: End of second external bridge layer changes =======================================================
|
||||
// ==============================================================================================================
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start";
|
||||
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
||||
tbb::parallel_for(
|
||||
|
@ -2858,7 +2963,7 @@ void PrintObject::bridge_over_infill()
|
|||
for (const ExPolygon &ep : new_internal_solids) {
|
||||
new_surfaces.emplace_back(stInternalSolid, ep);
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw("Aensuring_" + std::to_string(reinterpret_cast<uint64_t>(®ion)), to_polylines(additional_ensuring),
|
||||
to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)),
|
||||
|
@ -2873,6 +2978,144 @@ void PrintObject::bridge_over_infill()
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ======================================================================================================================================
|
||||
// === ORCA: Create a second internal bridge layer above the first bridge layer. ========================================================
|
||||
// ======================================================================================================================================
|
||||
if ( this->m_config.enable_extra_bridge_layer == eblApplyToAll || this->m_config.enable_extra_bridge_layer == eblInternalBridgeOnly) {
|
||||
// Process layers in parallel up to second-to-last
|
||||
tbb::parallel_for( tbb::blocked_range<size_t>(0, this->layers().size() - 1), [this](const tbb::blocked_range<size_t>& r) {
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); ++lidx)
|
||||
{
|
||||
Layer* layer = this->get_layer(lidx);
|
||||
|
||||
// (A) Gather internal bridging surfaces in the current layer
|
||||
ExPolygons bridging_current_layer;
|
||||
double bridging_angle_current = 0.0;
|
||||
|
||||
bool found_any_bridge = false;
|
||||
float offset_distance = 0.0f;
|
||||
|
||||
// Pick a region from which to retrieve the flow width
|
||||
if (!layer->regions().empty())
|
||||
offset_distance = layer->regions().front()->flow(frSolidInfill).scaled_width();
|
||||
|
||||
for (LayerRegion *region : layer->regions()) {
|
||||
for (const Surface &surf : region->fill_surfaces.surfaces) {
|
||||
if (surf.surface_type == stInternalBridge) {
|
||||
bridging_current_layer.push_back(surf.expolygon);
|
||||
bridging_angle_current = surf.bridge_angle; // Store the last bridging angle of the current print object
|
||||
found_any_bridge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no bridging in this layer, continue with the next
|
||||
if (!found_any_bridge || bridging_current_layer.empty())
|
||||
continue;
|
||||
|
||||
// (B) Shrink-expand to remove trivial bridging areas
|
||||
bridging_current_layer = offset_ex( shrink_ex(bridging_current_layer, offset_distance), offset_distance );
|
||||
|
||||
if (bridging_current_layer.empty())
|
||||
continue; // all bridging was trivial, continue with the next layer
|
||||
|
||||
// (C) If there is a next layer, identify overlapping stInternal & stInternalSolid areas and convert the overlap to stSecondInternalBridge
|
||||
if (lidx + 1 < this->layers().size()) {
|
||||
Layer* next_layer = this->get_layer(lidx + 1);
|
||||
|
||||
// second bridging angle is 90 degrees offset
|
||||
double bridging_angle_second = bridging_angle_current + M_PI / 2.0;
|
||||
|
||||
// Union the bridging polygons
|
||||
ExPolygons bridging_union = union_safety_offset_ex(bridging_current_layer);
|
||||
|
||||
for (LayerRegion *next_region : next_layer->regions()) {
|
||||
Surfaces next_new_surfaces;
|
||||
Surfaces keep_surfaces;
|
||||
|
||||
// 1) Do not modify (keep) anything that isn't stInternal or stInternalSolid
|
||||
for (const Surface &s : next_region->fill_surfaces.surfaces) {
|
||||
if ( (s.surface_type != stInternal) && (s.surface_type != stInternalSolid)) {
|
||||
keep_surfaces.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) For stInternal & stInternalSolid surfaces, check if they overlap bridging_union
|
||||
// 2a) Gather the next internal stInternalSolid surfaces first
|
||||
SurfacesPtr next_internals = next_region->fill_surfaces.filter_by_types({ stInternal, stInternalSolid });
|
||||
|
||||
// 2b) For every collected next stInternalSolid surface
|
||||
for (const Surface *s : next_internals) {
|
||||
// Intersect it with the current layer bridging polygons
|
||||
ExPolygons overlap = intersection_ex( s->expolygon, bridging_union, ApplySafetyOffset::Yes );
|
||||
|
||||
// Shrink + expand to remove trivial polygons
|
||||
overlap = offset_ex(shrink_ex(overlap, offset_distance), offset_distance);
|
||||
|
||||
// Overlapping portion found -> this will become the second internal bridge
|
||||
if (!overlap.empty()) {
|
||||
// Create second bridge surface
|
||||
Surface tmp{*s, {}};
|
||||
tmp.surface_type = stSecondInternalBridge;
|
||||
tmp.bridge_angle = bridging_angle_second;
|
||||
|
||||
// Insert bridging polygons
|
||||
for (const ExPolygon &ep : overlap) {
|
||||
next_new_surfaces.emplace_back(tmp, ep);
|
||||
}
|
||||
|
||||
// Calculate leftover polygons = s->expolygon - bridging_union
|
||||
ExPolygons leftover = diff_ex(s->expolygon, bridging_union, ApplySafetyOffset::Yes);
|
||||
// Shrink + expand to remove trivial polygons
|
||||
leftover = offset_ex(shrink_ex(leftover, offset_distance), offset_distance);
|
||||
|
||||
// Leftover polygons exist. Add them to the new surface maintaining their original attributes
|
||||
if (!leftover.empty()) {
|
||||
ExPolygons unified_leftover = union_safety_offset_ex(leftover);
|
||||
for (const ExPolygon &ep : unified_leftover) {
|
||||
// keep same type / angle as original
|
||||
Surface leftover_surf{*s, {}};
|
||||
leftover_surf.surface_type = s->surface_type;
|
||||
leftover_surf.bridge_angle = s->bridge_angle;
|
||||
next_new_surfaces.emplace_back(leftover_surf, ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // No overlapping portion found
|
||||
// keep the surface intact
|
||||
keep_surfaces.push_back(*s);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Rebuild next_region surfaces
|
||||
next_region->fill_surfaces.surfaces.clear();
|
||||
next_region->fill_surfaces.append(keep_surfaces);
|
||||
next_region->fill_surfaces.append(next_new_surfaces);
|
||||
} // end for next_layer->regions
|
||||
} // end if next layer
|
||||
}
|
||||
}); // end parallel_for
|
||||
|
||||
// =================================================================================================================
|
||||
// === ORCA: Interim workaround - for now the new stSecondInternalBridge surfaces are re-classified ===============
|
||||
// === back to an internal bridge. As a starting point, this improves bridging reliability as it extrudes ==========
|
||||
// === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase =============
|
||||
// =================================================================================================================
|
||||
for (size_t lidx = 0; lidx < this->layers().size(); ++lidx) {
|
||||
Layer* layer = this->get_layer(lidx);
|
||||
for (LayerRegion* region : layer->regions()) {
|
||||
for (Surface &surf : region->fill_surfaces.surfaces) {
|
||||
if (surf.surface_type == stSecondInternalBridge) {
|
||||
surf.surface_type = stInternalBridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ===========================================================================================
|
||||
// === ORCA: End of second bridging pass =====================================================
|
||||
// ===========================================================================================
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info();
|
||||
|
||||
|
|
|
@ -13,12 +13,16 @@ enum SurfaceType {
|
|||
stBottom,
|
||||
// Bottom horizontal surface, visible from the bottom, unsupported, printed with a bridging extrusion flow.
|
||||
stBottomBridge,
|
||||
// Second bridge surface above a bottom bridge.
|
||||
stInternalAfterExternalBridge,
|
||||
// Normal sparse infill.
|
||||
stInternal,
|
||||
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
|
||||
stInternalSolid,
|
||||
// 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow.
|
||||
stInternalBridge,
|
||||
// 2nd layer of dense infill over sparse infill, printed with a bridging extrusion flow.
|
||||
stSecondInternalBridge,
|
||||
// stInternal turns into void surfaces if the sparse infill is used for supports only,
|
||||
// or if sparse infill layers get combined into a single layer.
|
||||
stInternalVoid,
|
||||
|
|
|
@ -9737,6 +9737,8 @@ auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config
|
|||
_obj->config.set_key_value("wall_loops", new ConfigOptionInt(1));
|
||||
_obj->config.set_key_value("only_one_wall_top", new ConfigOptionBool(true));
|
||||
_obj->config.set_key_value("thick_internal_bridges", new ConfigOptionBool(false));
|
||||
_obj->config.set_key_value("enable_extra_bridge_layer", new ConfigOptionEnum<EnableExtraBridgeLayer>(eblDisabled));
|
||||
_obj->config.set_key_value("internal_bridge_density", new ConfigOptionPercent(100));
|
||||
_obj->config.set_key_value("sparse_infill_density", new ConfigOptionPercent(35));
|
||||
_obj->config.set_key_value("min_width_top_surface", new ConfigOptionFloatOrPercent(100,true));
|
||||
_obj->config.set_key_value("bottom_shell_layers", new ConfigOptionInt(2));
|
||||
|
|
|
@ -2105,8 +2105,10 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("bridge_flow");
|
||||
optgroup->append_single_option_line("internal_bridge_flow");
|
||||
optgroup->append_single_option_line("bridge_density");
|
||||
optgroup->append_single_option_line("internal_bridge_density");
|
||||
optgroup->append_single_option_line("thick_bridges");
|
||||
optgroup->append_single_option_line("thick_internal_bridges");
|
||||
optgroup->append_single_option_line("enable_extra_bridge_layer");
|
||||
optgroup->append_single_option_line("dont_filter_internal_bridges");
|
||||
optgroup->append_single_option_line("counterbore_hole_bridging","counterbore-hole-bridging");
|
||||
|
||||
|
|
Loading…
Reference in a new issue