mirror of https://github.com/KLayout/klayout.git
GDS2 reader/writer: paths can now use the Multi-XY extension.
This commit is contained in:
parent
0f0be1b0bc
commit
56b9c73015
|
|
@ -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 ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue