
/*

  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

