psemek/libs/ui/source/impl/size_polygon_utils.cpp
2023-05-06 12:55:06 +03:00

135 lines
3.6 KiB
C++

#include <psemek/ui/impl/size_polygon_utils.hpp>
#include <psemek/geom/math.hpp>
#include <psemek/util/not_implemented.hpp>
#include <psemek/util/cyclic_iterator.hpp>
namespace psemek::ui::impl
{
size_polygon shift(size_polygon polygon, geom::vector<float, 2> const & delta)
{
for (auto & p : polygon)
p += delta;
return polygon;
}
size_polygon min(size_polygon const & polygon, int dimension)
{
if (polygon.empty())
return {};
size_polygon result;
auto cit = util::make_cyclic_iterator(polygon);
std::size_t iterations = 0;
for (auto next = std::next(cit); (*next)[dimension] <= (*cit)[dimension] && iterations < polygon.size(); cit = next++, ++iterations);
if (iterations == polygon.size())
return polygon;
for (auto prev = std::prev(cit); (*prev)[dimension] <= (*cit)[dimension]; cit = prev--);
for (auto cjt = cit; (*cjt)[dimension] == (*cit)[dimension]; ++cjt)
result.push_back(*cjt);
return result;
}
size_polygon intersect(size_polygon const & polygon1, size_polygon const & polygon2)
{
// TODO: https://www.cs.jhu.edu/~misha/Spring16/ORourke82.pdf
(void)polygon1;
(void)polygon2;
util::not_implemented();
}
geom::interval<float> range(size_polygon const & polygon, int dimension)
{
geom::interval<float> result;
for (auto const & p : polygon)
result |= p[dimension];
return result;
}
size_polygon cut(size_polygon const & polygon, int dimension, geom::interval<float> const & range)
{
size_polygon result;
std::size_t iterations = 0;
for (auto it = util::make_cyclic_iterator(polygon), next_it = std::next(it); iterations != polygon.size(); (it = next_it++), ++iterations)
{
auto const & curr = *it;
auto const & next = *next_it;
auto intersection = [&](float value)
{
// curr + (next - curr) * t = value
float t = (value - curr[dimension]) / (next[dimension] - curr[dimension]);
geom::point<float, 2> p;
p[dimension] = value;
p[dimension ^ 1] = geom::lerp(curr[dimension ^ 1], next[dimension ^ 1], t);
return p;
};
auto classify = [&](float value)
{
if (value < range.min)
return -2;
else if (value == range.min)
return -1;
else if (value < range.max)
return 0;
else if (value == range.max)
return 1;
else // (value > range.max)
return 2;
};
int const curr_class = classify(curr[dimension]);
int const next_class = classify(next[dimension]);
if (curr_class >= -1 && curr_class <= 1)
result.push_back(curr);
bool const min = (curr_class == -2 && next_class >= 0) || (curr_class >= 0 && next_class == -2);
bool const max = (curr_class == 2 && next_class <= 0) || (curr_class <= 0 && next_class == 2);
if (range.min == range.max)
{
if (min) result.push_back(intersection(range.min));
}
else
{
if (min) result.push_back(intersection(range.min));
if (max) result.push_back(intersection(range.max));
if (min && max && curr_class == 2)
std::swap(result.back(), result[result.size() - 2]);
}
}
return result;
}
size_polygon sum_along(size_polygon const & polygon1, size_polygon const & polygon2, int dimension)
{
if (polygon1.empty() || polygon2.empty())
return size_polygon{};
int const other_dimension = dimension ^ 1;
auto range1 = range(polygon1, other_dimension);
auto range2 = range(polygon2, other_dimension);
auto common_range = range1 & range2;
if (common_range.empty())
return size_polygon{};
auto cut1 = cut(polygon1, other_dimension, common_range);
auto cut2 = cut(polygon2, other_dimension, common_range);
util::not_implemented();
}
}