GDS2 reader/writer: paths can now use the Multi-XY extension.

This commit is contained in:
Matthias Koefferlein 2017-09-03 23:24:50 +02:00
parent 0f0be1b0bc
commit 56b9c73015
6 changed files with 226 additions and 19 deletions

View File

@ -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<db::Point> 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 ();
}
}

View File

@ -108,6 +108,7 @@ private:
bool m_allow_multi_xy_records;
unsigned int m_box_mode;
std::map <tl::string, std::vector<std::string> > m_context_info;
std::vector <db::Point> m_all_points;
void read_context_info_cell ();
void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record);

View File

@ -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);

View File

@ -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

View File

@ -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<db::Point> 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<db::Point> 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);
}

View File

@ -17,7 +17,16 @@
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
@ -26,7 +35,16 @@
<string>GDS2 Writer Options</string>
</property>
<layout class="QGridLayout">
<property name="margin">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
@ -70,7 +88,7 @@
<widget class="QCheckBox" name="multi_xy_cbx">
<property name="text">
<string>Multi-XY record mode for boundaries
(enables infinitely large polygons at the cost of compatibility)</string>
(enables infinitely large polygons/paths at the cost of compatibility)</string>
</property>
</widget>
</item>
@ -107,7 +125,16 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>