WIP Ensure vertical wall thickness rework: bugfixes
This commit is contained in:
parent
785ef08656
commit
fbed29e209
@ -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]));
|
||||||
|
@ -185,6 +185,7 @@ Surfaces expand_bridges_detect_orientations(
|
|||||||
struct Bridge {
|
struct Bridge {
|
||||||
ExPolygon expolygon;
|
ExPolygon expolygon;
|
||||||
uint32_t group_id;
|
uint32_t group_id;
|
||||||
|
std::vector<RegionExpansionEx>::const_iterator bridge_expansion_begin;
|
||||||
double angle = -1;
|
double angle = -1;
|
||||||
};
|
};
|
||||||
std::vector<Bridge> bridges;
|
std::vector<Bridge> bridges;
|
||||||
@ -192,7 +193,7 @@ Surfaces expand_bridges_detect_orientations(
|
|||||||
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
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user