Reduce risk of DRC polygon split artefacts in deep mode (#722)

* Fixed a segfault with simple 'klayout -v'

* is_halfmanhattan polygon predicate, confine polygon splitting to halfmanhattan

Splitting any-angle polygons for area reduction in the
deep processor creates a risk of introducing grid-snap
artefacts. Hence we avoid this.
This commit is contained in:
Matthias Köfferlein 2021-02-05 23:28:04 +01:00 committed by GitHub
parent c837b306be
commit e31d7afb64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 181 additions and 15 deletions

View File

@ -601,7 +601,9 @@ ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db
}
}
if ((m_max_vertex_count >= 4 && poly.vertices () > m_max_vertex_count) || (m_area_ratio > 2.0 && poly.area_ratio () > m_area_ratio)) {
// NOTE: only halfmanhattan polygons are guaranteed not to generate grid snap artefacts when splitting.
// This is important to maintain the connection integrity of shape clusters.
if (poly.is_halfmanhattan () && ((m_max_vertex_count >= 4 && poly.vertices () > m_max_vertex_count) || (m_area_ratio > 2.0 && poly.area_ratio () > m_area_ratio))) {
std::vector <db::Polygon> split_polygons;
db::split_polygon (poly, split_polygons);

View File

@ -743,7 +743,29 @@ public:
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
@ -1671,6 +1693,19 @@ public:
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 the number of points in the polygon
*/
@ -2801,6 +2836,14 @@ public:
return m_hull.is_rectilinear ();
}
/**
* @brief Returns true, if the polygon is halfmanhattan
*/
bool is_halfmanhattan () const
{
return m_hull.is_halfmanhattan ();
}
/**
* @brief The number of holes
*

View File

@ -75,11 +75,6 @@ struct simple_polygon_defs
return c->hull ().size ();
}
static bool is_rectilinear (C *c)
{
return c->hull ().is_rectilinear ();
}
static bool is_empty (C *c)
{
return c->hull ().size () == 0;
@ -349,9 +344,16 @@ struct simple_polygon_defs
method_ext ("is_empty?", &is_empty,
"@brief Returns a value indicating whether the polygon is empty\n"
) +
method_ext ("is_rectilinear?", &is_rectilinear,
method ("is_rectilinear?", &C::is_rectilinear,
"@brief Returns a value indicating whether the polygon is rectilinear\n"
) +
method ("is_halfmanhattan?", &C::is_halfmanhattan,
"@brief Returns a value indicating whether the polygon is half-manhattan\n"
"Half-manhattan polygons have edges which are multiples of 45 degree. These polygons can be clipped at a rectangle without "
"potential grid snapping.\n"
"\n"
"This predicate was introduced in version 0.27.\n"
) +
method_ext ("inside?", &inside, gsi::arg ("p"),
"@brief Gets a value indicating whether the given point is inside the polygon\n"
"If the given point is inside or on the edge the polygon, true is returned. "
@ -871,11 +873,6 @@ struct polygon_defs
return c->vertices ();
}
static bool is_rectilinear (C *c)
{
return c->is_rectilinear ();
}
static bool is_empty (C *c)
{
return c->vertices () == 0;
@ -1151,9 +1148,16 @@ struct polygon_defs
method_ext ("is_empty?", &is_empty,
"@brief Returns a value indicating whether the polygon is empty\n"
) +
method_ext ("is_rectilinear?", &is_rectilinear,
method ("is_rectilinear?", &C::is_rectilinear,
"@brief Returns a value indicating whether the polygon is rectilinear\n"
) +
method ("is_halfmanhattan?", &C::is_halfmanhattan,
"@brief Returns a value indicating whether the polygon is half-manhattan\n"
"Half-manhattan polygons have edges which are multiples of 45 degree. These polygons can be clipped at a rectangle without "
"potential grid snapping.\n"
"\n"
"This predicate was introduced in version 0.27.\n"
) +
method_ext ("hash", &hash_value,
"@brief Computes a hash value\n"
"Returns a hash value for the given polygon. This method enables polygons as hash keys.\n"

View File

@ -83,6 +83,8 @@ TEST(1)
EXPECT_EQ (tl::to_string (p.area_ratio ()), "1");
EXPECT_EQ (p.perimeter (), db::Polygon::perimeter_type (2200));
EXPECT_EQ (p.is_box (), true);
EXPECT_EQ (p.is_rectilinear (), true);
EXPECT_EQ (p.is_halfmanhattan (), true);
c2.push_back (db::Point (10, 10));
c2.push_back (db::Point (10, 390));
@ -96,6 +98,9 @@ TEST(1)
c3.push_back (db::Point (90, 510));
p.insert_hole (c3.begin (), c3.end ());
EXPECT_EQ (p.holes (), size_t (2));
EXPECT_EQ (p.is_box (), false);
EXPECT_EQ (p.is_rectilinear (), true);
EXPECT_EQ (p.is_halfmanhattan (), true);
EXPECT_EQ (p.to_string (), std::string ("(0,0;0,1000;100,1000;100,0/10,10;90,10;90,390;10,390/10,510;90,510;90,890;10,890)"));
db::DPolygon dp (p, db::cast_op<db::DPoint, db::Point> ());
@ -148,6 +153,27 @@ TEST(1)
p.clear ();
EXPECT_EQ (p, empty);
c1.clear ();
c1.push_back (db::Point (0, 0));
c1.push_back (db::Point (0, 1000));
c1.push_back (db::Point (100, 1100));
c1.push_back (db::Point (100, 0));
p.assign_hull (c1.begin (), c1.end ());
EXPECT_EQ (p.is_box (), false);
EXPECT_EQ (p.is_rectilinear (), false);
EXPECT_EQ (p.is_halfmanhattan (), true);
c1.clear ();
c1.push_back (db::Point (0, 0));
c1.push_back (db::Point (0, 1000));
c1.push_back (db::Point (100, 1101));
c1.push_back (db::Point (100, 0));
p.assign_hull (c1.begin (), c1.end ());
EXPECT_EQ (p.is_box (), false);
EXPECT_EQ (p.is_rectilinear (), false);
EXPECT_EQ (p.is_halfmanhattan (), false);
}
@ -172,6 +198,8 @@ TEST(2)
EXPECT_EQ (tl::to_string (p.area_ratio ()), "1");
EXPECT_EQ (p.perimeter (), db::SimplePolygon::perimeter_type (2000+200));
EXPECT_EQ (p.is_box (), true);
EXPECT_EQ (p.is_rectilinear (), true);
EXPECT_EQ (p.is_halfmanhattan (), true);
EXPECT_EQ (p.to_string (), "(0,0;0,1000;100,1000;100,0)");
db::DSimplePolygon dp (p, db::cast_op<db::DPoint, db::Point> ());
@ -213,6 +241,24 @@ TEST(2)
p.clear ();
EXPECT_EQ (p, empty);
c1.clear ();
c1.push_back (db::Point (0, 0));
c1.push_back (db::Point (0, 1000));
c1.push_back (db::Point (100, 1100));
c1.push_back (db::Point (100, 0));
p.assign_hull (c1.begin (), c1.end ());
EXPECT_EQ (p.is_rectilinear (), false);
EXPECT_EQ (p.is_halfmanhattan (), true);
c1.clear ();
c1.push_back (db::Point (0, 0));
c1.push_back (db::Point (0, 1000));
c1.push_back (db::Point (100, 1101));
c1.push_back (db::Point (100, 0));
p.assign_hull (c1.begin (), c1.end ());
EXPECT_EQ (p.is_rectilinear (), false);
EXPECT_EQ (p.is_halfmanhattan (), false);
}
TEST(3)

View File

@ -1147,3 +1147,8 @@ TEST(27d_advancedShielding)
{
run_test (_this, "27", true);
}
TEST(28_inputFragmentation)
{
run_test (_this, "28", true);
}

View File

@ -1474,7 +1474,7 @@ GuiApplication::start_recording ()
lay::Dispatcher *
GuiApplication::dispatcher () const
{
return mp_mw->dispatcher ();
return mp_mw ? mp_mw->dispatcher () : 0;
}
void

18
testdata/drc/drcSimpleTests_28.drc vendored Normal file
View File

@ -0,0 +1,18 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l1.width(4.995.um, projection, angle_limit(50)).output(100, 0)

BIN
testdata/drc/drcSimpleTests_28.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au28d.gds vendored Normal file

Binary file not shown.

View File

@ -34,6 +34,7 @@ class DBPolygon_TestClass < TestBase
assert_equal( a.is_box?, false )
assert_equal( a.is_empty?, true )
assert_equal( a.is_rectilinear?, false )
assert_equal( a.is_halfmanhattan?, false )
b = a.dup
a = RBA::DPolygon::new( [ RBA::DPoint::new( 0, 1 ), RBA::DPoint::new( 1, 5 ), RBA::DPoint::new( 5, 5 ) ] )
@ -44,6 +45,7 @@ class DBPolygon_TestClass < TestBase
assert_equal( a.num_points_hull, 3 )
assert_equal( a.is_empty?, false )
assert_equal( a.is_rectilinear?, false )
assert_equal( a.is_halfmanhattan?, false )
c = a.dup
assert_equal( a == b, false )
@ -55,6 +57,7 @@ class DBPolygon_TestClass < TestBase
assert_equal( a.is_box?, true )
assert_equal( a.is_empty?, false )
assert_equal( a.is_rectilinear?, true )
assert_equal( a.is_halfmanhattan?, true )
assert_equal( a.to_s, "(5,-10;5,15;20,15;20,-10)" )
assert_equal( RBA::Polygon::new(a).to_s, "(5,-10;5,15;20,15;20,-10)" )
assert_equal( a.num_points_hull, 4 )
@ -148,6 +151,17 @@ class DBPolygon_TestClass < TestBase
p = RBA::DPolygon::ellipse( RBA::DBox::new(-10000, -20000, 30000, 40000), 4 )
assert_equal(p.to_s, "(10000,-20000;-10000,10000;10000,40000;30000,10000)")
# halfmanhattan variants
p = RBA::DPolygon::new([ RBA::DPoint::new( 0, 0 ), RBA::DPoint::new( 0, 100 ), RBA::DPoint::new( 100, 100 ) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, false)
p = RBA::DPolygon::new([ RBA::DPoint::new( 0, 0 ), RBA::DPoint::new( 0, 100 ), RBA::DPoint::new( 100, 101 ) ])
assert_equal(p.is_halfmanhattan?, false)
assert_equal(p.is_rectilinear?, false)
p = RBA::DPolygon::new([ RBA::DPoint::new( 0, 0 ), RBA::DPoint::new( 0, 100 ), RBA::DPoint::new( 100, 100 ), RBA::DPoint::new( 100, 0) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, true)
end
# Polygon basics
@ -300,6 +314,17 @@ class DBPolygon_TestClass < TestBase
p = RBA::Polygon::ellipse( RBA::Box::new(-10000, -20000, 30000, 40000), 4 )
assert_equal(p.to_s, "(10000,-20000;-10000,10000;10000,40000;30000,10000)")
# halfmanhattan variants
p = RBA::Polygon::new([ RBA::Point::new( 0, 0 ), RBA::Point::new( 0, 100 ), RBA::Point::new( 100, 100 ) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, false)
p = RBA::Polygon::new([ RBA::Point::new( 0, 0 ), RBA::Point::new( 0, 100 ), RBA::Point::new( 100, 101 ) ])
assert_equal(p.is_halfmanhattan?, false)
assert_equal(p.is_rectilinear?, false)
p = RBA::Polygon::new([ RBA::Point::new( 0, 0 ), RBA::Point::new( 0, 100 ), RBA::Point::new( 100, 100 ), RBA::Point::new( 100, 0) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, true)
end
# Polygon parametrized edge iterator
@ -367,6 +392,7 @@ class DBPolygon_TestClass < TestBase
assert_equal(p.to_s, "()")
assert_equal(p.is_empty?, true)
assert_equal(p.is_rectilinear?, false)
assert_equal(p.is_halfmanhattan?, false)
pts = [ RBA::Point::new(0, 0) ]
p = RBA::Polygon::new(pts)

View File

@ -119,6 +119,17 @@ class DBSimplePolygon_TestClass < TestBase
p = RBA::DSimplePolygon::ellipse( RBA::DBox::new(-10000, -20000, 30000, 40000), 4 )
assert_equal(p.to_s, "(10000,-20000;-10000,10000;10000,40000;30000,10000)")
# halfmanhattan variants
p = RBA::DSimplePolygon::new([ RBA::DPoint::new( 0, 0 ), RBA::DPoint::new( 0, 100 ), RBA::DPoint::new( 100, 100 ) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, false)
p = RBA::DSimplePolygon::new([ RBA::DPoint::new( 0, 0 ), RBA::DPoint::new( 0, 100 ), RBA::DPoint::new( 100, 101 ) ])
assert_equal(p.is_halfmanhattan?, false)
assert_equal(p.is_rectilinear?, false)
p = RBA::DSimplePolygon::new([ RBA::DPoint::new( 0, 0 ), RBA::DPoint::new( 0, 100 ), RBA::DPoint::new( 100, 100 ), RBA::DPoint::new( 100, 0) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, true)
end
# SimplePolygon basics
@ -245,6 +256,17 @@ class DBSimplePolygon_TestClass < TestBase
p = RBA::SimplePolygon::ellipse( RBA::Box::new(-10000, -20000, 30000, 40000), 4 )
assert_equal(p.to_s, "(10000,-20000;-10000,10000;10000,40000;30000,10000)")
# halfmanhattan variants
p = RBA::SimplePolygon::new([ RBA::Point::new( 0, 0 ), RBA::Point::new( 0, 100 ), RBA::Point::new( 100, 100 ) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, false)
p = RBA::SimplePolygon::new([ RBA::Point::new( 0, 0 ), RBA::Point::new( 0, 100 ), RBA::Point::new( 100, 101 ) ])
assert_equal(p.is_halfmanhattan?, false)
assert_equal(p.is_rectilinear?, false)
p = RBA::SimplePolygon::new([ RBA::Point::new( 0, 0 ), RBA::Point::new( 0, 100 ), RBA::Point::new( 100, 100 ), RBA::Point::new( 100, 0) ])
assert_equal(p.is_halfmanhattan?, true)
assert_equal(p.is_rectilinear?, true)
end
# raw mode polygons