WIP Ensure vertical wall thickness rework: bugfixes

This commit is contained in:
Vojtech Bubnik 2023-01-03 10:06:52 +01:00
parent 785ef08656
commit fbed29e209
2 changed files with 110 additions and 52 deletions

View File

@ -153,6 +153,46 @@ static inline void merge_splits(ClipperLib_Z::Paths &paths, std::vector<std::pai
} }
} }
using AABBTreeBBoxes = AABBTreeIndirect::Tree<2, coord_t>;
static AABBTreeBBoxes build_aabb_tree_over_expolygons(const ExPolygons &expolygons)
{
// Calculate bounding boxes of internal slices.
std::vector<AABBTreeIndirect::BoundingBoxWrapper> bboxes;
bboxes.reserve(expolygons.size());
for (size_t i = 0; i < expolygons.size(); ++ i)
bboxes.emplace_back(i, get_extents(expolygons[i].contour));
// Build AABB tree over bounding boxes of boundary expolygons.
AABBTreeBBoxes out;
out.build_modify_input(bboxes);
return out;
}
static int sample_in_expolygons(
// AABB tree over boundary expolygons
const AABBTreeBBoxes &aabb_tree,
const ExPolygons &expolygons,
const Point &sample)
{
int out = -1;
AABBTreeIndirect::traverse(aabb_tree,
[&sample](const AABBTreeBBoxes::Node &node) {
return node.bbox.contains(sample);
},
[&expolygons, &sample, &out](const AABBTreeBBoxes::Node &node) {
assert(node.is_leaf());
assert(node.is_valid());
if (expolygons[node.idx].contains(sample)) {
out = int(node.idx);
// Stop traversal.
return false;
}
// Continue traversal.
return true;
});
return out;
}
std::vector<WaveSeed> wave_seeds( std::vector<WaveSeed> wave_seeds(
// Source regions that are supposed to touch the boundary. // Source regions that are supposed to touch the boundary.
const ExPolygons &src, const ExPolygons &src,
@ -211,19 +251,7 @@ std::vector<WaveSeed> wave_seeds(
// AABBTree over bounding boxes of boundaries. // AABBTree over bounding boxes of boundaries.
// Only built if necessary, that is if any of the seed contours is closed, thus there is no intersection point // Only built if necessary, that is if any of the seed contours is closed, thus there is no intersection point
// with the boundary and all Z coordinates of the closed contour point to the source contour. // with the boundary and all Z coordinates of the closed contour point to the source contour.
using AABBTree = AABBTreeIndirect::Tree<2, coord_t>; AABBTreeBBoxes aabb_tree;
AABBTree aabb_tree;
auto init_aabb_tree = [&aabb_tree, &boundary]() {
if (aabb_tree.empty()) {
// Calculate bounding boxes of internal slices.
std::vector<AABBTreeIndirect::BoundingBoxWrapper> bboxes;
bboxes.reserve(boundary.size());
for (size_t i = 0; i < boundary.size(); ++ i)
bboxes.emplace_back(i, get_extents(boundary[i].contour));
// Build AABB tree over bounding boxes of boundary expolygons.
aabb_tree.build_modify_input(bboxes);
}
};
// Sort paths into their respective islands. // Sort paths into their respective islands.
// Each src x boundary will be processed (wave expanded) independently. // Each src x boundary will be processed (wave expanded) independently.
@ -262,24 +290,9 @@ std::vector<WaveSeed> wave_seeds(
// This should be a closed contour. // This should be a closed contour.
assert(front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end); assert(front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end);
// Find a source boundary expolygon of one sample of this closed path. // Find a source boundary expolygon of one sample of this closed path.
init_aabb_tree(); if (aabb_tree.empty())
Point sample(front.x(), front.y()); aabb_tree = build_aabb_tree_over_expolygons(boundary);
int boundary_id = -1; int boundary_id = sample_in_expolygons(aabb_tree, boundary, Point(front.x(), front.y()));
AABBTreeIndirect::traverse(aabb_tree,
[&sample](const AABBTree::Node &node) {
return node.bbox.contains(sample);
},
[&boundary, &sample, &boundary_id](const AABBTree::Node &node) {
assert(node.is_leaf());
assert(node.is_valid());
if (boundary[node.idx].contains(sample)) {
boundary_id = int(node.idx);
// Stop traversal.
return false;
}
// Continue traversal.
return true;
});
// Boundary that contains the sample point was found. // Boundary that contains the sample point was found.
assert(boundary_id >= 0); assert(boundary_id >= 0);
if (boundary_id >= 0) if (boundary_id >= 0)
@ -431,6 +444,7 @@ std::vector<RegionExpansionEx> propagate_waves_ex(const WaveSeeds &seeds, const
for (ExPolygon &ex : expolys) for (ExPolygon &ex : expolys)
out.push_back({ std::move(ex), it->src_id, it->boundary_id }); out.push_back({ std::move(ex), it->src_id, it->boundary_id });
} }
it = it2;
} }
return out; return out;
} }
@ -477,21 +491,44 @@ std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygon
ExPolygons out; ExPolygons out;
out.reserve(src.size()); out.reserve(src.size());
for (auto it = expanded.begin(); it != expanded.end();) { for (auto it = expanded.begin(); it != expanded.end();) {
auto it2 = it;
acc.clear();
for (; it2 != expanded.end() && it->src_id == it2->src_id; ++ it2)
acc.emplace_back(std::move(it2->polygon));
for (; last < it->src_id; ++ last) for (; last < it->src_id; ++ last)
out.emplace_back(std::move(src[last])); out.emplace_back(std::move(src[last]));
acc.clear();
assert(it->src_id == last);
for (; it != expanded.end() && it->src_id == last; ++ it)
acc.emplace_back(std::move(it->polygon));
//FIXME offset & merging could be more efficient, for example one does not need to copy the source expolygon //FIXME offset & merging could be more efficient, for example one does not need to copy the source expolygon
append(acc, to_polygons(std::move(src[it->src_id]))); ExPolygon &src_ex = src[last ++];
assert(! src_ex.contour.empty());
#if 0
{
static int iRun = 0;
BoundingBox bbox = get_extents(acc);
bbox.merge(get_extents(src_ex));
SVG svg(debug_out_path("expand_merge_expolygons-failed-union=%d.svg", iRun ++).c_str(), bbox);
svg.draw(acc);
svg.draw_outline(acc, "black", scale_(0.05));
svg.draw(src_ex, "red");
svg.Close();
}
#endif
Point sample = src_ex.contour.front();
append(acc, to_polygons(std::move(src_ex)));
ExPolygons merged = union_safety_offset_ex(acc); ExPolygons merged = union_safety_offset_ex(acc);
// Expanding one expolygon by waves should not change connectivity of the source expolygon: // Expanding one expolygon by waves should not change connectivity of the source expolygon:
// Single expolygon should be produced possibly with increased number of holes. // Single expolygon should be produced possibly with increased number of holes.
assert(merged.size() == 1); if (merged.size() > 1) {
if (! merged.empty()) // assert(merged.size() == 1);
// There is something wrong with the initial waves. Most likely the bridge was not valid at all
// or the boundary region was very close to some bridge edge, but not really touching.
// Pick only a single merged expolygon, which contains one sample point of the source expolygon.
auto aabb_tree = build_aabb_tree_over_expolygons(merged);
int id = sample_in_expolygons(aabb_tree, merged, sample);
assert(id != -1);
if (id != -1)
out.emplace_back(std::move(merged[id]));
} else if (merged.size() == 1)
out.emplace_back(std::move(merged.front())); out.emplace_back(std::move(merged.front()));
it = it2;
} }
for (; last < uint32_t(src.size()); ++ last) for (; last < uint32_t(src.size()); ++ last)
out.emplace_back(std::move(src[last])); out.emplace_back(std::move(src[last]));

View File

@ -183,16 +183,17 @@ Surfaces expand_bridges_detect_orientations(
// Cache for detecting bridge orientation and merging regions with overlapping expansions. // Cache for detecting bridge orientation and merging regions with overlapping expansions.
struct Bridge { struct Bridge {
ExPolygon expolygon; ExPolygon expolygon;
uint32_t group_id; uint32_t group_id;
double angle = -1; std::vector<RegionExpansionEx>::const_iterator bridge_expansion_begin;
double angle = -1;
}; };
std::vector<Bridge> bridges; std::vector<Bridge> bridges;
{ {
bridges.reserve(bridges_ex.size()); bridges.reserve(bridges_ex.size());
uint32_t group_id = 0; uint32_t group_id = 0;
for (ExPolygon &ex : bridges_ex) for (ExPolygon &ex : bridges_ex)
bridges.push_back({ std::move(ex), group_id ++ }); bridges.push_back({ std::move(ex), group_id ++, bridge_expansions.end() });
bridges_ex.clear(); bridges_ex.clear();
} }
@ -243,15 +244,24 @@ Surfaces expand_bridges_detect_orientations(
std::sort(bridge_anchors.begin(), bridge_anchors.end(), Algorithm::lower_by_src_and_boundary); std::sort(bridge_anchors.begin(), bridge_anchors.end(), Algorithm::lower_by_src_and_boundary);
auto it_bridge_anchor = bridge_anchors.begin(); auto it_bridge_anchor = bridge_anchors.begin();
Lines lines; Lines lines;
Polygons anchor_areas;
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) { for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
Bridge &bridge = bridges[bridge_id]; Bridge &bridge = bridges[bridge_id];
lines.clear(); // lines.clear();
for (++ it_bridge_anchor; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) anchor_areas.clear();
if (Points &polyline = it_bridge_anchor->path; polyline.size() >= 2) { int32_t last_anchor_id = -1;
reserve_more_power_of_2(lines, polyline.size() - 1); for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) {
for (size_t i = 1; i < polyline.size(); ++ i) if (last_anchor_id != int(it_bridge_anchor->boundary)) {
lines.push_back({ polyline[i - 1], polyline[1] }); last_anchor_id = int(it_bridge_anchor->boundary);
append(anchor_areas, std::move(to_polygons(shells[last_anchor_id])));
} }
// if (Points &polyline = it_bridge_anchor->path; polyline.size() >= 2) {
// reserve_more_power_of_2(lines, polyline.size() - 1);
// for (size_t i = 1; i < polyline.size(); ++ i)
// lines.push_back({ polyline[i - 1], polyline[1] });
// }
}
lines = to_lines(diff_pl(to_polylines(bridge.expolygon), expand(anchor_areas, float(SCALED_EPSILON))));
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon)); auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon));
bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x()); bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
// #if 1 // #if 1
@ -273,12 +283,23 @@ Surfaces expand_bridges_detect_orientations(
{ {
Polygons acc; Polygons acc;
Surface templ{ stBottomBridge, {} }; Surface templ{ stBottomBridge, {} };
std::sort(bridge_expansions.begin(), bridge_expansions.end(), [](auto &l, auto &r) {
return l.src_id < r.src_id || (l.src_id == r.src_id && l.boundary_id < r.boundary_id);
});
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end(); ) {
bridges[it->src_id].bridge_expansion_begin = it;
uint32_t src_id = it->src_id;
for (++ it; it != bridge_expansions.end() && it->src_id == src_id; ++ it) ;
}
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) { for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
acc.clear(); acc.clear();
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2) for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2)
if (group_id(bridge_id) == bridge_id) { if (group_id(bridge_id) == bridge_id) {
append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon))); append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon)));
append(acc, to_polygons(std::move(bridge_expansions[bridge_id2].expolygon))); auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin;
assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2);
for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion)
append(acc, to_polygons(std::move(it_bridge_expansion->expolygon)));
} }
//FIXME try to be smart and pick the best bridging angle for all? //FIXME try to be smart and pick the best bridging angle for all?
templ.bridge_angle = bridges[bridge_id].angle; templ.bridge_angle = bridges[bridge_id].angle;
@ -355,8 +376,8 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
const double custom_angle = this->region().config().bridge_angle.value; const double custom_angle = this->region().config().bridge_angle.value;
const auto params = Algorithm::RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps); const auto params = Algorithm::RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
bridges.surfaces = custom_angle > 0 ? bridges.surfaces = custom_angle > 0 ?
expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params) : expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, custom_angle) :
expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, custom_angle); expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params);
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
#if 0 #if 0
{ {