mirror of https://github.com/KLayout/klayout.git
commit
641d2404d2
|
|
@ -124,6 +124,7 @@ SOURCES = \
|
|||
gsiDeclDbLibrary.cc \
|
||||
gsiDeclDbManager.cc \
|
||||
gsiDeclDbMatrix.cc \
|
||||
gsiDeclDbMetaInfo.cc \
|
||||
gsiDeclDbPath.cc \
|
||||
gsiDeclDbPoint.cc \
|
||||
gsiDeclDbPolygon.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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -556,7 +556,7 @@ make_clip_variants (const db::Layout &layout,
|
|||
for (std::map <std::pair <db::cell_index_type, db::Box>, 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<db::DPoint> &linestarts)
|
||||
void
|
||||
hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector<db::DPoint> &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<db::DPoint> &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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<db::DPoint> &linestarts);
|
||||
DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector<db::DPoint> &linestarts, double &left, double &bottom);
|
||||
DB_PUBLIC std::vector<std::string> 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<db::DPoint> 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<C> bbox () const
|
||||
db::DBox bbox () const
|
||||
{
|
||||
db::DBox b = hershey_text_box (m_string, m_font);
|
||||
db::point<C> p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale));
|
||||
db::point<C> p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale));
|
||||
return box<C> (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 <db::DPoint> m_linestarts;
|
||||
double m_left, m_bottom;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -258,12 +258,12 @@ private:
|
|||
// -----------------------------------------------------------------
|
||||
// Implementation of the ProxyContextInfo class
|
||||
|
||||
ProxyContextInfo
|
||||
ProxyContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
|
||||
LayoutOrCellContextInfo
|
||||
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
|
||||
{
|
||||
ProxyContextInfo info;
|
||||
LayoutOrCellContextInfo info;
|
||||
|
||||
for (std::vector<std::string>::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<std::string>::const_iterator from, st
|
|||
|
||||
info.cell_name = ex.skip ();
|
||||
|
||||
} else if (ex.test ("META(")) {
|
||||
|
||||
std::pair<std::string, std::pair<tl::Variant, std::string> > 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<std::string>::const_iterator from, st
|
|||
}
|
||||
|
||||
void
|
||||
ProxyContextInfo::serialize (std::vector<std::string> &strings)
|
||||
LayoutOrCellContextInfo::serialize (std::vector<std::string> &strings)
|
||||
{
|
||||
if (! lib_name.empty ()) {
|
||||
strings.push_back ("LIB=" + lib_name);
|
||||
}
|
||||
for (std::map<std::string, tl::Variant> ::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<std::string> &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<meta_info_name_id_type>::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::vector<tl
|
|||
|
||||
}
|
||||
|
||||
bool
|
||||
Layout::has_context_info () const
|
||||
{
|
||||
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
|
||||
if (i->second.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 <std::string> &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<tl::Variant, std::string> &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 <std::string>::const_iterator from, std::vector <std::string>::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 <std::string> &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 <std::string>
|
|||
}
|
||||
|
||||
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<tl::Variant, std::string> &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 <const db::ColdProxy *> (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 <std::string>::const_iterator from, std::vector <std::string>::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 <std::string>:
|
|||
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 <std::string>::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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<std::string, tl::Variant> pcell_parameters;
|
||||
std::map<std::string, std::pair<tl::Variant, std::string> > meta_info;
|
||||
|
||||
static ProxyContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
|
||||
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
|
||||
void serialize (std::vector<std::string> &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<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
|
||||
typedef LayerIterator layer_iterator;
|
||||
typedef std::vector<MetaInfo> meta_info;
|
||||
typedef meta_info::const_iterator meta_info_iterator;
|
||||
typedef size_t meta_info_name_id_type;
|
||||
typedef std::map<meta_info_name_id_type, MetaInfo> 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 <std::string> &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 <std::string>::const_iterator from, std::vector <std::string>::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 <std::string> &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 <std::string>::const_iterator from, std::vector <std::string>::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 <class I>
|
||||
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 <class I>
|
||||
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<std::string, meta_info_name_id_type> m_meta_info_name_map;
|
||||
std::vector<std::string> m_meta_info_names;
|
||||
meta_info_map m_meta_info;
|
||||
std::map<db::cell_index_type, meta_info_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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ merge_layouts (db::Layout &target,
|
|||
std::map<db::cell_index_type, db::cell_index_type> new_cell_mapping;
|
||||
for (std::set<db::cell_index_type>::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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<std::string> 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 ()) {
|
||||
|
|
|
|||
|
|
@ -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<std::string, tl::Variant> params = pv;
|
||||
std::vector<size_t> terminal_order;
|
||||
|
||||
size_t defp = std::numeric_limits<size_t>::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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<db::Cell> 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 "
|
||||
|
|
|
|||
|
|
@ -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<db::MetaInfo> 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<db::Layout> 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<void (db::Layout::*) ()> (&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<void (db::Layout::*) (const std::string &name)> (&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"
|
||||
|
|
|
|||
|
|
@ -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<MetaInfo> 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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -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 <string>
|
||||
#include <iterator>
|
||||
|
||||
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<db::Layout *> (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<db::Layout> mp_layout;
|
||||
db::Layout::meta_info_iterator m_b, m_e;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -66,6 +66,9 @@ public:
|
|||
void begin_edge_differences ();
|
||||
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b);
|
||||
void end_edge_differences ();
|
||||
void begin_edge_pair_differences ();
|
||||
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &b);
|
||||
void end_edge_pair_differences ();
|
||||
void begin_text_differences ();
|
||||
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &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 <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -243,9 +243,14 @@ insert_point_path (const db::Path &p, const std::set<EdgeWithIndex> &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 <bool, db::Point> projected = db::Edge (p1, p2).projected (ins);
|
||||
db::Edge e (p1, p2);
|
||||
std::pair <bool, db::Point> 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<EdgeWithIndex> &sel, db::Po
|
|||
}
|
||||
|
||||
static void
|
||||
remove_redundant_points (std::vector <db::Point> &ctr)
|
||||
remove_redundant_points (std::vector <db::Point> &ctr, bool cyclic)
|
||||
{
|
||||
// compress contour (remove redundant points)
|
||||
// and assign to path
|
||||
|
||||
std::vector<db::Point>::iterator wp = ctr.begin ();
|
||||
std::vector<db::Point>::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<EdgeWithIndex> &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 <PointWithIndex, db::Point> &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<EdgeWithIndex> &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 <bool, db::Point> 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<EdgeWithIndex> &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<EdgeWithIndex> &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<int>::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<int>::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 <EdgeWithIndex> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ());
|
||||
if (tv_list != 0) {
|
||||
for (std::vector<db::DCplxTrans>::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 ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -651,11 +651,26 @@ static void collect_classes (const gsi::ClassBase *cls, std::list<const gsi::Cla
|
|||
{
|
||||
unsorted_classes.push_back (cls);
|
||||
|
||||
for (tl::weak_collection<gsi::ClassBase>::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<const gsi::ClassBase *> &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<const gsi::ClassBase *>
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -94,10 +94,7 @@
|
|||
<item>
|
||||
<widget class="QToolButton" name="newFolderButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>New folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -121,10 +118,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="addButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>New</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -141,10 +135,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="deleteButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -161,10 +152,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="renameButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Rename</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rename</string>
|
||||
|
|
@ -181,10 +169,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="importButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Import file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
|
|
@ -208,7 +193,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="saveAllButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Save all files (Ctrl+Shift+S)</p></body></html></string>
|
||||
<string>Save all files (Ctrl+Shift+S)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -228,7 +213,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="saveButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Save current file (Ctrl+S)</p></body></html></string>
|
||||
<string>Save current file (Ctrl+S)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -333,8 +318,8 @@ p, li { white-space: pre-wrap; }
|
|||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/back_16.png</normaloff>:/back_16.png</iconset>
|
||||
<iconset resource="../../icons/icons.qrc">
|
||||
<normaloff>:/back_16px.png</normaloff>:/back_16px.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
|
|
@ -347,8 +332,8 @@ p, li { white-space: pre-wrap; }
|
|||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/forward_16.png</normaloff>:/forward_16.png</iconset>
|
||||
<iconset resource="../../icons/icons.qrc">
|
||||
<normaloff>:/forward_16px.png</normaloff>:/forward_16px.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
|
|
@ -365,10 +350,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="closeButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Close tab</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>close</string>
|
||||
|
|
@ -432,10 +414,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="stopButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Stop script</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -452,10 +431,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="pauseButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Pause script (Ctrl+F5)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -482,10 +458,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="breakpointButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Set breakpoint (F9)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -505,10 +478,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="clearBreakpointsButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Clear all breakpoints (Ctrl+Shift+F9)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -535,10 +505,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="singleStepButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Step into procedure (F11)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>S</string>
|
||||
|
|
@ -558,10 +525,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="nextStepButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Step over procedure or block (F10)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>N</string>
|
||||
|
|
@ -588,10 +552,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="dbgOn">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Enable or disable debugging</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DBG</string>
|
||||
|
|
@ -618,10 +579,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="propertiesButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Edit properties of macro</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>P</string>
|
||||
|
|
@ -638,10 +596,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="setupButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Setup colors, formats, debugger</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>prop</string>
|
||||
|
|
@ -674,10 +629,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="findNextButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Find next (Ctrl+F)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>N</string>
|
||||
|
|
@ -687,7 +639,7 @@ p, li { white-space: pre-wrap; }
|
|||
<normaloff>:/find_16px.png</normaloff>:/find_16px.png</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F3</string>
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
|
|
@ -709,10 +661,7 @@ p, li { white-space: pre-wrap; }
|
|||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Show replace mode</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
@ -764,10 +713,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="replaceNextButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Replace and find next (Ctrl+R)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RN</string>
|
||||
|
|
@ -777,7 +723,7 @@ p, li { white-space: pre-wrap; }
|
|||
<normaloff>:/replace_16px.png</normaloff>:/replace_16px.png</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string/>
|
||||
<string>Ctrl+R</string>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
|
|
@ -787,10 +733,7 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QToolButton" name="replaceAllButton">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string>Replace all (Ctrl+Shift+R)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
|
|
@ -799,6 +742,9 @@ p, li { white-space: pre-wrap; }
|
|||
<iconset resource="../../icons/icons.qrc">
|
||||
<normaloff>:/replace_all_16px.png</normaloff>:/replace_all_16px.png</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+R</string>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
|
@ -1497,14 +1443,6 @@ p, li { white-space: pre-wrap; }
|
|||
<string>Case Sensitive</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSearchReplace">
|
||||
<property name="text">
|
||||
<string>Search & Replace</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveAs">
|
||||
<property name="text">
|
||||
<string>Save As</string>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -791,35 +791,6 @@ Bitmap::render_contour (std::vector<lay::RenderEdge> &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 ());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<db::ICplxTrans> &trans, const db::Box ®ion, int min_level, int max_level, const std::vector<int> &layers)
|
||||
Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector<db::DCplxTrans> &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector<int> &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 <db::CellInst> (*mp_layout, (unsigned int) layers [0]);
|
||||
m_cell_box_convert = db::box_convert <db::Cell> ((unsigned int) layers [0]);
|
||||
|
||||
} else {
|
||||
|
||||
m_box_convert = db::box_convert <db::CellInst> (*mp_layout);
|
||||
m_cell_box_convert = db::box_convert <db::Cell> ();
|
||||
|
||||
}
|
||||
|
||||
m_path.erase (m_path.begin (), m_path.end ());
|
||||
|
||||
for (std::vector<db::ICplxTrans>::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<db::DCplxTrans>::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<lay::LayerPropertiesConstIterator> 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<int> 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<db::ICplxTrans> trans;
|
||||
trans.reserve(trans_mu.size());
|
||||
for (std::vector<db::DCplxTrans>::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<int>::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<int>::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<db::ICplxTrans> 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<int> ());
|
||||
std::vector<db::DCplxTrans> 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<int> ());
|
||||
} 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<db::ICplxTrans> &trans, const db::Box ®ion, int min_level, int max_level, const std::vector<int> &layers = std::vector<int> ());
|
||||
void start (LayoutViewBase *view, unsigned int cv_index, const std::vector<db::DCplxTrans> &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector<int> &layers = std::vector<int> ());
|
||||
|
||||
/**
|
||||
* @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<db::InstElement> 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<int> 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<db::properties_id_type> *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<db::properties_id_type> *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;
|
||||
|
|
|
|||
|
|
@ -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<ViewOp> &scaled_view_ops (unsigned int lw);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<db::DCplxTrans>::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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
<item row="1" column="0">
|
||||
<widget class="QTabWidget" name="mode_tab">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
|
|
@ -186,6 +186,48 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>Meta Info</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Meta Info is additional system data shown here for information. Entries marked with a "*" are persisted in the layout file.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="meta_info_list">
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Key</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 <QDialog>
|
||||
|
|
@ -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<lay::GenericSyntaxHighlighterAttributes> mp_hl_attributes, mp_hl_basic_attributes;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<QPair<QString, QString> > queryItems = QUrlQuery (url.query ()).queryItems ();
|
||||
#else
|
||||
QList<QPair<QString, QString> > queryItems = url.queryItems ();
|
||||
#endif
|
||||
for (QList<QPair<QString, QString> >::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<db::Shape::object_type, size_t> 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<std::string, std::string> 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 <unsigned int> 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 << "<html>" << std::endl
|
||||
<< "<body>" << std::endl
|
||||
<< "<h2>" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'</h2>" << std::endl
|
||||
|
||||
const db::Layout &layout = m_h->layout ();
|
||||
<< "<p>" << std::endl
|
||||
<< "<table cellspacing=\"5\" cellpadding=\"5\">" << std::endl
|
||||
|
||||
std::ostringstream os;
|
||||
os.imbue (std::locale ("C"));
|
||||
<< "<tr>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Layer")) << "</th>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("All")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Boxes")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Polygons")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Paths")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Texts")) << "</th>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Edges")) << "</th>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Edge Pairs")) << "</th>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("User objects")) << "</th>" << std::endl
|
||||
<< "</tr>" << std::endl
|
||||
|
||||
<< "<tr>" << std::endl
|
||||
<< "<th></th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th>" << std::endl
|
||||
<< "</tr>" << std::endl
|
||||
;
|
||||
|
||||
db::CellCounter cc (&layout);
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000);
|
||||
for (std::vector <unsigned int>::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 <unsigned int> 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 << "<html>" << std::endl
|
||||
<< "<body>" << std::endl
|
||||
<< "<h2>" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'</h2>" << std::endl
|
||||
|
||||
<< "<p>" << std::endl
|
||||
<< "<table cellspacing=\"5\" cellpadding=\"5\">" << std::endl
|
||||
|
||||
<< "<tr>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Layer")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Boxes")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Polygons")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Paths")) << "</th>" << std::endl
|
||||
<< "<th colspan=\"3\" bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Texts")) << "</th>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("Edges")) << "</th>" << std::endl
|
||||
<< "<th bgcolor=\"#f0f0f0\">" << tl::to_string (QObject::tr ("User objects")) << "</th>" << std::endl
|
||||
<< "</tr>" << std::endl
|
||||
|
||||
<< "<tr>" << std::endl
|
||||
<< "<th></th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th><th>" << tl::to_string (QObject::tr ("(single)")) << "</th><th>" << tl::to_string (QObject::tr ("(arrays)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th>" << std::endl
|
||||
<< "<th>" << tl::to_string (QObject::tr ("(total)")) << "</th>" << std::endl
|
||||
os << "<tr>" << std::endl
|
||||
<< "<td>" << tl::escaped_to_html (layout.get_properties (*l).to_string (), true) << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.all_total () << "<br></br>" << st_flat.all_total () << "</td>" << std::endl
|
||||
// Boxes (total, single, array)
|
||||
<< "<td>" << st_hier.box_total () << "<br></br>" << st_flat.box_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.box_single () << "<br></br>" << st_flat.box_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.box_array () << "<br></br>" << st_flat.box_array () << "</td>" << std::endl
|
||||
// Polygons (total, single, array)
|
||||
<< "<td>" << st_hier.polygon_total () << "<br></br>" << st_flat.polygon_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.polygon_single () << "<br></br>" << st_flat.polygon_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.polygon_array () << "<br></br>" << st_flat.polygon_array () << "</td>" << std::endl
|
||||
// Paths (total, single, array)
|
||||
<< "<td>" << st_hier.path_total () << "<br></br>" << st_flat.path_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.path_single () << "<br></br>" << st_flat.path_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.path_array () << "<br></br>" << st_flat.path_array () << "</td>" << std::endl
|
||||
// Texts (total, single, array)
|
||||
<< "<td>" << st_hier.text_total () << "<br></br>" << st_flat.text_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.text_single () << "<br></br>" << st_flat.text_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.text_array () << "<br></br>" << st_flat.text_array () << "</td>" << std::endl
|
||||
// Edges (total)
|
||||
<< "<td>" << st_hier.edge_total () << "<br></br>" << st_flat.edge_total () << "</td>" << std::endl
|
||||
// EdgePairs (total)
|
||||
<< "<td>" << st_hier.edge_pair_total () << "<br></br>" << st_flat.edge_pair_total () << "</td>" << std::endl
|
||||
// User objects (total)
|
||||
<< "<td>" << st_hier.user_total () << "<br></br>" << st_flat.user_total () << "</td>" << std::endl
|
||||
// ...
|
||||
<< "<td>" << tl::to_string (QObject::tr ("(hier)")) << "<br></br>" << tl::to_string (QObject::tr ("(flat)")) << "</td>" << std::endl
|
||||
<< "</tr>" << std::endl
|
||||
;
|
||||
|
||||
db::CellCounter cc (&layout);
|
||||
}
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000);
|
||||
for (std::vector <unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
os << "</table>" << std::endl
|
||||
<< "</p>" << std::endl
|
||||
<< tl::to_string (QObject::tr ("<h4>Note</h4>"
|
||||
"<p>"
|
||||
"\"(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."
|
||||
"</p>"
|
||||
"<p>"
|
||||
"\"(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)\"."
|
||||
"</p>"
|
||||
))
|
||||
<< "</body>" << std::endl
|
||||
<< "</html>";
|
||||
|
||||
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 << "<html>" << std::endl
|
||||
<< "<body>" << std::endl
|
||||
<< "<h2>" << tl::to_string (QObject::tr ("Common Statistics For '")) << tl::escaped_to_html (m_h->name (), true) << "'</h2>" << std::endl
|
||||
<< "<p>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Path")) << ": </td><td>" << tl::escaped_to_html (m_h->filename (), true) << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
if (! m_h->save_options ().format ().empty ()) {
|
||||
os << "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Format")) << ": </td><td>" << tl::escaped_to_html (m_h->save_options ().format (), true) << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
}
|
||||
os << "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Technology")) << ": </td><td>" << tl::escaped_to_html (m_h->technology ()->description (), true) << tl::escaped_to_html (format_tech_name (m_h->tech_name ()), true) << "</td>"
|
||||
<< "</tr>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Database unit")) << ": </td><td>" << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "</td>"
|
||||
<< "</tr>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Number of cells")) << ": </td><td>" << num_cells << "</td>"
|
||||
<< "</tr>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Number of layers")) << ": </td><td>" << num_layers << "</td>"
|
||||
<< "</tr>" << 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 << "<tr><td>" << tl::escaped_to_html (d, true) << "</td><td>" << tl::escaped_to_html (meta->second.value.to_string (), true) << "</td></tr>" << std::endl;
|
||||
}
|
||||
os << "</table>" << std::endl
|
||||
<< "<h2>" << tl::to_string (QObject::tr ("Top Cells")) << "</h2>" << std::endl
|
||||
<< "<table>" << std::endl;
|
||||
for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) {
|
||||
os << "<tr><td>" << tl::escaped_to_html (layout.cell_name (*tc), true) << "</td></tr>" << std::endl;
|
||||
}
|
||||
os << "</table>" << std::endl;
|
||||
os << "</p>" << std::endl;
|
||||
|
||||
std::vector <unsigned int> layers_with_oasis_names;
|
||||
|
||||
std::vector <unsigned int> 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 << "<h2>" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "</h2>" << std::endl
|
||||
<< "<p><a href=\"" << tl::escaped_to_html (tl::to_string (s_per_layer_stat_path_ld), true) << "\">Detailed layer statistics</a></p>" << std::endl
|
||||
<< "<p>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< "<tr><td><b>" << tl::to_string (QObject::tr ("Layer/Datatype")) << "</b> </td>";
|
||||
if (! layers_with_oasis_names.empty ()) {
|
||||
os << "<td><b>" << tl::to_string (QObject::tr ("Layer name")) << "</b></td>";
|
||||
}
|
||||
if (with_shape_statistics) {
|
||||
os << "<td><b>" << tl::to_string (QObject::tr ("Shape count (hier)")) << "</b></td>";
|
||||
os << "<td><b>" << tl::to_string (QObject::tr ("Shape count (flat)")) << "</b></td>";
|
||||
}
|
||||
os << "</tr>" << std::endl;
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers_sorted_by_ld.size () * layout.cells (), 100000);
|
||||
for (std::vector <unsigned int>::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 << "<tr>" << std::endl
|
||||
<< "<td>" << layout.get_properties (*l).to_string () << "</td>" << std::endl
|
||||
// Boxes (total, single, array)
|
||||
<< "<td>" << st_hier.box_total () << "<br></br>" << st_flat.box_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.box_single () << "<br></br>" << st_flat.box_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.box_array () << "<br></br>" << st_flat.box_array () << "</td>" << std::endl
|
||||
// Polygons (total, single, array)
|
||||
<< "<td>" << st_hier.polygon_total () << "<br></br>" << st_flat.polygon_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.polygon_single () << "<br></br>" << st_flat.polygon_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.polygon_array () << "<br></br>" << st_flat.polygon_array () << "</td>" << std::endl
|
||||
// Paths (total, single, array)
|
||||
<< "<td>" << st_hier.path_total () << "<br></br>" << st_flat.path_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.path_single () << "<br></br>" << st_flat.path_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.path_array () << "<br></br>" << st_flat.path_array () << "</td>" << std::endl
|
||||
// Texts (total, single, array)
|
||||
<< "<td>" << st_hier.text_total () << "<br></br>" << st_flat.text_total () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.text_single () << "<br></br>" << st_flat.text_single () << "</td>" << std::endl
|
||||
<< "<td>" << st_hier.text_array () << "<br></br>" << st_flat.text_array () << "</td>" << std::endl
|
||||
// Edges (total)
|
||||
<< "<td>" << st_hier.edge_total () << "<br></br>" << st_flat.edge_total () << "</td>" << std::endl
|
||||
// User objects (total)
|
||||
<< "<td>" << st_hier.user_total () << "<br></br>" << st_flat.user_total () << "</td>" << std::endl
|
||||
// ...
|
||||
<< "<td>" << tl::to_string (QObject::tr ("(hier)")) << "<br></br>" << tl::to_string (QObject::tr ("(flat)")) << "</td>" << std::endl
|
||||
<< "</tr>" << std::endl
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
os << "</table>" << std::endl
|
||||
<< "</p>" << std::endl
|
||||
<< tl::to_string (QObject::tr ("<h4>Note</h4>"
|
||||
"<p>"
|
||||
"\"(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."
|
||||
"</p>"
|
||||
"<p>"
|
||||
"\"(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)\"."
|
||||
"</p>"
|
||||
))
|
||||
<< "</body>" << std::endl
|
||||
<< "</html>";
|
||||
|
||||
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 << "<html>" << std::endl
|
||||
<< "<body>" << std::endl
|
||||
<< "<h2>" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'</h2>" << std::endl
|
||||
<< "<p>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Path")) << ": </td><td>" << m_h->filename () << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
if (! m_h->save_options ().format ().empty ()) {
|
||||
os << "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Format")) << ": </td><td>" << m_h->save_options ().format () << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
}
|
||||
os << "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Technology")) << ": </td><td>" << m_h->technology ()->description () << format_tech_name (m_h->tech_name ()) << "</td>"
|
||||
<< "</tr>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Database unit")) << ": </td><td>" << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "</td>"
|
||||
<< "</tr>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Number of cells")) << ": </td><td>" << num_cells << "</td>"
|
||||
<< "</tr>" << std::endl
|
||||
<< "<tr>"
|
||||
<< "<td>" << tl::to_string (QObject::tr ("Number of layers")) << ": </td><td>" << num_layers << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) {
|
||||
os << "<tr><td>" << meta->description << "</td><td>" << meta->value << "</td></tr>" << std::endl;
|
||||
}
|
||||
os << "</table>" << std::endl
|
||||
<< "<h2>" << tl::to_string (QObject::tr ("Top Cells")) << "</h2>" << std::endl
|
||||
<< "<table>" << std::endl;
|
||||
for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) {
|
||||
os << "<tr><td>" << layout.cell_name (*tc) << "</td></tr>" << std::endl;
|
||||
}
|
||||
os << "</table>" << std::endl;
|
||||
os << "</p>" << std::endl;
|
||||
|
||||
std::vector <unsigned int> layers_with_oasis_names;
|
||||
|
||||
std::vector <unsigned int> 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 << "<h2>" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "</h2>" << std::endl
|
||||
<< "<p><a href=\"" << tl::to_string (s_per_layer_stat_path_ld) << "\">Detailed layer statistics</a></p>" << std::endl
|
||||
<< "<p>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< "<tr><td><b>" << tl::to_string (QObject::tr ("Layer/Datatype")) << "</b> </td>";
|
||||
const db::LayerProperties &lp = layout.get_properties (*i);
|
||||
os << "<tr>"
|
||||
<< "<td>" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "</td>";
|
||||
if (! layers_with_oasis_names.empty ()) {
|
||||
os << "<td><b>" << tl::to_string (QObject::tr ("Layer name")) << "</b></td>";
|
||||
os << "<td>" << tl::escaped_to_html (lp.name, true) << "</td>";
|
||||
}
|
||||
if (with_shape_statistics) {
|
||||
os << "<td>" << st_hier.all_total () << "</td>";
|
||||
os << "<td>" << st_flat.all_total () << "</td>";
|
||||
}
|
||||
os << "</tr>" << std::endl;
|
||||
|
||||
for (std::vector <unsigned int>::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 << "</table>" << std::endl;
|
||||
os << "</p>" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
if (! layers_with_oasis_names.empty ()) {
|
||||
|
||||
os << "<h2>" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "</h2>" << std::endl
|
||||
<< "<p><a href=\"" << tl::escaped_to_html (tl::to_string (s_per_layer_stat_path_name), true) << "\">Detailed layer statistics</a></p>" << std::endl
|
||||
<< "<p>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< "<tr><td><b>" << tl::to_string (QObject::tr ("Layer name")) << "</b> </td><td><b>" << tl::to_string (QObject::tr ("Layer/Datatype")) << "</b></td></tr>" << std::endl;
|
||||
|
||||
for (std::vector <unsigned int>::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 << "<tr>"
|
||||
<< "<td>" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "</td>";
|
||||
if (! layers_with_oasis_names.empty ()) {
|
||||
os << "<td>" << lp.name << "</td>";
|
||||
}
|
||||
os << "</tr>" << std::endl;
|
||||
<< "<td>" << tl::escaped_to_html (lp.name, true) << "</td>"
|
||||
<< "<td>" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
os << "</table>" << std::endl;
|
||||
os << "</p>" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
if (! layers_with_oasis_names.empty ()) {
|
||||
os << "</table>" << std::endl;
|
||||
os << "</p>" << std::endl;
|
||||
|
||||
os << "<h2>" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "</h2>" << std::endl
|
||||
<< "<p><a href=\"" << tl::to_string (s_per_layer_stat_path_name) << "\">Detailed layer statistics</a></p>" << std::endl
|
||||
<< "<p>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< "<tr><td><b>" << tl::to_string (QObject::tr ("Layer name")) << "</b> </td><td><b>" << tl::to_string (QObject::tr ("Layer/Datatype")) << "</b></td></tr>" << std::endl;
|
||||
|
||||
for (std::vector <unsigned int>::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 << "<tr>"
|
||||
<< "<td>" << lp.name << "</td>"
|
||||
<< "<td>" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "</td>"
|
||||
<< "</tr>" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os << "</table>" << std::endl;
|
||||
os << "</p>" << std::endl;
|
||||
os << "</body>" << std::endl
|
||||
<< "</html>" << std::endl;
|
||||
;
|
||||
|
||||
}
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
os << "</body>" << std::endl
|
||||
<< "</html>" << 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <tl::string, std::vector <std::string> >::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 <std::string> &strings = m_context_info.insert (std::make_pair (cn, std::vector <std::string> ())).first->second;
|
||||
|
||||
size_t attr = 0;
|
||||
|
|
|
|||
|
|
@ -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<db::cell_index_type> &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 <std::string> 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 <std::string>::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 <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
|
||||
|
||||
write_string_record (sPROPVALUE, *s);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
write_record_size (4);
|
||||
write_record (sENDEL);
|
||||
|
||||
}
|
||||
|
||||
for (std::vector<db::cell_index_type>::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 <std::string>::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 <std::string>::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<db::GDS2WriterOptions> ();
|
||||
|
||||
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 <std::pair <unsigned int, db::LayerProperties> > 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<db::cell_index_type>::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<db::cell_index_type>::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 <std::string> context_prop_strings;
|
||||
|
||||
for (std::vector<db::cell_index_type>::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 <std::string>::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 <std::string>::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
|
||||
|
|
|
|||
|
|
@ -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<cell_index_type> &cells);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
|
|
|||
|
|
@ -145,9 +145,10 @@ public:
|
|||
// Initialize the libname property from meta data with key "libname".
|
||||
db::GDS2WriterOptions *options = dynamic_cast<db::GDS2WriterOptions *> (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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -172,6 +172,8 @@ private:
|
|||
std::map <unsigned long, std::string> m_propstrings;
|
||||
std::map <unsigned long, std::string> m_propnames;
|
||||
|
||||
std::map <db::cell_index_type, std::vector<tl::Variant> > m_context_strings_per_cell;
|
||||
|
||||
tl::vector<db::CellInstArray> m_instances;
|
||||
tl::vector<db::CellInstArrayWithProperties> 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 <bool, db::properties_id_type> read_element_properties (db::PropertiesRepository &rep, bool ignore_special);
|
||||
void replace_forward_references_in_variant (tl::Variant &v);
|
||||
|
||||
unsigned char get_byte ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<db::cell_index_type> &cells, const db::Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers)
|
||||
{
|
||||
// write the property names collected so far in the order of the ID's.
|
||||
|
||||
std::vector<std::pair<unsigned long, std::string> > 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 <db::properties_id_type> 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<db::cell_index_type> &cells, const db::Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers)
|
||||
{
|
||||
// write the property strings collected so far in the order of the ID's.
|
||||
|
||||
std::vector<std::pair<unsigned long, const std::string *> > 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 <db::properties_id_type> 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 <std::string> 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<db::cell_index_type> &cells_by_index, const std::map<db::cell_index_type, size_t> *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<tl::Variant> 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<db::cell_index_type, size_t>::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<db::cell_index_type> &cells, const db::Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers)
|
||||
{
|
||||
// write present text strings
|
||||
|
||||
// collect present strings by ID
|
||||
std::vector<std::pair<unsigned long, const std::string *> > 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 <std::pair <unsigned int, db::LayerProperties> > &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<OASISWriterOptions> ();
|
||||
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<std::pair<std::string, unsigned int> > 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<std::pair<unsigned long, std::string> > rev_pn;
|
||||
rev_pn.reserve (m_propnames.size ());
|
||||
for (std::map <std::string, unsigned long>::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<std::pair<unsigned long, std::string> >::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 <db::properties_id_type> prop_ids_done;
|
||||
|
||||
for (std::vector<db::cell_index_type>::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 <std::pair <unsigned int, db::LayerProperties> >::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 <std::string> context_prop_strings;
|
||||
for (std::vector<db::cell_index_type>::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<std::pair<unsigned long, std::string> > rev_ps;
|
||||
rev_ps.reserve (m_propstrings.size ());
|
||||
for (std::map <std::string, unsigned long>::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<std::pair<unsigned long, std::string> >::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 <db::properties_id_type> prop_ids_done;
|
||||
|
||||
for (std::vector<db::cell_index_type>::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 <std::pair <unsigned int, db::LayerProperties> >::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 <std::string> context_prop_strings;
|
||||
for (std::vector<db::cell_index_type>::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 <std::string>::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<db::cell_index_type>::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<db::cell_index_type>::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<tl::Variant> 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<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
|
||||
|
||||
const db::Cell &cref (layout.cell (*cell));
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::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 <std::pair <unsigned int, db::LayerProperties> >::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 <std::string> 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<tl::Variant> 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<db::cell_index_type>::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 <std::string, unsigned long>::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 <std::string>::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 <std::string, unsigned long>::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<cell_index_type> no_cells;
|
||||
std::vector <std::pair <unsigned int, db::LayerProperties> > 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<db::cell_index_type>::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<db::cell_index_type>::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<tl::Variant> 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<db::cell_index_type, size_t>::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 <std::string, unsigned long>::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;
|
||||
|
||||
|
|
|
|||
|
|
@ -203,12 +203,14 @@ private:
|
|||
const db::Cell *mp_cell;
|
||||
int m_layer;
|
||||
int m_datatype;
|
||||
bool m_write_context_info;
|
||||
std::vector<db::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 <std::string, unsigned long> m_textstrings;
|
||||
|
|
@ -308,6 +310,12 @@ private:
|
|||
void write_pointlist (const std::vector<db::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<db::cell_index_type> &cells, const Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
|
||||
void write_propstring_table (size_t &propstrings_table_pos, const std::vector<db::cell_index_type> &cells, const Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
|
||||
void write_cellname_table (size_t &cellnames_table_pos, const std::vector<db::cell_index_type> &cells_by_index, const std::map<cell_index_type, size_t> *cell_positions, const Layout &layout);
|
||||
void write_textstring_table (size_t &textstrings_table_pos, const std::vector<db::cell_index_type> &cells, const Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
|
||||
void write_layername_table (size_t &layernames_table_pos, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -210,6 +210,22 @@ inline bool safe_isspace (char c)
|
|||
return c != 0 && static_cast<unsigned char> (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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue