WIP, more testcases, debugging

This commit is contained in:
Matthias Koefferlein 2025-04-20 19:09:24 +02:00
parent 88a1ccbcfb
commit f71210c64a
6 changed files with 442 additions and 92 deletions

View File

@ -300,8 +300,14 @@ Edge::crosses (const db::DEdge &e, const db::DEdge &other)
bool
Edge::crosses_including (const db::DEdge &e, const db::DEdge &other)
{
return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) <= 0 &&
other.side_of (e.p1 ()) * other.side_of (e.p2 ()) <= 0;
int sa = e.side_of (other.p1 ());
int sb = e.side_of (other.p2 ());
int s1 = sa * sb;
int s2 = other.side_of (e.p1 ()) * other.side_of (e.p2 ());
// e can end on other and so can other end on e, but both may not be coincident
return s1 <= 0 && s2 <= 0 && ! (sa == 0 && sb == 0);
}
db::DPoint

View File

@ -1099,14 +1099,19 @@ Triangulation::find_vertexes_along_line (const db::DPoint &p1, const db::DPoint
}
std::vector<Vertex *> result;
result.push_back (v);
while (v) {
Vertex *vn = 0;
for (auto e = v->begin_edges (); e != v->end_edges (); ++e) {
Vertex *vv = (*e)->other (v);
if (db::vprod_sign (e12.d (), *vv - *v) == 0 && db::sprod_sign (e12.d (), *vv - *v) > 0 && db::sprod_sign (e12.d (), *vv - e12.p2 ()) < 0) {
int cs = 0;
if (db::vprod_sign (e12.d (), *vv - *v) == 0 && db::sprod_sign (e12.d (), *vv - *v) > 0 && (cs = db::sprod_sign (e12.d (), *vv - e12.p2 ())) <= 0) {
result.push_back (vv);
vn = vv;
if (cs < 0) {
// continue searching
vn = vv;
}
break;
}
}
@ -1377,8 +1382,6 @@ Triangulation::make_contours (const Poly &poly, const Trans &trans, std::vector<
void
Triangulation::create_constrained_delaunay (const db::Region &region, const CplxTrans &trans)
{
clear ();
std::vector<std::vector<Vertex *> > edge_contours;
for (auto p = region.begin_merged (); ! p.at_end (); ++p) {
@ -1389,15 +1392,8 @@ Triangulation::create_constrained_delaunay (const db::Region &region, const Cplx
}
void
Triangulation::create_constrained_delaunay (const db::Polygon &p, const std::vector<Point> &vertexes, const CplxTrans &trans)
Triangulation::create_constrained_delaunay (const db::Polygon &p, const CplxTrans &trans)
{
clear ();
unsigned int id = 0;
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
insert_point (trans * *v)->set_is_precious (true, id++);
}
std::vector<std::vector<Vertex *> > edge_contours;
make_contours (p, trans, edge_contours);
@ -1405,15 +1401,8 @@ Triangulation::create_constrained_delaunay (const db::Polygon &p, const std::vec
}
void
Triangulation::create_constrained_delaunay (const db::DPolygon &p, const std::vector<DPoint> &vertexes, const DCplxTrans &trans)
Triangulation::create_constrained_delaunay (const db::DPolygon &p, const DCplxTrans &trans)
{
clear ();
unsigned int id = 0;
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
insert_point (trans * *v)->set_is_precious (true, id++);
}
std::vector<std::vector<Vertex *> > edge_contours;
make_contours (p, trans, edge_contours);
@ -1458,6 +1447,8 @@ Triangulation::triangulate (const db::Region &region, const TriangulationParamet
{
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
clear ();
create_constrained_delaunay (region, db::CplxTrans (dbu));
refine (parameters);
}
@ -1467,6 +1458,24 @@ Triangulation::triangulate (const db::Region &region, const TriangulationParamet
{
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
clear ();
create_constrained_delaunay (region, trans);
refine (parameters);
}
void
Triangulation::triangulate (const db::Region &region, const std::vector<db::Point> &vertexes, const TriangulationParameters &parameters, const db::CplxTrans &trans)
{
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
clear ();
unsigned int id = 0;
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
insert_point (trans * *v)->set_is_precious (true, id++);
}
create_constrained_delaunay (region, trans);
refine (parameters);
}
@ -1482,7 +1491,16 @@ Triangulation::triangulate (const db::Polygon &poly, const std::vector<db::Point
{
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu));
db::CplxTrans trans (dbu);
clear ();
unsigned int id = 0;
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
insert_point (trans * *v)->set_is_precious (true, id++);
}
create_constrained_delaunay (poly, trans);
refine (parameters);
}
@ -1497,7 +1515,14 @@ Triangulation::triangulate (const db::Polygon &poly, const std::vector<db::Point
{
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
create_constrained_delaunay (poly, vertexes, trans);
clear ();
unsigned int id = 0;
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
insert_point (trans * *v)->set_is_precious (true, id++);
}
create_constrained_delaunay (poly, trans);
refine (parameters);
}
@ -1512,7 +1537,14 @@ Triangulation::triangulate (const db::DPolygon &poly, const std::vector<db::DPoi
{
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
create_constrained_delaunay (poly, vertexes, trans);
clear ();
unsigned int id = 0;
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
insert_point (trans * *v)->set_is_precious (true, id++);
}
create_constrained_delaunay (poly, trans);
refine (parameters);
}

View File

@ -142,6 +142,7 @@ public:
// more versions
void triangulate (const db::Region &region, const TriangulationParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void triangulate (const db::Region &region, const std::vector<db::Point> &vertexes, const TriangulationParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void triangulate (const db::Polygon &poly, const TriangulationParameters &parameters, double dbu = 1.0);
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulationParameters &parameters, double dbu = 1.0);
void triangulate (const db::Polygon &poly, const TriangulationParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
@ -181,6 +182,17 @@ public:
*/
std::vector<Vertex *> find_vertexes_along_line (const db::DPoint &p1, const db::DPoint &p2) const;
/**
* @brief Removes the outside triangles.
*
* This method is useful in combination with the "remove_outside_triangles = false" triangulation
* parameter. In this mode, outside triangles are not removed after triangulation (the
* triangulated area is convex). This enables use of the "find" functions.
*
* This method can be used to finally remove the outside triangles if no longer needed.
*/
void remove_outside_triangles ();
/**
* @brief Statistics: number of flips (fixing)
*/
@ -197,6 +209,37 @@ public:
return m_hops;
}
/**
* @brief Creates a constrained Delaunay triangulation from the given Region
*
* This method is used internally by the "triangulation" method to create the basic triangulation,
* followed by a "refine" step.
*/
void create_constrained_delaunay (const db::Region &region, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Creates a constrained Delaunay triangulation from the given Polygon
*
* This method is used internally by the "triangulation" method to create the basic triangulation,
* followed by a "refine" step.
*/
void create_constrained_delaunay (const db::Polygon &poly, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Creates a constrained Delaunay triangulation from the given DPolygon
*
* This method is used internally by the "triangulation" method to create the basic triangulation,
* followed by a "refine" step.
*/
void create_constrained_delaunay (const db::DPolygon &poly, const DCplxTrans &trans = db::DCplxTrans ());
/**
* @brief Refines the triangulation using the given parameters
*
* This method is used internally by the "triangulation" method after creating the basic triangulation.
*/
void refine (const TriangulationParameters &param);
protected:
/**
* @brief Checks the polygon graph for consistency
@ -254,26 +297,6 @@ protected:
*/
void constrain (const std::vector<std::vector<Vertex *> > &contours);
/**
* @brief Removes the outside triangles.
*/
void remove_outside_triangles ();
/**
* @brief Creates a constrained Delaunay triangulation from the given Region
*/
void create_constrained_delaunay (const db::Region &region, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Creates a constrained Delaunay triangulation from the given Polygon
*/
void create_constrained_delaunay (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Creates a constrained Delaunay triangulation from the given DPolygon
*/
void create_constrained_delaunay (const db::DPolygon &poly, const std::vector<DPoint> &vertexes, const DCplxTrans &trans);
/**
* @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion)
*/
@ -308,7 +331,6 @@ private:
void insert_new_vertex(Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles_out);
std::vector<Edge *> ensure_edge_inner (Vertex *from, Vertex *to);
void join_edges (std::vector<Edge *> &edges);
void refine (const TriangulationParameters &param);
};
} // namespace plc

View File

@ -155,24 +155,28 @@ TEST(collect_vertexes)
tris.insert_point (0.5, 0.5);
std::vector<db::plc::Vertex *> vertexes = tris.find_vertexes_along_line (db::DPoint (0, 0), db::DPoint (1.5, 1.5));
EXPECT_EQ (vertexes.size (), size_t (3));
if (vertexes.size () >= size_t (3)) {
EXPECT_EQ (vertexes [0]->to_string (), "(0.2, 0.2)");
EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)");
EXPECT_EQ (vertexes [2]->to_string (), "(1, 1)");
EXPECT_EQ (vertexes.size (), size_t (4));
if (vertexes.size () >= size_t (4)) {
EXPECT_EQ (vertexes [0]->to_string (), "(0, 0)");
EXPECT_EQ (vertexes [1]->to_string (), "(0.2, 0.2)");
EXPECT_EQ (vertexes [2]->to_string (), "(0.5, 0.5)");
EXPECT_EQ (vertexes [3]->to_string (), "(1, 1)");
}
vertexes = tris.find_vertexes_along_line (db::DPoint (0, 0), db::DPoint (1.0, 1.0));
EXPECT_EQ (vertexes.size (), size_t (2));
if (vertexes.size () >= size_t (2)) {
EXPECT_EQ (vertexes [0]->to_string (), "(0.2, 0.2)");
EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)");
EXPECT_EQ (vertexes.size (), size_t (4));
if (vertexes.size () >= size_t (4)) {
EXPECT_EQ (vertexes [0]->to_string (), "(0, 0)");
EXPECT_EQ (vertexes [1]->to_string (), "(0.2, 0.2)");
EXPECT_EQ (vertexes [2]->to_string (), "(0.5, 0.5)");
EXPECT_EQ (vertexes [3]->to_string (), "(1, 1)");
}
vertexes = tris.find_vertexes_along_line (db::DPoint (1, 1), db::DPoint (0.25, 0.25));
EXPECT_EQ (vertexes.size (), size_t (1));
if (vertexes.size () >= size_t (1)) {
EXPECT_EQ (vertexes [0]->to_string (), "(0.5, 0.5)");
EXPECT_EQ (vertexes.size (), size_t (2));
if (vertexes.size () >= size_t (2)) {
EXPECT_EQ (vertexes [0]->to_string (), "(1, 1)");
EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)");
}
}

View File

@ -43,24 +43,74 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
rnetwork.clear ();
db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ()));
auto inv_trans = trans.inverted ();
// NOTE: currently we treat polygon ports and points where the location is the center of the bounding box
std::vector<db::Point> vp = vertex_ports;
vp.reserve (vertex_ports.size () + polygon_ports.size ());
for (auto pp = polygon_ports.begin (); pp != polygon_ports.end (); ++pp) {
vp.push_back (pp->box ().center ());
}
db::plc::Graph plc;
db::plc::Triangulation tri (&plc);
tri.triangulate (polygon, vp, m_tri_param, trans);
std::unordered_map <const db::plc::Vertex *, size_t> pp_vertexes;
// create a network node for each triangle node
if (polygon_ports.empty ()) {
tri.triangulate (polygon, vertex_ports, m_tri_param, trans);
} else {
// Subtract the polygon ports from the original polygon and compute the intersection.
// Hence we have coincident edges that we can use to identify the nodes that are
// connected for the polygon ports
db::Region org (polygon);
db::Region pp (polygon_ports.begin (), polygon_ports.end ());
db::Region residual_poly = org - pp;
// We must not remove outside triangles yet, as we need them for "find_vertexes_along_line"
db::plc::TriangulationParameters param = m_tri_param;
param.remove_outside_triangles = false;
tri.clear ();
unsigned int id = 0;
for (auto v = vertex_ports.begin (); v != vertex_ports.end (); ++v) {
tri.insert_point (trans * *v)->set_is_precious (true, id++);
}
for (auto p = polygon_ports.begin (); p != polygon_ports.end (); ++p) {
// create vertexes for the port polygon vertexes - this ensures we will find vertexes
// on the edges of the polygons - yet, they may be outside of the original polygon.
// In that case they will not be considered
for (auto e = p->begin_edge (); !e.at_end (); ++e) {
tri.insert_point (trans * (*e).p1 ())->set_is_precious (true, id);
}
}
// perform the triangulation
tri.create_constrained_delaunay (residual_poly, trans);
tri.refine (param);
// identify the vertexes present for the polygon port -> store them inside pp_vertexes
for (auto p = polygon_ports.begin (); p != polygon_ports.end (); ++p) {
for (auto e = p->begin_edge (); !e.at_end (); ++e) {
// NOTE: this currently only works if one of the end points is an actual
// vertex.
auto vport = tri.find_vertexes_along_line (trans * (*e).p1 (), trans * (*e).p2 ());
for (auto v = vport.begin (); v != vport.end (); ++v) {
pp_vertexes.insert (std::make_pair (*v, p - polygon_ports.begin ()));
}
}
}
tri.remove_outside_triangles ();
}
// Create a network node for each triangle node.
std::unordered_map<const db::plc::Vertex *, pex::RNode *> vertex2node;
std::unordered_set<size_t> vports_present;
std::map<size_t, pex::RNode *> pport_nodes;
size_t internal_node_id = 0;
@ -73,28 +123,71 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
continue;
}
pex::RNode::node_type type = pex::RNode::Internal;
size_t port_index = 0;
pex::RNode *n = 0;
if (vertex->is_precious ()) {
size_t idx = vertex->id ();
if (idx >= vertex_ports.size ()) {
type = pex::RNode::PolygonPort;
port_index = size_t (idx) - vertex_ports.size ();
auto ipp = pp_vertexes.find (vertex);
if (ipp != pp_vertexes.end ()) {
size_t port_index = ipp->second;
auto pn = pport_nodes.find (port_index);
if (pn != pport_nodes.end ()) {
n = pn->second;
} else {
type = pex::RNode::VertexPort;
port_index = size_t (idx);
n = rnetwork.create_node (pex::RNode::PolygonPort, port_index);
pport_nodes.insert (std::make_pair (port_index, n));
n->location = trans * polygon_ports [port_index].box ();
}
} else if (vertex->is_precious ()) {
size_t port_index = size_t (vertex->id ());
if (port_index < vertex_ports.size ()) {
n = rnetwork.create_node (pex::RNode::VertexPort, port_index);
n->location = db::DBox (*vertex, *vertex);
vports_present.insert (port_index);
}
} else {
port_index = internal_node_id++;
n = rnetwork.create_node (pex::RNode::Internal, internal_node_id++);
n->location = db::DBox (*vertex, *vertex);
}
pex::RNode *n = rnetwork.create_node (type, port_index);
db::DPoint loc = *vertex;
n->location = db::DBox (loc, loc);
if (n) {
vertex2node.insert (std::make_pair (vertex, n));
}
vertex2node.insert (std::make_pair (vertex, n));
}
}
// check for vertex ports not assigned to a node
// -> this may be an indication for a vertex port inside a polygon port
for (size_t iv = 0; iv < vertex_ports.size (); ++iv) {
if (vports_present.find (iv) != vports_present.end ()) {
continue;
}
db::Point vp = vertex_ports [iv];
for (auto p = polygon_ports.begin (); p != polygon_ports.end (); ++p) {
if (p->box ().contains (vp) && db::inside_poly_test<db::Polygon> (*p) (vp) >= 0) {
auto ip = pport_nodes.find (p - polygon_ports.begin ());
if (ip != pport_nodes.end ()) {
// create a new vertex port and short it to the polygon port
auto n = rnetwork.create_node (pex::RNode::VertexPort, iv);
n->location = db::DBox (trans * vp, trans * vp);
rnetwork.create_element (pex::RElement::short_value (), n, ip->second);
}
}
}
}
@ -121,17 +214,22 @@ TriangulationRExtractor::create_conductances (const db::plc::Polygon &tri, const
const db::plc::Vertex *p0 = tri.vertex (i + 1);
const db::plc::Vertex *p1 = tri.vertex (i + 2);
double a = fabs (db::vprod (*pm1 - *p0, *p1 - *p0) * 0.5);
double lm1 = (*p0 - *pm1).sq_length ();
double l0 = (*p1 - *p0).sq_length ();
double l1 = (*pm1 - *p1).sq_length ();
double s = (l0 + l1 - lm1) / (8.0 * a);
auto i0 = vertex2node.find (p0);
auto im1 = vertex2node.find (pm1);
rnetwork.create_element (s, i0->second, im1->second);
if (i0->second != im1->second) {
double a = fabs (db::vprod (*pm1 - *p0, *p1 - *p0) * 0.5);
double lm1 = (*p0 - *pm1).sq_length ();
double l0 = (*p1 - *p0).sq_length ();
double l1 = (*pm1 - *p1).sq_length ();
double s = (l0 + l1 - lm1) / (8.0 * a);
rnetwork.create_element (s, i0->second, im1->second);
}
}
}

View File

@ -67,3 +67,191 @@ TEST(extraction)
"R V1 V0 10.0938"
)
}
TEST(extraction_with_polygon_ports)
{
db::Point contour[] = {
db::Point (0, 0),
db::Point (0, 100),
db::Point (1000, 100),
db::Point (1000, 0)
};
db::Polygon poly;
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
double dbu = 0.001;
pex::RNetwork rn;
pex::TriangulationRExtractor rex (dbu);
std::vector<db::Point> vertex_ports;
std::vector<db::Polygon> polygon_ports;
polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100)));
polygon_ports.push_back (db::Polygon (db::Box (1000, 0, 1100, 100)));
rex.extract (poly, vertex_ports, polygon_ports, rn);
EXPECT_EQ (rn.to_string (),
"R P1 P0 10"
)
}
TEST(extraction_with_polygon_ports_inside)
{
db::Point contour[] = {
db::Point (-100, 0),
db::Point (-100, 100),
db::Point (1100, 100),
db::Point (1100, 0)
};
db::Polygon poly;
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
double dbu = 0.001;
pex::RNetwork rn;
pex::TriangulationRExtractor rex (dbu);
std::vector<db::Point> vertex_ports;
std::vector<db::Polygon> polygon_ports;
polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100)));
polygon_ports.push_back (db::Polygon (db::Box (1000, 0, 1100, 100)));
rex.extract (poly, vertex_ports, polygon_ports, rn);
EXPECT_EQ (rn.to_string (),
"R P1 P0 10"
)
}
TEST(extraction_split_by_ports)
{
db::Point contour[] = {
db::Point (-100, 0),
db::Point (-100, 100),
db::Point (1100, 100),
db::Point (1100, 0)
};
db::Polygon poly;
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
double dbu = 0.001;
pex::RNetwork rn;
pex::TriangulationRExtractor rex (dbu);
std::vector<db::Point> vertex_ports;
std::vector<db::Polygon> polygon_ports;
polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100)));
polygon_ports.push_back (db::Polygon (db::Box (1100, 0, 1200, 100)));
polygon_ports.push_back (db::Polygon (db::Box (500, 0, 600, 100)));
rex.extract (poly, vertex_ports, polygon_ports, rn);
EXPECT_EQ (rn.to_string (),
"R P2 P0 5\n"
"R P1 P2 5"
)
}
TEST(extraction_split_by_butting_port)
{
db::Point contour[] = {
db::Point (-100, 0),
db::Point (-100, 100),
db::Point (1100, 100),
db::Point (1100, 0)
};
db::Polygon poly;
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
double dbu = 0.001;
pex::RNetwork rn;
pex::TriangulationRExtractor rex (dbu);
std::vector<db::Point> vertex_ports;
std::vector<db::Polygon> polygon_ports;
polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100)));
polygon_ports.push_back (db::Polygon (db::Box (1100, 0, 1200, 100)));
polygon_ports.push_back (db::Polygon (db::Box (500, 100, 600, 200)));
rex.extract (poly, vertex_ports, polygon_ports, rn);
EXPECT_EQ (rn.to_string (),
"R P2 P0 4.84211\n"
"R P1 P2 4.84211\n"
"R P0 P1 281.111"
)
}
TEST(extraction_with_outside_polygon_port)
{
db::Point contour[] = {
db::Point (-100, 0),
db::Point (-100, 100),
db::Point (1100, 100),
db::Point (1100, 0)
};
db::Polygon poly;
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
double dbu = 0.001;
pex::RNetwork rn;
pex::TriangulationRExtractor rex (dbu);
std::vector<db::Point> vertex_ports;
std::vector<db::Polygon> polygon_ports;
polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100)));
polygon_ports.push_back (db::Polygon (db::Box (1100, 0, 1200, 100)));
polygon_ports.push_back (db::Polygon (db::Box (500, 200, 600, 300)));
rex.extract (poly, vertex_ports, polygon_ports, rn);
EXPECT_EQ (rn.to_string (),
"R P1 P0 11"
)
}
TEST(extraction_with_polygon_ports_and_vertex_port_inside)
{
db::Point contour[] = {
db::Point (-100, 0),
db::Point (-100, 100),
db::Point (1100, 100),
db::Point (1100, 0)
};
db::Polygon poly;
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
double dbu = 0.001;
pex::RNetwork rn;
pex::TriangulationRExtractor rex (dbu);
std::vector<db::Point> vertex_ports;
vertex_ports.push_back (db::Point (-50, 50));
std::vector<db::Polygon> polygon_ports;
polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100)));
polygon_ports.push_back (db::Polygon (db::Box (1000, 0, 1100, 100)));
rex.extract (poly, vertex_ports, polygon_ports, rn);
EXPECT_EQ (rn.to_string (),
"R V0 P0 0\n" // shorted because V0 is inside P0
"R P1 P0 10"
)
}