Organic supports improvements: Removing collisions with trees,
limiting how far tree bottoms at slanted surfaces could be extended down below their last full circle position. Placable areas are now calculated sitting on slightly inflated top surface to indicate support of tree bottoms at slanted surfaces.
This commit is contained in:
parent
2eebba82f8
commit
8593ad1f80
@ -685,6 +685,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
|
@ -453,6 +453,9 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
|
||||
// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon.
|
||||
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
|
@ -426,7 +426,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
|
||||
return (*result).get();
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
tree_supports_show_error("Not precalculated Placeable areas requested."sv, false);
|
||||
tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false);
|
||||
}
|
||||
if (orig_radius == 0)
|
||||
// Placable areas for radius 0 are calculated in the general collision code.
|
||||
@ -597,16 +597,19 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
// 3) Optionally calculate placables.
|
||||
if (calculate_placable) {
|
||||
// Now calculate the placable areas.
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(data.idx_begin, 1), data.idx_end),
|
||||
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh,
|
||||
min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel]
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end),
|
||||
[&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh,
|
||||
min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel]
|
||||
(const tbb::blocked_range<LayerIndex>& range) {
|
||||
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
|
||||
LayerIndex layer_idx_below = layer_idx - 1;
|
||||
LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1;
|
||||
assert(layer_idx_below >= 0);
|
||||
const Polygons ¤t = collision_areas_offsetted[layer_idx];
|
||||
const Polygons &below = collision_areas_offsetted[layer_idx_below];
|
||||
Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
|
||||
const Polygons &below = outlines[layer_idx_below];
|
||||
Polygons placable = diff(
|
||||
// Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface.
|
||||
offset(below, xy_distance),
|
||||
layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
|
||||
auto &dst = data_placeable[layer_idx];
|
||||
if (processing_last_mesh) {
|
||||
if (! dst.empty())
|
||||
|
@ -215,6 +215,7 @@ public:
|
||||
void clear() {
|
||||
this->clear_all_but_object_collision();
|
||||
m_collision_cache.clear();
|
||||
m_placeable_areas_cache.clear();
|
||||
}
|
||||
void clear_all_but_object_collision() {
|
||||
//m_collision_cache.clear_all_but_radius0();
|
||||
@ -223,7 +224,7 @@ public:
|
||||
m_avoidance_cache_slow.clear();
|
||||
m_avoidance_cache_to_model.clear();
|
||||
m_avoidance_cache_to_model_slow.clear();
|
||||
m_placeable_areas_cache.clear();
|
||||
m_placeable_areas_cache.clear_all_but_radius0();
|
||||
m_avoidance_cache_holefree.clear();
|
||||
m_avoidance_cache_holefree_to_model.clear();
|
||||
m_wall_restrictions_cache.clear();
|
||||
|
@ -299,7 +299,7 @@ static bool inline g_showed_critical_error = false;
|
||||
static bool inline g_showed_performance_warning = false;
|
||||
void tree_supports_show_error(std::string_view message, bool critical)
|
||||
{ // todo Remove! ONLY FOR PUBLIC BETA!!
|
||||
|
||||
// printf("Error: %s, critical: %d\n", message.data(), int(critical));
|
||||
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
|
||||
static bool showed_critical = false;
|
||||
static bool showed_performance = false;
|
||||
@ -2158,6 +2158,10 @@ static void increase_areas_one_layer(
|
||||
" Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist <<
|
||||
" to buildplate " << parent.state.to_buildplate << " gracious " << parent.state.to_model_gracious << " safe " << parent.state.can_use_safe_radius << " until move " << parent.state.dont_move_until;
|
||||
tree_supports_show_error("Potentially lost branch!"sv, true);
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
if (result)
|
||||
result->lost = true;
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
} else
|
||||
result = increase_single_area(volumes, config, settings, layer_idx, parent,
|
||||
settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer);
|
||||
@ -2211,6 +2215,9 @@ static void increase_areas_one_layer(
|
||||
// But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set.
|
||||
// A point can be set on the top most tip layer (maybe more if it should not move for a few layers).
|
||||
parent.state.result_on_layer_reset();
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
parent.state.verylost = true;
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
}
|
||||
|
||||
throw_on_cancel();
|
||||
@ -4234,26 +4241,35 @@ static std::vector<Polygons> draw_branches(
|
||||
branch.path.emplace_back(&start_element);
|
||||
// Traverse each branch until it branches again.
|
||||
SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
|
||||
assert(! first_parent.state.marked);
|
||||
assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx);
|
||||
branch.path.emplace_back(&first_parent);
|
||||
if (first_parent.parents.size() < 2)
|
||||
first_parent.state.marked = true;
|
||||
SupportElement *next_branch = nullptr;
|
||||
if (first_parent.parents.size() == 1)
|
||||
if (first_parent.parents.size() == 1) {
|
||||
for (SupportElement *parent = &first_parent;;) {
|
||||
assert(parent->state.marked);
|
||||
SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()];
|
||||
assert(! next_parent.state.marked);
|
||||
assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx);
|
||||
branch.path.emplace_back(&next_parent);
|
||||
if (next_parent.parents.size() > 1) {
|
||||
// Branching point was reached.
|
||||
next_branch = &next_parent;
|
||||
break;
|
||||
}
|
||||
next_parent.state.marked = true;
|
||||
if (next_parent.parents.size() == 0)
|
||||
// Tip is reached.
|
||||
break;
|
||||
parent = &next_parent;
|
||||
}
|
||||
} else if (first_parent.parents.size() > 1)
|
||||
// Branching point was reached.
|
||||
next_branch = &first_parent;
|
||||
assert(branch.path.size() >= 2);
|
||||
assert(next_branch == nullptr || ! next_branch->state.marked);
|
||||
branch.has_root = root;
|
||||
branch.has_tip = ! next_branch;
|
||||
out.branches.emplace_back(std::move(branch));
|
||||
@ -4263,13 +4279,37 @@ static std::vector<Polygons> draw_branches(
|
||||
}
|
||||
};
|
||||
|
||||
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx)
|
||||
for (SupportElement &start_element : move_bounds[layer_idx])
|
||||
if (! start_element.state.marked && ! start_element.parents.empty()) {
|
||||
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||
// int ielement;
|
||||
for (SupportElement& start_element : move_bounds[layer_idx]) {
|
||||
if (!start_element.state.marked && !start_element.parents.empty()) {
|
||||
#if 0
|
||||
int found = 0;
|
||||
if (layer_idx > 0) {
|
||||
for (auto& el : move_bounds[layer_idx - 1]) {
|
||||
for (auto iparent : el.parents)
|
||||
if (iparent == ielement)
|
||||
++found;
|
||||
}
|
||||
if (found != 0)
|
||||
printf("Found: %d\n", found);
|
||||
}
|
||||
#endif
|
||||
trees.push_back({});
|
||||
TreeVisitor::visit_recursive(move_bounds, start_element, trees.back());
|
||||
assert(! trees.back().branches.empty());
|
||||
assert(!trees.back().branches.empty());
|
||||
//FIXME debugging
|
||||
#if 0
|
||||
if (start_element.state.lost) {
|
||||
}
|
||||
else if (start_element.state.verylost) {
|
||||
} else
|
||||
trees.pop_back();
|
||||
#endif
|
||||
}
|
||||
// ++ ielement;
|
||||
}
|
||||
}
|
||||
|
||||
const SlicingParameters &slicing_params = print_object.slicing_parameters();
|
||||
MeshSlicingParams mesh_slicing_params;
|
||||
@ -4297,49 +4337,89 @@ static std::vector<Polygons> draw_branches(
|
||||
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);
|
||||
size_t num_empty = 0;
|
||||
//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);
|
||||
|
||||
size_t num_empty = 0;
|
||||
if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) {
|
||||
// Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
|
||||
std::vector<Polygons> bottom_extra_slices;
|
||||
Polygons rest_support;
|
||||
for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= 0; -- layer_idx) {
|
||||
struct BottomExtraSlice {
|
||||
Polygons polygons;
|
||||
Polygons supported;
|
||||
double area;
|
||||
double supported_area;
|
||||
};
|
||||
std::vector<BottomExtraSlice> bottom_extra_slices;
|
||||
Polygons rest_support;
|
||||
coord_t bottom_radius = config.getRadius(branch.path.front()->state);
|
||||
// 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 = std::max(0, layer_begin - layers_propagate_max);
|
||||
// Only propagate until the rest area is smaller than this threshold.
|
||||
double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius));
|
||||
// Only propagate until the rest area is smaller than this threshold.
|
||||
double support_area_min = 0.1 * M_PI * sqr(double(config.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));
|
||||
if (area(rest_support) < tiny_area_threshold)
|
||||
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.emplace_back(rest_support);
|
||||
// Measure how much the rest_support is actually supported.
|
||||
/*
|
||||
Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{}));
|
||||
double supported_area = area(supported);
|
||||
printf("Supported area: %d, %lf\n", layer_idx, supported_area);
|
||||
*/
|
||||
Polygons supported;
|
||||
double supported_area;
|
||||
bottom_extra_slices.push_back({ rest_support, std::move(supported), rest_support_area, supported_area });
|
||||
}
|
||||
// Now remove those bottom slices that are not supported at all.
|
||||
while (! bottom_extra_slices.empty() &&
|
||||
area(intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}))) < support_area_min)
|
||||
bottom_extra_slices.pop_back();
|
||||
layer_begin -= LayerIndex(bottom_extra_slices.size());
|
||||
slices.insert(slices.begin(), std::make_move_iterator(bottom_extra_slices.rbegin()), std::make_move_iterator(bottom_extra_slices.rend()));
|
||||
slices.insert(slices.begin(), bottom_extra_slices.size(), {});
|
||||
size_t i = 0;
|
||||
for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i)
|
||||
slices[i] = std::move(it->polygons);
|
||||
} else
|
||||
num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
|
||||
|
||||
layer_begin += LayerIndex(num_empty);
|
||||
for (; slices.back().empty(); -- 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);
|
||||
size_t new_size = size_t(new_end - new_begin);
|
||||
if (tree.first_layer_id == -1) {
|
||||
} else if (tree.slices.capacity() < new_size) {
|
||||
std::vector<Slice> new_slices;
|
||||
new_slices.reserve(new_size);
|
||||
if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
|
||||
new_slices.insert(new_slices.end(), dif, {});
|
||||
append(new_slices, std::move(tree.slices));
|
||||
tree.slices.swap(new_slices);
|
||||
} 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)
|
||||
if (Polygons &src = slices[i - layer_begin]; ! src.empty()) {
|
||||
Slice &dst = tree.slices[i - new_begin];
|
||||
if (++ dst.num_branches > 1)
|
||||
append(dst.polygons, std::move(src));
|
||||
else
|
||||
dst.polygons = std::move(std::move(src));
|
||||
}
|
||||
tree.first_layer_id = new_begin;
|
||||
while (! slices.empty() && slices.back().empty()) {
|
||||
slices.pop_back();
|
||||
-- layer_end;
|
||||
}
|
||||
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);
|
||||
size_t new_size = size_t(new_end - new_begin);
|
||||
if (tree.first_layer_id == -1) {
|
||||
} else if (tree.slices.capacity() < new_size) {
|
||||
std::vector<Slice> new_slices;
|
||||
new_slices.reserve(new_size);
|
||||
if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
|
||||
new_slices.insert(new_slices.end(), dif, {});
|
||||
append(new_slices, std::move(tree.slices));
|
||||
tree.slices.swap(new_slices);
|
||||
} 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)
|
||||
if (Polygons &src = slices[i - layer_begin]; ! src.empty()) {
|
||||
Slice &dst = tree.slices[i - new_begin];
|
||||
if (++ dst.num_branches > 1)
|
||||
append(dst.polygons, std::move(src));
|
||||
else
|
||||
dst.polygons = std::move(std::move(src));
|
||||
}
|
||||
tree.first_layer_id = new_begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, tbb::simple_partitioner());
|
||||
|
@ -93,6 +93,8 @@ struct AreaIncreaseSettings
|
||||
|
||||
struct TreeSupportSettings;
|
||||
|
||||
// #define TREE_SUPPORTS_TRACK_LOST
|
||||
|
||||
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
|
||||
struct SupportElementStateBits {
|
||||
SupportElementStateBits() :
|
||||
@ -102,6 +104,10 @@ struct SupportElementStateBits {
|
||||
supports_roof(false),
|
||||
can_use_safe_radius(false),
|
||||
skip_ovalisation(false),
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
lost(false),
|
||||
verylost(false),
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
deleted(false),
|
||||
marked(false)
|
||||
{}
|
||||
@ -136,6 +142,12 @@ struct SupportElementStateBits {
|
||||
*/
|
||||
bool skip_ovalisation : 1;
|
||||
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
// Likely a lost branch, debugging information.
|
||||
bool lost : 1;
|
||||
bool verylost : 1;
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
|
||||
// Not valid anymore, to be deleted.
|
||||
bool deleted : 1;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user