/* KLayout Layout Viewer Copyright (C) 2006-2017 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 */ #include "dbCellHullGenerator.h" #include "dbEdgeProcessor.h" #include "tlString.h" #include "utHead.h" static std::string h2s (const std::vector &hull) { std::string r; for (std::vector::const_iterator h = hull.begin (); h != hull.end (); ++h) { if (!r.empty ()) { r += ";"; } r += h->to_string (); } return r; } static bool check_hull (const std::vector &hull, const db::Shapes &shapes) { std::vector sp; for (db::ShapeIterator s = shapes.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Boxes | db::ShapeIterator::Paths); ! s.at_end (); ++s) { sp.push_back (db::Polygon ()); s->polygon (sp.back ()); } db::EdgeProcessor ep; std::vector out; ep.boolean (hull, sp, out, db::BooleanOp::BNotA, false, false); if (! out.empty ()) { std::cout << "check_hull(): not empty" << std::endl; for (std::vector::const_iterator p = out.begin (); p != out.end (); ++p) { std::cout << " " << p->to_string () << std::endl; } } return out.empty (); } TEST(1) { db::Manager m; db::Layout g (&m); unsigned int l1 = g.insert_layer (db::LayerProperties (1, 0)); unsigned int l2 = g.insert_layer (db::LayerProperties (2, 0)); db::Cell &c1 (g.cell (g.add_cell ())); c1.shapes (l1).insert (db::Box (0, 0, 2100, 2100)); c1.shapes (l2).insert (db::Box (-100, -100, 2000, 2000)); db::CellHullGenerator chg (g); std::vector hull; chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(-100,-100;-100,2000;0,2000;0,2100;2100,2100;2100,0;2000,0;2000,-100)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (0, 0, 2000, 200)); c1.shapes (l1).insert (db::Box (0, 0, 200, 2000)); hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,2000;200,2000;200,200;2000,200;2000,0)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (0, 0, 2000, 200)); c1.shapes (l1).insert (db::Box (0, 1800, 2000, 2000)); c1.shapes (l1).insert (db::Box (0, 0, 200, 2000)); hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,2000;2000,2000;2000,1800;200,1800;200,200;2000,200;2000,0)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (0, 0, 2000, 200)); c1.shapes (l1).insert (db::Box (0, 1800, 2000, 2000)); c1.shapes (l1).insert (db::Box (1800, 0, 2000, 2000)); c1.shapes (l1).insert (db::Box (0, 0, 200, 2000)); hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,2000;2000,2000;2000,0/200,200;1800,200;1800,1800;200,1800)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (0, 0, 2000, 100)); c1.shapes (l1).insert (db::Box (0, 150, 2000, 200)); c1.shapes (l1).insert (db::Box (0, 1800, 2000, 1900)); c1.shapes (l1).insert (db::Box (0, 1950, 2000, 2000)); c1.shapes (l1).insert (db::Box (1800, 0, 2000, 1900)); c1.shapes (l1).insert (db::Box (1950, 0, 2000, 2000)); c1.shapes (l1).insert (db::Box (0, 0, 100, 2000)); c1.shapes (l1).insert (db::Box (150, 0, 200, 2000)); hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,2000;2000,2000;2000,0/200,100;1800,100;1800,150;200,150/100,200;150,200;150,1800;100,1800/200,200;1800,200;1800,1800;200,1800/200,1900;1950,1900;1950,1950;200,1950)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (0, 0, 200, 200)); c1.shapes (l1).insert (db::Box (1800, 1800, 2000, 2000)); hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,200;200,200;200,0);(1800,1800;1800,2000;2000,2000;2000,1800)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); { db::Point pts[3] = { db::Point(0, 0), db::Point(0, 200), db::Point(200, 200) }; db::Polygon poly; poly.assign_hull (&pts[0], &pts[0] + 3); c1.shapes (l1).insert (poly); } { db::Point pts[3] = { db::Point(1800, 1800), db::Point(1800, 2000), db::Point(2000, 2000) }; db::Polygon poly; poly.assign_hull (&pts[0], &pts[0] + 3); c1.shapes (l1).insert (poly); } hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,200;200,200;200,0);(1800,1800;1800,2000;2000,2000;2000,1800)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (0, 0, 200, 200)); c1.shapes (l1).insert (db::Box (1800, 1800, 2000, 2000)); c1.shapes (l1).insert (db::Box (1500, 1800, 1700, 2000)); c1.shapes (l1).insert (db::Box (1500, 1500, 1700, 1700)); hull.clear (); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,200;200,200;200,0);(1500,1500;1500,2000;2000,2000;2000,1800;1700,1800;1700,1500)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); c1.shapes (l1).clear (); c1.shapes (l2).clear (); c1.shapes (l1).insert (db::Box (1900, 0, 2000, 2000)); c1.shapes (l1).insert (db::Box (1800, 0, 1900, 1950)); c1.shapes (l1).insert (db::Box (1700, 0, 1800, 1900)); c1.shapes (l1).insert (db::Box (1600, 0, 1700, 1950)); c1.shapes (l1).insert (db::Box (1500, 0, 1600, 1850)); c1.shapes (l1).insert (db::Box (1400, 0, 1500, 1950)); c1.shapes (l1).insert (db::Box (1300, 0, 1400, 1900)); c1.shapes (l1).insert (db::Box (1200, 0, 1300, 1750)); c1.shapes (l1).insert (db::Box (1100, 0, 1200, 1800)); c1.shapes (l1).insert (db::Box (1000, 0, 1100, 1950)); c1.shapes (l1).insert (db::Box ( 900, 0, 1000, 1800)); c1.shapes (l1).insert (db::Box ( 800, 0, 900, 1750)); c1.shapes (l1).insert (db::Box ( 700, 0, 800, 1700)); c1.shapes (l1).insert (db::Box ( 600, 0, 700, 1750)); c1.shapes (l1).insert (db::Box ( 500, 0, 600, 1850)); c1.shapes (l1).insert (db::Box ( 400, 0, 500, 1900)); c1.shapes (l1).insert (db::Box ( 300, 0, 400, 1950)); c1.shapes (l1).insert (db::Box ( 200, 0, 300, 1750)); c1.shapes (l1).insert (db::Box ( 100, 0, 200, 1800)); c1.shapes (l1).insert (db::Box ( 0, 0, 100, 1950)); hull.clear (); chg.set_complexity (0); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,1950;1000,1950;1000,2000;2000,2000;2000,0)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); hull.clear (); chg.set_complexity (20); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,1950;400,1950;400,1900;600,1900;600,1800;1000,1800;1000,1950;1900,1950;1900,2000;2000,2000;2000,0)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); hull.clear (); chg.set_complexity (40); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,1950;400,1950;400,1900;500,1900;500,1850;600,1850;600,1800;1000,1800;1000,1950;1200,1950;1200,1900;1400,1900;1400,1950;1900,1950;1900,2000;2000,2000;2000,0)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); hull.clear (); chg.set_complexity (1000); chg.generate_hull (c1, hull); EXPECT_EQ (h2s (hull), "(0,0;0,1950;100,1950;100,1800;200,1800;200,1750;300,1750;300,1950;400,1950;400,1900;500,1900;500,1850;600,1850;600,1750;700,1750;700,1700;800,1700;800,1750;900,1750;900,1800;1000,1800;1000,1950;1100,1950;1100,1800;1200,1800;1200,1750;1300,1750;1300,1900;1400,1900;1400,1950;1500,1950;1500,1850;1600,1850;1600,1950;1700,1950;1700,1900;1800,1900;1800,1950;1900,1950;1900,2000;2000,2000;2000,0)"); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); EXPECT_EQ (check_hull (hull, c1.shapes (l2)), true); } TEST(2) { db::Manager m; db::Layout g (&m); unsigned int l1 = g.insert_layer (db::LayerProperties (1, 0)); db::Cell &c1 (g.cell (g.add_cell ())); for (size_t i = 0; i < 10000; ++i) { db::Coord x = rand () % 2000; db::Coord y = rand () % 2000; c1.shapes (l1).insert (db::Box (x, y, x + 100, y + 100)); } db::CellHullGenerator chg (g); std::vector hull; chg.generate_hull (c1, hull); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); hull.clear (); chg.set_complexity (0); chg.generate_hull (c1, hull); EXPECT_EQ (hull.size (), size_t (1)); EXPECT_EQ (hull.front ().holes (), size_t (0)); EXPECT_EQ (hull.front ().hull ().size () <= 8, true); EXPECT_EQ (check_hull (hull, c1.shapes (l1)), true); }