mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 00:37:51 -06:00
Merge branch 'main' into dev/support-paint-vertical
This commit is contained in:
commit
a9a6f45f08
344 changed files with 29108 additions and 10950 deletions
|
@ -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>(®ion)), 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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue