194 lines
5.2 KiB
C++
194 lines
5.2 KiB
C++
#pragma once
|
|
|
|
#include <psemek/geom/gauss.hpp>
|
|
#include <psemek/cg/triangulation/delaunay.hpp>
|
|
#include <psemek/cg/dual.hpp>
|
|
#include <psemek/cg/bbox.hpp>
|
|
|
|
namespace psemek::cg
|
|
{
|
|
|
|
// Point #0 of the resulting dcel corresponds to the outer face,
|
|
// i.e. the point at infinity, and has unspecified coordinates.
|
|
template <typename Index = std::size_t, typename Iterator>
|
|
auto voronoi(Iterator begin, Iterator end)
|
|
{
|
|
using point_type = std::decay_t<decltype(*begin)>;
|
|
|
|
auto del = dual(delaunay<Index>(begin, end));
|
|
|
|
dcel<point_type, util::empty, Iterator, Index> result;
|
|
|
|
result.points.reserve(del.points.size());
|
|
for (std::size_t i = 0; i < del.points.size(); ++i)
|
|
{
|
|
auto p = del.point(i);
|
|
|
|
point_type q[3];
|
|
|
|
auto e = p.edge();
|
|
q[0] = *e.face().data();
|
|
e = e.twin().next();
|
|
q[1] = *e.face().data();
|
|
e = e.twin().next();
|
|
q[2] = *e.face().data();
|
|
|
|
using T = typename point_type::scalar_type;
|
|
|
|
geom::matrix<T, 3, 3> m;
|
|
geom::vector<T, 3> b;
|
|
|
|
for (std::size_t i = 0; i < 3; ++i)
|
|
{
|
|
m[i][0] = q[i][0];
|
|
m[i][1] = q[i][1];
|
|
m[i][2] = T{1};
|
|
|
|
b[i] = q[i][0] * q[i][0] + q[i][1] * q[i][1];
|
|
}
|
|
|
|
geom::gauss(m, b);
|
|
|
|
auto newp = result.push_point(point_type{b[0] / 2, b[1] / 2});
|
|
newp.edge(result.edge(p.edge().index()));
|
|
}
|
|
|
|
result.edges = std::move(del.edges);
|
|
result.faces = std::move(del.faces);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Replaces the #0 infinite point of a dcel with a proper outer face
|
|
template <typename Point, typename Face, typename Edge, typename Index>
|
|
void remove_infinite_point(dcel<Point, Face, Edge, Index> & dcel)
|
|
{
|
|
// insert outer face at position #0
|
|
auto outer_face = dcel.insert_face(0);
|
|
|
|
std::vector<std::size_t> delete_edges;
|
|
std::vector<std::size_t> delete_faces;
|
|
|
|
auto infinite_point = dcel.point(0);
|
|
|
|
// iterate edges of infinite vertex #0 and fix hull
|
|
for (auto e = infinite_point.edge();;)
|
|
{
|
|
for (auto n = e.next(); n.twin().origin() != infinite_point; n = n.next())
|
|
{
|
|
n.face(outer_face);
|
|
n.origin().edge(n);
|
|
if (!outer_face.edge())
|
|
outer_face.edge(n);
|
|
}
|
|
|
|
auto n = e;
|
|
for (; n.next() != e.twin(); n = n.next().twin());
|
|
|
|
if (n.origin() != infinite_point)
|
|
{
|
|
auto t = e.next();
|
|
for (; t.twin().origin() == infinite_point; t = t.twin().next());
|
|
|
|
n.next(t);
|
|
}
|
|
|
|
delete_faces.push_back(e.face().index());
|
|
delete_edges.push_back(e.index());
|
|
e = e.twin();
|
|
delete_edges.push_back(e.index());
|
|
e = e.next();
|
|
|
|
if (e == infinite_point.edge()) break;
|
|
}
|
|
|
|
// remove edges incident to infinite vertex #0
|
|
std::sort(delete_edges.begin(), delete_edges.end(), std::greater<>{});
|
|
for (auto e : delete_edges)
|
|
dcel.remove_edge(dcel.edge(e));
|
|
|
|
// remove vertex #0
|
|
dcel.remove_point(infinite_point);
|
|
|
|
std::sort(delete_faces.begin(), delete_faces.end(), std::greater<>{});
|
|
for (auto f : delete_faces)
|
|
dcel.remove_face(dcel.face(f));
|
|
}
|
|
|
|
// iteratively removes degenerate edges
|
|
template <typename Point, typename Face, typename Edge, typename Index>
|
|
void remove_degenerate_edges(dcel<Point, Face, Edge, Index> & dcel)
|
|
{
|
|
std::vector<std::size_t> delete_points;
|
|
std::vector<std::size_t> delete_edges;
|
|
|
|
for (std::size_t i = 0; i < dcel.points.size(); ++i)
|
|
{
|
|
auto p = dcel.point(i);
|
|
while (p.edge().twin().next() == p.edge())
|
|
{
|
|
delete_points.push_back(p.index());
|
|
delete_edges.push_back(p.edge().index());
|
|
delete_edges.push_back(p.edge().twin().index());
|
|
|
|
p.edge().next().origin().edge(p.edge().next());
|
|
p.edge().face().edge(p.edge().next());
|
|
|
|
auto n = p.edge();
|
|
while (true)
|
|
{
|
|
auto m = n.next().twin();
|
|
if (m == p.edge()) break;
|
|
n = m;
|
|
}
|
|
|
|
n.next(p.edge().next());
|
|
|
|
p = p.edge().next().origin();
|
|
}
|
|
}
|
|
|
|
std::sort(delete_points.begin(), delete_points.end(), std::greater<>{});
|
|
for (auto p : delete_points)
|
|
dcel.remove_point(dcel.point(p));
|
|
|
|
std::sort(delete_edges.begin(), delete_edges.end(), std::greater<>{});
|
|
for (auto e : delete_edges)
|
|
dcel.remove_edge(dcel.edge(e));
|
|
}
|
|
|
|
// Outputs 4 points such that the finite faces of the voronoi diagram of (anything
|
|
// inside the convex hull of input + the output points) covers the convex hull of input
|
|
template <typename InputIterator, typename OutputIterator>
|
|
OutputIterator bounded_voronoi_extra_points(InputIterator begin, InputIterator end, OutputIterator out)
|
|
{
|
|
using point_type = std::decay_t<decltype(*begin)>;
|
|
|
|
static_assert(point_type::dimension == 2);
|
|
|
|
auto box = bbox(begin, end);
|
|
|
|
point_type const center {(box[0].min + box[0].max) / 2, (box[1].min + box[1].max) / 2};
|
|
|
|
auto const R = std::sqrt(box[0].length() * box[0].length() + box[1].length() * box[1].length());
|
|
|
|
*out++ = point_type{center[0] + R, center[1]};
|
|
*out++ = point_type{center[0], center[1] + R};
|
|
*out++ = point_type{center[0] - R, center[1]};
|
|
*out++ = point_type{center[0], center[1] - R};
|
|
|
|
return out;
|
|
}
|
|
|
|
// Compute a clipped voronoi tessellation of (begin, end - 4)
|
|
// Last 4 points will be used for temporary data
|
|
template <typename InputIterator>
|
|
auto bounded_voronoi(InputIterator begin, InputIterator end)
|
|
{
|
|
bounded_voronoi_extra_points(begin, end - 4, end - 4);
|
|
auto dcel = cg::voronoi(begin, end);
|
|
cg::remove_infinite_point(dcel);
|
|
return dcel;
|
|
}
|
|
|
|
}
|