From df4221cfc90f6d4ac0f70678183dff6a1d21abda Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 20:25:30 +0200 Subject: [PATCH 01/35] First features to support cell meta data --- src/db/db/db.pro | 1 + src/db/db/dbLayout.cc | 119 +++++++++++++++++++++++++++------ src/db/db/dbLayout.h | 116 ++++++++++++++++++++++++++++++-- src/db/db/dbMetaInfo.h | 9 +-- src/db/db/gsiDeclDbCell.cc | 75 ++++++++++++++++++++- src/db/db/gsiDeclDbLayout.cc | 91 +++++-------------------- src/db/db/gsiDeclDbMetaInfo.cc | 105 +++++++++++++++++++++++++++++ src/db/db/gsiDeclDbMetaInfo.h | 95 ++++++++++++++++++++++++++ 8 files changed, 503 insertions(+), 108 deletions(-) create mode 100644 src/db/db/gsiDeclDbMetaInfo.cc create mode 100644 src/db/db/gsiDeclDbMetaInfo.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 066dbb5d2..6914e12fa 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -124,6 +124,7 @@ SOURCES = \ gsiDeclDbLibrary.cc \ gsiDeclDbManager.cc \ gsiDeclDbMatrix.cc \ + gsiDeclDbMetaInfo.cc \ gsiDeclDbPath.cc \ gsiDeclDbPoint.cc \ gsiDeclDbPolygon.cc \ diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index edeeb0800..8885fd6de 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1204,6 +1204,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 @@ -1748,6 +1753,58 @@ Layout::do_update () delete pr; } +static Layout::meta_info 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.find (ci)) { + 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.find (ci)) { + return m->second.end (); + } else { + return s_empty_meta.end (); + } +} + +const std::string & +Layout::meta_info_name (Layout::meta_info_name_id_type name_id) const +{ + static std::string empty; + return name_id < m_meta_info_names.size () ? m_meta_info_names[name_id] : empty; +} + +Layout::meta_info_name_id_type +Layout::meta_info_name_id (const std::string &name) +{ + auto n = m_meta_info_name_map.find (name); + if (n != m_meta_info_name_map.end ()) { + return n->second; + } else { + size_t id = m_meta_info_names.size (); + m_meta_info_names.push_back (name); + m_meta_info_name_map.insert (std::make_pair (name, id)); + return id; + } +} + +Layout::meta_info_name_id_type +Layout::meta_info_name_id (const std::string &name) const +{ + auto n = m_meta_info_name_map.find (name); + return n != m_meta_info_name_map.end () ? n->second : std::numeric_limits::max (); +} + void Layout::clear_meta () { @@ -1755,39 +1812,59 @@ 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 tl::Variant & +Layout::meta_info_value (meta_info_name_id_type name_id) const +{ + auto n = m_meta_info.find (name_id); + static tl::Variant null_value; + return n != m_meta_info.end () ? n->second.value : null_value; +} + +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 tl::Variant & +Layout::meta_info_value (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.value; } } - static const std::string s_empty; - return s_empty; + static tl::Variant null_value; + return null_value; } void diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 232f52465..265561f6b 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -465,7 +465,8 @@ public: typedef pcell_name_map::const_iterator pcell_iterator; typedef std::map, cell_index_type> lib_proxy_map; typedef LayerIterator layer_iterator; - typedef std::vector meta_info; + typedef size_t meta_info_name_id_type; + typedef std::map meta_info; typedef meta_info::const_iterator meta_info_iterator; /** @@ -1812,6 +1813,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 +1845,98 @@ 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 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 tl::Variant &meta_info_value (const std::string &name) const + { + return meta_info_value (meta_info_name_id (name)); + } + + /** + * @brief Gets the meta info value for a meta info object with the given name ID + */ + const tl::Variant &meta_info_value (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 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 tl::Variant &meta_info_value (db::cell_index_type ci, const std::string &name) const + { + return meta_info_value (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 tl::Variant &meta_info_value (db::cell_index_type ci, meta_info_name_id_type name_id) const; /** * @brief This event is triggered when the technology changes @@ -1876,7 +1978,11 @@ private: lib_proxy_map m_lib_proxy_map; bool m_do_cleanup; bool m_editable; + std::map m_meta_info_name_map; + std::vector m_meta_info_names; meta_info m_meta_info; + std::map m_meta_info_by_cell; + std::string m_tech_name; tl::Mutex m_lock; diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index e5410751e..9a3778f6a 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -36,14 +36,13 @@ namespace db * * 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) + : description (d), value (v) { // .. nothing else .. } @@ -53,9 +52,8 @@ struct DB_PUBLIC MetaInfo // .. nothing else .. } - std::string name; std::string description; - std::string value; + tl::Variant value; }; /** @@ -63,7 +61,6 @@ struct DB_PUBLIC MetaInfo */ inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const MetaInfo &v, bool no_self, void *parent) { - db::mem_stat (stat, purpose, cat, v.name, no_self, parent); db::mem_stat (stat, purpose, cat, v.description, no_self, parent); db::mem_stat (stat, purpose, cat, v.value, no_self, parent); } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 00db50666..fd86ac3a8 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -20,9 +20,8 @@ */ - - #include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" #include "gsiDeclDbHelpers.h" #include "dbLayout.h" @@ -984,6 +983,46 @@ 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_value (cell->cell_index (), name); + } +} + +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 +1819,38 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.23." ) + + gsi::method_ext ("add_meta_info", &cell_add_meta_info, gsi::arg ("info"), + "@brief Adds meta information to the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.7." + ) + + 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.7." + ) + + 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.7." + ) + + 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, an nil value will be returned.\n" + "\n" + "This method has been introduced in version 0.28.7." + ) + + 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.7." + ) + gsi::method_ext ("write", &write_simple, gsi::arg ("file_name"), "@brief Writes the cell to a layout file\n" "The format of the file will be determined from the file name. Only the cell and " diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index bff85e070..de20c8e6f 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" #include "dbLayout.h" #include "dbClip.h" #include "dbRecursiveShapeIterator.h" @@ -902,39 +903,14 @@ 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 MetaInfoIterator layout_each_meta_info (const db::Layout *layout) { - mi->name = n; -} - -static const std::string &layout_meta_get_name (const db::MetaInfo *mi) -{ - return mi->name; -} - -static void layout_meta_set_value (db::MetaInfo *mi, const std::string &n) -{ - 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 +973,6 @@ static void move_tree_shapes3 (db::Layout *layout, db::Layout &source_layout, co db::move_shapes (*layout, source_layout, trans, cm.source_cells (), cm.table (), lm.table ()); } -Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", - gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), - "@brief Creates a layout meta info object\n" - "@param name The name\n" - "@param value The value\n" - "@param description An optional description text\n" - ) + - gsi::method_ext ("name", &layout_meta_get_name, - "@brief Gets the name of the layout meta info object\n" - ) + - gsi::method_ext ("name=", &layout_meta_set_name, - "@brief Sets the name of the layout meta info object\n" - ) + - gsi::method_ext ("value", &layout_meta_get_value, - "@brief Gets the value of the layout meta info object\n" - ) + - gsi::method_ext ("value=", &layout_meta_set_value, - "@brief Sets the value of the layout meta info object\n" - ) + - gsi::method_ext ("description", &layout_meta_get_description, - "@brief Gets the description of the layout meta info object\n" - ) + - gsi::method_ext ("description=", &layout_meta_set_description, - "@brief Sets the description of the layout meta info object\n" - ), - "@brief A piece of layout meta information\n" - "Layout meta information is basically additional data that can be attached to a layout. " - "Layout readers may generate meta information and some writers will add layout information to " - "the layout object. Some writers will also read meta information to determine certain attributes.\n" - "\n" - "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. " - "Meta information is identified by a unique name and carries a string value plus an optional description string. " - "The description string is for information only and is not evaluated by code.\n" - "\n" - "See also \\Layout#each_meta_info and \\Layout#meta_info_value and \\Layout#remove_meta_info" - "\n" - "This class has been introduced in version 0.25." -); - static void dtransform (db::Layout *layout, const db::DTrans &trans) { db::CplxTrans dbu_trans (layout->dbu ()); @@ -1097,27 +1034,33 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.27.9." ) + - gsi::method ("add_meta_info", &db::Layout::add_meta_info, gsi::arg ("info"), + gsi::method_ext ("add_meta_info", &layout_add_meta_info, gsi::arg ("info"), "@brief Adds meta information to the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("remove_meta_info", &db::Layout::remove_meta_info, gsi::arg ("name"), + gsi::method ("clear_meta_info", static_cast (&db::Layout::clear_meta), + "@brief Clears the meta information of the layout\n" + "See \\LayoutMetaInfo for details about layouts and meta information." + "\n" + "This method has been introduced in version 0.28.7." + ) + + gsi::method ("remove_meta_info", static_cast (&db::Layout::remove_meta_info), gsi::arg ("name"), "@brief Removes meta information from the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("meta_info_value", &db::Layout::meta_info_value, gsi::arg ("name"), + gsi::method ("meta_info_value", static_cast (&db::Layout::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, an nil value will be returned.\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.7, 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::iterator_ext ("each_meta_info", &layout_each_meta_info, "@brief Iterates over the meta information of the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information.\n" "\n" diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc new file mode 100644 index 000000000..656a6d611 --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -0,0 +1,105 @@ + +/* + + 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 std::string &value, const std::string &description) +{ + return new MetaInfo (name, description, value); +} + +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; +} + + +Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", + gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), + "@brief Creates a layout meta info object\n" + "@param name The name\n" + "@param value The value\n" + "@param description An optional description text\n" + ) + + gsi::method_ext ("name", &layout_meta_get_name, + "@brief Gets the name of the layout meta info object\n" + ) + + gsi::method_ext ("name=", &layout_meta_set_name, + "@brief Sets the name of the layout meta info object\n" + ) + + gsi::method_ext ("value", &layout_meta_get_value, + "@brief Gets the value of the layout meta info object\n" + ) + + gsi::method_ext ("value=", &layout_meta_set_value, + "@brief Sets the value of the layout meta info object\n" + ) + + gsi::method_ext ("description", &layout_meta_get_description, + "@brief Gets the description of the layout meta info object\n" + ) + + gsi::method_ext ("description=", &layout_meta_set_description, + "@brief Sets the description of the layout meta info object\n" + ), + "@brief A piece of layout meta information\n" + "Layout meta information is basically additional data that can be attached to a layout. " + "Layout readers may generate meta information and some writers will add layout information to " + "the layout object. Some writers will also read meta information to determine certain attributes.\n" + "\n" + "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. " + "Meta information is identified by a unique name and carries a string value plus an optional description string. " + "The description string is for information only and is not evaluated by code.\n" + "\n" + "See also \\Layout#each_meta_info and \\Layout#meta_info_value and \\Layout#remove_meta_info" + "\n" + "This class has been introduced in version 0.25." +); + +} diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h new file mode 100644 index 000000000..aea636b05 --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.h @@ -0,0 +1,95 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_gsiDeclDbMetaInfo +#define _HDR_gsiDeclDbMetaInfo + +#include "dbLayout.h" +#include "tlVariant.h" +#include "tlObject.h" + +#include +#include + +namespace gsi +{ + +struct MetaInfo +{ + MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v) + : name (n), description (d), value (v) + { } + + MetaInfo () + : name (), description (), value () + { } + + std::string name; + std::string description; + tl::Variant value; +}; + +struct MetaInfoIterator +{ + typedef std::forward_iterator_tag iterator_category; + typedef MetaInfo value_type; + typedef void difference_type; + typedef MetaInfo reference; + typedef void pointer; + + MetaInfoIterator () + : mp_layout (), m_b (), m_e () + { } + + MetaInfoIterator (const db::Layout *layout, db::Layout::meta_info_iterator b, db::Layout::meta_info_iterator e) + : mp_layout (const_cast (layout)), m_b (b), m_e (e) + { } + + bool at_end () const + { + return !mp_layout || m_b == m_e; + } + + void operator++ () + { + if (mp_layout) { + ++m_b; + } + } + + MetaInfo operator* () const + { + if (mp_layout) { + return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second.description, m_b->second.value); + } else { + return MetaInfo (); + } + } + +private: + tl::weak_ptr mp_layout; + db::Layout::meta_info_iterator m_b, m_e; +}; + +} + +#endif From 1a0542f15524e6422ca8ea2b0132a021e339a857 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 21:31:02 +0200 Subject: [PATCH 02/35] Fixed two compiler warnings of hidden virtual methods --- src/db/db/dbEdgesUtils.cc | 2 ++ src/db/unit_tests/dbLayoutDiffTests.cc | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index e8b006992..09b9e17c2 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -384,6 +384,8 @@ struct DetectTagEdgeSink DetectTagEdgeSink (int tag) : fail_tag (tag), result (true) { } + virtual void put (const db::Edge &) { } + virtual void put (const db::Edge &, int tag) { if (tag == fail_tag) { diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc index 9419bacdb..011d2be8d 100644 --- a/src/db/unit_tests/dbLayoutDiffTests.cc +++ b/src/db/unit_tests/dbLayoutDiffTests.cc @@ -66,6 +66,9 @@ public: void begin_edge_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_edge_differences (); + void begin_edge_pair_differences (); + void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); + void end_edge_pair_differences (); void begin_text_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_text_differences (); @@ -339,6 +342,26 @@ TestDifferenceReceiver::end_edge_differences () { } +void +TestDifferenceReceiver::begin_edge_pair_differences () +{ + m_os << "layout_diff: edge pairs differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl; +} + +void +TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b) +{ + m_os << "Not in b but in a:" << std::endl; + print_diffs (pr, a, b); + m_os << "Not in a but in b:" << std::endl; + print_diffs (pr, b, a); +} + +void +TestDifferenceReceiver::end_edge_pair_differences () +{ +} + void TestDifferenceReceiver::begin_text_differences () { From 541f9a377f1b607baf05c772ff0a485cf2f4e528 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 21:31:25 +0200 Subject: [PATCH 03/35] Small refactoring, added persisted flag for meta info --- src/db/db/dbLayout.cc | 18 ++++---- src/db/db/dbLayout.h | 20 ++++----- src/db/db/dbMetaInfo.h | 6 ++- src/db/db/gsiDeclDbCell.cc | 24 ++++++++++- src/db/db/gsiDeclDbLayout.cc | 25 ++++++++++- src/db/db/gsiDeclDbMetaInfo.cc | 32 +++++++++++--- src/db/db/gsiDeclDbMetaInfo.h | 13 ++++-- src/lay/lay/layLibraryController.cc | 5 ++- src/laybasic/laybasic/layLayoutViewBase.cc | 43 +++++++++---------- src/layui/layui/layLayoutStatisticsForm.cc | 26 ++++++----- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 10 ++--- .../gds2/db_plugin/dbGDS2WriterBase.cc | 10 ++--- .../gds2/lay_plugin/layGDS2WriterPlugin.cc | 5 ++- .../streamers/gds2/unit_tests/dbGDS2Reader.cc | 4 +- .../streamers/magic/db_plugin/dbMAGReader.cc | 8 ++-- .../streamers/magic/db_plugin/dbMAGWriter.cc | 9 ++-- .../pcb/db_plugin/dbGerberImporter.cc | 2 +- 17 files changed, 169 insertions(+), 91 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 8885fd6de..f92b19a47 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1753,7 +1753,7 @@ Layout::do_update () delete pr; } -static Layout::meta_info s_empty_meta; +static Layout::meta_info_map s_empty_meta; Layout::meta_info_iterator Layout::begin_meta (db::cell_index_type ci) const @@ -1823,12 +1823,12 @@ Layout::remove_meta_info (meta_info_name_id_type name_id) m_meta_info.erase (name_id); } -const tl::Variant & -Layout::meta_info_value (meta_info_name_id_type name_id) const +const MetaInfo & +Layout::meta_info (meta_info_name_id_type name_id) const { auto n = m_meta_info.find (name_id); - static tl::Variant null_value; - return n != m_meta_info.end () ? n->second.value : null_value; + static MetaInfo null_value; + return n != m_meta_info.end () ? n->second : null_value; } void @@ -1852,18 +1852,18 @@ Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id } } -const tl::Variant & -Layout::meta_info_value (db::cell_index_type ci, meta_info_name_id_type name_id) const +const MetaInfo & +Layout::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 ()) { auto i = c->second.find (name_id); if (i != c->second.end ()) { - return i->second.value; + return i->second; } } - static tl::Variant null_value; + static MetaInfo null_value; return null_value; } diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 265561f6b..2038ae52b 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -466,8 +466,8 @@ public: typedef std::map, cell_index_type> lib_proxy_map; typedef LayerIterator layer_iterator; typedef size_t meta_info_name_id_type; - typedef std::map meta_info; - typedef meta_info::const_iterator meta_info_iterator; + typedef std::map meta_info_map; + typedef meta_info_map::const_iterator meta_info_iterator; /** * @brief A helper functor to compare "const char *" by the content @@ -1876,15 +1876,15 @@ public: * @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 tl::Variant &meta_info_value (const std::string &name) const + const MetaInfo &meta_info (const std::string &name) const { - return meta_info_value (meta_info_name_id (name)); + 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 tl::Variant &meta_info_value (meta_info_name_id_type name_id) const; + const MetaInfo &meta_info (meta_info_name_id_type name_id) const; /** * @brief Clears the meta information for a specific cell @@ -1927,16 +1927,16 @@ public: * @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 tl::Variant &meta_info_value (db::cell_index_type ci, const std::string &name) const + const MetaInfo &meta_info (db::cell_index_type ci, const std::string &name) const { - return meta_info_value (ci, meta_info_name_id (name)); + 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 tl::Variant &meta_info_value (db::cell_index_type ci, meta_info_name_id_type name_id) const; + 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 @@ -1980,8 +1980,8 @@ private: bool m_editable; std::map m_meta_info_name_map; std::vector m_meta_info_names; - meta_info m_meta_info; - std::map m_meta_info_by_cell; + meta_info_map m_meta_info; + std::map m_meta_info_by_cell; std::string m_tech_name; tl::Mutex m_lock; diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index 9a3778f6a..16166455d 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -41,19 +41,21 @@ namespace db */ struct DB_PUBLIC MetaInfo { - MetaInfo (const std::string &d, const tl::Variant &v) - : 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 description; tl::Variant value; + bool persisted; }; /** diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index fd86ac3a8..4acfc278b 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1010,7 +1010,18 @@ static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::strin static tl::Variant null_value; return null_value; } else { - return cell->layout ()->meta_info_value (cell->cell_index (), name); + 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 ()) { + static MetaInfo null_value; + return null_value; + } else { + const db::MetaInfo &value = cell->layout ()->meta_info (cell->cell_index (), name); + return MetaInfo (name, value); } } @@ -1841,7 +1852,16 @@ Class decl_Cell ("db", "Cell", "@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, an nil value 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.28.7." + ) + + gsi::method_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.7." ) + diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index de20c8e6f..d82c7c464 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -908,6 +908,18 @@ static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi) layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value)); } +static MetaInfo layout_get_meta_info (db::Layout *layout, const std::string &name) +{ + const db::MetaInfo &value = layout->meta_info (name); + return MetaInfo (name, value); +} + +static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name) +{ + const db::MetaInfo &value = layout->meta_info (name); + return value.value; +} + static MetaInfoIterator layout_each_meta_info (const db::Layout *layout) { return MetaInfoIterator (layout, layout->begin_meta (), layout->end_meta ()); @@ -1052,14 +1064,23 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("meta_info_value", static_cast (&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 nil value 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. Starting with version 0.28.7, the value is of variant type instead of string only.\n" ) + + gsi::method_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, a default object with empty fields will be returned.\n" + "\n" + "This method has been introduced in version 0.28.7.\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" diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index 656a6d611..1e182805e 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -27,9 +27,9 @@ namespace gsi { -static MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description) +static MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description, bool persisted) { - return new MetaInfo (name, description, value); + return new MetaInfo (name, description, value, persisted); } static void layout_meta_set_name (MetaInfo *mi, const std::string &n) @@ -62,13 +62,26 @@ static const std::string &layout_meta_get_description (const MetaInfo *mi) return mi->description; } +static void layout_meta_set_persisted (MetaInfo *mi, bool f) +{ + mi->persisted = f; +} + +static bool layout_meta_get_persisted (const MetaInfo *mi) +{ + return mi->persisted; +} + Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", - gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), + gsi::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.7.\n" ) + gsi::method_ext ("name", &layout_meta_get_name, "@brief Gets the name of the layout meta info object\n" @@ -87,6 +100,14 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", ) + gsi::method_ext ("description=", &layout_meta_set_description, "@brief Sets the description of the layout meta info object\n" + ) + + gsi::method_ext ("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.7.\n" + ) + + gsi::method_ext ("persisted=", &layout_meta_set_persisted, + "@brief Sets a value indicating whether the meta information will be persisted\n" + "This predicate was introduced in version 0.28.7.\n" ), "@brief A piece of layout meta information\n" "Layout meta information is basically additional data that can be attached to a layout. " @@ -97,9 +118,10 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "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" + "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" - "This class has been introduced in version 0.25." + "This class has been introduced in version 0.25 and was extended in version 0.28.7." ); } diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h index aea636b05..84bbadc0a 100644 --- a/src/db/db/gsiDeclDbMetaInfo.h +++ b/src/db/db/gsiDeclDbMetaInfo.h @@ -35,17 +35,22 @@ namespace gsi struct MetaInfo { - MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v) - : name (n), description (d), value (v) + 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 () + : name (), description (), value (), persisted (false) { } std::string name; std::string description; tl::Variant value; + bool persisted; }; struct MetaInfoIterator @@ -79,7 +84,7 @@ struct MetaInfoIterator MetaInfo operator* () const { if (mp_layout) { - return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second.description, m_b->second.value); + return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second); } else { return MetaInfo (); } diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index 0a42bcb37..a07b857a1 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -200,9 +200,10 @@ LibraryController::sync_files () reader.read (lib->layout ()); // Use the libname if there is one + db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) { - if (m->name == "libname" && ! m->value.empty ()) { - lib->set_name (m->value); + if (m->first == libname_name_id && ! m->second.value.is_nil ()) { + lib->set_name (m->second.value.to_string ()); break; } } diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c697441fc..b59495ef3 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -3202,6 +3202,25 @@ LayoutViewBase::reload_layout (unsigned int cv_index) goto_view (state); } +static void +get_lyp_from_meta_info (const db::Layout &layout, std::string &lyp_file, bool &add_other_layers) +{ + db::Layout::meta_info_name_id_type layer_properties_file_name_id = layout.meta_info_name_id ("layer-properties-file"); + db::Layout::meta_info_name_id_type layer_properties_add_other_layers_name_id = layout.meta_info_name_id ("layer-properties-add-other-layers"); + + for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { + if (meta->first == layer_properties_file_name_id) { + lyp_file = meta->second.value.to_string (); + } + if (meta->first == layer_properties_add_other_layers_name_id) { + try { + add_other_layers = meta->second.value.to_bool (); + } catch (...) { + } + } + } +} + unsigned int LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers) { @@ -3266,17 +3285,7 @@ LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, } // Give the layout object a chance to specify a certain layer property file - for (db::Layout::meta_info_iterator meta = cv->layout ().begin_meta (); meta != cv->layout ().end_meta (); ++meta) { - if (meta->name == "layer-properties-file") { - lyp_file = meta->value; - } - if (meta->name == "layer-properties-add-other-layers") { - try { - tl::from_string (meta->value, add_other_layers); - } catch (...) { - } - } - } + get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers); // interpolate the layout properties file name tl::Eval expr; @@ -3438,17 +3447,7 @@ LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOp } // Give the layout object a chance to specify a certain layer property file - for (db::Layout::meta_info_iterator meta = cv->layout().begin_meta (); meta != cv->layout().end_meta (); ++meta) { - if (meta->name == "layer-properties-file") { - lyp_file = meta->value; - } - if (meta->name == "layer-properties-add-other-layers") { - try { - tl::from_string (meta->value, add_other_layers); - } catch (...) { - } - } - } + get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers); // interpolate the layout properties file name tl::Eval expr; diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index b5d8af8cb..59b3200b7 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -612,7 +612,7 @@ StatisticsSource::get (const std::string &url) } os << "" << std::endl - << "" << layout.get_properties (*l).to_string () << "" << std::endl + << "" << tl::escaped_to_html (layout.get_properties (*l).to_string (), true) << "" << std::endl // Boxes (total, single, array) << "" << st_hier.box_total () << "

" << st_flat.box_total () << "" << std::endl << "" << st_hier.box_single () << "

" << st_flat.box_single () << "" << std::endl @@ -678,19 +678,19 @@ StatisticsSource::get (const std::string &url) os << "" << std::endl << "" << std::endl - << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'

" << std::endl + << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << tl::escaped_to_html (m_h->name (), true) << "'

" << std::endl << "

" << std::endl << "" << std::endl << "" - << "" + << "" << "" << std::endl; if (! m_h->save_options ().format ().empty ()) { os << "" - << "" + << "" << "" << std::endl; } os << "" - << "" + << "" << "" << std::endl << "" << "" @@ -702,13 +702,17 @@ StatisticsSource::get (const std::string &url) << "" << "" << std::endl; for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { - os << "" << std::endl; + std::string d = meta->second.description; + if (!d.empty ()) { + d = layout.meta_info_name (meta->first); + } + os << "" << std::endl; } os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << m_h->filename () << "" << tl::to_string (QObject::tr ("Path")) << ": " << tl::escaped_to_html (m_h->filename (), true) << "
" << tl::to_string (QObject::tr ("Format")) << ": " << m_h->save_options ().format () << "" << tl::to_string (QObject::tr ("Format")) << ": " << tl::escaped_to_html (m_h->save_options ().format (), true) << "
" << tl::to_string (QObject::tr ("Technology")) << ": " << m_h->technology ()->description () << format_tech_name (m_h->tech_name ()) << "" << tl::to_string (QObject::tr ("Technology")) << ": " << tl::escaped_to_html (m_h->technology ()->description (), true) << tl::escaped_to_html (format_tech_name (m_h->tech_name ()), true) << "
" << tl::to_string (QObject::tr ("Database unit")) << ": " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "" << tl::to_string (QObject::tr ("Number of layers")) << ": " << num_layers << "
" << meta->description << "" << meta->value << "
" << tl::escaped_to_html (d, true) << "" << tl::escaped_to_html (meta->second.value.to_string (), true) << "
" << std::endl << "

" << tl::to_string (QObject::tr ("Top Cells")) << "

" << std::endl << "" << std::endl; for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) { - os << "" << std::endl; + os << "" << std::endl; } os << "
" << layout.cell_name (*tc) << "
" << tl::escaped_to_html (layout.cell_name (*tc), true) << "
" << std::endl; os << "

" << std::endl; @@ -733,7 +737,7 @@ StatisticsSource::get (const std::string &url) if (! layers_sorted_by_ld.empty ()) { os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl + << "

Detailed layer statistics

" << std::endl << "

" << std::endl << "" << std::endl << ""; @@ -748,7 +752,7 @@ StatisticsSource::get (const std::string &url) os << "" << ""; if (! layers_with_oasis_names.empty ()) { - os << ""; + os << ""; } os << "" << std::endl; } @@ -762,7 +766,7 @@ StatisticsSource::get (const std::string &url) if (! layers_with_oasis_names.empty ()) { os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl + << "

Detailed layer statistics

" << std::endl << "

" << std::endl << "

" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << lp.name << "" << tl::escaped_to_html (lp.name, true) << "
" << std::endl << "" << std::endl; @@ -772,7 +776,7 @@ StatisticsSource::get (const std::string &url) const db::LayerProperties &lp = layout.get_properties (*i); if (! lp.name.empty ()) { os << "" - << "" + << "" << "" << "" << std::endl; } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index afc4e95fe..91c940d1c 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -185,8 +185,8 @@ GDS2ReaderBase::do_read (db::Layout &layout) unsigned int mod_time[6] = { 0, 0, 0, 0, 0, 0 }; unsigned int access_time[6] = { 0, 0, 0, 0, 0, 0 }; get_time (mod_time, access_time); - layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5]))); - layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5]))); + layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5]))); + layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5]))); long attr = 0; db::PropertiesRepository::properties_set layout_properties; @@ -234,9 +234,9 @@ GDS2ReaderBase::do_read (db::Layout &layout) double dbuu = get_double (); double dbum = get_double (); - layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu))); - layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum))); - layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), m_libname)); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum))); + layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), m_libname)); m_dbuu = dbuu; m_dbu = dbum * 1e6; /*in micron*/ diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 8fdf46fde..f6bb95413 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -86,9 +86,9 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S db::GDS2WriterOptions gds2_options = options.get_options (); - layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); - layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); - layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), gds2_options.libname)); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); + layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname)); std::vector > layers; options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber); @@ -123,8 +123,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); diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc index 443f63027..59a259d6b 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc @@ -145,9 +145,10 @@ public: // Initialize the libname property from meta data with key "libname". db::GDS2WriterOptions *options = dynamic_cast (o); if (options) { + db::Layout::meta_info_name_id_type libname_name_id = lh.layout().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator meta = lh.layout().begin_meta (); meta != lh.layout().end_meta (); ++meta) { - if (meta->name == "libname" && !meta->value.empty ()) { - options->libname = meta->value; + if (meta->first == libname_name_id && !meta->second.value.is_nil ()) { + options->libname = meta->second.value.to_string (); } } } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index 18f3c860e..33b2e3e2b 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -229,7 +229,7 @@ TEST(1) db::LayerMap map = reader.read (layout); EXPECT_EQ (fabs (layout.dbu () / 0.001 - 1.0) < 1e-6, true); - EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB"); + EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB"); EXPECT_EQ (layout.layers (), size_t (11)); EXPECT_EQ (map.mapping_str (0), "2/0 : 2/0"); @@ -289,7 +289,7 @@ TEST(2) db::Reader reader (file); map = reader.read (layout_none, options); EXPECT_EQ (fabs (layout_none.dbu () / 0.001 - 1.0) < 1e-6, true); - EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB"); + EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB"); } EXPECT_EQ (layout_none.layers (), size_t (0)); diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc index d76dd434f..3346c6c6f 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc @@ -316,7 +316,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: error (tl::to_string (tr ("Could not find 'magic' header line - is this a MAGIC file?"))); } - layout.add_meta_info (db::MetaInfo ("lambda", "lambda value (tech scaling)", tl::to_string (m_lambda))); + layout.add_meta_info ("lambda", db::MetaInfo ("lambda value (tech scaling)", tl::to_string (m_lambda))); bool valid_layer = false; unsigned int current_layer = 0; @@ -339,11 +339,11 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: if (&m_stream == &stream) { // initial file - store technology - layout.add_meta_info (db::MetaInfo ("magic_technology", tl::to_string (tr ("MAGIC technology string")), m_tech)); + layout.add_meta_info ("magic_technology", db::MetaInfo (tl::to_string (tr ("MAGIC technology string")), m_tech)); // propose this is the KLayout technology unless a good one is given if (! mp_klayout_tech) { - layout.add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), m_tech)); + layout.add_meta_info ("technology", db::MetaInfo (tl::to_string (tr ("Technology name")), m_tech)); } } @@ -357,7 +357,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: if (&m_stream == &stream) { // initial file - store timestamp - layout.add_meta_info (db::MetaInfo ("magic_timestamp", "MAGIC main file timestamp", tl::to_string (ts))); + layout.add_meta_info ("magic_timestamp", db::MetaInfo ("MAGIC main file timestamp", tl::to_string (ts))); } ex.expect_end (); diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc index 8b899164d..a104e5fef 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc @@ -82,11 +82,14 @@ MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa double lambda = m_options.lambda; if (lambda <= 0.0) { - const std::string &lv = layout.meta_info_value ("lambda"); - if (lv.empty ()) { + const tl::Variant &lv = layout.meta_info ("lambda").value; + if (lv.is_nil ()) { throw tl::Exception (tl::to_string (tr ("No lambda value configured for MAG writer and no 'lambda' metadata present in layout."))); + } else if (lv.is_a_string ()) { + tl::from_string (lv.to_string (), lambda); + } else if (lv.can_convert_to_double ()) { + lambda = lv.to_double (); } - tl::from_string (lv, lambda); } m_sf = layout.dbu () / lambda; diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc index d85ca8173..8b8982d24 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc @@ -1146,7 +1146,7 @@ public: std::string lyr_file = data.get_layer_properties_file (); if (! lyr_file.empty ()) { - layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file)); + layout.add_meta_info ("layer-properties-file", db::MetaInfo ("Layer Properties File", lyr_file)); } return m_layers; From b14c630ce9d690b10a11b14efa3ed26f5ad93a82 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 21:40:53 +0200 Subject: [PATCH 04/35] Scheduling update for 0.28.8 --- src/db/db/gsiDeclDbCell.cc | 12 ++++++------ src/db/db/gsiDeclDbLayout.cc | 6 +++--- src/db/db/gsiDeclDbMetaInfo.cc | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 4acfc278b..ecff73f45 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1834,19 +1834,19 @@ Class decl_Cell ("db", "Cell", "@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.7." + "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.7." + "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.7." + "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" @@ -1855,7 +1855,7 @@ Class decl_Cell ("db", "Cell", "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.7." + "This method has been introduced in version 0.28.8." ) + gsi::method_ext ("meta_info", &cell_meta_info, gsi::arg ("name"), "@brief Gets the meta information for a given name\n" @@ -1863,13 +1863,13 @@ Class decl_Cell ("db", "Cell", "\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.7." + "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.7." + "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" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index d82c7c464..c3734d715 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -1056,7 +1056,7 @@ Class decl_Layout ("db", "Layout", "@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.7." + "This method has been introduced in version 0.28.8." ) + gsi::method ("remove_meta_info", static_cast (&db::Layout::remove_meta_info), gsi::arg ("name"), "@brief Removes meta information from the layout\n" @@ -1071,7 +1071,7 @@ Class decl_Layout ("db", "Layout", "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. Starting with version 0.28.7, the value is of variant type instead of string only.\n" + "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::method_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"), "@brief Gets the meta information for a given name\n" @@ -1079,7 +1079,7 @@ Class decl_Layout ("db", "Layout", "\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.7.\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" diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index 1e182805e..d4161b0e6 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -81,7 +81,7 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "@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.7.\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" @@ -103,11 +103,11 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", ) + gsi::method_ext ("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.7.\n" + "This predicate was introduced in version 0.28.8.\n" ) + gsi::method_ext ("persisted=", &layout_meta_set_persisted, "@brief Sets a value indicating whether the meta information will be persisted\n" - "This predicate was introduced in version 0.28.7.\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. " @@ -121,7 +121,7 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "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" - "This class has been introduced in version 0.25 and was extended in version 0.28.7." + "This class has been introduced in version 0.25 and was extended in version 0.28.8." ); } From 78da3effa2258352162e53383ff937c2f8d79fe9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2023 21:24:26 +0200 Subject: [PATCH 05/35] Next steps - persistency of meta info --- src/db/db/dbColdProxy.cc | 4 +- src/db/db/dbColdProxy.h | 8 +- src/db/db/dbLayout.cc | 173 ++++++++++++++++-- src/db/db/dbLayout.h | 65 +++++-- src/db/db/dbLibrary.cc | 6 +- src/db/unit_tests/dbLayoutTests.cc | 6 +- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 41 ++++- .../gds2/db_plugin/dbGDS2WriterBase.cc | 171 ++++++++++------- .../gds2/db_plugin/dbGDS2WriterBase.h | 1 + 9 files changed, 368 insertions(+), 107 deletions(-) diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc index 738d9dea9..8cc57aacd 100644 --- a/src/db/db/dbColdProxy.cc +++ b/src/db/db/dbColdProxy.cc @@ -49,8 +49,8 @@ ColdProxy::cold_proxies_per_lib_name (const std::string &libname) } } -ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info) - : Cell (ci, layout), mp_context_info (new ProxyContextInfo (info)) +ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info) + : Cell (ci, layout), mp_context_info (new LayoutOrCellContextInfo (info)) { if (! info.lib_name.empty ()) { tl::MutexLocker locker (&s_map_mutex); diff --git a/src/db/db/dbColdProxy.h b/src/db/db/dbColdProxy.h index 4c2681e29..5c642a9a1 100644 --- a/src/db/db/dbColdProxy.h +++ b/src/db/db/dbColdProxy.h @@ -35,7 +35,7 @@ namespace db { -struct ProxyContextInfo; +struct LayoutOrCellContextInfo; /** * @brief A cell specialization: a cold proxy representing a library or PCell which has gone out of scope @@ -53,7 +53,7 @@ public: * * Creates a cold proxy represented by the ProxyContextInfo data. */ - ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info); + ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info); /** * @brief The destructor @@ -68,7 +68,7 @@ public: /** * @brief Get the library id */ - const ProxyContextInfo &context_info () const + const LayoutOrCellContextInfo &context_info () const { return *mp_context_info; } @@ -102,7 +102,7 @@ public: virtual std::string get_qualified_name () const; private: - ProxyContextInfo *mp_context_info; + LayoutOrCellContextInfo *mp_context_info; ColdProxy (const ColdProxy &d); ColdProxy &operator= (const ColdProxy &d); diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index f92b19a47..247fb5c58 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -258,12 +258,12 @@ private: // ----------------------------------------------------------------- // Implementation of the ProxyContextInfo class -ProxyContextInfo -ProxyContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) +LayoutOrCellContextInfo +LayoutOrCellContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) { - ProxyContextInfo info; + LayoutOrCellContextInfo info; - for (std::vector::const_iterator i = from; i != to; ++i) { + for (auto i = from; i != to; ++i) { tl::Extractor ex (i->c_str ()); @@ -290,6 +290,20 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st info.cell_name = ex.skip (); + } else if (ex.test ("META(")) { + + std::pair > vv; + + ex.read_word_or_quoted (vv.first); + if (ex.test (",")) { + ex.read_word_or_quoted (vv.second.second); + } + ex.test (")"); + ex.test ("="); + ex.read (vv.second.first); + + info.meta_info.insert(vv); + } } @@ -298,12 +312,12 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st } void -ProxyContextInfo::serialize (std::vector &strings) +LayoutOrCellContextInfo::serialize (std::vector &strings) { if (! lib_name.empty ()) { strings.push_back ("LIB=" + lib_name); } - for (std::map ::const_iterator p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) { + for (auto p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) { strings.push_back ("P(" + tl::to_word_or_quoted_string (p->first) + ")=" + p->second.to_parsable_string ()); } if (! pcell_name.empty ()) { @@ -312,6 +326,20 @@ ProxyContextInfo::serialize (std::vector &strings) if (! cell_name.empty ()) { strings.push_back ("CELL=" + cell_name); } + + std::string mv; + for (auto m = meta_info.begin (); m != meta_info.end (); ++m) { + mv.clear (); + mv += "META("; + mv += tl::to_word_or_quoted_string (m->first); + if (! m->second.second.empty ()) { + mv += ","; + mv += tl::to_word_or_quoted_string (m->second.second); + } + mv += ")="; + mv += m->second.first.to_parsable_string (); + strings.push_back (mv); + } } // ----------------------------------------------------------------- @@ -593,7 +621,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 +634,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 +661,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 +678,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); @@ -2446,10 +2474,85 @@ Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vectorsecond.persisted) { + return true; + } + } + + return false; +} + +bool +Layout::has_context_info (cell_index_type cell_index) const +{ + auto c = m_meta_info_by_cell.find (cell_index); + if (c != m_meta_info_by_cell.end ()) { + for (auto i = c->second.begin (); i != c->second.end (); ++i) { + if (i->second.persisted) { + return true; + } + } + } + + const db::Cell &cref = cell (cell_index); + if (cref.is_proxy () && ! cref.is_top ()) { + return true; + } else { + return false; + } +} + +bool +Layout::get_context_info (std::vector &strings) const +{ + LayoutOrCellContextInfo info; + if (! get_context_info (info)) { + return false; + } else { + info.serialize (strings); + return true; + } +} + +bool +Layout::get_context_info (LayoutOrCellContextInfo &info) const +{ + for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) { + if (i->second.persisted) { + std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; + mi.first = i->second.value; + mi.second = i->second.description; + } + } + + return true; +} + +void +Layout::fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to) +{ + fill_meta_info_from_context (LayoutOrCellContextInfo::deserialize (from, to)); +} + +void +Layout::fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info) +{ + if (! context_info.meta_info.empty ()) { + for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) { + meta_info_name_id_type name_id = meta_info_name_id (i->first); + m_meta_info [name_id] = MetaInfo (i->second.second, i->second.first, true); + } + } +} + bool Layout::get_context_info (cell_index_type cell_index, std::vector &strings) const { - ProxyContextInfo info; + LayoutOrCellContextInfo info; if (! get_context_info (cell_index, info)) { return false; } else { @@ -2459,8 +2562,19 @@ Layout::get_context_info (cell_index_type cell_index, std::vector } bool -Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) const +Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &info) const { + auto cmi = m_meta_info_by_cell.find (cell_index); + if (cmi != m_meta_info_by_cell.end ()) { + for (auto i = cmi->second.begin (); i != cmi->second.end (); ++i) { + if (i->second.persisted) { + std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; + mi.first = i->second.value; + mi.second = i->second.description; + } + } + } + const db::Cell *cptr = &cell (cell_index); const db::ColdProxy *cold_proxy = dynamic_cast (cptr); @@ -2509,6 +2623,27 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co return true; } +void +Layout::fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to) +{ + fill_meta_info_from_context (cell_index, LayoutOrCellContextInfo::deserialize (from, to)); +} + +void +Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info) +{ + if (! context_info.meta_info.empty ()) { + + meta_info_map &mi = m_meta_info_by_cell [cell_index]; + + for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) { + meta_info_name_id_type name_id = meta_info_name_id (i->first); + mi [name_id] = MetaInfo (i->second.second, i->second.first, true); + } + + } +} + void Layout::restore_proxies (ImportLayerMapping *layer_mapping) { @@ -2540,11 +2675,11 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector : return false; } - return recover_proxy_as (cell_index, ProxyContextInfo::deserialize (from, to), layer_mapping); + return recover_proxy_as (cell_index, LayoutOrCellContextInfo::deserialize (from, to), layer_mapping); } bool -Layout::recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &info, ImportLayerMapping *layer_mapping) +Layout::recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &info, ImportLayerMapping *layer_mapping) { if (! info.lib_name.empty ()) { @@ -2594,11 +2729,11 @@ Layout::recover_proxy (std::vector ::const_iterator from, std::vect return 0; } - return recover_proxy (ProxyContextInfo::deserialize (from, to)); + return recover_proxy (LayoutOrCellContextInfo::deserialize (from, to)); } db::Cell * -Layout::recover_proxy (const ProxyContextInfo &info) +Layout::recover_proxy (const LayoutOrCellContextInfo &info) { if (! info.lib_name.empty ()) { @@ -2626,7 +2761,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 ()) { @@ -2723,7 +2858,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; @@ -2754,7 +2889,7 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info) } void -Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index) +Layout::create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type target_cell_index) { tl_assert (m_cell_ptrs [target_cell_index] != 0); diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 2038ae52b..36730efa1 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -417,14 +417,15 @@ public: /** * @brief A binary object representing context information for regenerating library proxies and PCells */ -struct DB_PUBLIC ProxyContextInfo +struct DB_PUBLIC LayoutOrCellContextInfo { std::string lib_name; std::string cell_name; std::string pcell_name; std::map pcell_parameters; + std::map > meta_info; - static ProxyContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); + static LayoutOrCellContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); void serialize (std::vector &strings); }; @@ -1015,27 +1016,69 @@ public: /** * @brief Creates a cold proxy representing the given context information */ - cell_index_type create_cold_proxy (const db::ProxyContextInfo &info); + cell_index_type create_cold_proxy (const db::LayoutOrCellContextInfo &info); /** * @brief Subsitutes the given cell by a cold proxy representing the given context information */ - void create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type cell_index); + void create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type cell_index); + + /** + * @brief Gets a value indicating whether layout context info is provided / needed + */ + bool has_context_info() const; + + /** + * @brief Gets a value indicating whether layout context info is provided / needed + */ + bool has_context_info(cell_index_type cell_index) const; + + /** + * @brief Get the context information for the layout (for writing into a file) + * + * The context information is a sequence of strings which is pushed onto the given + * vector. It can be used to fill meta information with fill_meta_info_from_context. + */ + bool get_context_info (std::vector &strings) const; + + /** + * @brief Gets the context information as a binary object + */ + bool get_context_info (LayoutOrCellContextInfo &context_info) const; + + /** + * @brief Fills the layout's meta information from the context + */ + void fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to); + + /** + * @brief Fills the layout's meta information from the binary context + */ + void fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info); /** * @brief Get the context information for a given cell (for writing into a file) * * The context information is a sequence of strings which is pushed onto the given - * vector. It can be used to recover a respective proxy cell with the recover_proxy method. - * If the given cell is not a valid proxy or library references are missing, the method - * will return false. + * vector. It can be used to recover a respective proxy cell with the recover_proxy method + * or to fill meta information using fill_meta_info_from_context. */ bool get_context_info (cell_index_type cell_index, std::vector &context_info) const; /** * @brief Gets the context information as a binary object */ - bool get_context_info (cell_index_type cell_index, ProxyContextInfo &context_info) const; + bool get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &context_info) const; + + /** + * @brief Fills the layout's meta information from the context + */ + void fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to); + + /** + * @brief Fills the layout's meta information from the binary context + */ + void fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info); /** * @brief Recover a proxy cell from the given context info. @@ -1051,7 +1094,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. @@ -1073,7 +1116,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 @@ -2026,7 +2069,7 @@ private: /** * @brief Recovers a proxy without considering the library from context_info */ - db::Cell *recover_proxy_no_lib (const ProxyContextInfo &context_info); + db::Cell *recover_proxy_no_lib (const LayoutOrCellContextInfo &context_info); }; /** diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index a6075f5d6..59acfa95f 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -205,7 +205,7 @@ Library::remap_to (db::Library *other) if (! pn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); @@ -216,7 +216,7 @@ Library::remap_to (db::Library *other) if (! old_pcell_decl || ! new_pcell_decl) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); @@ -254,7 +254,7 @@ Library::remap_to (db::Library *other) if (! cn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index e159ec41c..372a495b3 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -543,7 +543,7 @@ TEST(5) db::Layout l (&m); EXPECT_EQ (l.technology_name (), ""); - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; info.lib_name = "LIB"; info.cell_name = "LIBCELL"; @@ -624,7 +624,7 @@ TEST(6) EXPECT_EQ (l.technology_name (), ""); - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; info.lib_name = "Basic"; info.pcell_name = "CIRCLE"; info.pcell_parameters ["actual_radius"] = tl::Variant (10.0); @@ -644,7 +644,7 @@ TEST(6) EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n"); - db::ProxyContextInfo info2; + db::LayoutOrCellContextInfo info2; l.get_context_info (cell->cell_index (), info2); info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 91c940d1c..1ae260b5c 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -289,13 +289,19 @@ GDS2ReaderBase::do_read (db::Layout &layout) db::cell_index_type cell_index = make_cell (layout, m_cellname); bool ignore_cell = false; - std::map >::const_iterator ctx = m_context_info.find (m_cellname); + auto ctx = m_context_info.find (m_cellname); if (ctx != m_context_info.end ()) { + CommonReaderLayerMapping layer_mapping (this, &layout); - if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) { + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + + if (layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { // ignore everything in that cell since it is created by the import: ignore_cell = true; } + + layout.fill_meta_info_from_context (cell_index, ci); + } db::Cell *cell = 0; @@ -386,6 +392,13 @@ GDS2ReaderBase::do_read (db::Layout &layout) } + // deserialize global context information + auto ctx = m_context_info.find (std::string ()); + if (ctx != m_context_info.end ()) { + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + layout.fill_meta_info_from_context (ci); + } + // check, if the last record is a ENDLIB if (rec_id != sENDLIB) { error (tl::to_string (tr ("ENDLIB record expected"))); @@ -396,12 +409,16 @@ void GDS2ReaderBase::read_context_info_cell () { short rec_id = 0; + std::string cn; // read cell content while ((rec_id = get_record ()) != sENDSTR) { progress_checkpoint (); + bool valid_hook = false; + cn.clear (); + if (rec_id == sSREF) { do { @@ -411,7 +428,7 @@ GDS2ReaderBase::read_context_info_cell () error (tl::to_string (tr ("SNAME record expected"))); } - std::string cn = get_string (); + cn = get_string (); rec_id = get_record (); while (rec_id == sSTRANS || rec_id == sANGLE || rec_id == sMAG) { @@ -421,6 +438,24 @@ GDS2ReaderBase::read_context_info_cell () error (tl::to_string (tr ("XY record expected"))); } + valid_hook = true; + + } else if (rec_id == sBOUNDARY) { + + rec_id = get_record (); + while (rec_id == sLAYER || rec_id == sDATATYPE) { + rec_id = get_record (); + } + if (rec_id != sXY) { + error (tl::to_string (tr ("XY record expected"))); + } + + valid_hook = true; + + } + + if (valid_hook) { + std::vector &strings = m_context_info.insert (std::make_pair (cn, std::vector ())).first->second; size_t attr = 0; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index f6bb95413..048ded3d6 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -72,6 +72,109 @@ inline int scale (double sf, int value) } } +void +GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells) +{ + write_record_size (4 + 12 * 2); + write_record (sBGNSTR); + write_time (time_data); + write_time (time_data); + + write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); + + std::vector context_prop_strings; + + if (layout.has_context_info ()) { + + // Use a dummy BOUNDARY element to attach the global context + + write_record_size (4); + write_record (sBOUNDARY); + + write_record_size (6); + write_record (sLAYER); + write_short (0); + + write_record_size (6); + write_record (sDATATYPE); + write_short (0); + + write_record_size (4 + 5 * 2 * 4); + write_record (sXY); + for (unsigned int i = 0; i < 10; ++i) { + write_int (0); + } + + context_prop_strings.clear (); + + if (layout.get_context_info (context_prop_strings)) { + + // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings + // will arrive) + for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { + + --s; + + write_record_size (6); + write_record (sPROPATTR); + write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + + write_string_record (sPROPVALUE, *s); + + } + + } + + write_record_size (4); + write_record (sENDEL); + + } + + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + + if (layout.has_context_info (*cell)) { + + write_record_size (4); + write_record (sSREF); + + write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); + + write_record_size (12); + write_record (sXY); + write_int (0); + write_int (0); + + context_prop_strings.clear (); + + if (layout.get_context_info (*cell, context_prop_strings)) { + + // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings + // will arrive) + for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { + + --s; + + write_record_size (6); + write_record (sPROPATTR); + write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + + write_string_record (sPROPVALUE, *s); + + } + + } + + write_record_size (4); + write_record (sENDEL); + + } + + } + + write_record_size (4); + write_record (sENDSTR); +} + void GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -178,73 +281,17 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // write context info - bool any_proxy = false; + bool has_context = false; if (options.write_context_info ()) { - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !any_proxy; ++cell) { - const db::Cell &cref = layout.cell (*cell); - if (cref.is_proxy () && ! cref.is_top ()) { - any_proxy = true; - } + has_context = layout.has_context_info (); + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !has_context; ++cell) { + has_context = layout.has_context_info (*cell); } } - if (any_proxy) { - - write_record_size (4 + 12 * 2); - write_record (sBGNSTR); - write_time (time_data); - write_time (time_data); - - write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); - - std::vector context_prop_strings; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref = layout.cell (*cell); - if (cref.is_proxy () && ! cref.is_top ()) { - - write_record_size (4); - write_record (sSREF); - - write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); - - write_record_size (12); - write_record (sXY); - write_int (0); - write_int (0); - - context_prop_strings.clear (); - - if (layout.get_context_info (*cell, context_prop_strings)) { - - // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings - // will arrive) - for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { - - --s; - - write_record_size (6); - write_record (sPROPATTR); - write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string - - write_string_record (sPROPVALUE, *s); - - } - - } - - write_record_size (4); - write_record (sENDEL); - - } - - } - - write_record_size (4); - write_record (sENDSTR); - + if (has_context) { + write_context_cell (layout, time_data, cells); } // body diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index 52ef2b7ee..94a4a81d5 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -168,6 +168,7 @@ private: db::WriterCellNameMap m_cell_name_map; void write_properties (const db::Layout &layout, db::properties_id_type prop_id); + void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells); }; } // namespace db From b36eb04afaf96f86460d8540179e64cb2022f474 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2023 22:34:11 +0200 Subject: [PATCH 06/35] Meta info persistency also for OASIS --- src/db/db/dbLayout.cc | 5 +- .../oasis/db_plugin/dbOASISReader.cc | 29 ++++-- .../oasis/db_plugin/dbOASISWriter.cc | 91 ++++++++++++------- 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 247fb5c58..aa6d27992 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -2564,6 +2564,8 @@ Layout::get_context_info (cell_index_type cell_index, std::vector bool 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) { @@ -2571,6 +2573,7 @@ Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &i std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; mi.first = i->second.value; mi.second = i->second.description; + any_meta = true; } } } @@ -2590,7 +2593,7 @@ Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &i 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 613e50a82..f30a9ea6e 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -36,8 +36,6 @@ namespace db { -// --------------------------------------------------------------- - // --------------------------------------------------------------- // OASISReader @@ -680,6 +678,8 @@ OASISReader::do_read (db::Layout &layout) m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; + bool has_context = false; + std::vector context_strings; mark_start_table (); @@ -1000,7 +1000,16 @@ OASISReader::do_read (db::Layout &layout) read_properties (layout.properties_repository ()); } - store_last_properties (layout.properties_repository (), layout_properties, true); + if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { + has_context = true; + context_strings.reserve (mm_last_value_list.get ().size ()); + for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { + context_strings.push_back (v->to_string ()); + } + } else { + // store cell properties + store_last_properties (layout.properties_repository (), layout_properties, true); + } mark_start_table (); @@ -1103,6 +1112,12 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } + if (has_context) { + // Restore layout meta info + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.fill_meta_info_from_context (info); + } + size_t pt = m_stream.pos (); if (table_offsets_at_end) { @@ -3246,7 +3261,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) context_strings.push_back (v->to_string ()); } } else { - // store cell properties + // store layout properties store_last_properties (layout.properties_repository (), cell_properties, true); } @@ -3344,10 +3359,12 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) m_instances_with_props.clear (); } - // Restore proxy cell (link to PCell or Library) + // Restore proxy cell (link to PCell or Library) and cell meta info if (has_context) { CommonReaderLayerMapping layer_mapping (this, &layout); - layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping); + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.recover_proxy_as (cell_index, info, &layer_mapping); + layout.fill_meta_info_from_context (cell_index, info); } m_cellname = ""; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 8e8da55ad..ec461199a 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1303,6 +1303,24 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_props (layout.prop_id ()); } + std::vector context_prop_strings; + + // write the global layout context information + + if (options.write_context_info () && layout.has_context_info () && layout.get_context_info (context_prop_strings)) { + + std::vector values; + values.reserve (context_prop_strings.size ()); + for (auto i = context_prop_strings.begin (); i != context_prop_strings.end (); ++i) { + values.push_back (tl::Variant (*i)); + } + + write_property_def (klayout_context_name, values, false); + + context_prop_strings.clear (); + + } + // build property name and value string tables { @@ -1360,25 +1378,25 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - if (options.write_context_info ()) { + // if needed, emit property name required for the PCell or meta info context information - // emit property name required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + if (options.write_context_info () && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { - 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; - - } + bool has_context = false; + { + LayoutOrCellContextInfo info; + has_context = layout.has_context_info () && layout.get_context_info (info); + } + 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); } } @@ -1445,27 +1463,36 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save if (options.write_context_info ()) { - // emit property string id's required for the PCell context information + // emit property string id's required for the PCell and meta info context information std::vector context_prop_strings; + + if (layout.has_context_info () && layout.get_context_info (context_prop_strings)) { + + for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { + if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (c->c_str ()); + ++m_propstring_id; + } + } + + } + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); + context_prop_strings.clear (); - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top ()) { + if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) { - context_prop_strings.clear (); - if (layout.get_context_info (*cell, context_prop_strings)) { - - for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { - if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { - begin_table (propstrings_table_pos); - write_record_id (9); - write_bstring (c->c_str ()); - ++m_propstring_id; - } + for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { + if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (c->c_str ()); + ++m_propstring_id; } - } } @@ -1601,8 +1628,6 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); @@ -1630,7 +1655,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 (); From b35113b80e7233d84f34263591037d5aba472a4c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2023 22:52:05 +0200 Subject: [PATCH 07/35] Bugfix. --- .../oasis/db_plugin/dbOASISWriter.cc | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index ec461199a..bb4b13576 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1383,17 +1383,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save if (options.write_context_info () && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { bool has_context = false; - { - LayoutOrCellContextInfo info; - has_context = layout.has_context_info () && layout.get_context_info (info); - } 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)); + 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); @@ -1466,19 +1462,6 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // emit property string id's required for the PCell and meta info context information std::vector context_prop_strings; - if (layout.has_context_info () && layout.get_context_info (context_prop_strings)) { - - for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { - if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { - begin_table (propstrings_table_pos); - write_record_id (9); - write_bstring (c->c_str ()); - ++m_propstring_id; - } - } - - } - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); From 3361802a20758caffa596b53e02f94865b2c19b1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 00:51:11 +0200 Subject: [PATCH 08/35] Tests and bugfixing for meta info persistence in OASIS + GDS --- src/db/db/dbLayout.cc | 12 ++ src/db/db/dbLayout.h | 3 + src/db/unit_tests/dbLayoutTests.cc | 60 ++++++- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 2 +- .../streamers/gds2/unit_tests/dbGDS2Writer.cc | 74 ++++++++ .../oasis/db_plugin/dbOASISReader.cc | 163 +++++++++++------- .../streamers/oasis/db_plugin/dbOASISReader.h | 1 + .../oasis/unit_tests/dbOASISWriterTests.cc | 77 +++++++++ 8 files changed, 325 insertions(+), 67 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index aa6d27992..a03a874a7 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -342,6 +342,18 @@ LayoutOrCellContextInfo::serialize (std::vector &strings) } } +bool +LayoutOrCellContextInfo::has_proxy_info () const +{ + return !pcell_name.empty () || !lib_name.empty (); +} + +bool +LayoutOrCellContextInfo::has_meta_info () const +{ + return !meta_info.empty (); +} + // ----------------------------------------------------------------- // Implementation of the Layout class diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 36730efa1..c82e5cb14 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -427,6 +427,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo static LayoutOrCellContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); void serialize (std::vector &strings); + + bool has_proxy_info () const; + bool has_meta_info () const; }; /** diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 372a495b3..3b438bce5 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -677,7 +677,7 @@ TEST(7_LayerProperties) db::Layout l (&m); EXPECT_EQ (l.is_valid_layer (0), false); - EXPECT_EQ (l.guiding_shape_layer (), 0); + EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0); EXPECT_EQ (l.is_special_layer (0), true); EXPECT_EQ (int (l.layers ()), 1); @@ -737,3 +737,61 @@ TEST(7_LayerProperties) EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1); EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1); } + +TEST(8_MetaInfo) +{ + db::Layout ly; + + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1); + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.has_context_info (), false); + + ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false)); + ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true)); + + EXPECT_EQ (ly.has_context_info (), true); + + EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info ("x").description, ""); + EXPECT_EQ (ly.meta_info ("x").persisted, false); + + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (ly.meta_info ("a").description, "description"); + EXPECT_EQ (ly.meta_info ("a").persisted, false); + + EXPECT_EQ (ly.meta_info (1).value.to_string (), "value"); + EXPECT_EQ (ly.meta_info (1).description, ""); + EXPECT_EQ (ly.meta_info (1).persisted, true); + + db::cell_index_type ci = ly.add_cell ("X"); + + EXPECT_EQ (ly.has_context_info (ci), false); + + ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false)); + ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true)); + + EXPECT_EQ (ly.has_context_info (ci), true); + + EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info (ci, "x").description, ""); + EXPECT_EQ (ly.meta_info (ci, "x").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1"); + EXPECT_EQ (ly.meta_info (ci, "a").description, "dd"); + EXPECT_EQ (ly.meta_info (ci, "a").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u"); + EXPECT_EQ (ly.meta_info (ci, 1).description, "d"); + EXPECT_EQ (ly.meta_info (ci, 1).persisted, true); + + EXPECT_EQ (ly.has_context_info (), true); + ly.clear_meta (); + EXPECT_EQ (ly.has_context_info (), false); + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil"); + + EXPECT_EQ (ly.has_context_info (ci), true); + ly.clear_meta (ci); + EXPECT_EQ (ly.has_context_info (ci), false); + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil"); +} diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 1ae260b5c..90e84d65d 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -295,7 +295,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) CommonReaderLayerMapping layer_mapping (this, &layout); LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); - if (layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { + 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; } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index d2eb315d6..648a5105f 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1193,6 +1193,80 @@ TEST(121) run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt); } +// Meta info +TEST(130) +{ + 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 = tl::TestBase::tmp_file ("tmp_GDS2Writer_130.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.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 = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} + // Extreme fracturing by max. points TEST(166) { diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index f30a9ea6e..7428612b8 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -597,6 +597,10 @@ OASISReader::read_offset_table () static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; +static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; +static const char *s_gds_property_propname = "S_GDS_PROPERTY"; + + void OASISReader::do_read (db::Layout &layout) { @@ -604,8 +608,8 @@ OASISReader::do_read (db::Layout &layout) char *mb; // prepare - m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY"); - m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT"); + m_s_gds_property_name_id = layout.properties_repository ().prop_name_id (s_gds_property_propname); + m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id (klayout_context_propname); // read magic bytes mb = (char *) m_stream.get (sizeof (magic_bytes) - 1); @@ -678,8 +682,7 @@ OASISReader::do_read (db::Layout &layout) m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; - bool has_context = false; - std::vector context_strings; + std::vector context_properties; mark_start_table (); @@ -827,16 +830,25 @@ OASISReader::do_read (db::Layout &layout) std::map ::iterator pf = m_propname_forward_references.find (id); if (pf != m_propname_forward_references.end ()) { - if (name == "S_GDS_PROPERTY") { + bool is_s_gds_property = false; + bool is_klayout_context_property = false; + + if (name == s_gds_property_propname) { + is_s_gds_property = true; + } else if (name == klayout_context_propname) { + is_klayout_context_property = true; + } + + // handle special case of forward references to S_GDS_PROPERTY and KLAYOUT_CONTEXT + if (is_s_gds_property || is_klayout_context_property) { db::PropertiesRepository &rep = layout.properties_repository (); - db::property_names_id_type s_gds_name_id = pf->second; // exchange the properties in the repository: first locate all // property sets that are affected std::vector pids; for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { - if (p->second.find (s_gds_name_id) != p->second.end ()) { + if (p->second.find (pf->second) != p->second.end ()) { pids.push_back (p->first); } } @@ -848,14 +860,26 @@ OASISReader::do_read (db::Layout &layout) db::PropertiesRepository::properties_set new_set; for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) { - if (s->first == s_gds_name_id) { + if (s->first == pf->second && is_s_gds_property) { + // S_GDS_PROPERTY translation if (!s->second.is_list () || s->second.get_list ().size () != 2) { error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); } new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); + } else if (s->first == pf->second && is_klayout_context_property) { + + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); + } + } else { new_set.insert (*s); } @@ -1001,11 +1025,7 @@ OASISReader::do_read (db::Layout &layout) } if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { - has_context = true; - context_strings.reserve (mm_last_value_list.get ().size ()); - for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { - context_strings.push_back (v->to_string ()); - } + context_properties.insert (context_properties.end (), mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ()); } else { // store cell properties store_last_properties (layout.properties_repository (), layout_properties, true); @@ -1112,12 +1132,6 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } - if (has_context) { - // Restore layout meta info - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - layout.fill_meta_info_from_context (info); - } - size_t pt = m_stream.pos (); if (table_offsets_at_end) { @@ -1153,54 +1167,14 @@ OASISReader::do_read (db::Layout &layout) // resolve all propvalue forward referenced if (! m_propvalue_forward_references.empty ()) { + for (auto i = context_properties.begin (); i != context_properties.end (); ++i) { + replace_forward_references_in_variant (*i); + } + for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { - for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { - - if (ps->second.is_id ()) { - - unsigned long id = (unsigned long) ps->second.to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - ps->second = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - - } else if (ps->second.is_list ()) { - - // Replace list elements as well - // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant - - const std::vector &l = ps->second.get_list (); - bool needs_replacement = false; - for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { - needs_replacement = ll->is_id (); - } - - if (needs_replacement) { - - std::vector new_list (l); - for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { - if (ll->is_id ()) { - unsigned long id = (unsigned long) ll->to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - *ll = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - } - } - - ps->second = tl::Variant (new_list.begin (), new_list.end ()); - - } - - } - + replace_forward_references_in_variant (ps->second); } - } m_propvalue_forward_references.clear (); @@ -1228,6 +1202,17 @@ OASISReader::do_read (db::Layout &layout) } + // Restore layout meta info + if (! context_properties.empty ()) { + std::vector context_strings; + context_strings.reserve (context_properties.size ()); + for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { + context_strings.push_back (s->to_string ()); + } + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.fill_meta_info_from_context (info); + } + // Check the table offsets vs. real occurrence if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname)); @@ -1246,6 +1231,52 @@ OASISReader::do_read (db::Layout &layout) } } +void +OASISReader::replace_forward_references_in_variant (tl::Variant &v) +{ + if (v.is_id ()) { + + unsigned long id = (unsigned long) v.to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + v = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + + } else if (v.is_list ()) { + + // Replace list elements as well + // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant + + const std::vector &l = v.get_list (); + bool needs_replacement = false; + for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { + needs_replacement = ll->is_id (); + } + + if (needs_replacement) { + + std::vector new_list (l); + for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { + if (ll->is_id ()) { + unsigned long id = (unsigned long) ll->to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + *ll = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + } + } + + v = tl::Variant (new_list.begin (), new_list.end ()); + + } + + } +} + void OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special) { @@ -3363,7 +3394,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) if (has_context) { CommonReaderLayerMapping layer_mapping (this, &layout); LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - layout.recover_proxy_as (cell_index, info, &layer_mapping); + if (info.has_proxy_info ()) { + layout.recover_proxy_as (cell_index, info, &layer_mapping); + } layout.fill_meta_info_from_context (cell_index, info); } diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index f9aae8537..78d6b9c41 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -210,6 +210,7 @@ private: void read_properties (db::PropertiesRepository &rep); void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special); std::pair read_element_properties (db::PropertiesRepository &rep, bool ignore_special); + void replace_forward_references_in_variant (tl::Variant &v); unsigned char get_byte () { diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 91566f61a..a41c7ab72 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -1860,3 +1860,80 @@ TEST(120_IrregularInstRepetitions) } } + +// Meta info +TEST(130) +{ + 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 = tl::TestBase::tmp_file ("tmp_OASISWriter_130.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + 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 = tl::TestBase::tmp_file ("tmp_OASISWriter_130b.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + 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"); +} + From 56fbba60c1051a9993398b5f0269fdf34dd8a55b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 01:22:46 +0200 Subject: [PATCH 09/35] Debugging & testing --- .../oasis/db_plugin/dbOASISReader.cc | 251 ++++++++++-------- 1 file changed, 144 insertions(+), 107 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 7428612b8..2d9c86dca 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -40,7 +40,7 @@ namespace db // OASISReader OASISReader::OASISReader (tl::InputStream &s) - : m_stream (s), + : m_stream (s), m_progress (tl::to_string (tr ("Reading OASIS file")), 10000), m_dbu (0.001), m_expect_strict_mode (-1), @@ -111,7 +111,7 @@ OASISReader::init (const db::LoadLayoutOptions &options) m_expect_strict_mode = oasis_options.expect_strict_mode; } -inline long long +inline long long OASISReader::get_long_long () { unsigned long long u = get_ulong_long (); @@ -122,13 +122,13 @@ OASISReader::get_long_long () } } -inline unsigned long long +inline unsigned long long OASISReader::get_ulong_long () { unsigned long long v = 0; unsigned long long vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -136,7 +136,7 @@ OASISReader::get_ulong_long () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned long long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } @@ -147,7 +147,7 @@ OASISReader::get_ulong_long () return v; } -inline long +inline long OASISReader::get_long () { unsigned long u = get_ulong (); @@ -168,13 +168,13 @@ OASISReader::get_ulong_for_divider () return l; } -inline unsigned long +inline unsigned long OASISReader::get_ulong () { unsigned long v = 0; unsigned long vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -182,7 +182,7 @@ OASISReader::get_ulong () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } @@ -193,7 +193,7 @@ OASISReader::get_ulong () return v; } -inline int +inline int OASISReader::get_int () { unsigned int u = get_uint (); @@ -204,13 +204,13 @@ OASISReader::get_int () } } -inline unsigned int +inline unsigned int OASISReader::get_uint () { unsigned int v = 0; unsigned int vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -218,7 +218,7 @@ OASISReader::get_uint () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned int) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned integer value overflow"))); } @@ -229,7 +229,7 @@ OASISReader::get_uint () return v; } -std::string +std::string OASISReader::get_str () { std::string s; @@ -258,11 +258,11 @@ OASISReader::get_real () if (t == 0) { - return double (get_ulong ()); + return double (get_ulong ()); } else if (t == 1) { - return -double (get_ulong ()); + return -double (get_ulong ()); } else if (t == 2) { @@ -445,7 +445,7 @@ OASISReader::get_gdelta (long grid) ly > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } - + return db::Vector (db::Coord (lx), db::Coord (ly)); } else { @@ -480,13 +480,13 @@ OASISReader::get_gdelta (long grid) } } -void +void OASISReader::error (const std::string &msg) { throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str ()); } -void +void OASISReader::warn (const std::string &msg, int wl) { if (warn_level () < wl) { @@ -497,7 +497,7 @@ OASISReader::warn (const std::string &msg, int wl) error (msg); } else { // TODO: compress - tl::warn << msg + tl::warn << msg << tl::to_string (tr (" (position=")) << m_stream.pos () << tl::to_string (tr (", cell=")) << m_cellname << ")"; @@ -516,7 +516,7 @@ struct LNameJoinOp1 }; /** - * @brief A helper class to join two layer map members + * @brief A helper class to join two layer map members * This implementation basically merged the datatype maps. */ struct LNameJoinOp2 @@ -533,16 +533,16 @@ struct LNameJoinOp2 * * This method will update m_table_start which is the location used as * the start position of a strict mode table. Every record except CBLOCK - * will update this position to point after the record. Hence m_table_start - * points to the beginning of a table when PROPNAME, CELLNAME or any + * will update this position to point after the record. Hence m_table_start + * points to the beginning of a table when PROPNAME, CELLNAME or any * other table-contained record is encountered. * Since CBLOCK does not update this record, the position of the table will * be the location of CBLOCK rather than that of the name record itself. - * PAD records will also call this method, so the beginning of a table + * PAD records will also call this method, so the beginning of a table * is right after any preceding PAD records and exactly at the location * of the first name record after PADs. */ -void +void OASISReader::mark_start_table () { // we need to this this to really finish a CBLOCK - this is a flaw @@ -554,7 +554,7 @@ OASISReader::mark_start_table () m_table_start = m_stream.pos (); } -void +void OASISReader::read_offset_table () { unsigned int of = 0; @@ -601,7 +601,7 @@ static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; static const char *s_gds_property_propname = "S_GDS_PROPERTY"; -void +void OASISReader::do_read (db::Layout &layout) { unsigned char r; @@ -844,8 +844,40 @@ OASISReader::do_read (db::Layout &layout) db::PropertiesRepository &rep = layout.properties_repository (); - // exchange the properties in the repository: first locate all - // property sets that are affected + // exchange properties in layout_properties + db::PropertiesRepository::properties_set new_set; + + for (db::PropertiesRepository::properties_set::const_iterator s = layout_properties.begin (); s != layout_properties.end (); ++s) { + if (s->first == pf->second && is_s_gds_property) { + + // S_GDS_PROPERTY translation + if (!s->second.is_list () || s->second.get_list ().size () != 2) { + error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); + } + + new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); + + } else if (s->first == pf->second && is_klayout_context_property) { + + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); + } + + } else { + new_set.insert (*s); + } + } + + new_set.swap (layout_properties); + + // exchange the properties in the repository + + // first locate all property sets that are affected std::vector pids; for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { if (p->second.find (pf->second) != p->second.end ()) { @@ -871,13 +903,18 @@ OASISReader::do_read (db::Layout &layout) } else if (s->first == pf->second && is_klayout_context_property) { - // feed context strings from klayout context property - if (s->second.is_list ()) { - for (auto l = s->second.begin (); l != s->second.end (); ++l) { - context_properties.push_back (*l); + if (*pid == layout.prop_id ()) { + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); } } else { - context_properties.push_back (s->second); + // TODO: should update that in cells (in case we encounter forward-referenced context properties + // for cells) } } else { @@ -964,7 +1001,7 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } - // read a layer name + // read a layer name std::string name = get_str (); db::ld_type dt1 = 0, dt2 = std::numeric_limits::max () - 1; @@ -1297,7 +1334,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie // Special properties are not turned into user properties except S_GDS_PROPERTY. // This is mode is used for cells and layouts so the standard properties do not appear as user properties. // For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names. - + } else if (mm_last_value_list.get ().size () == 0) { properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant ())); } else if (mm_last_value_list.get ().size () == 1) { @@ -1307,7 +1344,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie } } -std::pair +std::pair OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore_special) { db::PropertiesRepository::properties_set properties; @@ -1322,7 +1359,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore // skip PAD. mark_start_table (); - + } else if (m == 34 /*CBLOCK*/) { unsigned int type = get_uint (); @@ -1357,7 +1394,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } - } + } if (! properties.empty ()) { return std::make_pair (true, rep.properties_id (properties)); @@ -1366,7 +1403,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } } -void +void OASISReader::read_properties (db::PropertiesRepository &rep) { unsigned char m = get_byte (); @@ -1475,12 +1512,12 @@ OASISReader::read_properties (db::PropertiesRepository &rep) } - -void + +void OASISReader::read_pointlist (modal_variable > &pointlist, bool for_polygon) { unsigned int type = get_uint (); - + unsigned long n = 0; get (n); if (n == 0) { @@ -1571,13 +1608,13 @@ OASISReader::read_repetition () { unsigned int type = get_uint (); if (type == 0) { - + // reuse modal variable } else if (type == 1) { unsigned long nx = 0, ny = 0; - get (nx); + get (nx); get (ny); db::Coord dx = get_ucoord (); @@ -1588,7 +1625,7 @@ OASISReader::read_repetition () } else if (type == 2) { unsigned long nx = 0; - get (nx); + get (nx); db::Coord dx = get_ucoord (); @@ -1604,7 +1641,7 @@ OASISReader::read_repetition () mm_repetition = new RegularRepetition (db::Vector (0, 0), db::Vector (0, dy), 1, dy == 0 ? 1 : ny + 2); } else if (type == 4 || type == 5) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1629,7 +1666,7 @@ OASISReader::read_repetition () } } else if (type == 6 || type == 7) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1657,7 +1694,7 @@ OASISReader::read_repetition () unsigned long n = 0, m = 0; - get (n); + get (n); get (m); db::Vector dn = get_gdelta (); db::Vector dm = get_gdelta (); @@ -1667,7 +1704,7 @@ OASISReader::read_repetition () } else if (type == 9) { unsigned long n = 0; - get (n); + get (n); db::Vector dn = get_gdelta (); mm_repetition = new RegularRepetition (dn, db::Vector (0, 0), dn == db::Vector () ? 1 : n + 2, 1); @@ -1704,10 +1741,10 @@ OASISReader::read_repetition () return mm_repetition.get ().size () > 1; } -void +void OASISReader::do_read_placement (unsigned char r, bool xy_absolute, - db::Layout &layout, + db::Layout &layout, tl::vector &instances, tl::vector &instances_with_props) { @@ -1734,7 +1771,7 @@ OASISReader::do_read_placement (unsigned char r, } - } + } double mag = 1.0; bool mag_set = false; @@ -1772,7 +1809,7 @@ OASISReader::do_read_placement (unsigned char r, } else { angle = ((m & 0x06) >> 1); } - + mirror = (m & 0x01) != 0; if (m & 0x20) { @@ -1810,10 +1847,10 @@ OASISReader::do_read_placement (unsigned char r, db::CellInstArray inst; if (mag_set || angle < 0) { - inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::ICplxTrans (mag, angle_deg, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } else { - inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } @@ -1905,9 +1942,9 @@ OASISReader::do_read_placement (unsigned char r, } } -void +void OASISReader::do_read_text (bool xy_absolute, - db::cell_index_type cell_index, + db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -1951,7 +1988,7 @@ OASISReader::do_read_text (bool xy_absolute, mm_text_string = get_str (); } - } + } if (m & 0x1) { mm_textlayer = get_uint (); @@ -2030,7 +2067,7 @@ OASISReader::do_read_text (bool xy_absolute, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2078,9 +2115,9 @@ OASISReader::do_read_text (bool xy_absolute, } } -void +void OASISReader::do_read_rectangle (bool xy_absolute, - db::cell_index_type cell_index, + db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2095,13 +2132,13 @@ OASISReader::do_read_rectangle (bool xy_absolute, if (m & 0x40) { mm_geometry_w = get_ucoord_as_distance (); - } + } if (m & 0x80) { mm_geometry_h = mm_geometry_w; // TODO: really? } else { if (m & 0x20) { mm_geometry_h = get_ucoord_as_distance (); - } + } } if (m & 0x10) { @@ -2160,7 +2197,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2185,7 +2222,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } else { - + std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { @@ -2202,7 +2239,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } -void +void OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2290,7 +2327,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2310,7 +2347,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } ++p; } - + } } @@ -2345,7 +2382,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } } -void +void OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2459,7 +2496,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2481,7 +2518,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } - + } } @@ -2516,7 +2553,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } -void +void OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2628,7 +2665,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2675,7 +2712,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index } } -void +void OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2736,21 +2773,21 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index }, // type 1 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 2 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 3 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } @@ -2764,147 +2801,147 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index }, // type 5 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 6 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 7 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, -1, 0, 1 }, { 1, 0, 0, 0 } }, // type 8 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 9 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 10 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 11 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 12 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 1, 0 } }, // type 13 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 14 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 15 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 16 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 17 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 18 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 19 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 1, 0 } }, // type 20 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 0, 2, 0, 0 }, { 0, 0, 0, 0 } }, // type 21 { - { 0, 0, 0, 1 }, + { 0, 0, 0, 1 }, { 0, 2, 0, 1 }, { 0, 1, 0, 0 }, { 0, 0, 0, 1 } }, // type 22 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 2, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 23 { - { 1, 0, 0, 0 }, + { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 2, 0 }, { 1, 0, 0, 0 } }, // type 24 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 25 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 } @@ -2930,7 +2967,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index if (m[3] != 0) y += m[3] * mm_geometry_h.get (); pts [i] = db::Point (x, y); - + if (x > w) w = x; if (y > h) h = y; @@ -2987,7 +3024,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -3034,7 +3071,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index } } -void +void OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -3123,7 +3160,7 @@ OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, d array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -3206,10 +3243,10 @@ OASISReader::reset_modal_variables () mm_last_value_list.reset (); } -void +void OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) { - // clears current instance list + // clears current instance list m_instances.clear (); m_instances_with_props.clear (); @@ -3376,11 +3413,11 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) layout.cell (cell_index).prop_id (layout.properties_repository ().properties_id (cell_properties)); } - // insert all instances collected (inserting them once is + // insert all instances collected (inserting them once is // more effective than doing this every time) if (! m_instances.empty ()) { layout.cell (cell_index).insert (m_instances.begin (), m_instances.end ()); - // clear immediately, because if the cell is cleared before the instances are deleted, the + // clear immediately, because if the cell is cleared before the instances are deleted, the // array pointers (living in the repository) may no longer be valid m_instances.clear (); } From 551a80b70be379f4e2d6de88b460daa66c20179e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 23:28:38 +0200 Subject: [PATCH 10/35] Preparing OASIS test mode for forward references --- .../streamers/oasis/db_plugin/dbOASISFormat.h | 8 +- .../oasis/db_plugin/dbOASISWriter.cc | 700 +++++++++--------- .../streamers/oasis/db_plugin/dbOASISWriter.h | 8 + 3 files changed, 373 insertions(+), 343 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h index 05cc203ea..12e68c3de 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h @@ -101,7 +101,8 @@ public: * @brief The constructor */ OASISWriterOptions () - : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*") + : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), + write_std_properties (1), subst_char ("*"), tables_at_end (false) { // .. nothing yet .. } @@ -166,6 +167,11 @@ public: */ std::string subst_char; + /** + * @brief Hidden option, for testing mainly: write tables at end to force forward references + */ + bool tables_at_end; + /** * @brief Implementation of FormatSpecificWriterOptions */ diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index bb4b13576..a47e7ffcf 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -666,9 +666,11 @@ OASISWriter::OASISWriter () mp_layout (0), mp_cell (0), m_layer (0), m_datatype (0), + m_write_context_info (false), m_in_cblock (false), m_propname_id (0), m_propstring_id (0), + m_textstring_id (0), m_proptables_written (false), m_progress (tl::to_string (tr ("Writing OASIS file")), 10000) { @@ -1150,6 +1152,313 @@ OASISWriter::reset_modal_variables () mm_last_value_list.reset (); } +void +OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write the property names collected so far in the order of the ID's. + + std::vector > rev_pn; + rev_pn.reserve (m_propnames.size ()); + for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) { + rev_pn.push_back (std::make_pair (p->second, p->first)); + } + std::sort (rev_pn.begin (), rev_pn.end ()); + + for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) { + tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); + begin_table (propnames_table_pos); + write_record_id (7); + write_nstring (p->second.c_str ()); + } + + // collect and write the future property names + + std::set prop_ids_done; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + + if (cref.prop_id () != 0) { + begin_table (propnames_table_pos); + emit_propname_def (cref.prop_id ()); + } + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (inst->prop_id ()); + begin_table (propnames_table_pos); + emit_propname_def (inst->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (shape->prop_id ()); + begin_table (propnames_table_pos); + emit_propname_def (shape->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + shape.finish_array (); + } + } + + } + + // if needed, emit property name required for the PCell or meta info context information + + if (m_write_context_info && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { + + bool has_context = false; + for (auto cell = cells.begin (); cell != cells.end () && ! has_context; ++cell) { + LayoutOrCellContextInfo ci; + has_context = layout.has_context_info (*cell) && layout.get_context_info (*cell, ci); + } + + if (has_context) { + m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id++)); + begin_table (propnames_table_pos); + write_record_id (7); + write_nstring (klayout_context_name); + } + + } + + end_table (propnames_table_pos); +} + +void +OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write the property strings collected so far in the order of the ID's. + + std::vector > rev_ps; + rev_ps.reserve (m_propstrings.size ()); + for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { + rev_ps.push_back (std::make_pair (p->second, &p->first)); + } + std::sort (rev_ps.begin (), rev_ps.end ()); + + tl_assert (rev_ps.size () == size_t (m_propstring_id)); + + for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) { + tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); + begin_table (propstrings_table_pos); + write_record_id (9); + write_nstring (p->second->c_str ()); + } + + // collect and write the future property strings + + std::set prop_ids_done; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + + if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (cref.prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (cref.prop_id ()); + } + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (inst->prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (inst->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (shape->prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (shape->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + shape.finish_array (); + } + } + + } + + if (m_write_context_info) { + + // emit property string id's required for the PCell and meta info context information + std::vector context_prop_strings; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + m_progress.set (mp_stream->pos ()); + context_prop_strings.clear (); + + if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) { + + for (auto c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { + if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (c->c_str ()); + ++m_propstring_id; + } + } + + } + + } + + } + + end_table (propstrings_table_pos); +} + +void +OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const db::Layout &layout) +{ + bool sequential = true; + for (auto cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { + sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); + } + + // CELLNAME (implicit or explicit) + for (auto cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { + + begin_table (cellnames_table_pos); + + write_record_id (sequential ? 3 : 4); + write_nstring (layout.cell_name (*cell)); + if (! sequential) { + write ((unsigned long) *cell); + } + + if (m_options.write_std_properties > 1) { + + reset_modal_variables (); + + // write S_BOUNDING_BOX entries + + std::vector values; + + // TODO: how to set the "depends on external cells" flag? + db::Box bbox = layout.cell (*cell).bbox (); + if (bbox.empty ()) { + // empty box + values.push_back (tl::Variant ((unsigned int) 0x2)); + bbox = db::Box (0, 0, 0, 0); + } else { + values.push_back (tl::Variant ((unsigned int) 0x0)); + } + + values.push_back (tl::Variant (bbox.left ())); + values.push_back (tl::Variant (bbox.bottom ())); + values.push_back (tl::Variant (bbox.width ())); + values.push_back (tl::Variant (bbox.height ())); + + write_property_def (s_bounding_box_name, values, true); + + // PROPERTY record with S_CELL_OFFSET + if (cell_positions) { + std::map::const_iterator pp = cell_positions->find (*cell); + if (pp != cell_positions->end ()) { + write_property_def (s_cell_offset_name, tl::Variant (pp->second), true); + } else { + write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true); + } + } + + } + + } + + end_table (cellnames_table_pos); +} + +void +OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write present text strings + + // collect present strings by ID + std::vector > rev_ts; + rev_ts.reserve (m_textstrings.size ()); + for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) { + rev_ts.push_back (std::make_pair (p->second, &p->first)); + } + std::sort (rev_ts.begin (), rev_ts.end ()); + + tl_assert (rev_ts.size () == size_t (m_textstring_id)); + + for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) { + tl_assert (t->first == (unsigned long)(t - rev_ts.begin ())); + begin_table (textstrings_table_pos); + write_record_id (5); + write_nstring (t->second->c_str ()); + } + + // collect future test strings + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (m_textstrings.insert (std::make_pair (shape->text_string (), m_textstring_id)).second) { + begin_table (textstrings_table_pos); + write_record_id (5); + write_astring (shape->text_string ()); + ++m_textstring_id; + m_progress.set (mp_stream->pos ()); + } + ++shape; + } + } + + } + + end_table (textstrings_table_pos); +} + +void +OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vector > &layers) +{ + for (auto l = layers.begin (); l != layers.end (); ++l) { + + if (! l->second.name.empty ()) { + + begin_table (layernames_table_pos); + + // write mappings to text layer and shape layers + write_record_id (11); + write_nstring (l->second.name.c_str ()); + write_byte (3); + write ((unsigned long) l->second.layer); + write_byte (3); + write ((unsigned long) l->second.datatype); + + write_record_id (12); + write_nstring (l->second.name.c_str ()); + write_byte (3); + write ((unsigned long) l->second.layer); + write_byte (3); + write ((unsigned long) l->second.datatype); + + m_progress.set (mp_stream->pos ()); + + } + + } + + end_table (layernames_table_pos); +} + static bool must_write_cell (const db::Cell &cref) { // Don't write proxy cells which are not employed @@ -1162,6 +1471,7 @@ static bool skip_cell_body (const db::Cell &cref) return cref.is_ghost_cell () && cref.empty (); } + void OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -1172,6 +1482,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save m_layer = m_datatype = 0; m_in_cblock = false; m_cblock_buffer.clear (); + m_write_context_info = options.write_context_info (); m_options = options.get_options (); mp_stream = &stream; @@ -1252,6 +1563,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // We will collect the standard properties here: m_propstring_id = m_propname_id = 0; + m_textstring_id = 0; m_proptables_written = false; std::vector > init_props; @@ -1321,295 +1633,28 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - // build property name and value string tables - - { + // write the tables - // write the property names collected so far in the order of the ID's. - - std::vector > rev_pn; - rev_pn.reserve (m_propnames.size ()); - for (std::map ::const_iterator p = m_propnames.begin (); p != m_propnames.end (); ++p) { - rev_pn.push_back (std::make_pair (p->second, p->first)); - } - std::sort (rev_pn.begin (), rev_pn.end ()); + if (! m_options.tables_at_end) { - for (std::vector >::const_iterator p = rev_pn.begin (); p != rev_pn.end (); ++p) { - tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); - begin_table (propnames_table_pos); - write_record_id (7); - write_nstring (p->second.c_str ()); + 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); } - // collect and write the future property names - - std::set prop_ids_done; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - - if (cref.prop_id () != 0) { - begin_table (propnames_table_pos); - emit_propname_def (cref.prop_id ()); - } - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (inst->prop_id ()); - begin_table (propnames_table_pos); - emit_propname_def (inst->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - } - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (shape->prop_id ()); - begin_table (propnames_table_pos); - emit_propname_def (shape->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - shape.finish_array (); - } - } - - } - - // if needed, emit property name required for the PCell or meta info context information - - if (options.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); + write_textstring_table (textstrings_table_pos, cells, layout, layers); + write_layername_table (layernames_table_pos, layers); } - { - - // write the property strings collected so far in the order of the ID's. - - std::vector > rev_ps; - rev_ps.reserve (m_propstrings.size ()); - for (std::map ::const_iterator p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { - rev_ps.push_back (std::make_pair (p->second, p->first)); - } - std::sort (rev_ps.begin (), rev_ps.end ()); - - for (std::vector >::const_iterator p = rev_ps.begin (); p != rev_ps.end (); ++p) { - tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); - begin_table (propstrings_table_pos); - write_record_id (9); - write_nstring (p->second.c_str ()); - } - - // collect and write the future property strings - - std::set prop_ids_done; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - - if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (cref.prop_id ()); - begin_table (propnames_table_pos); - emit_propstring_def (cref.prop_id ()); - } - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (inst->prop_id ()); - begin_table (propstrings_table_pos); - emit_propstring_def (inst->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - } - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (shape->prop_id ()); - begin_table (propstrings_table_pos); - emit_propstring_def (shape->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - shape.finish_array (); - } - } - - } - - if (options.write_context_info ()) { - - // emit property string id's required for the PCell and meta info context information - std::vector context_prop_strings; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - m_progress.set (mp_stream->pos ()); - context_prop_strings.clear (); - - if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) { - - for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { - if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { - begin_table (propstrings_table_pos); - write_record_id (9); - write_bstring (c->c_str ()); - ++m_propstring_id; - } - } - - } - - } - - } - - end_table (propstrings_table_pos); - - } - - // Now we cannot open new property ID's in strict mode - m_proptables_written = true; - - // build cell name table now in non-strict mode (in strict mode it is written at the - // end because then we have the cell positions fo S_CELL_OFFSET) - - if (! m_options.strict_mode) { - - size_t pos = 0; - - bool sequential = true; - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { - sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); - } - - // CELLNAME (implicit or explicit) - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { - - begin_table (pos); - - write_record_id (sequential ? 3 : 4); - write_nstring (layout.cell_name (*cell)); - if (! sequential) { - write ((unsigned long) *cell); - } - - if (m_options.write_std_properties > 1) { - - reset_modal_variables (); - - // write S_BOUNDING_BOX entries - - std::vector values; - - // TODO: how to set the "depends on external cells" flag? - db::Box bbox = layout.cell (*cell).bbox (); - if (bbox.empty ()) { - // empty box - values.push_back (tl::Variant ((unsigned int) 0x2)); - bbox = db::Box (0, 0, 0, 0); - } else { - values.push_back (tl::Variant ((unsigned int) 0x0)); - } - - values.push_back (tl::Variant (bbox.left ())); - values.push_back (tl::Variant (bbox.bottom ())); - values.push_back (tl::Variant (bbox.width ())); - values.push_back (tl::Variant (bbox.height ())); - - write_property_def (s_bounding_box_name, values, true); - - } - - } - - end_table (pos); - - } - - // build text string table - - { - - unsigned int id = 0; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (m_textstrings.insert (std::make_pair (shape->text_string (), id)).second) { - begin_table (textstrings_table_pos); - write_record_id (5); - write_astring (shape->text_string ()); - ++id; - m_progress.set (mp_stream->pos ()); - } - ++shape; - } - } - - } - - end_table (textstrings_table_pos); - - } - - // write layernames table - - { - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (! l->second.name.empty ()) { - - begin_table (layernames_table_pos); - - // write mappings to text layer and shape layers - write_record_id (11); - write_nstring (l->second.name.c_str ()); - write_byte (3); - write ((unsigned long) l->second.layer); - write_byte (3); - write ((unsigned long) l->second.datatype); - - write_record_id (12); - write_nstring (l->second.name.c_str ()); - write_byte (3); - write ((unsigned long) l->second.layer); - write_byte (3); - write ((unsigned long) l->second.datatype); - - m_progress.set (mp_stream->pos ()); - - } - - } - - end_table (layernames_table_pos); - - } + // write cells for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { @@ -1694,66 +1739,31 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } + // write the tables if at end + + if (m_options.tables_at_end) { + + // do not consider future items as everything has been collected + std::vector no_cells; + std::vector > no_layers; + + write_propname_table (propnames_table_pos, no_cells, layout, no_layers); + write_propstring_table (propstrings_table_pos, no_cells, layout, no_layers); + + // Now we cannot open new property ID's in strict mode + m_proptables_written = true; + + write_textstring_table (textstrings_table_pos, no_cells, layout, no_layers); + + // write all layers here + write_layername_table (layernames_table_pos, layers); + + } + // write cell table at the end in strict mode (in that mode we need the cell positions // for the S_CELL_OFFSET properties) - - if (m_options.strict_mode) { - - bool sequential = true; - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { - sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); - } - - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { - - begin_table (cellnames_table_pos); - - // CELLNAME (explicit) - write_record_id (sequential ? 3 : 4); - write_nstring (layout.cell_name (*cell)); - if (! sequential) { - write ((unsigned long) *cell); - } - - reset_modal_variables (); - - if (m_options.write_std_properties > 1) { - - // write S_BOUNDING_BOX entries - - std::vector values; - - // TODO: how to set the "depends on external cells" flag? - db::Box bbox = layout.cell (*cell).bbox (); - if (bbox.empty ()) { - // empty box - values.push_back (tl::Variant ((unsigned int) 0x2)); - bbox = db::Box (0, 0, 0, 0); - } else { - values.push_back (tl::Variant ((unsigned int) 0x0)); - } - - values.push_back (tl::Variant (bbox.left ())); - values.push_back (tl::Variant (bbox.bottom ())); - values.push_back (tl::Variant (bbox.width ())); - values.push_back (tl::Variant (bbox.height ())); - - write_property_def (s_bounding_box_name, values, true); - - } - - // PROPERTY record with S_CELL_OFFSET - std::map::const_iterator pp = cell_positions.find (*cell); - if (pp != cell_positions.end ()) { - write_property_def (s_cell_offset_name, tl::Variant (pp->second), true); - } else { - write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true); - } - - } - - end_table (cellnames_table_pos); - + if (m_options.tables_at_end || m_options.strict_mode) { + write_cellname_table (cellnames_table_pos, cells_by_index, &cell_positions, layout); } // END record @@ -2349,9 +2359,15 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const m_progress.set (mp_stream->pos ()); db::Trans trans = text.trans (); + + unsigned long text_id = 0; std::map ::const_iterator ts = m_textstrings.find (text.string ()); - tl_assert (ts != m_textstrings.end ()); - unsigned long text_id = ts->second; + if (ts == m_textstrings.end ()) { + text_id = m_textstring_id++; + m_textstrings.insert (std::make_pair (text.string (), text_id)); + } else { + text_id = ts->second; + } unsigned char info = 0x20; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h index 6a853f370..0dd319515 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h @@ -203,12 +203,14 @@ private: const db::Cell *mp_cell; int m_layer; int m_datatype; + bool m_write_context_info; std::vector m_pointlist; tl::OutputMemoryStream m_cblock_buffer; tl::OutputMemoryStream m_cblock_compressed; bool m_in_cblock; unsigned long m_propname_id; unsigned long m_propstring_id; + unsigned long m_textstring_id; bool m_proptables_written; std::map m_textstrings; @@ -308,6 +310,12 @@ private: void write_pointlist (const std::vector &pointlist, bool for_polygons); void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep); + + void write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const Layout &layout); + void write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_layername_table (size_t &layernames_table_pos, const std::vector > &layers); }; } // namespace db From bc30887488829d03892db14754964d3066ce21f1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 23:49:01 +0200 Subject: [PATCH 11/35] Include table-at-end tests for OASIS writer/reader --- .../streamers/oasis/db_plugin/dbOASISWriter.cc | 2 +- .../oasis/unit_tests/dbOASISWriterTests.cc | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index a47e7ffcf..0af9dbb65 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1248,7 +1248,7 @@ OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::v 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 ()); + write_bstring (p->second->c_str ()); } // collect and write the future property strings diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index a41c7ab72..dba15c813 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -33,7 +33,7 @@ #include -void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress) +void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end) { { db::Manager m (false); @@ -60,6 +60,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = false; oasis_options.strict_mode = false; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); writer.write (layout, stream, options); } @@ -115,6 +116,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = true; oasis_options.strict_mode = true; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); writer.write (layout, stream, options); } @@ -164,6 +166,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = false; oasis_options.strict_mode = false; + oasis_options.tables_at_end = tables_at_end; oasis_options.write_std_properties = 2; options.set_options (oasis_options); writer.write (layout, stream, options); @@ -214,6 +217,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = true; oasis_options.strict_mode = true; + oasis_options.tables_at_end = tables_at_end; oasis_options.write_std_properties = 2; options.set_options (oasis_options); writer.write (layout, stream, options); @@ -255,6 +259,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.compression_level = compr; oasis_options.recompress = recompress; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); options.set_scale_factor (3.0); options.set_dbu (0.0005); @@ -308,11 +313,14 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com void run_test (tl::TestBase *_this, const char *file, bool scaling_test = true) { for (int recompress = 0; recompress < 2; ++recompress) { - run_test (_this, file, scaling_test, 0, recompress); - run_test (_this, file, scaling_test, 1, recompress); - run_test (_this, file, scaling_test, 2, recompress); - run_test (_this, file, scaling_test, 10, recompress); + run_test (_this, file, scaling_test, 0, recompress, false); + run_test (_this, file, scaling_test, 1, recompress, false); + run_test (_this, file, scaling_test, 2, recompress, false); + run_test (_this, file, scaling_test, 10, recompress, false); } + + // tables at end + run_test (_this, file, scaling_test, 2, false, true); } TEST(1) From d1f962a2284d030bca3db4f5cd20e670d5c29d8f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Apr 2023 23:47:47 +0200 Subject: [PATCH 12/35] Implementing meta data persistency also with strict OASIS and forward references --- .../oasis/db_plugin/dbOASISReader.cc | 90 +++++++++++++------ .../streamers/oasis/db_plugin/dbOASISReader.h | 2 + .../oasis/db_plugin/dbOASISWriter.cc | 20 ++++- .../oasis/unit_tests/dbOASISWriterTests.cc | 37 +++++++- 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 2d9c86dca..d87c74e6c 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -600,6 +600,16 @@ static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; static const char *s_gds_property_propname = "S_GDS_PROPERTY"; +static LayoutOrCellContextInfo +make_context_info (const std::vector &context_properties) +{ + std::vector context_strings; + context_strings.reserve (context_properties.size ()); + for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { + context_strings.push_back (s->to_string ()); + } + return LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); +} void OASISReader::do_read (db::Layout &layout) @@ -678,6 +688,8 @@ OASISReader::do_read (db::Layout &layout) m_propstrings.clear (); m_propnames.clear (); + m_context_strings_per_cell.clear (); + m_instances.clear (); m_instances_with_props.clear (); @@ -878,20 +890,28 @@ OASISReader::do_read (db::Layout &layout) // exchange the properties in the repository // first locate all property sets that are affected - std::vector pids; - for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { + std::map > cells_by_pid; + for (auto p = rep.begin (); p != rep.end (); ++p) { if (p->second.find (pf->second) != p->second.end ()) { - pids.push_back (p->first); + cells_by_pid.insert (std::make_pair (p->first, std::vector ())); + } + } + + // find cells using a specific pid + for (auto i = layout.begin (); i != layout.end (); ++i) { + auto cc = cells_by_pid.find (i->prop_id ()); + if (cc != cells_by_pid.end ()) { + cc->second.push_back (i->cell_index ()); } } // create new property sets for the ones we found - for (std::vector ::const_iterator pid = pids.begin (); pid != pids.end (); ++pid) { + for (auto pid = cells_by_pid.begin (); pid != cells_by_pid.end (); ++pid) { - const db::PropertiesRepository::properties_set &old_set = rep.properties (*pid); + const db::PropertiesRepository::properties_set &old_set = rep.properties (pid->first); db::PropertiesRepository::properties_set new_set; - for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) { + for (auto s = old_set.begin (); s != old_set.end (); ++s) { if (s->first == pf->second && is_s_gds_property) { // S_GDS_PROPERTY translation @@ -903,7 +923,9 @@ OASISReader::do_read (db::Layout &layout) } else if (s->first == pf->second && is_klayout_context_property) { - if (*pid == layout.prop_id ()) { + auto pid2c = cells_by_pid.find (pid->first); + + if (pid->first == layout.prop_id ()) { // feed context strings from klayout context property if (s->second.is_list ()) { for (auto l = s->second.begin (); l != s->second.end (); ++l) { @@ -912,9 +934,18 @@ OASISReader::do_read (db::Layout &layout) } else { context_properties.push_back (s->second); } - } else { - // TODO: should update that in cells (in case we encounter forward-referenced context properties - // for cells) + } + + // feed cell-specific context strings from klayout context property + for (auto c = pid2c->second.begin (); c != pid2c->second.end (); ++c) { + std::vector &vl = m_context_strings_per_cell [*c]; + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + vl.push_back (*l); + } + } else { + vl.push_back (s->second); + } } } else { @@ -922,7 +953,7 @@ OASISReader::do_read (db::Layout &layout) } } - rep.change_properties (*pid, new_set); + rep.change_properties (pid->first, new_set); } @@ -1207,6 +1238,11 @@ OASISReader::do_read (db::Layout &layout) for (auto i = context_properties.begin (); i != context_properties.end (); ++i) { replace_forward_references_in_variant (*i); } + for (auto c = m_context_strings_per_cell.begin (); c != m_context_strings_per_cell.end (); ++c) { + for (auto i = c->second.begin (); i != c->second.end (); ++i) { + replace_forward_references_in_variant (*i); + } + } for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { @@ -1241,15 +1277,22 @@ OASISReader::do_read (db::Layout &layout) // Restore layout meta info if (! context_properties.empty ()) { - std::vector context_strings; - context_strings.reserve (context_properties.size ()); - for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { - context_strings.push_back (s->to_string ()); - } - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + LayoutOrCellContextInfo info = make_context_info (context_properties); layout.fill_meta_info_from_context (info); } + // Restore proxy cell (link to PCell or Library) and cell meta info + if (! m_context_strings_per_cell.empty ()) { + CommonReaderLayerMapping layer_mapping (this, &layout); + for (auto cc = m_context_strings_per_cell.begin (); cc != m_context_strings_per_cell.end (); ++cc) { + LayoutOrCellContextInfo info = make_context_info (cc->second); + if (info.has_proxy_info ()) { + layout.recover_proxy_as (cc->first, info, &layer_mapping); + } + layout.fill_meta_info_from_context (cc->first, info); + } + } + // Check the table offsets vs. real occurrence if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname)); @@ -3255,7 +3298,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) bool xy_absolute = true; bool has_context = false; - std::vector context_strings; + std::vector context_strings; db::PropertiesRepository::properties_set cell_properties; // read next record @@ -3326,7 +3369,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) has_context = true; context_strings.reserve (mm_last_value_list.get ().size ()); for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { - context_strings.push_back (v->to_string ()); + context_strings.push_back (*v); } } else { // store layout properties @@ -3427,14 +3470,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) m_instances_with_props.clear (); } - // Restore proxy cell (link to PCell or Library) and cell meta info + // store the context strings for later if (has_context) { - CommonReaderLayerMapping layer_mapping (this, &layout); - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - if (info.has_proxy_info ()) { - layout.recover_proxy_as (cell_index, info, &layer_mapping); - } - layout.fill_meta_info_from_context (cell_index, info); + m_context_strings_per_cell [cell_index].swap (context_strings); } m_cellname = ""; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index 78d6b9c41..3e9ee8bf9 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -172,6 +172,8 @@ private: std::map m_propstrings; std::map m_propnames; + std::map > m_context_strings_per_cell; + tl::vector m_instances; tl::vector m_instances_with_props; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 0af9dbb65..6e54e9bfd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1691,17 +1691,29 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_record_id (28); write_byte (char (0xf6)); + unsigned long pnid = 0; std::map ::const_iterator pni = m_propnames.find (klayout_context_name); - tl_assert (pni != m_propnames.end ()); - write (pni->second); + if (pni == m_propnames.end ()) { + pnid = m_propname_id++; + m_propnames.insert (std::make_pair (klayout_context_name, pnid)); + } else { + pnid = pni->second; + } + write (pnid); write ((unsigned long) context_prop_strings.size ()); for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { write_byte (14); // b-string by reference number + unsigned long psid = 0; std::map ::const_iterator psi = m_propstrings.find (*c); - tl_assert (psi != m_propstrings.end ()); - write (psi->second); + if (psi == m_propstrings.end ()) { + psid = m_propstring_id++; + m_propstrings.insert (std::make_pair (*c, psid)).second; + } else { + psid = psi->second; + } + write (psid); } mm_last_property_name = klayout_context_name; diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index dba15c813..ac574affb 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -1870,7 +1870,8 @@ TEST(120_IrregularInstRepetitions) } // Meta info -TEST(130) +static void +run_test130 (tl::TestBase *_this, bool strict, bool tables_at_end) { db::Layout layout_org; @@ -1883,12 +1884,16 @@ TEST(130) 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 = tl::TestBase::tmp_file ("tmp_OASISWriter_130.oas"); + 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); } @@ -1915,12 +1920,16 @@ TEST(130) EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); - tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130b.oas"); + 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); @@ -1945,3 +1954,25 @@ TEST(130) 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); +} + From d9e0d107b1312e81464f56466bc89f31bbdad0dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Apr 2023 23:33:42 +0200 Subject: [PATCH 13/35] Bug fixes, RBA tests for meta info --- src/db/db/dbLayout.cc | 23 ++++++++-- src/db/db/dbLayout.h | 26 ++++++++++++ src/db/db/gsiDeclDbCell.cc | 13 +++--- src/db/db/gsiDeclDbLayout.cc | 14 ++++--- src/db/db/gsiDeclDbMetaInfo.cc | 20 ++++++++- testdata/ruby/dbLayoutTests2.rb | 74 ++++++++++++++++++++++++++++----- 6 files changed, 144 insertions(+), 26 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index a03a874a7..30b732167 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1799,7 +1799,7 @@ 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.find (ci)) { + if (m != m_meta_info_by_cell.end ()) { return m->second.begin (); } else { return s_empty_meta.begin (); @@ -1810,7 +1810,7 @@ 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.find (ci)) { + if (m != m_meta_info_by_cell.end ()) { return m->second.end (); } else { return s_empty_meta.end (); @@ -1871,6 +1871,12 @@ Layout::meta_info (meta_info_name_id_type name_id) const 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) { @@ -1907,7 +1913,18 @@ Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const 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); diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index c82e5cb14..73918fb5f 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1932,6 +1932,19 @@ public: */ 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 */ @@ -1954,6 +1967,19 @@ public: */ void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i); + /** + * @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. diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index ecff73f45..936fceeff 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1014,14 +1014,15 @@ static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::strin } } -static MetaInfo cell_meta_info (db::Cell *cell, const std::string &name) +static MetaInfo *cell_meta_info (db::Cell *cell, const std::string &name) { if (! cell->layout ()) { - static MetaInfo null_value; - return null_value; - } else { + 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 MetaInfo (name, value); + return new MetaInfo (name, value); + } else { + return 0; } } @@ -1857,7 +1858,7 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.28.8." ) + - gsi::method_ext ("meta_info", &cell_meta_info, gsi::arg ("name"), + 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" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index c3734d715..da083b9ee 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -908,10 +908,14 @@ static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi) layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value)); } -static MetaInfo layout_get_meta_info (db::Layout *layout, const std::string &name) +static MetaInfo *layout_get_meta_info (db::Layout *layout, const std::string &name) { - const db::MetaInfo &value = layout->meta_info (name); - return MetaInfo (name, value); + if (layout->has_meta_info (name)) { + const db::MetaInfo &value = layout->meta_info (name); + return new MetaInfo (name, value); + } else { + return 0; + } } static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name) @@ -1073,11 +1077,11 @@ Class decl_Layout ("db", "Layout", "\n" "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::method_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"), + 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, a default object with empty fields will be returned.\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" ) + diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index d4161b0e6..9835d86c4 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -27,7 +27,7 @@ namespace gsi { -static MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description, bool persisted) +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); } @@ -101,7 +101,7 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", gsi::method_ext ("description=", &layout_meta_set_description, "@brief Sets the description of the layout meta info object\n" ) + - gsi::method_ext ("persisted", &layout_meta_get_persisted, + 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" ) + @@ -118,6 +118,22 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "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" + "Note that only meta information marked with \\is_persisted? == true are 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" diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index e204b2e3c..32b291f2a 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -1044,36 +1044,39 @@ class DBLayoutTests2_TestClass < TestBase end # Meta information - def test_12 + def test_12a mi = RBA::LayoutMetaInfo::new("myinfo", "a") assert_equal(mi.name, "myinfo") assert_equal(mi.description, "") assert_equal(mi.value, "a") + assert_equal(mi.is_persisted?, false) mi.name = "x" mi.description = "y" mi.value = "z" + mi.persisted = true assert_equal(mi.name, "x") assert_equal(mi.description, "y") assert_equal(mi.value, "z") + assert_equal(mi.is_persisted?, true) ly = RBA::Layout::new ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "a")) - ly.add_meta_info(RBA::LayoutMetaInfo::new("another", "42", "description")) + ly.add_meta_info(RBA::LayoutMetaInfo::new("another", 42, "description")) assert_equal(ly.meta_info_value("myinfo"), "a") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) a = [] ly.each_meta_info { |mi| a << mi.name } assert_equal(a.join(","), "myinfo,another") a = [] - ly.each_meta_info { |mi| a << mi.value } + ly.each_meta_info { |mi| a << mi.value.to_s } assert_equal(a.join(","), "a,42") a = [] ly.each_meta_info { |mi| a << mi.description } @@ -1081,13 +1084,64 @@ class DBLayoutTests2_TestClass < TestBase ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "b")) assert_equal(ly.meta_info_value("myinfo"), "b") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) + + ly.remove_meta_info("doesnotexist") # should not fail ly.remove_meta_info("myinfo") - assert_equal(ly.meta_info_value("myinfo"), "") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("myinfo"), nil) + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) + + assert_equal(ly.meta_info("doesnotexist"), nil) + assert_equal(ly.meta_info("another").value, 42) + assert_equal(ly.meta_info("another").description, "description") + + ly.clear_meta_info + assert_equal(ly.meta_info_value("another"), nil) + assert_equal(ly.meta_info("another"), nil) + + # cellwise + + c1 = ly.create_cell("X") + c2 = ly.create_cell("U") + + c1.add_meta_info(RBA::LayoutMetaInfo::new("a", true)) + c1.add_meta_info(RBA::LayoutMetaInfo::new("b", [ 1, 17, 42 ])) + + assert_equal(c2.meta_info("a"), nil) + assert_equal(c2.meta_info_value("a"), nil) + + a = [] + c2.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "") + + assert_equal(c1.meta_info("a").value, true) + assert_equal(c1.meta_info("b").value, [ 1, 17, 42 ]) + assert_equal(c1.meta_info_value("b"), [ 1, 17, 42 ]) + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true,[1, 17, 42]") + + c1.remove_meta_info("doesnotexist") # should not fail + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true,[1, 17, 42]") + + c1.remove_meta_info("b") + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true") + + c1.clear_meta_info + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "") end From 70b6306635dca0c5ca076bb045e79bd7872e067b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Apr 2023 00:16:12 +0200 Subject: [PATCH 14/35] Tests for complex data for meta info, doc updates --- src/db/db/gsiDeclDbMetaInfo.cc | 35 ++++++++++++++++--- .../streamers/gds2/unit_tests/dbGDS2Writer.cc | 28 +++++++++++++-- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index 9835d86c4..ef760d438 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -86,26 +86,26 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", 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::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::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::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::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" ), @@ -131,12 +131,37 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", " keys for user properties. @/li\n" "@/ul\n" "\n" - "Note that only meta information marked with \\is_persisted? == true are stored in GDS or OASIS files. " + "Elementary (serializable) objects are: \\Box, \\DBox, \\Edge, \\DEdge, \\EdgePair, \\DEdgePair, " + "\\EdgePairs, \\Edges, \\LayerProperties, \\Matrix2d, \\Matrix3d, \\Path, \\DPath, \\Point, \\DPoint, " + "\\Polygon, \\DPolygon, \\SimplePolygon, \\DSimplePolygon, \\Region, \\Text, \\DText, \\Texts, " + "\\Trans, \\DTrans, \\CplxTrans, \\ICplxTrans, \\DCplxTrans, \\VCplxTrans, \\Vector, \\DVector " + "(list may not be complete).\n" + "\n" + "KLayout itself also generates meta information with specific keys. " + "For disambiguation, namespaces can be established by prefixing " + "the key strings with some unique identifier in XML fashion, like a domain name - " + "e.g. 'example.com:key'.\n" + "\n" + "@b Note: @/b only meta information marked with \\is_persisted? == true is stored in GDS or OASIS files. " "This is not the default setting, so you need to explicitly set that flag.\n" "\n" "See also \\Layout#each_meta_info, \\Layout#meta_info_value, \\Layout#meta_info and \\Layout#remove_meta_info as " "well as the corresponding \\Cell methods.\n" "\n" + "An example of how to attach persisted meta information to a cell is here:\n" + "\n" + "@code\n" + "ly = RBA::Layout::new\n" + "c1 = ly.create_cell(\"C1\")\n" + "\n" + "mi = RBA::LayoutMetaInfo::new(\"the-answer\", 42.0)\n" + "mi.persisted = true\n" + "c1.add_meta_info(mi)\n" + "\n" + "# will now hold this piece of meta information attached to cell 'C1':\n" + "ly.write(\"to.gds\")\n" + "@/code\n" + "\n" "This class has been introduced in version 0.25 and was extended in version 0.28.8." ); diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index 648a5105f..20499fc60 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1194,7 +1194,7 @@ TEST(121) } // Meta info -TEST(130) +TEST(130a) { db::Layout layout_org; @@ -1207,7 +1207,19 @@ TEST(130) 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 = tl::TestBase::tmp_file ("tmp_GDS2Writer_130.gds"); + // 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); @@ -1224,11 +1236,19 @@ TEST(130) 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; @@ -1237,6 +1257,10 @@ TEST(130) 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"); From c8d97871fdd1e64897cd7af94d68aacebdd3a5da Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Apr 2023 21:26:39 +0200 Subject: [PATCH 15/35] Enabling build of version agnostic plugins --- src/db/db/dbMetaInfo.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index 16166455d..50de04291 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -31,6 +31,9 @@ namespace db { +// Switch for version-agnostic code +#define KLAYOUT_META_INFO_V2 + /** * @brief A structure describing the meta information from the reader * From 707ebc4114b5beb0cc9a6058a49544c68c0400ef Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Apr 2023 23:27:42 +0200 Subject: [PATCH 16/35] Copying meta information of cells on various occasions too (e.g. clipboard copy & paste) --- src/db/db/dbCellMapping.cc | 2 +- src/db/db/dbCellVariants.cc | 1 + src/db/db/dbClip.cc | 2 +- src/db/db/dbClipboardData.cc | 8 +++++--- src/db/db/dbLayout.cc | 16 +++++++++++++++- src/db/db/dbLayout.h | 30 ++++++++++++++++++++++++++++++ src/db/db/dbLayoutUtils.cc | 2 +- src/edt/edt/edtMainService.cc | 2 +- 8 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index c4255f762..1cb4d698d 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -356,7 +356,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ()) && (! include_cells || include_cells->find (*b) != include_cells->end ())) { - db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b)); + db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); new_cells.push_back (new_cell); new_cells_b.push_back (*b); diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 741dded37..a71d9eb52 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -275,6 +275,7 @@ VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell var_name += "$VAR" + tl::to_string (index); ci_var = layout.add_cell (var_name.c_str ()); + layout.add_meta_info (ci_var, layout.begin_meta (*c), layout.end_meta (*c)); copy_shapes (layout, ci_var, *c); // a new entry for the variant diff --git a/src/db/db/dbClip.cc b/src/db/db/dbClip.cc index 72c926afc..d3d7bde90 100644 --- a/src/db/db/dbClip.cc +++ b/src/db/db/dbClip.cc @@ -556,7 +556,7 @@ make_clip_variants (const db::Layout &layout, for (std::map , db::cell_index_type>::iterator v = variants.begin (); v != variants.end (); ++v) { if (v->first.second != layout.cell (v->first.first).bbox () || &layout != &target_layout) { // need for a new cell - v->second = target_layout.add_cell (layout.cell_name (v->first.first)); + v->second = target_layout.add_cell (layout, v->first.first); } else { v->second = v->first.first; } diff --git a/src/db/db/dbClipboardData.cc b/src/db/db/dbClipboardData.cc index 36c515333..c8ade46af 100644 --- a/src/db/db/dbClipboardData.cc +++ b/src/db/db/dbClipboardData.cc @@ -201,7 +201,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C cell_map.insert (std::make_pair (c->cell_index (), pc->cell_index ())); } else { // fallback: create a new cell - cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ())))); + db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ()); + cell_map.insert (std::make_pair (c->cell_index (), ci)); } } else { @@ -217,7 +218,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C cell_map.insert (std::make_pair (c->cell_index (), tc)); } } else { - cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ())))); + db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ()); + cell_map.insert (std::make_pair (c->cell_index (), ci)); } } @@ -313,7 +315,7 @@ ClipboardData::cell_for_cell (const db::Layout &layout, db::cell_index_type cell return cm->second; } - db::cell_index_type target_cell_index = m_layout.add_cell (layout.cell_name (cell_index)); + db::cell_index_type target_cell_index = m_layout.add_cell (layout, cell_index); m_cell_index_map.insert (std::make_pair (cell_index, target_cell_index)); if (incomplete) { diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 30b732167..b191d4126 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -521,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; @@ -1297,7 +1303,15 @@ 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)); + add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci)); + return ci_new; +} + +cell_index_type Layout::add_cell (const char *name) { std::string b; diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 73918fb5f..cb79d2a8a 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -761,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 * @@ -1904,6 +1912,17 @@ public: */ void add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i); + /** + * @brief Adds meta information from a sequence + */ + template + void add_meta_info (const I &b, const I &e) + { + for (I i = b; i != e; ++i) { + m_meta_info.insert (b, e); + } + } + /** * @brief Removes the meta information object with the given name * The method will do nothing if no object with that name exists. @@ -1967,6 +1986,17 @@ public: */ void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i); + /** + * @brief Adds meta information from a sequence + */ + template + void add_meta_info (db::cell_index_type ci, const I &b, const I &e) + { + for (I i = b; i != e; ++i) { + m_meta_info_by_cell [ci].insert (b, e); + } + } + /** * @brief Gets a value indicating whether a meta info with the given name is present for the given cell */ diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index b22464d51..0e7faf14a 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -218,7 +218,7 @@ merge_layouts (db::Layout &target, std::map new_cell_mapping; for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { if (cell_mapping.find (*c) == cell_mapping.end ()) { - new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source.cell_name (*c)))); + new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source, *c))); } } diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index ec1e8bdc5..b678b1b62 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -800,7 +800,7 @@ MainService::cm_make_cell_variants () if (needs_variant) { // need to create a variant: create a new cell - db::cell_index_type new_cell_index = layout.add_cell (layout.cell_name (elem.inst_ptr.cell_index ())); + db::cell_index_type new_cell_index = layout.add_cell (layout, elem.inst_ptr.cell_index ()); // prepare a new variant cell db::Cell &new_cell = layout.cell (new_cell_index); From 7bdc8efc81bb3dc8c6b1fc672a066649acfb8386 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 May 2023 21:48:13 +0200 Subject: [PATCH 17/35] Displaying meta info in user properties dialog box for information Copy of meta info from cell to other layout now preserves names. In addition two bugs have been fixed: * Display of meta info for layout on statistics page did not show description * ghost cell status now is copied on cell copy --- src/db/db/dbLayout.cc | 11 +++++- src/layui/layui/UserPropertiesForm.ui | 44 +++++++++++++++++++++- src/layui/layui/layDialogs.cc | 26 +++++++++++++ src/layui/layui/layDialogs.h | 14 ++----- src/layui/layui/layLayoutPropertiesForm.cc | 2 +- src/layui/layui/layLayoutStatisticsForm.cc | 2 +- src/layui/layui/layLayoutViewFunctions.cc | 2 +- 7 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index b191d4126..0b53b70a1 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1307,7 +1307,16 @@ 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)); - add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (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; } diff --git a/src/layui/layui/UserPropertiesForm.ui b/src/layui/layui/UserPropertiesForm.ui index 76ba34ca4..8faf01105 100644 --- a/src/layui/layui/UserPropertiesForm.ui +++ b/src/layui/layui/UserPropertiesForm.ui @@ -49,7 +49,7 @@ - 1 + 0 @@ -186,6 +186,48 @@ + + + Meta Info + + + + + + Meta Info is additional system data shown here for information. Entries marked with a "*" are persisted in the layout file. + + + true + + + + + + + false + + + true + + + + Key + + + + + Description + + + + + Value + + + + + + diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index da242e201..d037ef658 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -1186,8 +1186,32 @@ 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; + + mp_ui->mode_tab->setTabVisible (2, m_begin_meta != m_end_meta); + + 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 +1233,8 @@ BEGIN_PROTECTED const db::PropertiesRepository::properties_set &props = mp_prep->properties (prop_id); set_properties (props); + set_meta_info (begin_meta, end_meta, cv->layout ()); + if (exec ()) { if (m_editable) { diff --git a/src/layui/layui/layDialogs.h b/src/layui/layui/layDialogs.h index 133e612dc..6cdd11b27 100644 --- a/src/layui/layui/layDialogs.h +++ b/src/layui/layui/layDialogs.h @@ -25,10 +25,7 @@ #ifndef HDR_layDialogs #define HDR_layDialogs -#include "dbPoint.h" -#include "dbVector.h" -#include "dbTypes.h" -#include "dbPropertiesRepository.h" +#include "dbLayout.h" #include "layuiCommon.h" #include @@ -36,12 +33,6 @@ class QTreeWidgetItem; -namespace db -{ - class Layout; - struct LayerProperties; -} - namespace lay { class GenericSyntaxHighlighterAttributes; @@ -431,6 +422,7 @@ public: virtual ~UserPropertiesForm (); bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id); + bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta); public slots: void add (); @@ -442,11 +434,13 @@ public slots: private: db::PropertiesRepository::properties_set get_properties (int tab); void set_properties (const db::PropertiesRepository::properties_set &props); + void set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout); void accept (); bool m_editable; db::PropertiesRepository *mp_prep; Ui::UserPropertiesForm *mp_ui; + db::Layout::meta_info_iterator m_begin_meta, m_end_meta; std::unique_ptr mp_hl_attributes, mp_hl_basic_attributes; }; diff --git a/src/layui/layui/layLayoutPropertiesForm.cc b/src/layui/layui/layLayoutPropertiesForm.cc index 1083d6b1a..55b5322d2 100644 --- a/src/layui/layui/layLayoutPropertiesForm.cc +++ b/src/layui/layui/layLayoutPropertiesForm.cc @@ -154,7 +154,7 @@ LayoutPropertiesForm::prop_pb_clicked () db::properties_id_type prop_id = layout.prop_id (); lay::UserPropertiesForm props_form (this); - if (props_form.show (mp_view, m_index, prop_id)) { + if (props_form.show (mp_view, m_index, prop_id, layout.begin_meta (), layout.end_meta ())) { mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Edit layout's user properties"))); layout.prop_id (prop_id); diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index 59b3200b7..782e826c2 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -703,7 +703,7 @@ StatisticsSource::get (const std::string &url) << "" << 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 ()) { + if (d.empty ()) { d = layout.meta_info_name (meta->first); } os << "" << std::endl; diff --git a/src/layui/layui/layLayoutViewFunctions.cc b/src/layui/layui/layLayoutViewFunctions.cc index b29cdc693..36bc3d6ad 100644 --- a/src/layui/layui/layLayoutViewFunctions.cc +++ b/src/layui/layui/layLayoutViewFunctions.cc @@ -440,7 +440,7 @@ LayoutViewFunctions::cm_cell_user_properties () db::properties_id_type prop_id = cell.prop_id (); lay::UserPropertiesForm props_form (parent_widget ()); - if (props_form.show (view (), cv_index, prop_id)) { + if (props_form.show (view (), cv_index, prop_id, layout.begin_meta (cell.cell_index ()), layout.end_meta (cell.cell_index ()))) { view ()->transaction (tl::to_string (tr ("Edit cell's user properties"))); cell.prop_id (prop_id); From 71487af4119dbaff08699b54870fcb06db325b38 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 3 May 2023 00:22:34 +0200 Subject: [PATCH 18/35] Some fixes for macro editor - added missing icons for back/forward - trying to improve behavior or find, search&replace - fixed tool tips of buttons - Ctrl-R, Ctrl+Shift+R for replace and "replace all" --- src/lay/lay/MacroEditorDialog.ui | 122 +++++++--------------------- src/lay/lay/layMacroEditorDialog.cc | 23 ++---- src/lay/lay/layMacroEditorDialog.h | 1 - src/lay/lay/layMacroEditorPage.cc | 94 +++++++++++++++------ src/lay/lay/layMacroEditorPage.h | 2 + 5 files changed, 111 insertions(+), 131 deletions(-) diff --git a/src/lay/lay/MacroEditorDialog.ui b/src/lay/lay/MacroEditorDialog.ui index 95b142033..80f62e53c 100644 --- a/src/lay/lay/MacroEditorDialog.ui +++ b/src/lay/lay/MacroEditorDialog.ui @@ -94,10 +94,7 @@ - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">New folder</p></body></html> + New folder ... @@ -121,10 +118,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;">New</p></body></html> + New ... @@ -141,10 +135,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;">Delete</p></body></html> + Delete ... @@ -161,10 +152,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Rename</p></body></html> + Rename Rename @@ -181,10 +169,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Import file</p></body></html> + Import file Import @@ -208,7 +193,7 @@ p, li { white-space: pre-wrap; } - <html><head/><body><p>Save all files (Ctrl+Shift+S)</p></body></html> + Save all files (Ctrl+Shift+S) ... @@ -228,7 +213,7 @@ p, li { white-space: pre-wrap; } - <html><head/><body><p>Save current file (Ctrl+S)</p></body></html> + Save current file (Ctrl+S) ... @@ -333,8 +318,8 @@ p, li { white-space: pre-wrap; } ... - - :/back_16.png:/back_16.png + + :/back_16px.png:/back_16px.png true @@ -347,8 +332,8 @@ p, li { white-space: pre-wrap; } ... - - :/forward_16.png:/forward_16.png + + :/forward_16px.png:/forward_16px.png true @@ -365,10 +350,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Close tab</p></body></html> + Close tab close @@ -432,10 +414,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Stop script</p></body></html> + Stop script ... @@ -452,10 +431,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pause script (Ctrl+F5)</p></body></html> + Pause script (Ctrl+F5) ... @@ -482,10 +458,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set breakpoint (F9)</p></body></html> + Set breakpoint (F9) ... @@ -505,10 +478,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Clear all breakpoints (Ctrl+Shift+F9)</p></body></html> + Clear all breakpoints (Ctrl+Shift+F9) ... @@ -535,10 +505,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Step into procedure (F11)</p></body></html> + Step into procedure (F11) S @@ -558,10 +525,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Step over procedure or block (F10)</p></body></html> + Step over procedure or block (F10) N @@ -588,10 +552,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Enable or disable debugging</p></body></html> + Enable or disable debugging DBG @@ -618,10 +579,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Edit properties of macro</p></body></html> + Edit properties of macro P @@ -638,10 +596,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Setup colors, formats, debugger</p></body></html> + Setup colors, formats, debugger prop @@ -674,10 +629,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Find next</p></body></html> + Find next (Ctrl+F) N @@ -687,7 +639,7 @@ p, li { white-space: pre-wrap; } :/find_16px.png:/find_16px.png - F3 + Ctrl+F true @@ -709,10 +661,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Show replace mode</p></body></html> + Show replace mode ... @@ -764,10 +713,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Replace and find next</p></body></html> + Replace and find next (Ctrl+R) RN @@ -777,7 +723,7 @@ p, li { white-space: pre-wrap; } :/replace_16px.png:/replace_16px.png - + Ctrl+R true @@ -787,10 +733,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Replace all</p></body></html> + Replace all (Ctrl+Shift+R) All @@ -799,6 +742,9 @@ p, li { white-space: pre-wrap; } :/replace_all_16px.png:/replace_all_16px.png + + Ctrl+Shift+R + true @@ -1497,14 +1443,6 @@ p, li { white-space: pre-wrap; } Case Sensitive - - - Search & Replace - - - Ctrl+F - - Save As diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 4924798da..3266b7c60 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -386,9 +386,6 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection connect (actionUseRegularExpressions, SIGNAL (triggered ()), this, SLOT (search_editing ())); connect (actionCaseSensitive, SIGNAL (triggered ()), this, SLOT (search_editing ())); - addAction (actionSearchReplace); - connect (actionSearchReplace, SIGNAL (triggered ()), this, SLOT (search_replace ())); - searchEditBox->set_clear_button_enabled (true); searchEditBox->set_options_button_enabled (true); searchEditBox->set_options_menu (m); @@ -1974,7 +1971,7 @@ MacroEditorDialog::find_next_button_clicked () apply_search (true); page->find_next (); - if (sender () != searchEditBox && sender () != replaceText) { + if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) { set_editor_focus (); } } @@ -1989,7 +1986,7 @@ MacroEditorDialog::find_prev_button_clicked () apply_search (true); page->find_prev (); - if (sender () != searchEditBox && sender () != replaceText) { + if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) { set_editor_focus (); } } @@ -2004,7 +2001,7 @@ MacroEditorDialog::replace_next_button_clicked () apply_search (true); page->replace_and_find_next (replaceText->text ()); - if (sender () != replaceText) { + if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) { set_editor_focus (); } } @@ -2034,12 +2031,6 @@ MacroEditorDialog::search_requested (const QString &s) search_editing (); } -void -MacroEditorDialog::search_replace () -{ - searchEditBox->setFocus (Qt::TabFocusReason); -} - void MacroEditorDialog::search_editing () { @@ -2050,7 +2041,9 @@ MacroEditorDialog::search_editing () apply_search (); page->find_reset (); // search from the initial position - page->find_next (); + if (! page->has_multi_block_selection ()) { + page->find_next (); + } } void @@ -2083,7 +2076,9 @@ MacroEditorDialog::do_search_edited () apply_search (); page->find_reset (); // search from the initial position - page->find_next (); + if (! page->has_multi_block_selection ()) { + page->find_next (); + } set_editor_focus (); } diff --git a/src/lay/lay/layMacroEditorDialog.h b/src/lay/lay/layMacroEditorDialog.h index 7461f2a2c..54d9428df 100644 --- a/src/lay/lay/layMacroEditorDialog.h +++ b/src/lay/lay/layMacroEditorDialog.h @@ -245,7 +245,6 @@ protected slots: void file_changed (const QString &path); void file_removed (const QString &path); void clear_log (); - void search_replace (); void apply_search () { apply_search (false); diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc index 1bb507820..93b85b986 100644 --- a/src/lay/lay/layMacroEditorPage.cc +++ b/src/lay/lay/layMacroEditorPage.cc @@ -1196,63 +1196,96 @@ 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; - } + + ps = mp_text->textCursor ().selectionStart (); + pe = mp_text->textCursor ().selectionEnd (); + + bs = mp_text->document ()->findBlock (ps); + be = mp_text->document ()->findBlock (pe); + ps -= bs.position (); + pe -= be.position (); + + } else if (first) { + + // don't replace first entry without selection + return; + } 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 (b == bs && i < ps) { + // ignore + } else if (b == be && i >= pe) { + // ignore + done = true; } else if (m_current_search.matchedLength () == 0) { break; // avoid an infinite loop + } 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 (); + + done = first; + + } else { + + o = i + m_current_search.matchedLength (); + + } + + } + + if (b == be || done) { + break; + } } @@ -1381,6 +1414,19 @@ MacroEditorPage::current_pos () const return mp_text->textCursor ().position () - mp_text->textCursor ().block ().position (); } +bool +MacroEditorPage::has_multi_block_selection () const +{ + QTextCursor c = mp_text->textCursor (); + if (c.selectionStart () != c.selectionEnd ()) { + QTextBlock s = mp_text->document ()->findBlock (c.selectionStart ()); + QTextBlock e = mp_text->document ()->findBlock (c.selectionEnd ()); + return e != s; + } else { + return false; + } +} + bool MacroEditorPage::tab_key_pressed () { diff --git a/src/lay/lay/layMacroEditorPage.h b/src/lay/lay/layMacroEditorPage.h index 960a13538..a689aefe5 100644 --- a/src/lay/lay/layMacroEditorPage.h +++ b/src/lay/lay/layMacroEditorPage.h @@ -246,6 +246,7 @@ public: int current_line () const; int current_pos () const; + bool has_multi_block_selection () const; void set_debugging_on (bool debugging_on); @@ -313,6 +314,7 @@ private: void fill_completer_list (); void complete (); QTextCursor get_completer_cursor (int &pos0, int &pos); + void replace_in_selection (const QString &replace, bool first); bool eventFilter (QObject *watched, QEvent *event); }; From 2e4153c04ab7c31ad05e8b34ad32478d4d76bcba Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 3 May 2023 21:38:10 +0200 Subject: [PATCH 19/35] Some more enhancements for search & replace in macro editor --- src/lay/lay/layMacroEditorPage.cc | 44 +++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc index 93b85b986..408949cd2 100644 --- a/src/lay/lay/layMacroEditorPage.cc +++ b/src/lay/lay/layMacroEditorPage.cc @@ -1220,15 +1220,18 @@ MacroEditorPage::replace_in_selection (const QString &replace, bool first) int pe = be.length (); QTextCursor c = mp_text->textCursor (); - if (c.hasSelection ()) { + bool has_selection = c.hasSelection (); + bool anchor_at_end = false; - ps = mp_text->textCursor ().selectionStart (); - pe = mp_text->textCursor ().selectionEnd (); + 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); - ps -= bs.position (); - pe -= be.position (); } else if (first) { @@ -1237,6 +1240,9 @@ MacroEditorPage::replace_in_selection (const QString &replace, bool first) } + ps -= bs.position (); + pe -= be.position (); + c.beginEditBlock (); bool done = false; @@ -1252,13 +1258,13 @@ MacroEditorPage::replace_in_selection (const QString &replace, bool first) int i = m_current_search.indexIn (b.text (), o); if (i < 0) { break; - } else if (b == bs && i < ps) { - // ignore - } else if (b == be && i >= pe) { - // ignore - done = true; } 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; } @@ -1273,7 +1279,16 @@ MacroEditorPage::replace_in_selection (const QString &replace, bool first) o = i + r.size (); - done = first; + 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 { @@ -1289,6 +1304,13 @@ MacroEditorPage::replace_in_selection (const QString &replace, bool first) } + 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 (); } From 4afa029297cddd813097ff47e66477f09ee2f916 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 5 May 2023 23:05:49 +0200 Subject: [PATCH 20/35] Partial edit mode fixes - do not remove last point of closed paths - fixed removal of redundant points in polygon/path edit - on-grid point insertion in paths and polygons for skew angle edges --- src/edt/edt/edtPartialService.cc | 43 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 18b5e98ad..add681c8f 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -243,9 +243,14 @@ insert_point_path (const db::Path &p, const std::set &sel, db::Po ctr.push_back (p1); if (! found && sel.find (EdgeWithIndex (db::Edge (p1, p2), n, n + 1, 0)) != sel.end ()) { // project the point onto the edge - std::pair projected = db::Edge (p1, p2).projected (ins); + db::Edge e (p1, p2); + std::pair projected = e.projected (ins); if (projected.first) { - ins = projected.second; + if (e.is_ortho ()) { + // NOTE: for skew edges we use the original point as the projected one usually + // is off-grid. + ins = projected.second; + } ctr.push_back (ins); found = true; } @@ -262,22 +267,28 @@ insert_point_path (const db::Path &p, const std::set &sel, db::Po } static void -remove_redundant_points (std::vector &ctr) +remove_redundant_points (std::vector &ctr, bool cyclic) { // compress contour (remove redundant points) // and assign to path std::vector::iterator wp = ctr.begin (); std::vector::const_iterator rp = ctr.begin (); - db::Point pm1 = *rp; - if (wp != ctr.end ()) { - ++wp; - ++rp; + db::Point pm1; + if (rp != ctr.end ()) { + if (cyclic) { + pm1 = ctr.back (); + } else { + pm1 = ctr.front (); + ++wp; + ++rp; + } while (rp != ctr.end ()) { db::Point p0 = *rp; if (p0 != pm1) { *wp++ = p0; } + pm1 = p0; ++rp; } } @@ -304,7 +315,7 @@ del_points_path (const db::Path &p, const std::set &sel) } } - remove_redundant_points (ctr); + remove_redundant_points (ctr, false); new_path.assign (ctr.begin (), ctr.end ()); return new_path; @@ -357,7 +368,7 @@ modify_path (db::Path &p, const std::map &new_points } if (compress) { - remove_redundant_points (ctr); + remove_redundant_points (ctr, false); } p.assign (ctr.begin (), ctr.end ()); @@ -384,10 +395,14 @@ insert_point_poly (const db::Polygon &p, const std::set &sel, db: ctr.push_back ((*e).p1 ()); if (! found && sel.find (EdgeWithIndex (*e, n, nn, c)) != sel.end ()) { - // project the point onto the edge + // project the point onto the edge - use the first edge the point projects to std::pair projected = (*e).projected (ins); if (projected.first) { - ins = projected.second; + if ((*e).is_ortho ()) { + // NOTE: for skew edges we use the original point as the projected one usually + // is off-grid. + ins = projected.second; + } ctr.push_back (ins); found = true; } @@ -397,7 +412,7 @@ insert_point_poly (const db::Polygon &p, const std::set &sel, db: if (found) { - remove_redundant_points (ctr); + remove_redundant_points (ctr, true); new_poly = p; if (c == 0) { @@ -433,7 +448,7 @@ del_points_poly (const db::Polygon &p, const std::set &sel) } } - remove_redundant_points (ctr); + remove_redundant_points (ctr, true); if (c == 0) { new_poly.assign_hull (ctr.begin (), ctr.end (), false /*compress*/); @@ -495,7 +510,7 @@ modify_polygon (db::Polygon &p, } if (compress) { - remove_redundant_points (ctr); + remove_redundant_points (ctr, true); } if (c == 0) { From d531418ad2300463bd975829f0a0f4eec7a86e2f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 5 May 2023 23:20:03 +0200 Subject: [PATCH 21/35] Layout statistics form - caching, refactoring, printing statistics for 'all' shapes --- src/layui/layui/layLayoutStatisticsForm.cc | 563 ++++++++++++--------- src/tl/tl/tlUri.cc | 9 +- 2 files changed, 331 insertions(+), 241 deletions(-) diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index b5d8af8cb..77c883bd2 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -28,6 +28,8 @@ #include "tlString.h" #include "tlExpression.h" #include "tlTimer.h" +#include "tlUri.h" +#include "tlFileUtils.h" #include "dbLayoutQuery.h" #include "dbCellGraphUtils.h" @@ -135,10 +137,10 @@ static std::string format_tech_name (const std::string &s) class StatisticsTemplateProcessor { public: - StatisticsTemplateProcessor (const QUrl &url, const db::Layout *layout) + StatisticsTemplateProcessor (const tl::URI &url, const db::Layout *layout) : mp_layout (layout) { - QResource res (QString::fromUtf8 (":/st/") + url.path ()); + QResource res (QString::fromUtf8 (":/st/") + QString::fromUtf8 (url.path ().c_str ())); #if QT_VERSION >= 0x60000 if (res.compressionAlgorithm () == QResource::ZlibCompression) { #else @@ -149,13 +151,9 @@ public: m_temp = QByteArray ((const char *)res.data (), (int)res.size ()); } -#if QT_VERSION >= 0x050000 - QList > queryItems = QUrlQuery (url.query ()).queryItems (); -#else - QList > queryItems = url.queryItems (); -#endif - for (QList >::const_iterator q = queryItems.begin (); q != queryItems.end (); ++q) { - m_top_eval.set_var (tl::to_string (q->first), tl::to_string (q->second)); + auto query_items = url.query (); + for (auto q = query_items.begin (); q != query_items.end (); ++q) { + m_top_eval.set_var (q->first, q->second); } } @@ -496,11 +494,27 @@ public: return count (db::Shape::Edge); } + size_t edge_pair_total () const + { + return count (db::Shape::EdgePair); + } + size_t user_total () const { return count (db::Shape::UserObject); } + size_t all_total () const + { + return box_total () + + polygon_total () + + path_total () + + text_total () + + edge_total () + + edge_pair_total () + + user_total (); + } + private: std::map m_count; }; @@ -517,278 +531,357 @@ public: // .. nothing yet .. } - std::string get (const std::string &url); + std::string get (const std::string &url) + { + auto p = m_page_cache.find (url); + if (p != m_page_cache.end ()) { + return p->second; + } else { + std::string t = get_impl (url); + m_page_cache [url] = t; + return t; + } + } + + void clear_cache () + { + m_page_cache.clear (); + } private: const lay::LayoutHandleRef m_h; + std::map m_page_cache; + + std::string index_page (const tl::URI &uri) const; + std::string per_layer_stat_page (const tl::URI &uri) const; + std::string get_impl (const std::string &url); }; +static std::string s_per_layer_stat_path_ld = "per-layer-stat-ld"; +static std::string s_per_layer_stat_path_name = "per-layer-stat-name"; + std::string -StatisticsSource::get (const std::string &url) +StatisticsSource::per_layer_stat_page (const tl::URI &uri) const { - static QString s_per_layer_stat_path_ld = QString::fromUtf8 ("per-layer-stat-ld"); - static QString s_per_layer_stat_path_name = QString::fromUtf8 ("per-layer-stat-name"); + // This is the default top level page + // TODO: handle other input as well - QUrl qurl (tl::to_qstring (url)); - QFileInfo fi (qurl.path ()); + const db::Layout &layout = m_h->layout (); - if (fi.suffix () == QString::fromUtf8 ("stxml")) { + std::ostringstream os; + os.imbue (std::locale ("C")); - StatisticsTemplateProcessor tp (qurl, &m_h->layout ()); - tp.process (); - std::string r = tp.get ().constData (); - return r; + std::vector layers; + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + layers.push_back (i); + } + } - } else if (fi.baseName () == s_per_layer_stat_path_ld || fi.baseName () == s_per_layer_stat_path_name) { + if (tl::basename (uri.path ()) == s_per_layer_stat_path_ld) { + std::sort (layers.begin (), layers.end (), CompareLDName (layout)); + } else { + std::sort (layers.begin (), layers.end (), CompareNameLD (layout)); + } - // This is the default top level page - // TODO: handle other input as well + os << "" << std::endl + << "" << std::endl + << "

" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'

" << std::endl - const db::Layout &layout = m_h->layout (); + << "

" << std::endl + << "

" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << lp.name << "" << tl::escaped_to_html (lp.name, true) << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "
" << tl::escaped_to_html (d, true) << "" << tl::escaped_to_html (meta->second.value.to_string (), true) << "
" << std::endl - std::ostringstream os; - os.imbue (std::locale ("C")); + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + ; + + db::CellCounter cc (&layout); + + tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000); + for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) { + + ShapeStatistics st_hier; + ShapeStatistics st_flat; + + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + + ShapeStatistics st; + st.compute (layout.cell (*c).shapes (*l)); + + st_hier += st; + st *= cc.weight (*c); + st_flat += st; + + ++progress; - std::vector layers; - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - layers.push_back (i); - } } - if (fi.baseName () == s_per_layer_stat_path_ld) { - std::sort (layers.begin (), layers.end (), CompareLDName (layout)); - } else { - std::sort (layers.begin (), layers.end (), CompareNameLD (layout)); - } - - os << "" << std::endl - << "" << std::endl - << "

" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'

" << std::endl - - << "

" << std::endl - << "

" << tl::to_string (QObject::tr ("Layer")) << "" << tl::to_string (QObject::tr ("All")) << "" << tl::to_string (QObject::tr ("Boxes")) << "" << tl::to_string (QObject::tr ("Polygons")) << "" << tl::to_string (QObject::tr ("Paths")) << "" << tl::to_string (QObject::tr ("Texts")) << "" << tl::to_string (QObject::tr ("Edges")) << "" << tl::to_string (QObject::tr ("Edge Pairs")) << "" << tl::to_string (QObject::tr ("User objects")) << "
" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "
" << std::endl - - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl + os << "" << std::endl + << "" << std::endl + << "" << std::endl + // Boxes (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Polygons (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Paths (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Texts (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Edges (total) + << "" << std::endl + // EdgePairs (total) + << "" << std::endl + // User objects (total) + << "" << std::endl + // ... + << "" << std::endl << "" << std::endl ; - db::CellCounter cc (&layout); + } - tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000); - for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) { + os << "
" << tl::to_string (QObject::tr ("Layer")) << "" << tl::to_string (QObject::tr ("Boxes")) << "" << tl::to_string (QObject::tr ("Polygons")) << "" << tl::to_string (QObject::tr ("Paths")) << "" << tl::to_string (QObject::tr ("Texts")) << "" << tl::to_string (QObject::tr ("Edges")) << "" << tl::to_string (QObject::tr ("User objects")) << "
" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "
" << tl::escaped_to_html (layout.get_properties (*l).to_string (), true) << "" << st_hier.all_total () << "

" << st_flat.all_total () << "
" << st_hier.box_total () << "

" << st_flat.box_total () << "
" << st_hier.box_single () << "

" << st_flat.box_single () << "
" << st_hier.box_array () << "

" << st_flat.box_array () << "
" << st_hier.polygon_total () << "

" << st_flat.polygon_total () << "
" << st_hier.polygon_single () << "

" << st_flat.polygon_single () << "
" << st_hier.polygon_array () << "

" << st_flat.polygon_array () << "
" << st_hier.path_total () << "

" << st_flat.path_total () << "
" << st_hier.path_single () << "

" << st_flat.path_single () << "
" << st_hier.path_array () << "

" << st_flat.path_array () << "
" << st_hier.text_total () << "

" << st_flat.text_total () << "
" << st_hier.text_single () << "

" << st_flat.text_single () << "
" << st_hier.text_array () << "

" << st_flat.text_array () << "
" << st_hier.edge_total () << "

" << st_flat.edge_total () << "
" << st_hier.edge_pair_total () << "

" << st_flat.edge_pair_total () << "
" << st_hier.user_total () << "

" << st_flat.user_total () << "
" << tl::to_string (QObject::tr ("(hier)")) << "

" << tl::to_string (QObject::tr ("(flat)")) << "
" << std::endl + << "

" << std::endl + << tl::to_string (QObject::tr ("

Note

" + "

" + "\"(hier)\" is the object count where each cell counts once. " + "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells." + "

" + "

" + "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. " + "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"." + "

" + )) + << "" << std::endl + << ""; + + return os.str (); +} + +std::string +StatisticsSource::index_page (const tl::URI & /*uri*/) const +{ + // maybe later ... + bool with_shape_statistics = false; + + // This is the default top level page + // TODO: handle other input as well + + const db::Layout &layout = m_h->layout (); + + std::ostringstream os; + os.imbue (std::locale ("C")); + + size_t num_cells = layout.cells (); + + size_t num_layers = 0; + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + ++num_layers; + } + } + + db::CellCounter cc (&layout); + + os << "" << std::endl + << "" << std::endl + << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << tl::escaped_to_html (m_h->name (), true) << "'

" << std::endl + << "

" << std::endl + << "" << std::endl + << "" + << "" + << "" << std::endl; + if (! m_h->save_options ().format ().empty ()) { + os << "" + << "" + << "" << std::endl; + } + os << "" + << "" + << "" << std::endl + << "" + << "" + << "" << std::endl + << "" + << "" + << "" << std::endl + << "" + << "" + << "" << 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 << "" << std::endl; + } + os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << tl::escaped_to_html (m_h->filename (), true) << "
" << tl::to_string (QObject::tr ("Format")) << ": " << tl::escaped_to_html (m_h->save_options ().format (), true) << "
" << tl::to_string (QObject::tr ("Technology")) << ": " << tl::escaped_to_html (m_h->technology ()->description (), true) << tl::escaped_to_html (format_tech_name (m_h->tech_name ()), true) << "
" << tl::to_string (QObject::tr ("Database unit")) << ": " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "
" << tl::to_string (QObject::tr ("Number of cells")) << ": " << num_cells << "
" << tl::to_string (QObject::tr ("Number of layers")) << ": " << num_layers << "
" << tl::escaped_to_html (d, true) << "" << tl::escaped_to_html (meta->second.value.to_string (), true) << "
" << std::endl + << "

" << tl::to_string (QObject::tr ("Top Cells")) << "

" << std::endl + << "" << std::endl; + for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) { + os << "" << std::endl; + } + os << "
" << tl::escaped_to_html (layout.cell_name (*tc), true) << "
" << std::endl; + os << "

" << std::endl; + + std::vector layers_with_oasis_names; + + std::vector layers_sorted_by_ld; + layers_sorted_by_ld.reserve (num_layers); + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + layers_sorted_by_ld.push_back (i); + const db::LayerProperties &lp = layout.get_properties (i); + if (! lp.name.empty ()) { + layers_with_oasis_names.push_back (i); + } + } + } + + std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout)); + std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout)); + + if (! layers_sorted_by_ld.empty ()) { + + os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl + << "

Detailed layer statistics

" << std::endl + << "

" << std::endl + << "" << std::endl + << ""; + if (! layers_with_oasis_names.empty ()) { + os << ""; + } + if (with_shape_statistics) { + os << ""; + os << ""; + } + os << "" << std::endl; + + tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers_sorted_by_ld.size () * layout.cells (), 100000); + for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) { + + if (! layout.is_valid_layer (*i)) { + continue; + } ShapeStatistics st_hier; ShapeStatistics st_flat; - for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + if (with_shape_statistics) { + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { - ShapeStatistics st; - st.compute (layout.cell (*c).shapes (*l)); + ShapeStatistics st; + st.compute (layout.cell (*c).shapes (*i)); - st_hier += st; - st *= cc.weight (*c); - st_flat += st; + st_hier += st; + st *= cc.weight (*c); + st_flat += st; - ++progress; + ++progress; - } - - os << "" << std::endl - << "" << std::endl - // Boxes (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Polygons (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Paths (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Texts (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Edges (total) - << "" << std::endl - // User objects (total) - << "" << std::endl - // ... - << "" << std::endl - << "" << std::endl - ; - - } - - os << "
" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  " << tl::to_string (QObject::tr ("Layer name")) << "" << tl::to_string (QObject::tr ("Shape count (hier)")) << "" << tl::to_string (QObject::tr ("Shape count (flat)")) << "
" << layout.get_properties (*l).to_string () << "" << st_hier.box_total () << "

" << st_flat.box_total () << "
" << st_hier.box_single () << "

" << st_flat.box_single () << "
" << st_hier.box_array () << "

" << st_flat.box_array () << "
" << st_hier.polygon_total () << "

" << st_flat.polygon_total () << "
" << st_hier.polygon_single () << "

" << st_flat.polygon_single () << "
" << st_hier.polygon_array () << "

" << st_flat.polygon_array () << "
" << st_hier.path_total () << "

" << st_flat.path_total () << "
" << st_hier.path_single () << "

" << st_flat.path_single () << "
" << st_hier.path_array () << "

" << st_flat.path_array () << "
" << st_hier.text_total () << "

" << st_flat.text_total () << "
" << st_hier.text_single () << "

" << st_flat.text_single () << "
" << st_hier.text_array () << "

" << st_flat.text_array () << "
" << st_hier.edge_total () << "

" << st_flat.edge_total () << "
" << st_hier.user_total () << "

" << st_flat.user_total () << "
" << tl::to_string (QObject::tr ("(hier)")) << "

" << tl::to_string (QObject::tr ("(flat)")) << "
" << std::endl - << "

" << std::endl - << tl::to_string (QObject::tr ("

Note

" - "

" - "\"(hier)\" is the object count where each cell counts once. " - "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells." - "

" - "

" - "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. " - "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"." - "

" - )) - << "" << std::endl - << ""; - - return os.str (); - - } else { - - // This is the default top level page - // TODO: handle other input as well - - const db::Layout &layout = m_h->layout (); - - std::ostringstream os; - os.imbue (std::locale ("C")); - - size_t num_cells = layout.cells (); - - size_t num_layers = 0; - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - ++num_layers; - } - } - - os << "" << std::endl - << "" << std::endl - << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'

" << std::endl - << "

" << std::endl - << "" << std::endl - << "" - << "" - << "" << std::endl; - if (! m_h->save_options ().format ().empty ()) { - os << "" - << "" - << "" << std::endl; - } - os << "" - << "" - << "" << std::endl - << "" - << "" - << "" << std::endl - << "" - << "" - << "" << std::endl - << "" - << "" - << "" << std::endl; - for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { - os << "" << std::endl; - } - os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << m_h->filename () << "
" << tl::to_string (QObject::tr ("Format")) << ": " << m_h->save_options ().format () << "
" << tl::to_string (QObject::tr ("Technology")) << ": " << m_h->technology ()->description () << format_tech_name (m_h->tech_name ()) << "
" << tl::to_string (QObject::tr ("Database unit")) << ": " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "
" << tl::to_string (QObject::tr ("Number of cells")) << ": " << num_cells << "
" << tl::to_string (QObject::tr ("Number of layers")) << ": " << num_layers << "
" << meta->description << "" << meta->value << "
" << std::endl - << "

" << tl::to_string (QObject::tr ("Top Cells")) << "

" << std::endl - << "" << std::endl; - for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) { - os << "" << std::endl; - } - os << "
" << layout.cell_name (*tc) << "
" << std::endl; - os << "

" << std::endl; - - std::vector layers_with_oasis_names; - - std::vector layers_sorted_by_ld; - layers_sorted_by_ld.reserve (num_layers); - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - layers_sorted_by_ld.push_back (i); - const db::LayerProperties &lp = layout.get_properties (i); - if (! lp.name.empty ()) { - layers_with_oasis_names.push_back (i); } } - } - std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout)); - std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout)); - - if (! layers_sorted_by_ld.empty ()) { - - os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl - << "

" << std::endl - << "" << std::endl - << ""; + const db::LayerProperties &lp = layout.get_properties (*i); + os << "" + << ""; if (! layers_with_oasis_names.empty ()) { - os << ""; + os << ""; + } + if (with_shape_statistics) { + os << ""; + os << ""; } os << "" << std::endl; - for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) { - if (layout.is_valid_layer (*i)) { - const db::LayerProperties &lp = layout.get_properties (*i); + } + + os << "
" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << tl::to_string (QObject::tr ("Layer name")) << "" << tl::escaped_to_html (lp.name, true) << "" << st_hier.all_total () << "" << st_flat.all_total () << "
" << std::endl; + os << "

" << std::endl; + + } + + if (! layers_with_oasis_names.empty ()) { + + os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl + << "

Detailed layer statistics

" << std::endl + << "

" << std::endl + << "" << std::endl + << "" << std::endl; + + for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) { + if (layout.is_valid_layer (*i)) { + const db::LayerProperties &lp = layout.get_properties (*i); + if (! lp.name.empty ()) { os << "" - << ""; - if (! layers_with_oasis_names.empty ()) { - os << ""; - } - os << "" << std::endl; + << "" + << "" + << "" << std::endl; } } - - os << "
" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << lp.name << "
" << tl::escaped_to_html (lp.name, true) << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "
" << std::endl; - os << "

" << std::endl; - } - if (! layers_with_oasis_names.empty ()) { + os << "" << std::endl; + os << "

" << std::endl; - os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl - << "

" << std::endl - << "" << std::endl - << "" << std::endl; - - for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) { - if (layout.is_valid_layer (*i)) { - const db::LayerProperties &lp = layout.get_properties (*i); - if (! lp.name.empty ()) { - os << "" - << "" - << "" - << "" << std::endl; - } - } - } + } - os << "
" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << lp.name << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "
" << std::endl; - os << "

" << std::endl; + os << "" << std::endl + << "" << std::endl; + ; - } + return os.str (); +} - os << "" << std::endl - << "" << std::endl; - ; +std::string +StatisticsSource::get_impl (const std::string &url) +{ + tl::URI uri (url); + std::string page = tl::basename (uri.path ()); - return os.str (); + if (tl::extension (page) == "stxml") { + + StatisticsTemplateProcessor tp (uri, &m_h->layout ()); + tp.process (); + std::string r = tp.get ().constData (); + return r; + + } else if (page == s_per_layer_stat_path_ld || page == s_per_layer_stat_path_name) { + + return per_layer_stat_page (uri); + + } else { + + return index_page (uri); } } diff --git a/src/tl/tl/tlUri.cc b/src/tl/tl/tlUri.cc index dbd3fc9e6..c416e1e8d 100644 --- a/src/tl/tl/tlUri.cc +++ b/src/tl/tl/tlUri.cc @@ -111,12 +111,9 @@ URI::URI (const std::string &uri) } m_scheme = unescape (m_scheme); - bool prefer_authority = true; - if (m_scheme == "file") { - prefer_authority = false; - // other schemes? - } else if (m_scheme.empty ()) { - prefer_authority = false; + bool prefer_authority = false; + if (m_scheme == "http" || m_scheme == "https") { + prefer_authority = true; } ex0 = ex; From 5fd54fa40ab26a56a48869824e8a1751221a7ed8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 6 Mar 2023 22:38:10 +0100 Subject: [PATCH 22/35] Netlist reader: anonymous circuits are not checked for known parameters --- src/db/db/dbNetlistSpiceReader.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 913fc43e8..7d5bf2805 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -237,11 +237,21 @@ public: typedef pin_list_type::const_iterator pin_const_iterator; SpiceCachedCircuit (const std::string &name) - : m_name (name) + : m_name (name), m_anonymous (false) { // .. nothing yet .. } + bool is_anonymous () const + { + return m_anonymous; + } + + void set_anonymous (bool f) + { + m_anonymous = f; + } + const std::string &name () const { return m_name; @@ -315,6 +325,7 @@ private: parameters_type m_parameters; pin_list_type m_pins; cards_type m_cards; + bool m_anonymous; }; static std::string @@ -1182,12 +1193,15 @@ SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &pref error (tl::sprintf (tl::to_string (tr ("Subcircuit '%s' not found in netlist")), model)); } else { db::SpiceCachedCircuit *cc_nc = mp_dict->create_cached_circuit (model); + cc_nc->set_anonymous (true); cc = cc_nc; std::vector pins; pins.resize (nn.size ()); cc_nc->set_pins (pins); } - } else { + } + + if (! cc->is_anonymous ()) { // issue warnings on unknown parameters which are skipped otherwise for (auto p = pv.begin (); p != pv.end (); ++p) { if (cc->parameters ().find (p->first) == cc->parameters ().end ()) { From 7ff9b4d8e67b83c0f779cbfde9fdd410819a22b7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Mar 2023 14:21:30 +0200 Subject: [PATCH 23/35] [consider merging] more consistent behavior of RBA/pya: enum classes are properly made available (was for example RBA::Qt::Qt_Keys instead of RBA::Qt_Keys and pya.Qt.Keys was no fully initialized type object) --- src/gsi/gsi/gsiClassBase.cc | 19 +++++++++++++++++-- src/pya/pya/pyaModule.cc | 2 +- src/rba/rba/rba.cc | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc index 600c11ab8..2d7696c28 100644 --- a/src/gsi/gsi/gsiClassBase.cc +++ b/src/gsi/gsi/gsiClassBase.cc @@ -651,11 +651,26 @@ static void collect_classes (const gsi::ClassBase *cls, std::list::const_iterator cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { collect_classes (cc.operator-> (), unsorted_classes); } } +static bool all_parts_available (const gsi::ClassBase *cls, const std::set &taken) +{ + if (cls->declaration () && cls->declaration () != cls && taken.find (cls->declaration ()) == taken.end ()) { + return false; + } + + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (! all_parts_available (cc.operator-> (), taken)) { + return false; + } + } + + return true; +} + std::list ClassBase::classes_in_definition_order (const char *mod_name) { @@ -687,7 +702,7 @@ ClassBase::classes_in_definition_order (const char *mod_name) continue; } - if ((*c)->declaration () && (*c)->declaration () != *c && taken.find ((*c)->declaration ()) == taken.end ()) { + if (! all_parts_available (*c, taken)) { // can't produce this class yet - it's a reference to another class which is not produced yet. more_classes.push_back (*c); continue; diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index e8fbc559a..7a1e83c1d 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -337,7 +337,7 @@ public: for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { if (! cc->name ().empty ()) { - PyTypeObject *child_class = make_class (cc.operator-> (), as_static); + PyTypeObject *child_class = make_class (cc->declaration (), as_static); PythonRef attr ((PyObject *) child_class, false /*borrowed*/); set_type_attr (type, cc->name ().c_str (), attr); } diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index d11eca094..b3327b218 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1654,7 +1654,7 @@ public: for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { if (! cc->name ().empty ()) { if (! is_registered (cc->declaration (), false)) { - make_class (cc->declaration (), false, klass, cc->declaration ()); + make_class (cc->declaration (), false, klass, cls); } else { VALUE child_class = ruby_cls (cc->declaration (), false); rb_define_const (klass, cc->name ().c_str (), child_class); From 7b4ff5d82328911017efd70fbb0e2134dc9ab7a6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 May 2023 21:40:36 +0200 Subject: [PATCH 24/35] Zoom in/out menu functions now use mouse position for zoom center instead of center (if mouse is inside window) --- src/laybasic/laybasic/layLayoutViewBase.cc | 17 +++++++++++++++-- src/laybasic/laybasic/layLayoutViewBase.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index b59495ef3..9d9e58598 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -3852,13 +3852,26 @@ LayoutViewBase::pan_center (const db::DPoint &p) void LayoutViewBase::zoom_in () { - shift_window (zoom_factor, 0.0, 0.0); + zoom_by (zoom_factor); } void LayoutViewBase::zoom_out () { - shift_window (1.0 / zoom_factor, 0.0, 0.0); + zoom_by (1.0 / zoom_factor); +} + +void +LayoutViewBase::zoom_by (double f) +{ + db::DBox b = mp_canvas->viewport ().box (); + + db::DPoint c = b.center (); + if (mp_canvas->mouse_in_window ()) { + c = mp_canvas->mouse_position_um (); + } + + zoom_box ((b.moved (db::DPoint () - c) * f).moved (c - db::DPoint ())); } void diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 0f8281458..d3328384b 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -2881,6 +2881,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); From e8048d66868e0eae8ef4415d9397896d9c18221e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 May 2023 19:51:15 +0200 Subject: [PATCH 25/35] WIP: preparations - introducing TextInfo --- src/db/db/dbHershey.cc | 152 +++++++++---------- src/db/db/dbHershey.h | 17 ++- src/laybasic/laybasic/layBitmap.cc | 59 ++------ src/laybasic/laybasic/layFinder.cc | 2 +- src/laybasic/laybasic/layLayoutCanvas.h | 7 +- src/laybasic/laybasic/layLayoutViewBase.h | 8 + src/laybasic/laybasic/layTextInfo.cc | 170 ++++++++++++++++++++++ src/laybasic/laybasic/layTextInfo.h | 89 +++++++++++ src/laybasic/laybasic/laybasic.pro | 2 + src/tl/tl/tlString.cc | 16 ++ src/tl/tl/tlString.h | 31 ++++ 11 files changed, 413 insertions(+), 140 deletions(-) create mode 100644 src/laybasic/laybasic/layTextInfo.cc create mode 100644 src/laybasic/laybasic/layTextInfo.h diff --git a/src/db/db/dbHershey.cc b/src/db/db/dbHershey.cc index 62b311d37..41e0726d3 100644 --- a/src/db/db/dbHershey.cc +++ b/src/db/db/dbHershey.cc @@ -101,17 +101,22 @@ hershey_count_edges (const std::string &s, unsigned int f) HersheyFont *fp = fonts [f]; size_t n = 0; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; + if (tl::skip_newline (cp)) { + + // skip new line - they don't contribute edges + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start; } - } else if (c < fp->end_char && c >= fp->start_char) { - n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start; + } } @@ -142,22 +147,27 @@ hershey_text_box (const std::string &s, unsigned int f) int w = 0; int h = fp->ymax; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { + + if (tl::skip_newline (cp)) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; - } if (w > wl) { wl = w; } + hl += line_spacing + h - fp->ymin; w = 0; - } else if (c < fp->end_char && c >= fp->start_char) { - w += fp->chars [c - fp->start_char].width; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - w += fp->chars [invalid_char - fp->start_char].width; + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + w += fp->chars [c - fp->start_char].width; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + w += fp->chars [invalid_char - fp->start_char].width; + } + } } @@ -170,7 +180,7 @@ hershey_text_box (const std::string &s, unsigned int f) return db::DBox (0, 0, wl, hl); } -void +void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts) { HersheyFont *fp = fonts [f]; @@ -179,20 +189,24 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig 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; + } + } } @@ -221,20 +235,18 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig } *l = p; } - } // ---------------------------------------------------------------------------- // basic_hershey_edge_iterator implementation basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, unsigned int f, const std::vector &line_starts) - : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_index (0), m_linestarts (line_starts) + : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_linestarts (line_starts) { m_fp = fonts [f]; - m_end = (unsigned int) m_string.size (); - m_new_char = true; + mp_cp = m_string.c_str (); - if (m_linestarts.size () == 0) { + if (m_linestarts.empty ()) { m_linestarts.push_back (db::DPoint (0.0, 0.0)); } m_pos = m_linestarts [0]; @@ -243,66 +255,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 +309,9 @@ basic_hershey_edge_iterator::get () void basic_hershey_edge_iterator::inc () { - if (m_new_char) { - get (); - } - if (! at_end ()) { ++m_edge; - if (m_edge == m_edge_end) { - ++m_index; - m_new_char = true; - } + get (); } } diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index 1e6b4990e..85387e4fc 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -51,11 +51,10 @@ public: void inc (); private: - bool m_new_char; unsigned int m_line; + const char *mp_cp; std::string m_string; unsigned int m_edge, m_edge_end; - unsigned int m_index, m_end; std::vector m_linestarts; db::DPoint m_pos; db::DVector m_delta; @@ -177,14 +176,16 @@ struct DB_PUBLIC_TEMPLATE hershey /** * @brief Obtain the size of the text * - * @return The bounding box of the text with the scaling applied + * @return The bounding box of the text with the scaling and justification applied */ - box bbox () const + db::DBox bbox () const { - db::DBox b = hershey_text_box (m_string, m_font); - db::point p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale)); - db::point p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale)); - return box (p1, p2); + db::DBox b = hershey_text_box (m_string, m_font) * (1.0 / m_scale); + if (! m_linestarts.empty ()) { + return b.moved (m_linestarts.front () - db::DPoint ()); + } else { + return b; + } } /** diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index af0a981f1..8755cab91 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -791,35 +791,6 @@ Bitmap::render_contour (std::vector &edges) } } -static unsigned char next_char_latin1_from_utf8 (const char *&cp, const char *cpf = 0) -{ - unsigned char c = *cp; - if ((c & 0xe0) == 0xc0) { - if ((cp[1] & 0xc0) == 0x80 && (! cpf || cpf > cp + 1)) { - unsigned int x = ((unsigned int) ((unsigned char) c & 0x1f) << 6) | (unsigned int) (cp[1] & 0x3f); - cp += 1; - if (x < 255) { - c = x; - } else { - c = '?'; - } - } else { - c = '?'; - } - } else if ((c & 0xf0) == 0xe0) { - if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (! cpf || cpf > cp + 2)) { - cp += 2; - } - c = '?'; - } else if ((c & 0xf8) == 0xf0) { - if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (cp[3] & 0xc0) == 0x80 && (! cpf || cpf > cp + 3)) { - cp += 3; - } - c = '?'; - } - return c; -} - void Bitmap::render_text (const lay::RenderText &text) { @@ -830,12 +801,11 @@ Bitmap::render_text (const lay::RenderText &text) // count the lines and max. characters per line unsigned int lines = 1; - for (const char *cp = text.text.c_str (); *cp; ++cp) { - if (*cp == '\012' || *cp == '\015') { - if (*cp == '\015' && cp[1] == '\012') { - ++cp; - } + for (const char *cp = text.text.c_str (); *cp; ) { + if (tl::skip_newline (cp)) { ++lines; + } else { + ++cp; } } @@ -858,10 +828,9 @@ Bitmap::render_text (const lay::RenderText &text) unsigned int length = 0; const char *cp = cp1; - while (*cp && *cp != '\012' && *cp != '\015') { - next_char_latin1_from_utf8 (cp); + while (*cp && !tl::is_newline (*cp)) { + tl::utf32_from_utf8 (cp); ++length; - ++cp; } double xx; @@ -880,11 +849,13 @@ Bitmap::render_text (const lay::RenderText &text) for ( ; cp1 != cp; ++cp1) { - unsigned char c = next_char_latin1_from_utf8 (cp1, cp); + uint32_t c = tl::utf32_from_utf8 (cp1, cp); + if (c < uint32_t (ff.first_char ()) || c >= uint32_t (ff.n_chars ()) + ff.first_char ()) { + // NOTE: '?' needs to be a valid character always + c = uint32_t ('?'); + } - size_t cc = c; // to suppress a compiler warning .. - if (c >= ff.first_char () && cc < size_t (ff.n_chars ()) + size_t (ff.first_char ()) - && xx > -100.0 && xx < double (width ())) { + if (xx > -100.0 && xx < double (width ())) { fill_pattern (int (y + 0.5), int (floor (xx)), ff.data () + (c - ff.first_char ()) * ff.height () * ff.stride (), ff.stride (), ff.height ()); } @@ -897,11 +868,7 @@ Bitmap::render_text (const lay::RenderText &text) } // next line - if (*cp1 == '\012' || *cp1 == '\015') { - if (*cp1 == '\015' && cp1[1] == '\012') { - ++cp1; - } - ++cp1; + if (tl::skip_newline (cp1)) { y -= double (ff.line_height ()); } diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 6fa5b6661..84903fad6 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -894,5 +894,5 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d } -} // namespace edt +} // namespace lay diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h index f92a48855..938219763 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.h +++ b/src/laybasic/laybasic/layLayoutCanvas.h @@ -320,6 +320,11 @@ public: return m_line_styles; } + /** + * @brief Reimplementation of ViewObjectCanvas: Resolution + */ + double resolution () const; + /** * @brief Reimplementation of ViewObjectCanvas: Background color */ @@ -444,8 +449,6 @@ private: void do_redraw_all (bool force_redraw = true); void prepare_drawing (); - virtual double resolution () const; - const std::vector &scaled_view_ops (unsigned int lw); }; diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index d3328384b..351830fc7 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -1721,6 +1721,14 @@ public: return mp_canvas; } + /** + * @brief Gets the canvas object (const version) + */ + const lay::LayoutCanvas *canvas () const + { + return mp_canvas; + } + #if defined(HAVE_QT) /** * @brief Gets the layer control panel diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc new file mode 100644 index 000000000..28c737580 --- /dev/null +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -0,0 +1,170 @@ + +/* + + 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, const db::DCplxTrans &vp_trans) + : 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 ()), + m_vp_trans (vp_trans) +{ + // .. nothing yet .. +} + +TextInfo::TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution, const db::DCplxTrans &vp_trans) + : m_default_text_size (default_text_size), + m_default_font (default_font), + m_apply_text_trans (apply_text_trans), + m_resolution (resolution), + m_vp_trans (vp_trans) +{ + // .. nothing yet .. +} + +db::DBox +TextInfo::get_bbox (const db::DText &text) const +{ + // offset in pixels (space between origin and text) + const double offset = 2.0; + + db::DFTrans fp (db::DFTrans::r0); + 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) { + fp = db::DFTrans (m_vp_trans.fp_trans () * text.trans ().fp_trans ()); + h = m_vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size); + } else { + h = m_vp_trans.ctrans (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::DVector tp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h); + db::DVector tp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h); + db::DPoint dp = db::DPoint () + text.trans ().disp (); + + db::DBox b (dp + fp (tp1), dp + fp (tp2)); + + if (font == db::DefaultFont) { + + 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; + if (valign == db::VAlignBottom || valign == db::NoVAlign) { + ytop = b.bottom (); + ytop += double (ff.line_height () * (lines - 1) + ff.height ()); + } else if (valign == db::VAlignCenter) { + ytop = b.center ().y (); + ytop += double ((ff.line_height () * (lines - 1) + ff.height ()) / 2); + } else { + ytop = b.top (); + } + + // compute the bottom position + double ybottom = ytop - ff.line_height () * (lines - 1); + + // left and right position + bool first = false; + 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 (); + } + xl -= 0.5; + + 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).transformed (m_vp_trans.inverted ()); + + } else { + + db::DHershey ht (text.string (), font); + ht.justify (b.transformed (m_vp_trans.inverted ()), halign, valign); + return ht.bbox (); + + } +} + +} diff --git a/src/laybasic/laybasic/layTextInfo.h b/src/laybasic/laybasic/layTextInfo.h new file mode 100644 index 000000000..ce700f89c --- /dev/null +++ b/src/laybasic/laybasic/layTextInfo.h @@ -0,0 +1,89 @@ + +/* + + 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 + * @param vp_trans The effective micron-to-pixel transformation + */ + TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans); + + /** + * @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, const db::DCplxTrans &vp_trans); + + /** + * @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. + */ + db::DBox operator() (const db::DText &text) const + { + return get_bbox (text); + } + +private: + double m_default_text_size; + db::Font m_default_font; + bool m_apply_text_trans; + double m_resolution; + db::DCplxTrans m_vp_trans; + + db::DBox get_bbox (const db::DText &text) const; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index f3b530330..bcd01c6d1 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -52,6 +52,7 @@ SOURCES += \ layEditable.cc \ layEditorServiceBase.cc \ layFinder.cc \ + layTextInfo.cc \ layFixedFont.cc \ layLayoutCanvas.cc \ layLineStylePalette.cc \ @@ -104,6 +105,7 @@ HEADERS += \ layEditorServiceBase.h \ layLayoutCanvas.h \ layFinder.h \ + layTextInfo.h \ layFixedFont.h \ layLayoutViewBase.h \ layLineStylePalette.h \ diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index bf3c32225..90758df1c 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -210,6 +210,22 @@ inline bool safe_isspace (char c) return c != 0 && static_cast (c) < 0x80 && isspace (c); } +// ------------------------------------------------------------------------- +// Utility: skip a newline + +bool skip_newline (const char *&cp) +{ + if (*cp == '\012' || *cp == '\015') { + if (*cp == '\015' && cp[1] == '\012') { + ++cp; + } + ++cp; + return true; + } else { + return false; + } +} + // ------------------------------------------------------------------------- // Utility: a strtod version that is independent of the locale diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index aa2f18490..33bc0413b 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -987,6 +987,37 @@ TL_PUBLIC uint32_t utf32_upcase (uint32_t c32); */ TL_PUBLIC uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0); +/** + * @brief Checks if the next characters are CR, LF or CR+LF and skips them + * + * This function returns true, if a line separated was found and skipped + */ +TL_PUBLIC bool skip_newline (const char *&cp); + +/** + * @brief checks if the given character is a CR character + */ +inline bool is_cr (char c) +{ + return c == '\015'; +} + +/** + * @brief checks if the given character is a LF character + */ +inline bool is_lf (char c) +{ + return c == '\012'; +} + +/** + * @brief checks if the given character is a CR or LF character + */ +inline bool is_newline (char c) +{ + return is_cr (c) || is_lf (c); +} + } // namespace tl #endif From 0b0f62130caad1cffbfffb66bea44c5969fb74c1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 May 2023 23:29:02 +0200 Subject: [PATCH 26/35] WIP: refactoring, debugging needed --- src/edt/edt/edtPartialService.cc | 69 ++++++++++++----- src/edt/edt/edtService.cc | 8 +- src/edt/edt/edtService.h | 1 + src/laybasic/laybasic/layFinder.cc | 109 ++++++++++++++++----------- src/laybasic/laybasic/layFinder.h | 32 ++++++-- src/laybasic/laybasic/layTextInfo.cc | 22 +++--- src/laybasic/laybasic/layTextInfo.h | 15 ++-- 7 files changed, 165 insertions(+), 91 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index add681c8f..8e69a836e 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -742,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; }; @@ -751,25 +751,25 @@ private: // PartialShapeFinder implementation PartialShapeFinder::PartialShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator::flags_type flags) - : lay::ShapeFinder (point_mode, top_level_sel, flags) + : lay::ShapeFinder (point_mode, top_level_sel, flags, 0) { set_test_count (point_sel_tests); } void -PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/) +PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int /*level*/) { if (! point_mode ()) { for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { checkpoint (); const db::Shapes &shapes = cell.shapes (*l); - db::ShapeIterator shape = shapes.begin_touching (search_box, flags (), prop_sel (), inv_prop_sel ()); + db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ()); while (! shape.at_end ()) { checkpoint (); @@ -799,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)); } } @@ -816,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)); } } @@ -840,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)); } } @@ -852,8 +852,21 @@ 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::Box tb = t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->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)); + } + } } @@ -875,14 +888,14 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (hit_box))) { checkpoint (); const db::Shapes &shapes = cell.shapes (*l); std::vector edge_sel; - db::ShapeIterator shape = shapes.begin_touching (search_box, flags (), prop_sel (), inv_prop_sel ()); + db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ()); while (! shape.at_end ()) { bool match = false; @@ -983,11 +996,27 @@ 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::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->text (), vp)); + if (tb.contains (hit_box.center ())) { + d = tp.distance (hit_box.center ()); + edge_sel.clear (); + edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + match = true; + } + + } else { + + if (hit_box.contains (tp)) { + d = tp.distance (hit_box.center ()); + edge_sel.clear (); + edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + match = true; + } + } } diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 188f115f0..b8d2ca8c4 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -472,6 +472,8 @@ Service::selection_bbox () // TODO: this is done multiple times - once for each service! TransformationVariants tv (view ()); + lay::TextInfo text_info (view ()); + db::DBox box; for (objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) { @@ -486,7 +488,11 @@ Service::selection_bbox () const std::vector *tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ()); if (tv_list != 0) { for (std::vector::const_iterator t = tv_list->begin (); t != tv_list->end (); ++t) { - box += *t * (ctx_trans * r->shape ().bbox ()); + if (r->shape ().is_text ()) { + box += *t * text_info.bbox (ctx_trans * r->shape ().text (), *t); + } else { + box += *t * (ctx_trans * r->shape ().bbox ()); + } } } diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 9108c77a5..14e16c22f 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -32,6 +32,7 @@ #include "layMarker.h" #include "laySnap.h" #include "layObjectInstPath.h" +#include "layTextInfo.h" #include "tlColor.h" #include "dbLayout.h" #include "dbShape.h" diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 84903fad6..3dc6ba216 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -23,6 +23,7 @@ #include "tlProgress.h" #include "layFinder.h" +#include "layTextInfo.h" namespace lay { @@ -86,10 +87,11 @@ Finder::closer (double d) } void -Finder::start (lay::LayoutViewBase *view, const lay::CellView &cv, unsigned int cv_index, const std::vector &trans, const db::Box ®ion, int min_level, int max_level, const std::vector &layers) +Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers) { + const lay::CellView &cv = view->cellview (cv_index); + m_layers = layers; - m_region = region; mp_layout = &cv->layout (); mp_view = view; m_cv_index = cv_index; @@ -97,17 +99,27 @@ Finder::start (lay::LayoutViewBase *view, const lay::CellView &cv, unsigned int m_max_level = std::max (m_min_level, std::min (max_level, m_top_level_sel ? ((int) cv.specific_path ().size () + 1) : max_level)); if (layers.size () == 1) { + m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]); m_cell_box_convert = db::box_convert ((unsigned int) layers [0]); + } else { + m_box_convert = db::box_convert (*mp_layout); m_cell_box_convert = db::box_convert (); + } m_path.erase (m_path.begin (), m_path.end ()); - for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) { - do_find (*cv.cell (), int (cv.specific_path ().size ()), *t * cv.context_trans ()); + for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) { + + db::VCplxTrans it = (*t * db::CplxTrans (mp_layout->dbu ())).inverted (); + m_region = it * region; + m_scan_region = it * scan_region; + + do_find (*cv.cell (), int (cv.specific_path ().size ()), *t, cv.context_trans ()); + } } @@ -155,7 +167,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,10 +176,12 @@ 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 @@ -175,22 +189,25 @@ Finder::do_find (const db::Cell &cell, int level, const db::ICplxTrans &t) && (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 +228,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 +293,9 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu) m_context_layers.clear (); m_cells_with_context.clear (); + lay::TextInfo text_info (view); + mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0; + std::vector lprops; for (lay::LayerPropertiesConstIterator lp = view->begin_layers (); ! lp.at_end (); ++lp) { if (lp->is_visual ()) { @@ -340,6 +361,9 @@ ShapeFinder::find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops m_cells_with_context.clear (); m_context_layers.clear (); + lay::TextInfo text_info (view); + mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0; + std::vector layers; layers.push_back (lprops.layer_index ()); bool result = find_internal (view, lprops.cellview_index (), &lprops.prop_sel (), lprops.inverse_prop_sel (), lprops.hier_levels (), lprops.trans (), layers, region_mu); @@ -360,12 +384,10 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co m_topcell = cv.cell_index (); - double dbu = cv->layout ().dbu (); - db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu; - std::vector trans; - trans.reserve(trans_mu.size()); - for (std::vector::const_iterator t = trans_mu.begin(); t != trans_mu.end(); ++t) { - trans.push_back(db::VCplxTrans(1.0 / dbu) * *t * db::CplxTrans(dbu)); + db::DBox scan_region_mu = region_mu; + if (mp_text_info) { + // for catching all labels we search the whole view area + scan_region_mu = view->viewport ().box (); } mp_prop_sel = prop_sel; @@ -384,7 +406,7 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co // actually find try { - start (view, cv, m_cv_index, trans, region, min_level, max_level, layers); + start (view, m_cv_index, trans_mu, region_mu, scan_region_mu, min_level, max_level, layers); } catch (StopException) { // .. } @@ -406,7 +428,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 +458,25 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { const db::Shapes &shapes = cell.shapes ((unsigned int) *l); - db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel); + db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel); while (! shape.at_end ()) { checkpoint (); + db::Box bbox; + if (text_info () && shape->is_text ()) { + db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; + bbox = t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->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 +504,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { checkpoint (); const db::Shapes &shapes = cell.shapes (*l); - db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel); + db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel); while (! shape.at_end ()) { bool match = false; @@ -488,7 +518,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 +559,13 @@ 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; + box = t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->text (), vp); + } // point-like boxes are handles which attract the finder if (box.width () == 0 && box.height () == 0) { @@ -545,21 +579,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 ())) { + 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 +705,11 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db m_topcell = cv.cell ()->cell_index (); mp_view = view; - double dbu = cv->layout ().dbu (); - db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu; - // actually find try { - std::vector tv; - tv.push_back (db::VCplxTrans (1.0 / dbu) * trans_mu * db::CplxTrans (dbu)); - start (view, cv, cv_index, tv, region, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ()); + std::vector tv; + tv.push_back (trans_mu); + start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ()); } catch (StopException) { // .. } @@ -696,7 +719,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 ()) { diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index e269fa3e0..065e69741 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -44,6 +44,8 @@ namespace tl namespace lay { +class TextInfo; + /** * @brief A generic finder class * @@ -152,8 +154,17 @@ protected: * are used). For each matching cell, the "visit_cell" method is called. A * path of instantiations up to the top cell is maintained and accessible by * the path() accessor. + * + * @param view The layout view to run the scan on + * @param cv_index The cell view to run the scan on + * @param trans A set of visual transformations applied to the display (layer properties transformations) in micron space + * @param region The hit region which the object is checked against + * @param scan_region The region where the object is looked up (can be bigger than the hit region for visual label box detection) + * @param min_level The minimum hierarchy level to check + * @param max_level The maximum hierarchy level to check + * @param layers A set of layers to check */ - void start (LayoutViewBase *view, const lay::CellView &cv, unsigned int cv_index, const std::vector &trans, const db::Box ®ion, int min_level, int max_level, const std::vector &layers = std::vector ()); + void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ()); /** * @brief Provide a basic edge test facility @@ -172,7 +183,7 @@ protected: unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match); private: - void do_find (const db::Cell &cell, int level, const db::ICplxTrans &t); + void do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t); /** * @brief Visitor sugar function @@ -181,7 +192,7 @@ private: * cell. It may use the "closer" method to determine if something is closer * to whatever. */ - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level) = 0; + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level) = 0; int m_min_level, m_max_level; std::vector m_path; @@ -189,6 +200,7 @@ private: lay::LayoutViewBase *mp_view; unsigned int m_cv_index; db::Box m_region; + db::Box m_scan_region; std::vector m_layers; double m_distance; bool m_point_mode; @@ -233,7 +245,12 @@ protected: return m_flags; } - unsigned int cv_index () const + const lay::TextInfo *text_info () const + { + return mp_text_info; + } + + unsigned int cv_index () const { return m_cv_index; } @@ -261,7 +278,8 @@ protected: void checkpoint (); private: - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/); + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level); + bool find_internal (LayoutViewBase *view, unsigned int cv_index, const std::set *prop_sel, @@ -276,6 +294,7 @@ private: db::ShapeIterator::flags_type m_flags; unsigned int m_cv_index; db::cell_index_type m_topcell; + const lay::TextInfo *mp_text_info; const std::set *mp_prop_sel; bool m_inv_prop_sel; int m_tries; @@ -314,7 +333,8 @@ public: } private: - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level); + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level); + bool find_internal (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox ®ion_mu); unsigned int m_cv_index; diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc index 28c737580..1a3b4af3b 100644 --- a/src/laybasic/laybasic/layTextInfo.cc +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -29,28 +29,26 @@ namespace lay { -TextInfo::TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans) +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 ()), - m_vp_trans (vp_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, const db::DCplxTrans &vp_trans) +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), - m_vp_trans (vp_trans) + m_resolution (resolution) { // .. nothing yet .. } db::DBox -TextInfo::get_bbox (const db::DText &text) const +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; @@ -60,10 +58,10 @@ TextInfo::get_bbox (const db::DText &text) const db::Font font = text.font () == db::NoFont ? m_default_font : text.font (); if (m_apply_text_trans && font != db::NoFont && font != db::DefaultFont) { - fp = db::DFTrans (m_vp_trans.fp_trans () * text.trans ().fp_trans ()); - h = m_vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size); + fp = db::DFTrans (vp_trans.fp_trans () * text.trans ().fp_trans ()); + h = vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size); } else { - h = m_vp_trans.ctrans (m_default_text_size); + h = vp_trans.ctrans (m_default_text_size); } db::HAlign halign = text.halign (); @@ -156,12 +154,12 @@ TextInfo::get_bbox (const db::DText &text) const } - return db::DBox (xleft, ybottom, xright, ytop).transformed (m_vp_trans.inverted ()); + return db::DBox (xleft, ybottom, xright, ytop).transformed (vp_trans.inverted ()); } else { db::DHershey ht (text.string (), font); - ht.justify (b.transformed (m_vp_trans.inverted ()), halign, valign); + ht.justify (b.transformed (vp_trans.inverted ()), halign, valign); return ht.bbox (); } diff --git a/src/laybasic/laybasic/layTextInfo.h b/src/laybasic/laybasic/layTextInfo.h index ce700f89c..c48a1ab52 100644 --- a/src/laybasic/laybasic/layTextInfo.h +++ b/src/laybasic/laybasic/layTextInfo.h @@ -46,9 +46,8 @@ public: * @brief Constructor * * @param view The LayoutView from which to take the text display parameters - * @param vp_trans The effective micron-to-pixel transformation */ - TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans); + TextInfo (const LayoutViewBase *view); /** * @brief Constructor @@ -59,7 +58,7 @@ public: * @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, const db::DCplxTrans &vp_trans); + 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 @@ -67,11 +66,11 @@ public: * 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 operator() (const db::DText &text) const - { - return get_bbox (text); - } + db::DBox bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const; private: double m_default_text_size; @@ -79,8 +78,6 @@ private: bool m_apply_text_trans; double m_resolution; db::DCplxTrans m_vp_trans; - - db::DBox get_bbox (const db::DText &text) const; }; } From 89177b5ffd7fe2acf975db001391236364aedbee Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 May 2023 19:01:16 +0200 Subject: [PATCH 27/35] Fixed a build issue on Qt <5.15 --- src/layui/layui/layDialogs.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index d037ef658..dae96cc00 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -1192,7 +1192,9 @@ UserPropertiesForm::set_meta_info (db::Layout::meta_info_iterator begin_meta, db 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 (); From 45394b801a7258e475a96d14151a3e0e5b2ae0de Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 May 2023 21:37:15 +0200 Subject: [PATCH 28/35] Debugging TextInfo, added tests --- src/db/db/dbHershey.h | 7 +- src/laybasic/laybasic/layTextInfo.cc | 10 +-- src/laybasic/unit_tests/layTextInfoTests.cc | 89 +++++++++++++++++++++ src/laybasic/unit_tests/unit_tests.pro | 1 + 4 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 src/laybasic/unit_tests/layTextInfoTests.cc diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index 85387e4fc..b5b60d0f3 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -180,12 +180,11 @@ struct DB_PUBLIC_TEMPLATE hershey */ db::DBox bbox () const { - db::DBox b = hershey_text_box (m_string, m_font) * (1.0 / m_scale); + db::DBox b = hershey_text_box (m_string, m_font); if (! m_linestarts.empty ()) { - return b.moved (m_linestarts.front () - db::DPoint ()); - } else { - return b; + b.move (m_linestarts.back () - db::DPoint ()); } + return b * m_scale; } /** diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc index 1a3b4af3b..d72f39510 100644 --- a/src/laybasic/laybasic/layTextInfo.cc +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -104,21 +104,22 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const // 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 += double (ff.line_height () * (lines - 1) + ff.height ()); + ytop += htot; } else if (valign == db::VAlignCenter) { ytop = b.center ().y (); - ytop += double ((ff.line_height () * (lines - 1) + ff.height ()) / 2); + ytop += htot * 0.5; } else { ytop = b.top (); } // compute the bottom position - double ybottom = ytop - ff.line_height () * (lines - 1); + double ybottom = ytop - htot; // left and right position - bool first = false; + bool first = true; double xleft = 0.0, xright = 0.0; const char *cp = text.string (); @@ -140,7 +141,6 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const } else { xl = b.left (); } - xl -= 0.5; double xr = xl + double (ff.width () * length); diff --git a/src/laybasic/unit_tests/layTextInfoTests.cc b/src/laybasic/unit_tests/layTextInfoTests.cc new file mode 100644 index 000000000..2d872feb8 --- /dev/null +++ b/src/laybasic/unit_tests/layTextInfoTests.cc @@ -0,0 +1,89 @@ + +/* + + 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; + + lay::TextInfo ti (&lv); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;36,37)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(6,11;18,18.5)"); + EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;60,52)"); + + 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)"); + + 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)"); + + lv.text_font (db::Font::StickFont); + ti = lay::TextInfo (&lv); + + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;72,47)"); + EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;134,83)"); + + text3 = text2; + text3.valign (db::VAlignCenter); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-10.5;134,50.5)"); + text3.valign (db::VAlignTop); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-43;134,18)"); + + text3 = text2; + text3.halign (db::HAlignCenter); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-51,22;71,83)"); + text3.halign (db::HAlignRight); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-114,22;8,83)"); + + lv.default_text_size (4.2); + ti = lay::TextInfo (&lv); + + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;24,27)"); + EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;36.4,34.2)"); + + +} diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro index 3e585c8ca..face73fbb 100644 --- a/src/laybasic/unit_tests/unit_tests.pro +++ b/src/laybasic/unit_tests/unit_tests.pro @@ -14,6 +14,7 @@ SOURCES = \ layParsedLayerSource.cc \ layRenderer.cc \ layAbstractMenuTests.cc \ + layTextInfoTests.cc \ laySnapTests.cc INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic From c6f159d720ded542be2b6857bd8a14530bfff274 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 May 2023 22:36:56 +0200 Subject: [PATCH 29/35] Adding frame to text display, debugging --- src/laybasic/laybasic/layBitmap.cc | 2 +- src/laybasic/laybasic/layMarker.cc | 27 +++++++++++++++++++++ src/laybasic/laybasic/layMarker.h | 6 +++++ src/laybasic/laybasic/layTextInfo.cc | 2 +- src/laybasic/unit_tests/layTextInfoTests.cc | 2 +- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index 8755cab91..73725629b 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -847,7 +847,7 @@ 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) { 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 ()) { diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc index b540e2ad2..a7a8bb1c0 100644 --- a/src/laybasic/laybasic/layMarker.cc +++ b/src/laybasic/laybasic/layMarker.cc @@ -30,6 +30,7 @@ #include "layViewOp.h" #include "layRenderer.h" #include "layLayoutViewBase.h" +#include "layTextInfo.h" #include "tlAssert.h" namespace lay @@ -625,11 +626,24 @@ ShapeMarker::render (const Viewport &vp, ViewObjectCanvas &canvas) if (trans_vector ()) { for (std::vector::const_iterator tr = trans_vector ()->begin (); tr != trans_vector ()->end (); ++tr) { db::CplxTrans t = vp.trans () * *tr * trans (); + if (m_shape.is_text () && text) { + // draw a frame around the text + lay::TextInfo ti (view ()); + db::DCplxTrans vp_trans = vp.trans () * *tr; + db::DBox box = ti.bbox (trans () * m_shape.text (), vp_trans).enlarged (db::DVector (2.0 / vp_trans.mag (), 2.0 / vp_trans.mag ())); + 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::DBox box = ti.bbox (trans () * m_shape.text (), vp.trans ()).enlarged (db::DVector (2.0 / vp.trans ().mag (), 2.0 / vp.trans ().mag ())); + 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 +1095,15 @@ 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::DBox box = ti.bbox (*m_object.dtext, db::DCplxTrans (t)).enlarged (db::DVector (2.0 / t.mag (), 2.0 / t.mag ())); + r.draw (box, db::DCplxTrans (t), 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 +1300,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 (db::DVector (2.0 / t.mag (), 2.0 / t.mag ())); + r.draw (box, t, 0, text, 0, 0); + } r.draw (*m_object.text, t, fill, contour, vertex, text); } else if (m_type == Edge) { r.draw (*m_object.edge, t, fill, contour, vertex, text); diff --git a/src/laybasic/laybasic/layMarker.h b/src/laybasic/laybasic/layMarker.h index c687e4eff..d807dbf24 100644 --- a/src/laybasic/laybasic/layMarker.h +++ b/src/laybasic/laybasic/layMarker.h @@ -226,6 +226,12 @@ public: protected: void get_bitmaps (const Viewport &vp, ViewObjectCanvas &canvas, lay::CanvasPlane *&fill, lay::CanvasPlane *&frame, lay::CanvasPlane *&vertex, lay::CanvasPlane *&text); + lay::LayoutViewBase *view () + { + return mp_view; + } + +private: tl::Color m_color; tl::Color m_frame_color; char m_line_width, m_vertex_size, m_halo; diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc index d72f39510..f123d5fd1 100644 --- a/src/laybasic/laybasic/layTextInfo.cc +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -83,7 +83,7 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const db::DVector tp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h); db::DVector tp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h); - db::DPoint dp = db::DPoint () + text.trans ().disp (); + db::DPoint dp = vp_trans * (db::DPoint () + text.trans ().disp ()); db::DBox b (dp + fp (tp1), dp + fp (tp2)); diff --git a/src/laybasic/unit_tests/layTextInfoTests.cc b/src/laybasic/unit_tests/layTextInfoTests.cc index 2d872feb8..3b3259360 100644 --- a/src/laybasic/unit_tests/layTextInfoTests.cc +++ b/src/laybasic/unit_tests/layTextInfoTests.cc @@ -46,7 +46,7 @@ TEST(1) lay::TextInfo ti (&lv); EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;36,37)"); - EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(6,11;18,18.5)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,21;23,28.5)"); EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;60,52)"); text3 = text2; From c45d1d70d4808bed614556ba426f3658c4dc8f3e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 May 2023 23:16:03 +0200 Subject: [PATCH 30/35] Debugging --- src/laybasic/laybasic/layFinder.cc | 7 ++++--- src/laybasic/laybasic/layTextInfo.h | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 3dc6ba216..ba12f5c72 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -118,7 +118,7 @@ Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vect m_region = it * region; m_scan_region = it * scan_region; - do_find (*cv.cell (), int (cv.specific_path ().size ()), *t, cv.context_trans ()); + do_find (*cv.cell (), int (cv.specific_path ().size ()), view->viewport ().trans () * *t, cv.context_trans ()); } } @@ -185,7 +185,7 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons } } 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)) { @@ -387,6 +387,7 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co db::DBox scan_region_mu = region_mu; if (mp_text_info) { // for catching all labels we search the whole view area + // @@@ multiple passes - with and without texts (with texts with big area) scan_region_mu = view->viewport ().box (); } @@ -580,7 +581,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: } if (! match && box.contains (hit_box.center ())) { - d = t.ctrans (poly_dist (poly.begin_edge (), point)); + d = t.ctrans (poly_dist (poly.begin_edge (), point)); match = true; } diff --git a/src/laybasic/laybasic/layTextInfo.h b/src/laybasic/laybasic/layTextInfo.h index c48a1ab52..c99a47563 100644 --- a/src/laybasic/laybasic/layTextInfo.h +++ b/src/laybasic/laybasic/layTextInfo.h @@ -77,7 +77,6 @@ private: db::Font m_default_font; bool m_apply_text_trans; double m_resolution; - db::DCplxTrans m_vp_trans; }; } From 1422dfb93da6a37ac4d65710a1bc5748290d5cf0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 May 2023 18:57:24 +0200 Subject: [PATCH 31/35] Bugfix, text reference made the program assert --- src/db/db/dbHershey.cc | 14 ++++++- src/db/db/dbHershey.h | 17 +++++---- src/edt/edt/edtPartialService.cc | 8 +++- src/laybasic/laybasic/layFinder.cc | 42 +++++++++++++++------ src/laybasic/laybasic/layMarker.cc | 22 ++++++++--- src/laybasic/laybasic/layTextInfo.cc | 12 +++--- src/laybasic/unit_tests/layTextInfoTests.cc | 40 ++++++++++++++++---- 7 files changed, 112 insertions(+), 43 deletions(-) diff --git a/src/db/db/dbHershey.cc b/src/db/db/dbHershey.cc index 41e0726d3..7e862d4a2 100644 --- a/src/db/db/dbHershey.cc +++ b/src/db/db/dbHershey.cc @@ -177,12 +177,15 @@ hershey_text_box (const std::string &s, unsigned int f) } hl += h; - return db::DBox (0, 0, wl, hl); + return db::DBox (0, fp->ymin, wl, hl); } void -hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts) +hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts, double &left, double &bottom) { + left = 0.0; + bottom = 0.0; + HersheyFont *fp = fonts [f]; int hl = 0; @@ -234,6 +237,13 @@ 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 ()); + } } } diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index b5b60d0f3..2c446a7cd 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -38,7 +38,7 @@ struct HersheyFont; DB_PUBLIC int hershey_font_width (unsigned int f); DB_PUBLIC int hershey_font_height (unsigned int f); DB_PUBLIC db::DBox hershey_text_box (const std::string &s, unsigned int f); -DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts); +DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts, double &left, double &bottom); DB_PUBLIC std::vector hershey_font_names (); DB_PUBLIC size_t hershey_count_edges (const std::string &s, unsigned int f); @@ -137,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 .. } @@ -150,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 .. } @@ -181,9 +183,7 @@ struct DB_PUBLIC_TEMPLATE hershey db::DBox bbox () const { db::DBox b = hershey_text_box (m_string, m_font); - if (! m_linestarts.empty ()) { - b.move (m_linestarts.back () - db::DPoint ()); - } + b.move (db::DVector (m_left, m_bottom)); return b * m_scale; } @@ -220,7 +220,7 @@ 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 { @@ -245,7 +245,7 @@ struct DB_PUBLIC_TEMPLATE hershey if (m_scale > 1e-6) { db::DPoint p1 (b.p1 ().x () / m_scale, b.p1 ().y () / m_scale); db::DPoint p2 (b.p2 ().x () / m_scale, b.p2 ().y () / m_scale); - hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts); + hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts, m_left, m_bottom); } } @@ -294,6 +294,7 @@ private: Font m_font; double m_scale; std::vector m_linestarts; + double m_left, m_bottom; }; /** diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 8e69a836e..8950458f0 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -856,7 +856,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co if (text_info ()) { db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; - db::Box tb = t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->text (), vp); + 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)); } @@ -1000,7 +1002,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co if (text_info ()) { db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; - db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->text (), vp)); + 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 (); diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index ba12f5c72..acb79f9f5 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -384,13 +384,6 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co m_topcell = cv.cell_index (); - db::DBox scan_region_mu = region_mu; - if (mp_text_info) { - // for catching all labels we search the whole view area - // @@@ multiple passes - with and without texts (with texts with big area) - scan_region_mu = view->viewport ().box (); - } - mp_prop_sel = prop_sel; m_inv_prop_sel = inv_prop_sel; @@ -405,13 +398,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, m_cv_index, trans_mu, region_mu, scan_region_mu, 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 (); } @@ -471,7 +485,9 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: db::Box bbox; if (text_info () && shape->is_text ()) { db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; - bbox = t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->text (), vp); + db::Text text; + shape->text (text); + bbox = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp); } else { bbox = shape->bbox (); } @@ -565,7 +581,9 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: db::Box box = shape->bbox (); if (text_info () && shape->is_text ()) { db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; - box = t_dbu.inverted () * text_info ()->bbox (t_dbu * shape->text (), vp); + 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 diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc index a7a8bb1c0..16d0ca19b 100644 --- a/src/laybasic/laybasic/layMarker.cc +++ b/src/laybasic/laybasic/layMarker.cc @@ -36,6 +36,13 @@ 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, @@ -630,7 +637,9 @@ ShapeMarker::render (const Viewport &vp, ViewObjectCanvas &canvas) // draw a frame around the text lay::TextInfo ti (view ()); db::DCplxTrans vp_trans = vp.trans () * *tr; - db::DBox box = ti.bbox (trans () * m_shape.text (), vp_trans).enlarged (db::DVector (2.0 / vp_trans.mag (), 2.0 / vp_trans.mag ())); + 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); @@ -641,7 +650,9 @@ ShapeMarker::render (const Viewport &vp, ViewObjectCanvas &canvas) if (m_shape.is_text () && text) { // draw a frame around the text lay::TextInfo ti (view ()); - db::DBox box = ti.bbox (trans () * m_shape.text (), vp.trans ()).enlarged (db::DVector (2.0 / vp.trans ().mag (), 2.0 / vp.trans ().mag ())); + 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); @@ -1101,8 +1112,9 @@ Marker::draw (lay::Renderer &r, const db::CplxTrans &t, lay::CanvasPlane *fill, if (view () && text) { // draw a frame around the text lay::TextInfo ti (view ()); - db::DBox box = ti.bbox (*m_object.dtext, db::DCplxTrans (t)).enlarged (db::DVector (2.0 / t.mag (), 2.0 / t.mag ())); - r.draw (box, db::DCplxTrans (t), 0, text, 0, 0); + 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) { @@ -1303,7 +1315,7 @@ DMarker::render (const Viewport &vp, ViewObjectCanvas &canvas) if (view () && text) { // draw a frame around the text lay::TextInfo ti (view ()); - db::DBox box = ti.bbox (*m_object.text, t).enlarged (db::DVector (2.0 / t.mag (), 2.0 / t.mag ())); + 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); diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc index f123d5fd1..4f7402a73 100644 --- a/src/laybasic/laybasic/layTextInfo.cc +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -53,14 +53,14 @@ 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; - db::DFTrans fp (db::DFTrans::r0); + 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) { - fp = db::DFTrans (vp_trans.fp_trans () * text.trans ().fp_trans ()); h = vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size); } else { + tt = db::DTrans (tt.disp ()); h = vp_trans.ctrans (m_default_text_size); } @@ -83,9 +83,9 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const db::DVector tp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h); db::DVector tp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h); - db::DPoint dp = vp_trans * (db::DPoint () + text.trans ().disp ()); + db::DPoint dp = vp_trans * db::DPoint (); - db::DBox b (dp + fp (tp1), dp + fp (tp2)); + db::DBox b (dp + tp1, dp + tp2); if (font == db::DefaultFont) { @@ -154,13 +154,13 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const } - return db::DBox (xleft, ybottom, xright, ytop).transformed (vp_trans.inverted ()); + return db::DBox (xleft, ybottom, xright, ytop).transformed (vp_trans.inverted ()).transformed (tt); } else { db::DHershey ht (text.string (), font); ht.justify (b.transformed (vp_trans.inverted ()), halign, valign); - return ht.bbox (); + return ht.bbox ().transformed (tt); } } diff --git a/src/laybasic/unit_tests/layTextInfoTests.cc b/src/laybasic/unit_tests/layTextInfoTests.cc index 3b3259360..0888f9203 100644 --- a/src/laybasic/unit_tests/layTextInfoTests.cc +++ b/src/laybasic/unit_tests/layTextInfoTests.cc @@ -44,46 +44,70 @@ TEST(1) db::DText text3; + // Default font lay::TextInfo ti (&lv); EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;36,37)"); EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,21;23,28.5)"); 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,22;72,47)"); - EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;134,83)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,15;72,47)"); + 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,-10.5;134,50.5)"); + 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,-43;134,18)"); + 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,22;71,83)"); + 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,22;8,83)"); + 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,22;24,27)"); - EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;36.4,34.2)"); + 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)"); } From 424c039b7eb5ff40776c86189a2c4aa26b48de84 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 May 2023 19:03:41 +0200 Subject: [PATCH 32/35] More precise testing of edges on mouse hit - without this patch, the selected objects in dense regions are not at the mouse pointer's tip, but somewhat away --- src/laybasic/laybasic/layFinder.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index acb79f9f5..168efce12 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -137,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 { From da52eeb7c3d45a3766827d7a5af469b71cda33a0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 May 2023 20:05:28 +0200 Subject: [PATCH 33/35] Fixed text bbox computation under the presence of global view transformations and 'apply text transformation' false --- src/db/db/dbHershey.h | 6 +++--- src/laybasic/laybasic/layTextInfo.cc | 21 ++++++++++----------- src/laybasic/unit_tests/layTextInfoTests.cc | 13 +++++++++++++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index 2c446a7cd..f747bb186 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -224,7 +224,7 @@ struct DB_PUBLIC_TEMPLATE hershey } 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)); diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc index 4f7402a73..8b700c127 100644 --- a/src/laybasic/laybasic/layTextInfo.cc +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -51,17 +51,17 @@ 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; + 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 = vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size); + h = text.size () > 0 ? text.size () : m_default_text_size; } else { - tt = db::DTrans (tt.disp ()); - h = vp_trans.ctrans (m_default_text_size); + tt = db::DTrans (vp_trans.fp_trans ().inverted ().angle (), tt.disp ()); + h = m_default_text_size; } db::HAlign halign = text.halign (); @@ -81,14 +81,13 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const fx = -1.0; } - db::DVector tp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h); - db::DVector tp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h); - db::DPoint dp = vp_trans * db::DPoint (); - - db::DBox b (dp + tp1, dp + tp2); + 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 @@ -154,12 +153,12 @@ TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const } - return db::DBox (xleft, ybottom, xright, ytop).transformed (vp_trans.inverted ()).transformed (tt); + return (db::DBox (xleft, ybottom, xright, ytop) * (1.0 / vp_trans.mag ())).transformed (tt); } else { db::DHershey ht (text.string (), font); - ht.justify (b.transformed (vp_trans.inverted ()), halign, valign); + ht.justify (db::DBox (dp1, dp2), halign, valign); return ht.bbox ().transformed (tt); } diff --git a/src/laybasic/unit_tests/layTextInfoTests.cc b/src/laybasic/unit_tests/layTextInfoTests.cc index 0888f9203..ef30de467 100644 --- a/src/laybasic/unit_tests/layTextInfoTests.cc +++ b/src/laybasic/unit_tests/layTextInfoTests.cc @@ -47,7 +47,10 @@ TEST(1) // 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 @@ -69,6 +72,11 @@ TEST(1) 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 @@ -110,4 +118,9 @@ TEST(1) 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)"); } From f25d20be209cc0f79271813dfb7cc626ad85b291 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 May 2023 22:38:30 +0200 Subject: [PATCH 34/35] [bugfix] making 'R', 'L' and 'C' parameters too for the respective Spice elements (beside 'value') --- src/db/db/dbNetlistSpiceReaderDelegate.cc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.cc b/src/db/db/dbNetlistSpiceReaderDelegate.cc index 905360073..88d559050 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.cc +++ b/src/db/db/dbNetlistSpiceReaderDelegate.cc @@ -381,6 +381,9 @@ void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std: error (tl::to_string (tr ("Can't find a value for a R, C or L device"))); } + // store the value under the element name always + pv[element] = tl::Variant (value); + } else { // others: n-terminal devices with a model (last node) @@ -416,8 +419,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin std::map params = pv; std::vector terminal_order; - size_t defp = std::numeric_limits::max (); - double mult = 1.0; auto mp = params.find ("M"); if (mp != params.end ()) { @@ -461,8 +462,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // Apply multiplier (divider, according to ngspice manual) value /= mult; - - defp = db::DeviceClassResistor::param_id_R; + params["R"] = tl::Variant (value); // Apply multiplier to other parameters static const char *scale_params[] = { "A", "P", "W" }; @@ -492,8 +492,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // Apply multiplier (divider, according to ngspice manual) value /= mult; - - defp = db::DeviceClassInductor::param_id_L; + params["L"] = tl::Variant (value); } else if (element == "C") { @@ -525,8 +524,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // Apply multiplier value *= mult; - - defp = db::DeviceClassCapacitor::param_id_C; + params["C"] = tl::Variant (value); // Apply multiplier to other parameters static const char *scale_params[] = { "A", "P" }; @@ -657,8 +655,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin double pv = 0.0; if (v != params.end ()) { pv = v->second.to_double (); - } else if (i->id () == defp) { - pv = value; } else { continue; } From 0027bba8c6050b096a19153aa5e0fcb9b7f33531 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 May 2023 23:41:21 +0200 Subject: [PATCH 35/35] Fixed LayoutView test after text selection is no longer confined to text origin points --- testdata/ruby/layLayoutView.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/ruby/layLayoutView.rb b/testdata/ruby/layLayoutView.rb index 9ec0bd57c..fdd384cc6 100644 --- a/testdata/ruby/layLayoutView.rb +++ b/testdata/ruby/layLayoutView.rb @@ -186,7 +186,7 @@ class LAYLayoutView_TestClass < TestBase view.set_config("search-range-box", "5") view.select_from(RBA::DBox::new(-1.0, -1.0, 1.0, 1.0)) assert_equal(selection_changed, 1) - assert_equal(view.selection_size, 4) + assert_equal(view.selection_size, 2) assert_equal(view.has_selection?, true) view.select_from(RBA::DPoint::new(0, 0), RBA::LayoutView::Invert)