Added detection for a missing Voronoi vertex. If the missing Voronoi vertex is detected, try to resolve it by rotation input polygons.
This commit is contained in:
parent
adf2d21c67
commit
5db91f1944
1 changed files with 87 additions and 2 deletions
|
@ -378,6 +378,52 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead
|
|||
constructFromPolygons(polys);
|
||||
}
|
||||
|
||||
bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector<SkeletalTrapezoidation::Segment> &segments) {
|
||||
for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
|
||||
if (!cell.incident_edge())
|
||||
continue; // There is no spoon
|
||||
|
||||
if (cell.contains_segment()) {
|
||||
const SkeletalTrapezoidation::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
|
||||
const Point from = source_segment.from();
|
||||
const Point to = source_segment.to();
|
||||
|
||||
// Find starting edge
|
||||
// Find end edge
|
||||
bool seen_possible_start = false;
|
||||
bool after_start = false;
|
||||
bool ending_edge_is_set_before_start = false;
|
||||
VoronoiUtils::vd_t::edge_type *starting_vd_edge = nullptr;
|
||||
VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr;
|
||||
VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge();
|
||||
do {
|
||||
if (edge->is_infinite()) continue;
|
||||
|
||||
Vec2i64 v0 = VoronoiUtils::p(edge->vertex0());
|
||||
Vec2i64 v1 = VoronoiUtils::p(edge->vertex1());
|
||||
|
||||
assert(!(v0 == to.cast<int64_t>() && v1 == from.cast<int64_t>()));
|
||||
if (v0 == to.cast<int64_t>() && !after_start) { // Use the last edge which starts in source_segment.to
|
||||
starting_vd_edge = edge;
|
||||
seen_possible_start = true;
|
||||
} else if (seen_possible_start) {
|
||||
after_start = true;
|
||||
}
|
||||
|
||||
if (v1 == from.cast<int64_t>() && (!ending_vd_edge || ending_edge_is_set_before_start)) {
|
||||
ending_edge_is_set_before_start = !after_start;
|
||||
ending_vd_edge = edge;
|
||||
}
|
||||
} while (edge = edge->next(), edge != cell.incident_edge());
|
||||
|
||||
if (!starting_vd_edge || !ending_vd_edge || starting_vd_edge == ending_vd_edge)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
||||
{
|
||||
// Check self intersections.
|
||||
|
@ -399,8 +445,37 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
|||
Geometry::VoronoiDiagram voronoi_diagram;
|
||||
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
|
||||
|
||||
for (vd_t::cell_type cell : voronoi_diagram.cells())
|
||||
{
|
||||
// Try to detect cases when some Voronoi vertex is missing.
|
||||
// When any Voronoi vertex is missing, rotate input polygon and try again.
|
||||
const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments);
|
||||
const double fix_angle = PI / 6;
|
||||
std::unordered_map<Point, Point, PointHash> vertex_mapping;
|
||||
Polygons polys_copy = polys;
|
||||
if (has_missing_voronoi_vertex) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth.";
|
||||
for (Polygon &poly : polys_copy)
|
||||
poly.rotate(fix_angle);
|
||||
|
||||
assert(polys_copy.size() == polys.size());
|
||||
for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) {
|
||||
assert(polys_copy[poly_idx].size() == polys[poly_idx].size());
|
||||
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx)
|
||||
vertex_mapping.insert({polys[poly_idx][point_idx], polys_copy[poly_idx][point_idx]});
|
||||
}
|
||||
|
||||
segments.clear();
|
||||
for (size_t poly_idx = 0; poly_idx < polys_copy.size(); poly_idx++)
|
||||
for (size_t point_idx = 0; point_idx < polys_copy[poly_idx].size(); point_idx++)
|
||||
segments.emplace_back(&polys_copy, poly_idx, point_idx);
|
||||
|
||||
voronoi_diagram.clear();
|
||||
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
|
||||
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
|
||||
if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
|
||||
BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input.";
|
||||
}
|
||||
|
||||
for (vd_t::cell_type cell : voronoi_diagram.cells()) {
|
||||
if (!cell.incident_edge())
|
||||
continue; // There is no spoon
|
||||
|
||||
|
@ -455,6 +530,16 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
|||
prev_edge->to->data.distance_to_boundary = 0;
|
||||
}
|
||||
|
||||
if (has_missing_voronoi_vertex) {
|
||||
for (node_t &node : graph.nodes) {
|
||||
// If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
|
||||
if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end())
|
||||
node.p = node_it->second;
|
||||
else
|
||||
node.p.rotate(-fix_angle);
|
||||
}
|
||||
}
|
||||
|
||||
separatePointyQuadEndNodes();
|
||||
|
||||
graph.fixNodeDuplication();
|
||||
|
|
Loading…
Reference in a new issue