diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 4e5e03581..e3d1edba8 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -450,7 +450,7 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co } EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index e6cd117c1..7e31ca54a 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -642,7 +642,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, } EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); @@ -664,6 +664,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord std::auto_ptr result (new FlatEdgePairs ()); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index e40c54898..081ac4c99 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -1404,6 +1404,7 @@ DeepEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord ensure_merged_edges_valid (); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 572e7a6ba..1d2cd5673 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1466,6 +1466,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons ensure_merged_polygons_valid (); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); @@ -1494,6 +1495,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, b ensure_merged_polygons_valid (); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbEdgePairRelations.cc b/src/db/db/dbEdgePairRelations.cc index 2c60966bb..2ba777d99 100644 --- a/src/db/db/dbEdgePairRelations.cc +++ b/src/db/db/dbEdgePairRelations.cc @@ -66,6 +66,12 @@ bool euclidian_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge db::Edge g (other); int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); + + // "kissing corner" issue: force include zero if the edges are collinear and overlap. + if (! include_zero && s1 == 0 && s2 == 0 && e.intersect (g)) { + include_zero = true; + } + int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e @@ -203,6 +209,12 @@ static bool var_near_part_of_edge (bool include_zero, db::Coord d, db::Coord dd, db::Edge g (other); int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); + + // "kissing corner" issue: force include zero if the edges are collinear and overlap + if (! include_zero && s1 == 0 && s2 == 0 && e.intersect (g)) { + include_zero = true; + } + int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index eb1114e1a..82a70313f 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -66,6 +66,21 @@ Edge2EdgeCheckBase::prepare_next_pass () return false; } +static inline bool shields (const db::EdgePair &ep, const db::Edge &q) +{ + db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ()); + db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ()); + + std::pair ip1 = pe1.intersect_point (q); + std::pair ip2 = pe2.intersect_point (q); + + if (ip1.first && ip2.first) { + return ip1.second != ip2.second || (ip1.second != q.p1 () && ip2.second != q.p2 ()); + } else { + return false; + } +} + void Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) { @@ -128,8 +143,7 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { if (! m_ep_discarded [*i]) { db::EdgePair ep = m_ep [*i].normalized (); - if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && - db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { + if (shields (ep, *o2)) { m_ep_discarded [*i] = true; } } diff --git a/src/db/unit_tests/dbEdgePairRelations.cc b/src/db/unit_tests/dbEdgePairRelations.cc index 2097e08ae..da7061089 100644 --- a/src/db/unit_tests/dbEdgePairRelations.cc +++ b/src/db/unit_tests/dbEdgePairRelations.cc @@ -346,7 +346,7 @@ TEST(7) res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, -20)), &output); EXPECT_EQ (res, false); f.set_include_zero (false); - res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, -20)), &output); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, 11)), &output); EXPECT_EQ (res, false); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (1, 30), db::Point (1, -20)), &output); EXPECT_EQ (res, true); @@ -355,3 +355,50 @@ TEST(7) EXPECT_EQ (res, false); } +TEST(8_KissingCornerProblem) +{ + // The kissing corner problem is solved by allowing distance-0 width and space relations and checking them + // if the projection is >0. + + db::EdgeRelationFilter f (db::WidthRelation, 10); + f.set_include_zero (false); + db::EdgePair output; + bool res; + + f.set_metrics (db::Euclidian); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,90;0,100):(0,110;0,100)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,40;0,100):(0,110;0,50)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,0;0,-10)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f = db::EdgeRelationFilter (db::SpaceRelation, 10); + f.set_include_zero (false); + + f.set_metrics (db::Euclidian); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,90):(0,100;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,40):(0,50;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,10;0,0):(0,-10;0,0)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); +} diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index 23e7fd371..1e4b27501 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -740,7 +740,7 @@ TEST(20) EXPECT_EQ (r1.has_valid_edges (), false); db::Edges r2 (db::RecursiveShapeIterator (ly, ly.cell (top), l2), false); EXPECT_EQ (r2.has_valid_edges (), false); - EXPECT_EQ (r1.separation_check (r2, 20).to_string (), "(50,0;50,30)/(40,40;40,10);(63,30;80,30)/(97,40;80,40);(80,30;80,20)/(80,40;80,50);(50,40;50,57)/(40,40;40,23);(80,70;80,40)/(80,40;80,70);(60,40;50,40)/(30,40;40,40)"); + EXPECT_EQ (r1.separation_check (r2, 20).to_string (), "(50,0;50,30)/(40,40;40,10);(63,30;80,30)/(97,40;80,40);(50,40;50,57)/(40,40;40,23);(80,70;80,40)/(80,40;80,70)"); EXPECT_EQ (r1.separation_check (r2, 20, false, db::Projection).to_string (), "(50,10;50,30)/(40,30;40,10);(80,70;80,40)/(80,40;80,70)"); EXPECT_EQ (r1.separation_check (r2, 20, false, db::Euclidian, 90, 1).to_string (), "(50,0;50,30)/(40,40;40,10);(80,70;80,40)/(80,40;80,70)"); } diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index c3a0a68c5..1a78bf780 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -533,3 +533,89 @@ TEST(12_NetlistJoinLabels) CHECKPOINT (); compare_netlists (_this, output, au); } + +TEST(13a_KissingCorners) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_13a.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/kissing_corners.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au13a.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + CHECKPOINT (); + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(13b_KissingCornersDeep) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_13b.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/kissing_corners.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au13b.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + CHECKPOINT (); + db::compare_layouts (_this, layout, au, db::NoNormalization); +} diff --git a/testdata/drc/drcSimpleTests_13a.drc b/testdata/drc/drcSimpleTests_13a.drc new file mode 100644 index 000000000..4f1d62ed0 --- /dev/null +++ b/testdata/drc/drcSimpleTests_13a.drc @@ -0,0 +1,17 @@ +source($drc_test_source) +target($drc_test_target, "TOP") + +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(100.nm).output(100, 0) +l1.space(100.nm).output(101, 0) +l1.notch(100.nm).output(102, 0) +l2.separation(l3, 100.nm).output(103, 0) +l2.overlap(l3, 100.nm).output(104, 0) + diff --git a/testdata/drc/drcSimpleTests_13b.drc b/testdata/drc/drcSimpleTests_13b.drc new file mode 100644 index 000000000..5dcc56571 --- /dev/null +++ b/testdata/drc/drcSimpleTests_13b.drc @@ -0,0 +1,19 @@ +source($drc_test_source) +target($drc_test_target, "TOP") + +deep + +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(100.nm).output(100, 0) +l1.space(100.nm).output(101, 0) +l1.notch(100.nm).output(102, 0) +l2.separation(l3, 100.nm).output(103, 0) +l2.overlap(l3, 100.nm).output(104, 0) + diff --git a/testdata/drc/drcSimpleTests_au13a.gds b/testdata/drc/drcSimpleTests_au13a.gds new file mode 100644 index 000000000..0e7e5b8c4 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au13a.gds differ diff --git a/testdata/drc/drcSimpleTests_au13b.gds b/testdata/drc/drcSimpleTests_au13b.gds new file mode 100644 index 000000000..0e7e5b8c4 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au13b.gds differ diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index bf5fabfd9..022252773 100644 Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ diff --git a/testdata/drc/kissing_corners.gds b/testdata/drc/kissing_corners.gds new file mode 100644 index 000000000..af4b474ff Binary files /dev/null and b/testdata/drc/kissing_corners.gds differ