From 56b9c730156c8384250795942137d40e87d5c749 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 Sep 2017 23:24:50 +0200 Subject: [PATCH] GDS2 reader/writer: paths can now use the Multi-XY extension. --- src/db/db/dbGDS2ReaderBase.cc | 57 +++++++-- src/db/db/dbGDS2ReaderBase.h | 1 + src/db/db/dbGDS2WriterBase.cc | 31 +++-- src/db/db/dbGDS2WriterBase.h | 2 +- src/db/unit_tests/dbGDS2Writer.cc | 119 ++++++++++++++++++ src/laybasic/laybasic/GDS2WriterOptionPage.ui | 35 +++++- 6 files changed, 226 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbGDS2ReaderBase.cc b/src/db/db/dbGDS2ReaderBase.cc index b877e6f28..0e3703bc7 100644 --- a/src/db/db/dbGDS2ReaderBase.cc +++ b/src/db/db/dbGDS2ReaderBase.cc @@ -604,13 +604,13 @@ GDS2ReaderBase::read_boundary (db::Layout &layout, db::Cell &cell, bool from_box // Try to detect Multi-XY records. A good indication may be a huge point count. if (xy_length > 2000) { - std::vector all_points; - all_points.reserve (xy_length * 2); // allocate some (hopefully enough) elements + m_all_points.clear (); + m_all_points.reserve (xy_length * 2); // allocate some (hopefully enough) elements while (true) { for (GDS2XY *xy = xy_data; xy < xy_data + xy_length; ++xy) { - all_points.push_back (pt_conv (*xy)); + m_all_points.push_back (pt_conv (*xy)); } if ((rec_id = get_record ()) == sXY) { @@ -626,11 +626,11 @@ GDS2ReaderBase::read_boundary (db::Layout &layout, db::Cell &cell, bool from_box } // remove redundant start and endpoint - if (! all_points.empty () && all_points.back () == all_points.front ()) { - all_points.pop_back (); + if (! m_all_points.empty () && m_all_points.back () == m_all_points.front ()) { + m_all_points.pop_back (); } - poly.assign_hull (all_points.begin (), all_points.end (), false /*no compression*/); + poly.assign_hull (m_all_points.begin (), m_all_points.end (), false /*no compression*/); } else { @@ -662,6 +662,9 @@ GDS2ReaderBase::read_boundary (db::Layout &layout, db::Cell &cell, bool from_box while ((rec_id = get_record ()) == sXY) { // read over multi-XY records + if (! m_allow_multi_xy_records) { + error (tl::to_string (QObject::tr ("Multiple XY records detected on BOUNDARY element (reader is configured not to allow this)"))); + } } unget_record (rec_id); @@ -740,9 +743,39 @@ GDS2ReaderBase::read_path (db::Layout &layout, db::Cell &cell) // this will copy the path: db::Path path; + + // Try to detect Multi-XY records. A good indication may be a huge point count. + if (xy_length > 2000) { + + m_all_points.clear (); + m_all_points.reserve (xy_length * 2); // allocate some (hopefully enough) elements + + while (true) { + + for (GDS2XY *xy = xy_data; xy < xy_data + xy_length; ++xy) { + m_all_points.push_back (pt_conv (*xy)); + } + + if ((rec_id = get_record ()) == sXY) { + xy_data = get_xy_data (xy_length); + if (! m_allow_multi_xy_records) { + error (tl::to_string (QObject::tr ("Multiple XY records detected on PATH element (reader is configured not to allow this)"))); + } + } else { + unget_record (rec_id); + break; + } + + } + + path.assign (m_all_points.begin (), m_all_points.end ()); + + } else { + path.assign (xy_data, xy_data + xy_length, pt_conv); + } + path.width (w); path.extensions (bgn_ext, end_ext); - path.assign (xy_data, xy_data + xy_length, pt_conv); path.round (type == 1); if (path.points () < 1) { @@ -761,7 +794,17 @@ GDS2ReaderBase::read_path (db::Layout &layout, db::Cell &cell) } } else { + + while ((rec_id = get_record ()) == sXY) { + // read over multi-XY records + if (! m_allow_multi_xy_records) { + error (tl::to_string (QObject::tr ("Multiple XY records detected on PATH element (reader is configured not to allow this)"))); + } + } + unget_record (rec_id); + finish_element (); + } } diff --git a/src/db/db/dbGDS2ReaderBase.h b/src/db/db/dbGDS2ReaderBase.h index b489e1d74..03cd876ad 100644 --- a/src/db/db/dbGDS2ReaderBase.h +++ b/src/db/db/dbGDS2ReaderBase.h @@ -108,6 +108,7 @@ private: bool m_allow_multi_xy_records; unsigned int m_box_mode; std::map > m_context_info; + std::vector m_all_points; void read_context_info_cell (); void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record); diff --git a/src/db/db/dbGDS2WriterBase.cc b/src/db/db/dbGDS2WriterBase.cc index 4a2f138bd..f5c003337 100644 --- a/src/db/db/dbGDS2WriterBase.cc +++ b/src/db/db/dbGDS2WriterBase.cc @@ -313,7 +313,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S shape->polygon (poly); write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false); } else { - write_path (layer, datatype, sf, *shape, layout, shape->prop_id ()); + write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ()); } } else if (shape->is_box ()) { write_box (layer, datatype, sf, *shape, layout, shape->prop_id ()); @@ -499,7 +499,7 @@ GDS2WriterBase::write_box (int layer, int datatype, double sf, const db::Shape & } void -GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id) +GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape &shape, bool multi_xy, const db::Layout &layout, db::properties_id_type prop_id) { // instantiate the path and draw db::Path path; @@ -549,11 +549,28 @@ GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape } - write_record_size (4 + int16_t (path.points ()) * 2 * 4); - write_record (sXY); - for (db::Path::iterator p = path.begin (); p != path.end (); ++p) { - write_int (scale (sf, (*p).x ())); - write_int (scale (sf, (*p).y ())); + size_t n = path.points (); + + db::Path::iterator p = path.begin (); + while (n > 0) { + + // determine number of points to write (all or slice for multi XY mode) + size_t nxy = n; + if (n > 8100 && multi_xy) { + nxy = 8000; + } + + write_record_size (4 + int16_t (nxy) * 2 * 4); + write_record (sXY); + + // write path .. + for ( ; p != path.end () && nxy > 0; ++p) { + write_int (scale (sf, (*p).x ())); + write_int (scale (sf, (*p).y ())); + --nxy; + --n; + } + } finish (layout, prop_id); diff --git a/src/db/db/dbGDS2WriterBase.h b/src/db/db/dbGDS2WriterBase.h index a0d7c49ed..cd168a812 100644 --- a/src/db/db/dbGDS2WriterBase.h +++ b/src/db/db/dbGDS2WriterBase.h @@ -242,7 +242,7 @@ protected: /** * @brief Write a shape as path */ - void write_path (int layer, int datatype, double sf, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id); + void write_path (int layer, int datatype, double sf, const db::Shape &shape, bool multi_xy, const db::Layout &layout, db::properties_id_type prop_id); /** * @brief Write a shape as text diff --git a/src/db/unit_tests/dbGDS2Writer.cc b/src/db/unit_tests/dbGDS2Writer.cc index 42e33dc86..9c82a9a54 100644 --- a/src/db/unit_tests/dbGDS2Writer.cc +++ b/src/db/unit_tests/dbGDS2Writer.cc @@ -1007,4 +1007,123 @@ TEST(115) EXPECT_EQ (std::string (os.string ()), std::string (expected)) } +TEST(116) +{ + // big paths with multi-xy + db::Manager m; + db::Layout g (&m); + + db::LayerProperties lp1; + lp1.layer = 1; + lp1.datatype = 0; + + g.insert_layer (0, lp1); + + db::Cell &c1 (g.cell (g.add_cell ("TOP"))); + + db::Path path; + path.width (100); + std::vector pts; + for (int i = 0; i < 10000; ++i) { + pts.push_back (db::Point (i * 10, (i % 10) * 1000)); + } + path.assign (pts.begin (), pts.end ()); + c1.shapes (0).insert (path); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_116.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + db::GDS2WriterOptions gds2_options; + gds2_options.multi_xy_records = true; + options.set_format ("GDS2"); + options.set_options (gds2_options); + db::Writer writer (options); + writer.write (g, out); + } + + db::Layout gg; + + { + db::LoadLayoutOptions options; + db::GDS2ReaderOptions gds2_options; + gds2_options.allow_multi_xy_records = true; + options.set_options (gds2_options); + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (gg); + } + + EXPECT_EQ (gg.cell_by_name ("TOP").first, true); + const db::Cell &cc1 (gg.cell (gg.cell_by_name ("TOP").second)); + + EXPECT_EQ (gg.get_properties (0) == lp1, true); + EXPECT_EQ (cc1.shapes (0).size (), size_t (1)); + + db::Shape s1 = *cc1.shapes (0).begin (db::ShapeIterator::All); + db::Path pp; + s1.path (pp); + EXPECT_EQ (pp == path, true); +} + +TEST(117) +{ + // big polygons with multi-xy + + db::Manager m; + db::Layout g (&m); + + db::LayerProperties lp1; + lp1.layer = 1; + lp1.datatype = 0; + + g.insert_layer (0, lp1); + + db::Cell &c1 (g.cell (g.add_cell ("TOP"))); + + db::Polygon poly; + std::vector pts; + for (int i = 0; i < 10000; ++i) { + pts.push_back (db::Point (i * 10, (i % 10) * 1000)); + } + poly.assign_hull (pts.begin (), pts.end ()); + c1.shapes (0).insert (poly); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_117.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + db::GDS2WriterOptions gds2_options; + gds2_options.multi_xy_records = true; + options.set_format ("GDS2"); + options.set_options (gds2_options); + db::Writer writer (options); + writer.write (g, out); + } + + db::Layout gg; + + { + db::LoadLayoutOptions options; + db::GDS2ReaderOptions gds2_options; + gds2_options.allow_multi_xy_records = true; + options.set_options (gds2_options); + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (gg); + } + + EXPECT_EQ (gg.cell_by_name ("TOP").first, true); + const db::Cell &cc1 (gg.cell (gg.cell_by_name ("TOP").second)); + + EXPECT_EQ (gg.get_properties (0) == lp1, true); + EXPECT_EQ (cc1.shapes (0).size (), size_t (1)); + + db::Shape s1 = *cc1.shapes (0).begin (db::ShapeIterator::All); + db::Polygon pp; + s1.polygon (pp); + EXPECT_EQ (pp == poly, true); +} diff --git a/src/laybasic/laybasic/GDS2WriterOptionPage.ui b/src/laybasic/laybasic/GDS2WriterOptionPage.ui index 6a7a00508..4cc503688 100644 --- a/src/laybasic/laybasic/GDS2WriterOptionPage.ui +++ b/src/laybasic/laybasic/GDS2WriterOptionPage.ui @@ -17,7 +17,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -26,7 +35,16 @@ GDS2 Writer Options - + + 9 + + + 9 + + + 9 + + 9 @@ -70,7 +88,7 @@ Multi-XY record mode for boundaries -(enables infinitely large polygons at the cost of compatibility) +(enables infinitely large polygons/paths at the cost of compatibility) @@ -107,7 +125,16 @@ QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0