PrusaSlicer-NonPlainar/src/libslic3r/MutablePolygon.hpp

377 lines
15 KiB
C++

#ifndef slic3r_MutablePolygon_hpp_
#define slic3r_MutablePolygon_hpp_
#include "Point.hpp"
#include "Polygon.hpp"
#include "ExPolygon.hpp"
namespace Slic3r {
// Polygon implemented as a loop of double linked elements.
// All elements are allocated in a single std::vector<>, thus integer indices are used for
// referencing the previous and next element and inside iterators to survive reallocation
// of the vector.
class MutablePolygon
{
public:
using IndexType = int32_t;
using PointType = Point;
class const_iterator {
public:
bool operator==(const const_iterator &rhs) const { assert(m_data == rhs.m_data); assert(this->valid()); return m_idx == rhs.m_idx; }
bool operator!=(const const_iterator &rhs) const { return ! (*this == rhs); }
const_iterator& operator--() { assert(this->valid()); m_idx = m_data->at(m_idx).prev; return *this; }
const_iterator operator--(int) { const_iterator result(*this); --(*this); return result; }
const_iterator& operator++() { assert(this->valid()); m_idx = m_data->at(m_idx).next; return *this; }
const_iterator operator++(int) { const_iterator result(*this); ++(*this); return result; }
const_iterator prev() const { assert(this->valid()); return { m_data, m_data->at(m_idx).prev }; }
const_iterator next() const { assert(this->valid()); return { m_data, m_data->at(m_idx).next }; }
bool valid() const { return m_idx >= 0; }
const PointType& operator*() const { return m_data->at(m_idx).point; }
const PointType* operator->() const { return &m_data->at(m_idx).point; }
const MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
IndexType size() const { assert(this->valid()); return m_data->size(); }
private:
const_iterator(const MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
friend class MutablePolygon;
const MutablePolygon *m_data;
IndexType m_idx;
};
class iterator {
public:
bool operator==(const iterator &rhs) const { assert(m_data == rhs.m_data); assert(this->valid()); return m_idx == rhs.m_idx; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
iterator& operator--() { assert(this->valid()); m_idx = m_data->at(m_idx).prev; return *this; }
iterator operator--(int) { iterator result(*this); --(*this); return result; }
iterator& operator++() { assert(this->valid()); m_idx = m_data->at(m_idx).next; return *this; }
iterator operator++(int) { iterator result(*this); ++(*this); return result; }
iterator prev() const { assert(this->valid()); return { m_data, m_data->at(m_idx).prev }; }
iterator next() const { assert(this->valid()); return { m_data, m_data->at(m_idx).next }; }
bool valid() const { return m_idx >= 0; }
PointType& operator*() const { return m_data->at(m_idx).point; }
PointType* operator->() const { return &m_data->at(m_idx).point; }
MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
IndexType size() const { assert(this->valid()); return m_data->size(); }
iterator& remove() { m_idx = m_data->remove(*this).m_idx; return *this; }
iterator insert(const PointType pt) const { return m_data->insert(*this, pt); }
private:
iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
friend class MutablePolygon;
MutablePolygon *m_data;
IndexType m_idx;
friend class range;
};
// Iterator range for maintaining a range of unprocessed items, see smooth_outward().
class range
{
public:
range(MutablePolygon& poly) : range(poly.begin(), poly.end()) {}
range(MutablePolygon::iterator begin, MutablePolygon::iterator end) : m_begin(begin), m_end(end) {}
// Start of a range, inclusive. If range is empty, then ! begin().valid().
MutablePolygon::iterator begin() const { return m_begin; }
// End of a range, inclusive. If range is empty, then ! end().valid().
MutablePolygon::iterator end() const { return m_end; }
// Is the range empty?
bool empty() const { return !m_begin.valid(); }
// Return begin() and shorten the range by advancing front.
MutablePolygon::iterator process_next() {
assert(!this->empty());
MutablePolygon::iterator out = m_begin;
this->advance_front();
return out;
}
void advance_front() {
assert(! this->empty());
if (m_begin == m_end)
this->make_empty();
else
++ m_begin;
}
void retract_back() {
assert(! this->empty());
if (m_begin == m_end)
this->make_empty();
else
-- m_end;
}
MutablePolygon::iterator remove_front(MutablePolygon::iterator it) {
if (! this->empty() && m_begin == it)
this->advance_front();
return it.remove();
}
MutablePolygon::iterator remove_back(MutablePolygon::iterator it) {
if (! this->empty() && m_end == it)
this->retract_back();
return it.remove();
}
private:
// Range from begin to end, inclusive.
// If the range is valid, then both m_begin and m_end are invalid.
MutablePolygon::iterator m_begin;
MutablePolygon::iterator m_end;
void make_empty() {
m_begin.m_idx = -1;
m_end.m_idx = -1;
}
};
MutablePolygon() = default;
MutablePolygon(const Polygon &rhs, size_t reserve = 0) : MutablePolygon(rhs.points.begin(), rhs.points.end(), reserve) {}
MutablePolygon(std::initializer_list<Point> rhs, size_t reserve = 0) : MutablePolygon(rhs.begin(), rhs.end(), reserve) {}
template<typename IT>
MutablePolygon(IT begin, IT end, size_t reserve = 0) {
this->assign_inner(begin, end, reserve);
};
template<typename IT>
void assign(IT begin, IT end, size_t reserve = 0) {
m_data.clear();
m_head = IndexType(-1);
m_head_free = { IndexType(-1) };
this->assign_inner(begin, end, reserve);
};
void assign(const Polygon &rhs, size_t reserve = 0) {
assign(rhs.points.begin(), rhs.points.end(), reserve);
}
void polygon(Polygon &out) const {
out.points.clear();
if (this->valid()) {
out.points.reserve(this->size());
auto it = this->cbegin();
out.points.emplace_back(*it);
for (++ it; it != this->cbegin(); ++ it)
out.points.emplace_back(*it);
}
};
Polygon polygon() const {
Polygon out;
this->polygon(out);
return out;
};
bool empty() const { return m_size == 0; }
size_t size() const { return m_size; }
size_t capacity() const { return m_data.capacity(); }
bool valid() const { return m_size >= 3; }
void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); }
iterator begin() { return { this, m_head }; }
const_iterator cbegin() const { return { this, m_head }; }
const_iterator begin() const { return this->cbegin(); }
// End points to the last item before roll over. This is different from the usual end() concept!
iterator end() { return { this, this->empty() ? -1 : this->at(m_head).prev }; }
const_iterator cend() const { return { this, this->empty() ? -1 : this->at(m_head).prev }; }
const_iterator end() const { return this->cend(); }
// Returns iterator following the removed element. Returned iterator will become invalid if last point is removed.
// If begin() is removed, then the next element will become the new begin().
iterator remove(const iterator it) { assert(it.m_data == this); return { this, this->remove(it.m_idx) }; }
// Insert a new point before it. Returns iterator to the newly inserted point.
// begin() will not change, end() may point to the newly inserted point.
iterator insert(const iterator it, const PointType pt) { assert(it.m_data == this); return { this, this->insert(it.m_idx, pt) }; }
private:
struct LinkedPoint {
// 8 bytes
PointType point;
// 4 bytes
IndexType prev;
// 4 bytes
IndexType next;
};
std::vector<LinkedPoint> m_data;
// Number of points in the linked list.
IndexType m_size { 0 };
IndexType m_head { IndexType(-1) };
// Head of the free list.
IndexType m_head_free { IndexType(-1) };
LinkedPoint& at(IndexType i) { return m_data[i]; }
const LinkedPoint& at(IndexType i) const { return m_data[i]; }
template<typename IT>
void assign_inner(IT begin, IT end, size_t reserve) {
m_size = IndexType(end - begin);
if (m_size > 0) {
m_head = 0;
m_data.reserve(std::max<size_t>(m_size, reserve));
auto i = IndexType(-1);
auto j = IndexType(1);
for (auto it = begin; it != end; ++ it)
m_data.push_back({ *it, i ++, j ++ });
m_data.front().prev = m_size - 1;
m_data.back ().next = 0;
}
};
IndexType remove(const IndexType i) {
assert(i >= 0);
assert(m_size > 0);
assert(m_head != -1);
LinkedPoint &lp = this->at(i);
IndexType prev = lp.prev;
IndexType next = lp.next;
lp.next = m_head_free;
m_head_free = i;
if (-- m_size == 0)
m_head = -1;
else if (m_head == i)
m_head = next;
assert(! this->empty() || (prev == i && next == i));
if (this->empty())
return IndexType(-1);
this->at(prev).next = next;
this->at(next).prev = prev;
return next;
}
IndexType insert(const IndexType i, const Point pt) {
assert(i >= 0);
IndexType n;
IndexType j = this->at(i).prev;
if (m_head_free == -1) {
// Allocate a new item.
n = IndexType(m_data.size());
m_data.push_back({ pt, j, i });
} else {
n = m_head_free;
LinkedPoint &nlp = this->at(n);
m_head_free = nlp.next;
nlp = { pt, j, i };
}
this->at(j).next = n;
this->at(i).prev = n;
++ m_size;
return n;
}
/*
IndexType insert(const IndexType i, const Point pt) {
assert(i >= 0);
if (this->at(i).point == pt)
return i;
IndexType j = this->at(i).next;
if (this->at(j).point == pt)
return i;
IndexType n;
if (m_head_free == -1) {
// Allocate a new item.
n = IndexType(m_data.size());
m_data.push_back({ pt, i, j });
} else {
LinkedPoint &nlp = this->at(m_head_free);
m_head_free = nlp.next;
nlp = { pt, i, j };
}
this->at(i).next = n;
this->at(j).prev = n;
++ m_size;
return n;
}
*/
};
inline bool operator==(const MutablePolygon &p1, const MutablePolygon &p2)
{
if (p1.size() != p2.size())
return false;
if (p1.empty())
return true;
auto begin = p1.cbegin();
auto it = begin;
auto it2 = p2.cbegin();
for (;;) {
if (! (*it == *it2))
return false;
if (++ it == begin)
return true;
++ it2;
}
}
inline bool operator!=(const MutablePolygon &p1, const MutablePolygon &p2) { return ! (p1 == p2); }
// Remove exact duplicate points. May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon);
void remove_duplicates(MutablePolygon &polygon, double eps);
// Remove nearly duplicate points. If a distance between two points is less than scaled_eps
// and if the angle between its surrounding lines is less than max_angle, the point will be removed.
// May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon, coord_t scaled_eps, const double max_angle);
inline ExPolygons remove_duplicates(ExPolygons expolygons, coord_t scaled_eps, double max_angle)
{
MutablePolygon mp;
for (ExPolygon &expolygon : expolygons) {
mp.assign(expolygon.contour, expolygon.contour.size() * 2);
remove_duplicates(mp, scaled_eps, max_angle);
mp.polygon(expolygon.contour);
for (Polygon &hole : expolygon.holes) {
mp.assign(hole, hole.size() * 2);
remove_duplicates(mp, scaled_eps, max_angle);
mp.polygon(hole);
}
expolygon.holes.erase(std::remove_if(expolygon.holes.begin(), expolygon.holes.end(), [](const auto &p) { return p.empty(); }), expolygon.holes.end());
}
expolygons.erase(std::remove_if(expolygons.begin(), expolygons.end(), [](const auto &p) { return p.empty(); }), expolygons.end());
return expolygons;
}
void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled);
inline Polygon smooth_outward(Polygon polygon, coord_t clip_dist_scaled)
{
MutablePolygon mp(polygon, polygon.size() * 2);
smooth_outward(mp, clip_dist_scaled);
mp.polygon(polygon);
return polygon;
}
inline Polygons smooth_outward(Polygons polygons, coord_t clip_dist_scaled)
{
MutablePolygon mp;
for (Polygon &polygon : polygons) {
mp.assign(polygon, polygon.size() * 2);
smooth_outward(mp, clip_dist_scaled);
mp.polygon(polygon);
}
polygons.erase(std::remove_if(polygons.begin(), polygons.end(), [](const auto &p){ return p.empty(); }), polygons.end());
return polygons;
}
inline ExPolygons smooth_outward(ExPolygons expolygons, coord_t clip_dist_scaled)
{
MutablePolygon mp;
for (ExPolygon &expolygon : expolygons) {
mp.assign(expolygon.contour, expolygon.contour.size() * 2);
smooth_outward(mp, clip_dist_scaled);
mp.polygon(expolygon.contour);
for (Polygon &hole : expolygon.holes) {
mp.assign(hole, hole.size() * 2);
smooth_outward(mp, clip_dist_scaled);
mp.polygon(hole);
}
expolygon.holes.erase(std::remove_if(expolygon.holes.begin(), expolygon.holes.end(), [](const auto &p) { return p.empty(); }), expolygon.holes.end());
}
expolygons.erase(std::remove_if(expolygons.begin(), expolygons.end(), [](const auto &p) { return p.empty(); }), expolygons.end());
return expolygons;
}
}
#endif // slic3r_MutablePolygon_hpp_