Merge pull request #1358 from KLayout/wip2

Wip2
This commit is contained in:
Matthias Köfferlein 2023-05-12 23:31:26 +02:00 committed by GitHub
commit 641d2404d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 3653 additions and 1464 deletions

View File

@ -124,6 +124,7 @@ SOURCES = \
gsiDeclDbLibrary.cc \
gsiDeclDbManager.cc \
gsiDeclDbMatrix.cc \
gsiDeclDbMetaInfo.cc \
gsiDeclDbPath.cc \
gsiDeclDbPoint.cc \
gsiDeclDbPolygon.cc \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
};
/**

View File

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

View File

@ -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);
};
/**

View File

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

View File

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

View File

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

View File

@ -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 ()) {

View File

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

View File

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

View File

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

View File

@ -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."
);
}

View File

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

View File

@ -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 ()
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -94,10 +94,7 @@
<item>
<widget class="QToolButton" name="newFolderButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;New folder&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;New&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;Delete&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Rename&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Import file&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save all files (Ctrl+Shift+S)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save current file (Ctrl+S)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Close tab&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Stop script&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Pause script (Ctrl+F5)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Set breakpoint (F9)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Clear all breakpoints (Ctrl+Shift+F9)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Step into procedure (F11)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Step over procedure or block (F10)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Enable or disable debugging&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Edit properties of macro&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Setup colors, formats, debugger&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Find next&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Show replace mode&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Replace and find next&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Replace all&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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 &amp; Replace</string>
</property>
<property name="shortcut">
<string>Ctrl+F</string>
</property>
</action>
<action name="actionSaveAs">
<property name="text">
<string>Save As</string>

View File

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

View File

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

View File

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

View File

@ -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 ()
{

View File

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

View File

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

View File

@ -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 &region, 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 &region, 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 &region_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

View File

@ -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 &region, 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 &region, 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 &region_mu);
unsigned int m_cv_index;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &quot;*&quot; 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>

View File

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

View File

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

View File

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

View File

@ -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")) << ":&nbsp;</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")) << ":&nbsp;</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")) << ":&nbsp;</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")) << ":&nbsp;</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")) << ":&nbsp;</td><td>" << num_cells << "</td>"
<< "</tr>" << std::endl
<< "<tr>"
<< "<td>" << tl::to_string (QObject::tr ("Number of layers")) << ":&nbsp;</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>&nbsp;&nbsp;</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")) << ":&nbsp;</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")) << ":&nbsp;</td><td>" << m_h->save_options ().format () << "</td>"
<< "</tr>" << std::endl;
}
os << "<tr>"
<< "<td>" << tl::to_string (QObject::tr ("Technology")) << ":&nbsp;</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")) << ":&nbsp;</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")) << ":&nbsp;</td><td>" << num_cells << "</td>"
<< "</tr>" << std::endl
<< "<tr>"
<< "<td>" << tl::to_string (QObject::tr ("Number of layers")) << ":&nbsp;</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>&nbsp;&nbsp;</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>&nbsp;&nbsp;</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>&nbsp;&nbsp;</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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 ()
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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