Add monotone chain simple polygon triangulation
This commit is contained in:
parent
2e44978ff3
commit
b2a714f88a
1 changed files with 360 additions and 0 deletions
360
libs/cg/include/psemek/cg/triangulation/monotone.hpp
Normal file
360
libs/cg/include/psemek/cg/triangulation/monotone.hpp
Normal file
|
|
@ -0,0 +1,360 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/geom/point.hpp>
|
||||||
|
#include <psemek/geom/orientation.hpp>
|
||||||
|
#include <psemek/geom/contains.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Iterator, typename OutputIterator, typename IndexType = std::size_t>
|
||||||
|
void monotone_triangulation(Iterator begin, Iterator end, OutputIterator out)
|
||||||
|
{
|
||||||
|
IndexType const count = std::distance(begin, end);
|
||||||
|
IndexType const null = IndexType(-1);
|
||||||
|
|
||||||
|
auto at = [begin](auto i){ return *(begin + i); };
|
||||||
|
|
||||||
|
// Initialize dcel
|
||||||
|
|
||||||
|
std::vector<IndexType> start(count, 0);
|
||||||
|
std::vector<IndexType> next(count, 0);
|
||||||
|
std::vector<IndexType> prev(count, 0);
|
||||||
|
std::vector<IndexType> twin(count, 0);
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
start[i] = i;
|
||||||
|
next[i] = (i + 1) % count;
|
||||||
|
prev[i] = (i + count - 1) % count;
|
||||||
|
twin[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto finish = [&](auto i)
|
||||||
|
{
|
||||||
|
return start[next[i]];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vertex_id
|
||||||
|
{
|
||||||
|
IndexType index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sweepline_compare
|
||||||
|
{
|
||||||
|
struct is_transparent{};
|
||||||
|
|
||||||
|
Iterator begin;
|
||||||
|
IndexType count;
|
||||||
|
|
||||||
|
auto const & at(IndexType i) const
|
||||||
|
{
|
||||||
|
return *(begin + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(IndexType i, IndexType j) const
|
||||||
|
{
|
||||||
|
auto const i0 = at(i);
|
||||||
|
auto const i1 = at((i + 1) % count);
|
||||||
|
auto const j0 = at(j);
|
||||||
|
auto const j1 = at((j + 1) % count);
|
||||||
|
|
||||||
|
auto const oi0 = geom::orientation(i0, i1, j0);
|
||||||
|
auto const oi1 = geom::orientation(i0, i1, j1);
|
||||||
|
|
||||||
|
if (oi0 == oi1)
|
||||||
|
return oi0 == geom::sign_t::positive;
|
||||||
|
|
||||||
|
return geom::orientation(j0, j1, i0) == geom::sign_t::negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(IndexType e, vertex_id const & v) const
|
||||||
|
{
|
||||||
|
return geom::orientation(at(e), at((e + 1) % count), at(v.index)) == geom::sign_t::positive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(vertex_id const & v, IndexType e) const
|
||||||
|
{
|
||||||
|
return geom::orientation(at(e), at((e + 1) % count), at(v.index)) == geom::sign_t::negative;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Edges intersecting sweepline
|
||||||
|
std::set<IndexType, sweepline_compare> sweepline(sweepline_compare{begin, count});
|
||||||
|
|
||||||
|
std::vector<IndexType> events(count, 0);
|
||||||
|
std::vector<IndexType> helper(count, 0);
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < count; ++i)
|
||||||
|
events[i] = i;
|
||||||
|
|
||||||
|
std::sort(events.begin(), events.end(), [&](auto i, auto j){ return at(i) < at(j); });
|
||||||
|
|
||||||
|
enum class event_type
|
||||||
|
{
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
split,
|
||||||
|
merge,
|
||||||
|
regular,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto classify = [&](auto e) -> event_type
|
||||||
|
{
|
||||||
|
auto p = (e + count - 1) % count;
|
||||||
|
auto n = (e + 1) % count;
|
||||||
|
|
||||||
|
bool lp = at(p) < at(e);
|
||||||
|
bool ln = at(n) < at(e);
|
||||||
|
|
||||||
|
auto s = geom::orientation(at(p), at(e), at(n));
|
||||||
|
|
||||||
|
if (!lp && !ln && s == geom::sign_t::positive)
|
||||||
|
return event_type::start;
|
||||||
|
else if (lp && ln && s == geom::sign_t::positive)
|
||||||
|
return event_type::end;
|
||||||
|
else if (!lp && !ln && s == geom::sign_t::negative)
|
||||||
|
return event_type::split;
|
||||||
|
else if (lp && ln && s == geom::sign_t::negative)
|
||||||
|
return event_type::merge;
|
||||||
|
else
|
||||||
|
return event_type::regular;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto connect = [&](auto i, auto j)
|
||||||
|
{
|
||||||
|
auto const vi = at(i);
|
||||||
|
auto const vj = at(j);
|
||||||
|
|
||||||
|
auto ni = i;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto f = at(finish(ni));
|
||||||
|
auto p = at(start[prev[ni]]);
|
||||||
|
bool contains;
|
||||||
|
if (geom::orientation(f, vi, p) == geom::sign_t::negative)
|
||||||
|
contains = geom::orientation(f, vi, vj) == geom::sign_t::negative && geom::orientation(vj, vi, p) == geom::sign_t::negative;
|
||||||
|
else
|
||||||
|
contains = geom::orientation(f, vi, vj) == geom::sign_t::negative || geom::orientation(vj, vi, p) == geom::sign_t::negative;
|
||||||
|
if (contains)
|
||||||
|
break;
|
||||||
|
ni = twin[prev[ni]];
|
||||||
|
}
|
||||||
|
auto pi = prev[ni];
|
||||||
|
|
||||||
|
auto nj = j;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto f = at(finish(nj));
|
||||||
|
auto p = at(start[prev[nj]]);
|
||||||
|
bool contains;
|
||||||
|
if (geom::orientation(f, vj, p) == geom::sign_t::negative)
|
||||||
|
contains = geom::orientation(f, vj, vi) == geom::sign_t::negative && geom::orientation(vi, vj, p) == geom::sign_t::negative;
|
||||||
|
else
|
||||||
|
contains = geom::orientation(f, vj, vi) == geom::sign_t::negative || geom::orientation(vi, vj, p) == geom::sign_t::negative;
|
||||||
|
if (contains)
|
||||||
|
break;
|
||||||
|
nj = twin[prev[nj]];
|
||||||
|
}
|
||||||
|
auto pj = prev[nj];
|
||||||
|
|
||||||
|
IndexType eij = start.size();
|
||||||
|
IndexType eji = eij + 1;
|
||||||
|
|
||||||
|
start.emplace_back();
|
||||||
|
start.emplace_back();
|
||||||
|
next.emplace_back();
|
||||||
|
next.emplace_back();
|
||||||
|
prev.emplace_back();
|
||||||
|
prev.emplace_back();
|
||||||
|
twin.emplace_back();
|
||||||
|
twin.emplace_back();
|
||||||
|
|
||||||
|
start[eij] = i;
|
||||||
|
next[eij] = nj;
|
||||||
|
prev[eij] = pi;
|
||||||
|
twin[eij] = eji;
|
||||||
|
|
||||||
|
start[eji] = j;
|
||||||
|
next[eji] = ni;
|
||||||
|
prev[eji] = pj;
|
||||||
|
twin[eji] = eij;
|
||||||
|
|
||||||
|
next[pi] = eij;
|
||||||
|
prev[nj] = eij;
|
||||||
|
|
||||||
|
next[pj] = eji;
|
||||||
|
prev[ni] = eji;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partition into monotone parts
|
||||||
|
for (auto e : events)
|
||||||
|
{
|
||||||
|
auto p = (e + count - 1) % count;
|
||||||
|
|
||||||
|
switch (classify(e))
|
||||||
|
{
|
||||||
|
case event_type::start:
|
||||||
|
sweepline.insert(e);
|
||||||
|
helper[e] = e;
|
||||||
|
break;
|
||||||
|
case event_type::end:
|
||||||
|
if (auto h = helper[p]; classify(h) == event_type::merge)
|
||||||
|
connect(e, h);
|
||||||
|
sweepline.erase(p);
|
||||||
|
break;
|
||||||
|
case event_type::split:
|
||||||
|
{
|
||||||
|
auto l = *std::prev(sweepline.upper_bound(vertex_id{e}));
|
||||||
|
connect(e, helper[l]);
|
||||||
|
helper[l] = e;
|
||||||
|
}
|
||||||
|
sweepline.insert(e);
|
||||||
|
helper[e] = e;
|
||||||
|
break;
|
||||||
|
case event_type::merge:
|
||||||
|
if (auto h = helper[p]; classify(h) == event_type::merge)
|
||||||
|
connect(e, h);
|
||||||
|
sweepline.erase(p);
|
||||||
|
{
|
||||||
|
auto l = *std::prev(sweepline.upper_bound(vertex_id{e}));
|
||||||
|
if (auto h = helper[l]; classify(h) == event_type::merge)
|
||||||
|
connect(e, h);
|
||||||
|
helper[l] = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case event_type::regular:
|
||||||
|
if (at(e) < at((e + 1) % count))
|
||||||
|
{
|
||||||
|
if (auto h = helper[p]; classify(h) == event_type::merge)
|
||||||
|
connect(e, h);
|
||||||
|
sweepline.erase(p);
|
||||||
|
sweepline.insert(e);
|
||||||
|
helper[e] = e;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto l = *std::prev(sweepline.upper_bound(vertex_id{e}));
|
||||||
|
if (auto h = helper[l]; classify(h) == event_type::merge)
|
||||||
|
connect(e, h);
|
||||||
|
helper[l] = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangulate monotone parts
|
||||||
|
std::vector<bool> used(start.size(), false);
|
||||||
|
std::vector<IndexType> stack;
|
||||||
|
for (IndexType e = 0; e < start.size(); ++e)
|
||||||
|
{
|
||||||
|
if (used[e]) continue;
|
||||||
|
|
||||||
|
auto le = e;
|
||||||
|
used[e] = true;
|
||||||
|
// Mark the whole part as used and find leftmost point
|
||||||
|
for (auto ee = next[e]; ee != e; ee = next[ee])
|
||||||
|
{
|
||||||
|
used[ee] = true;
|
||||||
|
if (at(start[ee]) < at(start[le]))
|
||||||
|
le = ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stack_left = true;
|
||||||
|
|
||||||
|
auto re = prev[le];
|
||||||
|
stack.push_back(start[le]);
|
||||||
|
le = next[le];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
bool last = (le == re);
|
||||||
|
|
||||||
|
IndexType v;
|
||||||
|
bool left;
|
||||||
|
if (at(start[le]) < at(start[re]))
|
||||||
|
{
|
||||||
|
v = start[le];
|
||||||
|
le = next[le];
|
||||||
|
left = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v = start[re];
|
||||||
|
re = prev[re];
|
||||||
|
left = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left && stack_left)
|
||||||
|
{
|
||||||
|
while (stack.size() >= 2 && geom::orientation(at(v), at(stack.back()), at(stack[stack.size() - 2])) == geom::sign_t::negative)
|
||||||
|
{
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = stack[stack.size() - 2];
|
||||||
|
*out++ = stack.back();
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
stack.push_back(v);
|
||||||
|
}
|
||||||
|
else if (!left && !stack_left)
|
||||||
|
{
|
||||||
|
while (stack.size() >= 2 && geom::orientation(at(v), at(stack.back()), at(stack[stack.size() - 2])) == geom::sign_t::positive)
|
||||||
|
{
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = stack.back();
|
||||||
|
*out++ = stack[stack.size() - 2];
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
stack.push_back(v);
|
||||||
|
}
|
||||||
|
else if (left && !stack_left)
|
||||||
|
{
|
||||||
|
auto last = stack.back();
|
||||||
|
while (stack.size() >= 2)
|
||||||
|
{
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = stack.back();
|
||||||
|
*out++ = stack[stack.size() - 2];
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
stack.pop_back();
|
||||||
|
stack.push_back(last);
|
||||||
|
stack.push_back(v);
|
||||||
|
}
|
||||||
|
else if (!left && stack_left)
|
||||||
|
{
|
||||||
|
auto last = stack.back();
|
||||||
|
while (stack.size() >= 2)
|
||||||
|
{
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = stack[stack.size() - 2];
|
||||||
|
*out++ = stack.back();
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
stack.pop_back();
|
||||||
|
stack.push_back(last);
|
||||||
|
stack.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_left = left;
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IndexType, typename Iterator>
|
||||||
|
std::vector<IndexType> monotone_triangulation(Iterator begin, Iterator end)
|
||||||
|
{
|
||||||
|
std::vector<IndexType> result;
|
||||||
|
monotone_triangulation<Iterator, std::back_insert_iterator<std::vector<IndexType>>, IndexType>(begin, end, std::back_inserter(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue