diff --git a/src/db/db/db.pro b/src/db/db/db.pro
index 066dbb5d2..6914e12fa 100644
--- a/src/db/db/db.pro
+++ b/src/db/db/db.pro
@@ -124,6 +124,7 @@ SOURCES = \
gsiDeclDbLibrary.cc \
gsiDeclDbManager.cc \
gsiDeclDbMatrix.cc \
+ gsiDeclDbMetaInfo.cc \
gsiDeclDbPath.cc \
gsiDeclDbPoint.cc \
gsiDeclDbPolygon.cc \
diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc
index c4255f762..1cb4d698d 100644
--- a/src/db/db/dbCellMapping.cc
+++ b/src/db/db/dbCellMapping.cc
@@ -356,7 +356,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
&& (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())
&& (! include_cells || include_cells->find (*b) != include_cells->end ())) {
- db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b));
+ db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b);
new_cells.push_back (new_cell);
new_cells_b.push_back (*b);
diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc
index 741dded37..a71d9eb52 100644
--- a/src/db/db/dbCellVariants.cc
+++ b/src/db/db/dbCellVariants.cc
@@ -275,6 +275,7 @@ VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell
var_name += "$VAR" + tl::to_string (index);
ci_var = layout.add_cell (var_name.c_str ());
+ layout.add_meta_info (ci_var, layout.begin_meta (*c), layout.end_meta (*c));
copy_shapes (layout, ci_var, *c);
// a new entry for the variant
diff --git a/src/db/db/dbClip.cc b/src/db/db/dbClip.cc
index 72c926afc..d3d7bde90 100644
--- a/src/db/db/dbClip.cc
+++ b/src/db/db/dbClip.cc
@@ -556,7 +556,7 @@ make_clip_variants (const db::Layout &layout,
for (std::map , db::cell_index_type>::iterator v = variants.begin (); v != variants.end (); ++v) {
if (v->first.second != layout.cell (v->first.first).bbox () || &layout != &target_layout) {
// need for a new cell
- v->second = target_layout.add_cell (layout.cell_name (v->first.first));
+ v->second = target_layout.add_cell (layout, v->first.first);
} else {
v->second = v->first.first;
}
diff --git a/src/db/db/dbClipboardData.cc b/src/db/db/dbClipboardData.cc
index 36c515333..c8ade46af 100644
--- a/src/db/db/dbClipboardData.cc
+++ b/src/db/db/dbClipboardData.cc
@@ -201,7 +201,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C
cell_map.insert (std::make_pair (c->cell_index (), pc->cell_index ()));
} else {
// fallback: create a new cell
- cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ()))));
+ db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ());
+ cell_map.insert (std::make_pair (c->cell_index (), ci));
}
} else {
@@ -217,7 +218,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C
cell_map.insert (std::make_pair (c->cell_index (), tc));
}
} else {
- cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ()))));
+ db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ());
+ cell_map.insert (std::make_pair (c->cell_index (), ci));
}
}
@@ -313,7 +315,7 @@ ClipboardData::cell_for_cell (const db::Layout &layout, db::cell_index_type cell
return cm->second;
}
- db::cell_index_type target_cell_index = m_layout.add_cell (layout.cell_name (cell_index));
+ db::cell_index_type target_cell_index = m_layout.add_cell (layout, cell_index);
m_cell_index_map.insert (std::make_pair (cell_index, target_cell_index));
if (incomplete) {
diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc
index 738d9dea9..8cc57aacd 100644
--- a/src/db/db/dbColdProxy.cc
+++ b/src/db/db/dbColdProxy.cc
@@ -49,8 +49,8 @@ ColdProxy::cold_proxies_per_lib_name (const std::string &libname)
}
}
-ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info)
- : Cell (ci, layout), mp_context_info (new ProxyContextInfo (info))
+ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info)
+ : Cell (ci, layout), mp_context_info (new LayoutOrCellContextInfo (info))
{
if (! info.lib_name.empty ()) {
tl::MutexLocker locker (&s_map_mutex);
diff --git a/src/db/db/dbColdProxy.h b/src/db/db/dbColdProxy.h
index 4c2681e29..5c642a9a1 100644
--- a/src/db/db/dbColdProxy.h
+++ b/src/db/db/dbColdProxy.h
@@ -35,7 +35,7 @@
namespace db
{
-struct ProxyContextInfo;
+struct LayoutOrCellContextInfo;
/**
* @brief A cell specialization: a cold proxy representing a library or PCell which has gone out of scope
@@ -53,7 +53,7 @@ public:
*
* Creates a cold proxy represented by the ProxyContextInfo data.
*/
- ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info);
+ ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info);
/**
* @brief The destructor
@@ -68,7 +68,7 @@ public:
/**
* @brief Get the library id
*/
- const ProxyContextInfo &context_info () const
+ const LayoutOrCellContextInfo &context_info () const
{
return *mp_context_info;
}
@@ -102,7 +102,7 @@ public:
virtual std::string get_qualified_name () const;
private:
- ProxyContextInfo *mp_context_info;
+ LayoutOrCellContextInfo *mp_context_info;
ColdProxy (const ColdProxy &d);
ColdProxy &operator= (const ColdProxy &d);
diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc
index e8b006992..09b9e17c2 100644
--- a/src/db/db/dbEdgesUtils.cc
+++ b/src/db/db/dbEdgesUtils.cc
@@ -384,6 +384,8 @@ struct DetectTagEdgeSink
DetectTagEdgeSink (int tag)
: fail_tag (tag), result (true) { }
+ virtual void put (const db::Edge &) { }
+
virtual void put (const db::Edge &, int tag)
{
if (tag == fail_tag) {
diff --git a/src/db/db/dbHershey.cc b/src/db/db/dbHershey.cc
index 62b311d37..7e862d4a2 100644
--- a/src/db/db/dbHershey.cc
+++ b/src/db/db/dbHershey.cc
@@ -101,17 +101,22 @@ hershey_count_edges (const std::string &s, unsigned int f)
HersheyFont *fp = fonts [f];
size_t n = 0;
- for (const char *cp = s.c_str (); *cp; ++cp) {
+ for (const char *cp = s.c_str (); *cp; ) {
- unsigned char c = (unsigned char) *cp;
- if (c == '\012' || c == '\015') {
- if (c == '\015' && cp[1] == '\012') {
- ++cp;
+ if (tl::skip_newline (cp)) {
+
+ // skip new line - they don't contribute edges
+
+ } else {
+
+ uint32_t c = tl::utf32_from_utf8 (cp);
+
+ if (c < fp->end_char && c >= fp->start_char) {
+ n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start;
+ } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
+ n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start;
}
- } else if (c < fp->end_char && c >= fp->start_char) {
- n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start;
- } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
- n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start;
+
}
}
@@ -142,22 +147,27 @@ hershey_text_box (const std::string &s, unsigned int f)
int w = 0;
int h = fp->ymax;
- for (const char *cp = s.c_str (); *cp; ++cp) {
+ for (const char *cp = s.c_str (); *cp; ) {
+
+ if (tl::skip_newline (cp)) {
- unsigned char c = (unsigned char) *cp;
- if (c == '\012' || c == '\015') {
- if (c == '\015' && cp[1] == '\012') {
- ++cp;
- }
if (w > wl) {
wl = w;
}
+
hl += line_spacing + h - fp->ymin;
w = 0;
- } else if (c < fp->end_char && c >= fp->start_char) {
- w += fp->chars [c - fp->start_char].width;
- } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
- w += fp->chars [invalid_char - fp->start_char].width;
+
+ } else {
+
+ uint32_t c = tl::utf32_from_utf8 (cp);
+
+ if (c < fp->end_char && c >= fp->start_char) {
+ w += fp->chars [c - fp->start_char].width;
+ } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
+ w += fp->chars [invalid_char - fp->start_char].width;
+ }
+
}
}
@@ -167,32 +177,39 @@ hershey_text_box (const std::string &s, unsigned int f)
}
hl += h;
- return db::DBox (0, 0, wl, hl);
+ return db::DBox (0, fp->ymin, wl, hl);
}
-void
-hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts)
+void
+hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts, double &left, double &bottom)
{
+ left = 0.0;
+ bottom = 0.0;
+
HersheyFont *fp = fonts [f];
int hl = 0;
int w = 0;
int h = fp->ymax;
- for (const char *cp = s.c_str (); *cp; ++cp) {
+ for (const char *cp = s.c_str (); *cp; ) {
+
+ if (tl::skip_newline (cp)) {
- unsigned char c = (unsigned char) *cp;
- if (c == '\012' || c == '\015') {
- if (c == '\015' && cp[1] == '\012') {
- ++cp;
- }
linestarts.push_back (db::DPoint (w, -hl));
hl += line_spacing + h - fp->ymin;
w = 0;
- } else if (c < fp->end_char && c >= fp->start_char) {
- w += fp->chars [c - fp->start_char].width;
- } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
- w += fp->chars [invalid_char - fp->start_char].width;
+
+ } else {
+
+ uint32_t c = tl::utf32_from_utf8 (cp);
+
+ if (c < fp->end_char && c >= fp->start_char) {
+ w += fp->chars [c - fp->start_char].width;
+ } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
+ w += fp->chars [invalid_char - fp->start_char].width;
+ }
+
}
}
@@ -220,21 +237,26 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig
p += db::DVector (0, l->y ());
}
*l = p;
+ if (l == linestarts.begin ()) {
+ left = l->x ();
+ bottom = l->y ();
+ } else {
+ left = std::min (left, l->x ());
+ bottom = std::min (bottom, l->y ());
+ }
}
-
}
// ----------------------------------------------------------------------------
// basic_hershey_edge_iterator implementation
basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, unsigned int f, const std::vector &line_starts)
- : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_index (0), m_linestarts (line_starts)
+ : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_linestarts (line_starts)
{
m_fp = fonts [f];
- m_end = (unsigned int) m_string.size ();
- m_new_char = true;
+ mp_cp = m_string.c_str ();
- if (m_linestarts.size () == 0) {
+ if (m_linestarts.empty ()) {
m_linestarts.push_back (db::DPoint (0.0, 0.0));
}
m_pos = m_linestarts [0];
@@ -243,66 +265,47 @@ basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s,
bool
basic_hershey_edge_iterator::at_end () const
{
- return m_index >= m_end;
+ return *mp_cp == 0 && m_edge == m_edge_end;
}
db::DEdge
basic_hershey_edge_iterator::get ()
{
- while (m_new_char && !at_end ()) {
-
- unsigned char c = (unsigned char) (m_index < m_end ? m_string [m_index] : ' ');
+ while (m_edge == m_edge_end && *mp_cp) {
m_pos += m_delta;
- if (c < m_fp->end_char && c >= m_fp->start_char) {
+ m_edge = m_edge_end = 0;
+ m_delta = db::DVector ();
- m_edge = m_fp->chars [c - m_fp->start_char].edge_start;
- m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end;
- m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0);
+ if (tl::skip_newline (mp_cp)) {
- } else if (c != '\012' && c != '\015'
- && invalid_char < m_fp->end_char
- && invalid_char >= m_fp->start_char) {
+ ++m_line;
- m_edge = m_fp->chars [invalid_char - m_fp->start_char].edge_start;
- m_edge_end = m_fp->chars [invalid_char - m_fp->start_char].edge_end;
- m_delta = db::DVector (m_fp->chars [invalid_char - m_fp->start_char].width, 0);
+ if (m_line >= m_linestarts.size ()) {
+ db::DPoint last;
+ last = m_linestarts.back ();
+ last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing));
+ m_linestarts.push_back (last);
+ }
+ m_pos = m_linestarts [m_line];
} else {
- m_edge = m_edge_end = 0;
- m_delta = db::DVector ();
+ uint32_t c = tl::utf32_from_utf8 (mp_cp);
- }
-
- if (m_edge == m_edge_end) {
-
- if (c == '\012' || c == '\015') {
- if (c == '\015' && m_string.size () > m_index + 1 && m_string [m_index] == '\012') {
- ++m_index;
- }
- ++m_line;
- if (m_line >= m_linestarts.size ()) {
- m_linestarts.push_back (m_linestarts.back () + db::DVector (0.0, -(m_fp->ymax - m_fp->ymin + line_spacing)));
- }
- m_pos = m_linestarts [m_line];
+ if (c < m_fp->start_char || c >= m_fp->end_char) {
+ c = invalid_char;
}
- ++m_index;
+ if (c < m_fp->end_char && c >= m_fp->start_char) {
+ m_edge = m_fp->chars [c - m_fp->start_char].edge_start;
+ m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end;
+ m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0);
+ }
- } else {
- m_new_char = false;
}
- }
- while (m_line >= m_linestarts.size ()) {
- db::DPoint last;
- if (m_linestarts.size () > 0) {
- last = m_linestarts.back ();
- last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing));
- }
- m_linestarts.push_back (last);
}
if (!at_end ()) {
@@ -316,16 +319,9 @@ basic_hershey_edge_iterator::get ()
void
basic_hershey_edge_iterator::inc ()
{
- if (m_new_char) {
- get ();
- }
-
if (! at_end ()) {
++m_edge;
- if (m_edge == m_edge_end) {
- ++m_index;
- m_new_char = true;
- }
+ get ();
}
}
diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h
index 1e6b4990e..f747bb186 100644
--- a/src/db/db/dbHershey.h
+++ b/src/db/db/dbHershey.h
@@ -38,7 +38,7 @@ struct HersheyFont;
DB_PUBLIC int hershey_font_width (unsigned int f);
DB_PUBLIC int hershey_font_height (unsigned int f);
DB_PUBLIC db::DBox hershey_text_box (const std::string &s, unsigned int f);
-DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts);
+DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts, double &left, double &bottom);
DB_PUBLIC std::vector hershey_font_names ();
DB_PUBLIC size_t hershey_count_edges (const std::string &s, unsigned int f);
@@ -51,11 +51,10 @@ public:
void inc ();
private:
- bool m_new_char;
unsigned int m_line;
+ const char *mp_cp;
std::string m_string;
unsigned int m_edge, m_edge_end;
- unsigned int m_index, m_end;
std::vector m_linestarts;
db::DPoint m_pos;
db::DVector m_delta;
@@ -138,7 +137,8 @@ struct DB_PUBLIC_TEMPLATE hershey
hershey ()
: m_string (),
m_font (DefaultFont),
- m_scale (1.0)
+ m_scale (1.0),
+ m_left (0.0), m_bottom (0.0)
{
// .. nothing yet ..
}
@@ -151,7 +151,8 @@ struct DB_PUBLIC_TEMPLATE hershey
hershey (const std::string &s, Font f)
: m_string (s),
m_font (f),
- m_scale (1.0)
+ m_scale (1.0),
+ m_left (0.0), m_bottom (0.0)
{
// .. nothing yet ..
}
@@ -177,14 +178,13 @@ struct DB_PUBLIC_TEMPLATE hershey
/**
* @brief Obtain the size of the text
*
- * @return The bounding box of the text with the scaling applied
+ * @return The bounding box of the text with the scaling and justification applied
*/
- box bbox () const
+ db::DBox bbox () const
{
db::DBox b = hershey_text_box (m_string, m_font);
- db::point p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale));
- db::point p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale));
- return box (p1, p2);
+ b.move (db::DVector (m_left, m_bottom));
+ return b * m_scale;
}
/**
@@ -220,11 +220,11 @@ struct DB_PUBLIC_TEMPLATE hershey
db::DPoint p1 (b.p1 ().x () / m_scale, b.p1 ().y () / m_scale);
db::DPoint p2 (b.p2 ().x () / m_scale, b.p2 ().y () / m_scale);
- hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts);
+ hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts, m_left, m_bottom);
} else {
- if (b.width () > 0 && b.height () > 0) {
+ if (coord_traits::less (0, b.width ()) && coord_traits::less (0, b.height ())) {
db::DBox tbx (hershey_text_box (m_string, m_font));
double fx = double (b.width ()) / double (tbx.width ());
@@ -232,11 +232,11 @@ struct DB_PUBLIC_TEMPLATE hershey
double f = std::min (fx, fy);
m_scale = f * (1.0 - 2.0 * margin);
- } else if (b.width () > 0) {
+ } else if (coord_traits::less (0, b.width ())) {
m_scale = double (b.width ()) / double (hershey_font_width (m_font));
- } else if (b.height () > 0) {
+ } else if (coord_traits::less (0, b.height ())) {
m_scale = double (b.height ()) / double (hershey_font_height (m_font));
@@ -245,7 +245,7 @@ struct DB_PUBLIC_TEMPLATE hershey
if (m_scale > 1e-6) {
db::DPoint p1 (b.p1 ().x () / m_scale, b.p1 ().y () / m_scale);
db::DPoint p2 (b.p2 ().x () / m_scale, b.p2 ().y () / m_scale);
- hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts);
+ hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts, m_left, m_bottom);
}
}
@@ -294,6 +294,7 @@ private:
Font m_font;
double m_scale;
std::vector m_linestarts;
+ double m_left, m_bottom;
};
/**
diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc
index edeeb0800..0b53b70a1 100644
--- a/src/db/db/dbLayout.cc
+++ b/src/db/db/dbLayout.cc
@@ -258,12 +258,12 @@ private:
// -----------------------------------------------------------------
// Implementation of the ProxyContextInfo class
-ProxyContextInfo
-ProxyContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to)
+LayoutOrCellContextInfo
+LayoutOrCellContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to)
{
- ProxyContextInfo info;
+ LayoutOrCellContextInfo info;
- for (std::vector::const_iterator i = from; i != to; ++i) {
+ for (auto i = from; i != to; ++i) {
tl::Extractor ex (i->c_str ());
@@ -290,6 +290,20 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st
info.cell_name = ex.skip ();
+ } else if (ex.test ("META(")) {
+
+ std::pair > vv;
+
+ ex.read_word_or_quoted (vv.first);
+ if (ex.test (",")) {
+ ex.read_word_or_quoted (vv.second.second);
+ }
+ ex.test (")");
+ ex.test ("=");
+ ex.read (vv.second.first);
+
+ info.meta_info.insert(vv);
+
}
}
@@ -298,12 +312,12 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st
}
void
-ProxyContextInfo::serialize (std::vector &strings)
+LayoutOrCellContextInfo::serialize (std::vector &strings)
{
if (! lib_name.empty ()) {
strings.push_back ("LIB=" + lib_name);
}
- for (std::map ::const_iterator p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) {
+ for (auto p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) {
strings.push_back ("P(" + tl::to_word_or_quoted_string (p->first) + ")=" + p->second.to_parsable_string ());
}
if (! pcell_name.empty ()) {
@@ -312,6 +326,32 @@ ProxyContextInfo::serialize (std::vector &strings)
if (! cell_name.empty ()) {
strings.push_back ("CELL=" + cell_name);
}
+
+ std::string mv;
+ for (auto m = meta_info.begin (); m != meta_info.end (); ++m) {
+ mv.clear ();
+ mv += "META(";
+ mv += tl::to_word_or_quoted_string (m->first);
+ if (! m->second.second.empty ()) {
+ mv += ",";
+ mv += tl::to_word_or_quoted_string (m->second.second);
+ }
+ mv += ")=";
+ mv += m->second.first.to_parsable_string ();
+ strings.push_back (mv);
+ }
+}
+
+bool
+LayoutOrCellContextInfo::has_proxy_info () const
+{
+ return !pcell_name.empty () || !lib_name.empty ();
+}
+
+bool
+LayoutOrCellContextInfo::has_meta_info () const
+{
+ return !meta_info.empty ();
}
// -----------------------------------------------------------------
@@ -481,7 +521,13 @@ Layout::operator= (const Layout &d)
}
m_dbu = d.m_dbu;
+
m_meta_info = d.m_meta_info;
+ m_meta_info_by_cell = d.m_meta_info_by_cell;
+ m_meta_info_names = d.m_meta_info_names;
+ m_meta_info_name_map = d.m_meta_info_name_map;
+
+ m_tech_name = d.m_tech_name;
}
return *this;
@@ -593,7 +639,7 @@ Layout::set_technology_name (const std::string &tech)
if (! pn.first) {
// substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@@ -606,7 +652,7 @@ Layout::set_technology_name (const std::string &tech)
if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@@ -633,7 +679,7 @@ Layout::set_technology_name (const std::string &tech)
if (! cn.first) {
// unlink this proxy: substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@@ -650,7 +696,7 @@ Layout::set_technology_name (const std::string &tech)
db::cell_index_type ci = (*lp)->Cell::cell_index ();
// substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@@ -1204,6 +1250,11 @@ Layout::take_cell (cell_index_type ci)
m_cell_ptrs [ci] = 0;
+ auto mi = m_meta_info_by_cell.find (ci);
+ if (mi != m_meta_info_by_cell.end ()) {
+ m_meta_info_by_cell.erase (mi);
+ }
+
// Using free cell indices does have one significant drawback:
// The cellview references cannot be uniquely classified as being invalid - because the
// ID might be reused. This causes problems, when a cell is being deleted and subsequently a
@@ -1252,7 +1303,24 @@ Layout::uniquify_cell_name (const char *name) const
}
}
-cell_index_type
+cell_index_type
+Layout::add_cell (const db::Layout &other, db::cell_index_type ci)
+{
+ cell_index_type ci_new = add_cell (other.cell_name (ci));
+ cell (ci_new).set_ghost_cell (other.cell (ci).is_ghost_cell ());
+
+ if (&other == this) {
+ add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci));
+ } else {
+ for (auto m = other.begin_meta (ci); m != other.end_meta (ci); ++m) {
+ add_meta_info (ci_new, meta_info_name_id (other.meta_info_name (m->first)), m->second);
+ }
+ }
+
+ return ci_new;
+}
+
+cell_index_type
Layout::add_cell (const char *name)
{
std::string b;
@@ -1748,6 +1816,58 @@ Layout::do_update ()
delete pr;
}
+static Layout::meta_info_map s_empty_meta;
+
+Layout::meta_info_iterator
+Layout::begin_meta (db::cell_index_type ci) const
+{
+ auto m = m_meta_info_by_cell.find (ci);
+ if (m != m_meta_info_by_cell.end ()) {
+ return m->second.begin ();
+ } else {
+ return s_empty_meta.begin ();
+ }
+}
+
+Layout::meta_info_iterator
+Layout::end_meta (db::cell_index_type ci) const
+{
+ auto m = m_meta_info_by_cell.find (ci);
+ if (m != m_meta_info_by_cell.end ()) {
+ return m->second.end ();
+ } else {
+ return s_empty_meta.end ();
+ }
+}
+
+const std::string &
+Layout::meta_info_name (Layout::meta_info_name_id_type name_id) const
+{
+ static std::string empty;
+ return name_id < m_meta_info_names.size () ? m_meta_info_names[name_id] : empty;
+}
+
+Layout::meta_info_name_id_type
+Layout::meta_info_name_id (const std::string &name)
+{
+ auto n = m_meta_info_name_map.find (name);
+ if (n != m_meta_info_name_map.end ()) {
+ return n->second;
+ } else {
+ size_t id = m_meta_info_names.size ();
+ m_meta_info_names.push_back (name);
+ m_meta_info_name_map.insert (std::make_pair (name, id));
+ return id;
+ }
+}
+
+Layout::meta_info_name_id_type
+Layout::meta_info_name_id (const std::string &name) const
+{
+ auto n = m_meta_info_name_map.find (name);
+ return n != m_meta_info_name_map.end () ? n->second : std::numeric_limits::max ();
+}
+
void
Layout::clear_meta ()
{
@@ -1755,42 +1875,79 @@ Layout::clear_meta ()
}
void
-Layout::add_meta_info (const MetaInfo &i)
+Layout::add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i)
{
- for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) {
- if (m->name == i.name) {
- *m = i;
- return;
- }
- }
- m_meta_info.push_back (i);
+ m_meta_info[name_id] = i;
}
void
-Layout::remove_meta_info (const std::string &name)
+Layout::remove_meta_info (meta_info_name_id_type name_id)
{
- for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) {
- if (m->name == name) {
- m_meta_info.erase (m);
- return;
- }
+ m_meta_info.erase (name_id);
+}
+
+const MetaInfo &
+Layout::meta_info (meta_info_name_id_type name_id) const
+{
+ auto n = m_meta_info.find (name_id);
+ static MetaInfo null_value;
+ return n != m_meta_info.end () ? n->second : null_value;
+}
+
+bool
+Layout::has_meta_info (meta_info_name_id_type name_id) const
+{
+ return m_meta_info.find (name_id) != m_meta_info.end ();
+}
+
+void
+Layout::clear_meta (db::cell_index_type ci)
+{
+ m_meta_info_by_cell.erase (ci);
+}
+
+void
+Layout::add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i)
+{
+ m_meta_info_by_cell[ci][name_id] = i;
+}
+
+void
+Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id)
+{
+ auto c = m_meta_info_by_cell.find (ci);
+ if (c != m_meta_info_by_cell.end ()) {
+ c->second.erase (name_id);
}
}
-const std::string &
-Layout::meta_info_value (const std::string &name) const
+const MetaInfo &
+Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const
{
- for (meta_info::const_iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) {
- if (m->name == name) {
- return m->value;
+ auto c = m_meta_info_by_cell.find (ci);
+ if (c != m_meta_info_by_cell.end ()) {
+ auto i = c->second.find (name_id);
+ if (i != c->second.end ()) {
+ return i->second;
}
}
- static const std::string s_empty;
- return s_empty;
+ static MetaInfo null_value;
+ return null_value;
}
-void
+bool
+Layout::has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const
+{
+ auto c = m_meta_info_by_cell.find (ci);
+ if (c != m_meta_info_by_cell.end ()) {
+ return c->second.find (name_id) != c->second.end ();
+ } else {
+ return false;
+ }
+}
+
+void
Layout::swap_layers (unsigned int a, unsigned int b)
{
tl_assert (m_layers.layer_state (a) != LayoutLayers::Free);
@@ -2369,10 +2526,85 @@ Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vectorsecond.persisted) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Layout::has_context_info (cell_index_type cell_index) const
+{
+ auto c = m_meta_info_by_cell.find (cell_index);
+ if (c != m_meta_info_by_cell.end ()) {
+ for (auto i = c->second.begin (); i != c->second.end (); ++i) {
+ if (i->second.persisted) {
+ return true;
+ }
+ }
+ }
+
+ const db::Cell &cref = cell (cell_index);
+ if (cref.is_proxy () && ! cref.is_top ()) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool
+Layout::get_context_info (std::vector &strings) const
+{
+ LayoutOrCellContextInfo info;
+ if (! get_context_info (info)) {
+ return false;
+ } else {
+ info.serialize (strings);
+ return true;
+ }
+}
+
+bool
+Layout::get_context_info (LayoutOrCellContextInfo &info) const
+{
+ for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
+ if (i->second.persisted) {
+ std::pair &mi = info.meta_info [m_meta_info_names [i->first] ];
+ mi.first = i->second.value;
+ mi.second = i->second.description;
+ }
+ }
+
+ return true;
+}
+
+void
+Layout::fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to)
+{
+ fill_meta_info_from_context (LayoutOrCellContextInfo::deserialize (from, to));
+}
+
+void
+Layout::fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info)
+{
+ if (! context_info.meta_info.empty ()) {
+ for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) {
+ meta_info_name_id_type name_id = meta_info_name_id (i->first);
+ m_meta_info [name_id] = MetaInfo (i->second.second, i->second.first, true);
+ }
+ }
+}
+
bool
Layout::get_context_info (cell_index_type cell_index, std::vector &strings) const
{
- ProxyContextInfo info;
+ LayoutOrCellContextInfo info;
if (! get_context_info (cell_index, info)) {
return false;
} else {
@@ -2382,8 +2614,22 @@ Layout::get_context_info (cell_index_type cell_index, std::vector
}
bool
-Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) const
+Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &info) const
{
+ bool any_meta = false;
+
+ auto cmi = m_meta_info_by_cell.find (cell_index);
+ if (cmi != m_meta_info_by_cell.end ()) {
+ for (auto i = cmi->second.begin (); i != cmi->second.end (); ++i) {
+ if (i->second.persisted) {
+ std::pair &mi = info.meta_info [m_meta_info_names [i->first] ];
+ mi.first = i->second.value;
+ mi.second = i->second.description;
+ any_meta = true;
+ }
+ }
+ }
+
const db::Cell *cptr = &cell (cell_index);
const db::ColdProxy *cold_proxy = dynamic_cast (cptr);
@@ -2399,7 +2645,7 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co
const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
if (! lib) {
- return false; // abort
+ return any_meta; // abort
} else {
// one level of library indirection
@@ -2432,6 +2678,27 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co
return true;
}
+void
+Layout::fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to)
+{
+ fill_meta_info_from_context (cell_index, LayoutOrCellContextInfo::deserialize (from, to));
+}
+
+void
+Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info)
+{
+ if (! context_info.meta_info.empty ()) {
+
+ meta_info_map &mi = m_meta_info_by_cell [cell_index];
+
+ for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) {
+ meta_info_name_id_type name_id = meta_info_name_id (i->first);
+ mi [name_id] = MetaInfo (i->second.second, i->second.first, true);
+ }
+
+ }
+}
+
void
Layout::restore_proxies (ImportLayerMapping *layer_mapping)
{
@@ -2463,11 +2730,11 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector :
return false;
}
- return recover_proxy_as (cell_index, ProxyContextInfo::deserialize (from, to), layer_mapping);
+ return recover_proxy_as (cell_index, LayoutOrCellContextInfo::deserialize (from, to), layer_mapping);
}
bool
-Layout::recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &info, ImportLayerMapping *layer_mapping)
+Layout::recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &info, ImportLayerMapping *layer_mapping)
{
if (! info.lib_name.empty ()) {
@@ -2517,11 +2784,11 @@ Layout::recover_proxy (std::vector ::const_iterator from, std::vect
return 0;
}
- return recover_proxy (ProxyContextInfo::deserialize (from, to));
+ return recover_proxy (LayoutOrCellContextInfo::deserialize (from, to));
}
db::Cell *
-Layout::recover_proxy (const ProxyContextInfo &info)
+Layout::recover_proxy (const LayoutOrCellContextInfo &info)
{
if (! info.lib_name.empty ()) {
@@ -2549,7 +2816,7 @@ Layout::recover_proxy (const ProxyContextInfo &info)
}
db::Cell *
-Layout::recover_proxy_no_lib (const ProxyContextInfo &info)
+Layout::recover_proxy_no_lib (const LayoutOrCellContextInfo &info)
{
if (! info.pcell_name.empty ()) {
@@ -2646,7 +2913,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
}
cell_index_type
-Layout::create_cold_proxy (const db::ProxyContextInfo &info)
+Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
// create a new unique name
std::string b;
@@ -2677,7 +2944,7 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info)
}
void
-Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index)
+Layout::create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type target_cell_index)
{
tl_assert (m_cell_ptrs [target_cell_index] != 0);
diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h
index 232f52465..cb79d2a8a 100644
--- a/src/db/db/dbLayout.h
+++ b/src/db/db/dbLayout.h
@@ -417,15 +417,19 @@ public:
/**
* @brief A binary object representing context information for regenerating library proxies and PCells
*/
-struct DB_PUBLIC ProxyContextInfo
+struct DB_PUBLIC LayoutOrCellContextInfo
{
std::string lib_name;
std::string cell_name;
std::string pcell_name;
std::map pcell_parameters;
+ std::map > meta_info;
- static ProxyContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to);
+ static LayoutOrCellContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to);
void serialize (std::vector &strings);
+
+ bool has_proxy_info () const;
+ bool has_meta_info () const;
};
/**
@@ -465,8 +469,9 @@ public:
typedef pcell_name_map::const_iterator pcell_iterator;
typedef std::map, cell_index_type> lib_proxy_map;
typedef LayerIterator layer_iterator;
- typedef std::vector meta_info;
- typedef meta_info::const_iterator meta_info_iterator;
+ typedef size_t meta_info_name_id_type;
+ typedef std::map meta_info_map;
+ typedef meta_info_map::const_iterator meta_info_iterator;
/**
* @brief A helper functor to compare "const char *" by the content
@@ -756,6 +761,14 @@ public:
*/
cell_index_type add_cell (const char *name = 0);
+ /**
+ * @brief Adds a cell using another cell as a template
+ *
+ * This method will use the name of the other cell and initialize the
+ * new cell with the meta info from the other cell.
+ */
+ cell_index_type add_cell (const db::Layout &other, db::cell_index_type ci);
+
/**
* @brief Add a cell without a name
*
@@ -1014,27 +1027,69 @@ public:
/**
* @brief Creates a cold proxy representing the given context information
*/
- cell_index_type create_cold_proxy (const db::ProxyContextInfo &info);
+ cell_index_type create_cold_proxy (const db::LayoutOrCellContextInfo &info);
/**
* @brief Subsitutes the given cell by a cold proxy representing the given context information
*/
- void create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type cell_index);
+ void create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type cell_index);
+
+ /**
+ * @brief Gets a value indicating whether layout context info is provided / needed
+ */
+ bool has_context_info() const;
+
+ /**
+ * @brief Gets a value indicating whether layout context info is provided / needed
+ */
+ bool has_context_info(cell_index_type cell_index) const;
+
+ /**
+ * @brief Get the context information for the layout (for writing into a file)
+ *
+ * The context information is a sequence of strings which is pushed onto the given
+ * vector. It can be used to fill meta information with fill_meta_info_from_context.
+ */
+ bool get_context_info (std::vector &strings) const;
+
+ /**
+ * @brief Gets the context information as a binary object
+ */
+ bool get_context_info (LayoutOrCellContextInfo &context_info) const;
+
+ /**
+ * @brief Fills the layout's meta information from the context
+ */
+ void fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to);
+
+ /**
+ * @brief Fills the layout's meta information from the binary context
+ */
+ void fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info);
/**
* @brief Get the context information for a given cell (for writing into a file)
*
* The context information is a sequence of strings which is pushed onto the given
- * vector. It can be used to recover a respective proxy cell with the recover_proxy method.
- * If the given cell is not a valid proxy or library references are missing, the method
- * will return false.
+ * vector. It can be used to recover a respective proxy cell with the recover_proxy method
+ * or to fill meta information using fill_meta_info_from_context.
*/
bool get_context_info (cell_index_type cell_index, std::vector &context_info) const;
/**
* @brief Gets the context information as a binary object
*/
- bool get_context_info (cell_index_type cell_index, ProxyContextInfo &context_info) const;
+ bool get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &context_info) const;
+
+ /**
+ * @brief Fills the layout's meta information from the context
+ */
+ void fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to);
+
+ /**
+ * @brief Fills the layout's meta information from the binary context
+ */
+ void fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info);
/**
* @brief Recover a proxy cell from the given context info.
@@ -1050,7 +1105,7 @@ public:
/**
* @brief Recover a proxy cell from the given binary context info object.
*/
- db::Cell *recover_proxy (const ProxyContextInfo &context_info);
+ db::Cell *recover_proxy (const LayoutOrCellContextInfo &context_info);
/**
* @brief Recover a proxy cell from the given context info.
@@ -1072,7 +1127,7 @@ public:
*
* See the string-based version of "recover_proxy_as" for details.
*/
- bool recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &context_info, ImportLayerMapping *layer_mapping = 0);
+ bool recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info, ImportLayerMapping *layer_mapping = 0);
/**
* @brief Restores proxies as far as possible
@@ -1812,6 +1867,31 @@ public:
return m_meta_info.end ();
}
+ /**
+ * @brief Delivers the meta information (begin iterator) per cell
+ */
+ meta_info_iterator begin_meta (db::cell_index_type ci) const;
+
+ /**
+ * @brief Delivers the meta information (end iterator) per cell
+ */
+ meta_info_iterator end_meta (db::cell_index_type ci) const;
+
+ /**
+ * @brief Gets the meta informatio name by ID
+ */
+ const std::string &meta_info_name (meta_info_name_id_type name_id) const;
+
+ /**
+ * @brief Gets the meta information name ID for a specific string
+ */
+ meta_info_name_id_type meta_info_name_id (const std::string &name) const;
+
+ /**
+ * @brief Gets the meta information name ID for a specific string (const version)
+ */
+ meta_info_name_id_type meta_info_name_id (const std::string &name);
+
/**
* @brief Clears the meta information
*/
@@ -1819,22 +1899,146 @@ public:
/**
* @brief Adds meta information
- * The given meta information object is appended at the end of the meta information list.
+ * The given meta information object is added to the meta information list.
* If a meta info object with the same name already exists it is overwritten.
*/
- void add_meta_info (const MetaInfo &i);
+ void add_meta_info (const std::string &name, const MetaInfo &i)
+ {
+ add_meta_info (meta_info_name_id (name), i);
+ }
+
+ /**
+ * @brief Adds meta information (variant with name ID)
+ */
+ void add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i);
+
+ /**
+ * @brief Adds meta information from a sequence
+ */
+ template
+ void add_meta_info (const I &b, const I &e)
+ {
+ for (I i = b; i != e; ++i) {
+ m_meta_info.insert (b, e);
+ }
+ }
/**
* @brief Removes the meta information object with the given name
* The method will do nothing if no object with that name exists.
*/
- void remove_meta_info (const std::string &name);
+ void remove_meta_info (const std::string &name)
+ {
+ remove_meta_info (meta_info_name_id (name));
+ }
+
+ /**
+ * @brief Removes the meta information object with the given name ID
+ */
+ void remove_meta_info (meta_info_name_id_type name_id);
/**
* @brief Gets the meta info value for a meta info object with the given name
* If no object with that name exists, an empty string is returned
*/
- const std::string &meta_info_value (const std::string &name) const;
+ const MetaInfo &meta_info (const std::string &name) const
+ {
+ return meta_info (meta_info_name_id (name));
+ }
+
+ /**
+ * @brief Gets the meta info value for a meta info object with the given name ID
+ */
+ const MetaInfo &meta_info (meta_info_name_id_type name_id) const;
+
+ /**
+ * @brief Gets a value indicating whether a meta info with the given name is present
+ */
+ bool has_meta_info (const std::string &name) const
+ {
+ return has_meta_info (meta_info_name_id (name));
+ }
+
+ /**
+ * @brief Gets a value indicating whether a meta info with the given name is present
+ */
+ bool has_meta_info (meta_info_name_id_type name_id) const;
+
+ /**
+ * @brief Clears the meta information for a specific cell
+ */
+ void clear_meta (db::cell_index_type ci);
+
+ /**
+ * @brief Adds meta information for a given cell
+ * The given meta information object is to the meta information list for the given cell.
+ * If a meta info object with the same name already exists it is overwritten.
+ */
+ void add_meta_info (db::cell_index_type ci, const std::string &name, const MetaInfo &i)
+ {
+ add_meta_info (ci, meta_info_name_id (name), i);
+ }
+
+ /**
+ * @brief Adds meta information for a given cell (version with name ID)
+ * The given meta information object is appended at the end of the meta information list.
+ * If a meta info object with the same name already exists it is overwritten.
+ */
+ void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i);
+
+ /**
+ * @brief Adds meta information from a sequence
+ */
+ template
+ void add_meta_info (db::cell_index_type ci, const I &b, const I &e)
+ {
+ for (I i = b; i != e; ++i) {
+ m_meta_info_by_cell [ci].insert (b, e);
+ }
+ }
+
+ /**
+ * @brief Gets a value indicating whether a meta info with the given name is present for the given cell
+ */
+ bool has_meta_info (db::cell_index_type ci, const std::string &name) const
+ {
+ return has_meta_info (ci, meta_info_name_id (name));
+ }
+
+ /**
+ * @brief Gets a value indicating whether a meta info with the given name is present for the given cell
+ */
+ bool has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const;
+
+ /**
+ * @brief Removes the meta information object with the given name from the given cell
+ * The method will do nothing if no object with that name exists.
+ */
+ void remove_meta_info (db::cell_index_type ci, const std::string &name)
+ {
+ remove_meta_info (ci, meta_info_name_id (name));
+ }
+
+ /**
+ * @brief Removes the meta information object with the given name ID from the given cell
+ * The method will do nothing if no object with that name exists.
+ */
+ void remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id);
+
+ /**
+ * @brief Gets the meta info value for a meta info object with the given name for the given cell
+ * If no object with that name exists, an empty string is returned
+ */
+ const MetaInfo &meta_info (db::cell_index_type ci, const std::string &name) const
+ {
+ return meta_info (ci, meta_info_name_id (name));
+ }
+
+ /**
+ * @brief Gets the meta info value for a meta info object with the given name ID for the given cell
+ * If no object with that name exists, an empty string is returned
+ */
+ const MetaInfo &meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const;
/**
* @brief This event is triggered when the technology changes
@@ -1876,7 +2080,11 @@ private:
lib_proxy_map m_lib_proxy_map;
bool m_do_cleanup;
bool m_editable;
- meta_info m_meta_info;
+ std::map m_meta_info_name_map;
+ std::vector m_meta_info_names;
+ meta_info_map m_meta_info;
+ std::map m_meta_info_by_cell;
+
std::string m_tech_name;
tl::Mutex m_lock;
@@ -1920,7 +2128,7 @@ private:
/**
* @brief Recovers a proxy without considering the library from context_info
*/
- db::Cell *recover_proxy_no_lib (const ProxyContextInfo &context_info);
+ db::Cell *recover_proxy_no_lib (const LayoutOrCellContextInfo &context_info);
};
/**
diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc
index b22464d51..0e7faf14a 100644
--- a/src/db/db/dbLayoutUtils.cc
+++ b/src/db/db/dbLayoutUtils.cc
@@ -218,7 +218,7 @@ merge_layouts (db::Layout &target,
std::map new_cell_mapping;
for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) {
if (cell_mapping.find (*c) == cell_mapping.end ()) {
- new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source.cell_name (*c))));
+ new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source, *c)));
}
}
diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc
index a6075f5d6..59acfa95f 100644
--- a/src/db/db/dbLibrary.cc
+++ b/src/db/db/dbLibrary.cc
@@ -205,7 +205,7 @@ Library::remap_to (db::Library *other)
if (! pn.first) {
// substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
@@ -216,7 +216,7 @@ Library::remap_to (db::Library *other)
if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
@@ -254,7 +254,7 @@ Library::remap_to (db::Library *other)
if (! cn.first) {
// substitute by a cold proxy
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h
index e5410751e..50de04291 100644
--- a/src/db/db/dbMetaInfo.h
+++ b/src/db/db/dbMetaInfo.h
@@ -31,31 +31,34 @@
namespace db
{
+// Switch for version-agnostic code
+#define KLAYOUT_META_INFO_V2
+
/**
* @brief A structure describing the meta information from the reader
*
* In the meta information block, the reader provides additional information
* about the file and content etc.
- * "name" is a unique name that can be used to identify the information.
* "description" is a "speaking" description of the information.
* "value" is the value of the specific part of meta information.
*/
struct DB_PUBLIC MetaInfo
{
- MetaInfo (const std::string &n, const std::string &d, const std::string &v)
- : name (n), description (d), value (v)
+ MetaInfo (const std::string &d, const tl::Variant &v, bool p = false)
+ : description (d), value (v), persisted (p)
{
// .. nothing else ..
}
MetaInfo ()
+ : persisted (false)
{
// .. nothing else ..
}
- std::string name;
std::string description;
- std::string value;
+ tl::Variant value;
+ bool persisted;
};
/**
@@ -63,7 +66,6 @@ struct DB_PUBLIC MetaInfo
*/
inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const MetaInfo &v, bool no_self, void *parent)
{
- db::mem_stat (stat, purpose, cat, v.name, no_self, parent);
db::mem_stat (stat, purpose, cat, v.description, no_self, parent);
db::mem_stat (stat, purpose, cat, v.value, no_self, parent);
}
diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc
index 913fc43e8..7d5bf2805 100644
--- a/src/db/db/dbNetlistSpiceReader.cc
+++ b/src/db/db/dbNetlistSpiceReader.cc
@@ -237,11 +237,21 @@ public:
typedef pin_list_type::const_iterator pin_const_iterator;
SpiceCachedCircuit (const std::string &name)
- : m_name (name)
+ : m_name (name), m_anonymous (false)
{
// .. nothing yet ..
}
+ bool is_anonymous () const
+ {
+ return m_anonymous;
+ }
+
+ void set_anonymous (bool f)
+ {
+ m_anonymous = f;
+ }
+
const std::string &name () const
{
return m_name;
@@ -315,6 +325,7 @@ private:
parameters_type m_parameters;
pin_list_type m_pins;
cards_type m_cards;
+ bool m_anonymous;
};
static std::string
@@ -1182,12 +1193,15 @@ SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &pref
error (tl::sprintf (tl::to_string (tr ("Subcircuit '%s' not found in netlist")), model));
} else {
db::SpiceCachedCircuit *cc_nc = mp_dict->create_cached_circuit (model);
+ cc_nc->set_anonymous (true);
cc = cc_nc;
std::vector pins;
pins.resize (nn.size ());
cc_nc->set_pins (pins);
}
- } else {
+ }
+
+ if (! cc->is_anonymous ()) {
// issue warnings on unknown parameters which are skipped otherwise
for (auto p = pv.begin (); p != pv.end (); ++p) {
if (cc->parameters ().find (p->first) == cc->parameters ().end ()) {
diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.cc b/src/db/db/dbNetlistSpiceReaderDelegate.cc
index 905360073..88d559050 100644
--- a/src/db/db/dbNetlistSpiceReaderDelegate.cc
+++ b/src/db/db/dbNetlistSpiceReaderDelegate.cc
@@ -381,6 +381,9 @@ void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std:
error (tl::to_string (tr ("Can't find a value for a R, C or L device")));
}
+ // store the value under the element name always
+ pv[element] = tl::Variant (value);
+
} else {
// others: n-terminal devices with a model (last node)
@@ -416,8 +419,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
std::map params = pv;
std::vector terminal_order;
- size_t defp = std::numeric_limits::max ();
-
double mult = 1.0;
auto mp = params.find ("M");
if (mp != params.end ()) {
@@ -461,8 +462,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// Apply multiplier (divider, according to ngspice manual)
value /= mult;
-
- defp = db::DeviceClassResistor::param_id_R;
+ params["R"] = tl::Variant (value);
// Apply multiplier to other parameters
static const char *scale_params[] = { "A", "P", "W" };
@@ -492,8 +492,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// Apply multiplier (divider, according to ngspice manual)
value /= mult;
-
- defp = db::DeviceClassInductor::param_id_L;
+ params["L"] = tl::Variant (value);
} else if (element == "C") {
@@ -525,8 +524,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// Apply multiplier
value *= mult;
-
- defp = db::DeviceClassCapacitor::param_id_C;
+ params["C"] = tl::Variant (value);
// Apply multiplier to other parameters
static const char *scale_params[] = { "A", "P" };
@@ -657,8 +655,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
double pv = 0.0;
if (v != params.end ()) {
pv = v->second.to_double ();
- } else if (i->id () == defp) {
- pv = value;
} else {
continue;
}
diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc
index 00db50666..936fceeff 100644
--- a/src/db/db/gsiDeclDbCell.cc
+++ b/src/db/db/gsiDeclDbCell.cc
@@ -20,9 +20,8 @@
*/
-
-
#include "gsiDecl.h"
+#include "gsiDeclDbMetaInfo.h"
#include "gsiDeclDbHelpers.h"
#include "dbLayout.h"
@@ -984,6 +983,58 @@ static db::Layout *layout (db::Cell *cell)
return cell->layout ();
}
+static void cell_clear_meta_info (db::Cell *cell)
+{
+ if (cell->layout ()) {
+ cell->layout ()->clear_meta (cell->cell_index ());
+ }
+}
+
+static void cell_remove_meta_info (db::Cell *cell, const std::string &name)
+{
+ if (cell->layout ()) {
+ cell->layout ()->remove_meta_info (cell->cell_index (), name);
+ }
+}
+
+static void cell_add_meta_info (db::Cell *cell, const MetaInfo &mi)
+{
+ if (cell->layout ()) {
+ cell->layout ()->add_meta_info (cell->cell_index (), mi.name, db::MetaInfo (mi.description, mi.value));
+ }
+}
+
+static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::string &name)
+{
+ if (! cell->layout ()) {
+ static tl::Variant null_value;
+ return null_value;
+ } else {
+ return cell->layout ()->meta_info (cell->cell_index (), name).value;
+ }
+}
+
+static MetaInfo *cell_meta_info (db::Cell *cell, const std::string &name)
+{
+ if (! cell->layout ()) {
+ return 0;
+ } else if (cell->layout ()->has_meta_info (cell->cell_index (), name)) {
+ const db::MetaInfo &value = cell->layout ()->meta_info (cell->cell_index (), name);
+ return new MetaInfo (name, value);
+ } else {
+ return 0;
+ }
+}
+
+static gsi::MetaInfoIterator cell_each_meta_info (const db::Cell *cell)
+{
+ if (! cell->layout ()) {
+ return gsi::MetaInfoIterator ();
+ } else {
+ return gsi::MetaInfoIterator (cell->layout (), cell->layout ()->begin_meta (cell->cell_index ()), cell->layout ()->end_meta (cell->cell_index ()));
+ }
+}
+
static bool cell_has_prop_id (const db::Cell *c)
{
return c->prop_id () != 0;
@@ -1780,6 +1831,47 @@ Class decl_Cell ("db", "Cell",
"\n"
"This method has been introduced in version 0.23."
) +
+ gsi::method_ext ("add_meta_info", &cell_add_meta_info, gsi::arg ("info"),
+ "@brief Adds meta information to the cell\n"
+ "See \\LayoutMetaInfo for details about cells and meta information.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
+ gsi::method_ext ("clear_meta_info", &cell_clear_meta_info,
+ "@brief Clears the meta information of the cell\n"
+ "See \\LayoutMetaInfo for details about cells and meta information.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
+ gsi::method_ext ("remove_meta_info", &cell_remove_meta_info, gsi::arg ("name"),
+ "@brief Removes meta information from the cell\n"
+ "See \\LayoutMetaInfo for details about cells and meta information.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
+ gsi::method_ext ("meta_info_value", &cell_meta_info_value, gsi::arg ("name"),
+ "@brief Gets the meta information value for a given name\n"
+ "See \\LayoutMetaInfo for details about cells and meta information.\n"
+ "\n"
+ "If no meta information with the given name exists, a nil value will be returned.\n"
+ "A more generic version that delivers all fields of the meta information is \\meta_info.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
+ gsi::factory_ext ("meta_info", &cell_meta_info, gsi::arg ("name"),
+ "@brief Gets the meta information for a given name\n"
+ "See \\LayoutMetaInfo for details about cells and meta information.\n"
+ "\n"
+ "If no meta information with the given name exists, a default object with empty fields will be returned.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
+ gsi::iterator_ext ("each_meta_info", &cell_each_meta_info,
+ "@brief Iterates over the meta information of the cell\n"
+ "See \\LayoutMetaInfo for details about cells and meta information.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
gsi::method_ext ("write", &write_simple, gsi::arg ("file_name"),
"@brief Writes the cell to a layout file\n"
"The format of the file will be determined from the file name. Only the cell and "
diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc
index bff85e070..da083b9ee 100644
--- a/src/db/db/gsiDeclDbLayout.cc
+++ b/src/db/db/gsiDeclDbLayout.cc
@@ -22,6 +22,7 @@
#include "gsiDecl.h"
+#include "gsiDeclDbMetaInfo.h"
#include "dbLayout.h"
#include "dbClip.h"
#include "dbRecursiveShapeIterator.h"
@@ -902,39 +903,30 @@ static db::Cell *create_cell4 (db::Layout *layout, const std::string &name, cons
return &layout->cell (layout->get_lib_proxy (lib, lib_cell));
}
-static db::MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description)
+static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi)
{
- return new db::MetaInfo (name, description, value);
+ layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value));
}
-static void layout_meta_set_name (db::MetaInfo *mi, const std::string &n)
+static MetaInfo *layout_get_meta_info (db::Layout *layout, const std::string &name)
{
- mi->name = n;
+ if (layout->has_meta_info (name)) {
+ const db::MetaInfo &value = layout->meta_info (name);
+ return new MetaInfo (name, value);
+ } else {
+ return 0;
+ }
}
-static const std::string &layout_meta_get_name (const db::MetaInfo *mi)
+static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name)
{
- return mi->name;
+ const db::MetaInfo &value = layout->meta_info (name);
+ return value.value;
}
-static void layout_meta_set_value (db::MetaInfo *mi, const std::string &n)
+static MetaInfoIterator layout_each_meta_info (const db::Layout *layout)
{
- mi->value = n;
-}
-
-static const std::string &layout_meta_get_value (const db::MetaInfo *mi)
-{
- return mi->value;
-}
-
-static void layout_meta_set_description (db::MetaInfo *mi, const std::string &n)
-{
- mi->description = n;
-}
-
-static const std::string &layout_meta_get_description (const db::MetaInfo *mi)
-{
- return mi->description;
+ return MetaInfoIterator (layout, layout->begin_meta (), layout->end_meta ());
}
static void scale_and_snap1 (db::Layout *layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d)
@@ -997,45 +989,6 @@ static void move_tree_shapes3 (db::Layout *layout, db::Layout &source_layout, co
db::move_shapes (*layout, source_layout, trans, cm.source_cells (), cm.table (), lm.table ());
}
-Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo",
- gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()),
- "@brief Creates a layout meta info object\n"
- "@param name The name\n"
- "@param value The value\n"
- "@param description An optional description text\n"
- ) +
- gsi::method_ext ("name", &layout_meta_get_name,
- "@brief Gets the name of the layout meta info object\n"
- ) +
- gsi::method_ext ("name=", &layout_meta_set_name,
- "@brief Sets the name of the layout meta info object\n"
- ) +
- gsi::method_ext ("value", &layout_meta_get_value,
- "@brief Gets the value of the layout meta info object\n"
- ) +
- gsi::method_ext ("value=", &layout_meta_set_value,
- "@brief Sets the value of the layout meta info object\n"
- ) +
- gsi::method_ext ("description", &layout_meta_get_description,
- "@brief Gets the description of the layout meta info object\n"
- ) +
- gsi::method_ext ("description=", &layout_meta_set_description,
- "@brief Sets the description of the layout meta info object\n"
- ),
- "@brief A piece of layout meta information\n"
- "Layout meta information is basically additional data that can be attached to a layout. "
- "Layout readers may generate meta information and some writers will add layout information to "
- "the layout object. Some writers will also read meta information to determine certain attributes.\n"
- "\n"
- "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. "
- "Meta information is identified by a unique name and carries a string value plus an optional description string. "
- "The description string is for information only and is not evaluated by code.\n"
- "\n"
- "See also \\Layout#each_meta_info and \\Layout#meta_info_value and \\Layout#remove_meta_info"
- "\n"
- "This class has been introduced in version 0.25."
-);
-
static void dtransform (db::Layout *layout, const db::DTrans &trans)
{
db::CplxTrans dbu_trans (layout->dbu ());
@@ -1097,27 +1050,42 @@ Class decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.27.9."
) +
- gsi::method ("add_meta_info", &db::Layout::add_meta_info, gsi::arg ("info"),
+ gsi::method_ext ("add_meta_info", &layout_add_meta_info, gsi::arg ("info"),
"@brief Adds meta information to the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information."
"\n"
"This method has been introduced in version 0.25."
) +
- gsi::method ("remove_meta_info", &db::Layout::remove_meta_info, gsi::arg ("name"),
+ gsi::method ("clear_meta_info", static_cast (&db::Layout::clear_meta),
+ "@brief Clears the meta information of the layout\n"
+ "See \\LayoutMetaInfo for details about layouts and meta information."
+ "\n"
+ "This method has been introduced in version 0.28.8."
+ ) +
+ gsi::method ("remove_meta_info", static_cast (&db::Layout::remove_meta_info), gsi::arg ("name"),
"@brief Removes meta information from the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information."
"\n"
"This method has been introduced in version 0.25."
) +
- gsi::method ("meta_info_value", &db::Layout::meta_info_value, gsi::arg ("name"),
+ gsi::method_ext ("meta_info_value", &layout_get_meta_info_value, gsi::arg ("name"),
"@brief Gets the meta information value for a given name\n"
"See \\LayoutMetaInfo for details about layouts and meta information.\n"
"\n"
- "If no meta information with the given name exists, an empty string will be returned.\n"
+ "If no meta information with the given name exists, a nil value will be returned.\n"
+ "A more generic version that delivers all fields of the meta information is \\meta_info.\n"
"\n"
- "This method has been introduced in version 0.25."
+ "This method has been introduced in version 0.25. Starting with version 0.28.8, the value is of variant type instead of string only.\n"
) +
- gsi::iterator ("each_meta_info", &db::Layout::begin_meta, &db::Layout::end_meta,
+ gsi::factory_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"),
+ "@brief Gets the meta information for a given name\n"
+ "See \\LayoutMetaInfo for details about layouts and meta information.\n"
+ "\n"
+ "If no meta information with the given name exists, nil is returned.\n"
+ "\n"
+ "This method has been introduced in version 0.28.8.\n"
+ ) +
+ gsi::iterator_ext ("each_meta_info", &layout_each_meta_info,
"@brief Iterates over the meta information of the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information.\n"
"\n"
diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc
new file mode 100644
index 000000000..ef760d438
--- /dev/null
+++ b/src/db/db/gsiDeclDbMetaInfo.cc
@@ -0,0 +1,168 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2023 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 "gsiDecl.h"
+#include "gsiDeclDbMetaInfo.h"
+
+namespace gsi
+{
+
+static MetaInfo *layout_meta_info_ctor (const std::string &name, const tl::Variant &value, const std::string &description, bool persisted)
+{
+ return new MetaInfo (name, description, value, persisted);
+}
+
+static void layout_meta_set_name (MetaInfo *mi, const std::string &n)
+{
+ mi->name = n;
+}
+
+static const std::string &layout_meta_get_name (const MetaInfo *mi)
+{
+ return mi->name;
+}
+
+static void layout_meta_set_value (MetaInfo *mi, const tl::Variant &n)
+{
+ mi->value = n;
+}
+
+static const tl::Variant &layout_meta_get_value (const MetaInfo *mi)
+{
+ return mi->value;
+}
+
+static void layout_meta_set_description (MetaInfo *mi, const std::string &n)
+{
+ mi->description = n;
+}
+
+static const std::string &layout_meta_get_description (const MetaInfo *mi)
+{
+ return mi->description;
+}
+
+static void layout_meta_set_persisted (MetaInfo *mi, bool f)
+{
+ mi->persisted = f;
+}
+
+static bool layout_meta_get_persisted (const MetaInfo *mi)
+{
+ return mi->persisted;
+}
+
+
+Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo",
+ gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), gsi::arg ("persisted", false),
+ "@brief Creates a layout meta info object\n"
+ "@param name The name\n"
+ "@param value The value\n"
+ "@param description An optional description text\n"
+ "@param persisted If true, the meta information will be persisted in some file formats, like GDS2\n"
+ "\n"
+ "The 'persisted' attribute has been introduced in version 0.28.8.\n"
+ ) +
+ gsi::method_ext ("name", &layout_meta_get_name,
+ "@brief Gets the name of the layout meta info object\n"
+ ) +
+ gsi::method_ext ("name=", &layout_meta_set_name, gsi::arg ("name"),
+ "@brief Sets the name of the layout meta info object\n"
+ ) +
+ gsi::method_ext ("value", &layout_meta_get_value,
+ "@brief Gets the value of the layout meta info object\n"
+ ) +
+ gsi::method_ext ("value=", &layout_meta_set_value, gsi::arg ("value"),
+ "@brief Sets the value of the layout meta info object\n"
+ ) +
+ gsi::method_ext ("description", &layout_meta_get_description,
+ "@brief Gets the description of the layout meta info object\n"
+ ) +
+ gsi::method_ext ("description=", &layout_meta_set_description, gsi::arg ("description"),
+ "@brief Sets the description of the layout meta info object\n"
+ ) +
+ gsi::method_ext ("is_persisted?", &layout_meta_get_persisted,
+ "@brief Gets a value indicating whether the meta information will be persisted\n"
+ "This predicate was introduced in version 0.28.8.\n"
+ ) +
+ gsi::method_ext ("persisted=", &layout_meta_set_persisted, gsi::arg ("flag"),
+ "@brief Sets a value indicating whether the meta information will be persisted\n"
+ "This predicate was introduced in version 0.28.8.\n"
+ ),
+ "@brief A piece of layout meta information\n"
+ "Layout meta information is basically additional data that can be attached to a layout. "
+ "Layout readers may generate meta information and some writers will add layout information to "
+ "the layout object. Some writers will also read meta information to determine certain attributes.\n"
+ "\n"
+ "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. "
+ "Meta information is identified by a unique name and carries a string value plus an optional description string. "
+ "The description string is for information only and is not evaluated by code.\n"
+ "\n"
+ "Meta information can be attached to the layout object and to cells. It is similar to "
+ "user properties. The differences are:\n"
+ "\n"
+ "@ul\n"
+ "@li Meta information is stored differently in GDS and OASIS files using the context information added "
+ " by KLayout to annotated PCell or library cells too. Hence meta information does not pollute "
+ " the standard user properties space. @/li\n"
+ "@li The value of meta information can be complex serializable types such as lists, hashes and elementary "
+ " objects such as \\Box or \\DBox. Scalar types include floats and booleans. @/li\n"
+ "@li Meta information keys are strings and are supported also for GDS which only accepts integer number "
+ " keys for user properties. @/li\n"
+ "@/ul\n"
+ "\n"
+ "Elementary (serializable) objects are: \\Box, \\DBox, \\Edge, \\DEdge, \\EdgePair, \\DEdgePair, "
+ "\\EdgePairs, \\Edges, \\LayerProperties, \\Matrix2d, \\Matrix3d, \\Path, \\DPath, \\Point, \\DPoint, "
+ "\\Polygon, \\DPolygon, \\SimplePolygon, \\DSimplePolygon, \\Region, \\Text, \\DText, \\Texts, "
+ "\\Trans, \\DTrans, \\CplxTrans, \\ICplxTrans, \\DCplxTrans, \\VCplxTrans, \\Vector, \\DVector "
+ "(list may not be complete).\n"
+ "\n"
+ "KLayout itself also generates meta information with specific keys. "
+ "For disambiguation, namespaces can be established by prefixing "
+ "the key strings with some unique identifier in XML fashion, like a domain name - "
+ "e.g. 'example.com:key'.\n"
+ "\n"
+ "@b Note: @/b only meta information marked with \\is_persisted? == true is stored in GDS or OASIS files. "
+ "This is not the default setting, so you need to explicitly set that flag.\n"
+ "\n"
+ "See also \\Layout#each_meta_info, \\Layout#meta_info_value, \\Layout#meta_info and \\Layout#remove_meta_info as "
+ "well as the corresponding \\Cell methods.\n"
+ "\n"
+ "An example of how to attach persisted meta information to a cell is here:\n"
+ "\n"
+ "@code\n"
+ "ly = RBA::Layout::new\n"
+ "c1 = ly.create_cell(\"C1\")\n"
+ "\n"
+ "mi = RBA::LayoutMetaInfo::new(\"the-answer\", 42.0)\n"
+ "mi.persisted = true\n"
+ "c1.add_meta_info(mi)\n"
+ "\n"
+ "# will now hold this piece of meta information attached to cell 'C1':\n"
+ "ly.write(\"to.gds\")\n"
+ "@/code\n"
+ "\n"
+ "This class has been introduced in version 0.25 and was extended in version 0.28.8."
+);
+
+}
diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h
new file mode 100644
index 000000000..84bbadc0a
--- /dev/null
+++ b/src/db/db/gsiDeclDbMetaInfo.h
@@ -0,0 +1,100 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2023 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
+
+*/
+
+#ifndef _HDR_gsiDeclDbMetaInfo
+#define _HDR_gsiDeclDbMetaInfo
+
+#include "dbLayout.h"
+#include "tlVariant.h"
+#include "tlObject.h"
+
+#include
+#include
+
+namespace gsi
+{
+
+struct MetaInfo
+{
+ MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v, bool p)
+ : name (n), description (d), value (v), persisted (p)
+ { }
+
+ MetaInfo (const std::string &n, const db::MetaInfo &mi)
+ : name (n), description (mi.description), value (mi.value), persisted (mi.persisted)
+ { }
+
+ MetaInfo ()
+ : name (), description (), value (), persisted (false)
+ { }
+
+ std::string name;
+ std::string description;
+ tl::Variant value;
+ bool persisted;
+};
+
+struct MetaInfoIterator
+{
+ typedef std::forward_iterator_tag iterator_category;
+ typedef MetaInfo value_type;
+ typedef void difference_type;
+ typedef MetaInfo reference;
+ typedef void pointer;
+
+ MetaInfoIterator ()
+ : mp_layout (), m_b (), m_e ()
+ { }
+
+ MetaInfoIterator (const db::Layout *layout, db::Layout::meta_info_iterator b, db::Layout::meta_info_iterator e)
+ : mp_layout (const_cast (layout)), m_b (b), m_e (e)
+ { }
+
+ bool at_end () const
+ {
+ return !mp_layout || m_b == m_e;
+ }
+
+ void operator++ ()
+ {
+ if (mp_layout) {
+ ++m_b;
+ }
+ }
+
+ MetaInfo operator* () const
+ {
+ if (mp_layout) {
+ return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second);
+ } else {
+ return MetaInfo ();
+ }
+ }
+
+private:
+ tl::weak_ptr mp_layout;
+ db::Layout::meta_info_iterator m_b, m_e;
+};
+
+}
+
+#endif
diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc
index 9419bacdb..011d2be8d 100644
--- a/src/db/unit_tests/dbLayoutDiffTests.cc
+++ b/src/db/unit_tests/dbLayoutDiffTests.cc
@@ -66,6 +66,9 @@ public:
void begin_edge_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b);
void end_edge_differences ();
+ void begin_edge_pair_differences ();
+ void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b);
+ void end_edge_pair_differences ();
void begin_text_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b);
void end_text_differences ();
@@ -339,6 +342,26 @@ TestDifferenceReceiver::end_edge_differences ()
{
}
+void
+TestDifferenceReceiver::begin_edge_pair_differences ()
+{
+ m_os << "layout_diff: edge pairs differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
+}
+
+void
+TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b)
+{
+ m_os << "Not in b but in a:" << std::endl;
+ print_diffs (pr, a, b);
+ m_os << "Not in a but in b:" << std::endl;
+ print_diffs (pr, b, a);
+}
+
+void
+TestDifferenceReceiver::end_edge_pair_differences ()
+{
+}
+
void
TestDifferenceReceiver::begin_text_differences ()
{
diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc
index e159ec41c..3b438bce5 100644
--- a/src/db/unit_tests/dbLayoutTests.cc
+++ b/src/db/unit_tests/dbLayoutTests.cc
@@ -543,7 +543,7 @@ TEST(5)
db::Layout l (&m);
EXPECT_EQ (l.technology_name (), "");
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
info.lib_name = "LIB";
info.cell_name = "LIBCELL";
@@ -624,7 +624,7 @@ TEST(6)
EXPECT_EQ (l.technology_name (), "");
- db::ProxyContextInfo info;
+ db::LayoutOrCellContextInfo info;
info.lib_name = "Basic";
info.pcell_name = "CIRCLE";
info.pcell_parameters ["actual_radius"] = tl::Variant (10.0);
@@ -644,7 +644,7 @@ TEST(6)
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n");
- db::ProxyContextInfo info2;
+ db::LayoutOrCellContextInfo info2;
l.get_context_info (cell->cell_index (), info2);
info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0);
@@ -677,7 +677,7 @@ TEST(7_LayerProperties)
db::Layout l (&m);
EXPECT_EQ (l.is_valid_layer (0), false);
- EXPECT_EQ (l.guiding_shape_layer (), 0);
+ EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0);
EXPECT_EQ (l.is_special_layer (0), true);
EXPECT_EQ (int (l.layers ()), 1);
@@ -737,3 +737,61 @@ TEST(7_LayerProperties)
EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1);
EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1);
}
+
+TEST(8_MetaInfo)
+{
+ db::Layout ly;
+
+ EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0);
+ EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1);
+ EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0);
+ EXPECT_EQ (ly.has_context_info (), false);
+
+ ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false));
+ ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true));
+
+ EXPECT_EQ (ly.has_context_info (), true);
+
+ EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil");
+ EXPECT_EQ (ly.meta_info ("x").description, "");
+ EXPECT_EQ (ly.meta_info ("x").persisted, false);
+
+ EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5");
+ EXPECT_EQ (ly.meta_info ("a").description, "description");
+ EXPECT_EQ (ly.meta_info ("a").persisted, false);
+
+ EXPECT_EQ (ly.meta_info (1).value.to_string (), "value");
+ EXPECT_EQ (ly.meta_info (1).description, "");
+ EXPECT_EQ (ly.meta_info (1).persisted, true);
+
+ db::cell_index_type ci = ly.add_cell ("X");
+
+ EXPECT_EQ (ly.has_context_info (ci), false);
+
+ ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false));
+ ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true));
+
+ EXPECT_EQ (ly.has_context_info (ci), true);
+
+ EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil");
+ EXPECT_EQ (ly.meta_info (ci, "x").description, "");
+ EXPECT_EQ (ly.meta_info (ci, "x").persisted, false);
+
+ EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1");
+ EXPECT_EQ (ly.meta_info (ci, "a").description, "dd");
+ EXPECT_EQ (ly.meta_info (ci, "a").persisted, false);
+
+ EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u");
+ EXPECT_EQ (ly.meta_info (ci, 1).description, "d");
+ EXPECT_EQ (ly.meta_info (ci, 1).persisted, true);
+
+ EXPECT_EQ (ly.has_context_info (), true);
+ ly.clear_meta ();
+ EXPECT_EQ (ly.has_context_info (), false);
+ EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil");
+
+ EXPECT_EQ (ly.has_context_info (ci), true);
+ ly.clear_meta (ci);
+ EXPECT_EQ (ly.has_context_info (ci), false);
+ EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil");
+}
diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc
index ec1e8bdc5..b678b1b62 100644
--- a/src/edt/edt/edtMainService.cc
+++ b/src/edt/edt/edtMainService.cc
@@ -800,7 +800,7 @@ MainService::cm_make_cell_variants ()
if (needs_variant) {
// need to create a variant: create a new cell
- db::cell_index_type new_cell_index = layout.add_cell (layout.cell_name (elem.inst_ptr.cell_index ()));
+ db::cell_index_type new_cell_index = layout.add_cell (layout, elem.inst_ptr.cell_index ());
// prepare a new variant cell
db::Cell &new_cell = layout.cell (new_cell_index);
diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc
index 18b5e98ad..8950458f0 100644
--- a/src/edt/edt/edtPartialService.cc
+++ b/src/edt/edt/edtPartialService.cc
@@ -243,9 +243,14 @@ insert_point_path (const db::Path &p, const std::set &sel, db::Po
ctr.push_back (p1);
if (! found && sel.find (EdgeWithIndex (db::Edge (p1, p2), n, n + 1, 0)) != sel.end ()) {
// project the point onto the edge
- std::pair projected = db::Edge (p1, p2).projected (ins);
+ db::Edge e (p1, p2);
+ std::pair projected = e.projected (ins);
if (projected.first) {
- ins = projected.second;
+ if (e.is_ortho ()) {
+ // NOTE: for skew edges we use the original point as the projected one usually
+ // is off-grid.
+ ins = projected.second;
+ }
ctr.push_back (ins);
found = true;
}
@@ -262,22 +267,28 @@ insert_point_path (const db::Path &p, const std::set &sel, db::Po
}
static void
-remove_redundant_points (std::vector &ctr)
+remove_redundant_points (std::vector &ctr, bool cyclic)
{
// compress contour (remove redundant points)
// and assign to path
std::vector::iterator wp = ctr.begin ();
std::vector::const_iterator rp = ctr.begin ();
- db::Point pm1 = *rp;
- if (wp != ctr.end ()) {
- ++wp;
- ++rp;
+ db::Point pm1;
+ if (rp != ctr.end ()) {
+ if (cyclic) {
+ pm1 = ctr.back ();
+ } else {
+ pm1 = ctr.front ();
+ ++wp;
+ ++rp;
+ }
while (rp != ctr.end ()) {
db::Point p0 = *rp;
if (p0 != pm1) {
*wp++ = p0;
}
+ pm1 = p0;
++rp;
}
}
@@ -304,7 +315,7 @@ del_points_path (const db::Path &p, const std::set &sel)
}
}
- remove_redundant_points (ctr);
+ remove_redundant_points (ctr, false);
new_path.assign (ctr.begin (), ctr.end ());
return new_path;
@@ -357,7 +368,7 @@ modify_path (db::Path &p, const std::map &new_points
}
if (compress) {
- remove_redundant_points (ctr);
+ remove_redundant_points (ctr, false);
}
p.assign (ctr.begin (), ctr.end ());
@@ -384,10 +395,14 @@ insert_point_poly (const db::Polygon &p, const std::set &sel, db:
ctr.push_back ((*e).p1 ());
if (! found && sel.find (EdgeWithIndex (*e, n, nn, c)) != sel.end ()) {
- // project the point onto the edge
+ // project the point onto the edge - use the first edge the point projects to
std::pair projected = (*e).projected (ins);
if (projected.first) {
- ins = projected.second;
+ if ((*e).is_ortho ()) {
+ // NOTE: for skew edges we use the original point as the projected one usually
+ // is off-grid.
+ ins = projected.second;
+ }
ctr.push_back (ins);
found = true;
}
@@ -397,7 +412,7 @@ insert_point_poly (const db::Polygon &p, const std::set &sel, db:
if (found) {
- remove_redundant_points (ctr);
+ remove_redundant_points (ctr, true);
new_poly = p;
if (c == 0) {
@@ -433,7 +448,7 @@ del_points_poly (const db::Polygon &p, const std::set &sel)
}
}
- remove_redundant_points (ctr);
+ remove_redundant_points (ctr, true);
if (c == 0) {
new_poly.assign_hull (ctr.begin (), ctr.end (), false /*compress*/);
@@ -495,7 +510,7 @@ modify_polygon (db::Polygon &p,
}
if (compress) {
- remove_redundant_points (ctr);
+ remove_redundant_points (ctr, true);
}
if (c == 0) {
@@ -727,7 +742,7 @@ public:
}
private:
- virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/);
+ virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level);
founds_vector_type m_founds;
};
@@ -736,25 +751,25 @@ private:
// PartialShapeFinder implementation
PartialShapeFinder::PartialShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator::flags_type flags)
- : lay::ShapeFinder (point_mode, top_level_sel, flags)
+ : lay::ShapeFinder (point_mode, top_level_sel, flags, 0)
{
set_test_count (point_sel_tests);
}
void
-PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/)
+PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int /*level*/)
{
if (! point_mode ()) {
for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) {
checkpoint ();
const db::Shapes &shapes = cell.shapes (*l);
- db::ShapeIterator shape = shapes.begin_touching (search_box, flags (), prop_sel (), inv_prop_sel ());
+ db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ());
while (! shape.at_end ()) {
checkpoint ();
@@ -784,9 +799,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
++ee;
unsigned int nn = ee.at_end () ? 0 : n + 1;
- if (search_box.contains ((*e).p1 ())) {
+ if (hit_box.contains ((*e).p1 ())) {
edges.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, c));
- if (search_box.contains ((*e).p2 ())) {
+ if (hit_box.contains ((*e).p2 ())) {
edges.push_back (EdgeWithIndex (*e, n, nn, c));
}
}
@@ -801,9 +816,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
db::Point pl;
unsigned int n = 0;
for (db::Shape::point_iterator pt = shape->begin_point (); pt != shape->end_point (); ++pt, ++n) {
- if (search_box.contains (*pt)) {
+ if (hit_box.contains (*pt)) {
edges.push_back (EdgeWithIndex (db::Edge (*pt, *pt), n, n, 0));
- if (pl_set && search_box.contains (pl)) {
+ if (pl_set && hit_box.contains (pl)) {
edges.push_back (EdgeWithIndex (db::Edge (pl, *pt), n - 1, n, 0));
}
}
@@ -825,9 +840,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
++ee;
unsigned int nn = ee.at_end () ? 0 : n + 1;
- if (search_box.contains ((*e).p1 ())) {
+ if (hit_box.contains ((*e).p1 ())) {
edges.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, 0));
- if (search_box.contains ((*e).p2 ())) {
+ if (hit_box.contains ((*e).p2 ())) {
edges.push_back (EdgeWithIndex (*e, n, nn, 0));
}
}
@@ -837,8 +852,23 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
} else if (shape->is_text ()) {
db::Point tp (shape->text_trans () * db::Point ());
- if (search_box.contains (tp)) {
- edges.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
+
+ if (text_info ()) {
+
+ db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
+ db::Text text;
+ shape->text (text);
+ db::Box tb = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp);
+ if (tb.inside (hit_box)) {
+ edges.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
+ }
+
+ } else {
+
+ if (hit_box.contains (tp)) {
+ edges.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
+ }
+
}
}
@@ -860,14 +890,14 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (hit_box))) {
checkpoint ();
const db::Shapes &shapes = cell.shapes (*l);
std::vector edge_sel;
- db::ShapeIterator shape = shapes.begin_touching (search_box, flags (), prop_sel (), inv_prop_sel ());
+ db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ());
while (! shape.at_end ()) {
bool match = false;
@@ -968,11 +998,29 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
} else if (shape->is_text ()) {
db::Point tp (shape->text_trans () * db::Point ());
- if (search_box.contains (tp)) {
- d = tp.distance (search_box.center ());
- edge_sel.clear ();
- edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
- match = true;
+
+ if (text_info ()) {
+
+ db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
+ db::Text text;
+ shape->text (text);
+ db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp));
+ if (tb.contains (hit_box.center ())) {
+ d = tp.distance (hit_box.center ());
+ edge_sel.clear ();
+ edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
+ match = true;
+ }
+
+ } else {
+
+ if (hit_box.contains (tp)) {
+ d = tp.distance (hit_box.center ());
+ edge_sel.clear ();
+ edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
+ match = true;
+ }
+
}
}
diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc
index 188f115f0..b8d2ca8c4 100644
--- a/src/edt/edt/edtService.cc
+++ b/src/edt/edt/edtService.cc
@@ -472,6 +472,8 @@ Service::selection_bbox ()
// TODO: this is done multiple times - once for each service!
TransformationVariants tv (view ());
+ lay::TextInfo text_info (view ());
+
db::DBox box;
for (objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
@@ -486,7 +488,11 @@ Service::selection_bbox ()
const std::vector *tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ());
if (tv_list != 0) {
for (std::vector::const_iterator t = tv_list->begin (); t != tv_list->end (); ++t) {
- box += *t * (ctx_trans * r->shape ().bbox ());
+ if (r->shape ().is_text ()) {
+ box += *t * text_info.bbox (ctx_trans * r->shape ().text (), *t);
+ } else {
+ box += *t * (ctx_trans * r->shape ().bbox ());
+ }
}
}
diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h
index 9108c77a5..14e16c22f 100644
--- a/src/edt/edt/edtService.h
+++ b/src/edt/edt/edtService.h
@@ -32,6 +32,7 @@
#include "layMarker.h"
#include "laySnap.h"
#include "layObjectInstPath.h"
+#include "layTextInfo.h"
#include "tlColor.h"
#include "dbLayout.h"
#include "dbShape.h"
diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc
index 600c11ab8..2d7696c28 100644
--- a/src/gsi/gsi/gsiClassBase.cc
+++ b/src/gsi/gsi/gsiClassBase.cc
@@ -651,11 +651,26 @@ static void collect_classes (const gsi::ClassBase *cls, std::list::const_iterator cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
+ for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
collect_classes (cc.operator-> (), unsorted_classes);
}
}
+static bool all_parts_available (const gsi::ClassBase *cls, const std::set &taken)
+{
+ if (cls->declaration () && cls->declaration () != cls && taken.find (cls->declaration ()) == taken.end ()) {
+ return false;
+ }
+
+ for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
+ if (! all_parts_available (cc.operator-> (), taken)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
std::list
ClassBase::classes_in_definition_order (const char *mod_name)
{
@@ -687,7 +702,7 @@ ClassBase::classes_in_definition_order (const char *mod_name)
continue;
}
- if ((*c)->declaration () && (*c)->declaration () != *c && taken.find ((*c)->declaration ()) == taken.end ()) {
+ if (! all_parts_available (*c, taken)) {
// can't produce this class yet - it's a reference to another class which is not produced yet.
more_classes.push_back (*c);
continue;
diff --git a/src/lay/lay/MacroEditorDialog.ui b/src/lay/lay/MacroEditorDialog.ui
index 95b142033..80f62e53c 100644
--- a/src/lay/lay/MacroEditorDialog.ui
+++ b/src/lay/lay/MacroEditorDialog.ui
@@ -94,10 +94,7 @@
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">New folder</p></body></html>
+ New folder
...
@@ -121,10 +118,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;">New</p></body></html>
+ New
...
@@ -141,10 +135,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;">Delete</p></body></html>
+ Delete
...
@@ -161,10 +152,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Rename</p></body></html>
+ Rename
Rename
@@ -181,10 +169,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Import file</p></body></html>
+ Import file
Import
@@ -208,7 +193,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head/><body><p>Save all files (Ctrl+Shift+S)</p></body></html>
+ Save all files (Ctrl+Shift+S)
...
@@ -228,7 +213,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head/><body><p>Save current file (Ctrl+S)</p></body></html>
+ Save current file (Ctrl+S)
...
@@ -333,8 +318,8 @@ p, li { white-space: pre-wrap; }
...
-
- :/back_16.png:/back_16.png
+
+ :/back_16px.png:/back_16px.png
true
@@ -347,8 +332,8 @@ p, li { white-space: pre-wrap; }
...
-
- :/forward_16.png:/forward_16.png
+
+ :/forward_16px.png:/forward_16px.png
true
@@ -365,10 +350,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Close tab</p></body></html>
+ Close tab
close
@@ -432,10 +414,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Stop script</p></body></html>
+ Stop script
...
@@ -452,10 +431,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pause script (Ctrl+F5)</p></body></html>
+ Pause script (Ctrl+F5)
...
@@ -482,10 +458,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set breakpoint (F9)</p></body></html>
+ Set breakpoint (F9)
...
@@ -505,10 +478,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Clear all breakpoints (Ctrl+Shift+F9)</p></body></html>
+ Clear all breakpoints (Ctrl+Shift+F9)
...
@@ -535,10 +505,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Step into procedure (F11)</p></body></html>
+ Step into procedure (F11)
S
@@ -558,10 +525,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Step over procedure or block (F10)</p></body></html>
+ Step over procedure or block (F10)
N
@@ -588,10 +552,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Enable or disable debugging</p></body></html>
+ Enable or disable debugging
DBG
@@ -618,10 +579,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Edit properties of macro</p></body></html>
+ Edit properties of macro
P
@@ -638,10 +596,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Setup colors, formats, debugger</p></body></html>
+ Setup colors, formats, debugger
prop
@@ -674,10 +629,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Find next</p></body></html>
+ Find next (Ctrl+F)
N
@@ -687,7 +639,7 @@ p, li { white-space: pre-wrap; }
:/find_16px.png:/find_16px.png
- F3
+ Ctrl+F
true
@@ -709,10 +661,7 @@ p, li { white-space: pre-wrap; }
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Show replace mode</p></body></html>
+ Show replace mode
...
@@ -764,10 +713,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Replace and find next</p></body></html>
+ Replace and find next (Ctrl+R)
RN
@@ -777,7 +723,7 @@ p, li { white-space: pre-wrap; }
:/replace_16px.png:/replace_16px.png
-
+ Ctrl+R
true
@@ -787,10 +733,7 @@ p, li { white-space: pre-wrap; }
-
- <html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Replace all</p></body></html>
+ Replace all (Ctrl+Shift+R)
All
@@ -799,6 +742,9 @@ p, li { white-space: pre-wrap; }
:/replace_all_16px.png:/replace_all_16px.png
+
+ Ctrl+Shift+R
+
true
@@ -1497,14 +1443,6 @@ p, li { white-space: pre-wrap; }
Case Sensitive
-
-
- Search & Replace
-
-
- Ctrl+F
-
-
Save As
diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc
index 0a42bcb37..a07b857a1 100644
--- a/src/lay/lay/layLibraryController.cc
+++ b/src/lay/lay/layLibraryController.cc
@@ -200,9 +200,10 @@ LibraryController::sync_files ()
reader.read (lib->layout ());
// Use the libname if there is one
+ db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) {
- if (m->name == "libname" && ! m->value.empty ()) {
- lib->set_name (m->value);
+ if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
+ lib->set_name (m->second.value.to_string ());
break;
}
}
diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc
index 4924798da..3266b7c60 100644
--- a/src/lay/lay/layMacroEditorDialog.cc
+++ b/src/lay/lay/layMacroEditorDialog.cc
@@ -386,9 +386,6 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection
connect (actionUseRegularExpressions, SIGNAL (triggered ()), this, SLOT (search_editing ()));
connect (actionCaseSensitive, SIGNAL (triggered ()), this, SLOT (search_editing ()));
- addAction (actionSearchReplace);
- connect (actionSearchReplace, SIGNAL (triggered ()), this, SLOT (search_replace ()));
-
searchEditBox->set_clear_button_enabled (true);
searchEditBox->set_options_button_enabled (true);
searchEditBox->set_options_menu (m);
@@ -1974,7 +1971,7 @@ MacroEditorDialog::find_next_button_clicked ()
apply_search (true);
page->find_next ();
- if (sender () != searchEditBox && sender () != replaceText) {
+ if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) {
set_editor_focus ();
}
}
@@ -1989,7 +1986,7 @@ MacroEditorDialog::find_prev_button_clicked ()
apply_search (true);
page->find_prev ();
- if (sender () != searchEditBox && sender () != replaceText) {
+ if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) {
set_editor_focus ();
}
}
@@ -2004,7 +2001,7 @@ MacroEditorDialog::replace_next_button_clicked ()
apply_search (true);
page->replace_and_find_next (replaceText->text ());
- if (sender () != replaceText) {
+ if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) {
set_editor_focus ();
}
}
@@ -2034,12 +2031,6 @@ MacroEditorDialog::search_requested (const QString &s)
search_editing ();
}
-void
-MacroEditorDialog::search_replace ()
-{
- searchEditBox->setFocus (Qt::TabFocusReason);
-}
-
void
MacroEditorDialog::search_editing ()
{
@@ -2050,7 +2041,9 @@ MacroEditorDialog::search_editing ()
apply_search ();
page->find_reset (); // search from the initial position
- page->find_next ();
+ if (! page->has_multi_block_selection ()) {
+ page->find_next ();
+ }
}
void
@@ -2083,7 +2076,9 @@ MacroEditorDialog::do_search_edited ()
apply_search ();
page->find_reset (); // search from the initial position
- page->find_next ();
+ if (! page->has_multi_block_selection ()) {
+ page->find_next ();
+ }
set_editor_focus ();
}
diff --git a/src/lay/lay/layMacroEditorDialog.h b/src/lay/lay/layMacroEditorDialog.h
index 7461f2a2c..54d9428df 100644
--- a/src/lay/lay/layMacroEditorDialog.h
+++ b/src/lay/lay/layMacroEditorDialog.h
@@ -245,7 +245,6 @@ protected slots:
void file_changed (const QString &path);
void file_removed (const QString &path);
void clear_log ();
- void search_replace ();
void apply_search ()
{
apply_search (false);
diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc
index 1bb507820..408949cd2 100644
--- a/src/lay/lay/layMacroEditorPage.cc
+++ b/src/lay/lay/layMacroEditorPage.cc
@@ -1196,66 +1196,121 @@ MacroEditorPage::replace_and_find_next (const QString &replace)
return;
}
- QTextCursor c = mp_text->textCursor ();
- if (c.hasSelection ()) {
- QTextBlock b = c.block ();
- int o = std::max (0, c.position () - b.position ());
- if (m_current_search.indexIn (b.text (), o) == o) {
- c.insertText (interpolate_string (replace, m_current_search));
- }
- }
-
+ replace_in_selection (replace, true);
find_next ();
}
-void
+void
MacroEditorPage::replace_all (const QString &replace)
{
if (! mp_macro || mp_macro->is_readonly ()) {
return;
}
+ replace_in_selection (replace, false);
+}
+
+void
+MacroEditorPage::replace_in_selection (const QString &replace, bool first)
+{
const QTextDocument *doc = mp_text->document ();
QTextBlock bs = doc->begin (), be = doc->end ();
+ int ps = 0;
+ int pe = be.length ();
QTextCursor c = mp_text->textCursor ();
- if (c.hasSelection ()) {
- QTextBlock s = mp_text->document ()->findBlock (mp_text->textCursor ().selectionStart ());
- QTextBlock e = mp_text->document ()->findBlock (mp_text->textCursor ().selectionEnd ());
- if (e != s) {
- bs = s;
- be = e;
- }
+ bool has_selection = c.hasSelection ();
+ bool anchor_at_end = false;
+
+ if (has_selection) {
+
+ anchor_at_end = (c.selectionStart () == c.position ());
+
+ ps = c.selectionStart ();
+ pe = c.selectionEnd ();
+
+ bs = mp_text->document ()->findBlock (ps);
+ be = mp_text->document ()->findBlock (pe);
+
+ } else if (first) {
+
+ // don't replace first entry without selection
+ return;
+
}
+ ps -= bs.position ();
+ pe -= be.position ();
+
c.beginEditBlock ();
- for (QTextBlock b = bs; b != be; b = b.next()) {
+ bool done = false;
+
+ for (QTextBlock b = bs; ; b = b.next()) {
int o = 0;
- while (true) {
+ while (!done) {
+
+ bool substitute = false;
int i = m_current_search.indexIn (b.text (), o);
if (i < 0) {
break;
} else if (m_current_search.matchedLength () == 0) {
break; // avoid an infinite loop
+ } else if (b == bs && i < ps) {
+ // ignore
+ } else if (b == be && i + m_current_search.matchedLength () > pe) {
+ // ignore
+ done = true;
+ } else {
+ substitute = true;
}
- QString r = interpolate_string (replace, m_current_search);
+ if (substitute) {
- c.setPosition (i + b.position () + m_current_search.matchedLength ());
- c.setPosition (i + b.position (), QTextCursor::KeepAnchor);
- c.insertText (r);
+ QString r = interpolate_string (replace, m_current_search);
- o = i + r.size ();
+ c.setPosition (i + b.position () + m_current_search.matchedLength ());
+ c.setPosition (i + b.position (), QTextCursor::KeepAnchor);
+ c.insertText (r);
- }
+ o = i + r.size ();
+
+ if (first) {
+
+ // in single-selection mode, put cursor past substitution
+ c.setPosition (i + b.position ());
+ has_selection = false;
+ done = true;
+
+ } else if (b == be) {
+ pe += int (r.size ()) - int (m_current_search.matchedLength ());
+ }
+
+ } else {
+
+ o = i + m_current_search.matchedLength ();
+
+ }
+
+ }
+
+ if (b == be || done) {
+ break;
+ }
}
+ if (has_selection) {
+ // restore selection which might have changed due to insert
+ c.setPosition (anchor_at_end ? be.position () + pe : bs.position () + ps);
+ c.setPosition (!anchor_at_end ? be.position () + pe : bs.position () + ps, QTextCursor::KeepAnchor);
+ }
+
+ mp_text->setTextCursor (c);
c.endEditBlock ();
}
@@ -1381,6 +1436,19 @@ MacroEditorPage::current_pos () const
return mp_text->textCursor ().position () - mp_text->textCursor ().block ().position ();
}
+bool
+MacroEditorPage::has_multi_block_selection () const
+{
+ QTextCursor c = mp_text->textCursor ();
+ if (c.selectionStart () != c.selectionEnd ()) {
+ QTextBlock s = mp_text->document ()->findBlock (c.selectionStart ());
+ QTextBlock e = mp_text->document ()->findBlock (c.selectionEnd ());
+ return e != s;
+ } else {
+ return false;
+ }
+}
+
bool
MacroEditorPage::tab_key_pressed ()
{
diff --git a/src/lay/lay/layMacroEditorPage.h b/src/lay/lay/layMacroEditorPage.h
index 960a13538..a689aefe5 100644
--- a/src/lay/lay/layMacroEditorPage.h
+++ b/src/lay/lay/layMacroEditorPage.h
@@ -246,6 +246,7 @@ public:
int current_line () const;
int current_pos () const;
+ bool has_multi_block_selection () const;
void set_debugging_on (bool debugging_on);
@@ -313,6 +314,7 @@ private:
void fill_completer_list ();
void complete ();
QTextCursor get_completer_cursor (int &pos0, int &pos);
+ void replace_in_selection (const QString &replace, bool first);
bool eventFilter (QObject *watched, QEvent *event);
};
diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc
index af0a981f1..73725629b 100644
--- a/src/laybasic/laybasic/layBitmap.cc
+++ b/src/laybasic/laybasic/layBitmap.cc
@@ -791,35 +791,6 @@ Bitmap::render_contour (std::vector &edges)
}
}
-static unsigned char next_char_latin1_from_utf8 (const char *&cp, const char *cpf = 0)
-{
- unsigned char c = *cp;
- if ((c & 0xe0) == 0xc0) {
- if ((cp[1] & 0xc0) == 0x80 && (! cpf || cpf > cp + 1)) {
- unsigned int x = ((unsigned int) ((unsigned char) c & 0x1f) << 6) | (unsigned int) (cp[1] & 0x3f);
- cp += 1;
- if (x < 255) {
- c = x;
- } else {
- c = '?';
- }
- } else {
- c = '?';
- }
- } else if ((c & 0xf0) == 0xe0) {
- if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (! cpf || cpf > cp + 2)) {
- cp += 2;
- }
- c = '?';
- } else if ((c & 0xf8) == 0xf0) {
- if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (cp[3] & 0xc0) == 0x80 && (! cpf || cpf > cp + 3)) {
- cp += 3;
- }
- c = '?';
- }
- return c;
-}
-
void
Bitmap::render_text (const lay::RenderText &text)
{
@@ -830,12 +801,11 @@ Bitmap::render_text (const lay::RenderText &text)
// count the lines and max. characters per line
unsigned int lines = 1;
- for (const char *cp = text.text.c_str (); *cp; ++cp) {
- if (*cp == '\012' || *cp == '\015') {
- if (*cp == '\015' && cp[1] == '\012') {
- ++cp;
- }
+ for (const char *cp = text.text.c_str (); *cp; ) {
+ if (tl::skip_newline (cp)) {
++lines;
+ } else {
+ ++cp;
}
}
@@ -858,10 +828,9 @@ Bitmap::render_text (const lay::RenderText &text)
unsigned int length = 0;
const char *cp = cp1;
- while (*cp && *cp != '\012' && *cp != '\015') {
- next_char_latin1_from_utf8 (cp);
+ while (*cp && !tl::is_newline (*cp)) {
+ tl::utf32_from_utf8 (cp);
++length;
- ++cp;
}
double xx;
@@ -878,13 +847,15 @@ Bitmap::render_text (const lay::RenderText &text)
if (y > -0.5 && y < double (height () + ff.height () - 1) - 0.5) {
- for ( ; cp1 != cp; ++cp1) {
+ while (cp1 != cp) {
- unsigned char c = next_char_latin1_from_utf8 (cp1, cp);
+ uint32_t c = tl::utf32_from_utf8 (cp1, cp);
+ if (c < uint32_t (ff.first_char ()) || c >= uint32_t (ff.n_chars ()) + ff.first_char ()) {
+ // NOTE: '?' needs to be a valid character always
+ c = uint32_t ('?');
+ }
- size_t cc = c; // to suppress a compiler warning ..
- if (c >= ff.first_char () && cc < size_t (ff.n_chars ()) + size_t (ff.first_char ())
- && xx > -100.0 && xx < double (width ())) {
+ if (xx > -100.0 && xx < double (width ())) {
fill_pattern (int (y + 0.5), int (floor (xx)), ff.data () + (c - ff.first_char ()) * ff.height () * ff.stride (), ff.stride (), ff.height ());
}
@@ -897,11 +868,7 @@ Bitmap::render_text (const lay::RenderText &text)
}
// next line
- if (*cp1 == '\012' || *cp1 == '\015') {
- if (*cp1 == '\015' && cp1[1] == '\012') {
- ++cp1;
- }
- ++cp1;
+ if (tl::skip_newline (cp1)) {
y -= double (ff.line_height ());
}
diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc
index 6fa5b6661..168efce12 100644
--- a/src/laybasic/laybasic/layFinder.cc
+++ b/src/laybasic/laybasic/layFinder.cc
@@ -23,6 +23,7 @@
#include "tlProgress.h"
#include "layFinder.h"
+#include "layTextInfo.h"
namespace lay
{
@@ -86,10 +87,11 @@ Finder::closer (double d)
}
void
-Finder::start (lay::LayoutViewBase *view, const lay::CellView &cv, unsigned int cv_index, const std::vector &trans, const db::Box ®ion, int min_level, int max_level, const std::vector &layers)
+Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers)
{
+ const lay::CellView &cv = view->cellview (cv_index);
+
m_layers = layers;
- m_region = region;
mp_layout = &cv->layout ();
mp_view = view;
m_cv_index = cv_index;
@@ -97,17 +99,27 @@ Finder::start (lay::LayoutViewBase *view, const lay::CellView &cv, unsigned int
m_max_level = std::max (m_min_level, std::min (max_level, m_top_level_sel ? ((int) cv.specific_path ().size () + 1) : max_level));
if (layers.size () == 1) {
+
m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]);
m_cell_box_convert = db::box_convert ((unsigned int) layers [0]);
+
} else {
+
m_box_convert = db::box_convert (*mp_layout);
m_cell_box_convert = db::box_convert ();
+
}
m_path.erase (m_path.begin (), m_path.end ());
- for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) {
- do_find (*cv.cell (), int (cv.specific_path ().size ()), *t * cv.context_trans ());
+ for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) {
+
+ db::VCplxTrans it = (*t * db::CplxTrans (mp_layout->dbu ())).inverted ();
+ m_region = it * region;
+ m_scan_region = it * scan_region;
+
+ do_find (*cv.cell (), int (cv.specific_path ().size ()), view->viewport ().trans () * *t, cv.context_trans ());
+
}
}
@@ -125,8 +137,11 @@ Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, double &dis
double d1 = p1.double_distance (m_region.center ());
double d2 = p2.double_distance (m_region.center ());
- // snap to the point - nothing can get closer
- distance = 0.0;
+ double d = std::min (d1, d2);
+ if (! match || d < distance) {
+ distance = d;
+ }
+
if (d1 < d2) {
ret = 1;
} else {
@@ -155,7 +170,7 @@ Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, double &dis
}
void
-Finder::do_find (const db::Cell &cell, int level, const db::ICplxTrans &t)
+Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t)
{
if (level <= m_max_level /*take level of cell itself*/
&& cell.is_proxy ()
@@ -164,33 +179,38 @@ Finder::do_find (const db::Cell &cell, int level, const db::ICplxTrans &t)
// when looking at the guiding shape layer, we can visit this cell as well allowing to find the guiding shapes
- db::Box touch_box (t.inverted () * m_region);
+ db::ICplxTrans it = t.inverted ();
+ db::Box scan_box (it * m_scan_region);
+ db::Box hit_box (it * m_region);
if (level >= m_min_level) {
- visit_cell (cell, touch_box, t, level);
+ visit_cell (cell, hit_box, scan_box, vp, t, level);
}
} else if (level < m_max_level
- && (t * m_cell_box_convert (cell)).touches (m_region)
+ && (t * m_cell_box_convert (cell)).touches (m_scan_region)
&& (mp_view->select_inside_pcells_mode () || !cell.is_proxy ())
&& !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index)) {
- db::Box touch_box (t.inverted () * m_region);
+ db::ICplxTrans it = t.inverted ();
+ db::Box scan_box (it * m_scan_region);
+ db::Box hit_box (it * m_region);
if (level >= m_min_level) {
- visit_cell (cell, touch_box, t, level);
+ visit_cell (cell, hit_box, scan_box, vp, t, level);
}
- db::Cell::touching_iterator inst = cell.begin_touching (touch_box);
+ db::Cell::touching_iterator inst = cell.begin_touching (scan_box);
while (! inst.at_end ()) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
- for (db::CellInstArray::iterator p = cell_inst.begin_touching (touch_box, m_box_convert); ! p.at_end (); ++p) {
+ for (db::CellInstArray::iterator p = cell_inst.begin_touching (scan_box, m_box_convert); ! p.at_end (); ++p) {
m_path.push_back (db::InstElement (*inst, p));
do_find (mp_layout->cell (cell_inst.object ().cell_index ()),
level + 1,
+ vp,
t * cell_inst.complex_trans (*p));
m_path.pop_back ();
@@ -211,6 +231,7 @@ ShapeFinder::ShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator
: Finder (point_mode, top_level_sel),
mp_excludes ((excludes && !excludes->empty ()) ? excludes : 0),
m_flags (flags), m_cv_index (0), m_topcell (0),
+ mp_text_info (0),
mp_prop_sel (0), m_inv_prop_sel (false), mp_progress (0)
{
m_tries = point_sel_tests;
@@ -275,6 +296,9 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu)
m_context_layers.clear ();
m_cells_with_context.clear ();
+ lay::TextInfo text_info (view);
+ mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0;
+
std::vector lprops;
for (lay::LayerPropertiesConstIterator lp = view->begin_layers (); ! lp.at_end (); ++lp) {
if (lp->is_visual ()) {
@@ -340,6 +364,9 @@ ShapeFinder::find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops
m_cells_with_context.clear ();
m_context_layers.clear ();
+ lay::TextInfo text_info (view);
+ mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0;
+
std::vector layers;
layers.push_back (lprops.layer_index ());
bool result = find_internal (view, lprops.cellview_index (), &lprops.prop_sel (), lprops.inverse_prop_sel (), lprops.hier_levels (), lprops.trans (), layers, region_mu);
@@ -360,14 +387,6 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co
m_topcell = cv.cell_index ();
- double dbu = cv->layout ().dbu ();
- db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu;
- std::vector trans;
- trans.reserve(trans_mu.size());
- for (std::vector::const_iterator t = trans_mu.begin(); t != trans_mu.end(); ++t) {
- trans.push_back(db::VCplxTrans(1.0 / dbu) * *t * db::CplxTrans(dbu));
- }
-
mp_prop_sel = prop_sel;
m_inv_prop_sel = inv_prop_sel;
@@ -382,13 +401,34 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co
max_level = hier_sel.to_level (ctx_path_length, max_level);
}
- // actually find
+ auto flags_saved = m_flags;
+
try {
- start (view, cv, m_cv_index, trans, region, min_level, max_level, layers);
+
+ if ((m_flags & db::ShapeIterator::Texts) != 0 && mp_text_info) {
+
+ m_flags = db::ShapeIterator::Texts;
+
+ // for catching all labels we search the whole view area
+ db::DBox scan_region_mu = view->viewport ().box ();
+ start (view, m_cv_index, trans_mu, region_mu, scan_region_mu, min_level, max_level, layers);
+
+ m_flags = db::ShapeIterator::flags_type (flags_saved - db::ShapeIterator::Texts);
+
+ }
+
+ // another pass with tight search box and without texts
+ start (view, m_cv_index, trans_mu, region_mu, region_mu, min_level, max_level, layers);
+
} catch (StopException) {
- // ..
+ // ...
+ } catch (...) {
+ m_flags = flags_saved;
+ throw;
}
+ m_flags = flags_saved;
+
// return true if anything was found
return ! m_founds.empty ();
}
@@ -406,7 +446,7 @@ ShapeFinder::checkpoint ()
}
void
-ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/)
+ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int /*level*/)
{
if (! m_context_layers.empty ()) {
@@ -436,17 +476,27 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const
for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) {
const db::Shapes &shapes = cell.shapes ((unsigned int) *l);
- db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel);
+ db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end ()) {
checkpoint ();
+ db::Box bbox;
+ if (text_info () && shape->is_text ()) {
+ db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
+ db::Text text;
+ shape->text (text);
+ bbox = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp);
+ } else {
+ bbox = shape->bbox ();
+ }
+
// in box mode, just test the boxes
- if (shape->bbox ().inside (search_box)) {
+ if (bbox.inside (hit_box)) {
m_founds.push_back (lay::ObjectInstPath ());
m_founds.back ().set_cv_index (m_cv_index);
@@ -474,13 +524,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const
for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) {
checkpoint ();
const db::Shapes &shapes = cell.shapes (*l);
- db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel);
+ db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end ()) {
bool match = false;
@@ -488,7 +538,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const
checkpoint ();
- db::Point point (search_box.center ());
+ db::Point point (hit_box.center ());
// in point mode, test the edges and use a "closest" criterion
if (shape->is_polygon ()) {
@@ -529,9 +579,15 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const
match = true;
}
- } else if (shape->is_box ()) {
+ } else if (shape->is_box () || shape->is_text ()) {
- const db::Box &box = shape->box ();
+ db::Box box = shape->bbox ();
+ if (text_info () && shape->is_text ()) {
+ db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
+ db::Text text;
+ shape->text (text);
+ box = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp);
+ }
// point-like boxes are handles which attract the finder
if (box.width () == 0 && box.height () == 0) {
@@ -545,21 +601,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const
test_edge (t, *e, d, match);
}
- if (! match && box.contains (search_box.center ())) {
- d = t.ctrans (poly_dist (poly.begin_edge (), point));
+ if (! match && box.contains (hit_box.center ())) {
+ d = t.ctrans (poly_dist (poly.begin_edge (), point));
match = true;
}
}
- } else if (shape->is_text ()) {
-
- db::Point tp (shape->text_trans () * db::Point ());
- if (search_box.contains (tp)) {
- d = t.ctrans (tp.distance (search_box.center ()));
- match = true;
- }
-
}
if (match) {
@@ -679,14 +727,11 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db
m_topcell = cv.cell ()->cell_index ();
mp_view = view;
- double dbu = cv->layout ().dbu ();
- db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu;
-
// actually find
try {
- std::vector tv;
- tv.push_back (db::VCplxTrans (1.0 / dbu) * trans_mu * db::CplxTrans (dbu));
- start (view, cv, cv_index, tv, region, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ());
+ std::vector tv;
+ tv.push_back (trans_mu);
+ start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ());
} catch (StopException) {
// ..
}
@@ -696,7 +741,7 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db
}
void
-InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level)
+InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::Box & /*scan_box*/, const db::DCplxTrans & /*vp*/, const db::ICplxTrans &t, int level)
{
if (! point_mode ()) {
@@ -894,5 +939,5 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
}
-} // namespace edt
+} // namespace lay
diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h
index e269fa3e0..065e69741 100644
--- a/src/laybasic/laybasic/layFinder.h
+++ b/src/laybasic/laybasic/layFinder.h
@@ -44,6 +44,8 @@ namespace tl
namespace lay
{
+class TextInfo;
+
/**
* @brief A generic finder class
*
@@ -152,8 +154,17 @@ protected:
* are used). For each matching cell, the "visit_cell" method is called. A
* path of instantiations up to the top cell is maintained and accessible by
* the path() accessor.
+ *
+ * @param view The layout view to run the scan on
+ * @param cv_index The cell view to run the scan on
+ * @param trans A set of visual transformations applied to the display (layer properties transformations) in micron space
+ * @param region The hit region which the object is checked against
+ * @param scan_region The region where the object is looked up (can be bigger than the hit region for visual label box detection)
+ * @param min_level The minimum hierarchy level to check
+ * @param max_level The maximum hierarchy level to check
+ * @param layers A set of layers to check
*/
- void start (LayoutViewBase *view, const lay::CellView &cv, unsigned int cv_index, const std::vector &trans, const db::Box ®ion, int min_level, int max_level, const std::vector &layers = std::vector ());
+ void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ());
/**
* @brief Provide a basic edge test facility
@@ -172,7 +183,7 @@ protected:
unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match);
private:
- void do_find (const db::Cell &cell, int level, const db::ICplxTrans &t);
+ void do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t);
/**
* @brief Visitor sugar function
@@ -181,7 +192,7 @@ private:
* cell. It may use the "closer" method to determine if something is closer
* to whatever.
*/
- virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level) = 0;
+ virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level) = 0;
int m_min_level, m_max_level;
std::vector m_path;
@@ -189,6 +200,7 @@ private:
lay::LayoutViewBase *mp_view;
unsigned int m_cv_index;
db::Box m_region;
+ db::Box m_scan_region;
std::vector m_layers;
double m_distance;
bool m_point_mode;
@@ -233,7 +245,12 @@ protected:
return m_flags;
}
- unsigned int cv_index () const
+ const lay::TextInfo *text_info () const
+ {
+ return mp_text_info;
+ }
+
+ unsigned int cv_index () const
{
return m_cv_index;
}
@@ -261,7 +278,8 @@ protected:
void checkpoint ();
private:
- virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/);
+ virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level);
+
bool find_internal (LayoutViewBase *view,
unsigned int cv_index,
const std::set *prop_sel,
@@ -276,6 +294,7 @@ private:
db::ShapeIterator::flags_type m_flags;
unsigned int m_cv_index;
db::cell_index_type m_topcell;
+ const lay::TextInfo *mp_text_info;
const std::set *mp_prop_sel;
bool m_inv_prop_sel;
int m_tries;
@@ -314,7 +333,8 @@ public:
}
private:
- virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level);
+ virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level);
+
bool find_internal (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox ®ion_mu);
unsigned int m_cv_index;
diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h
index f92a48855..938219763 100644
--- a/src/laybasic/laybasic/layLayoutCanvas.h
+++ b/src/laybasic/laybasic/layLayoutCanvas.h
@@ -320,6 +320,11 @@ public:
return m_line_styles;
}
+ /**
+ * @brief Reimplementation of ViewObjectCanvas: Resolution
+ */
+ double resolution () const;
+
/**
* @brief Reimplementation of ViewObjectCanvas: Background color
*/
@@ -444,8 +449,6 @@ private:
void do_redraw_all (bool force_redraw = true);
void prepare_drawing ();
- virtual double resolution () const;
-
const std::vector &scaled_view_ops (unsigned int lw);
};
diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc
index c697441fc..9d9e58598 100644
--- a/src/laybasic/laybasic/layLayoutViewBase.cc
+++ b/src/laybasic/laybasic/layLayoutViewBase.cc
@@ -3202,6 +3202,25 @@ LayoutViewBase::reload_layout (unsigned int cv_index)
goto_view (state);
}
+static void
+get_lyp_from_meta_info (const db::Layout &layout, std::string &lyp_file, bool &add_other_layers)
+{
+ db::Layout::meta_info_name_id_type layer_properties_file_name_id = layout.meta_info_name_id ("layer-properties-file");
+ db::Layout::meta_info_name_id_type layer_properties_add_other_layers_name_id = layout.meta_info_name_id ("layer-properties-add-other-layers");
+
+ for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) {
+ if (meta->first == layer_properties_file_name_id) {
+ lyp_file = meta->second.value.to_string ();
+ }
+ if (meta->first == layer_properties_add_other_layers_name_id) {
+ try {
+ add_other_layers = meta->second.value.to_bool ();
+ } catch (...) {
+ }
+ }
+ }
+}
+
unsigned int
LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers)
{
@@ -3266,17 +3285,7 @@ LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview,
}
// Give the layout object a chance to specify a certain layer property file
- for (db::Layout::meta_info_iterator meta = cv->layout ().begin_meta (); meta != cv->layout ().end_meta (); ++meta) {
- if (meta->name == "layer-properties-file") {
- lyp_file = meta->value;
- }
- if (meta->name == "layer-properties-add-other-layers") {
- try {
- tl::from_string (meta->value, add_other_layers);
- } catch (...) {
- }
- }
- }
+ get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers);
// interpolate the layout properties file name
tl::Eval expr;
@@ -3438,17 +3447,7 @@ LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOp
}
// Give the layout object a chance to specify a certain layer property file
- for (db::Layout::meta_info_iterator meta = cv->layout().begin_meta (); meta != cv->layout().end_meta (); ++meta) {
- if (meta->name == "layer-properties-file") {
- lyp_file = meta->value;
- }
- if (meta->name == "layer-properties-add-other-layers") {
- try {
- tl::from_string (meta->value, add_other_layers);
- } catch (...) {
- }
- }
- }
+ get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers);
// interpolate the layout properties file name
tl::Eval expr;
@@ -3853,13 +3852,26 @@ LayoutViewBase::pan_center (const db::DPoint &p)
void
LayoutViewBase::zoom_in ()
{
- shift_window (zoom_factor, 0.0, 0.0);
+ zoom_by (zoom_factor);
}
void
LayoutViewBase::zoom_out ()
{
- shift_window (1.0 / zoom_factor, 0.0, 0.0);
+ zoom_by (1.0 / zoom_factor);
+}
+
+void
+LayoutViewBase::zoom_by (double f)
+{
+ db::DBox b = mp_canvas->viewport ().box ();
+
+ db::DPoint c = b.center ();
+ if (mp_canvas->mouse_in_window ()) {
+ c = mp_canvas->mouse_position_um ();
+ }
+
+ zoom_box ((b.moved (db::DPoint () - c) * f).moved (c - db::DPoint ()));
}
void
diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h
index 0f8281458..351830fc7 100644
--- a/src/laybasic/laybasic/layLayoutViewBase.h
+++ b/src/laybasic/laybasic/layLayoutViewBase.h
@@ -1721,6 +1721,14 @@ public:
return mp_canvas;
}
+ /**
+ * @brief Gets the canvas object (const version)
+ */
+ const lay::LayoutCanvas *canvas () const
+ {
+ return mp_canvas;
+ }
+
#if defined(HAVE_QT)
/**
* @brief Gets the layer control panel
@@ -2881,6 +2889,8 @@ private:
bool has_max_hier () const;
int max_hier_level () const;
+ void zoom_by (double f);
+
void update_event_handlers ();
void viewport_changed ();
void cellview_changed (unsigned int index);
diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc
index b540e2ad2..16d0ca19b 100644
--- a/src/laybasic/laybasic/layMarker.cc
+++ b/src/laybasic/laybasic/layMarker.cc
@@ -30,11 +30,19 @@
#include "layViewOp.h"
#include "layRenderer.h"
#include "layLayoutViewBase.h"
+#include "layTextInfo.h"
#include "tlAssert.h"
namespace lay
{
+static db::DVector text_box_enlargement (const db::DCplxTrans &vp_trans)
+{
+ // 4.0 is the text box border in pixels
+ double b = 4.0 / vp_trans.mag ();
+ return db::DVector (b, b);
+}
+
// ------------------------------------------------------------------------
void render_cell_inst (const db::Layout &layout, const db::CellInstArray &inst, const db::CplxTrans &trans, lay::Renderer &r,
@@ -625,11 +633,28 @@ ShapeMarker::render (const Viewport &vp, ViewObjectCanvas &canvas)
if (trans_vector ()) {
for (std::vector::const_iterator tr = trans_vector ()->begin (); tr != trans_vector ()->end (); ++tr) {
db::CplxTrans t = vp.trans () * *tr * trans ();
+ if (m_shape.is_text () && text) {
+ // draw a frame around the text
+ lay::TextInfo ti (view ());
+ db::DCplxTrans vp_trans = vp.trans () * *tr;
+ db::Text t;
+ m_shape.text (t);
+ db::DBox box = ti.bbox (trans () * t, vp_trans).enlarged (text_box_enlargement (vp_trans));
+ r.draw (box, vp_trans, 0, text, 0, 0);
+ }
r.draw (m_shape, t, fill, contour, vertex, text);
r.draw_propstring (m_shape, &ly->properties_repository (), text, t);
}
} else {
db::CplxTrans t = vp.trans () * trans ();
+ if (m_shape.is_text () && text) {
+ // draw a frame around the text
+ lay::TextInfo ti (view ());
+ db::Text t;
+ m_shape.text (t);
+ db::DBox box = ti.bbox (trans () * t, vp.trans ()).enlarged (text_box_enlargement (vp.trans ()));
+ r.draw (box, vp.trans (), 0, text, 0, 0);
+ }
r.draw (m_shape, t, fill, contour, vertex, text);
r.draw_propstring (m_shape, &ly->properties_repository (), text, t);
}
@@ -1081,8 +1106,16 @@ Marker::draw (lay::Renderer &r, const db::CplxTrans &t, lay::CanvasPlane *fill,
} else if (m_type == DPath) {
r.draw (*m_object.dpath, db::DCplxTrans (t), fill, contour, vertex, text);
} else if (m_type == Text) {
+ // TODO: in order to draw the box we'd need a separation of dbu-to-micron and micron-to-pixel transformations ...
r.draw (*m_object.text, t, fill, contour, vertex, text);
} else if (m_type == DText) {
+ if (view () && text) {
+ // draw a frame around the text
+ lay::TextInfo ti (view ());
+ db::DCplxTrans dt (t);
+ db::DBox box = ti.bbox (*m_object.dtext, dt).enlarged (text_box_enlargement (dt));
+ r.draw (box, dt, 0, text, 0, 0);
+ }
r.draw (*m_object.dtext, db::DCplxTrans (t), fill, contour, vertex, text);
} else if (m_type == Edge) {
r.draw (*m_object.edge, t, fill, contour, vertex, text);
@@ -1279,6 +1312,12 @@ DMarker::render (const Viewport &vp, ViewObjectCanvas &canvas)
} else if (m_type == Path) {
r.draw (*m_object.path, t, fill, contour, vertex, text);
} else if (m_type == Text) {
+ if (view () && text) {
+ // draw a frame around the text
+ lay::TextInfo ti (view ());
+ db::DBox box = ti.bbox (*m_object.text, t).enlarged (text_box_enlargement (t));
+ r.draw (box, t, 0, text, 0, 0);
+ }
r.draw (*m_object.text, t, fill, contour, vertex, text);
} else if (m_type == Edge) {
r.draw (*m_object.edge, t, fill, contour, vertex, text);
diff --git a/src/laybasic/laybasic/layMarker.h b/src/laybasic/laybasic/layMarker.h
index c687e4eff..d807dbf24 100644
--- a/src/laybasic/laybasic/layMarker.h
+++ b/src/laybasic/laybasic/layMarker.h
@@ -226,6 +226,12 @@ public:
protected:
void get_bitmaps (const Viewport &vp, ViewObjectCanvas &canvas, lay::CanvasPlane *&fill, lay::CanvasPlane *&frame, lay::CanvasPlane *&vertex, lay::CanvasPlane *&text);
+ lay::LayoutViewBase *view ()
+ {
+ return mp_view;
+ }
+
+private:
tl::Color m_color;
tl::Color m_frame_color;
char m_line_width, m_vertex_size, m_halo;
diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc
new file mode 100644
index 000000000..8b700c127
--- /dev/null
+++ b/src/laybasic/laybasic/layTextInfo.cc
@@ -0,0 +1,167 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2023 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 "layTextInfo.h"
+#include "layFixedFont.h"
+#include "layLayoutViewBase.h"
+#include "layLayoutCanvas.h"
+
+namespace lay
+{
+
+TextInfo::TextInfo (const LayoutViewBase *view)
+ : m_default_text_size (view->default_text_size ()),
+ m_default_font (db::Font (view->text_font ())),
+ m_apply_text_trans (view->apply_text_trans ()),
+ m_resolution (view->canvas ()->resolution ())
+{
+ // .. nothing yet ..
+}
+
+TextInfo::TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution)
+ : m_default_text_size (default_text_size),
+ m_default_font (default_font),
+ m_apply_text_trans (apply_text_trans),
+ m_resolution (resolution)
+{
+ // .. nothing yet ..
+}
+
+db::DBox
+TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const
+{
+ // offset in pixels (space between origin and text)
+ const double offset = 2.0 / vp_trans.mag ();
+
+ db::DTrans tt = text.trans ();
+ db::DCoord h;
+ db::Font font = text.font () == db::NoFont ? m_default_font : text.font ();
+
+ if (m_apply_text_trans && font != db::NoFont && font != db::DefaultFont) {
+ h = text.size () > 0 ? text.size () : m_default_text_size;
+ } else {
+ tt = db::DTrans (vp_trans.fp_trans ().inverted ().angle (), tt.disp ());
+ h = m_default_text_size;
+ }
+
+ db::HAlign halign = text.halign ();
+ db::VAlign valign = text.valign ();
+
+ double fy = 0.0;
+ if (valign == db::VAlignBottom || valign == db::NoVAlign) {
+ fy = 1.0;
+ } else if (valign == db::VAlignTop) {
+ fy = -1.0;
+ }
+
+ double fx = 0.0;
+ if (halign == db::HAlignLeft || halign == db::NoHAlign) {
+ fx = 1.0;
+ } else if (halign == db::HAlignRight) {
+ fx = -1.0;
+ }
+
+ db::DPoint dp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h);
+ db::DPoint dp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h);
+
+ if (font == db::DefaultFont) {
+
+ db::DBox b (dp1 * vp_trans.mag (), dp2 * vp_trans.mag ());
+
+ const lay::FixedFont &ff = lay::FixedFont::get_font (m_resolution);
+
+ // count the lines
+
+ unsigned int lines = 1;
+ for (const char *cp = text.string (); *cp; ) {
+ if (tl::skip_newline (cp)) {
+ ++lines;
+ } else {
+ tl::utf32_from_utf8 (cp);
+ }
+ }
+
+ // compute the actual top left position
+ double ytop;
+ double htot = double (ff.line_height () * (lines - 1) + ff.height ());
+ if (valign == db::VAlignBottom || valign == db::NoVAlign) {
+ ytop = b.bottom ();
+ ytop += htot;
+ } else if (valign == db::VAlignCenter) {
+ ytop = b.center ().y ();
+ ytop += htot * 0.5;
+ } else {
+ ytop = b.top ();
+ }
+
+ // compute the bottom position
+ double ybottom = ytop - htot;
+
+ // left and right position
+ bool first = true;
+ double xleft = 0.0, xright = 0.0;
+
+ const char *cp = text.string ();
+ while (*cp) {
+
+ unsigned int length = 0;
+ while (*cp && !tl::skip_newline (cp)) {
+ tl::utf32_from_utf8 (cp);
+ ++length;
+ }
+
+ double xl;
+ if (halign == db::HAlignRight) {
+ xl = b.right ();
+ xl -= double (ff.width () * length);
+ } else if (halign == db::HAlignCenter) {
+ xl = b.center ().x ();
+ xl -= double (ff.width () * length / 2);
+ } else {
+ xl = b.left ();
+ }
+
+ double xr = xl + double (ff.width () * length);
+
+ if (first || xl < xleft) {
+ xleft = xl;
+ }
+ if (first || xr > xright) {
+ xright = xr;
+ }
+ first = false;
+
+ }
+
+ return (db::DBox (xleft, ybottom, xright, ytop) * (1.0 / vp_trans.mag ())).transformed (tt);
+
+ } else {
+
+ db::DHershey ht (text.string (), font);
+ ht.justify (db::DBox (dp1, dp2), halign, valign);
+ return ht.bbox ().transformed (tt);
+
+ }
+}
+
+}
diff --git a/src/laybasic/laybasic/layTextInfo.h b/src/laybasic/laybasic/layTextInfo.h
new file mode 100644
index 000000000..c99a47563
--- /dev/null
+++ b/src/laybasic/laybasic/layTextInfo.h
@@ -0,0 +1,85 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2023 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
+
+*/
+
+
+#ifndef HDR_layTextInfo
+#define HDR_layTextInfo
+
+#include "laybasicCommon.h"
+
+#include "dbText.h"
+#include "dbBox.h"
+
+namespace lay
+{
+
+class LayoutViewBase;
+
+/**
+ * @brief A class providing information about a text's visual bounding box
+ *
+ * The class can act as a BoxConverter.
+ */
+class LAYBASIC_PUBLIC TextInfo
+{
+public:
+ /**
+ * @brief Constructor
+ *
+ * @param view The LayoutView from which to take the text display parameters
+ */
+ TextInfo (const LayoutViewBase *view);
+
+ /**
+ * @brief Constructor
+ *
+ * @param default_text_size The default text size in micron
+ * @param default_font The default font
+ * @param apply_text_trans True if text transformations are to be applied
+ * @param resolution The resolution value (logical pixel size per physical unit pixel)
+ * @param vp_trans The effective micron-to-pixel transformation
+ */
+ TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution);
+
+ /**
+ * @brief Gets the visual bounding box of the given DText object
+ *
+ * The visual bounding box is returned in micrometer units.
+ * It encloses the glyphs of the text, taking into account the
+ * text view settings by the view.
+ *
+ * @param text The text object
+ * @param vp_trans The effective micron-to-pixel transformation
+ */
+ db::DBox bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const;
+
+private:
+ double m_default_text_size;
+ db::Font m_default_font;
+ bool m_apply_text_trans;
+ double m_resolution;
+};
+
+}
+
+#endif
+
diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro
index f3b530330..bcd01c6d1 100644
--- a/src/laybasic/laybasic/laybasic.pro
+++ b/src/laybasic/laybasic/laybasic.pro
@@ -52,6 +52,7 @@ SOURCES += \
layEditable.cc \
layEditorServiceBase.cc \
layFinder.cc \
+ layTextInfo.cc \
layFixedFont.cc \
layLayoutCanvas.cc \
layLineStylePalette.cc \
@@ -104,6 +105,7 @@ HEADERS += \
layEditorServiceBase.h \
layLayoutCanvas.h \
layFinder.h \
+ layTextInfo.h \
layFixedFont.h \
layLayoutViewBase.h \
layLineStylePalette.h \
diff --git a/src/laybasic/unit_tests/layTextInfoTests.cc b/src/laybasic/unit_tests/layTextInfoTests.cc
new file mode 100644
index 000000000..ef30de467
--- /dev/null
+++ b/src/laybasic/unit_tests/layTextInfoTests.cc
@@ -0,0 +1,126 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2023 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 "layTextInfo.h"
+#include "layLayoutViewBase.h"
+
+#include "tlUnitTest.h"
+
+TEST(1)
+{
+ lay::LayoutViewBase lv (0, false, 0);
+ lv.resize (200, 100);
+ lv.zoom_box (db::DBox (0, 0, 200, 100));
+
+ lv.default_text_size (21);
+ lv.text_font (db::Font::DefaultFont);
+
+ db::DText text;
+ text.string ("ABC");
+ text.trans (db::DTrans (db::DVector (10.0, 20.0)));
+
+ db::DText text2;
+ text2.string ("ABC\nCDEFGH");
+ text2.trans (db::DTrans (db::DVector (10.0, 20.0)));
+
+ db::DText text3;
+
+ // Default font
+ lay::TextInfo ti (&lv);
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;36,37)");
+ // global transformation changes the dimension as the default font is not scaled or rotated
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,21;23,28.5)");
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans (db::DFTrans (1))).to_string (), "(12,-6;27,18)");
+ // long text
+ EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;60,52)");
+
+ // valign
+ text3 = text2;
+ text3.valign (db::VAlignCenter);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,5;60,35)");
+ text3.valign (db::VAlignTop);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-12;60,18)");
+
+ // halign
+ text3 = text2;
+ text3.halign (db::HAlignCenter);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-14,22;34,52)");
+ text3.halign (db::HAlignRight);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-40,22;8,52)");
+
+ // Herschey font
+ lv.text_font (db::Font::StickFont);
+ ti = lay::TextInfo (&lv);
+
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,15;72,47)");
+ // global trans only scales pixel-based border but does not modify the outline in
+ // "apply transformation" mode
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,14;71,46)");
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans (db::DFTrans (1))).to_string (), "(12,15;72,47)");
+ // long text
+ EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,15;134,83)");
+
+ // valign
+ text3 = text2;
+ text3.valign (db::VAlignCenter);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-17.5;134,50.5)");
+ text3.valign (db::VAlignTop);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-50;134,18)");
+
+ // halign
+ text3 = text2;
+ text3.halign (db::HAlignCenter);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-51,15;71,83)");
+ text3.halign (db::HAlignRight);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-114,15;8,83)");
+
+ // smaller size as default
+ lv.default_text_size (4.2);
+ ti = lay::TextInfo (&lv);
+
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,20.6;24,27)");
+ EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,20.6;36.4,34.2)");
+
+ // text with explicit size
+ text3 = text2;
+ text3.size (21);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,15;134,83)");
+
+ // text with rotation
+ text3.trans (db::DTrans (1, db::DVector (10.0, 20.0)));
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-53,22;15,144)");
+
+ // text with rotation and default font (-> rotation ignored)
+ text3.font (db::Font::DefaultFont);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,22;60,52)");
+ text3.font (db::Font::StickFont);
+
+ // apply_text_trans = false
+ lv.apply_text_trans (false);
+ ti = lay::TextInfo (&lv);
+ EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,20.6;36.4,34.2)");
+ // with apply_text_trans false, the global transformation does change the text
+ // bounding box.
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,20.6;24,27)");
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,19.6;23,26)");
+ EXPECT_EQ (ti.bbox (text, db::DCplxTrans (db::DFTrans (1))).to_string (), "(10.6,6;17,18)");
+}
diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro
index 3e585c8ca..face73fbb 100644
--- a/src/laybasic/unit_tests/unit_tests.pro
+++ b/src/laybasic/unit_tests/unit_tests.pro
@@ -14,6 +14,7 @@ SOURCES = \
layParsedLayerSource.cc \
layRenderer.cc \
layAbstractMenuTests.cc \
+ layTextInfoTests.cc \
laySnapTests.cc
INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic
diff --git a/src/layui/layui/UserPropertiesForm.ui b/src/layui/layui/UserPropertiesForm.ui
index 76ba34ca4..8faf01105 100644
--- a/src/layui/layui/UserPropertiesForm.ui
+++ b/src/layui/layui/UserPropertiesForm.ui
@@ -49,7 +49,7 @@
-
- 1
+ 0
@@ -186,6 +186,48 @@
+
+
+ Meta Info
+
+
+ -
+
+
+ Meta Info is additional system data shown here for information. Entries marked with a "*" are persisted in the layout file.
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+ true
+
+
+
+ Key
+
+
+
+
+ Description
+
+
+
+
+ Value
+
+
+
+
+
+
diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc
index da242e201..dae96cc00 100644
--- a/src/layui/layui/layDialogs.cc
+++ b/src/layui/layui/layDialogs.cc
@@ -1186,8 +1186,34 @@ UserPropertiesForm::set_properties (const db::PropertiesRepository::properties_s
mp_ui->text_edit->setPlainText (tl::to_qstring (text));
}
+void
+UserPropertiesForm::set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout)
+{
+ m_begin_meta = begin_meta;
+ m_end_meta = end_meta;
+
+#if QT_VERSION >= 0x50F00
+ mp_ui->mode_tab->setTabVisible (2, m_begin_meta != m_end_meta);
+#endif
+
+ mp_ui->meta_info_list->clear ();
+
+ for (auto m = m_begin_meta; m != m_end_meta; ++m) {
+ QTreeWidgetItem *entry = new QTreeWidgetItem (mp_ui->meta_info_list);
+ entry->setText (0, tl::to_qstring ((m->second.persisted ? "*" : "") + layout.meta_info_name (m->first)));
+ entry->setText (1, tl::to_qstring (m->second.description));
+ entry->setText (2, tl::to_qstring (m->second.value.to_parsable_string ()));
+ }
+}
+
bool
UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id)
+{
+ return show (view, cv_index, prop_id, db::Layout::meta_info_iterator (), db::Layout::meta_info_iterator ());
+}
+
+bool
+UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta)
{
bool ret = false;
@@ -1209,6 +1235,8 @@ BEGIN_PROTECTED
const db::PropertiesRepository::properties_set &props = mp_prep->properties (prop_id);
set_properties (props);
+ set_meta_info (begin_meta, end_meta, cv->layout ());
+
if (exec ()) {
if (m_editable) {
diff --git a/src/layui/layui/layDialogs.h b/src/layui/layui/layDialogs.h
index 133e612dc..6cdd11b27 100644
--- a/src/layui/layui/layDialogs.h
+++ b/src/layui/layui/layDialogs.h
@@ -25,10 +25,7 @@
#ifndef HDR_layDialogs
#define HDR_layDialogs
-#include "dbPoint.h"
-#include "dbVector.h"
-#include "dbTypes.h"
-#include "dbPropertiesRepository.h"
+#include "dbLayout.h"
#include "layuiCommon.h"
#include
@@ -36,12 +33,6 @@
class QTreeWidgetItem;
-namespace db
-{
- class Layout;
- struct LayerProperties;
-}
-
namespace lay
{
class GenericSyntaxHighlighterAttributes;
@@ -431,6 +422,7 @@ public:
virtual ~UserPropertiesForm ();
bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id);
+ bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta);
public slots:
void add ();
@@ -442,11 +434,13 @@ public slots:
private:
db::PropertiesRepository::properties_set get_properties (int tab);
void set_properties (const db::PropertiesRepository::properties_set &props);
+ void set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout);
void accept ();
bool m_editable;
db::PropertiesRepository *mp_prep;
Ui::UserPropertiesForm *mp_ui;
+ db::Layout::meta_info_iterator m_begin_meta, m_end_meta;
std::unique_ptr mp_hl_attributes, mp_hl_basic_attributes;
};
diff --git a/src/layui/layui/layLayoutPropertiesForm.cc b/src/layui/layui/layLayoutPropertiesForm.cc
index 1083d6b1a..55b5322d2 100644
--- a/src/layui/layui/layLayoutPropertiesForm.cc
+++ b/src/layui/layui/layLayoutPropertiesForm.cc
@@ -154,7 +154,7 @@ LayoutPropertiesForm::prop_pb_clicked ()
db::properties_id_type prop_id = layout.prop_id ();
lay::UserPropertiesForm props_form (this);
- if (props_form.show (mp_view, m_index, prop_id)) {
+ if (props_form.show (mp_view, m_index, prop_id, layout.begin_meta (), layout.end_meta ())) {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Edit layout's user properties")));
layout.prop_id (prop_id);
diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc
index b5d8af8cb..77c883bd2 100644
--- a/src/layui/layui/layLayoutStatisticsForm.cc
+++ b/src/layui/layui/layLayoutStatisticsForm.cc
@@ -28,6 +28,8 @@
#include "tlString.h"
#include "tlExpression.h"
#include "tlTimer.h"
+#include "tlUri.h"
+#include "tlFileUtils.h"
#include "dbLayoutQuery.h"
#include "dbCellGraphUtils.h"
@@ -135,10 +137,10 @@ static std::string format_tech_name (const std::string &s)
class StatisticsTemplateProcessor
{
public:
- StatisticsTemplateProcessor (const QUrl &url, const db::Layout *layout)
+ StatisticsTemplateProcessor (const tl::URI &url, const db::Layout *layout)
: mp_layout (layout)
{
- QResource res (QString::fromUtf8 (":/st/") + url.path ());
+ QResource res (QString::fromUtf8 (":/st/") + QString::fromUtf8 (url.path ().c_str ()));
#if QT_VERSION >= 0x60000
if (res.compressionAlgorithm () == QResource::ZlibCompression) {
#else
@@ -149,13 +151,9 @@ public:
m_temp = QByteArray ((const char *)res.data (), (int)res.size ());
}
-#if QT_VERSION >= 0x050000
- QList > queryItems = QUrlQuery (url.query ()).queryItems ();
-#else
- QList > queryItems = url.queryItems ();
-#endif
- for (QList >::const_iterator q = queryItems.begin (); q != queryItems.end (); ++q) {
- m_top_eval.set_var (tl::to_string (q->first), tl::to_string (q->second));
+ auto query_items = url.query ();
+ for (auto q = query_items.begin (); q != query_items.end (); ++q) {
+ m_top_eval.set_var (q->first, q->second);
}
}
@@ -496,11 +494,27 @@ public:
return count (db::Shape::Edge);
}
+ size_t edge_pair_total () const
+ {
+ return count (db::Shape::EdgePair);
+ }
+
size_t user_total () const
{
return count (db::Shape::UserObject);
}
+ size_t all_total () const
+ {
+ return box_total () +
+ polygon_total () +
+ path_total () +
+ text_total () +
+ edge_total () +
+ edge_pair_total () +
+ user_total ();
+ }
+
private:
std::map m_count;
};
@@ -517,278 +531,357 @@ public:
// .. nothing yet ..
}
- std::string get (const std::string &url);
+ std::string get (const std::string &url)
+ {
+ auto p = m_page_cache.find (url);
+ if (p != m_page_cache.end ()) {
+ return p->second;
+ } else {
+ std::string t = get_impl (url);
+ m_page_cache [url] = t;
+ return t;
+ }
+ }
+
+ void clear_cache ()
+ {
+ m_page_cache.clear ();
+ }
private:
const lay::LayoutHandleRef m_h;
+ std::map m_page_cache;
+
+ std::string index_page (const tl::URI &uri) const;
+ std::string per_layer_stat_page (const tl::URI &uri) const;
+ std::string get_impl (const std::string &url);
};
+static std::string s_per_layer_stat_path_ld = "per-layer-stat-ld";
+static std::string s_per_layer_stat_path_name = "per-layer-stat-name";
+
std::string
-StatisticsSource::get (const std::string &url)
+StatisticsSource::per_layer_stat_page (const tl::URI &uri) const
{
- static QString s_per_layer_stat_path_ld = QString::fromUtf8 ("per-layer-stat-ld");
- static QString s_per_layer_stat_path_name = QString::fromUtf8 ("per-layer-stat-name");
+ // This is the default top level page
+ // TODO: handle other input as well
- QUrl qurl (tl::to_qstring (url));
- QFileInfo fi (qurl.path ());
+ const db::Layout &layout = m_h->layout ();
- if (fi.suffix () == QString::fromUtf8 ("stxml")) {
+ std::ostringstream os;
+ os.imbue (std::locale ("C"));
- StatisticsTemplateProcessor tp (qurl, &m_h->layout ());
- tp.process ();
- std::string r = tp.get ().constData ();
- return r;
+ std::vector layers;
+ for (unsigned int i = 0; i < layout.layers (); ++i) {
+ if (layout.is_valid_layer (i)) {
+ layers.push_back (i);
+ }
+ }
- } else if (fi.baseName () == s_per_layer_stat_path_ld || fi.baseName () == s_per_layer_stat_path_name) {
+ if (tl::basename (uri.path ()) == s_per_layer_stat_path_ld) {
+ std::sort (layers.begin (), layers.end (), CompareLDName (layout));
+ } else {
+ std::sort (layers.begin (), layers.end (), CompareNameLD (layout));
+ }
- // This is the default top level page
- // TODO: handle other input as well
+ os << "" << std::endl
+ << "" << std::endl
+ << "" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'
" << std::endl
- const db::Layout &layout = m_h->layout ();
+ << "" << std::endl
+ << "
" << std::endl
- std::ostringstream os;
- os.imbue (std::locale ("C"));
+ << "" << std::endl
+ << "| " << tl::to_string (QObject::tr ("Layer")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("All")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Boxes")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Polygons")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Paths")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Texts")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Edges")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Edge Pairs")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("User objects")) << " | " << std::endl
+ << "
" << std::endl
+
+ << "" << std::endl
+ << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
+ << "
" << std::endl
+ ;
+
+ db::CellCounter cc (&layout);
+
+ tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000);
+ for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) {
+
+ ShapeStatistics st_hier;
+ ShapeStatistics st_flat;
+
+ for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
+
+ ShapeStatistics st;
+ st.compute (layout.cell (*c).shapes (*l));
+
+ st_hier += st;
+ st *= cc.weight (*c);
+ st_flat += st;
+
+ ++progress;
- std::vector layers;
- for (unsigned int i = 0; i < layout.layers (); ++i) {
- if (layout.is_valid_layer (i)) {
- layers.push_back (i);
- }
}
- if (fi.baseName () == s_per_layer_stat_path_ld) {
- std::sort (layers.begin (), layers.end (), CompareLDName (layout));
- } else {
- std::sort (layers.begin (), layers.end (), CompareNameLD (layout));
- }
-
- os << "" << std::endl
- << "" << std::endl
- << "" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'
" << std::endl
-
- << "" << std::endl
- << "
" << std::endl
-
- << "" << std::endl
- << "| " << tl::to_string (QObject::tr ("Layer")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("Boxes")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("Polygons")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("Paths")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("Texts")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("Edges")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("User objects")) << " | " << std::endl
- << "
" << std::endl
-
- << "" << std::endl
- << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
- << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
+ os << "
" << std::endl
+ << "| " << tl::escaped_to_html (layout.get_properties (*l).to_string (), true) << " | " << std::endl
+ << "" << st_hier.all_total () << " " << st_flat.all_total () << " | " << std::endl
+ // Boxes (total, single, array)
+ << "" << st_hier.box_total () << " " << st_flat.box_total () << " | " << std::endl
+ << "" << st_hier.box_single () << " " << st_flat.box_single () << " | " << std::endl
+ << "" << st_hier.box_array () << " " << st_flat.box_array () << " | " << std::endl
+ // Polygons (total, single, array)
+ << "" << st_hier.polygon_total () << " " << st_flat.polygon_total () << " | " << std::endl
+ << "" << st_hier.polygon_single () << " " << st_flat.polygon_single () << " | " << std::endl
+ << "" << st_hier.polygon_array () << " " << st_flat.polygon_array () << " | " << std::endl
+ // Paths (total, single, array)
+ << "" << st_hier.path_total () << " " << st_flat.path_total () << " | " << std::endl
+ << "" << st_hier.path_single () << " " << st_flat.path_single () << " | " << std::endl
+ << "" << st_hier.path_array () << " " << st_flat.path_array () << " | " << std::endl
+ // Texts (total, single, array)
+ << "" << st_hier.text_total () << " " << st_flat.text_total () << " | " << std::endl
+ << "" << st_hier.text_single () << " " << st_flat.text_single () << " | " << std::endl
+ << "" << st_hier.text_array () << " " << st_flat.text_array () << " | " << std::endl
+ // Edges (total)
+ << "" << st_hier.edge_total () << " " << st_flat.edge_total () << " | " << std::endl
+ // EdgePairs (total)
+ << "" << st_hier.edge_pair_total () << " " << st_flat.edge_pair_total () << " | " << std::endl
+ // User objects (total)
+ << "" << st_hier.user_total () << " " << st_flat.user_total () << " | " << std::endl
+ // ...
+ << "" << tl::to_string (QObject::tr ("(hier)")) << " " << tl::to_string (QObject::tr ("(flat)")) << " | " << std::endl
<< "
" << std::endl
;
- db::CellCounter cc (&layout);
+ }
- tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000);
- for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) {
+ os << "
" << std::endl
+ << "" << std::endl
+ << tl::to_string (QObject::tr ("Note
"
+ ""
+ "\"(hier)\" is the object count where each cell counts once. "
+ "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells."
+ "
"
+ ""
+ "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. "
+ "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"."
+ "
"
+ ))
+ << "" << std::endl
+ << "";
+
+ return os.str ();
+}
+
+std::string
+StatisticsSource::index_page (const tl::URI & /*uri*/) const
+{
+ // maybe later ...
+ bool with_shape_statistics = false;
+
+ // This is the default top level page
+ // TODO: handle other input as well
+
+ const db::Layout &layout = m_h->layout ();
+
+ std::ostringstream os;
+ os.imbue (std::locale ("C"));
+
+ size_t num_cells = layout.cells ();
+
+ size_t num_layers = 0;
+ for (unsigned int i = 0; i < layout.layers (); ++i) {
+ if (layout.is_valid_layer (i)) {
+ ++num_layers;
+ }
+ }
+
+ db::CellCounter cc (&layout);
+
+ os << "" << std::endl
+ << "" << std::endl
+ << "" << tl::to_string (QObject::tr ("Common Statistics For '")) << tl::escaped_to_html (m_h->name (), true) << "'
" << std::endl
+ << "" << std::endl
+ << "
" << std::endl
+ << ""
+ << "| " << tl::to_string (QObject::tr ("Path")) << ": | " << tl::escaped_to_html (m_h->filename (), true) << " | "
+ << "
" << std::endl;
+ if (! m_h->save_options ().format ().empty ()) {
+ os << ""
+ << "| " << tl::to_string (QObject::tr ("Format")) << ": | " << tl::escaped_to_html (m_h->save_options ().format (), true) << " | "
+ << "
" << std::endl;
+ }
+ os << ""
+ << "| " << tl::to_string (QObject::tr ("Technology")) << ": | " << tl::escaped_to_html (m_h->technology ()->description (), true) << tl::escaped_to_html (format_tech_name (m_h->tech_name ()), true) << " | "
+ << "
" << std::endl
+ << ""
+ << "| " << tl::to_string (QObject::tr ("Database unit")) << ": | " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << " | "
+ << "
" << std::endl
+ << ""
+ << "| " << tl::to_string (QObject::tr ("Number of cells")) << ": | " << num_cells << " | "
+ << "
" << std::endl
+ << ""
+ << "| " << tl::to_string (QObject::tr ("Number of layers")) << ": | " << num_layers << " | "
+ << "
" << std::endl;
+ for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) {
+ std::string d = meta->second.description;
+ if (!d.empty ()) {
+ d = layout.meta_info_name (meta->first);
+ }
+ os << "| " << tl::escaped_to_html (d, true) << " | " << tl::escaped_to_html (meta->second.value.to_string (), true) << " |
" << std::endl;
+ }
+ os << "
" << std::endl
+ << "" << tl::to_string (QObject::tr ("Top Cells")) << "
" << std::endl
+ << "" << std::endl;
+ for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) {
+ os << "| " << tl::escaped_to_html (layout.cell_name (*tc), true) << " |
" << std::endl;
+ }
+ os << "
" << std::endl;
+ os << "" << std::endl;
+
+ std::vector layers_with_oasis_names;
+
+ std::vector layers_sorted_by_ld;
+ layers_sorted_by_ld.reserve (num_layers);
+ for (unsigned int i = 0; i < layout.layers (); ++i) {
+ if (layout.is_valid_layer (i)) {
+ layers_sorted_by_ld.push_back (i);
+ const db::LayerProperties &lp = layout.get_properties (i);
+ if (! lp.name.empty ()) {
+ layers_with_oasis_names.push_back (i);
+ }
+ }
+ }
+
+ std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout));
+ std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout));
+
+ if (! layers_sorted_by_ld.empty ()) {
+
+ os << "" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "
" << std::endl
+ << "Detailed layer statistics
" << std::endl
+ << "" << std::endl
+ << "
" << std::endl
+ << "| " << tl::to_string (QObject::tr ("Layer/Datatype")) << " | ";
+ if (! layers_with_oasis_names.empty ()) {
+ os << "" << tl::to_string (QObject::tr ("Layer name")) << " | ";
+ }
+ if (with_shape_statistics) {
+ os << "" << tl::to_string (QObject::tr ("Shape count (hier)")) << " | ";
+ os << "" << tl::to_string (QObject::tr ("Shape count (flat)")) << " | ";
+ }
+ os << "
" << std::endl;
+
+ tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers_sorted_by_ld.size () * layout.cells (), 100000);
+ for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) {
+
+ if (! layout.is_valid_layer (*i)) {
+ continue;
+ }
ShapeStatistics st_hier;
ShapeStatistics st_flat;
- for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
+ if (with_shape_statistics) {
+ for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
- ShapeStatistics st;
- st.compute (layout.cell (*c).shapes (*l));
+ ShapeStatistics st;
+ st.compute (layout.cell (*c).shapes (*i));
- st_hier += st;
- st *= cc.weight (*c);
- st_flat += st;
+ st_hier += st;
+ st *= cc.weight (*c);
+ st_flat += st;
- ++progress;
+ ++progress;
- }
-
- os << "" << std::endl
- << "| " << layout.get_properties (*l).to_string () << " | " << std::endl
- // Boxes (total, single, array)
- << "" << st_hier.box_total () << " " << st_flat.box_total () << " | " << std::endl
- << "" << st_hier.box_single () << " " << st_flat.box_single () << " | " << std::endl
- << "" << st_hier.box_array () << " " << st_flat.box_array () << " | " << std::endl
- // Polygons (total, single, array)
- << "" << st_hier.polygon_total () << " " << st_flat.polygon_total () << " | " << std::endl
- << "" << st_hier.polygon_single () << " " << st_flat.polygon_single () << " | " << std::endl
- << "" << st_hier.polygon_array () << " " << st_flat.polygon_array () << " | " << std::endl
- // Paths (total, single, array)
- << "" << st_hier.path_total () << " " << st_flat.path_total () << " | " << std::endl
- << "" << st_hier.path_single () << " " << st_flat.path_single () << " | " << std::endl
- << "" << st_hier.path_array () << " " << st_flat.path_array () << " | " << std::endl
- // Texts (total, single, array)
- << "" << st_hier.text_total () << " " << st_flat.text_total () << " | " << std::endl
- << "" << st_hier.text_single () << " " << st_flat.text_single () << " | " << std::endl
- << "" << st_hier.text_array () << " " << st_flat.text_array () << " | " << std::endl
- // Edges (total)
- << "" << st_hier.edge_total () << " " << st_flat.edge_total () << " | " << std::endl
- // User objects (total)
- << "" << st_hier.user_total () << " " << st_flat.user_total () << " | " << std::endl
- // ...
- << "" << tl::to_string (QObject::tr ("(hier)")) << " " << tl::to_string (QObject::tr ("(flat)")) << " | " << std::endl
- << "
" << std::endl
- ;
-
- }
-
- os << "
" << std::endl
- << "" << std::endl
- << tl::to_string (QObject::tr ("Note
"
- ""
- "\"(hier)\" is the object count where each cell counts once. "
- "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells."
- "
"
- ""
- "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. "
- "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"."
- "
"
- ))
- << "" << std::endl
- << "";
-
- return os.str ();
-
- } else {
-
- // This is the default top level page
- // TODO: handle other input as well
-
- const db::Layout &layout = m_h->layout ();
-
- std::ostringstream os;
- os.imbue (std::locale ("C"));
-
- size_t num_cells = layout.cells ();
-
- size_t num_layers = 0;
- for (unsigned int i = 0; i < layout.layers (); ++i) {
- if (layout.is_valid_layer (i)) {
- ++num_layers;
- }
- }
-
- os << "" << std::endl
- << "" << std::endl
- << "" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'
" << std::endl
- << "" << std::endl
- << "
" << std::endl
- << ""
- << "| " << tl::to_string (QObject::tr ("Path")) << ": | " << m_h->filename () << " | "
- << "
" << std::endl;
- if (! m_h->save_options ().format ().empty ()) {
- os << ""
- << "| " << tl::to_string (QObject::tr ("Format")) << ": | " << m_h->save_options ().format () << " | "
- << "
" << std::endl;
- }
- os << ""
- << "| " << tl::to_string (QObject::tr ("Technology")) << ": | " << m_h->technology ()->description () << format_tech_name (m_h->tech_name ()) << " | "
- << "
" << std::endl
- << ""
- << "| " << tl::to_string (QObject::tr ("Database unit")) << ": | " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << " | "
- << "
" << std::endl
- << ""
- << "| " << tl::to_string (QObject::tr ("Number of cells")) << ": | " << num_cells << " | "
- << "
" << std::endl
- << ""
- << "| " << tl::to_string (QObject::tr ("Number of layers")) << ": | " << num_layers << " | "
- << "
" << std::endl;
- for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) {
- os << "| " << meta->description << " | " << meta->value << " |
" << std::endl;
- }
- os << "
" << std::endl
- << "" << tl::to_string (QObject::tr ("Top Cells")) << "
" << std::endl
- << "" << std::endl;
- for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) {
- os << "| " << layout.cell_name (*tc) << " |
" << std::endl;
- }
- os << "
" << std::endl;
- os << "" << std::endl;
-
- std::vector layers_with_oasis_names;
-
- std::vector layers_sorted_by_ld;
- layers_sorted_by_ld.reserve (num_layers);
- for (unsigned int i = 0; i < layout.layers (); ++i) {
- if (layout.is_valid_layer (i)) {
- layers_sorted_by_ld.push_back (i);
- const db::LayerProperties &lp = layout.get_properties (i);
- if (! lp.name.empty ()) {
- layers_with_oasis_names.push_back (i);
}
}
- }
- std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout));
- std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout));
-
- if (! layers_sorted_by_ld.empty ()) {
-
- os << "" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "
" << std::endl
- << "Detailed layer statistics
" << std::endl
- << "" << std::endl
- << "
" << std::endl
- << "| " << tl::to_string (QObject::tr ("Layer/Datatype")) << " | ";
+ const db::LayerProperties &lp = layout.get_properties (*i);
+ os << "
"
+ << "| " << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << " | ";
if (! layers_with_oasis_names.empty ()) {
- os << "" << tl::to_string (QObject::tr ("Layer name")) << " | ";
+ os << "" << tl::escaped_to_html (lp.name, true) << " | ";
+ }
+ if (with_shape_statistics) {
+ os << "" << st_hier.all_total () << " | ";
+ os << "" << st_flat.all_total () << " | ";
}
os << "
" << std::endl;
- for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) {
- if (layout.is_valid_layer (*i)) {
- const db::LayerProperties &lp = layout.get_properties (*i);
+ }
+
+ os << "
" << std::endl;
+ os << "" << std::endl;
+
+ }
+
+ if (! layers_with_oasis_names.empty ()) {
+
+ os << "" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "
" << std::endl
+ << "Detailed layer statistics
" << std::endl
+ << "" << std::endl
+ << "
" << std::endl
+ << "| " << tl::to_string (QObject::tr ("Layer name")) << " | " << tl::to_string (QObject::tr ("Layer/Datatype")) << " |
" << std::endl;
+
+ for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) {
+ if (layout.is_valid_layer (*i)) {
+ const db::LayerProperties &lp = layout.get_properties (*i);
+ if (! lp.name.empty ()) {
os << ""
- << "| " << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << " | ";
- if (! layers_with_oasis_names.empty ()) {
- os << "" << lp.name << " | ";
- }
- os << "
" << std::endl;
+ << "" << tl::escaped_to_html (lp.name, true) << " | "
+ << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << " | "
+ << "" << std::endl;
}
}
-
- os << "
" << std::endl;
- os << "" << std::endl;
-
}
- if (! layers_with_oasis_names.empty ()) {
+ os << "
" << std::endl;
+ os << "
" << std::endl;
- os << "" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "
" << std::endl
- << "Detailed layer statistics
" << std::endl
- << "" << std::endl
- << "
" << std::endl
- << "| " << tl::to_string (QObject::tr ("Layer name")) << " | " << tl::to_string (QObject::tr ("Layer/Datatype")) << " |
" << std::endl;
-
- for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) {
- if (layout.is_valid_layer (*i)) {
- const db::LayerProperties &lp = layout.get_properties (*i);
- if (! lp.name.empty ()) {
- os << ""
- << "| " << lp.name << " | "
- << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << " | "
- << "
" << std::endl;
- }
- }
- }
+ }
- os << "
" << std::endl;
- os << "" << std::endl;
+ os << "" << std::endl
+ << "" << std::endl;
+ ;
- }
+ return os.str ();
+}
- os << "" << std::endl
- << "" << std::endl;
- ;
+std::string
+StatisticsSource::get_impl (const std::string &url)
+{
+ tl::URI uri (url);
+ std::string page = tl::basename (uri.path ());
- return os.str ();
+ if (tl::extension (page) == "stxml") {
+
+ StatisticsTemplateProcessor tp (uri, &m_h->layout ());
+ tp.process ();
+ std::string r = tp.get ().constData ();
+ return r;
+
+ } else if (page == s_per_layer_stat_path_ld || page == s_per_layer_stat_path_name) {
+
+ return per_layer_stat_page (uri);
+
+ } else {
+
+ return index_page (uri);
}
}
diff --git a/src/layui/layui/layLayoutViewFunctions.cc b/src/layui/layui/layLayoutViewFunctions.cc
index b29cdc693..36bc3d6ad 100644
--- a/src/layui/layui/layLayoutViewFunctions.cc
+++ b/src/layui/layui/layLayoutViewFunctions.cc
@@ -440,7 +440,7 @@ LayoutViewFunctions::cm_cell_user_properties ()
db::properties_id_type prop_id = cell.prop_id ();
lay::UserPropertiesForm props_form (parent_widget ());
- if (props_form.show (view (), cv_index, prop_id)) {
+ if (props_form.show (view (), cv_index, prop_id, layout.begin_meta (cell.cell_index ()), layout.end_meta (cell.cell_index ()))) {
view ()->transaction (tl::to_string (tr ("Edit cell's user properties")));
cell.prop_id (prop_id);
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc
index afc4e95fe..90e84d65d 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc
@@ -185,8 +185,8 @@ GDS2ReaderBase::do_read (db::Layout &layout)
unsigned int mod_time[6] = { 0, 0, 0, 0, 0, 0 };
unsigned int access_time[6] = { 0, 0, 0, 0, 0, 0 };
get_time (mod_time, access_time);
- layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5])));
- layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5])));
+ layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5])));
+ layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5])));
long attr = 0;
db::PropertiesRepository::properties_set layout_properties;
@@ -234,9 +234,9 @@ GDS2ReaderBase::do_read (db::Layout &layout)
double dbuu = get_double ();
double dbum = get_double ();
- layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu)));
- layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum)));
- layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), m_libname));
+ layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu)));
+ layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum)));
+ layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), m_libname));
m_dbuu = dbuu;
m_dbu = dbum * 1e6; /*in micron*/
@@ -289,13 +289,19 @@ GDS2ReaderBase::do_read (db::Layout &layout)
db::cell_index_type cell_index = make_cell (layout, m_cellname);
bool ignore_cell = false;
- std::map >::const_iterator ctx = m_context_info.find (m_cellname);
+ auto ctx = m_context_info.find (m_cellname);
if (ctx != m_context_info.end ()) {
+
CommonReaderLayerMapping layer_mapping (this, &layout);
- if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) {
+ LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ());
+
+ if (ci.has_proxy_info () && layout.recover_proxy_as (cell_index, ci, &layer_mapping)) {
// ignore everything in that cell since it is created by the import:
ignore_cell = true;
}
+
+ layout.fill_meta_info_from_context (cell_index, ci);
+
}
db::Cell *cell = 0;
@@ -386,6 +392,13 @@ GDS2ReaderBase::do_read (db::Layout &layout)
}
+ // deserialize global context information
+ auto ctx = m_context_info.find (std::string ());
+ if (ctx != m_context_info.end ()) {
+ LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ());
+ layout.fill_meta_info_from_context (ci);
+ }
+
// check, if the last record is a ENDLIB
if (rec_id != sENDLIB) {
error (tl::to_string (tr ("ENDLIB record expected")));
@@ -396,12 +409,16 @@ void
GDS2ReaderBase::read_context_info_cell ()
{
short rec_id = 0;
+ std::string cn;
// read cell content
while ((rec_id = get_record ()) != sENDSTR) {
progress_checkpoint ();
+ bool valid_hook = false;
+ cn.clear ();
+
if (rec_id == sSREF) {
do {
@@ -411,7 +428,7 @@ GDS2ReaderBase::read_context_info_cell ()
error (tl::to_string (tr ("SNAME record expected")));
}
- std::string cn = get_string ();
+ cn = get_string ();
rec_id = get_record ();
while (rec_id == sSTRANS || rec_id == sANGLE || rec_id == sMAG) {
@@ -421,6 +438,24 @@ GDS2ReaderBase::read_context_info_cell ()
error (tl::to_string (tr ("XY record expected")));
}
+ valid_hook = true;
+
+ } else if (rec_id == sBOUNDARY) {
+
+ rec_id = get_record ();
+ while (rec_id == sLAYER || rec_id == sDATATYPE) {
+ rec_id = get_record ();
+ }
+ if (rec_id != sXY) {
+ error (tl::to_string (tr ("XY record expected")));
+ }
+
+ valid_hook = true;
+
+ }
+
+ if (valid_hook) {
+
std::vector &strings = m_context_info.insert (std::make_pair (cn, std::vector ())).first->second;
size_t attr = 0;
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc
index 8fdf46fde..048ded3d6 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc
@@ -72,6 +72,109 @@ inline int scale (double sf, int value)
}
}
+void
+GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells)
+{
+ write_record_size (4 + 12 * 2);
+ write_record (sBGNSTR);
+ write_time (time_data);
+ write_time (time_data);
+
+ write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$");
+
+ std::vector context_prop_strings;
+
+ if (layout.has_context_info ()) {
+
+ // Use a dummy BOUNDARY element to attach the global context
+
+ write_record_size (4);
+ write_record (sBOUNDARY);
+
+ write_record_size (6);
+ write_record (sLAYER);
+ write_short (0);
+
+ write_record_size (6);
+ write_record (sDATATYPE);
+ write_short (0);
+
+ write_record_size (4 + 5 * 2 * 4);
+ write_record (sXY);
+ for (unsigned int i = 0; i < 10; ++i) {
+ write_int (0);
+ }
+
+ context_prop_strings.clear ();
+
+ if (layout.get_context_info (context_prop_strings)) {
+
+ // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
+ // will arrive)
+ for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
+
+ --s;
+
+ write_record_size (6);
+ write_record (sPROPATTR);
+ write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string
+
+ write_string_record (sPROPVALUE, *s);
+
+ }
+
+ }
+
+ write_record_size (4);
+ write_record (sENDEL);
+
+ }
+
+ for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
+
+ if (layout.has_context_info (*cell)) {
+
+ write_record_size (4);
+ write_record (sSREF);
+
+ write_string_record (sSNAME, m_cell_name_map.cell_name (*cell));
+
+ write_record_size (12);
+ write_record (sXY);
+ write_int (0);
+ write_int (0);
+
+ context_prop_strings.clear ();
+
+ if (layout.get_context_info (*cell, context_prop_strings)) {
+
+ // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
+ // will arrive)
+ for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
+
+ --s;
+
+ write_record_size (6);
+ write_record (sPROPATTR);
+ write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string
+
+ write_string_record (sPROPVALUE, *s);
+
+ }
+
+ }
+
+ write_record_size (4);
+ write_record (sENDEL);
+
+ }
+
+ }
+
+ write_record_size (4);
+ write_record (sENDSTR);
+}
+
void
GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
@@ -86,9 +189,9 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
db::GDS2WriterOptions gds2_options = options.get_options ();
- layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units))));
- layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6)));
- layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), gds2_options.libname));
+ layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units))));
+ layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6)));
+ layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname));
std::vector > layers;
options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber);
@@ -123,8 +226,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
}
std::string str_time = tl::sprintf ("%d/%d/%d %d:%02d:%02d", time_data[1], time_data[2], time_data[0], time_data[3], time_data[4], time_data[5]);
- layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), str_time));
- layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), str_time));
+ layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time));
+ layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time));
bool multi_xy = gds2_options.multi_xy_records;
size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8);
@@ -178,73 +281,17 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
// write context info
- bool any_proxy = false;
+ bool has_context = false;
if (options.write_context_info ()) {
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !any_proxy; ++cell) {
- const db::Cell &cref = layout.cell (*cell);
- if (cref.is_proxy () && ! cref.is_top ()) {
- any_proxy = true;
- }
+ has_context = layout.has_context_info ();
+ for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !has_context; ++cell) {
+ has_context = layout.has_context_info (*cell);
}
}
- if (any_proxy) {
-
- write_record_size (4 + 12 * 2);
- write_record (sBGNSTR);
- write_time (time_data);
- write_time (time_data);
-
- write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$");
-
- std::vector context_prop_strings;
-
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
-
- const db::Cell &cref = layout.cell (*cell);
- if (cref.is_proxy () && ! cref.is_top ()) {
-
- write_record_size (4);
- write_record (sSREF);
-
- write_string_record (sSNAME, m_cell_name_map.cell_name (*cell));
-
- write_record_size (12);
- write_record (sXY);
- write_int (0);
- write_int (0);
-
- context_prop_strings.clear ();
-
- if (layout.get_context_info (*cell, context_prop_strings)) {
-
- // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
- // will arrive)
- for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
-
- --s;
-
- write_record_size (6);
- write_record (sPROPATTR);
- write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string
-
- write_string_record (sPROPVALUE, *s);
-
- }
-
- }
-
- write_record_size (4);
- write_record (sENDEL);
-
- }
-
- }
-
- write_record_size (4);
- write_record (sENDSTR);
-
+ if (has_context) {
+ write_context_cell (layout, time_data, cells);
}
// body
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h
index 52ef2b7ee..94a4a81d5 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h
@@ -168,6 +168,7 @@ private:
db::WriterCellNameMap m_cell_name_map;
void write_properties (const db::Layout &layout, db::properties_id_type prop_id);
+ void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells);
};
} // namespace db
diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc
index 443f63027..59a259d6b 100644
--- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc
+++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc
@@ -145,9 +145,10 @@ public:
// Initialize the libname property from meta data with key "libname".
db::GDS2WriterOptions *options = dynamic_cast (o);
if (options) {
+ db::Layout::meta_info_name_id_type libname_name_id = lh.layout().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator meta = lh.layout().begin_meta (); meta != lh.layout().end_meta (); ++meta) {
- if (meta->name == "libname" && !meta->value.empty ()) {
- options->libname = meta->value;
+ if (meta->first == libname_name_id && !meta->second.value.is_nil ()) {
+ options->libname = meta->second.value.to_string ();
}
}
}
diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc
index 18f3c860e..33b2e3e2b 100644
--- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc
+++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc
@@ -229,7 +229,7 @@ TEST(1)
db::LayerMap map = reader.read (layout);
EXPECT_EQ (fabs (layout.dbu () / 0.001 - 1.0) < 1e-6, true);
- EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB");
+ EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB");
EXPECT_EQ (layout.layers (), size_t (11));
EXPECT_EQ (map.mapping_str (0), "2/0 : 2/0");
@@ -289,7 +289,7 @@ TEST(2)
db::Reader reader (file);
map = reader.read (layout_none, options);
EXPECT_EQ (fabs (layout_none.dbu () / 0.001 - 1.0) < 1e-6, true);
- EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB");
+ EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB");
}
EXPECT_EQ (layout_none.layers (), size_t (0));
diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc
index d2eb315d6..20499fc60 100644
--- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc
+++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc
@@ -1193,6 +1193,104 @@ TEST(121)
run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt);
}
+// Meta info
+TEST(130a)
+{
+ db::Layout layout_org;
+
+ layout_org.add_cell ("U");
+ db::cell_index_type ci = layout_org.add_cell ("X");
+
+ layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true));
+ layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
+
+ layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true));
+ layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true));
+
+ // complex type
+ tl::Variant v2;
+ v2.set_array ();
+ v2.insert ("x", "value_for_x");
+ v2.insert ("y", db::DBox (1.5, 2.5, 3.5, 4.5));
+ tl::Variant v1;
+ v1.set_list (0);
+ v1.push (-1.5);
+ v1.push (v2);
+ layout_org.add_meta_info (ci, "complex", db::MetaInfo ("", v1, true));
+ layout_org.add_meta_info ("complex", db::MetaInfo ("", v1, true));
+
+ std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130a.gds");
+
+ {
+ tl::OutputStream out (tmp_file);
+ db::SaveLayoutOptions options;
+ db::Writer writer (options);
+ writer.write (layout_org, out);
+ }
+
+ db::Layout layout_read;
+
+ {
+ tl::InputStream in (tmp_file);
+ db::Reader reader (in);
+ reader.read (layout_read);
+ }
+
+ EXPECT_EQ (layout_read.has_meta_info ("x"), false);
+ EXPECT_EQ (layout_read.has_meta_info ("a"), true);
+ EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5");
+ EXPECT_EQ (layout_read.meta_info ("a").description, "description");
+ EXPECT_EQ (layout_read.has_meta_info ("b"), true);
+ EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
+ EXPECT_EQ (layout_read.meta_info ("b").description, "");
+ EXPECT_EQ (layout_read.has_meta_info ("complex"), true);
+ EXPECT_EQ (layout_read.meta_info ("complex").value.is_list (), true);
+ EXPECT_EQ (layout_read.meta_info ("complex").value.size (), size_t (2));
+ EXPECT_EQ (layout_read.meta_info ("complex").value.begin () [1].is_array (), true);
+ EXPECT_EQ (layout_read.meta_info ("complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)");
+
+ db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
+
+ EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true");
+ EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd");
+ EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
+ EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d");
+ EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.is_list (), true);
+ EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.size (), size_t (2));
+ EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.begin () [1].is_array (), true);
+ EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)");
+
+ tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds");
+
+ {
+ tl::OutputStream out (tmp_file);
+ db::SaveLayoutOptions options;
+ options.set_write_context_info (false);
+ db::Writer writer (options);
+ writer.write (layout_org, out);
+ }
+
+ layout_read = db::Layout ();
+
+ {
+ tl::InputStream in (tmp_file);
+ db::Reader reader (in);
+ reader.read (layout_read);
+ }
+
+ EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
+
+ ci2 = layout_read.cell_by_name ("X").second;
+
+ EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
+}
+
// Extreme fracturing by max. points
TEST(166)
{
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc
index d76dd434f..3346c6c6f 100644
--- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc
@@ -316,7 +316,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl:
error (tl::to_string (tr ("Could not find 'magic' header line - is this a MAGIC file?")));
}
- layout.add_meta_info (db::MetaInfo ("lambda", "lambda value (tech scaling)", tl::to_string (m_lambda)));
+ layout.add_meta_info ("lambda", db::MetaInfo ("lambda value (tech scaling)", tl::to_string (m_lambda)));
bool valid_layer = false;
unsigned int current_layer = 0;
@@ -339,11 +339,11 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl:
if (&m_stream == &stream) {
// initial file - store technology
- layout.add_meta_info (db::MetaInfo ("magic_technology", tl::to_string (tr ("MAGIC technology string")), m_tech));
+ layout.add_meta_info ("magic_technology", db::MetaInfo (tl::to_string (tr ("MAGIC technology string")), m_tech));
// propose this is the KLayout technology unless a good one is given
if (! mp_klayout_tech) {
- layout.add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), m_tech));
+ layout.add_meta_info ("technology", db::MetaInfo (tl::to_string (tr ("Technology name")), m_tech));
}
}
@@ -357,7 +357,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl:
if (&m_stream == &stream) {
// initial file - store timestamp
- layout.add_meta_info (db::MetaInfo ("magic_timestamp", "MAGIC main file timestamp", tl::to_string (ts)));
+ layout.add_meta_info ("magic_timestamp", db::MetaInfo ("MAGIC main file timestamp", tl::to_string (ts)));
}
ex.expect_end ();
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc
index 8b899164d..a104e5fef 100644
--- a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc
@@ -82,11 +82,14 @@ MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
double lambda = m_options.lambda;
if (lambda <= 0.0) {
- const std::string &lv = layout.meta_info_value ("lambda");
- if (lv.empty ()) {
+ const tl::Variant &lv = layout.meta_info ("lambda").value;
+ if (lv.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("No lambda value configured for MAG writer and no 'lambda' metadata present in layout.")));
+ } else if (lv.is_a_string ()) {
+ tl::from_string (lv.to_string (), lambda);
+ } else if (lv.can_convert_to_double ()) {
+ lambda = lv.to_double ();
}
- tl::from_string (lv, lambda);
}
m_sf = layout.dbu () / lambda;
diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h
index 05cc203ea..12e68c3de 100644
--- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h
+++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h
@@ -101,7 +101,8 @@ public:
* @brief The constructor
*/
OASISWriterOptions ()
- : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*")
+ : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false),
+ write_std_properties (1), subst_char ("*"), tables_at_end (false)
{
// .. nothing yet ..
}
@@ -166,6 +167,11 @@ public:
*/
std::string subst_char;
+ /**
+ * @brief Hidden option, for testing mainly: write tables at end to force forward references
+ */
+ bool tables_at_end;
+
/**
* @brief Implementation of FormatSpecificWriterOptions
*/
diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc
index 613e50a82..d87c74e6c 100644
--- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc
+++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc
@@ -36,13 +36,11 @@
namespace db
{
-// ---------------------------------------------------------------
-
// ---------------------------------------------------------------
// OASISReader
OASISReader::OASISReader (tl::InputStream &s)
- : m_stream (s),
+ : m_stream (s),
m_progress (tl::to_string (tr ("Reading OASIS file")), 10000),
m_dbu (0.001),
m_expect_strict_mode (-1),
@@ -113,7 +111,7 @@ OASISReader::init (const db::LoadLayoutOptions &options)
m_expect_strict_mode = oasis_options.expect_strict_mode;
}
-inline long long
+inline long long
OASISReader::get_long_long ()
{
unsigned long long u = get_ulong_long ();
@@ -124,13 +122,13 @@ OASISReader::get_long_long ()
}
}
-inline unsigned long long
+inline unsigned long long
OASISReader::get_ulong_long ()
{
unsigned long long v = 0;
unsigned long long vm = 1;
char c;
-
+
do {
unsigned char *b = (unsigned char *) m_stream.get (1);
if (! b) {
@@ -138,7 +136,7 @@ OASISReader::get_ulong_long ()
return 0;
}
c = *b;
- if (vm > std::numeric_limits ::max () / 128 &&
+ if (vm > std::numeric_limits ::max () / 128 &&
(unsigned long long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) {
error (tl::to_string (tr ("Unsigned long value overflow")));
}
@@ -149,7 +147,7 @@ OASISReader::get_ulong_long ()
return v;
}
-inline long
+inline long
OASISReader::get_long ()
{
unsigned long u = get_ulong ();
@@ -170,13 +168,13 @@ OASISReader::get_ulong_for_divider ()
return l;
}
-inline unsigned long
+inline unsigned long
OASISReader::get_ulong ()
{
unsigned long v = 0;
unsigned long vm = 1;
char c;
-
+
do {
unsigned char *b = (unsigned char *) m_stream.get (1);
if (! b) {
@@ -184,7 +182,7 @@ OASISReader::get_ulong ()
return 0;
}
c = *b;
- if (vm > std::numeric_limits ::max () / 128 &&
+ if (vm > std::numeric_limits ::max () / 128 &&
(unsigned long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) {
error (tl::to_string (tr ("Unsigned long value overflow")));
}
@@ -195,7 +193,7 @@ OASISReader::get_ulong ()
return v;
}
-inline int
+inline int
OASISReader::get_int ()
{
unsigned int u = get_uint ();
@@ -206,13 +204,13 @@ OASISReader::get_int ()
}
}
-inline unsigned int
+inline unsigned int
OASISReader::get_uint ()
{
unsigned int v = 0;
unsigned int vm = 1;
char c;
-
+
do {
unsigned char *b = (unsigned char *) m_stream.get (1);
if (! b) {
@@ -220,7 +218,7 @@ OASISReader::get_uint ()
return 0;
}
c = *b;
- if (vm > std::numeric_limits ::max () / 128 &&
+ if (vm > std::numeric_limits ::max () / 128 &&
(unsigned int) (c & 0x7f) > (std::numeric_limits ::max () / vm)) {
error (tl::to_string (tr ("Unsigned integer value overflow")));
}
@@ -231,7 +229,7 @@ OASISReader::get_uint ()
return v;
}
-std::string
+std::string
OASISReader::get_str ()
{
std::string s;
@@ -260,11 +258,11 @@ OASISReader::get_real ()
if (t == 0) {
- return double (get_ulong ());
+ return double (get_ulong ());
} else if (t == 1) {
- return -double (get_ulong ());
+ return -double (get_ulong ());
} else if (t == 2) {
@@ -447,7 +445,7 @@ OASISReader::get_gdelta (long grid)
ly > (long long) (std::numeric_limits ::max ())) {
error (tl::to_string (tr ("Coordinate value overflow")));
}
-
+
return db::Vector (db::Coord (lx), db::Coord (ly));
} else {
@@ -482,13 +480,13 @@ OASISReader::get_gdelta (long grid)
}
}
-void
+void
OASISReader::error (const std::string &msg)
{
throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str ());
}
-void
+void
OASISReader::warn (const std::string &msg, int wl)
{
if (warn_level () < wl) {
@@ -499,7 +497,7 @@ OASISReader::warn (const std::string &msg, int wl)
error (msg);
} else {
// TODO: compress
- tl::warn << msg
+ tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
@@ -518,7 +516,7 @@ struct LNameJoinOp1
};
/**
- * @brief A helper class to join two layer map members
+ * @brief A helper class to join two layer map members
* This implementation basically merged the datatype maps.
*/
struct LNameJoinOp2
@@ -535,16 +533,16 @@ struct LNameJoinOp2
*
* This method will update m_table_start which is the location used as
* the start position of a strict mode table. Every record except CBLOCK
- * will update this position to point after the record. Hence m_table_start
- * points to the beginning of a table when PROPNAME, CELLNAME or any
+ * will update this position to point after the record. Hence m_table_start
+ * points to the beginning of a table when PROPNAME, CELLNAME or any
* other table-contained record is encountered.
* Since CBLOCK does not update this record, the position of the table will
* be the location of CBLOCK rather than that of the name record itself.
- * PAD records will also call this method, so the beginning of a table
+ * PAD records will also call this method, so the beginning of a table
* is right after any preceding PAD records and exactly at the location
* of the first name record after PADs.
*/
-void
+void
OASISReader::mark_start_table ()
{
// we need to this this to really finish a CBLOCK - this is a flaw
@@ -556,7 +554,7 @@ OASISReader::mark_start_table ()
m_table_start = m_stream.pos ();
}
-void
+void
OASISReader::read_offset_table ()
{
unsigned int of = 0;
@@ -599,15 +597,29 @@ OASISReader::read_offset_table ()
static const char magic_bytes[] = { "%SEMI-OASIS\015\012" };
-void
+static const char *klayout_context_propname = "KLAYOUT_CONTEXT";
+static const char *s_gds_property_propname = "S_GDS_PROPERTY";
+
+static LayoutOrCellContextInfo
+make_context_info (const std::vector &context_properties)
+{
+ std::vector context_strings;
+ context_strings.reserve (context_properties.size ());
+ for (auto s = context_properties.begin (); s != context_properties.end (); ++s) {
+ context_strings.push_back (s->to_string ());
+ }
+ return LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ());
+}
+
+void
OASISReader::do_read (db::Layout &layout)
{
unsigned char r;
char *mb;
// prepare
- m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY");
- m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT");
+ m_s_gds_property_name_id = layout.properties_repository ().prop_name_id (s_gds_property_propname);
+ m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id (klayout_context_propname);
// read magic bytes
mb = (char *) m_stream.get (sizeof (magic_bytes) - 1);
@@ -676,10 +688,13 @@ OASISReader::do_read (db::Layout &layout)
m_propstrings.clear ();
m_propnames.clear ();
+ m_context_strings_per_cell.clear ();
+
m_instances.clear ();
m_instances_with_props.clear ();
db::PropertiesRepository::properties_set layout_properties;
+ std::vector context_properties;
mark_start_table ();
@@ -827,41 +842,118 @@ OASISReader::do_read (db::Layout &layout)
std::map ::iterator pf = m_propname_forward_references.find (id);
if (pf != m_propname_forward_references.end ()) {
- if (name == "S_GDS_PROPERTY") {
+ bool is_s_gds_property = false;
+ bool is_klayout_context_property = false;
+
+ if (name == s_gds_property_propname) {
+ is_s_gds_property = true;
+ } else if (name == klayout_context_propname) {
+ is_klayout_context_property = true;
+ }
+
+ // handle special case of forward references to S_GDS_PROPERTY and KLAYOUT_CONTEXT
+ if (is_s_gds_property || is_klayout_context_property) {
db::PropertiesRepository &rep = layout.properties_repository ();
- db::property_names_id_type s_gds_name_id = pf->second;
- // exchange the properties in the repository: first locate all
- // property sets that are affected
- std::vector pids;
- for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) {
- if (p->second.find (s_gds_name_id) != p->second.end ()) {
- pids.push_back (p->first);
+ // exchange properties in layout_properties
+ db::PropertiesRepository::properties_set new_set;
+
+ for (db::PropertiesRepository::properties_set::const_iterator s = layout_properties.begin (); s != layout_properties.end (); ++s) {
+ if (s->first == pf->second && is_s_gds_property) {
+
+ // S_GDS_PROPERTY translation
+ if (!s->second.is_list () || s->second.get_list ().size () != 2) {
+ error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements")));
+ }
+
+ new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1]));
+
+ } else if (s->first == pf->second && is_klayout_context_property) {
+
+ // feed context strings from klayout context property
+ if (s->second.is_list ()) {
+ for (auto l = s->second.begin (); l != s->second.end (); ++l) {
+ context_properties.push_back (*l);
+ }
+ } else {
+ context_properties.push_back (s->second);
+ }
+
+ } else {
+ new_set.insert (*s);
+ }
+ }
+
+ new_set.swap (layout_properties);
+
+ // exchange the properties in the repository
+
+ // first locate all property sets that are affected
+ std::map > cells_by_pid;
+ for (auto p = rep.begin (); p != rep.end (); ++p) {
+ if (p->second.find (pf->second) != p->second.end ()) {
+ cells_by_pid.insert (std::make_pair (p->first, std::vector ()));
+ }
+ }
+
+ // find cells using a specific pid
+ for (auto i = layout.begin (); i != layout.end (); ++i) {
+ auto cc = cells_by_pid.find (i->prop_id ());
+ if (cc != cells_by_pid.end ()) {
+ cc->second.push_back (i->cell_index ());
}
}
// create new property sets for the ones we found
- for (std::vector ::const_iterator pid = pids.begin (); pid != pids.end (); ++pid) {
+ for (auto pid = cells_by_pid.begin (); pid != cells_by_pid.end (); ++pid) {
- const db::PropertiesRepository::properties_set &old_set = rep.properties (*pid);
+ const db::PropertiesRepository::properties_set &old_set = rep.properties (pid->first);
db::PropertiesRepository::properties_set new_set;
- for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) {
- if (s->first == s_gds_name_id) {
+ for (auto s = old_set.begin (); s != old_set.end (); ++s) {
+ if (s->first == pf->second && is_s_gds_property) {
+ // S_GDS_PROPERTY translation
if (!s->second.is_list () || s->second.get_list ().size () != 2) {
error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements")));
}
new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1]));
+ } else if (s->first == pf->second && is_klayout_context_property) {
+
+ auto pid2c = cells_by_pid.find (pid->first);
+
+ if (pid->first == layout.prop_id ()) {
+ // feed context strings from klayout context property
+ if (s->second.is_list ()) {
+ for (auto l = s->second.begin (); l != s->second.end (); ++l) {
+ context_properties.push_back (*l);
+ }
+ } else {
+ context_properties.push_back (s->second);
+ }
+ }
+
+ // feed cell-specific context strings from klayout context property
+ for (auto c = pid2c->second.begin (); c != pid2c->second.end (); ++c) {
+ std::vector &vl = m_context_strings_per_cell [*c];
+ if (s->second.is_list ()) {
+ for (auto l = s->second.begin (); l != s->second.end (); ++l) {
+ vl.push_back (*l);
+ }
+ } else {
+ vl.push_back (s->second);
+ }
+ }
+
} else {
new_set.insert (*s);
}
}
- rep.change_properties (*pid, new_set);
+ rep.change_properties (pid->first, new_set);
}
@@ -940,7 +1032,7 @@ OASISReader::do_read (db::Layout &layout)
layout_properties.clear ();
}
- // read a layer name
+ // read a layer name
std::string name = get_str ();
db::ld_type dt1 = 0, dt2 = std::numeric_limits::max () - 1;
@@ -1000,7 +1092,12 @@ OASISReader::do_read (db::Layout &layout)
read_properties (layout.properties_repository ());
}
- store_last_properties (layout.properties_repository (), layout_properties, true);
+ if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) {
+ context_properties.insert (context_properties.end (), mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ());
+ } else {
+ // store cell properties
+ store_last_properties (layout.properties_repository (), layout_properties, true);
+ }
mark_start_table ();
@@ -1138,54 +1235,19 @@ OASISReader::do_read (db::Layout &layout)
// resolve all propvalue forward referenced
if (! m_propvalue_forward_references.empty ()) {
- for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) {
-
- for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) {
-
- if (ps->second.is_id ()) {
-
- unsigned long id = (unsigned long) ps->second.to_id ();
- std::map ::const_iterator fw = m_propvalue_forward_references.find (id);
- if (fw != m_propvalue_forward_references.end ()) {
- ps->second = tl::Variant (fw->second);
- } else {
- error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
- }
-
- } else if (ps->second.is_list ()) {
-
- // Replace list elements as well
- // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant
-
- const std::vector &l = ps->second.get_list ();
- bool needs_replacement = false;
- for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) {
- needs_replacement = ll->is_id ();
- }
-
- if (needs_replacement) {
-
- std::vector new_list (l);
- for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) {
- if (ll->is_id ()) {
- unsigned long id = (unsigned long) ll->to_id ();
- std::map ::const_iterator fw = m_propvalue_forward_references.find (id);
- if (fw != m_propvalue_forward_references.end ()) {
- *ll = tl::Variant (fw->second);
- } else {
- error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
- }
- }
- }
-
- ps->second = tl::Variant (new_list.begin (), new_list.end ());
-
- }
-
- }
-
+ for (auto i = context_properties.begin (); i != context_properties.end (); ++i) {
+ replace_forward_references_in_variant (*i);
+ }
+ for (auto c = m_context_strings_per_cell.begin (); c != m_context_strings_per_cell.end (); ++c) {
+ for (auto i = c->second.begin (); i != c->second.end (); ++i) {
+ replace_forward_references_in_variant (*i);
}
+ }
+ for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) {
+ for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) {
+ replace_forward_references_in_variant (ps->second);
+ }
}
m_propvalue_forward_references.clear ();
@@ -1213,6 +1275,24 @@ OASISReader::do_read (db::Layout &layout)
}
+ // Restore layout meta info
+ if (! context_properties.empty ()) {
+ LayoutOrCellContextInfo info = make_context_info (context_properties);
+ layout.fill_meta_info_from_context (info);
+ }
+
+ // Restore proxy cell (link to PCell or Library) and cell meta info
+ if (! m_context_strings_per_cell.empty ()) {
+ CommonReaderLayerMapping layer_mapping (this, &layout);
+ for (auto cc = m_context_strings_per_cell.begin (); cc != m_context_strings_per_cell.end (); ++cc) {
+ LayoutOrCellContextInfo info = make_context_info (cc->second);
+ if (info.has_proxy_info ()) {
+ layout.recover_proxy_as (cc->first, info, &layer_mapping);
+ }
+ layout.fill_meta_info_from_context (cc->first, info);
+ }
+ }
+
// Check the table offsets vs. real occurrence
if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) {
warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname));
@@ -1231,6 +1311,52 @@ OASISReader::do_read (db::Layout &layout)
}
}
+void
+OASISReader::replace_forward_references_in_variant (tl::Variant &v)
+{
+ if (v.is_id ()) {
+
+ unsigned long id = (unsigned long) v.to_id ();
+ std::map ::const_iterator fw = m_propvalue_forward_references.find (id);
+ if (fw != m_propvalue_forward_references.end ()) {
+ v = tl::Variant (fw->second);
+ } else {
+ error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
+ }
+
+ } else if (v.is_list ()) {
+
+ // Replace list elements as well
+ // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant
+
+ const std::vector &l = v.get_list ();
+ bool needs_replacement = false;
+ for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) {
+ needs_replacement = ll->is_id ();
+ }
+
+ if (needs_replacement) {
+
+ std::vector new_list (l);
+ for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) {
+ if (ll->is_id ()) {
+ unsigned long id = (unsigned long) ll->to_id ();
+ std::map ::const_iterator fw = m_propvalue_forward_references.find (id);
+ if (fw != m_propvalue_forward_references.end ()) {
+ *ll = tl::Variant (fw->second);
+ } else {
+ error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
+ }
+ }
+ }
+
+ v = tl::Variant (new_list.begin (), new_list.end ());
+
+ }
+
+ }
+}
+
void
OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special)
{
@@ -1251,7 +1377,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie
// Special properties are not turned into user properties except S_GDS_PROPERTY.
// This is mode is used for cells and layouts so the standard properties do not appear as user properties.
// For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names.
-
+
} else if (mm_last_value_list.get ().size () == 0) {
properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant ()));
} else if (mm_last_value_list.get ().size () == 1) {
@@ -1261,7 +1387,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie
}
}
-std::pair
+std::pair
OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore_special)
{
db::PropertiesRepository::properties_set properties;
@@ -1276,7 +1402,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore
// skip PAD.
mark_start_table ();
-
+
} else if (m == 34 /*CBLOCK*/) {
unsigned int type = get_uint ();
@@ -1311,7 +1437,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore
}
- }
+ }
if (! properties.empty ()) {
return std::make_pair (true, rep.properties_id (properties));
@@ -1320,7 +1446,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore
}
}
-void
+void
OASISReader::read_properties (db::PropertiesRepository &rep)
{
unsigned char m = get_byte ();
@@ -1429,12 +1555,12 @@ OASISReader::read_properties (db::PropertiesRepository &rep)
}
-
-void
+
+void
OASISReader::read_pointlist (modal_variable > &pointlist, bool for_polygon)
{
unsigned int type = get_uint ();
-
+
unsigned long n = 0;
get (n);
if (n == 0) {
@@ -1525,13 +1651,13 @@ OASISReader::read_repetition ()
{
unsigned int type = get_uint ();
if (type == 0) {
-
+
// reuse modal variable
} else if (type == 1) {
unsigned long nx = 0, ny = 0;
- get (nx);
+ get (nx);
get (ny);
db::Coord dx = get_ucoord ();
@@ -1542,7 +1668,7 @@ OASISReader::read_repetition ()
} else if (type == 2) {
unsigned long nx = 0;
- get (nx);
+ get (nx);
db::Coord dx = get_ucoord ();
@@ -1558,7 +1684,7 @@ OASISReader::read_repetition ()
mm_repetition = new RegularRepetition (db::Vector (0, 0), db::Vector (0, dy), 1, dy == 0 ? 1 : ny + 2);
} else if (type == 4 || type == 5) {
-
+
IrregularRepetition *rep = new IrregularRepetition ();
mm_repetition = rep;
@@ -1583,7 +1709,7 @@ OASISReader::read_repetition ()
}
} else if (type == 6 || type == 7) {
-
+
IrregularRepetition *rep = new IrregularRepetition ();
mm_repetition = rep;
@@ -1611,7 +1737,7 @@ OASISReader::read_repetition ()
unsigned long n = 0, m = 0;
- get (n);
+ get (n);
get (m);
db::Vector dn = get_gdelta ();
db::Vector dm = get_gdelta ();
@@ -1621,7 +1747,7 @@ OASISReader::read_repetition ()
} else if (type == 9) {
unsigned long n = 0;
- get (n);
+ get (n);
db::Vector dn = get_gdelta ();
mm_repetition = new RegularRepetition (dn, db::Vector (0, 0), dn == db::Vector () ? 1 : n + 2, 1);
@@ -1658,10 +1784,10 @@ OASISReader::read_repetition ()
return mm_repetition.get ().size () > 1;
}
-void
+void
OASISReader::do_read_placement (unsigned char r,
bool xy_absolute,
- db::Layout &layout,
+ db::Layout &layout,
tl::vector &instances,
tl::vector &instances_with_props)
{
@@ -1688,7 +1814,7 @@ OASISReader::do_read_placement (unsigned char r,
}
- }
+ }
double mag = 1.0;
bool mag_set = false;
@@ -1726,7 +1852,7 @@ OASISReader::do_read_placement (unsigned char r,
} else {
angle = ((m & 0x06) >> 1);
}
-
+
mirror = (m & 0x01) != 0;
if (m & 0x20) {
@@ -1764,10 +1890,10 @@ OASISReader::do_read_placement (unsigned char r,
db::CellInstArray inst;
if (mag_set || angle < 0) {
- inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()),
+ inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()),
db::ICplxTrans (mag, angle_deg, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb);
} else {
- inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()),
+ inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()),
db::Trans (angle, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb);
}
@@ -1859,9 +1985,9 @@ OASISReader::do_read_placement (unsigned char r,
}
}
-void
+void
OASISReader::do_read_text (bool xy_absolute,
- db::cell_index_type cell_index,
+ db::cell_index_type cell_index,
db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -1905,7 +2031,7 @@ OASISReader::do_read_text (bool xy_absolute,
mm_text_string = get_str ();
}
- }
+ }
if (m & 0x1) {
mm_textlayer = get_uint ();
@@ -1984,7 +2110,7 @@ OASISReader::do_read_text (bool xy_absolute,
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -2032,9 +2158,9 @@ OASISReader::do_read_text (bool xy_absolute,
}
}
-void
+void
OASISReader::do_read_rectangle (bool xy_absolute,
- db::cell_index_type cell_index,
+ db::cell_index_type cell_index,
db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -2049,13 +2175,13 @@ OASISReader::do_read_rectangle (bool xy_absolute,
if (m & 0x40) {
mm_geometry_w = get_ucoord_as_distance ();
- }
+ }
if (m & 0x80) {
mm_geometry_h = mm_geometry_w; // TODO: really?
} else {
if (m & 0x20) {
mm_geometry_h = get_ucoord_as_distance ();
- }
+ }
}
if (m & 0x10) {
@@ -2114,7 +2240,7 @@ OASISReader::do_read_rectangle (bool xy_absolute,
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -2139,7 +2265,7 @@ OASISReader::do_read_rectangle (bool xy_absolute,
}
} else {
-
+
std::pair pp = read_element_properties (layout.properties_repository (), false);
if (ll.first) {
@@ -2156,7 +2282,7 @@ OASISReader::do_read_rectangle (bool xy_absolute,
}
}
-void
+void
OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -2244,7 +2370,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index,
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -2264,7 +2390,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index,
}
++p;
}
-
+
}
}
@@ -2299,7 +2425,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index,
}
}
-void
+void
OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -2413,7 +2539,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db:
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -2435,7 +2561,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db:
}
}
-
+
}
}
@@ -2470,7 +2596,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db:
}
}
-void
+void
OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -2582,7 +2708,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -2629,7 +2755,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index
}
}
-void
+void
OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -2690,21 +2816,21 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index
},
// type 1
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, -1, 0, 0 }
},
// type 2
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 1, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 0, 0 }
},
// type 3
{
- { 0, 1, 0, 0 },
+ { 0, 1, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 0, 0 }
@@ -2718,147 +2844,147 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index
},
// type 5
{
- { 0, 1, 0, 0 },
+ { 0, 1, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, -1, 0, 0 }
},
// type 6
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 1, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, -1, 0, 0 }
},
// type 7
{
- { 0, 1, 0, 0 },
+ { 0, 1, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, -1, 0, 1 },
{ 1, 0, 0, 0 }
},
// type 8
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, -1, 1 },
{ 1, 0, 0, 0 }
},
// type 9
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, -1, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 0, 0 }
},
// type 10
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 1, 0 }
},
// type 11
{
- { 0, 0, 1, 0 },
+ { 0, 0, 1, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 0, 0 }
},
// type 12
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, -1, 1 },
{ 1, 0, 1, 0 }
},
// type 13
{
- { 0, 0, 1, 0 },
+ { 0, 0, 1, 0 },
{ 0, 0, -1, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 0, 0 }
},
// type 14
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, -1, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 1, 0 }
},
// type 15
{
- { 0, 0, 1, 0 },
+ { 0, 0, 1, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, -1, 1 },
{ 1, 0, 0, 0 }
},
// type 16
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 1, 0 },
{ 1, 0, 0, 0 },
{ 0, 0, 0, 0 }
},
// type 17
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 1, 0 },
{ 1, 0, 1, 0 },
{ 0, 0, 0, 0 }
},
// type 18
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 1, 0, 1, 0 },
{ 1, 0, 0, 0 },
{ 0, 0, 0, 0 }
},
// type 19
{
- { 0, 0, 1, 0 },
+ { 0, 0, 1, 0 },
{ 1, 0, 1, 0 },
{ 1, 0, 0, 0 },
{ 0, 0, 1, 0 }
},
// type 20
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 1, 0, 1 },
{ 0, 2, 0, 0 },
{ 0, 0, 0, 0 }
},
// type 21
{
- { 0, 0, 0, 1 },
+ { 0, 0, 0, 1 },
{ 0, 2, 0, 1 },
{ 0, 1, 0, 0 },
{ 0, 0, 0, 1 }
},
// type 22
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 2, 0 },
{ 1, 0, 1, 0 },
{ 0, 0, 0, 0 }
},
// type 23
{
- { 1, 0, 0, 0 },
+ { 1, 0, 0, 0 },
{ 0, 0, 1, 0 },
{ 1, 0, 2, 0 },
{ 1, 0, 0, 0 }
},
// type 24
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 0, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 0, 0 }
},
// type 25
{
- { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
{ 0, 0, 1, 0 },
{ 1, 0, 1, 0 },
{ 1, 0, 0, 0 }
@@ -2884,7 +3010,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index
if (m[3] != 0) y += m[3] * mm_geometry_h.get ();
pts [i] = db::Point (x, y);
-
+
if (x > w) w = x;
if (y > h) h = y;
@@ -2941,7 +3067,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -2988,7 +3114,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index
}
}
-void
+void
OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout)
{
unsigned char m = get_byte ();
@@ -3077,7 +3203,7 @@ OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, d
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
-
+
if (pp.first) {
cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second));
} else {
@@ -3160,10 +3286,10 @@ OASISReader::reset_modal_variables ()
mm_last_value_list.reset ();
}
-void
+void
OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
{
- // clears current instance list
+ // clears current instance list
m_instances.clear ();
m_instances_with_props.clear ();
@@ -3172,7 +3298,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
bool xy_absolute = true;
bool has_context = false;
- std::vector context_strings;
+ std::vector context_strings;
db::PropertiesRepository::properties_set cell_properties;
// read next record
@@ -3243,10 +3369,10 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
has_context = true;
context_strings.reserve (mm_last_value_list.get ().size ());
for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) {
- context_strings.push_back (v->to_string ());
+ context_strings.push_back (*v);
}
} else {
- // store cell properties
+ // store layout properties
store_last_properties (layout.properties_repository (), cell_properties, true);
}
@@ -3330,11 +3456,11 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
layout.cell (cell_index).prop_id (layout.properties_repository ().properties_id (cell_properties));
}
- // insert all instances collected (inserting them once is
+ // insert all instances collected (inserting them once is
// more effective than doing this every time)
if (! m_instances.empty ()) {
layout.cell (cell_index).insert (m_instances.begin (), m_instances.end ());
- // clear immediately, because if the cell is cleared before the instances are deleted, the
+ // clear immediately, because if the cell is cleared before the instances are deleted, the
// array pointers (living in the repository) may no longer be valid
m_instances.clear ();
}
@@ -3344,10 +3470,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
m_instances_with_props.clear ();
}
- // Restore proxy cell (link to PCell or Library)
+ // store the context strings for later
if (has_context) {
- CommonReaderLayerMapping layer_mapping (this, &layout);
- layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping);
+ m_context_strings_per_cell [cell_index].swap (context_strings);
}
m_cellname = "";
diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h
index f9aae8537..3e9ee8bf9 100644
--- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h
+++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h
@@ -172,6 +172,8 @@ private:
std::map m_propstrings;
std::map m_propnames;
+ std::map > m_context_strings_per_cell;
+
tl::vector m_instances;
tl::vector m_instances_with_props;
@@ -210,6 +212,7 @@ private:
void read_properties (db::PropertiesRepository &rep);
void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special);
std::pair read_element_properties (db::PropertiesRepository &rep, bool ignore_special);
+ void replace_forward_references_in_variant (tl::Variant &v);
unsigned char get_byte ()
{
diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc
index 8e8da55ad..6e54e9bfd 100644
--- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc
+++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc
@@ -666,9 +666,11 @@ OASISWriter::OASISWriter ()
mp_layout (0),
mp_cell (0),
m_layer (0), m_datatype (0),
+ m_write_context_info (false),
m_in_cblock (false),
m_propname_id (0),
m_propstring_id (0),
+ m_textstring_id (0),
m_proptables_written (false),
m_progress (tl::to_string (tr ("Writing OASIS file")), 10000)
{
@@ -1150,6 +1152,313 @@ OASISWriter::reset_modal_variables ()
mm_last_value_list.reset ();
}
+void
+OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers)
+{
+ // write the property names collected so far in the order of the ID's.
+
+ std::vector > rev_pn;
+ rev_pn.reserve (m_propnames.size ());
+ for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) {
+ rev_pn.push_back (std::make_pair (p->second, p->first));
+ }
+ std::sort (rev_pn.begin (), rev_pn.end ());
+
+ for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) {
+ tl_assert (p->first == (unsigned long)(p - rev_pn.begin ()));
+ begin_table (propnames_table_pos);
+ write_record_id (7);
+ write_nstring (p->second.c_str ());
+ }
+
+ // collect and write the future property names
+
+ std::set prop_ids_done;
+
+ for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
+
+ const db::Cell &cref (layout.cell (*cell));
+
+ if (cref.prop_id () != 0) {
+ begin_table (propnames_table_pos);
+ emit_propname_def (cref.prop_id ());
+ }
+
+ for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
+ if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
+ prop_ids_done.insert (inst->prop_id ());
+ begin_table (propnames_table_pos);
+ emit_propname_def (inst->prop_id ());
+ m_progress.set (mp_stream->pos ());
+ }
+ }
+
+ for (auto l = layers.begin (); l != layers.end (); ++l) {
+ db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
+ while (! shape.at_end ()) {
+ if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
+ prop_ids_done.insert (shape->prop_id ());
+ begin_table (propnames_table_pos);
+ emit_propname_def (shape->prop_id ());
+ m_progress.set (mp_stream->pos ());
+ }
+ shape.finish_array ();
+ }
+ }
+
+ }
+
+ // if needed, emit property name required for the PCell or meta info context information
+
+ if (m_write_context_info && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) {
+
+ bool has_context = false;
+ for (auto cell = cells.begin (); cell != cells.end () && ! has_context; ++cell) {
+ LayoutOrCellContextInfo ci;
+ has_context = layout.has_context_info (*cell) && layout.get_context_info (*cell, ci);
+ }
+
+ if (has_context) {
+ m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id++));
+ begin_table (propnames_table_pos);
+ write_record_id (7);
+ write_nstring (klayout_context_name);
+ }
+
+ }
+
+ end_table (propnames_table_pos);
+}
+
+void
+OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers)
+{
+ // write the property strings collected so far in the order of the ID's.
+
+ std::vector > rev_ps;
+ rev_ps.reserve (m_propstrings.size ());
+ for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
+ rev_ps.push_back (std::make_pair (p->second, &p->first));
+ }
+ std::sort (rev_ps.begin (), rev_ps.end ());
+
+ tl_assert (rev_ps.size () == size_t (m_propstring_id));
+
+ for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) {
+ tl_assert (p->first == (unsigned long)(p - rev_ps.begin ()));
+ begin_table (propstrings_table_pos);
+ write_record_id (9);
+ write_bstring (p->second->c_str ());
+ }
+
+ // collect and write the future property strings
+
+ std::set prop_ids_done;
+
+ for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
+
+ const db::Cell &cref (layout.cell (*cell));
+
+ if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) {
+ prop_ids_done.insert (cref.prop_id ());
+ begin_table (propstrings_table_pos);
+ emit_propstring_def (cref.prop_id ());
+ }
+
+ for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
+ if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
+ prop_ids_done.insert (inst->prop_id ());
+ begin_table (propstrings_table_pos);
+ emit_propstring_def (inst->prop_id ());
+ m_progress.set (mp_stream->pos ());
+ }
+ }
+
+ for (auto l = layers.begin (); l != layers.end (); ++l) {
+ db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
+ while (! shape.at_end ()) {
+ if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
+ prop_ids_done.insert (shape->prop_id ());
+ begin_table (propstrings_table_pos);
+ emit_propstring_def (shape->prop_id ());
+ m_progress.set (mp_stream->pos ());
+ }
+ shape.finish_array ();
+ }
+ }
+
+ }
+
+ if (m_write_context_info) {
+
+ // emit property string id's required for the PCell and meta info context information
+ std::vector context_prop_strings;
+
+ for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
+
+ m_progress.set (mp_stream->pos ());
+ context_prop_strings.clear ();
+
+ if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) {
+
+ for (auto c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
+ if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) {
+ begin_table (propstrings_table_pos);
+ write_record_id (9);
+ write_bstring (c->c_str ());
+ ++m_propstring_id;
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+ end_table (propstrings_table_pos);
+}
+
+void
+OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const db::Layout &layout)
+{
+ bool sequential = true;
+ for (auto cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
+ sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
+ }
+
+ // CELLNAME (implicit or explicit)
+ for (auto cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
+
+ begin_table (cellnames_table_pos);
+
+ write_record_id (sequential ? 3 : 4);
+ write_nstring (layout.cell_name (*cell));
+ if (! sequential) {
+ write ((unsigned long) *cell);
+ }
+
+ if (m_options.write_std_properties > 1) {
+
+ reset_modal_variables ();
+
+ // write S_BOUNDING_BOX entries
+
+ std::vector values;
+
+ // TODO: how to set the "depends on external cells" flag?
+ db::Box bbox = layout.cell (*cell).bbox ();
+ if (bbox.empty ()) {
+ // empty box
+ values.push_back (tl::Variant ((unsigned int) 0x2));
+ bbox = db::Box (0, 0, 0, 0);
+ } else {
+ values.push_back (tl::Variant ((unsigned int) 0x0));
+ }
+
+ values.push_back (tl::Variant (bbox.left ()));
+ values.push_back (tl::Variant (bbox.bottom ()));
+ values.push_back (tl::Variant (bbox.width ()));
+ values.push_back (tl::Variant (bbox.height ()));
+
+ write_property_def (s_bounding_box_name, values, true);
+
+ // PROPERTY record with S_CELL_OFFSET
+ if (cell_positions) {
+ std::map::const_iterator pp = cell_positions->find (*cell);
+ if (pp != cell_positions->end ()) {
+ write_property_def (s_cell_offset_name, tl::Variant (pp->second), true);
+ } else {
+ write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true);
+ }
+ }
+
+ }
+
+ }
+
+ end_table (cellnames_table_pos);
+}
+
+void
+OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers)
+{
+ // write present text strings
+
+ // collect present strings by ID
+ std::vector > rev_ts;
+ rev_ts.reserve (m_textstrings.size ());
+ for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) {
+ rev_ts.push_back (std::make_pair (p->second, &p->first));
+ }
+ std::sort (rev_ts.begin (), rev_ts.end ());
+
+ tl_assert (rev_ts.size () == size_t (m_textstring_id));
+
+ for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) {
+ tl_assert (t->first == (unsigned long)(t - rev_ts.begin ()));
+ begin_table (textstrings_table_pos);
+ write_record_id (5);
+ write_nstring (t->second->c_str ());
+ }
+
+ // collect future test strings
+
+ for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
+
+ const db::Cell &cref (layout.cell (*cell));
+ for (auto l = layers.begin (); l != layers.end (); ++l) {
+ db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts));
+ while (! shape.at_end ()) {
+ if (m_textstrings.insert (std::make_pair (shape->text_string (), m_textstring_id)).second) {
+ begin_table (textstrings_table_pos);
+ write_record_id (5);
+ write_astring (shape->text_string ());
+ ++m_textstring_id;
+ m_progress.set (mp_stream->pos ());
+ }
+ ++shape;
+ }
+ }
+
+ }
+
+ end_table (textstrings_table_pos);
+}
+
+void
+OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vector > &layers)
+{
+ for (auto l = layers.begin (); l != layers.end (); ++l) {
+
+ if (! l->second.name.empty ()) {
+
+ begin_table (layernames_table_pos);
+
+ // write mappings to text layer and shape layers
+ write_record_id (11);
+ write_nstring (l->second.name.c_str ());
+ write_byte (3);
+ write ((unsigned long) l->second.layer);
+ write_byte (3);
+ write ((unsigned long) l->second.datatype);
+
+ write_record_id (12);
+ write_nstring (l->second.name.c_str ());
+ write_byte (3);
+ write ((unsigned long) l->second.layer);
+ write_byte (3);
+ write ((unsigned long) l->second.datatype);
+
+ m_progress.set (mp_stream->pos ());
+
+ }
+
+ }
+
+ end_table (layernames_table_pos);
+}
+
static bool must_write_cell (const db::Cell &cref)
{
// Don't write proxy cells which are not employed
@@ -1162,6 +1471,7 @@ static bool skip_cell_body (const db::Cell &cref)
return cref.is_ghost_cell () && cref.empty ();
}
+
void
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
@@ -1172,6 +1482,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
m_layer = m_datatype = 0;
m_in_cblock = false;
m_cblock_buffer.clear ();
+ m_write_context_info = options.write_context_info ();
m_options = options.get_options ();
mp_stream = &stream;
@@ -1252,6 +1563,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
// We will collect the standard properties here:
m_propstring_id = m_propname_id = 0;
+ m_textstring_id = 0;
m_proptables_written = false;
std::vector > init_props;
@@ -1303,306 +1615,47 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_props (layout.prop_id ());
}
- // build property name and value string tables
-
- {
-
- // write the property names collected so far in the order of the ID's.
-
- std::vector > rev_pn;
- rev_pn.reserve (m_propnames.size ());
- for (std::map ::const_iterator p = m_propnames.begin (); p != m_propnames.end (); ++p) {
- rev_pn.push_back (std::make_pair (p->second, p->first));
- }
- std::sort (rev_pn.begin (), rev_pn.end ());
-
- for (std::vector >::const_iterator p = rev_pn.begin (); p != rev_pn.end (); ++p) {
- tl_assert (p->first == (unsigned long)(p - rev_pn.begin ()));
- begin_table (propnames_table_pos);
- write_record_id (7);
- write_nstring (p->second.c_str ());
- }
-
- // collect and write the future property names
-
- std::set prop_ids_done;
-
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
-
- const db::Cell &cref (layout.cell (*cell));
-
- if (cref.prop_id () != 0) {
- begin_table (propnames_table_pos);
- emit_propname_def (cref.prop_id ());
- }
-
- for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
- if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
- prop_ids_done.insert (inst->prop_id ());
- begin_table (propnames_table_pos);
- emit_propname_def (inst->prop_id ());
- m_progress.set (mp_stream->pos ());
- }
- }
-
- for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
- db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
- while (! shape.at_end ()) {
- if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
- prop_ids_done.insert (shape->prop_id ());
- begin_table (propnames_table_pos);
- emit_propname_def (shape->prop_id ());
- m_progress.set (mp_stream->pos ());
- }
- shape.finish_array ();
- }
- }
-
- }
-
- if (options.write_context_info ()) {
-
- // emit property name required for the PCell context information
- std::vector context_prop_strings;
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
-
- const db::Cell &cref (layout.cell (*cell));
- if (cref.is_proxy () && ! cref.is_top () && layout.get_context_info (*cell, context_prop_strings)) {
-
- if (m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id)).second) {
- begin_table (propnames_table_pos);
- write_record_id (7);
- write_nstring (klayout_context_name);
- ++m_propname_id;
- }
- break;
-
- }
-
- }
-
- }
-
- end_table (propnames_table_pos);
-
- }
-
- {
-
- // write the property strings collected so far in the order of the ID's.
-
- std::vector > rev_ps;
- rev_ps.reserve (m_propstrings.size ());
- for (std::map ::const_iterator p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
- rev_ps.push_back (std::make_pair (p->second, p->first));
- }
- std::sort (rev_ps.begin (), rev_ps.end ());
-
- for (std::vector >::const_iterator p = rev_ps.begin (); p != rev_ps.end (); ++p) {
- tl_assert (p->first == (unsigned long)(p - rev_ps.begin ()));
- begin_table (propstrings_table_pos);
- write_record_id (9);
- write_nstring (p->second.c_str ());
- }
-
- // collect and write the future property strings
-
- std::set prop_ids_done;
-
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
-
- const db::Cell &cref (layout.cell (*cell));
-
- if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) {
- prop_ids_done.insert (cref.prop_id ());
- begin_table (propnames_table_pos);
- emit_propstring_def (cref.prop_id ());
- }
-
- for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
- if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
- prop_ids_done.insert (inst->prop_id ());
- begin_table (propstrings_table_pos);
- emit_propstring_def (inst->prop_id ());
- m_progress.set (mp_stream->pos ());
- }
- }
-
- for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
- db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
- while (! shape.at_end ()) {
- if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
- prop_ids_done.insert (shape->prop_id ());
- begin_table (propstrings_table_pos);
- emit_propstring_def (shape->prop_id ());
- m_progress.set (mp_stream->pos ());
- }
- shape.finish_array ();
- }
- }
-
- }
-
- if (options.write_context_info ()) {
-
- // emit property string id's required for the PCell context information
- std::vector context_prop_strings;
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
-
- m_progress.set (mp_stream->pos ());
-
- const db::Cell &cref (layout.cell (*cell));
- if (cref.is_proxy () && ! cref.is_top ()) {
-
- context_prop_strings.clear ();
- if (layout.get_context_info (*cell, context_prop_strings)) {
-
- for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
- if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) {
- begin_table (propstrings_table_pos);
- write_record_id (9);
- write_bstring (c->c_str ());
- ++m_propstring_id;
- }
- }
-
- }
-
- }
-
- }
-
- }
-
- end_table (propstrings_table_pos);
-
- }
-
- // Now we cannot open new property ID's in strict mode
- m_proptables_written = true;
-
- // build cell name table now in non-strict mode (in strict mode it is written at the
- // end because then we have the cell positions fo S_CELL_OFFSET)
-
- if (! m_options.strict_mode) {
-
- size_t pos = 0;
-
- bool sequential = true;
- for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
- sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
- }
-
- // CELLNAME (implicit or explicit)
- for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
-
- begin_table (pos);
-
- write_record_id (sequential ? 3 : 4);
- write_nstring (layout.cell_name (*cell));
- if (! sequential) {
- write ((unsigned long) *cell);
- }
-
- if (m_options.write_std_properties > 1) {
-
- reset_modal_variables ();
-
- // write S_BOUNDING_BOX entries
-
- std::vector values;
-
- // TODO: how to set the "depends on external cells" flag?
- db::Box bbox = layout.cell (*cell).bbox ();
- if (bbox.empty ()) {
- // empty box
- values.push_back (tl::Variant ((unsigned int) 0x2));
- bbox = db::Box (0, 0, 0, 0);
- } else {
- values.push_back (tl::Variant ((unsigned int) 0x0));
- }
-
- values.push_back (tl::Variant (bbox.left ()));
- values.push_back (tl::Variant (bbox.bottom ()));
- values.push_back (tl::Variant (bbox.width ()));
- values.push_back (tl::Variant (bbox.height ()));
-
- write_property_def (s_bounding_box_name, values, true);
-
- }
-
- }
-
- end_table (pos);
-
- }
-
- // build text string table
-
- {
-
- unsigned int id = 0;
-
- for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
-
- const db::Cell &cref (layout.cell (*cell));
- for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
- db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts));
- while (! shape.at_end ()) {
- if (m_textstrings.insert (std::make_pair (shape->text_string (), id)).second) {
- begin_table (textstrings_table_pos);
- write_record_id (5);
- write_astring (shape->text_string ());
- ++id;
- m_progress.set (mp_stream->pos ());
- }
- ++shape;
- }
- }
-
- }
-
- end_table (textstrings_table_pos);
-
- }
-
- // write layernames table
-
- {
-
- for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
-
- if (! l->second.name.empty ()) {
-
- begin_table (layernames_table_pos);
-
- // write mappings to text layer and shape layers
- write_record_id (11);
- write_nstring (l->second.name.c_str ());
- write_byte (3);
- write ((unsigned long) l->second.layer);
- write_byte (3);
- write ((unsigned long) l->second.datatype);
-
- write_record_id (12);
- write_nstring (l->second.name.c_str ());
- write_byte (3);
- write ((unsigned long) l->second.layer);
- write_byte (3);
- write ((unsigned long) l->second.datatype);
-
- m_progress.set (mp_stream->pos ());
-
- }
-
- }
-
- end_table (layernames_table_pos);
-
- }
-
std::vector context_prop_strings;
+ // write the global layout context information
+
+ if (options.write_context_info () && layout.has_context_info () && layout.get_context_info (context_prop_strings)) {
+
+ std::vector values;
+ values.reserve (context_prop_strings.size ());
+ for (auto i = context_prop_strings.begin (); i != context_prop_strings.end (); ++i) {
+ values.push_back (tl::Variant (*i));
+ }
+
+ write_property_def (klayout_context_name, values, false);
+
+ context_prop_strings.clear ();
+
+ }
+
+ // write the tables
+
+ if (! m_options.tables_at_end) {
+
+ write_propname_table (propnames_table_pos, cells, layout, layers);
+ write_propstring_table (propstrings_table_pos, cells, layout, layers);
+
+ // Now we cannot open new property ID's in strict mode
+ m_proptables_written = true;
+
+ // build cell name table now in non-strict mode (in strict mode it is written at the
+ // end because then we have the cell positions fo S_CELL_OFFSET)
+ if (! m_options.strict_mode) {
+ write_cellname_table (cellnames_table_pos, cells_by_index, 0, layout);
+ }
+
+ write_textstring_table (textstrings_table_pos, cells, layout, layers);
+ write_layername_table (layernames_table_pos, layers);
+
+ }
+
+ // write cells
+
for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
m_progress.set (mp_stream->pos ());
@@ -1630,7 +1683,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
}
// context information as property named KLAYOUT_CONTEXT
- if (cref.is_proxy () && options.write_context_info ()) {
+ if (options.write_context_info () && layout.has_context_info (*cell)) {
context_prop_strings.clear ();
@@ -1638,17 +1691,29 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_record_id (28);
write_byte (char (0xf6));
+ unsigned long pnid = 0;
std::map ::const_iterator pni = m_propnames.find (klayout_context_name);
- tl_assert (pni != m_propnames.end ());
- write (pni->second);
+ if (pni == m_propnames.end ()) {
+ pnid = m_propname_id++;
+ m_propnames.insert (std::make_pair (klayout_context_name, pnid));
+ } else {
+ pnid = pni->second;
+ }
+ write (pnid);
write ((unsigned long) context_prop_strings.size ());
for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
write_byte (14); // b-string by reference number
+ unsigned long psid = 0;
std::map ::const_iterator psi = m_propstrings.find (*c);
- tl_assert (psi != m_propstrings.end ());
- write (psi->second);
+ if (psi == m_propstrings.end ()) {
+ psid = m_propstring_id++;
+ m_propstrings.insert (std::make_pair (*c, psid)).second;
+ } else {
+ psid = psi->second;
+ }
+ write (psid);
}
mm_last_property_name = klayout_context_name;
@@ -1686,66 +1751,31 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
}
+ // write the tables if at end
+
+ if (m_options.tables_at_end) {
+
+ // do not consider future items as everything has been collected
+ std::vector no_cells;
+ std::vector > no_layers;
+
+ write_propname_table (propnames_table_pos, no_cells, layout, no_layers);
+ write_propstring_table (propstrings_table_pos, no_cells, layout, no_layers);
+
+ // Now we cannot open new property ID's in strict mode
+ m_proptables_written = true;
+
+ write_textstring_table (textstrings_table_pos, no_cells, layout, no_layers);
+
+ // write all layers here
+ write_layername_table (layernames_table_pos, layers);
+
+ }
+
// write cell table at the end in strict mode (in that mode we need the cell positions
// for the S_CELL_OFFSET properties)
-
- if (m_options.strict_mode) {
-
- bool sequential = true;
- for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
- sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
- }
-
- for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
-
- begin_table (cellnames_table_pos);
-
- // CELLNAME (explicit)
- write_record_id (sequential ? 3 : 4);
- write_nstring (layout.cell_name (*cell));
- if (! sequential) {
- write ((unsigned long) *cell);
- }
-
- reset_modal_variables ();
-
- if (m_options.write_std_properties > 1) {
-
- // write S_BOUNDING_BOX entries
-
- std::vector values;
-
- // TODO: how to set the "depends on external cells" flag?
- db::Box bbox = layout.cell (*cell).bbox ();
- if (bbox.empty ()) {
- // empty box
- values.push_back (tl::Variant ((unsigned int) 0x2));
- bbox = db::Box (0, 0, 0, 0);
- } else {
- values.push_back (tl::Variant ((unsigned int) 0x0));
- }
-
- values.push_back (tl::Variant (bbox.left ()));
- values.push_back (tl::Variant (bbox.bottom ()));
- values.push_back (tl::Variant (bbox.width ()));
- values.push_back (tl::Variant (bbox.height ()));
-
- write_property_def (s_bounding_box_name, values, true);
-
- }
-
- // PROPERTY record with S_CELL_OFFSET
- std::map::const_iterator pp = cell_positions.find (*cell);
- if (pp != cell_positions.end ()) {
- write_property_def (s_cell_offset_name, tl::Variant (pp->second), true);
- } else {
- write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true);
- }
-
- }
-
- end_table (cellnames_table_pos);
-
+ if (m_options.tables_at_end || m_options.strict_mode) {
+ write_cellname_table (cellnames_table_pos, cells_by_index, &cell_positions, layout);
}
// END record
@@ -2341,9 +2371,15 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const
m_progress.set (mp_stream->pos ());
db::Trans trans = text.trans ();
+
+ unsigned long text_id = 0;
std::map ::const_iterator ts = m_textstrings.find (text.string ());
- tl_assert (ts != m_textstrings.end ());
- unsigned long text_id = ts->second;
+ if (ts == m_textstrings.end ()) {
+ text_id = m_textstring_id++;
+ m_textstrings.insert (std::make_pair (text.string (), text_id));
+ } else {
+ text_id = ts->second;
+ }
unsigned char info = 0x20;
diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h
index 6a853f370..0dd319515 100644
--- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h
+++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h
@@ -203,12 +203,14 @@ private:
const db::Cell *mp_cell;
int m_layer;
int m_datatype;
+ bool m_write_context_info;
std::vector m_pointlist;
tl::OutputMemoryStream m_cblock_buffer;
tl::OutputMemoryStream m_cblock_compressed;
bool m_in_cblock;
unsigned long m_propname_id;
unsigned long m_propstring_id;
+ unsigned long m_textstring_id;
bool m_proptables_written;
std::map m_textstrings;
@@ -308,6 +310,12 @@ private:
void write_pointlist (const std::vector &pointlist, bool for_polygons);
void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep);
+
+ void write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers);
+ void write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers);
+ void write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const Layout &layout);
+ void write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers);
+ void write_layername_table (size_t &layernames_table_pos, const std::vector > &layers);
};
} // namespace db
diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc
index 91566f61a..ac574affb 100644
--- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc
+++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc
@@ -33,7 +33,7 @@
#include
-void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress)
+void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end)
{
{
db::Manager m (false);
@@ -60,6 +60,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = false;
oasis_options.strict_mode = false;
+ oasis_options.tables_at_end = tables_at_end;
options.set_options (oasis_options);
writer.write (layout, stream, options);
}
@@ -115,6 +116,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = true;
oasis_options.strict_mode = true;
+ oasis_options.tables_at_end = tables_at_end;
options.set_options (oasis_options);
writer.write (layout, stream, options);
}
@@ -164,6 +166,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = false;
oasis_options.strict_mode = false;
+ oasis_options.tables_at_end = tables_at_end;
oasis_options.write_std_properties = 2;
options.set_options (oasis_options);
writer.write (layout, stream, options);
@@ -214,6 +217,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = true;
oasis_options.strict_mode = true;
+ oasis_options.tables_at_end = tables_at_end;
oasis_options.write_std_properties = 2;
options.set_options (oasis_options);
writer.write (layout, stream, options);
@@ -255,6 +259,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.compression_level = compr;
oasis_options.recompress = recompress;
+ oasis_options.tables_at_end = tables_at_end;
options.set_options (oasis_options);
options.set_scale_factor (3.0);
options.set_dbu (0.0005);
@@ -308,11 +313,14 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
void run_test (tl::TestBase *_this, const char *file, bool scaling_test = true)
{
for (int recompress = 0; recompress < 2; ++recompress) {
- run_test (_this, file, scaling_test, 0, recompress);
- run_test (_this, file, scaling_test, 1, recompress);
- run_test (_this, file, scaling_test, 2, recompress);
- run_test (_this, file, scaling_test, 10, recompress);
+ run_test (_this, file, scaling_test, 0, recompress, false);
+ run_test (_this, file, scaling_test, 1, recompress, false);
+ run_test (_this, file, scaling_test, 2, recompress, false);
+ run_test (_this, file, scaling_test, 10, recompress, false);
}
+
+ // tables at end
+ run_test (_this, file, scaling_test, 2, false, true);
}
TEST(1)
@@ -1860,3 +1868,111 @@ TEST(120_IrregularInstRepetitions)
}
}
+
+// Meta info
+static void
+run_test130 (tl::TestBase *_this, bool strict, bool tables_at_end)
+{
+ db::Layout layout_org;
+
+ layout_org.add_cell ("U");
+ db::cell_index_type ci = layout_org.add_cell ("X");
+
+ layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true));
+ layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
+
+ layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true));
+ layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true));
+
+ std::string tmp_file = _this->tmp_file ("tmp_OASISWriter1.oas");
+
+ {
+ tl::OutputStream out (tmp_file);
+ db::OASISWriterOptions oasis_options;
+ oasis_options.strict_mode = strict;
+ oasis_options.tables_at_end = tables_at_end;
+ db::SaveLayoutOptions options;
+ options.set_format ("OASIS");
+ options.set_options (oasis_options);
+ db::Writer writer (options);
+ writer.write (layout_org, out);
+ }
+
+ db::Layout layout_read;
+
+ {
+ tl::InputStream in (tmp_file);
+ db::Reader reader (in);
+ reader.read (layout_read);
+ }
+
+ EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5");
+ EXPECT_EQ (layout_read.meta_info ("a").description, "description");
+ EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
+ EXPECT_EQ (layout_read.meta_info ("b").description, "");
+
+ db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
+
+ EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true");
+ EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd");
+ EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
+ EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d");
+
+ tmp_file = _this->tmp_file ("tmp_OASISWriter2.oas");
+
+ {
+ tl::OutputStream out (tmp_file);
+ db::OASISWriterOptions oasis_options;
+ oasis_options.strict_mode = strict;
+ oasis_options.tables_at_end = tables_at_end;
+ db::SaveLayoutOptions options;
+ options.set_format ("OASIS");
+ options.set_options (oasis_options);
+ options.set_write_context_info (false);
+ db::Writer writer (options);
+ writer.write (layout_org, out);
+ }
+
+ layout_read = db::Layout ();
+
+ {
+ tl::InputStream in (tmp_file);
+ db::Reader reader (in);
+ reader.read (layout_read);
+ }
+
+ EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
+
+ ci2 = layout_read.cell_by_name ("X").second;
+
+ EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
+ EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
+}
+
+// Meta info
+
+TEST(130a)
+{
+ run_test130 (_this, false, false);
+}
+
+TEST(130b)
+{
+ run_test130 (_this, true, false);
+}
+
+TEST(130c)
+{
+ run_test130 (_this, false, true);
+}
+
+TEST(130d)
+{
+ run_test130 (_this, true, true);
+}
+
diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc
index d85ca8173..8b8982d24 100644
--- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc
+++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc
@@ -1146,7 +1146,7 @@ public:
std::string lyr_file = data.get_layer_properties_file ();
if (! lyr_file.empty ()) {
- layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file));
+ layout.add_meta_info ("layer-properties-file", db::MetaInfo ("Layer Properties File", lyr_file));
}
return m_layers;
diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc
index e8fbc559a..7a1e83c1d 100644
--- a/src/pya/pya/pyaModule.cc
+++ b/src/pya/pya/pyaModule.cc
@@ -337,7 +337,7 @@ public:
for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
if (! cc->name ().empty ()) {
- PyTypeObject *child_class = make_class (cc.operator-> (), as_static);
+ PyTypeObject *child_class = make_class (cc->declaration (), as_static);
PythonRef attr ((PyObject *) child_class, false /*borrowed*/);
set_type_attr (type, cc->name ().c_str (), attr);
}
diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc
index d11eca094..b3327b218 100644
--- a/src/rba/rba/rba.cc
+++ b/src/rba/rba/rba.cc
@@ -1654,7 +1654,7 @@ public:
for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
if (! cc->name ().empty ()) {
if (! is_registered (cc->declaration (), false)) {
- make_class (cc->declaration (), false, klass, cc->declaration ());
+ make_class (cc->declaration (), false, klass, cls);
} else {
VALUE child_class = ruby_cls (cc->declaration (), false);
rb_define_const (klass, cc->name ().c_str (), child_class);
diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc
index bf3c32225..90758df1c 100644
--- a/src/tl/tl/tlString.cc
+++ b/src/tl/tl/tlString.cc
@@ -210,6 +210,22 @@ inline bool safe_isspace (char c)
return c != 0 && static_cast (c) < 0x80 && isspace (c);
}
+// -------------------------------------------------------------------------
+// Utility: skip a newline
+
+bool skip_newline (const char *&cp)
+{
+ if (*cp == '\012' || *cp == '\015') {
+ if (*cp == '\015' && cp[1] == '\012') {
+ ++cp;
+ }
+ ++cp;
+ return true;
+ } else {
+ return false;
+ }
+}
+
// -------------------------------------------------------------------------
// Utility: a strtod version that is independent of the locale
diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h
index aa2f18490..33bc0413b 100644
--- a/src/tl/tl/tlString.h
+++ b/src/tl/tl/tlString.h
@@ -987,6 +987,37 @@ TL_PUBLIC uint32_t utf32_upcase (uint32_t c32);
*/
TL_PUBLIC uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0);
+/**
+ * @brief Checks if the next characters are CR, LF or CR+LF and skips them
+ *
+ * This function returns true, if a line separated was found and skipped
+ */
+TL_PUBLIC bool skip_newline (const char *&cp);
+
+/**
+ * @brief checks if the given character is a CR character
+ */
+inline bool is_cr (char c)
+{
+ return c == '\015';
+}
+
+/**
+ * @brief checks if the given character is a LF character
+ */
+inline bool is_lf (char c)
+{
+ return c == '\012';
+}
+
+/**
+ * @brief checks if the given character is a CR or LF character
+ */
+inline bool is_newline (char c)
+{
+ return is_cr (c) || is_lf (c);
+}
+
} // namespace tl
#endif
diff --git a/src/tl/tl/tlUri.cc b/src/tl/tl/tlUri.cc
index dbd3fc9e6..c416e1e8d 100644
--- a/src/tl/tl/tlUri.cc
+++ b/src/tl/tl/tlUri.cc
@@ -111,12 +111,9 @@ URI::URI (const std::string &uri)
}
m_scheme = unescape (m_scheme);
- bool prefer_authority = true;
- if (m_scheme == "file") {
- prefer_authority = false;
- // other schemes?
- } else if (m_scheme.empty ()) {
- prefer_authority = false;
+ bool prefer_authority = false;
+ if (m_scheme == "http" || m_scheme == "https") {
+ prefer_authority = true;
}
ex0 = ex;
diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb
index e204b2e3c..32b291f2a 100644
--- a/testdata/ruby/dbLayoutTests2.rb
+++ b/testdata/ruby/dbLayoutTests2.rb
@@ -1044,36 +1044,39 @@ class DBLayoutTests2_TestClass < TestBase
end
# Meta information
- def test_12
+ def test_12a
mi = RBA::LayoutMetaInfo::new("myinfo", "a")
assert_equal(mi.name, "myinfo")
assert_equal(mi.description, "")
assert_equal(mi.value, "a")
+ assert_equal(mi.is_persisted?, false)
mi.name = "x"
mi.description = "y"
mi.value = "z"
+ mi.persisted = true
assert_equal(mi.name, "x")
assert_equal(mi.description, "y")
assert_equal(mi.value, "z")
+ assert_equal(mi.is_persisted?, true)
ly = RBA::Layout::new
ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "a"))
- ly.add_meta_info(RBA::LayoutMetaInfo::new("another", "42", "description"))
+ ly.add_meta_info(RBA::LayoutMetaInfo::new("another", 42, "description"))
assert_equal(ly.meta_info_value("myinfo"), "a")
- assert_equal(ly.meta_info_value("doesnotexist"), "")
- assert_equal(ly.meta_info_value("another"), "42")
+ assert_equal(ly.meta_info_value("doesnotexist"), nil)
+ assert_equal(ly.meta_info_value("another"), 42)
a = []
ly.each_meta_info { |mi| a << mi.name }
assert_equal(a.join(","), "myinfo,another")
a = []
- ly.each_meta_info { |mi| a << mi.value }
+ ly.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "a,42")
a = []
ly.each_meta_info { |mi| a << mi.description }
@@ -1081,13 +1084,64 @@ class DBLayoutTests2_TestClass < TestBase
ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "b"))
assert_equal(ly.meta_info_value("myinfo"), "b")
- assert_equal(ly.meta_info_value("doesnotexist"), "")
- assert_equal(ly.meta_info_value("another"), "42")
+ assert_equal(ly.meta_info_value("doesnotexist"), nil)
+ assert_equal(ly.meta_info_value("another"), 42)
+
+ ly.remove_meta_info("doesnotexist") # should not fail
ly.remove_meta_info("myinfo")
- assert_equal(ly.meta_info_value("myinfo"), "")
- assert_equal(ly.meta_info_value("doesnotexist"), "")
- assert_equal(ly.meta_info_value("another"), "42")
+ assert_equal(ly.meta_info_value("myinfo"), nil)
+ assert_equal(ly.meta_info_value("doesnotexist"), nil)
+ assert_equal(ly.meta_info_value("another"), 42)
+
+ assert_equal(ly.meta_info("doesnotexist"), nil)
+ assert_equal(ly.meta_info("another").value, 42)
+ assert_equal(ly.meta_info("another").description, "description")
+
+ ly.clear_meta_info
+ assert_equal(ly.meta_info_value("another"), nil)
+ assert_equal(ly.meta_info("another"), nil)
+
+ # cellwise
+
+ c1 = ly.create_cell("X")
+ c2 = ly.create_cell("U")
+
+ c1.add_meta_info(RBA::LayoutMetaInfo::new("a", true))
+ c1.add_meta_info(RBA::LayoutMetaInfo::new("b", [ 1, 17, 42 ]))
+
+ assert_equal(c2.meta_info("a"), nil)
+ assert_equal(c2.meta_info_value("a"), nil)
+
+ a = []
+ c2.each_meta_info { |mi| a << mi.value.to_s }
+ assert_equal(a.join(","), "")
+
+ assert_equal(c1.meta_info("a").value, true)
+ assert_equal(c1.meta_info("b").value, [ 1, 17, 42 ])
+ assert_equal(c1.meta_info_value("b"), [ 1, 17, 42 ])
+
+ a = []
+ c1.each_meta_info { |mi| a << mi.value.to_s }
+ assert_equal(a.join(","), "true,[1, 17, 42]")
+
+ c1.remove_meta_info("doesnotexist") # should not fail
+
+ a = []
+ c1.each_meta_info { |mi| a << mi.value.to_s }
+ assert_equal(a.join(","), "true,[1, 17, 42]")
+
+ c1.remove_meta_info("b")
+
+ a = []
+ c1.each_meta_info { |mi| a << mi.value.to_s }
+ assert_equal(a.join(","), "true")
+
+ c1.clear_meta_info
+
+ a = []
+ c1.each_meta_info { |mi| a << mi.value.to_s }
+ assert_equal(a.join(","), "")
end
diff --git a/testdata/ruby/layLayoutView.rb b/testdata/ruby/layLayoutView.rb
index 9ec0bd57c..fdd384cc6 100644
--- a/testdata/ruby/layLayoutView.rb
+++ b/testdata/ruby/layLayoutView.rb
@@ -186,7 +186,7 @@ class LAYLayoutView_TestClass < TestBase
view.set_config("search-range-box", "5")
view.select_from(RBA::DBox::new(-1.0, -1.0, 1.0, 1.0))
assert_equal(selection_changed, 1)
- assert_equal(view.selection_size, 4)
+ assert_equal(view.selection_size, 2)
assert_equal(view.has_selection?, true)
view.select_from(RBA::DPoint::new(0, 0), RBA::LayoutView::Invert)