Merge branch 'main' into dev/support-paint-vertical

This commit is contained in:
Noisyfox 2025-02-23 15:50:21 +08:00 committed by GitHub
commit a9a6f45f08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
344 changed files with 29108 additions and 10950 deletions

View file

@ -1077,8 +1077,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "rotate_solid_infill_direction"
|| opt_key == "ensure_vertical_shell_thickness"
|| 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"
@ -1089,7 +1091,9 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_anchor_max"
|| opt_key == "top_surface_line_width"
|| opt_key == "initial_layer_line_width"
|| opt_key == "small_area_infill_flow_compensation") {
|| opt_key == "small_area_infill_flow_compensation"
|| opt_key == "lattice_angle_1"
|| opt_key == "lattice_angle_2") {
steps.emplace_back(posInfill);
} else if (opt_key == "sparse_infill_pattern") {
steps.emplace_back(posPrepareInfill);
@ -1442,7 +1446,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(
@ -2741,6 +2849,10 @@ void PrintObject::bridge_over_infill()
// Also, use Infill pattern that is neutral for angle determination, since there are no infill lines.
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine, 0);
}
// ORCA: Internal bridge angle override
if (candidate.region->region().config().internal_bridge_angle > 0)
bridging_angle = candidate.region->region().config().internal_bridge_angle.value * PI / 180.0; // Convert degrees to radians
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
@ -2849,7 +2961,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>(&region)), to_polylines(additional_ensuring),
to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)),
@ -2864,6 +2976,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();
@ -3497,6 +3747,7 @@ void PrintObject::combine_infill()
((infill_pattern == ipRectilinear ||
infill_pattern == ipMonotonic ||
infill_pattern == ipGrid ||
infill_pattern == ip2DLattice ||
infill_pattern == ipLine ||
infill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
layerms.back()->flow(frSolidInfill).scaled_width();