mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 01:01:57 +00:00
Merge upstream/main into belt/rebase/may-18
Reconciles the belt-printer branch with upstream PRs through #13723. Six files had conflicts; three additional files needed manual follow-up fixes where the auto-merge produced code that referenced upstream-renamed fields or changed function signatures. Notable reconciliations: - TreeSupport.cpp: kept belt-floor early-exit branches around HEAD's drop-down logic, folded upstream's `(distance_to_top > 0 ? 1 : 0)` formula into the non-belt-floor path (upstream PR #11812). Dropped dead `roof_enabled`/`force_tip_to_roof` locals. - TreeSupport3D.cpp: combined upstream's safety-offset + remove_small changes with HEAD's belt-floor clip in the per-slice trim loop. Dropped HEAD's `else` block (superseded by upstream's rewritten bottom-contact propagation) and re-added the belt-floor clip into the new propagation loop. Gated the propagation on belt printers to prevent OOM when belt-floor clipping produces empty initial slices. - TriangleSelector.{cpp,hpp}: merged both new `select_patch` parameters (HEAD's `up_direction` and upstream's `select_partially`); body uses `dot(up_direction)` for the overhang angle check and forwards `select_partially` to `select_triangle`. - SupportMaterial.cpp: `slicing_params.soluble_interface` → `zero_gap_interface_bottom` in HEAD's `detect_belt_floor_bottom_contacts`, matching upstream's same-purpose rename at line 2495. - Custom.json, GCodeWriter.cpp: simple additive merges (kept entries / includes from both sides). Verified by building OrcaSlicer (RelWithDebInfo) after a full deps rebuild (Eigen v5.0.1, libigl v2.6.0 are now managed deps) and slicing a scaled Benchy on the NORMALIZER belt-printer profile without OOM. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,6 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
@@ -55,7 +54,6 @@
|
||||
#define _L(s) Slic3r::I18N::translate(s)
|
||||
#endif
|
||||
|
||||
//#define TREESUPPORT_DEBUG_SVG
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@@ -133,7 +131,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
const PrintObjectConfig &object_config = print_object.config();
|
||||
if (object_config.support_top_z_distance < EPSILON)
|
||||
// || min_feature_size < scaled<coord_t>(0.1) that is the minimum line width
|
||||
TreeSupportSettings::soluble = true;
|
||||
TreeSupportSettings::zero_top_z_gap = true;
|
||||
}
|
||||
|
||||
size_t largest_printed_mesh_idx = 0;
|
||||
@@ -296,16 +294,6 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
//FIXME enforcer_overhang_offset is a fudge constant!
|
||||
enforced_overhangs = diff(offset(union_ex(enforced_overhangs), enforcer_overhang_offset),
|
||||
lower_layer.lslices);
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
// if (! intersecting_edges(enforced_overhangs).empty())
|
||||
{
|
||||
static int irun = 0;
|
||||
SVG::export_expolygons(debug_out_path("treesupport-self-intersections-%d.svg", ++irun),
|
||||
{ { { current_layer.lslices }, { "current_layer.lslices", "yellow", 0.5f } },
|
||||
{ { lower_layer.lslices }, { "lower_layer.lslices", "gray", 0.5f } },
|
||||
{ { union_ex(enforced_overhangs) }, { "enforced_overhangs", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
}
|
||||
#endif // TREESUPPORT_DEBUG_SVG
|
||||
//check_self_intersections(enforced_overhangs, "generate_overhangs - enforced overhangs2");
|
||||
overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
|
||||
//check_self_intersections(overhangs, "generate_overhangs - enforcers");
|
||||
@@ -731,7 +719,8 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
(support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) :
|
||||
support_params.base_angle;
|
||||
|
||||
fill_params.density = float(roof ? support_params.interface_density : scaled<float>(filler->spacing) / (scaled<float>(filler->spacing) + float(support_infill_distance)));
|
||||
// ORCA: use top-specific interface density after separating top/bottom settings.
|
||||
fill_params.density = float(roof ? support_params.top_interface_density : scaled<float>(filler->spacing) / (scaled<float>(filler->spacing) + float(support_infill_distance)));
|
||||
fill_params.dont_adjust = true;
|
||||
|
||||
Polylines out;
|
||||
@@ -1304,7 +1293,7 @@ static void generate_initial_areas(
|
||||
;
|
||||
const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
|
||||
const bool roof_enabled = num_support_roof_layers > 0;
|
||||
const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area);
|
||||
const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.zero_gap_interface_top || sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area);
|
||||
// cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point
|
||||
// may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang
|
||||
// does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it.
|
||||
@@ -1819,11 +1808,6 @@ static void increase_areas_one_layer(
|
||||
// Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall.
|
||||
volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist);
|
||||
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-%d-%ld.svg", layer_idx, int(merging_area_idx)),
|
||||
{ { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } },
|
||||
{ { union_ex(parent.influence_area) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
#endif // TREESUPPORT_DEBUG_SVG
|
||||
|
||||
Polygons to_bp_data, to_model_data;
|
||||
coord_t radius = support_element_collision_radius(config, elem);
|
||||
@@ -1954,11 +1938,6 @@ static void increase_areas_one_layer(
|
||||
// was never made for precision in the single digit micron range.
|
||||
offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow,
|
||||
wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2);
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-slow-%d-%ld.svg", layer_idx, int(merging_area_idx)),
|
||||
{ { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } },
|
||||
{ { union_ex(offset_slow) }, { "offset_slow", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
#endif // TREESUPPORT_DEBUG_SVG
|
||||
}
|
||||
if (offset_fast.empty() && settings.increase_speed != slow_speed) {
|
||||
if (offset_independant_faster)
|
||||
@@ -1968,11 +1947,6 @@ static void increase_areas_one_layer(
|
||||
const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed);
|
||||
offset_fast = safe_offset_inc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independant_faster ? 2 : 1);
|
||||
}
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-fast-%d-%ld.svg", layer_idx, int(merging_area_idx)),
|
||||
{ { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } },
|
||||
{ { union_ex(offset_fast) }, { "offset_fast", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
#endif // TREESUPPORT_DEBUG_SVG
|
||||
}
|
||||
}
|
||||
std::optional<SupportElementState> result;
|
||||
@@ -3530,18 +3504,6 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
move_bounds, interface_placer, throw_on_cancel);
|
||||
auto t_gen = std::chrono::high_resolution_clock::now();
|
||||
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++layer_idx) {
|
||||
Polygons polys;
|
||||
for (auto& area : move_bounds[layer_idx])
|
||||
append(polys, area.influence_area);
|
||||
if (auto begin = move_bounds[layer_idx].begin(); begin != move_bounds[layer_idx].end())
|
||||
SVG::export_expolygons(debug_out_path("treesupport-initial_areas-%d.svg", layer_idx),
|
||||
{ { { union_ex(volumes.getWallRestriction(support_element_collision_radius(config, begin->state), layer_idx, begin->state.use_min_xy_dist)) },
|
||||
{ "wall_restricrictions", "gray", 0.5f } },
|
||||
{ { union_ex(polys) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
}
|
||||
#endif // TREESUPPORT_DEBUG_SVG
|
||||
|
||||
// ### Propagate the influence areas downwards. This is an inherently serial operation.
|
||||
print.set_status(60, _L("Generating support"));
|
||||
@@ -3894,95 +3856,191 @@ void organic_draw_branches(
|
||||
const double bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.;
|
||||
slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
|
||||
}
|
||||
|
||||
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
||||
|
||||
// ORCA: guard against empty slices from meshing.
|
||||
if (slices.empty())
|
||||
continue;
|
||||
|
||||
bottom_contacts.clear();
|
||||
// ORCA: trim tiny fragments to reduce degenerate polygon booleans.
|
||||
const double tiny_area = tiny_area_threshold();
|
||||
//FIXME parallelize?
|
||||
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++i) {
|
||||
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
|
||||
slices[i] = intersection(slices[i], volumes.m_bed_area);
|
||||
// ORCA: safety offset when trimming collision/bed to improve robustness.
|
||||
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true), ApplySafetyOffset::Yes); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
|
||||
slices[i] = intersection(slices[i], volumes.m_bed_area, ApplySafetyOffset::Yes);
|
||||
// Belt floor: clip branch slices against the belt surface plane.
|
||||
LayerIndex belt_idx = layer_begin + i;
|
||||
if (belt_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[belt_idx].empty())
|
||||
slices[i] = diff(slices[i], volumes.m_belt_floor[belt_idx]);
|
||||
remove_small(slices[i], tiny_area);
|
||||
}
|
||||
|
||||
size_t num_empty = 0;
|
||||
|
||||
if (slices.front().empty()) {
|
||||
// Some of the initial layers are empty.
|
||||
num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
|
||||
} else {
|
||||
if (branch.has_root) {
|
||||
if (config.support_rests_on_model && branch.path.front()->state.to_model_gracious) {
|
||||
if (config.settings.support_floor_layers > 0)
|
||||
//FIXME one may just take the whole tree slice as bottom interface.
|
||||
bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {})));
|
||||
} else if (layer_begin > 0) {
|
||||
// Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
|
||||
struct BottomExtraSlice {
|
||||
Polygons polygons;
|
||||
double area;
|
||||
};
|
||||
std::vector<BottomExtraSlice> bottom_extra_slices;
|
||||
Polygons rest_support;
|
||||
coord_t bottom_radius = support_element_radius(config, *branch.path.front());
|
||||
// Don't propagate further than 1.5 * bottom radius.
|
||||
//LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height;
|
||||
LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height;
|
||||
LayerIndex layer_bottommost = branch.path.front()->state.verylost ?
|
||||
// If the tree bottom is hanging in the air, bring it down to some surface.
|
||||
0 :
|
||||
//FIXME the "verylost" branches should stop when crossing another support.
|
||||
std::max(0, layer_begin - layers_propagate_max);
|
||||
double support_area_min_radius = M_PI * sqr(double(config.branch_radius));
|
||||
double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius);
|
||||
// Only propagate until the rest area is smaller than this threshold.
|
||||
//double support_area_min = 0.1 * support_area_min_radius;
|
||||
for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
|
||||
rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false));
|
||||
// Belt floor: clip propagated support at belt surface.
|
||||
if (layer_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[layer_idx].empty())
|
||||
rest_support = diff(rest_support, volumes.m_belt_floor[layer_idx]);
|
||||
double rest_support_area = area(rest_support);
|
||||
if (rest_support_area < support_area_stop)
|
||||
// Don't propagate a fraction of the tree contact surface.
|
||||
break;
|
||||
bottom_extra_slices.push_back({ rest_support, rest_support_area });
|
||||
}
|
||||
// Now remove those bottom slices that are not supported at all.
|
||||
#if 0
|
||||
while (! bottom_extra_slices.empty()) {
|
||||
Polygons this_bottom_contacts = intersection_clipped(
|
||||
bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
|
||||
if (area(this_bottom_contacts) < support_area_min)
|
||||
bottom_extra_slices.pop_back();
|
||||
else {
|
||||
// At least a fraction of the tree bottom is considered to be supported.
|
||||
if (config.settings.support_floor_layers > 0)
|
||||
// Turn this fraction of the tree bottom into a contact layer.
|
||||
bottom_contacts.emplace_back(std::move(this_bottom_contacts));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (config.support_rests_on_model && config.settings.support_floor_layers > 0)
|
||||
for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i)
|
||||
bottom_contacts.emplace_back(
|
||||
intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {})));
|
||||
layer_begin -= LayerIndex(bottom_extra_slices.size());
|
||||
slices.insert(slices.begin(), bottom_extra_slices.size(), {});
|
||||
auto it_dst = slices.begin();
|
||||
for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src)
|
||||
*it_dst ++ = std::move(it_src->polygons);
|
||||
}
|
||||
}
|
||||
|
||||
recover_pending_branch_roofs(interface_placer, branch.path, layer_begin, slices);
|
||||
}
|
||||
|
||||
layer_begin += LayerIndex(num_empty);
|
||||
// ORCA: trim leading empty slices to keep layer indices aligned.
|
||||
if (num_empty >= slices.size())
|
||||
continue;
|
||||
|
||||
if (num_empty > 0) {
|
||||
slices.erase(slices.begin(), slices.begin() + num_empty);
|
||||
layer_begin += LayerIndex(num_empty);
|
||||
}
|
||||
|
||||
// ORCA: use the trimmed front slice as the contact reference.
|
||||
Polygons slice_front_contact = slices.front();
|
||||
|
||||
if (branch.has_root) {
|
||||
if (branch.path.front()->state.to_model_gracious) {
|
||||
if (config.settings.support_floor_layers > 0) {
|
||||
// If bottom Z gap is non-zero, keep bottom contacts even when not touching the model.
|
||||
Polygons contacts;
|
||||
|
||||
// ORCA: non-zero bottom Z should not be clipped by placeable areas.
|
||||
if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0)
|
||||
contacts = slice_front_contact;
|
||||
else {
|
||||
Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {});
|
||||
contacts = intersection_clipped(slice_front_contact, placeable, ApplySafetyOffset::Yes);
|
||||
}
|
||||
|
||||
remove_small(contacts, tiny_area);
|
||||
|
||||
// ORCA: ensure bottom contacts exist if clipping removed them.
|
||||
if (contacts.empty() && config.support_rests_on_model && layer_begin > 0 && !slice_front_contact.empty())
|
||||
contacts = slice_front_contact;
|
||||
if (!contacts.empty())
|
||||
bottom_contacts.emplace_back(std::move(contacts));
|
||||
}
|
||||
} else if (layer_begin > 0 && (volumes.m_belt_floor.empty() || num_empty == 0)) {
|
||||
// Belt-floor clipping makes initial slices empty often; without this
|
||||
// gate, "verylost" branches propagate rest_support down to layer 0 and
|
||||
// OOM on tall belt prints.
|
||||
// Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
|
||||
struct BottomExtraSlice {
|
||||
Polygons polygons;
|
||||
double area;
|
||||
};
|
||||
std::vector<BottomExtraSlice> bottom_extra_slices;
|
||||
Polygons rest_support;
|
||||
coord_t bottom_radius = support_element_radius(config, *branch.path.front());
|
||||
// Don't propagate further than 1.5 * bottom radius.
|
||||
//LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height;
|
||||
LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height;
|
||||
LayerIndex layer_bottommost = branch.path.front()->state.verylost ?
|
||||
// If the tree bottom is hanging in the air, bring it down to some surface.
|
||||
0 :
|
||||
//FIXME the "verylost" branches should stop when crossing another support.
|
||||
std::max(0, layer_begin - layers_propagate_max);
|
||||
double support_area_min_radius = M_PI * sqr(double(config.branch_radius));
|
||||
double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius);
|
||||
// Only propagate until the rest area is smaller than this threshold.
|
||||
//double support_area_min = 0.1 * support_area_min_radius;
|
||||
for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
|
||||
LayerIndex collision_layer = (layer_idx == layer_begin - 1) ? layer_begin : layer_idx;
|
||||
Polygons collision = volumes.getCollision(0, collision_layer, false);
|
||||
rest_support = diff_clipped(rest_support.empty() ? slice_front_contact : rest_support, collision, ApplySafetyOffset::Yes);
|
||||
// Belt floor: clip propagated support at belt surface.
|
||||
if (layer_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[layer_idx].empty())
|
||||
rest_support = diff(rest_support, volumes.m_belt_floor[layer_idx]);
|
||||
remove_small(rest_support, tiny_area);
|
||||
double rest_support_area = area(rest_support);
|
||||
if (rest_support_area < support_area_stop)
|
||||
// Don't propagate a fraction of the tree contact surface.
|
||||
break;
|
||||
bottom_extra_slices.push_back({ rest_support, rest_support_area });
|
||||
}
|
||||
// Now remove those bottom slices that are not supported at all.
|
||||
#if 0
|
||||
while (! bottom_extra_slices.empty()) {
|
||||
Polygons this_bottom_contacts = intersection_clipped(
|
||||
bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
|
||||
if (area(this_bottom_contacts) < support_area_min)
|
||||
bottom_extra_slices.pop_back();
|
||||
else {
|
||||
// At least a fraction of the tree bottom is considered to be supported.
|
||||
if (config.settings.support_floor_layers > 0)
|
||||
// Turn this fraction of the tree bottom into a contact layer.
|
||||
bottom_contacts.emplace_back(std::move(this_bottom_contacts));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (config.settings.support_floor_layers > 0) {
|
||||
Polygons contacts;
|
||||
if (!bottom_extra_slices.empty()) {
|
||||
const int contact_idx = int(bottom_extra_slices.size()) - 1; // Use the lowest contact slice as the footprint.
|
||||
|
||||
// ORCA: non-zero bottom Z should not be clipped by placeable areas.
|
||||
if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0)
|
||||
contacts = intersection_clipped(bottom_extra_slices[contact_idx].polygons, Polygons{volumes.m_bed_area}, ApplySafetyOffset::Yes);
|
||||
else {
|
||||
Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {});
|
||||
contacts = intersection_clipped(bottom_extra_slices[contact_idx].polygons, placeable, ApplySafetyOffset::Yes);
|
||||
}
|
||||
} else {
|
||||
// Fallback: use the current contact slice when no propagation happened.
|
||||
if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0)
|
||||
contacts = slice_front_contact;
|
||||
else {
|
||||
Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {});
|
||||
contacts = intersection_clipped(slice_front_contact, placeable, ApplySafetyOffset::Yes);
|
||||
}
|
||||
}
|
||||
|
||||
remove_small(contacts, tiny_area);
|
||||
|
||||
if (!contacts.empty())
|
||||
bottom_contacts.emplace_back(std::move(contacts));
|
||||
|
||||
// ORCA: ensure bottom contacts exist if clipping removed them.
|
||||
if (bottom_contacts.empty() && config.support_rests_on_model && layer_begin > 0 && !slice_front_contact.empty())
|
||||
bottom_contacts.emplace_back(slice_front_contact);
|
||||
}
|
||||
layer_begin -= LayerIndex(bottom_extra_slices.size());
|
||||
slices.insert(slices.begin(), bottom_extra_slices.size(), {});
|
||||
auto it_dst = slices.begin();
|
||||
for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src)
|
||||
*it_dst ++ = std::move(it_src->polygons);
|
||||
}
|
||||
|
||||
// ORCA: retain bottom contacts even when no placeable areas intersect.
|
||||
if (branch.has_root && config.support_rests_on_model && branch.path.front()->state.layer_idx > 0 &&
|
||||
config.settings.support_floor_layers > 0 && config.z_distance_bottom_layers > 0 &&
|
||||
bottom_contacts.empty() && !slice_front_contact.empty())
|
||||
bottom_contacts.emplace_back(slice_front_contact);
|
||||
|
||||
}
|
||||
// ORCA: bottom contacts provide the footprint; interface layers are built later.
|
||||
|
||||
#if 0
|
||||
//FIXME branch.has_tip seems to not be reliable.
|
||||
if (branch.has_tip && interface_placer.support_parameters.has_top_contacts)
|
||||
// Add top slices to top contacts / interfaces / base interfaces.
|
||||
for (int i = int(branch.path.size()) - 1; i >= 0; -- i) {
|
||||
const SupportElement &el = *branch.path[i];
|
||||
if (el.state.missing_roof_layers == 0)
|
||||
break;
|
||||
//FIXME Move or not?
|
||||
interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx,
|
||||
interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (! slices.empty() && slices.back().empty()) {
|
||||
slices.pop_back();
|
||||
-- layer_end;
|
||||
}
|
||||
|
||||
// ORCA: recompute layer_end after trimming trailing empty slices.
|
||||
layer_end = layer_begin + LayerIndex(slices.size());
|
||||
|
||||
if (layer_begin < layer_end) {
|
||||
LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin);
|
||||
LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end);
|
||||
@@ -3998,22 +4056,28 @@ void organic_draw_branches(
|
||||
} else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
|
||||
tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
|
||||
tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
|
||||
layer_begin -= LayerIndex(num_empty);
|
||||
for (LayerIndex i = layer_begin; i != layer_end; ++ i) {
|
||||
int j = i - layer_begin;
|
||||
if (Polygons &src = slices[j]; ! src.empty()) {
|
||||
Polygons &src = slices[j];
|
||||
bool has_bottom_contacts = j < int(bottom_contacts.size()) && !bottom_contacts[j].empty();
|
||||
|
||||
// ORCA: preserve bottom contacts even if base polygons are empty.
|
||||
if (!src.empty() || has_bottom_contacts) {
|
||||
Slice &dst = tree.slices[i - new_begin];
|
||||
if (++ dst.num_branches > 1) {
|
||||
append(dst.polygons, std::move(src));
|
||||
if (j < int(bottom_contacts.size()))
|
||||
if (!src.empty())
|
||||
append(dst.polygons, std::move(src));
|
||||
if (has_bottom_contacts)
|
||||
append(dst.bottom_contacts, std::move(bottom_contacts[j]));
|
||||
} else {
|
||||
dst.polygons = std::move(std::move(src));
|
||||
if (j < int(bottom_contacts.size()))
|
||||
if (!src.empty())
|
||||
dst.polygons = std::move(src);
|
||||
if (has_bottom_contacts)
|
||||
dst.bottom_contacts = std::move(bottom_contacts[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tree.first_layer_id = new_begin;
|
||||
}
|
||||
}
|
||||
@@ -4026,10 +4090,15 @@ void organic_draw_branches(
|
||||
Tree &tree = trees[tree_id];
|
||||
for (Slice &slice : tree.slices)
|
||||
if (slice.num_branches > 1) {
|
||||
slice.polygons = union_(slice.polygons);
|
||||
slice.bottom_contacts = union_(slice.bottom_contacts);
|
||||
// ORCA: avoid union_ on empty containers.
|
||||
if (!slice.polygons.empty())
|
||||
slice.polygons = union_(slice.polygons);
|
||||
if (!slice.bottom_contacts.empty())
|
||||
slice.bottom_contacts = union_(slice.bottom_contacts);
|
||||
|
||||
slice.num_branches = 1;
|
||||
}
|
||||
|
||||
throw_on_cancel();
|
||||
}
|
||||
}, tbb::simple_partitioner());
|
||||
@@ -4042,17 +4111,27 @@ void organic_draw_branches(
|
||||
std::vector<Slice> slices(num_layers, Slice{});
|
||||
for (Tree &tree : trees)
|
||||
if (tree.first_layer_id >= 0) {
|
||||
for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i)
|
||||
if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) {
|
||||
for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i) {
|
||||
Slice &src = tree.slices[i - tree.first_layer_id];
|
||||
bool has_bottom_contacts = !src.bottom_contacts.empty();
|
||||
|
||||
// ORCA: preserve bottom contacts even if base polygons are empty.
|
||||
if (!src.polygons.empty() || has_bottom_contacts) {
|
||||
Slice &dst = slices[i];
|
||||
|
||||
if (++ dst.num_branches > 1) {
|
||||
append(dst.polygons, std::move(src.polygons));
|
||||
append(dst.bottom_contacts, std::move(src.bottom_contacts));
|
||||
if (!src.polygons.empty())
|
||||
append(dst.polygons, std::move(src.polygons));
|
||||
if (has_bottom_contacts)
|
||||
append(dst.bottom_contacts, std::move(src.bottom_contacts));
|
||||
} else {
|
||||
dst.polygons = std::move(src.polygons);
|
||||
dst.bottom_contacts = std::move(src.bottom_contacts);
|
||||
if (!src.polygons.empty())
|
||||
dst.polygons = std::move(src.polygons);
|
||||
if (has_bottom_contacts)
|
||||
dst.bottom_contacts = std::move(src.bottom_contacts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
|
||||
@@ -4060,8 +4139,11 @@ void organic_draw_branches(
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
Slice &slice = slices[layer_idx];
|
||||
assert(intermediate_layers[layer_idx] == nullptr);
|
||||
Polygons base_layer_polygons = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
|
||||
Polygons bottom_contact_polygons = slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts);
|
||||
// ORCA: avoid union_ on empty inputs.
|
||||
Polygons base_layer_polygons = slice.polygons.empty() ? Polygons{} :
|
||||
(slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons));
|
||||
Polygons bottom_contact_polygons = slice.bottom_contacts.empty() ? Polygons{} :
|
||||
(slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts));
|
||||
|
||||
if (! base_layer_polygons.empty()) {
|
||||
// Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
|
||||
|
||||
Reference in New Issue
Block a user