klayout/src/db/db/dbPolygon.h

3841 lines
101 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbPolygon
#define HDR_dbPolygon
#include "dbCommon.h"
#include "dbTypes.h"
#include "dbMemStatistics.h"
#include "dbPoint.h"
#include "dbTrans.h"
#include "dbEdge.h"
#include "dbBox.h"
#include "dbObjectTag.h"
#include "dbShapeRepository.h"
#include "tlTypeTraits.h"
#include "tlVector.h"
#include "tlAlgorithm.h"
#include "tlAssert.h"
#include <cstddef>
#include <cmath>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
namespace db {
template <class Coord> class generic_repository;
class ArrayRepository;
template <class Contour, class Tr> class polygon_contour_iterator;
// define the default compression mode:
// double coordinate polygons are not compressed - this is not beneficial
template<class X>
inline bool default_compression ()
{
return true;
}
template<>
inline bool default_compression<db::Coord> ()
{
return true;
}
template<>
inline bool default_compression<db::DCoord> ()
{
return false;
}
/**
* @brief A "closed" contour type
*
* A contour is a set of points that form a closed loop.
* Contours are stored "normalized", that is with the
* "smallest" point (as determined by the operator<) first
* and a clockwise or counter-clockwise orientation for
* hull and holes respectively.
*/
// NOTE: we do explicit instantiation, so the exposure is declared
// as DB_PUBLIC - as if it wasn't a template
template <class C>
class DB_PUBLIC polygon_contour
{
public:
typedef C coord_type;
typedef size_t size_type;
typedef db::coord_traits<coord_type> coord_traits;
typedef typename coord_traits::distance_type distance_type;
typedef typename coord_traits::perimeter_type perimeter_type;
typedef typename coord_traits::area_type area_type;
typedef db::point<C> point_type;
typedef db::vector<C> vector_type;
typedef point_type value_type;
typedef db::box<C> box_type;
typedef db::simple_trans<C> trans_type;
typedef tl::vector<point_type> container_type;
typedef typename container_type::const_iterator const_iterator;
typedef polygon_contour_iterator<polygon_contour, db::unit_trans<C> > simple_iterator;
private:
/**
* @brief A helper predicate function that returns true if p1-p2 is colinear with p2-p3
*/
static
bool is_colinear (const point_type &p1, const point_type &p2, const point_type &p3, bool remove_reflected)
{
if (db::coord_traits<C>::vprod_sign (p1.x (), p1.y (), p3.x (), p3.y (), p2.x (), p2.y ()) == 0) {
return remove_reflected || (db::coord_traits<C>::sprod_sign (p1.x (), p1.y (), p3.x (), p3.y (), p2.x (), p2.y ()) < 0);
} else {
return false;
}
}
public:
/**
* @brief Default ctor
*
* This ctor creates an empty contour.
*/
polygon_contour ()
: mp_points (0), m_size (0)
{
// .. nothing yet ..
}
/**
* @brief Copy ctor
*/
polygon_contour (const polygon_contour &d)
: m_size (d.m_size)
{
if (d.mp_points == 0) {
mp_points = 0;
} else {
point_type *p = new point_type [m_size];
point_type *pp = (point_type *) ((size_t) d.mp_points & ~3);
mp_points = (point_type *)((size_t) p | ((size_t) d.mp_points & 3));
for (unsigned int i = 0; i < m_size; ++i) {
p[i] = pp[i];
}
}
}
/**
* @brief Destructor
*/
~polygon_contour ()
{
release ();
}
/**
* @brief Assignment
*/
polygon_contour &operator= (const polygon_contour &d)
{
if (&d != this) {
release ();
new (this) polygon_contour (d);
}
return *this;
}
/**
* @brief Contour created from a sequence
*
* This ctor creates a contour from a given sequence (from,to].
* The contour is stored "normalized".
*
* @param from Begin of the sequence
* @param to End of the sequence
* @param hole true, if a hole is constructed, false for a hull
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*/
template <class Iter>
polygon_contour (Iter from, Iter to, bool hole, bool compress = default_compression<C> (), bool normalize = true, bool remove_reflected = false)
: mp_points (0), m_size (0)
{
assign (from, to, hole, compress, normalize, remove_reflected);
}
/**
* @brief Contour created from a sequence with transformation
*
* This ctor creates a contour from a given sequence (from,to], each point.
* transformed with the transformation "tr".
* The contour is stored "normalized".
*
* @param from Begin of the sequence
* @param to End of the sequence
* @param tr The transformation to apply
* @param hole true, if a hole is constructed, false for a hull
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*/
template <class Iter, class Trans>
polygon_contour (Iter from, Iter to, Trans tr, bool hole, bool compress = default_compression<C> (), bool normalize = true, bool remove_reflected = false)
: mp_points (0), m_size (0)
{
assign (from, to, tr, hole, compress, normalize, remove_reflected);
}
/**
* @brief Fill the contour from a sequence
*
* This ctor fills the contour with a given sequence (from,to].
* The contour is stored "normalized".
*
* @param from Begin of the sequence
* @param to End of the sequence
* @param hole true, if a hole is constructed, false for a hull
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*/
template <class Iter>
void assign (Iter from, Iter to, bool hole, bool compress = default_compression<C> (), bool normalize = true, bool remove_reflected = false)
{
assign (from, to, db::unit_trans<C> (), hole, compress, normalize, remove_reflected);
}
/**
* @brief Fill the contour from a sequence with transformation
*
* This ctor fills the contour with a given sequence (from,to], each point.
* transformed with the transformation "tr".
* The contour is stored "normalized".
*
* @param from Begin of the sequence
* @param to End of the sequence
* @param tr The transformation to apply
* @param hole true, if a hole is constructed, false for a hull
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param normalize true, if the sequence shall be normalized (oriented)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*/
template <class Iter, class Trans>
void assign (Iter from, Iter to, Trans tr, bool hole, bool compress = default_compression<C> (), bool normalize = true, bool remove_reflected = false)
{
if (compress && remove_reflected) {
// with less than 3 points there is nothing to do at all ..
if (std::distance (from, to) < 3) {
release ();
return;
}
// the remove_reflected case is somewhat more complicated: it may happen that bends vanish because the
// next edge is member of a spike edge pair. Thus the simple single-pass approach no longer holds.
// We therefore need to create an intermediate buffer that is used to eliminate redundant points successively.
std::vector<point_type> points;
points.reserve (std::distance (from, to));
for (Iter i = from; i != to; ++i) {
points.push_back (point_type (tr (*i)));
}
bool any_skipped = true;
while (any_skipped) {
typename std::vector<point_type>::iterator w = points.begin ();
typename std::vector<point_type>::const_iterator p = points.begin ();
point_type plast (points.back ());
point_type pcurr (points.front ());
point_type pnext;
any_skipped = false;
do {
if (++p == points.end ()) {
p = points.begin ();
}
pnext = *p;
if (pcurr == plast || pcurr == pnext || is_colinear (plast, pcurr, pnext, remove_reflected)) {
// skip this point ..
any_skipped = true;
} else {
// we have a point to consider
*w++ = pcurr;
plast = pcurr;
}
pcurr = pnext;
} while (p != points.begin ());
points.erase (w, points.end ());
// if not enough points remain, stop here.
if (points.size () < 3) {
release ();
return;
}
}
// Do the normal assignment now.
assign (points.begin (), points.end (), db::unit_trans<C> (), hole, compress, normalize, false);
} else if (! compress) {
release ();
// on empty source there is nothing to do
if (from == to) {
return;
}
// count distinct points, determine minimum and if the contour is manhattan
size_type n = 0;
// count distinct points and determine minimum
Iter p = from;
bool min_set = false;
point_type pmin;
Iter min = p;
while (p != to) {
// we have a point to consider
++n;
point_type pcurr = point_type (tr (*p));
// determine min point and corresponding iterator
if (! min_set || pcurr < pmin) {
pmin = pcurr;
min = p;
min_set = true;
}
++p;
}
point_type *pts;
m_size = n;
pts = new point_type [m_size];
// copy distinct points now
p = min;
for (n = 0; n < m_size; ++n) {
pts [n] = point_type (tr (*p));
if (++p == to) {
p = from;
}
}
// normalize the orientation if required
if (normalize) {
area_type a = 0;
point_type pl = pts[m_size - 1];
const point_type *p = pts;
for (size_type i = 0; i < m_size; ++i, ++p) {
a += vprod (pl - point_type (), *p - point_type ());
pl = *p;
}
bool clockwise = (a < 0);
if ((! clockwise) != hole) {
std::reverse (pts + 1, pts + n);
}
}
// and store the pointer along with the hole flag
tl_assert (((size_t) pts & 3) == 0);
mp_points = (point_type *) ((size_t) pts | (hole ? 2 : 0));
} else {
release ();
// with less than 3 points there is nothing to do at all ..
if (std::distance (from, to) < 3) {
return;
}
// count distinct points, determine minimum and if the contour is manhattan
bool ortho = normalize; // don't compress manhattan sequences if not normalizing - normalisation is a prerequisite for compression!
size_type n = 0;
// count distinct points and determine minimum
Iter p = from;
point_type plast (tr (*p++));
Iter pp = p;
point_type pcurr (tr (*p++));
if (pcurr.equal (plast)) {
do {
pp = p;
pcurr = point_type (tr (*p++));
} while (pcurr.equal (plast) && p != to);
if (p == to) {
return;
}
}
point_type pnext;
bool min_set = false;
bool one_round = false;
point_type pmin;
Iter min = p;
Iter pstop = to;
do {
pnext = point_type (tr (*p));
if (pcurr.equal (plast) || pcurr.equal (pnext) || is_colinear (plast, pcurr, pnext, remove_reflected)) {
// skip this point ..
} else {
if (one_round) {
if (pp == pstop) {
// we found the closing point - stop now.
break;
}
} else if (pstop == to) {
pstop = pp;
}
// we have a point to consider
++n;
// test, if the contour is manhattan
// there is a strict criterion what is "manhattan": only such segment pairs
// that really bend by 90 degree are recognized as manhattan.
if (ortho && !(( coord_traits::equals (plast.x (), pcurr.x ()) && ! coord_traits::equals (plast.y (), pcurr.y ()) &&
! coord_traits::equals (pcurr.x (), pnext.x ()) && coord_traits::equals (pcurr.y (), pnext.y ())) ||
(! coord_traits::equals (plast.x (), pcurr.x ()) && coord_traits::equals (plast.y (), pcurr.y ()) &&
coord_traits::equals (pcurr.x (), pnext.x ()) && ! coord_traits::equals (pcurr.y (), pnext.y ())))) {
// non-manhattan
ortho = false;
}
// determine min point and corresponding iterator
if (! min_set || pcurr < pmin) {
pmin = pcurr;
min = pp;
min_set = true;
}
plast = pcurr;
}
pcurr = pnext;
pp = p;
if (++p == to) {
p = from;
}
if (pp == from) {
if (one_round) {
// stop on second round
return;
}
one_round = true;
}
} while (true);
// if there are less than 3 points remaining, stop now
if (n < 3) {
return;
}
point_type *pts;
bool clockwise;
// in ortho mode, allocate only half the number of points and compress
if (ortho) {
tl_assert ((n % 2) == 0);
m_size = n / 2;
pts = new point_type [m_size];
// determine orientation:
// it is that simple since we know that the segments attached to
// the min point can only point to positive x or y direction:
p = min;
pcurr = pmin;
do {
if (++p == to) {
p = from;
}
pnext = point_type (tr (*p));
} while (pnext.equal (pcurr));
bool eqx = coord_traits::equals (pnext.x (), pcurr.x ());
bool eqy = coord_traits::equals (pnext.y (), pcurr.y ());
clockwise = eqx;
// perform the actual compression: just store those points that are
// distinct by x and y:
n = 0;
plast = pts [n++] = pcurr;
while (n < m_size) {
do {
pcurr = pnext;
if (++p == to) {
p = from;
}
pnext = point_type (tr (*p));
} while (coord_traits::equals (plast.x (), pcurr.x ()) || coord_traits::equals (plast.y (), pcurr.y ()) ||
coord_traits::equals (pnext.x (), pcurr.x ()) != eqx || coord_traits::equals (pnext.y (), pcurr.y ()) != eqy);
plast = pts [n++] = pcurr;
}
} else {
m_size = n;
pts = new point_type [m_size];
// copy distinct points now
n = 0;
p = min;
point_type plast (tr (*p++));
if (p == to) {
p = from;
}
point_type pcurr (tr (*p++));
if (p == to) {
p = from;
}
area_type a = 0;
pts [n++] = plast;
while (true) {
pnext = point_type (tr (*p));
if (pcurr.equal (plast) || pcurr.equal (pnext) || is_colinear (plast, pcurr, pnext, remove_reflected)) {
// skip this point ..
} else if (n == m_size) {
// the contour has closed
a += vprod (plast - point_type (), pcurr - point_type ());
break;
} else {
// remember this point
pts [n++] = pcurr;
a += vprod (plast - point_type (), pcurr - point_type ());
plast = pcurr;
}
pcurr = pnext;
if (++p == to) {
p = from;
}
}
clockwise = (a < 0);
}
// normalize the orientation
if ((! clockwise) != hole && normalize) {
std::reverse (pts + 1, pts + n);
}
// and store the pointer along with two flags: ortho mode and hole flag
tl_assert (((size_t) pts & 3) == 0);
mp_points = (point_type *) ((size_t) pts | (hole ? 2 : 0) | (ortho ? 1 : 0));
}
}
/**
* @brief Moves the contour.
*
* Moves the contour by the given displacement.
* Modifies the polygon with the moved contour.
* Moving a contour is a fast operation since no renormalization is required.
*
* @param d The displacement to apply.
*
* @return The moved contour.
*/
polygon_contour<C> &move (const vector_type &d)
{
point_type *p = (point_type *) ((size_t) mp_points & ~3);
for (size_type i = 0; i < m_size; ++i, ++p) {
*p += d;
}
return *this;
}
/**
* @brief Return the moved contour
*
* Moves the contour by the given displacement and returns a new object.
* Does not modify the contour.
*
* @param d The displacement to apply.
*
* @return The moved contour.
*/
polygon_contour<C> moved (const vector_type &d) const
{
polygon_contour<C> cont (*this);
cont.move (d);
return cont;
}
/**
* @brief Transform the contour with a generic transformation.
*
* Transforms the contour with the given transformation.
* Modifies the polygon with the transformed contour.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*
* @return The transformed contour.
*/
template <class Trans>
polygon_contour<C> &transform (const Trans &tr, bool compress = default_compression<C> (), bool remove_reflected = false)
{
// does the transformation the hard way: extract and insert again
std::vector<point_type> buffer;
size_type n = size ();
buffer.reserve (n);
for (size_type i = 0; i < n; ++i) {
buffer.push_back ((*this) [i]);
}
assign (buffer.begin (), buffer.end (), tr, is_hole (), compress, true, remove_reflected);
return *this;
}
/**
* @brief Transform the contour.
*
* Transforms the contour with the given transformation.
* Modifies the polygon with the transformed contour.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*
* @return The transformed contour.
*/
polygon_contour<C> &transform (const trans_type &tr, bool compress = default_compression<C> (), bool remove_reflected = false)
{
if (tr.rot () == trans_type::r0 && !compress) {
move (tr.disp ());
} else {
// does the transformation the hard way: extract and insert again
std::vector<point_type> buffer;
size_type n = size ();
buffer.reserve (n);
for (size_type i = 0; i < n; ++i) {
buffer.push_back ((*this) [i]);
}
assign (buffer.begin (), buffer.end (), tr, is_hole (), compress, true, remove_reflected);
}
return *this;
}
/**
* @brief Transform the contour.
*
* Transforms the contour with the given transformation.
* Does not modify the contour but returns the transformed contour.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected true, to remove reflecting spikes if compress is true
*
* @return The transformed contour.
*/
template <class Tr>
polygon_contour<typename Tr::target_coord_type> transformed (const Tr &t, bool compress = default_compression<typename Tr::target_coord_type> (), bool remove_reflected = false) const
{
// construct the transformed polygon
typedef polygon_contour_iterator<polygon_contour<C>, db::unit_trans<C> > iter;
return polygon_contour<typename Tr::target_coord_type> (iter (this, 0), iter (this, size ()), t, is_hole (), compress, true, remove_reflected);
}
/**
* @brief begin iterator for the points of this contour
*/
simple_iterator begin () const
{
return simple_iterator (this, 0);
}
/**
* @brief end iterator for the points of this contour
*/
simple_iterator end () const
{
return simple_iterator (this, size ());
}
/**
* @brief returns true if the contour is a rectilinear (manhattan) contour
*/
bool is_rectilinear () const
{
if (((size_t) mp_points & 1) != 0) {
return true;
}
if (m_size < 2) {
return false;
}
point_type pl = mp_points [m_size - 1];
for (size_t i = 0; i < m_size; ++i) {
point_type p = mp_points [i];
if (! coord_traits::equals (p.x (), pl.x ()) && ! coord_traits::equals (p.y (), pl.y ())) {
return false;
}
pl = p;
}
return true;
}
/**
* @brief returns true if the contour is a half-manhattan contour (multiples of 45 degree)
*/
bool is_halfmanhattan () const
{
if (((size_t) mp_points & 1) != 0) {
return true;
}
if (m_size < 2) {
return false;
}
point_type pl = mp_points [m_size - 1];
for (size_t i = 0; i < m_size; ++i) {
point_type p = mp_points [i];
if (! coord_traits::equals (p.x (), pl.x ()) && ! coord_traits::equals (p.y (), pl.y ()) && ! coord_traits::equals (std::abs (p.x () - pl.x ()), std::abs (p.y () - pl.y ()))) {
return false;
}
pl = p;
}
return true;
}
/**
* @brief Returns true if the contour is a hole
*
* Since this method employs the orientation property the results are only valid for
* contours with more than 2 points.
*/
bool is_hole () const
{
return ((size_t) mp_points & 2) != 0;
}
/**
* @brief The area of the contour
*/
area_type area () const
{
return area2 () / 2;
}
/**
* @brief The upper bound area for a manhattan approximation of the contour times
*/
area_type area_upper_manhattan_bound () const
{
return area_upper_manhattan_bound2 () / 2;
}
/**
* @brief The area of the contour times 2
* For integer area types, this is the more precise value as the division
* by 2 might round off.
*/
area_type area2 () const
{
size_type n = size ();
if (n < 3) {
return 0;
}
area_type a = 0;
point_type pl = (*this) [n - 1];
for (size_type p = 0; p < n; ++p) {
point_type pp = (*this) [p];
a += db::vprod (pp - point_type (), pl - point_type ());
pl = pp;
}
return a;
}
/**
* @brief The upper bound area for a manhattan approximation of the contour times 2
* This area takes non-manhattan edges as being manhattan-interpolated to the outside.
*/
area_type area_upper_manhattan_bound2 () const
{
size_type n = size ();
if (n < 3) {
return 0;
}
area_type a = 0;
point_type pl = (*this) [n - 1];
for (size_type p = 0; p < n; ++p) {
point_type pp = (*this) [p];
int sdx = pp.x () < pl.x () ? -1 : (pp.x () == pl.x () ? 0 : 1);
int sdy = pp.y () < pl.y () ? -1 : (pp.y () == pl.y () ? 0 : 1);
if (sdx != 0 && sdy != 0) {
point_type pm;
// determine an interpolation point
if (sdx * sdy < 0) {
pm = point_type (pp.x (), pl.y ());
} else {
pm = point_type (pl.x (), pp.y ());
}
a += db::vprod (pm - point_type (), pl - point_type ());
a += db::vprod (pp - point_type (), pm - point_type ());
} else {
a += db::vprod (pp - point_type (), pl - point_type ());
}
pl = pp;
}
return a;
}
/**
* @brief The perimeter of the contour
*/
perimeter_type perimeter () const
{
size_type n = size ();
if (n < 2) {
return 0;
}
double d = 0;
point_type pl = (*this) [n - 1];
for (size_type p = 0; p < n; ++p) {
point_type pp = (*this) [p];
d += pp.double_distance (pl);
pl = pp;
}
return coord_traits::rounded_perimeter (d);
}
/**
* @brief Random access operator
*
* The time for the access operation is guaranteed to be constant.
*/
point_type operator[] (size_type index) const
{
size_t f = (size_t) mp_points;
point_type *pts = (point_type *) (f & ~3);
if ((f & 1) != 0) {
if ((index & 1) != 0) {
if ((f & 2) != 0) {
return point_type (pts [((index + 1) / 2) % m_size].x (), pts [(index - 1) / 2].y ());
} else {
return point_type (pts [(index - 1) / 2].x (), pts [((index + 1) / 2) % m_size].y ());
}
} else {
return pts [index / 2];
}
} else {
return pts [index];
}
}
/**
* @brief Sizing
*
* Shifts the contour outwards (dx,dy>0) or inwards (dx,dy<0).
* May create invalid (self-overlapping, reverse oriented) contours.
* The sign of dx and dy should be identical.
*
* The mode defines at which bending angle cutoff occurs
* 0: >0
* 1: >45
* 2: >90
* 3: >135
* 4: >168 (approx)
* other: >179 (approx)
*/
void size (coord_type dx, coord_type dy, unsigned int mode);
/**
* @brief Return the size of the contour
*
* The time for the size operation is guaranteed to be constant.
*/
size_type size () const
{
if ((size_t) mp_points & 1) {
return m_size * 2;
} else {
return m_size;
}
}
/**
* @brief Compute the bounding box
*
* The bounding box is computed on-the-fly and is not stored.
* Computing the bbox is not a cheap operation.
* It is somewhat simplified in the manhattan case since it
* can be computed from the reduced point set.
*/
box_type bbox () const
{
box_type box;
point_type *p = (point_type *) ((size_t) mp_points & ~3);
for (size_type i = 0; i < m_size; ++i, ++p) {
box += *p;
}
return box;
}
/**
* @brief Clear the contour
*/
void clear ()
{
release ();
}
/**
* @brief Equality test
*/
bool operator== (const polygon_contour<C> &d) const
{
if (size () != d.size ()) {
return false;
}
if (is_hole () != d.is_hole ()) {
return false;
}
simple_iterator p1 = begin (), p2 = d.begin ();
while (p1 != end ()) {
if (*p1 != *p2) {
return false;
}
++p1; ++p2;
}
return true;
}
/**
* @brief Inequality test
*/
bool operator!= (const polygon_contour<C> &d) const
{
return !operator== (d);
}
/**
* @brief Less than-operator
*/
bool operator< (const polygon_contour<C> &d) const
{
if (size () != d.size ()) {
return size () < d.size ();
}
if (is_hole () != d.is_hole ()) {
return is_hole () < d.is_hole ();
}
simple_iterator p1 = begin (), p2 = d.begin ();
while (p1 != end ()) {
if (*p1 != *p2) {
return *p1 < *p2;
}
++p1; ++p2;
}
return false;
}
/**
* @brief Fuzzy equality test
*/
bool equal (const polygon_contour<C> &d) const
{
if (size () != d.size ()) {
return false;
}
if (is_hole () != d.is_hole ()) {
return false;
}
simple_iterator p1 = begin (), p2 = d.begin ();
while (p1 != end ()) {
if (! (*p1).equal (*p2)) {
return false;
}
++p1; ++p2;
}
return true;
}
/**
* @brief Fuzzy inequality test
*/
bool not_equal (const polygon_contour<C> &d) const
{
return ! equal (d);
}
/**
* @brief Fuzzy less than-operator
*/
bool less (const polygon_contour<C> &d) const
{
if (size () != d.size ()) {
return size () < d.size ();
}
if (is_hole () != d.is_hole ()) {
return is_hole () < d.is_hole ();
}
simple_iterator p1 = begin (), p2 = d.begin ();
while (p1 != end ()) {
if (! (*p1).equal (*p2)) {
return (*p1).less (*p2);
}
++p1; ++p2;
}
return false;
}
/**
* @brief swap with a different contour
*/
void swap (polygon_contour<C> &d)
{
std::swap (m_size, d.m_size);
std::swap (mp_points, d.mp_points);
}
/**
* @brief Collect memory statistics
*/
void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const
{
if (! no_self) {
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
stat->add (typeid (point_type []), (void *) mp_points, sizeof (point_type) * m_size, sizeof (point_type) * m_size, (void *) this, purpose, cat);
}
private:
point_type *mp_points;
size_type m_size;
void release ()
{
point_type *p = (point_type *) ((size_t) mp_points & ~3);
if (p) {
delete [] p;
}
mp_points = 0;
m_size = 0;
}
};
} // namespace db
namespace std
{
// injecting a global std::swap for polygons into the
// std namespace
template <class C>
void swap (db::polygon_contour<C> &a, db::polygon_contour<C> &b)
{
a.swap (b);
}
}
namespace db
{
/**
* @brief The contour point iterator
*
* The point iterator delivers all points of a contour.
* It is based on the random access operator of the contour
*/
template <class Contour, class Tr>
class polygon_contour_iterator
{
public:
typedef Contour contour_type;
typedef typename contour_type::value_type point_type;
typedef typename contour_type::value_type value_type;
typedef void pointer;
typedef value_type reference;
typedef typename point_type::coord_type coord_type;
typedef std::bidirectional_iterator_tag iterator_category;
typedef Tr trans_type;
typedef ptrdiff_t difference_type;
/**
* @brief The default constructor
*/
polygon_contour_iterator ()
: mp_contour (0), m_index (0), m_reverse (false)
{
// .. nothing yet ..
}
/**
* @brief The standard constructor
*/
polygon_contour_iterator (const Contour *contour, size_t n, bool reverse = false)
: mp_contour (contour), m_index (n), m_reverse (reverse)
{
// .. nothing yet ..
}
/**
* @brief The standard constructor with a transformation
*/
template <class T>
polygon_contour_iterator (const polygon_contour_iterator<Contour, T> &d, const trans_type &trans, bool reverse = false)
: mp_contour (d.mp_contour), m_index (d.m_index), m_trans (trans), m_reverse (reverse)
{
// .. nothing yet ..
}
/**
* @brief Sorting order
*/
bool operator< (const polygon_contour_iterator &d) const
{
return m_index < d.m_index;
}
/**
* @brief Equality test
*/
bool operator== (const polygon_contour_iterator &d) const
{
return m_index == d.m_index;
}
/**
* @brief Inequality test
*/
bool operator!= (const polygon_contour_iterator &d) const
{
return m_index != d.m_index;
}
/**
* @brief Point access
*/
point_type operator* () const
{
return m_trans ((*mp_contour) [m_index]);
}
/**
* @brief Addition of differences
*/
polygon_contour_iterator operator+ (difference_type d) const
{
if (m_reverse) {
return polygon_contour_iterator (mp_contour, m_index - d);
} else {
return polygon_contour_iterator (mp_contour, m_index + d);
}
}
/**
* @brief Addition of distances
*/
polygon_contour_iterator &operator+= (difference_type d)
{
if (m_reverse) {
m_index -= d;
} else {
m_index += d;
}
return *this;
}
/**
* @brief Subtraction of distances
*/
polygon_contour_iterator operator- (difference_type d) const
{
return operator+ (-d);
}
/**
* @brief Subtraction of distances
*/
polygon_contour_iterator &operator-= (difference_type d)
{
return operator+= (-d);
}
/**
* @brief Subtraction of iterators
*/
difference_type operator- (const polygon_contour_iterator &d) const
{
if (m_reverse) {
return d.m_index - m_index;
} else {
return m_index - d.m_index;
}
}
/**
* @brief Increment operator
*/
polygon_contour_iterator &operator++ ()
{
return operator+= (1);
}
/**
* @brief Postfix increment operator
*/
polygon_contour_iterator operator++ (int)
{
polygon_contour_iterator i (*this);
operator+= (1);
return i;
}
/**
* @brief Decrement operator
*/
polygon_contour_iterator &operator-- ()
{
return operator-= (1);
}
/**
* @brief Postfix decrement operator
*/
polygon_contour_iterator operator-- (int)
{
polygon_contour_iterator i (*this);
operator-= (1);
return i;
}
private:
template <class Ct, class T> friend class polygon_contour_iterator;
const contour_type *mp_contour;
size_t m_index;
trans_type m_trans;
bool m_reverse;
};
/**
* @brief The polygon edge iterator
*
* The edge iterator delivers all edges of the polygon with a
* distinct orientation (inside is 'right', outside is 'left).
*/
template <class P, class Tr>
class polygon_edge_iterator
{
public:
typedef P polygon_type;
typedef typename polygon_type::coord_type coord_type;
typedef typename polygon_type::contour_type contour_type;
typedef db::edge<coord_type> edge_type;
typedef edge_type value_type;
typedef db::point<coord_type> point_type;
typedef Tr trans_type;
typedef void pointer; // no operator->
typedef edge_type reference; // operator* returns a value
typedef std::bidirectional_iterator_tag iterator_category;
typedef void difference_type;
/**
* @brief The default constructor
*/
polygon_edge_iterator ()
: mp_polygon (0), m_ctr (0), m_num_ctr (0), m_pt (0), m_trans ()
{
// .. nothing yet ..
}
/**
* @brief The standard constructor
*/
polygon_edge_iterator (const polygon_type &polygon)
: mp_polygon (&polygon), m_ctr (0), m_num_ctr (polygon.holes () + 1), m_pt (0), m_trans ()
{
// A polygon may be "empty": then it does not even have a hull ..
if (mp_polygon->hull ().size () == 0) {
m_num_ctr = 0;
}
}
/**
* @brief The standard constructor with a transformation
*/
polygon_edge_iterator (const polygon_type &polygon, const trans_type &trans)
: mp_polygon (&polygon), m_ctr (0), m_num_ctr (polygon.holes () + 1), m_pt (0), m_trans (trans)
{
// A polygon may be "empty": then it does not even have a hull ..
if (mp_polygon->hull ().size () == 0) {
m_num_ctr = 0;
}
}
/**
* @brief The standard constructor for one specific contour
*/
polygon_edge_iterator (const polygon_type &polygon, unsigned int ctr)
: mp_polygon (&polygon), m_ctr (ctr), m_num_ctr (std::min (polygon.holes (), ctr) + 1), m_pt (0), m_trans ()
{
// The contour may be "empty"
while (m_ctr < m_num_ctr && mp_polygon->contour (m_ctr).size () == 0) {
++m_ctr;
}
}
/**
* @brief The standard constructor for one specific contour with a transformation
*/
polygon_edge_iterator (const polygon_type &polygon, unsigned int ctr, const trans_type &trans)
: mp_polygon (&polygon), m_ctr (ctr), m_num_ctr (std::min (polygon.holes (), ctr) + 1), m_pt (0), m_trans (trans)
{
// The contour may be "empty"
while (m_ctr < m_num_ctr && mp_polygon->contour (m_ctr).size () == 0) {
++m_ctr;
}
}
/**
* @brief at_end predicate
*/
bool at_end () const
{
return m_ctr >= m_num_ctr;
}
/**
* @brief Gets the contour number
*/
unsigned int contour () const
{
return m_ctr;
}
/**
* @brief Edge access
*/
edge_type operator* () const
{
const contour_type *c = get_ctr ();
point_type p1 (m_trans ((*c) [m_pt]));
point_type p2 (m_trans ((*c) [m_pt + 1 >= c->size () ? 0 : m_pt + 1]));
// to maintain the edge orientation we need to swap start end end point
// if the transformation is mirroring
if (m_trans.is_mirror ()) {
return edge_type (p2, p1);
} else {
return edge_type (p1, p2);
}
}
/**
* @brief Increment operator
*/
polygon_edge_iterator &operator++ ()
{
const contour_type *c = get_ctr ();
if (++m_pt == c->size ()) {
m_pt = 0;
// polygons may contain empty contours (holes): skip those
do {
++m_ctr;
} while (! at_end () && get_ctr ()->size () == 0);
}
return *this;
}
/**
* @brief Decrement operator
*/
polygon_edge_iterator &operator-- ()
{
if (m_pt == 0) {
// polygons may contain empty contours (holes): skip those
do {
--m_ctr;
} while (m_ctr > 0 && get_ctr ()->size () == 0);
m_pt = get_ctr ()->size () - 1;
} else {
--m_pt;
}
return *this;
}
private:
const polygon_type *mp_polygon;
unsigned int m_ctr, m_num_ctr;
size_t m_pt;
trans_type m_trans;
// fetch the contour pointer to the current contour
const contour_type *get_ctr () const
{
return &(mp_polygon->contour (m_ctr));
}
};
/**
* @brief A polygon class
*
* A polygon consists of an outer hull and zero to many
* holes. Each contour consists of several points. The point
* list is normalized such that the leftmost, lowest point is
* the first one. The orientation is normalized such that
* the orientation of the hull contour is clockwise, while
* the orientation of the holes is counterclockwise.
*
* It is in no way checked that the contours are not over-
* lapping. This must be ensured by the user of the object
* when filling the contours.
*/
template <class C>
class DB_PUBLIC_TEMPLATE polygon
{
public:
typedef C coord_type;
typedef db::edge<coord_type> edge_type;
typedef db::simple_trans<coord_type> trans_type;
typedef db::point<coord_type> point_type;
typedef db::vector<coord_type> vector_type;
typedef db::box<coord_type> box_type;
typedef db::coord_traits<coord_type> coord_traits;
typedef typename coord_traits::distance_type distance_type;
typedef typename coord_traits::perimeter_type perimeter_type;
typedef typename coord_traits::area_type area_type;
typedef polygon_contour<C> contour_type;
typedef tl::vector<contour_type> contour_list_type;
typedef db::polygon_edge_iterator< polygon<C>, db::unit_trans<C> > polygon_edge_iterator;
typedef db::polygon_contour_iterator< contour_type, db::unit_trans<C> > polygon_contour_iterator;
typedef db::object_tag< polygon<C> > tag;
/**
* @brief The default constructor.
*
* The default constructor creates a empty polygon.
*/
polygon ()
{
// create an entry for the hull contour
m_ctrs.push_back (contour_type ());
}
/**
* @brief The copy constructor from another polygon with a transformation
*
* The transformation must have the ability to transform a point to another point of type C.
*
* @param p The source polygon
* @param tr The transformation to apply on assignment
* @param compress True, if the contours shall be compressed
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class D, class T>
polygon (const db::polygon<D> &p, const T &tr, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_ctrs.resize (p.holes () + 1);
m_ctrs [0].assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
for (unsigned int i = 0; i < m_ctrs.size () - 1; ++i) {
m_ctrs [i + 1].assign (p.begin_hole (i), p.end_hole (i), tr, true, compress, normalize, remove_reflected);
}
}
/**
* @brief The copy constructor from another polygon a different coordinate type
*
* @param p The source polygon
* @param compress True, if the contours shall be compressed
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*/
template <class D>
explicit polygon (const db::polygon<D> &p, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
db::point_coord_converter<C, D> tr;
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_ctrs.resize (p.holes () + 1);
m_ctrs [0].assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
for (unsigned int i = 0; i < m_ctrs.size () - 1; ++i) {
m_ctrs [i + 1].assign (p.begin_hole (i), p.end_hole (i), tr, true, compress, normalize, remove_reflected);
}
}
/**
* @brief The box constructor.
*
* Creates a polygon from a box
*/
explicit polygon (const db::box<C> &b)
{
// create an entry for the hull contour
m_ctrs.push_back (contour_type ());
point_type p[4];
p[0] = point_type (b.left (), b.bottom ());
p[1] = point_type (b.left (), b.top ());
p[2] = point_type (b.right (), b.top ());
p[3] = point_type (b.right (), b.bottom ());
m_ctrs.back ().assign (p, p + 4, false, false /*don't compress*/);
m_bbox = b;
}
/**
* @brief The (dummy) translation operator
*/
void translate (const polygon<C> &d, db::generic_repository<C> &, db::ArrayRepository &)
{
*this = d;
}
/**
* @brief The (dummy) translation operator with transformation
*/
template <class T>
void translate (const polygon<C> &d, const T &t, db::generic_repository<C> &, db::ArrayRepository &)
{
*this = d;
transform (t, false);
}
/**
* @brief A less operator to establish a sorting order.
*/
bool operator< (const polygon<C> &b) const
{
// do the simple tests first
if (holes () < b.holes ()) {
return true;
} else if (holes () != b.holes ()) {
return false;
}
if (m_bbox < b.m_bbox) {
return true;
} else if (m_bbox != b.m_bbox) {
return false;
}
// since the list of holes is maintained sorted, we can just
// compare by comparing the holes contours (all must be equal)
typename contour_list_type::const_iterator hh = b.m_ctrs.begin ();
typename contour_list_type::const_iterator h = m_ctrs.begin ();
while (h != m_ctrs.end ()) {
if (*h < *hh) {
return true;
} else if (*h != *hh) {
return false;
}
++h;
++hh;
}
return false;
}
/**
* @brief Equality test
*/
bool operator== (const polygon<C> &b) const
{
if (m_bbox == b.m_bbox && holes () == b.holes ()) {
// since the list of holes is maintained sorted, we can just
// compare by comparing the holes contours (all must be equal)
typename contour_list_type::const_iterator hh = b.m_ctrs.begin ();
typename contour_list_type::const_iterator h = m_ctrs.begin ();
while (h != m_ctrs.end ()) {
if (*h != *hh) {
return false;
}
++h;
++hh;
}
return true;
} else {
return false;
}
}
/**
* @brief Inequality test
*/
bool operator!= (const polygon<C> &b) const
{
return !operator== (b);
}
/**
* @brief A fuzzy less operator to establish a sorting order.
*/
bool less (const polygon<C> &b) const
{
// do the simple tests first
if (holes () < b.holes ()) {
return true;
} else if (holes () != b.holes ()) {
return false;
}
if (m_bbox.less (b.m_bbox)) {
return true;
} else if (m_bbox.not_equal (b.m_bbox)) {
return false;
}
// since the list of holes is maintained sorted, we can just
// compare by comparing the holes contours (all must be equal)
typename contour_list_type::const_iterator hh = b.m_ctrs.begin ();
typename contour_list_type::const_iterator h = m_ctrs.begin ();
while (h != m_ctrs.end ()) {
if (h->less (*hh)) {
return true;
} else if (h->not_equal (*hh)) {
return false;
}
++h;
++hh;
}
return false;
}
/**
* @brief Fuzzy equality test
*/
bool equal (const polygon<C> &b) const
{
if (m_bbox.equal (b.m_bbox) && holes () == b.holes ()) {
// since the list of holes is maintained sorted, we can just
// compare by comparing the holes contours (all must be equal)
typename contour_list_type::const_iterator hh = b.m_ctrs.begin ();
typename contour_list_type::const_iterator h = m_ctrs.begin ();
while (h != m_ctrs.end ()) {
if (h->not_equal (*hh)) {
return false;
}
++h;
++hh;
}
return true;
} else {
return false;
}
}
/**
* @brief Fuzzy inequality test
*/
bool not_equal (const polygon<C> &b) const
{
return !equal (b);
}
/**
* @brief Compatibility with polygon_ref
*/
polygon<C> instantiate () const
{
return *this;
}
/**
* @brief Compatibility with polygon_ref
*/
void instantiate (polygon<C> &poly) const
{
poly = *this;
}
/**
* @brief Returns true, if the polygon is a simple box
*/
bool is_box () const
{
return m_ctrs.size () == 1 && m_ctrs [0].size () == 4 && m_ctrs [0].is_rectilinear ();
}
/**
* @brief Returns true, if the polygon is rectilinear
*/
bool is_rectilinear () const
{
for (size_t i = 0; i < m_ctrs.size (); ++i) {
if (! m_ctrs [i].is_rectilinear ()) {
return false;
}
}
return true;
}
/**
* @brief Returns true, if the polygon is halfmanhattan
*/
bool is_halfmanhattan () const
{
for (size_t i = 0; i < m_ctrs.size (); ++i) {
if (! m_ctrs [i].is_halfmanhattan ()) {
return false;
}
}
return true;
}
/**
* @brief Returns a value indicating that the polygon is an empty one
*/
bool is_empty () const
{
return m_ctrs.size () == size_t (1) && m_ctrs[0].size () == 0;
}
/**
* @brief Returns the number of points in the polygon
*/
size_t vertices () const
{
size_t n = 0;
for (typename contour_list_type::const_iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
n += h->size ();
}
return n;
}
/**
* @brief Returns the area ratio between the polygon's bounding box and actual area
*
* This number is a measure how well the polygon is approximated by the bounding box.
* Values are bigger than 1 for well-formed polygons. Bigger values mean worse
* approximation.
*/
double area_ratio () const
{
area_type a = area2 ();
if (a == 0) {
// By our definition, an empty polygon has an area ratio of 0
return 0.0;
} else {
return double (box ().area ()) / (0.5 * a);
}
}
/**
* @brief Returns the area ratio between the polygon's bounding box and upper manhattan approximation bound
*
* This number is a measure how well the polygon is approximated by the bounding box,
* while assuming an outer manhattan approximation is a good measure for the polygon's
* area.
* Values are bigger than 1 for well-formed polygons. Bigger values mean worse
* approximation.
*/
double area_upper_manhattan_bound_ratio () const
{
area_type a = this->area_upper_manhattan_bound2 ();
if (a == 0) {
// By our definition, an empty polygon has an area ratio of 0
return 0.0;
} else {
return double (box ().area ()) / (0.5 * a);
}
}
/**
* @brief The hull "begin" point iterator
*
* The hull point sequence delivers the points that the hull
* is made of.
*/
polygon_contour_iterator begin_hull () const
{
return polygon_contour_iterator (& hull (), 0);
}
/**
* @brief The hull "end" point iterator
*
* The hull point sequence delivers the points that the hull
* is made of.
*/
polygon_contour_iterator end_hull () const
{
return polygon_contour_iterator (& hull (), hull ().size ());
}
/**
* @brief The hole "begin" point iterator
*
* The hole point sequence delivers the points that the specific hole
* is made of.
*/
polygon_contour_iterator begin_hole (unsigned int h) const
{
return polygon_contour_iterator (& begin_holes () [h], 0);
}
/**
* @brief The hole "end" point iterator
*
* The hole point sequence delivers the points that the specific hole
* is made of.
*/
polygon_contour_iterator end_hole (unsigned int h) const
{
return polygon_contour_iterator (& begin_holes () [h], begin_holes () [h].size ());
}
/**
* @brief Compress the polygon.
*
* Compressing means removing redundant points (identical, colinear)
*
* @param remove_reflected Removes reflected edges (spiked) if this parameter is true.
* @return The compressed polygon.
*/
polygon<C> &compress (bool remove_reflected = false)
{
// compress the contours by employing the transform method with a unit transformation
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
h->transform (db::unit_trans<C> (), true /*compress*/, remove_reflected);
}
m_bbox = m_ctrs [0].bbox ();
return *this;
}
/**
* @brief Transform the polygon.
*
* Transforms the polygon with the given transformation.
* Modifies the polygon with the transformed polygon.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*
* @return The transformed polygon.
*/
template <class Trans>
polygon<C> &transform (const Trans &t, bool compress = default_compression<C> (), bool remove_reflected = false)
{
// insert the transformed holes and normalize
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
h->transform (t, compress, remove_reflected);
}
// transform or recompute the bbox
if (t.is_ortho ()) {
m_bbox.transform (t);
} else {
m_bbox = m_ctrs [0].bbox ();
}
// keep the list of holes sorted ..
tl::sort (m_ctrs.begin () + 1, m_ctrs.end ());
return *this;
}
/**
* @brief Transform the polygon.
*
* Transforms the polygon with the given transformation.
* Does not modify the polygon but returns the transformed polygon.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*
* @return The transformed polygon.
*/
template <class Tr>
polygon<typename Tr::target_coord_type> transformed_ext (const Tr &t, bool compress = default_compression<typename Tr::target_coord_type> (), bool remove_reflected = false) const
{
typedef typename Tr::target_coord_type target_coord_type;
polygon<target_coord_type> poly;
// transform the contours and add as new ones
poly.assign_hull (begin_hull (), end_hull (), t, compress, remove_reflected);
for (unsigned int h = 0; h < holes (); ++h) {
poly.insert_hole (begin_hole (h), end_hole (h), t, compress, remove_reflected);
}
return poly;
}
/**
* @brief Transform the polygon.
*
* Transforms the polygon with the given transformation.
* Does not modify the polygon but returns the transformed polygon.
*
* @param t The transformation to apply.
*
* @return The transformed polygon.
*/
template <class Tr>
polygon<typename Tr::target_coord_type> transformed (const Tr &t) const
{
return this->transformed_ext<Tr> (t);
}
/**
* @brief Returns the scaled polygon
*/
db::polygon<db::DCoord>
scaled (double s) const
{
db::complex_trans<C, db::DCoord> ct (s);
return this->transformed (ct);
}
/**
* @brief Returns the moved polyon
*
* Moves the polygon by the given offset and returns the
* moved polygon. The polygon is not modified.
*
* @param p The distance to move the polygon.
*
* @return The moved polygon.
*/
polygon<C> moved (const vector<C> &p) const
{
polygon<C> b (*this);
b.move (p);
return b;
}
/**
* @brief Moves the polygon.
*
* Moves the polygon by the given offset and returns the
* moved polygon. The polygon is overwritten.
*
* @param p The distance to move the polygon.
*
* @return The moved polygon.
*/
polygon<C> &move (const vector<C> &d)
{
m_bbox.move (d);
// move the contours
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
h->move (d);
}
return *this;
}
/**
* @brief Returns the bounding box of the polygon
*/
const db::box<C> &box () const
{
return m_bbox;
}
/**
* @brief Clears the polygon
*
* @param n The number of holes to reserve memory for
*/
void clear (unsigned int n = 0)
{
m_bbox = db::box<C> ();
m_ctrs.clear ();
m_ctrs.reserve (n + 1);
m_ctrs.push_back (contour_type ());
}
/**
* @brief Reserves memory for n holes
*
* @param n The number of holes to reserve memory for
*/
void reserve_holes (unsigned int n)
{
m_ctrs.reserve (n + 1);
}
/**
* @brief Assign the contour from another contour
*
* Replaces the outer contour by the given other contour
* The given contour must not be a hole contour and is_hole must be false.
*
* @param other The other contour
*/
void assign_hull (const contour_type &other)
{
tl_assert (! other.is_hole ());
m_ctrs [0] = other;
m_bbox = m_ctrs [0].bbox ();
}
/**
* @brief Set the outer contour
*
* Replaces the outer contour by the points given by
* the sequence [start,end). This method will update the
* bounding box and normalize the hull, so it is oriented
* properly.
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I>
void assign_hull (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [0].assign (start, end, false, compress, normalize, remove_reflected);
m_bbox = m_ctrs [0].bbox ();
}
/**
* @brief Set the outer contour by a transformed set of points
*
* Replaces the outer contour by the points given by
* the sequence [start,end), transformed with the operator op.
* This method will update the bounding box and normalize the hull,
* so it is oriented properly.
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
*/
template <class I, class T>
void assign_hull (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [0].assign (start, end, op, false, compress, normalize, remove_reflected);
m_bbox = m_ctrs [0].bbox ();
}
/**
* @brief Assigns a hole from another contour
*
* Replaced a hole with a copy of the given contour.
* The given contour must be a hole contour and is_hole must be true.
*
* @param h The hole index of the hole to replace
* @param other The other contour
*/
void assign_hole (unsigned int h, const contour_type &other)
{
tl_assert (other.is_hole ());
m_ctrs [h + 1] = other;
}
/**
* @brief Set a hole contour
*
* Replaces a hole contour by the points given by
* the sequence [start,end). This method will update the
* bounding box and normalize the contour, so it is oriented
* properly.
*
* @param h The hole index of the hole to replace
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I>
void assign_hole (unsigned int h, I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [h + 1].assign (start, end, true, compress, normalize, remove_reflected);
}
/**
* @brief Set a hole by a transformed set of points
*
* Replaces a hole contour by the points given by
* the sequence [start,end), transformed with the operator op.
* This method will update the bounding box and normalize the contour,
* so it is oriented properly.
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
*/
template <class I, class T>
void assign_hole (unsigned int h, I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [h + 1].assign (start, end, op, true, compress, normalize, remove_reflected);
}
/**
* @brief Add a hole from another contour
*
* Add a contour hole as a copy of the given contour.
* The given contour must be a hole contour and is_hole must be true.
*
* @param other The other contour
*/
void insert_hole (const contour_type &other)
{
tl_assert (other.is_hole ());
contour_type &h = add_hole ();
h = other;
}
/**
* @brief Add a hole
*
* Adds a hole contour with the points given by
* the sequence [start,end). This method will update the
* bounding box and normalize the hull, so it is oriented
* properly.
* It is not checked, whether the hole really is inside
* the hull.
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I>
void insert_hole (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
insert_hole (start, end, db::unit_trans<C> (), compress, remove_reflected, normalize);
}
/**
* @brief Add a hole of transformed points
*
* Adds a hole contour with the points given by
* the sequence [start,end), transformed with the given
* function. This method will update the bounding box and
* normalize the hull, so it is oriented properly.
* It is not checked, whether the hole really is inside
* the hull.
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I, class T>
void insert_hole (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
// add the hole
contour_type &h = add_hole ();
h.assign (start, end, op, true, compress, normalize, remove_reflected);
}
/**
* @brief Sort the holes
*
* Sorting the holes makes certain algorithms more effective.
*/
void sort_holes ()
{
if (! m_ctrs.empty ()) {
std::sort (m_ctrs.begin () + 1, m_ctrs.end ());
}
}
/**
* @brief Outer contour
*/
const contour_type &hull () const
{
return m_ctrs [0];
}
/**
* @brief The contour of the nth hole
*/
const contour_type &hole (unsigned int h) const
{
return m_ctrs [h + 1];
}
/**
* @brief The number of holes
*/
unsigned int holes () const
{
return (unsigned int) m_ctrs.size () - 1;
}
/**
* @brief Hole iterator: start
*/
typename contour_list_type::const_iterator begin_holes () const
{
return m_ctrs.begin () + 1;
}
/**
* @brief Hole iterator: end
*/
typename contour_list_type::const_iterator end_holes () const
{
return m_ctrs.end ();
}
/**
* @brief The edge iterator begin function
*
* The edge iterator delivers all edges of the polygon.
*
* @return the begin value of the iterator
*/
polygon_edge_iterator begin_edge () const
{
return polygon_edge_iterator (*this);
}
/**
* @brief The edge iterator begin function for a specific contour
*
* The edge iterator delivers all edges of the polygon for the given contour.
* Contour 0 is the hull, 1 the first hole and so forth.
*
* @return the begin value of the iterator
*/
polygon_edge_iterator begin_edge (unsigned int ctr) const
{
return polygon_edge_iterator (*this, ctr);
}
/**
* @brief The area of the polygon
*/
area_type area () const
{
area_type a = 0;
for (typename contour_list_type::const_iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
a += h->area ();
}
return a;
}
/**
* @brief The area of the polygon - upper manhattan bound
*
* This gives the upper area bound for a manhattan approximation of the
* polygon.
*/
area_type area_upper_manhattan_bound () const
{
area_type a = 0;
for (typename contour_list_type::const_iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
a += h->area_upper_manhattan_bound ();
}
return a;
}
/**
* @brief The area of the polygon times 2
* For integer area types, this is the more precise value as the division
* by 2 might round off.
*/
area_type area2 () const
{
area_type a = 0;
for (typename contour_list_type::const_iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
a += h->area2 ();
}
return a;
}
/**
* @brief The area of the polygon - upper manhattan bound times 2
*
* This gives the upper area bound for a manhattan approximation of the
* polygon.
*/
area_type area_upper_manhattan_bound2 () const
{
area_type a = 0;
for (typename contour_list_type::const_iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
a += h->area_upper_manhattan_bound2 ();
}
return a;
}
/**
* @brief The perimeter of the polygon
*/
perimeter_type perimeter () const
{
perimeter_type p = 0;
for (typename contour_list_type::const_iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
p += h->perimeter ();
}
return p;
}
/**
* @brief The string conversion function
*/
std::string to_string () const
{
std::string s = "(";
// the hull contour
for (polygon_contour_iterator p = begin_hull (); p != end_hull (); ++p) {
if (p != begin_hull ()) {
s += ";";
}
s += (*p).to_string ();
}
// and the hole contours
for (unsigned int h = 0; h < holes (); ++h) {
s += "/";
for (polygon_contour_iterator p = begin_hole (h); p != end_hole (h); ++p) {
if (p != begin_hole (h)) {
s += ";";
}
s += (*p).to_string ();
}
}
s += ")";
return s;
}
/**
* @brief Swap the polygon with another one
*
* The global std::swap function injected into the std namespace
* is redirected to this implementation.
*/
void swap (polygon<C> &d)
{
m_ctrs.swap (d.m_ctrs);
std::swap (m_bbox, d.m_bbox);
}
/**
* @brief Direct access to the contours (used for iterators)
*/
const contour_type &contour (unsigned int n) const
{
return m_ctrs [n];
}
/**
* @brief Reduce the polygon
*
* Reduction of a polygon normalizes the polygon by extracting
* a suitable transformation and placing the polygon in a unique
* way.
*
* @return The transformation that must be applied to render the original polygon
*/
void reduce (simple_trans<coord_type> &tr)
{
if (! m_ctrs.empty () && m_ctrs [0].size () > 0) {
vector_type d (m_ctrs [0][0] - point_type ());
move (-d);
tr = simple_trans<coord_type> (simple_trans<coord_type>::r0, d);
}
}
/**
* @brief Reduce the polygon
*
* Reduction of a polygon normalizes the polygon by extracting
* a suitable transformation and placing the polygon in a unique
* way.
*
* @return The transformation that must be applied to render the original polygon
*/
void reduce (disp_trans<coord_type> &tr)
{
if (! m_ctrs.empty () && m_ctrs [0].size () > 0) {
vector_type d (m_ctrs [0][0] - point_type ());
move (-d);
tr = disp_trans<coord_type> (d);
}
}
/**
* @brief Reduce the polygon for unit transformation references
*
* Does not do any reduction since no transformation can be provided.
*
* @return A unit transformation
*/
void reduce (unit_trans<C> &)
{
// .. nothing ..
}
/**
* @brief Sizing with isotropic valued
*
* This convenience method is identical to size (d, d, mode).
*/
void size (coord_type d, unsigned int mode = 2)
{
size (d, d, mode);
}
/**
* @brief Sizing with anisotropic values
*
* Shifts the polygon contours outwards (dx,dy>0) or inwards (dx,dy<0).
* May create invalid (self-overlapping, reverse oriented) polygons. In particular, holes
* may grow larger than the hull (in which case the bbox is not correct because it is computed
* from the hull only) and contours do not vanish if shrinked below their own size but are reversed.
* This method is intended to be used in simple cases or as preparation step for a full-blown sizing
* algorithm using a merge step.
* The sign of dx and dy should be identical.
*
* The mode defines at which bending angle cutoff occurs
* 0: >0
* 1: >45
* 2: >90
* 3: >135
* 4: >168 (approx)
* other: >179 (approx)
*/
void size (coord_type dx, coord_type dy, unsigned int mode = 2)
{
for (typename contour_list_type::iterator c = m_ctrs.begin (); c != m_ctrs.end (); ++c) {
c->size (dx, dy, mode);
}
m_bbox = m_ctrs [0].bbox ();
}
/**
* @brief Sizing with isotropic values, const version
*
* @return the sized polygon
*/
polygon sized (coord_type d, unsigned int mode = 2) const
{
polygon copy (*this);
copy.size (d, mode);
return copy;
}
/**
* @brief Sizing with anisotropic values, const version
*
* @return the sized polygon
*/
polygon sized (coord_type dx, coord_type dy, unsigned int mode = 2) const
{
polygon copy (*this);
copy.size (dx, dy, mode);
return copy;
}
void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const
{
db::mem_stat (stat, purpose, cat, m_ctrs, no_self, parent);
db::mem_stat (stat, purpose, cat, m_bbox, no_self, parent);
}
private:
contour_list_type m_ctrs;
db::box<C> m_bbox;
// Add a new hole entry to the end.
// This is done somewhat more intelligently here because
// we want to avoid excessive copying.
contour_type &add_hole ()
{
if (m_ctrs.size () == m_ctrs.capacity ()) {
// if the capacity is less than expected, create
// a new vector with twice as much elements and swap the elements
contour_list_type new_holes;
new_holes.reserve (m_ctrs.size () * 2);
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
new_holes.push_back (contour_type ());
h->swap (new_holes.back ());
}
// swap the old and new holes list
m_ctrs.swap (new_holes);
}
// add a new hole contour and return a reference to it
m_ctrs.push_back (contour_type ());
return m_ctrs.back ();
}
};
/**
* @brief A simple polygon class
*
* A simple polygon consists of an outer hull only.
* The contour consists of several points. The point
* list is normalized such that the leftmost, lowest point is
* the first one. The orientation is normalized such that
* the orientation of the hull contour is clockwise.
*
* It is in no way checked that the contours are not over-
* lapping. This must be ensured by the user of the object
* when filling the contours.
*/
template <class C>
class DB_PUBLIC_TEMPLATE simple_polygon
{
public:
typedef C coord_type;
typedef db::edge<coord_type> edge_type;
typedef db::simple_trans<coord_type> trans_type;
typedef db::point<coord_type> point_type;
typedef db::vector<coord_type> vector_type;
typedef db::box<coord_type> box_type;
typedef db::coord_traits<coord_type> coord_traits;
typedef typename coord_traits::distance_type distance_type;
typedef typename coord_traits::perimeter_type perimeter_type;
typedef typename coord_traits::area_type area_type;
typedef polygon_contour<C> contour_type;
typedef tl::vector<contour_type> contour_list_type;
typedef db::polygon_edge_iterator< simple_polygon<C>, db::unit_trans<C> > polygon_edge_iterator;
typedef db::polygon_contour_iterator< contour_type, db::unit_trans<C> > polygon_contour_iterator;
typedef db::object_tag< simple_polygon<C> > tag;
/**
* @brief The default constructor.
*
* The default constructor creates a empty polygon.
*/
simple_polygon ()
{
// .. nothing yet ..
}
/**
* @brief The box constructor.
*
* Creates a polygon from a box
*/
explicit simple_polygon (const db::box<C> &b)
{
// fill the contour
point_type p[4];
p[0] = point_type (b.left (), b.bottom ());
p[1] = point_type (b.left (), b.top ());
p[2] = point_type (b.right (), b.top ());
p[3] = point_type (b.right (), b.bottom ());
m_hull.assign (p, p + 4, false, false /*don't compress*/);
m_bbox = b;
}
/**
* @brief The constructor from a polygon
*
* Creates a simple polygon from a polygon
* TODO: currently there is no treatment of holes!
*/
explicit simple_polygon (const db::polygon<C> &p)
{
assign_hull (p.hull ());
tl_assert (p.holes () == 0);
}
/**
* @brief The copy constructor from another polygon with a transformation
*
* The transformation must have the ability to transform a point to another point of type C.
*
* @param p The source polygon
* @param tr The transformation to apply
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class D, class T>
simple_polygon (const db::simple_polygon<D> &p, const T &tr, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
}
/**
* @brief The copy constructor from another polygon with a different coordinate type
*
* @param p The source polygon
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class D>
explicit simple_polygon (const db::simple_polygon<D> &p, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
db::point_coord_converter<C, D> tr;
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
}
/**
* @brief Compatibility with polygon_ref
*/
simple_polygon<C> instantiate () const
{
return *this;
}
/**
* @brief Compatibility with polygon_ref
*/
void instantiate (simple_polygon<C> &poly) const
{
poly = *this;
}
/**
* @brief The (dummy) translation operator
*/
void translate (const simple_polygon<C> &d, db::generic_repository<C> &, db::ArrayRepository &)
{
*this = d;
}
/**
* @brief The (dummy) translation operator with transformation
*/
template <class T>
void translate (const simple_polygon<C> &d, const T &t, db::generic_repository<C> &, db::ArrayRepository &)
{
*this = d;
transform (t);
}
/**
* @brief A less operator to establish a sorting order.
*/
bool operator< (const simple_polygon<C> &b) const
{
if (m_bbox < b.m_bbox) {
return true;
} else if (m_bbox != b.m_bbox) {
return false;
}
return m_hull < b.m_hull;
}
/**
* @brief Equality test
*/
bool operator== (const simple_polygon<C> &b) const
{
return m_hull == b.m_hull;
}
/**
* @brief Inequality test
*/
bool operator!= (const simple_polygon<C> &b) const
{
return !operator== (b);
}
/**
* @brief A fuzzy less operator to establish a sorting order.
*/
bool less (const simple_polygon<C> &b) const
{
if (m_bbox.less (b.m_bbox)) {
return true;
} else if (m_bbox.not_equal (b.m_bbox)) {
return false;
}
return m_hull.less (b.m_hull);
}
/**
* @brief Fuzzy equality test
*/
bool equal (const simple_polygon<C> &b) const
{
return m_hull.equal (b.m_hull);
}
/**
* @brief Fuzzy inequality test
*/
bool not_equal (const simple_polygon<C> &b) const
{
return ! equal (b);
}
/**
* @brief The hull "begin" point iterator
*
* The hull point sequence delivers the points that the hull
* is made of.
*/
polygon_contour_iterator begin_hull () const
{
return polygon_contour_iterator (& hull (), 0);
}
/**
* @brief The hull "end" point iterator
*
* The hull point sequence delivers the points that the hull
* is made of.
*/
polygon_contour_iterator end_hull () const
{
return polygon_contour_iterator (& hull (), hull ().size ());
}
/**
* @brief The hole "begin" point iterator
*
* The hole point sequence delivers the points that the specific hole
* is made of.
*/
polygon_contour_iterator begin_hole (unsigned int h) const
{
return polygon_contour_iterator (& begin_holes () [h], 0);
}
/**
* @brief The hole "end" point iterator
*
* The hole point sequence delivers the points that the specific hole
* is made of.
*/
polygon_contour_iterator end_hole (unsigned int h) const
{
return polygon_contour_iterator (& begin_holes () [h], begin_holes () [h].size ());
}
/**
* @brief Compress the polygon.
*
* Compressing means removing redundant points (identical, colinear)
*
* @param remove_reflected Removes reflected edges (spiked) if this parameter is true.
* @return The compressed polygon.
*/
simple_polygon<C> &compress (bool remove_reflected = false)
{
// compress the polygon by employing the transform method
m_hull.transform (db::unit_trans<C> (), true, remove_reflected);
m_bbox = m_hull.bbox ();
return *this;
}
/**
* @brief Transform the polygon.
*
* Transforms the polygon with the given transformation.
* Modifies the polygon with the transformed polygon.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*
* @return The transformed polygon.
*/
template <class Tr>
simple_polygon<C> &transform (const Tr &t, bool compress = default_compression<C> (), bool remove_reflected = false)
{
m_hull.transform (t, compress, remove_reflected);
// transform or recompute the bbox
if (t.is_ortho ()) {
m_bbox.transform (t);
} else {
m_bbox = m_hull.bbox ();
}
return *this;
}
/**
* @brief Transform the polygon.
*
* Transforms the polygon with the given transformation.
* Does not modify the polygon but returns the transformed polygon.
*
* @param t The transformation to apply.
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*
* @return The transformed polygon.
*/
template <class Tr>
simple_polygon<typename Tr::target_coord_type> transformed_ext (const Tr &t, bool compress = default_compression<typename Tr::target_coord_type> (), bool remove_reflected = false) const
{
typedef typename Tr::target_coord_type target_coord_type;
simple_polygon<target_coord_type> poly;
// transform the contours and add as new ones
poly.assign_hull (begin_hull (), end_hull (), t, compress, remove_reflected);
return poly;
}
/**
* @brief Transform the polygon.
*
* Transforms the polygon with the given transformation.
* Does not modify the polygon but returns the transformed polygon.
*
* @param t The transformation to apply.
*
* @return The transformed polygon.
*/
template <class Tr>
simple_polygon<typename Tr::target_coord_type> transformed (const Tr &t) const
{
return this->transformed_ext<Tr> (t);
}
/**
* @brief Returns the scaled polygon
*/
db::simple_polygon<db::DCoord>
scaled (double s) const
{
db::complex_trans<C, db::DCoord> ct (s);
return this->transformed (ct);
}
/**
* @brief Returns the moved polyon
*
* Moves the polygon by the given offset and returns the
* moved polygon. The polygon is not modified.
*
* @param p The distance to move the polygon.
*
* @return The moved polygon.
*/
simple_polygon<C> moved (const vector<C> &p) const
{
simple_polygon<C> b (*this);
b.move (p);
return b;
}
/**
* @brief Moves the polygon.
*
* Moves the polygon by the given offset and returns the
* moved polygon. The polygon is overwritten.
*
* @param p The distance to move the polygon.
*
* @return The moved polygon.
*/
simple_polygon<C> &move (const vector<C> &d)
{
m_bbox.move (d);
m_hull.move (d);
return *this;
}
/**
* @brief Returns the bounding box of the polygon
*/
const db::box<C> &box () const
{
return m_bbox;
}
/**
* @brief Clears the polygon
*/
void clear (unsigned int /*n*/ = 0)
{
m_bbox = db::box<C> ();
m_hull.clear ();
}
/**
* @brief Set the outer contour from another contour
*
* Replaces the outer contour by the given other contour
* The given contour must not be a hole contour and is_hole must be false.
*
* @param other The other contour
*/
void assign_hull (const contour_type &other)
{
tl_assert (! other.is_hole ());
m_hull = other;
m_bbox = m_hull.bbox ();
}
/**
* @brief Set the outer contour
*
* Replaces the outer contour by the points given by
* the sequence [start,end). This method will update the
* bounding box and normalize the hull, so it is oriented
* properly.
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*/
template <class I>
void assign_hull (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false)
{
m_hull.assign (start, end, false, compress, true /*normalize*/, remove_reflected);
m_bbox = m_hull.bbox ();
}
/**
* @brief Set the outer contour by a transformed set of points
*
* Replaces the outer contour by the points given by
* the sequence [start,end), transformed with the operator op.
* This method will update the bounding box and normalize the hull,
* so it is oriented properly.
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I, class T>
void assign_hull (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_hull.assign (start, end, op, false, compress, normalize, remove_reflected);
m_bbox = m_hull.bbox ();
}
/**
* @brief Outer contour
*/
const contour_type &hull () const
{
return m_hull;
}
/**
* @brief Returns true, if the polygon is a simple box
*/
bool is_box () const
{
return m_hull.size () == 4 && m_hull.is_rectilinear ();
}
/**
* @brief Returns true, if the polygon is rectilinear
*/
bool is_rectilinear () const
{
return m_hull.is_rectilinear ();
}
/**
* @brief Returns true, if the polygon is halfmanhattan
*/
bool is_halfmanhattan () const
{
return m_hull.is_halfmanhattan ();
}
/**
* @brief Returns a value indicating that the polygon is an empty one
*/
bool is_empty () const
{
return m_hull.size () == 0;
}
/**
* @brief The number of holes
*
* This method is provided for compatibility with the standard polygon but does
* always return 0.
*/
unsigned int holes () const
{
return 0;
}
/**
* @brief Hole iterator: start
*
* This method is provided for compatibility with the standard polygon but does
* always return the begin iterator of an empty list.
*/
typename contour_list_type::const_iterator begin_holes () const
{
// we exploit here the fact that the iterator of an empty vector is always 0
contour_list_type ctrs;
return ctrs.begin ();
}
/**
* @brief Hole iterator: end
*
* This method is provided for compatibility with the standard polygon but does
* always return the end iterator of an empty list.
*/
typename contour_list_type::const_iterator end_holes () const
{
// we exploit here the fact that the iterator of an empty vector is always 0
contour_list_type ctrs;
return ctrs.end ();
}
/**
* @brief A dummy implementation of "insert_hole" provided for template instantiation
*
* Asserts, if begin called.
*/
template <class I>
void insert_hole (I, I, bool /*compress*/ = default_compression<C> ())
{
tl_assert (false);
}
/**
* @brief A dummy implementation of "insert_hole" provided for template instantiation
*
* Asserts, if begin called.
*/
template <class I, class T>
void insert_hole (I, I, const T &, bool /*compress*/ = default_compression<C> ())
{
tl_assert (false);
}
/**
* @brief A dummy implementation of "sort_holes" provided for template instantiation
*
* Asserts, if begin called.
*/
void sort_holes ()
{
tl_assert (false);
}
/**
* @brief A dummy implementation of "hole" provided for template instantiation
*
* Asserts, if begin called.
*/
const contour_type &hole (unsigned int /*h*/) const
{
tl_assert (false);
return hull (); // to please the compiler
}
/**
* @brief The edge iterator begin function
*
* The edge iterator delivers all edges of the polygon.
*
* @return the begin value of the iterator
*/
polygon_edge_iterator begin_edge () const
{
return polygon_edge_iterator (*this);
}
/**
* @brief The area of the polygon
*/
area_type area () const
{
return m_hull.area ();
}
/**
* @brief The area of the polygon - upper manhattan bound
*
* This gives the upper area bound for a manhattan approximation of the
* polygon.
*/
area_type area_upper_manhattan_bound () const
{
return m_hull.area_upper_manhattan_bound ();
}
/**
* @brief The area of the polygon times 2
* For integer area types, this is the more precise value as the division
* by 2 might round off.
*/
area_type area2 () const
{
return m_hull.area2 ();
}
/**
* @brief The area of the polygon - upper manhattan bound times 2
*
* This gives the upper area bound for a manhattan approximation of the
* polygon.
*/
area_type area_upper_manhattan_bound2 () const
{
return m_hull.area_upper_manhattan_bound2 ();
}
/**
* @brief The perimeter of the polygon
*/
perimeter_type perimeter () const
{
return m_hull.perimeter ();
}
/**
* @brief The string conversion function
*/
std::string to_string () const
{
std::string s = "(";
// the hull contour
for (polygon_contour_iterator p = begin_hull (); p != end_hull (); ++p) {
if (p != begin_hull ()) {
s += ";";
}
s += (*p).to_string ();
}
s += ")";
return s;
}
/**
* @brief Swap the polygon with another one
*
* The global std::swap function injected into the std namespace
* is redirected to this implementation.
*/
void swap (simple_polygon<C> &d)
{
m_hull.swap (d.m_hull);
std::swap (m_bbox, d.m_bbox);
}
/**
* @brief Direct access to the contours (used for iterators)
*/
const contour_type &contour (unsigned int /*n*/) const
{
return m_hull;
}
/**
* @brief Reduce the polygon
*
* Reduction of a polygon normalizes the polygon by extracting
* a suitable transformation and placing the polygon in a unique
* way.
*
* @return The transformation that must be applied to render the original polygon
*/
void reduce (simple_trans<coord_type> &tr)
{
if (m_hull.size () < 1) {
tr = simple_trans<coord_type> ();
} else {
point_type d (m_hull [0]);
move (-d);
tr = simple_trans<coord_type> (simple_trans<coord_type>::r0, d);
}
}
/**
* @brief Reduce the polygon
*
* Reduction of a polygon normalizes the polygon by extracting
* a suitable transformation and placing the polygon in a unique
* way.
*
* @return The transformation that must be applied to render the original polygon
*/
void reduce (disp_trans<coord_type> &tr)
{
if (m_hull.size () < 1) {
tr = disp_trans<coord_type> ();
} else {
vector_type d (m_hull [0]);
move (-d);
tr = disp_trans<coord_type> (d);
}
}
/**
* @brief Reduce the polygon for unit transformation references
*
* Does not do any reduction since no transformation can be provided.
*
* @return A unit transformation
*/
void reduce (unit_trans<C> &)
{
// .. nothing ..
}
/**
* @brief Return the number of points in the polygon
*/
size_t vertices () const
{
return m_hull.size ();
}
/**
* @brief Returns the area ratio between the polygon's bounding box and actual area
*
* This number is a measure how well the polygon is approximated by the bounding box.
* Values are bigger than 1 for well-formed polygons. Bigger values mean worse
* approximation.
*/
double area_ratio () const
{
area_type a = area2 ();
if (a == 0) {
// By our definition, an empty polygon has an area ratio of 0
return 0.0;
} else {
return double (box ().area ()) / (0.5 * a);
}
}
/**
* @brief Returns the area ratio between the polygon's bounding box and upper manhattan approximation bound
*
* This number is a measure how well the polygon is approximated by the bounding box,
* while assuming an outer manhattan approximation is a good measure for the polygon's
* area.
* Values are bigger than 1 for well-formed polygons. Bigger values mean worse
* approximation.
*/
double area_upper_manhattan_bound_ratio () const
{
area_type a = this->area_upper_manhattan_bound2 ();
if (a == 0) {
// By our definition, an empty polygon has an area ratio of 0
return 0.0;
} else {
return double (box ().area ()) / (0.5 * a);
}
}
void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const
{
db::mem_stat (stat, purpose, cat, m_hull, no_self, parent);
db::mem_stat (stat, purpose, cat, m_bbox, no_self, parent);
}
private:
contour_type m_hull;
db::box<C> m_bbox;
};
/**
* @brief A polygon reference
*
* A polygon reference is basically a proxy to a polygon and
* is used to implement polygon references with a repository.
*/
template <class Poly, class Trans>
class polygon_ref
: public shape_ref<Poly, Trans>
{
public:
typedef typename Poly::coord_type coord_type;
typedef typename Poly::point_type point_type;
typedef typename Poly::box_type box_type;
typedef typename Poly::edge_type edge_type;
typedef Trans trans_type;
typedef Poly polygon_type;
typedef db::polygon_edge_iterator<Poly, trans_type> polygon_edge_iterator;
typedef db::polygon_contour_iterator<typename polygon_type::contour_type, trans_type> polygon_contour_iterator;
typedef db::generic_repository<coord_type> repository_type;
typedef typename Poly::distance_type distance_type;
typedef typename Poly::perimeter_type perimeter_type;
typedef typename Poly::area_type area_type;
typedef db::object_tag< polygon_ref<Poly, Trans> > tag;
/**
* @brief The default constructor.
*
* The default constructor creates a invalid polygon reference
*/
polygon_ref ()
: shape_ref<Poly, Trans> ()
{
// .. nothing yet ..
}
/**
* @brief The constructor creating a reference from an actual polygon
*/
polygon_ref (const polygon_type &p, repository_type &rep)
: shape_ref<Poly, Trans> (p, rep)
{
// .. nothing yet ..
}
/**
* @brief The constructor creating a reference from an polygon pointer and transformation
*
* The polygon pointer passed is assumed to reside in a proper repository.
*/
polygon_ref (const polygon_type *p, const Trans &t)
: shape_ref<Poly, Trans> (p, t)
{
// .. nothing yet ..
}
/**
* @brief The translation constructor.
*
* This constructor allows one to copy a polygon reference from one
* repository to another
*/
polygon_ref (const polygon_ref &ref, repository_type &rep)
: shape_ref<Poly, Trans> (ref, rep)
{
// .. nothing yet ..
}
/**
* @brief The edge iterator
*
* The edge iterator delivers all edges of the polygon.
*
* @return the begin value of the iterator
*/
polygon_edge_iterator begin_edge () const
{
return polygon_edge_iterator (this->obj (), this->trans ());
}
/**
* @brief The edge iterator for a given contour
*
* The edge iterator delivers all edges of the polygon for the given contour.
* Contour 0 is the hull, 1 the first hole and so forth.
*
* @return the begin value of the iterator
*/
polygon_edge_iterator begin_edge (unsigned int ctr) const
{
return polygon_edge_iterator (this->obj (), ctr, this->trans ());
}
/**
* @brief The hull iterator begin function
*/
polygon_contour_iterator begin_hull () const
{
if (this->trans ().is_mirror ()) {
return polygon_contour_iterator (--(this->obj ().end_hull ()), this->trans (), true);
} else {
return polygon_contour_iterator (this->obj ().begin_hull (), this->trans (), false);
}
}
/**
* @brief The hull iterator end function
*/
polygon_contour_iterator end_hull () const
{
if (this->trans ().is_mirror ()) {
return polygon_contour_iterator (--(this->obj ().begin_hull ()), this->trans (), true);
} else {
return polygon_contour_iterator (this->obj ().end_hull (), this->trans (), false);
}
}
/**
* @brief The hole iterator begin function
*/
polygon_contour_iterator begin_hole (unsigned int h) const
{
if (this->trans ().is_mirror ()) {
return polygon_contour_iterator (--(this->obj ().end_hole (h)), this->trans (), true);
} else {
return polygon_contour_iterator (this->obj ().begin_hole (h), this->trans (), false);
}
}
/**
* @brief The hull iterator end function
*/
polygon_contour_iterator end_hole (unsigned int h) const
{
if (this->trans ().is_mirror ()) {
return polygon_contour_iterator (--(this->obj ().begin_hole (h)), this->trans (), true);
} else {
return polygon_contour_iterator (this->obj ().end_hole (h), this->trans (), false);
}
}
/**
* @brief The area ratio of the polygon
*/
double area_ratio () const
{
return this->obj ().area_ratio ();
}
/**
* @brief The area of the polygon
*/
area_type area () const
{
return this->obj ().area ();
}
/**
* @brief The area of the polygon times 2
* For integer area types, this is the more precise value as the division
* by 2 might round off.
*/
area_type area2 () const
{
return this->obj ().area2 ();
}
/**
* @brief The perimeter of the polygon
*/
perimeter_type perimeter () const
{
return this->obj ().perimeter ();
}
/**
* @brief Returns a value indicating whether the polygon is a box
*/
bool is_box () const
{
return this->obj ().is_box ();
}
/**
* @brief Returns a value indicating whether the polygon is rectilinear
*/
bool is_rectilinear () const
{
return this->obj ().is_rectilinear ();
}
/**
* @brief Returns the number of vertices
*/
size_t vertices () const
{
return this->obj ().vertices ();
}
/**
* @brief Return the transformed object
*
* This version does not change the object and is const.
*/
template <class TargetTrans>
polygon_ref<Poly, TargetTrans> transformed (const TargetTrans &t) const
{
polygon_ref<Poly, TargetTrans> pref (this->ptr (), this->trans ());
pref.transform (t);
return pref;
}
/**
* @brief A dummy implementation of the "scaled" function for API compatibility
*/
polygon_ref<Poly, Trans> scaled (double) const
{
tl_assert (false); // not implemented
return *this;
}
};
/**
* @brief Binary * operator (transformation)
*
* Transforms the polygon reference with the given transformation and
* returns the result.
*
* @param t The transformation to apply
* @param p The polygon reference to transform
* @return t * p
*/
template <class Poly, class Tr, class TargetTr>
inline polygon_ref<Poly, TargetTr>
operator* (const TargetTr &t, const polygon_ref<Poly, Tr> &p)
{
return p.transformed (t);
}
/**
* @brief Binary * operator (transformation)
*
* Transforms the polygon with the given transformation and
* returns the result.
*
* @param t The transformation to apply
* @param p The polygon to transform
* @return t * p
*/
template <class Tr>
inline polygon<typename Tr::target_coord_type>
operator* (const Tr &t, const polygon<typename Tr::coord_type> &p)
{
return p.transformed (t);
}
/**
* @brief Binary * operator (transformation)
*
* Transforms the simple polygon with the given transformation and
* returns the result.
*
* @param t The transformation to apply
* @param p The polygon to transform
* @return t * p
*/
template <class Tr>
inline simple_polygon<typename Tr::target_coord_type>
operator* (const Tr &t, const simple_polygon<typename Tr::coord_type> &p)
{
return p.transformed (t);
}
/**
* @brief Binary * operator (scaling)
*
* @param p The polygon to scale.
* @param s The scaling factor
*
* @return The scaled polygon
*/
template <class C>
inline polygon<db::DCoord>
operator* (const polygon<C> &p, double s)
{
return p.scaled (s);
}
/**
* @brief Binary * operator (scaling)
*
* @param p The polygon to scale.
* @param s The scaling factor
*
* @return The scaled polygon
*/
template <class C>
inline simple_polygon<db::DCoord>
operator* (const simple_polygon<C> &p, double s)
{
return p.scaled (s);
}
/**
* @brief Inside predicate
*
* This template function returns 1, if the point is inside (not on)
* the polygon. It returns 0, if the point is on the polygon and -1
* if outside. It is made into a template in order to be able to operate
* on every kind of edge iterator.
*
* @param edge The edge iterator of the polygon
* @param pt The point to test
*/
template<class Iter, class Point>
int inside_poly (Iter edge, const Point &pt)
{
int wrapcount_left = 0;
while (! edge.at_end ()) {
if ((*edge).p1 ().y () <= pt.y () && (*edge).p2 ().y () > pt.y ()) {
int side = (*edge).side_of (pt);
if (side < 0) {
++wrapcount_left;
} else if (side == 0) {
// "on" the line is excluded in the predicate
return 0;
}
} else if ((*edge).p2 ().y () <= pt.y () && (*edge).p1 ().y () > pt.y ()) {
int side = (*edge).side_of (pt);
if (side > 0) {
--wrapcount_left;
} else if (side == 0) {
// "on" the line is excluded in the predicate
return 0;
}
} else if ((*edge).p1 ().y () == pt.y () && (*edge).p2 ().y () == pt.y () &&
(((*edge).p1 ().x () <= pt.x () && (*edge).p2 ().x () >= pt.x ()) ||
((*edge).p2 ().x () <= pt.x () && (*edge).p1 ().x () >= pt.x ()))) {
// "on" the horizontal line is excluded in the predicate
return 0;
}
++edge;
}
return (wrapcount_left != 0) ? 1 : -1;
}
/**
* @brief Output stream insertion operator
*/
template <class C>
inline std::ostream &
operator<< (std::ostream &os, const polygon<C> &p)
{
return (os << p.to_string ());
}
/**
* @brief Output stream insertion operator
*/
template <class C>
inline std::ostream &
operator<< (std::ostream &os, const simple_polygon<C> &p)
{
return (os << p.to_string ());
}
/**
* @brief The standard polygon typedef
*/
typedef polygon<db::Coord> Polygon;
/**
* @brief The double coordinate polygon typedef
*/
typedef polygon<db::DCoord> DPolygon;
/**
* @brief The simple polygon typedef
*/
typedef simple_polygon<db::Coord> SimplePolygon;
/**
* @brief The double coordinate simple polygon typedef
*/
typedef simple_polygon<db::DCoord> DSimplePolygon;
/**
* @brief The standard polygon reference typedef
*/
typedef polygon_ref<Polygon, Disp> PolygonRef;
/**
* @brief The double coordinate polygon reference typedef
*/
typedef polygon_ref<DPolygon, DDisp> DPolygonRef;
/**
* @brief The simple polygon reference typedef
*/
typedef polygon_ref<SimplePolygon, Disp> SimplePolygonRef;
/**
* @brief The double coordinate simple polygon reference typedef
*/
typedef polygon_ref<DSimplePolygon, DDisp> DSimplePolygonRef;
/**
* @brief The standard polygon reference (without transformation) typedef
*/
typedef polygon_ref<Polygon, UnitTrans> PolygonPtr;
/**
* @brief The double coordinate polygon reference (without transformation) typedef
*/
typedef polygon_ref<DPolygon, DUnitTrans> DPolygonPtr;
/**
* @brief The simple polygon reference (without transformation) typedef
*/
typedef polygon_ref<SimplePolygon, UnitTrans> SimplePolygonPtr;
/**
* @brief The double coordinate simple polygon reference (without transformation) typedef
*/
typedef polygon_ref<DSimplePolygon, DUnitTrans> DSimplePolygonPtr;
/**
* @brief Collect memory statistics
*/
template <class X>
inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const polygon_contour<X> &x, bool no_self = false, void *parent = 0)
{
x.mem_stat (stat, purpose, cat, no_self, parent);
}
/**
* @brief Collect memory statistics
*/
template <class X>
inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const polygon<X> &x, bool no_self = false, void *parent = 0)
{
x.mem_stat (stat, purpose, cat, no_self, parent);
}
/**
* @brief Collect memory statistics
*/
template <class X>
inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const simple_polygon<X> &x, bool no_self = false, void *parent = 0)
{
x.mem_stat (stat, purpose, cat, no_self, parent);
}
} // namespace db
namespace std
{
// injecting a global std::swap for polygons into the
// std namespace
template <class C>
void swap (db::polygon<C> &a, db::polygon<C> &b)
{
a.swap (b);
}
// injecting a global std::swap for polygons into the
// std namespace
template <class C>
void swap (db::simple_polygon<C> &a, db::simple_polygon<C> &b)
{
a.swap (b);
}
} // namespace std
namespace tl
{
/**
* @brief Special extractors for the polygons
*/
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Polygon &p);
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::DPolygon &p);
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::SimplePolygon &p);
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::DSimplePolygon &p);
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Polygon &p);
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::DPolygon &p);
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::SimplePolygon &p);
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::DSimplePolygon &p);
} // namespace tl
#endif