mirror of https://github.com/KLayout/klayout.git
Fixed #116 (polygon cutting issue)
Last step: polygon cut algorithm with fallback: merge before cut on invalid polygons.
This commit is contained in:
parent
9622a2e669
commit
a4b396535f
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace db
|
namespace db
|
||||||
{
|
{
|
||||||
|
|
@ -187,7 +188,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class PolygonType, class Edge>
|
template <class PolygonType, class Edge>
|
||||||
void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
||||||
{
|
{
|
||||||
typedef typename PolygonType::point_type point_type;
|
typedef typename PolygonType::point_type point_type;
|
||||||
typedef typename PolygonType::coord_type coord_type;
|
typedef typename PolygonType::coord_type coord_type;
|
||||||
|
|
@ -268,7 +269,7 @@ void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygo
|
||||||
if (nc == 0) {
|
if (nc == 0) {
|
||||||
// the hull is fully on the right side -> just output the input polygon and that's it.
|
// the hull is fully on the right side -> just output the input polygon and that's it.
|
||||||
right_of_line->put (&input);
|
right_of_line->put (&input);
|
||||||
return;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// remember hole contours for later assignment
|
// remember hole contours for later assignment
|
||||||
hole_polygons.push_back (PolygonType ());
|
hole_polygons.push_back (PolygonType ());
|
||||||
|
|
@ -310,7 +311,7 @@ void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (jj == loose_ends.end ()) {
|
if (jj == loose_ends.end ()) {
|
||||||
tl_assert (false); // @@@ cannot cut
|
return false; // cannot cut (self-overlapping, self-intersecting)
|
||||||
}
|
}
|
||||||
std::swap (*jj, *i);
|
std::swap (*jj, *i);
|
||||||
}
|
}
|
||||||
|
|
@ -454,12 +455,156 @@ void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygo
|
||||||
right_of_line->put (&*hole);
|
right_of_line->put (&*hole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class PolygonType>
|
||||||
|
struct get_sink_type;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct get_sink_type<db::Polygon> { typedef db::PolygonSink result; };
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct get_sink_type<db::SimplePolygon> { typedef db::SimplePolygonSink result; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A polygon sink for the edge processor that feeds the polygon into the cut algorithm
|
||||||
|
*/
|
||||||
|
template <class Sink, class PolygonType, class Edge>
|
||||||
|
struct cut_polygon_sink
|
||||||
|
: public Sink
|
||||||
|
{
|
||||||
|
cut_polygon_sink (const Edge &_line, CutPolygonReceiverBase *_right_of_line)
|
||||||
|
: line (_line), right_of_line (_right_of_line)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void put (const PolygonType &poly)
|
||||||
|
{
|
||||||
|
tl_assert (_cut_polygon_internal (poly, line, right_of_line));
|
||||||
|
}
|
||||||
|
|
||||||
|
Edge line;
|
||||||
|
CutPolygonReceiverBase *right_of_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The cut algorithm driver with fallback to polygon merging when the cut fails
|
||||||
|
* TODO: this is kind of inefficient as we will first merge all and the cut just a half.
|
||||||
|
* This means for producing both parts we do this twice. But remember, this is just a
|
||||||
|
* fallback.
|
||||||
|
*/
|
||||||
|
template <class PolygonType, class Edge>
|
||||||
|
void cut_polygon_internal_int (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
||||||
|
{
|
||||||
|
bool ok = _cut_polygon_internal (input, line, right_of_line);
|
||||||
|
if (! ok) {
|
||||||
|
|
||||||
|
// If the cut operation fails on the plain input, merge the input polygon and try again
|
||||||
|
|
||||||
|
db::EdgeProcessor ep;
|
||||||
|
ep.insert_sequence (input.begin_edge ());
|
||||||
|
db::SimpleMerge op;
|
||||||
|
|
||||||
|
cut_polygon_sink<typename get_sink_type<PolygonType>::result, PolygonType, Edge> sink (line, right_of_line);
|
||||||
|
db::PolygonGenerator pg (sink);
|
||||||
|
ep.process (pg, op);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A transforming receiver that is put between a int cut algorithm and the double output receiver
|
||||||
|
*/
|
||||||
|
template <class PolygonType, class IPolygonType>
|
||||||
|
class cut_polygon_receiver_double_impl
|
||||||
|
: public CutPolygonReceiverBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cut_polygon_receiver_double_impl ()
|
||||||
|
: mp_next (0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void set_next (CutPolygonReceiverBase *next)
|
||||||
|
{
|
||||||
|
mp_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_trans (const db::CplxTrans &tr)
|
||||||
|
{
|
||||||
|
m_tr = tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void put (const void *p)
|
||||||
|
{
|
||||||
|
PolygonType pp = ((const IPolygonType *) p)->transformed (m_tr, false);
|
||||||
|
mp_next->put ((void *) &pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CutPolygonReceiverBase *mp_next;
|
||||||
|
db::CplxTrans m_tr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class PolygonType>
|
||||||
|
class cut_polygon_receiver_double;
|
||||||
|
|
||||||
|
// template specializations for the double types
|
||||||
|
template <> class cut_polygon_receiver_double<db::DPolygon> : public cut_polygon_receiver_double_impl<db::DPolygon, db::Polygon> { };
|
||||||
|
template <> class cut_polygon_receiver_double<db::DSimplePolygon> : public cut_polygon_receiver_double_impl<db::DSimplePolygon, db::SimplePolygon> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The cut algorithm driver for double types
|
||||||
|
* This driver will first try to guess a database unit (based on the bounding box) and then
|
||||||
|
* transform the polygon to int. On output, the polygon is transformed back to double.
|
||||||
|
*/
|
||||||
|
template <class PolygonType, class Edge>
|
||||||
|
void cut_polygon_internal_double (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
||||||
|
{
|
||||||
|
db::DBox bbox = input.box ();
|
||||||
|
bbox += db::DBox (0, 0, 0, 0);
|
||||||
|
bbox += line.bbox ();
|
||||||
|
|
||||||
|
// guess DBU
|
||||||
|
double dbu = std::max (1e-10, std::max (bbox.width (), bbox.height ()) / (std::numeric_limits<db::Coord>::max () / 2));
|
||||||
|
dbu = pow (10.0, ceil (log10 (dbu)));
|
||||||
|
|
||||||
|
db::CplxTrans tr (dbu);
|
||||||
|
cut_polygon_receiver_double<PolygonType> rec;
|
||||||
|
rec.set_trans (tr);
|
||||||
|
rec.set_next (right_of_line);
|
||||||
|
|
||||||
|
cut_polygon_internal_int (input.transformed (tr.inverted (), false), line.transformed (tr.inverted ()), &rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> DB_PUBLIC void cut_polygon_internal (const db::Polygon &polygon, const db::Polygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||||
|
{
|
||||||
|
cut_polygon_internal_int (polygon, line, right_of_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||||
|
{
|
||||||
|
cut_polygon_internal_int (polygon, line, right_of_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||||
|
{
|
||||||
|
cut_polygon_internal_double (polygon, line, right_of_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||||
|
{
|
||||||
|
cut_polygon_internal_double (polygon, line, right_of_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
template DB_PUBLIC void cut_polygon_internal<> (const db::Polygon &polygon, const db::Polygon::edge_type &line, CutPolygonReceiverBase *right_of_line);
|
|
||||||
template DB_PUBLIC void cut_polygon_internal<> (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line);
|
|
||||||
template DB_PUBLIC void cut_polygon_internal<> (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, CutPolygonReceiverBase *right_of_line);
|
|
||||||
template DB_PUBLIC void cut_polygon_internal<> (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line);
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Implementation of split_polygon
|
// Implementation of split_polygon
|
||||||
|
|
|
||||||
|
|
@ -2069,3 +2069,175 @@ TEST(322)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cut self-overlapping polygon
|
||||||
|
TEST(400)
|
||||||
|
{
|
||||||
|
std::vector <db::Point> c;
|
||||||
|
c.push_back (db::Point (0, 0));
|
||||||
|
c.push_back (db::Point (0, 100));
|
||||||
|
c.push_back (db::Point (1000, 100));
|
||||||
|
c.push_back (db::Point (1000, 1000));
|
||||||
|
c.push_back (db::Point (0, 1000));
|
||||||
|
c.push_back (db::Point (0, 900));
|
||||||
|
c.push_back (db::Point (900, 900));
|
||||||
|
c.push_back (db::Point (900, 0));
|
||||||
|
|
||||||
|
{
|
||||||
|
db::Polygon in;
|
||||||
|
in.assign_hull (c.begin (), c.end ());
|
||||||
|
std::vector<db::Polygon> right_of;
|
||||||
|
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)");
|
||||||
|
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::SimplePolygon in;
|
||||||
|
in.assign_hull (c.begin (), c.end ());
|
||||||
|
std::vector<db::SimplePolygon> right_of;
|
||||||
|
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)");
|
||||||
|
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut self-overlapping polygon (with double types)
|
||||||
|
TEST(401)
|
||||||
|
{
|
||||||
|
std::vector <db::DPoint> c;
|
||||||
|
c.push_back (db::DPoint (0, 0));
|
||||||
|
c.push_back (db::DPoint (0, 100));
|
||||||
|
c.push_back (db::DPoint (1000, 100));
|
||||||
|
c.push_back (db::DPoint (1000, 1000));
|
||||||
|
c.push_back (db::DPoint (0, 1000));
|
||||||
|
c.push_back (db::DPoint (0, 900));
|
||||||
|
c.push_back (db::DPoint (900, 900));
|
||||||
|
c.push_back (db::DPoint (900, 0));
|
||||||
|
|
||||||
|
{
|
||||||
|
db::DPolygon in;
|
||||||
|
in.assign_hull (c.begin (), c.end ());
|
||||||
|
std::vector<db::DPolygon> right_of;
|
||||||
|
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)");
|
||||||
|
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::DSimplePolygon in;
|
||||||
|
in.assign_hull (c.begin (), c.end ());
|
||||||
|
std::vector<db::DSimplePolygon> right_of;
|
||||||
|
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)");
|
||||||
|
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (2));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)");
|
||||||
|
EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut empty polygons
|
||||||
|
TEST(402)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
db::Polygon in;
|
||||||
|
std::vector<db::Polygon> right_of;
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
db::SimplePolygon in;
|
||||||
|
std::vector<db::SimplePolygon> right_of;
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
db::DPolygon in;
|
||||||
|
std::vector<db::DPolygon> right_of;
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
db::DSimplePolygon in;
|
||||||
|
std::vector<db::DSimplePolygon> right_of;
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut point-like polygons
|
||||||
|
TEST(403)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
db::Polygon in (db::Box (1000, 0, 1000, 0));
|
||||||
|
std::vector<db::Polygon> right_of;
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (1));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-(
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::SimplePolygon in (db::Box (1000, 0, 1000, 0));
|
||||||
|
std::vector<db::SimplePolygon> right_of;
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (1));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-(
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::DPolygon in (db::DBox (1000, 0, 1000, 0));
|
||||||
|
std::vector<db::DPolygon> right_of;
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (1));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-(
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::DSimplePolygon in (db::DBox (1000, 0, 1000, 0));
|
||||||
|
std::vector<db::DSimplePolygon> right_of;
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (1));
|
||||||
|
EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-(
|
||||||
|
right_of.clear ();
|
||||||
|
db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of));
|
||||||
|
EXPECT_EQ (right_of.size (), size_t (0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue