diff --git a/Changelog b/Changelog index 959aa18685..9a07c1d8bc 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,69 @@ +0.30.9 (2026-05-25): +* Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true) +* Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed +* Enhancement: %GITHUB%/issues/2337 [Feature Request] Add to_bytes and from_bytes to shape classes +* Bugfix: %GITHUB%/issues/2339 Ruby ReportDatabase merging can lead to internal error +* Bugfix: %GITHUB%/issues/2343 copy_tree implicitly drops collinear points +* Bugfix: %GITHUB%/issues/2344 Instance properties inside a PCell are not preserved in KLayout +* Bugfix: %GITHUB%/issues/2345 Substrate sequestration + Although this ticket was more a discussion, this enhancement fixes the behavior of + empty layers in deep mode, which allows placing device terminals on them. + This eliminates the need for the workaround mentioned in the ticket. +* Bugfix: %GITHUB%/issues/2350 strm2oas klayout/testdata/gds/issue_1835.gds a.oas, a.oas is empty + With this fix, it is possible to save and load layouts that use a single library references + or PCell variants as top cell. This is not strictly a supported use model, but may be + useful in some cases. The "strm2xxx" tools now allow translating such files from OASIS + to GDS and back. +* Bugfix: %GITHUB%/issues/2356 Edit options always shown, even for klayout -ne +* Enhancement: because of issue #2356 and #2335, the "Editor Options" dock was overhauled + * It is called "Tool Options" now as it hosts not just shape editor options + * The Ruler tool got its own page as the original page was not functional and + rulers have other options. This replicates some settings from the "Ruler + and annotations setup" page. + * The dock widget's visibility can be controlled in the same way than the other + docks. The "Edit/Editor Options" entry moved to "View/Tool Options". It turns + on or off the tool options dock, which is populated with pages depending on the tool. + The default key binding (F3) will now show or hide this dock. +* Enhancement: A feature to compute a density map. It is found inside Tools/Density Map. + The density map can be computed from one or multiple layers, the region can be + specified in terms of visible region, rulers or bounding boxes of certain layers + or the whole cell. The tool will generate an image overlay over the layout + holding the density data. +* Bugfix: using "xkill" on a KLayout instance ended in a crash +* Enhancement: settings of search features are persisted (see discussion 2868) +* Enhancement: when moving rulers with "snap to objects", all points of the + rulers snap to edges or vertexes now. +* Bugfix: false color maps in images where incorrectly interpolated in some + cases. +* Bugfix: small bugfix in L2N/LVSDB reader - default values of parameters were + treated as int. +* Bugfix: properties on objects with properties (such as BoxWithProperties) where + not properly serialized. Specifically their representation did not include the + type. Hence it was not possible to retrieve properties on deserialization with + "from_s". The fix is to annotate the property values with type information. + NOTE: this is a change that is not backward-compatible. +* Bugfix: snapping rulers to edges parallel to the allowed ruler direction resulted + in skew rulers. +* Enhancement: renamed ruler option for more clarity - changed + 'snap to objects (unless disabled in template)' to 'never snap to object' (inverted) +* Enhancement: enhancements in the script API + * Cell#is_cold_proxy?: indicates if a cell is a "defunc" library reference + * Cell#library_cell_name: gets the library cell name for a "defunc" reference + * Cell#library_name: gets the library name for a "defunc" reference + * Cell#pcell_name: gets the pcell name for a "defunct" reference + * Layout#delete_cells: now available with a list of cell object + * Layout#delete_cell_rec: now available with a cell object + * Layout#prune_cell: now available with a cell object + * Layout#prune_cells: for pruning multiple cells in one call + * Layout#prune_subcells: now with a cell object + * Layout#prune_subcells: now with multiple root cells + * Layout#flatten: now with a cell object + * Layout#flatten_into: now with cell objects + * Layout#delete_cell: now with a cell object + * Default value "all" for "levels" argument in "prune_subcells" and "prune_cell" + * Default value "all" for "levels" and "true" for "prune" argument in "flatten" + * Default value "all" for "levels" and "unity" for "trans" argument in "flatten_into" + 0.30.8 (2026-04-14): * Enhancement: %GITHUB%/issues/2248 Switch layouts but without losing the handle * Bugfix: %GITHUB%/issues/2299 gf180mcu DRC performance regression. diff --git a/Changelog.Debian b/Changelog.Debian index 3b572f5f49..44cba48fc2 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.9-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Mon, 25 May 2026 19:46:05 +0200 + klayout (0.30.8-1) unstable; urgency=low * New features and bugfixes diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui index 1f5ec4a293..c732cebded 100644 --- a/src/ant/ant/RulerConfigPage4.ui +++ b/src/ant/ant/RulerConfigPage4.ui @@ -594,12 +594,12 @@ - + If checked, snap to edges or vertices of objects unless disabled above - Snap to objects + Never snap to objects @@ -893,7 +893,7 @@ style_cb outline_cb t_angle_cb - t_snap_cbx + t_never_snap_cbx diff --git a/src/ant/ant/RulerOptions.ui b/src/ant/ant/RulerOptions.ui new file mode 100644 index 0000000000..151f8f6581 --- /dev/null +++ b/src/ant/ant/RulerOptions.ui @@ -0,0 +1,242 @@ + + + RulerOptions + + + + 0 + 0 + 400 + 446 + + + + + 0 + 0 + + + + Form + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + 400 + 446 + + + + + 2 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Snapping + + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Snap to grid + + + + + + + Snap to edge/vertex + + + + + + + + + + Angle Constraints + + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Angle + + + + + Diagonal + + + + + Diagonal only + + + + + Orthogonal + + + + + Horizontal only + + + + + Vertical only + + + + + + + + Ruler direction + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Note: certain rulers may ignore these options + + + true + + + + + + + Qt::Vertical + + + + 121 + 70 + + + + + + + + + + + + scrollArea + snap_to_grid_cbx + snap_to_objects_cbx + angle_cb + + + + diff --git a/src/ant/ant/ant.pro b/src/ant/ant/ant.pro index 1d1286c735..dbd46c3747 100644 --- a/src/ant/ant/ant.pro +++ b/src/ant/ant/ant.pro @@ -14,6 +14,7 @@ FORMS = \ RulerConfigPage3.ui \ RulerConfigPage4.ui \ RulerPropertiesPage.ui \ + RulerOptions.ui \ } @@ -22,10 +23,12 @@ FORMS = \ HEADERS = \ antConfigPage.h \ antPropertiesPage.h \ + antRulerOptionsPage.h SOURCES = \ antConfigPage.cc \ antPropertiesPage.cc \ + antRulerOptionsPage.cc # Enabled without Qt: diff --git a/src/ant/ant/antConfigPage.cc b/src/ant/ant/antConfigPage.cc index f92c2c58ee..9706615ef6 100644 --- a/src/ant/ant/antConfigPage.cc +++ b/src/ant/ant/antConfigPage.cc @@ -375,7 +375,7 @@ ConfigPage4::show () mp_ui->style_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].style ()); mp_ui->outline_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].outline ()); mp_ui->t_angle_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].angle_constraint ()); - mp_ui->t_snap_cbx->setChecked (m_ruler_templates [m_current_template].snap ()); + mp_ui->t_never_snap_cbx->setChecked (! m_ruler_templates [m_current_template].snap ()); mp_ui->t_mode_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].mode ()); mp_ui->main_position->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].main_position ()); @@ -410,7 +410,7 @@ ConfigPage4::commit () ant::Template::ruler_mode_type mode = ant::Template::ruler_mode_type (mp_ui->t_mode_cb->currentIndex ()); m_ruler_templates [m_current_template].set_mode (mode); - m_ruler_templates [m_current_template].snap (mp_ui->t_snap_cbx->isChecked ()); + m_ruler_templates [m_current_template].snap (! mp_ui->t_never_snap_cbx->isChecked ()); m_ruler_templates [m_current_template].set_main_position (Object::position_type (mp_ui->main_position->currentIndex ())); m_ruler_templates [m_current_template].set_main_xalign (Object::alignment_type (mp_ui->main_xalign->currentIndex ())); diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index 0fc87fed97..8235a563fe 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -25,6 +25,7 @@ #include "layConverters.h" #include "layDispatcher.h" #include "layAbstractMenu.h" +#include "layLayoutViewBase.h" #include "tlColor.h" #include "tlLog.h" #if defined(HAVE_QT) @@ -139,11 +140,10 @@ PluginDeclaration::create_plugin (db::Manager *manager, lay::Dispatcher *, lay:: } std::vector -PluginDeclaration::additional_editor_options_pages () const +PluginDeclaration::additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const { std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); + names.push_back ("ant::RulerOptions"); return names; } diff --git a/src/ant/ant/antPlugin.h b/src/ant/ant/antPlugin.h index 334a460023..25a13a7a57 100644 --- a/src/ant/ant/antPlugin.h +++ b/src/ant/ant/antPlugin.h @@ -54,7 +54,7 @@ class PluginDeclaration virtual void uninitialize (lay::Dispatcher *); virtual bool menu_activated (const std::string &symbol) const; - virtual std::vector additional_editor_options_pages () const; + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *view) const; void register_annotation_template (const ant::Template &t, lay::Plugin *plugin = 0); void unregister_annotation_template (const std::string &category, lay::Plugin *plugin = 0); diff --git a/src/ant/ant/antRulerOptionsPage.cc b/src/ant/ant/antRulerOptionsPage.cc new file mode 100644 index 0000000000..199525ad31 --- /dev/null +++ b/src/ant/ant/antRulerOptionsPage.cc @@ -0,0 +1,119 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 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 + +*/ + +#if defined(HAVE_QT) + +#include "antRulerOptionsPage.h" +#include "antConfig.h" +#include "laySnap.h" +#include "layConverters.h" +#include "layDispatcher.h" +#include "tlString.h" +#include "tlClassRegistry.h" + +#include "ui_RulerOptions.h" + +namespace ant +{ + +// ------------------------------------------------------------------ +// RulerOptionsPage implementation + +RulerOptionsPage::RulerOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) +{ + mp_ui = new Ui::RulerOptions (); + mp_ui->setupUi (this); + + connect (mp_ui->angle_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->snap_to_grid_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->snap_to_objects_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); +} + +RulerOptionsPage::~RulerOptionsPage () +{ + delete mp_ui; + mp_ui = 0; +} + +std::string +RulerOptionsPage::title () const +{ + return tl::to_string (QObject::tr ("Ruler Options")); +} + +// Must match order in angle_cb +static std::vector s_ac_options = +{ + lay::AC_Any, + lay::AC_Diagonal, + lay::AC_DiagonalOnly, + lay::AC_Ortho, + lay::AC_Horizontal, + lay::AC_Vertical +}; + +void +RulerOptionsPage::apply (lay::Dispatcher *root) +{ + lay::angle_constraint_type ac = lay::AC_Any; + int ai = mp_ui->angle_cb->currentIndex (); + if (ai >= 0 && ai < int (s_ac_options.size ())) { + ac = s_ac_options [ai]; + } + + lay::ACConverter acc; + root->config_set (cfg_ruler_snap_mode, acc.to_string (ac)); + + root->config_set (cfg_ruler_obj_snap, tl::to_string (mp_ui->snap_to_objects_cbx->isChecked ())); + root->config_set (cfg_ruler_grid_snap, tl::to_string (mp_ui->snap_to_grid_cbx->isChecked ())); +} + +void +RulerOptionsPage::setup (lay::Dispatcher *root) +{ + lay::ACConverter acc; + lay::angle_constraint_type ac; + + ac = lay::AC_Any; + root->config_get (cfg_ruler_snap_mode, ac, acc); + for (int ai = 0; ai < int (s_ac_options.size ()); ++ai) { + if (s_ac_options [ai] == ac) { + mp_ui->angle_cb->setCurrentIndex (ai); + } + } + + bool snap_to_grid = false; + root->config_get (cfg_ruler_grid_snap, snap_to_grid); + mp_ui->snap_to_grid_cbx->setChecked (snap_to_grid); + + bool snap_to_objects = false; + root->config_get (cfg_ruler_obj_snap, snap_to_objects); + mp_ui->snap_to_objects_cbx->setChecked (snap_to_objects); +} + +static tl::RegisteredClass s_factory_ruler_options (new lay::EditorOptionsPageFactory ("ant::RulerOptions"), 0); + +} + +#endif + diff --git a/src/ant/ant/antRulerOptionsPage.h b/src/ant/ant/antRulerOptionsPage.h new file mode 100644 index 0000000000..4c20b573ad --- /dev/null +++ b/src/ant/ant/antRulerOptionsPage.h @@ -0,0 +1,66 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 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 + +*/ + +#if defined(HAVE_QT) + +#ifndef HDR_antRulerOptionsPage +#define HDR_antRulerOptionsPage + +#include "antCommon.h" + +#include "layEditorOptionsPageWidget.h" + +namespace Ui +{ + class RulerOptions; +} + +namespace ant +{ + +/** + * @brief The generic properties page + */ +class RulerOptionsPage + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + RulerOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~RulerOptionsPage (); + + virtual std::string title () const; + virtual int order () const { return -10; } + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); + +private: + Ui::RulerOptions *mp_ui; +}; + +} + +#endif + +#endif + diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index fe45fbecb1..faed89a27f 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1610,7 +1610,7 @@ Service::move_transform (const db::DPoint & /*p*/, db::DFTrans tr, lay::angle_co return; } - auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac; + auto ac_eff = ac == lay::AC_Global ? lay::AC_Any : ac; clear_mouse_cursors (); if (m_move_mode == MoveSelected) { @@ -1634,11 +1634,12 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) return; } - auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac; clear_mouse_cursors (); if (m_move_mode == MoveSelected) { + auto ac_eff = ac == lay::AC_Global ? lay::AC_Any : ac; + db::DVector dp = p - m_p1; dp = lay::snap_angle (dp, ac_eff); @@ -1656,7 +1657,28 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) } else if (m_move_mode != MoveNone) { - db::DPoint ps = snap2_visual (m_p1, p, &m_current, ac); + db::DPoint ps; + + if (m_move_mode == MoveP1 && m_current.segments () == 1) { + + // when moving p1 in a normal ruler, observe the currently active angle constraints and use p2 as reference + db::DPoint pref = m_current.seg_p2 (m_seg_index); + ps = snap2_visual (pref, p, &m_current, ac); + + } else if (m_move_mode == MoveP2 && m_current.segments () == 1) { + + // when moving p2 in a normal ruler, observe the currently active angle constraints and use p1 as reference + db::DPoint pref = m_current.seg_p1 (m_seg_index); + ps = snap2_visual (pref, p, &m_current, ac); + + } else { + + // other move modes use the angle constraint imposed by the modifier buttons + // and the start point for reference + ps = snap2_visual (m_p1, p, &m_current, ac == lay::AC_Global ? lay::AC_Any : ac); + + } + m_trans = db::DTrans (ps - m_p1); apply_partial_move (ps); diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 617ea3f1fa..15cb0fb2ef 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -600,30 +600,7 @@ CommonReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) // A cleanup may be necessary because of the following scenario: if library proxies contain subcells // which are proxies themselves, the proxy update may make them orphans (the proxies are regenerated). // The cleanup will removed these. - - // Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete - // the first (non-cold) proxy if there are only proxy top cells. - // We never clean up the top cell if there is a single one. This catches the case of having - // defunct proxies for top cells. - - std::set keep; - if (layout.end_top_cells () - layout.begin_top_down () == 1) { - keep.insert (*layout.begin_top_down ()); - } else { - for (auto c = layout.begin_top_down (); c != layout.end_top_cells (); ++c) { - const db::Cell *cptr = &layout.cell (*c); - if (cptr->is_proxy ()) { - if (! dynamic_cast (cptr) && keep.empty ()) { - keep.insert (*c); - } - } else { - keep.clear (); - break; - } - } - } - - layout.cleanup (keep); + layout.cleanup (); return layer_map_out (); } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 5ded121e1e..3ff11ccce6 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1389,7 +1389,6 @@ namespace { public: DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout) - : mp_layout (& layout) { // gets the text annotation property ID - // this is how the texts are passed for annotating the net names @@ -1445,7 +1444,6 @@ namespace private: std::pair m_text_annot_name_id; - const db::Layout *mp_layout; }; } diff --git a/src/db/db/dbEdgePairFilters.h b/src/db/db/dbEdgePairFilters.h index 3d8342b93e..0881b26496 100644 --- a/src/db/db/dbEdgePairFilters.h +++ b/src/db/db/dbEdgePairFilters.h @@ -119,7 +119,6 @@ class DB_PUBLIC InternalAngleEdgePairFilter virtual bool wants_variants () const { return false; } private: - bool m_inverted; db::EdgeAngleChecker m_checker; }; diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index ad6acd0b79..b2c02005a7 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -244,7 +244,6 @@ struct DB_PUBLIC EdgeOrientationFilter } private: - bool m_inverse; db::MagnificationAndOrientationReducer m_vars; EdgeAngleChecker m_checker; }; diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index fb9cbbc043..a69e8b4bbb 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1723,6 +1723,84 @@ Layout::refresh () } } +std::set +Layout::cells_to_cleanup (const std::set &keep_always) const +{ + std::set to_clean; + + // Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete + // the first (non-cold) proxy if there are only proxy top cells. + // We never clean up the top cell if there is a single one. This catches the case of having + // defunct proxies for top cells. + + if (end_top_cells () - begin_top_down () > 1) { + + std::set keep; + + for (auto c = begin_top_down (); c != end_top_cells (); ++c) { + const db::Cell *cptr = &cell (*c); + if (cptr->is_proxy ()) { + if (! dynamic_cast (cptr) && keep.empty ()) { + keep.insert (*c); + } + } else { + keep.clear (); + break; + } + + } + + for (auto c = begin_top_down (); c != end_top_cells (); ++c) { + if (cell (*c).is_proxy () && keep.find (*c) == keep.end () && keep_always.find (*c) == keep_always.end ()) { + to_clean.insert (*c); + } + } + + } + + // determine all cells that can be deleted as well because they are called + // by cells registered for cleanup and nowhere else + + if (! to_clean.empty ()) { + + // collect the called cells + std::set all_called; + for (auto c = to_clean.begin (); c != to_clean.end (); ++c) { + cell (*c).collect_called_cells (all_called, -1); + } + + // remove all called cells which are not proxies - we don't want to + // clean up those and their children. + std::set called; + for (auto c = all_called.begin (); c != all_called.end (); ++c) { + if (cell (*c).is_proxy ()) { + called.insert (*c); + } + } + + // From these cells erase all cells that have parents outside the subtree of our cell. + // Make sure this is done recursively by doing this top-down. + for (auto c = begin_top_down (); c != end_top_down (); ++c) { + if (called.find (*c) != called.end ()) { + const db::Cell &ccref = cell (*c); + for (db::Cell::parent_cell_iterator pc = ccref.begin_parent_cells (); pc != ccref.end_parent_cells (); ++pc) { + if (to_clean.find (*pc) == to_clean.end () && called.find (*pc) == called.end ()) { + // we have a parent outside the subset considered currently (either the cell was never in or + // it was removed itself already): remove this cell from the set of valid subcells. + called.erase (*c); + break; + } + } + } + } + + to_clean.insert (called.begin (), called.end ()); + + } + + return to_clean; +} + void Layout::cleanup (const std::set &keep) { @@ -1801,28 +1879,17 @@ Layout::cleanup (const std::set &keep) } - std::set cells_to_delete; - // deleting cells may create new top cells which need to be deleted as well, hence we iterate - // until there are no more cells to delete - while (true) { + // Remove all cells that are targeted for cleanup - // delete all cells that are top cells and are proxies. Those cells are proxies no longer required. - for (top_down_iterator c = begin_top_down (); c != end_top_cells (); ++c) { - if (cell (*c).is_proxy () && keep.find (*c) == keep.end ()) { - cells_to_delete.insert (*c); - } - } - - if (cells_to_delete.empty ()) { - break; + { + std::set cells_to_delete = cells_to_cleanup (keep); + if (! cells_to_delete.empty ()) { + delete_cells (cells_to_delete); } - - delete_cells (cells_to_delete); - cells_to_delete.clear (); - } + // Try to ensure that cell names reflect the library cell names. The latter is good for LVS for example. for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ++c) { @@ -2965,12 +3032,7 @@ Layout::has_context_info (cell_index_type cell_index) const } } - const db::Cell &cref = cell (cell_index); - if (cref.is_proxy () && ! cref.is_top ()) { - return true; - } else { - return false; - } + return cell (cell_index).is_proxy (); } bool diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index b9bc0f1c96..36ec43dc5a 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1682,6 +1682,19 @@ class DB_PUBLIC Layout */ void cleanup (const std::set &keep = std::set ()); + /** + * @brief Gets a list of cells which are cleanup candidates + * + * The returned list also includes cells that are indirectly cleaned up. + * The cleanup only removes top cells which represent unused proxies + * with some exceptions - for example proxies that are single top cells + * are retained. + * + * However, removing the top cells should not produce new top cells + * of proxies, so these cells need to be removed as well. + */ + std::set cells_to_cleanup (const std::set &keep = std::set ()) const; + /** * @brief Calls "update" on all cells of the layout * diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index b4dcf2608c..2a62e55efc 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -458,7 +458,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo std::string param_name; read_word_or_quoted (param_name); int primary = read_int (); - int default_value = read_double (); + double default_value = read_double (); if (! dc->has_parameter_with_name (param_name)) { db::DeviceParameterDefinition pd; pd.set_name (param_name); diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 236a3f38c2..33c14497b2 100644 --- a/src/db/db/dbSaveLayoutOptions.cc +++ b/src/db/db/dbSaveLayoutOptions.cc @@ -372,12 +372,19 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set cleaned_cells = layout.cells_to_cleanup (); + bool has_skipped_replica = false; // check if we have skipped replicas if (has_context) { for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end () && ! has_skipped_replica; ++cell) { - has_skipped_replica = cell->can_skip_replica (); + if (cell->can_skip_replica () && cleaned_cells.find (cell->cell_index ()) == cleaned_cells.end ()) { + has_skipped_replica = true; + } } } @@ -386,7 +393,7 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set is_top ()) { + if (cell->is_top () && cleaned_cells.find (cell->cell_index ()) == cleaned_cells.end ()) { cells.insert (cell->cell_index ()); collect_called_cells_unskipped (cell->cell_index (), layout, cells); } @@ -394,8 +401,10 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set cell_index ()); + for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) { + if (cleaned_cells.find (cell->cell_index ()) == cleaned_cells.end ()) { + cells.insert (cell->cell_index ()); + } } } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 6e0718fe35..07ca93e648 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -32,6 +32,7 @@ #include "dbLibraryProxy.h" #include "dbLibraryManager.h" #include "dbLibrary.h" +#include "dbColdProxy.h" #include "dbLayout.h" #include "dbLayoutUtils.h" #include "dbLayerMapping.h" @@ -949,6 +950,11 @@ static std::vector caller_cells (const db::Cell *c) return std::vector (ids.begin (), ids.end ()); } +static bool is_cold_proxy (const db::Cell *cell) +{ + return dynamic_cast (cell) != 0; +} + static bool is_library_cell (const db::Cell *cell) { return dynamic_cast (cell) != 0; @@ -974,6 +980,36 @@ static db::Library *library (const db::Cell *cell) } } +static std::string library_name (const db::Cell *cell) +{ + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().lib_name; + } else { + const db::Library *l = library (cell); + if (l) { + return l->get_name (); + } else { + return std::string (); + } + } +} + +static std::string library_cell_name (const db::Cell *cell) +{ + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().cell_name; + } else { + const db::Library *l = library (cell); + if (l) { + return l->layout ().cell_name (library_cell_index (cell)); + } else { + return std::string (); + } + } +} + static void change_library_ref (db::Cell *cell, db::lib_id_type lib_id, db::cell_index_type cell_index) { db::LibraryProxy *l = dynamic_cast (cell); @@ -1173,13 +1209,25 @@ static const std::vector &pcell_parameters (const db::Cell *cell) static tl::Variant pcell_parameter (const db::Cell *cell, const std::string &name) { - return cell->layout ()->get_pcell_parameter (cell->cell_index (), name); + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + const auto &pp = cp->context_info ().pcell_parameters; + auto i = pp.find (name); + return i != pp.end () ? i->second : tl::Variant (); + } else { + return cell->layout ()->get_pcell_parameter (cell->cell_index (), name); + } } static std::map pcell_parameters_by_name (const db::Cell *cell) { - tl_assert (cell->layout () != 0); - return cell->layout ()->get_named_pcell_parameters (cell->cell_index ()); + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().pcell_parameters; + } else { + tl_assert (cell->layout () != 0); + return cell->layout ()->get_named_pcell_parameters (cell->cell_index ()); + } } static void refresh (db::Cell *cell) @@ -1203,6 +1251,21 @@ static const db::PCellDeclaration *pcell_declaration (const db::Cell *cell) } } +static std::string pcell_name (const db::Cell *cell) +{ + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().pcell_name; + } else { + const db::PCellDeclaration *pd = pcell_declaration (cell); + if (pd) { + return pd->name (); + } else { + return std::string (); + } + } +} + static const db::PCellDeclaration *pcell_declaration_of_inst (const db::Cell *cell, const db::Cell::instance_type &ref) { tl_assert (cell->layout () != 0); @@ -3193,7 +3256,7 @@ Class decl_Cell ("db", "Cell", "This method has been introduced in version 0.20.\n" ) + gsi::method ("is_proxy?", &db::Cell::is_proxy, - "@brief Returns true, if the cell presents some external entity \n" + "@brief Returns true, if the cell presents some external entity\n" "A cell may represent some data which is imported from some other source, i.e.\n" "a library. Such cells are called \"proxy cells\". For a library reference, the\n" "proxy cell is some kind of pointer to the library and the cell within the library.\n" @@ -3201,11 +3264,31 @@ Class decl_Cell ("db", "Cell", "For PCells, this data can even be computed through some script.\n" "A PCell proxy represents all instances with a given set of parameters.\n" "\n" - "Proxy cells cannot be modified, except that pcell parameters can be modified\n" - "and PCell instances can be recomputed.\n" + "Proxy cells should not be modified directly - i.e. the shapes or instances should not\n" + "be touched. However, you can change PCell parameters (\\change_pcell_parameter, \\change_pcell_parameters)\n" + "or change the library reference (\\change_ref).\n" "\n" "This method has been introduced in version 0.22.\n" ) + + gsi::method_ext ("is_cold_proxy?", &is_cold_proxy, + "@brief Returns true, if the cell is a 'cold proxy'\n" + "Cold proxies are cells that refer to a library cell or PCell variant, but can temporarily not be resolved -\n" + "for example, because the library is not installed. Such cells are basically placeholders\n" + "for library references and also carry PCell parameter information needed to establish\n" + "the link to the library PCell, once the library is available again.\n" + "\n" + "You can use \\library_name to obtain the name of the library the proxy points to, " + "\\library_cell_name to obtain the cell name in that library, " + "\\pcell_name to obtain the PCell name if it is a PCell proxy, " + "and \\pcell_parameter or \\pcell_parameters_by_name to obtain the PCell parameters.\n" + "\n" + "Cold proxies cannot be created or modified. Cold proxies are basically error indicators " + "and should be fixed by installing the respective library. Their layout state\n" + "reflects the last version of the layout when the cell was functional and properly\n" + "linked to a library. Still, they can be used in read-only applications.\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("is_library_cell?", &is_library_cell, "@brief Returns true, if the cell is a proxy cell pointing to a library cell\n" "If the cell is imported from some library, this attribute returns true.\n" @@ -3231,10 +3314,32 @@ Class decl_Cell ("db", "Cell", ) + gsi::method_ext ("library", &library, "@brief Returns a reference to the library from which the cell is imported\n" - "if the cell is not imported from a library, this reference is nil.\n" + "If the cell is not imported from a library, this reference is nil.\n" "\n" "This method has been introduced in version 0.22.\n" ) + + gsi::method_ext ("library_cell_name", &library_cell_name, + "@brief Returns the cell name inside the library from which the cell is imported\n" + "If the cell is not imported from a library, the return value is an empty string.\n" + "This method is basically a convenience function, equivalent to taking the name\n" + "from \\library and \\library_cell_index.\n" + "Note that for PCells, 'library_cell_name' is the name of the PCell proxy cell inside " + "the library, not the name of the PCell.\n" + "\n" + "However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the name of cell inside the (missing) library.\n" + "\n" + "This method has been introduced in version 0.30.8.\n" + ) + + gsi::method_ext ("library_name", &library_name, + "@brief Returns the name of the library from which the cell is imported\n" + "If the cell is not imported from a library, the return value is an empty string.\n" + "This method is basically a convenience function, equivalent to taking the name\n" + "from \\library. However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the name of the (missing) library.\n" + "\n" + "This method has been introduced in version 0.30.8.\n" + ) + gsi::method_ext ("change_ref", &change_library_ref, gsi::arg ("lib_id"), gsi::arg ("lib_cell_index"), "@brief Changes the reference to a different library cell\n" "This method requires a cell that is a library reference (i.e. \\is_library_cell? is true). It will " @@ -3307,6 +3412,9 @@ Class decl_Cell ("db", "Cell", "If the cell is not a PCell variant or the name is not a valid PCell parameter name, " "the return value is nil.\n" "\n" + "This method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the value of the given stored PCell parameter.\n" + "\n" "This method has been introduced in version 0.25." ) + gsi::method_ext ("pcell_parameters_by_name", &pcell_parameters_by_name, @@ -3316,8 +3424,20 @@ Class decl_Cell ("db", "Cell", "method returns an empty dictionary. This method also returns the PCell parameters if\n" "the cell is a PCell imported from a library.\n" "\n" + "This method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the names and values of the stored PCell parameters.\n" + "\n" "This method has been introduced in version 0.24.\n" ) + + gsi::method_ext ("pcell_name", &pcell_name, + "@brief Returns the PCell name if the cell is a PCell variant\n" + "If this cell is not a PCell variant, this method returns an empty string.\n" + "This method is basically a convenience function, equivalent to taking the name\n" + "from \\pcell_declaration. However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the name of the (missing) PCell.\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("pcell_declaration", &pcell_declaration, "@brief Returns a reference to the PCell declaration\n" "If this cell is not a PCell variant, this method returns nil.\n" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 9d76ae5e94..46329ec133 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -642,12 +642,22 @@ static tl::Variant get_property_from_id (db::properties_id_type id, const tl::Va } static void -delete_cells (db::Layout *layout, const std::vector &cell_indices) +delete_cells (db::Layout *layout, const std::vector &cell_indexes) { - for (auto ci = cell_indices.begin (); ci != cell_indices.end (); ++ci) { + for (auto ci = cell_indexes.begin (); ci != cell_indexes.end (); ++ci) { check_cell_index (layout, *ci); } - layout->delete_cells (cell_indices.begin (), cell_indices.end ()); + layout->delete_cells (cell_indexes.begin (), cell_indexes.end ()); +} + +static void +delete_cells_ptr (db::Layout *layout, const std::vector &cells) +{ + std::set cell_indexes; + for (auto c = cells.begin (); c != cells.end (); ++c) { + cell_indexes.insert ((*c)->cell_index ()); + } + layout->delete_cells (cell_indexes); } static void @@ -657,20 +667,76 @@ delete_cell_rec (db::Layout *layout, db::cell_index_type cell_index) layout->delete_cell_rec (cell_index); } -static void +static void +delete_cell_rec_ptr (db::Layout *layout, db::Cell *cell) +{ + layout->delete_cell_rec (cell->cell_index ()); +} + +static void prune_cell (db::Layout *layout, db::cell_index_type cell_index, int levels) { check_cell_index (layout, cell_index); layout->prune_cell (cell_index, levels); } -static void +static void +prune_cell_ptr (db::Layout *layout, db::Cell *cell, int levels) +{ + layout->prune_cell (cell->cell_index (), levels); +} + +static void +prune_cells (db::Layout *layout, const std::vector &cell_indexes, int levels) +{ + for (auto ci = cell_indexes.begin (); ci != cell_indexes.end (); ++ci) { + check_cell_index (layout, *ci); + } + layout->prune_cells (cell_indexes.begin (), cell_indexes.end (), levels); +} + +static void +prune_cells_ptr (db::Layout *layout, const std::vector &cells, int levels) +{ + std::set cell_indexes; + for (auto c = cells.begin (); c != cells.end (); ++c) { + cell_indexes.insert ((*c)->cell_index ()); + } + layout->prune_cells (cell_indexes, levels); +} + +static void prune_subcells (db::Layout *layout, db::cell_index_type cell_index, int levels) { check_cell_index (layout, cell_index); layout->prune_subcells (cell_index, levels); } +static void +prune_subcells_ptr (db::Layout *layout, db::Cell *cell, int levels) +{ + layout->prune_subcells (cell->cell_index (), levels); +} + +static void +prune_subcells_many (db::Layout *layout, const std::vector &cell_indexes, int levels) +{ + for (auto ci = cell_indexes.begin (); ci != cell_indexes.end (); ++ci) { + check_cell_index (layout, *ci); + } + layout->prune_subcells (cell_indexes.begin (), cell_indexes.end (), levels); +} + +static void +prune_subcells_many_ptr (db::Layout *layout, const std::vector &cells, int levels) +{ + std::set cell_indexes; + for (auto c = cells.begin (); c != cells.end (); ++c) { + cell_indexes.insert ((*c)->cell_index ()); + } + layout->prune_subcells (cell_indexes, levels); +} + static void flatten (db::Layout *layout, db::cell_index_type cell_index, int levels, bool prune) { @@ -678,7 +744,16 @@ flatten (db::Layout *layout, db::cell_index_type cell_index, int levels, bool pr layout->flatten (layout->cell (cell_index), levels, prune); } -static void +static void +flatten_ptr (db::Layout *layout, db::Cell *cell, int levels, bool prune) +{ + if (! cell) { + return; + } + layout->flatten (*cell, levels, prune); +} + +static void flatten_into (db::Layout *layout, db::cell_index_type cell_index, db::cell_index_type target_cell_index, const db::ICplxTrans &t, int levels) { check_cell_index (layout, cell_index); @@ -686,7 +761,16 @@ flatten_into (db::Layout *layout, db::cell_index_type cell_index, db::cell_index layout->flatten (layout->cell (cell_index), layout->cell (target_cell_index), t, levels); } -static void +static void +flatten_into_ptr (db::Layout *layout, db::Cell *cell, db::Cell *target_cell, const db::ICplxTrans &t, int levels) +{ + if (! cell || ! target_cell) { + return; + } + layout->flatten (*cell, *target_cell, t, levels); +} + +static void write_simple (db::Layout *layout, const std::string &filename) { db::SaveLayoutOptions options; @@ -866,6 +950,11 @@ static void delete_cell (db::Layout *ly, db::cell_index_type ci) ly->delete_cell (ci); } +static void delete_cell_ptr (db::Layout *ly, db::Cell *cell) +{ + ly->delete_cell (cell->cell_index ()); +} + static void rename_cell (db::Layout *ly, db::cell_index_type ci, const std::string &name) { check_cell_index (ly, ci); @@ -1655,6 +1744,20 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + + gsi::method_ext ("delete_cell", &delete_cell_ptr, gsi::arg ("cell"), + "@brief Deletes a cell\n" + "\n" + "This deletes a cell but not the sub cells of the cell.\n" + "These subcells will likely become new top cells unless they are used\n" + "otherwise.\n" + "All instances of this cell are deleted as well.\n" + "Hint: to delete multiple cells, use \"delete_cells\" which is \n" + "far more efficient in this case.\n" + "\n" + "@param cell The cell to delete\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("delete_cells", &delete_cells, gsi::arg ("cell_index_list"), "@brief Deletes multiple cells\n" "\n" @@ -1667,19 +1770,73 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + - gsi::method_ext ("prune_subcells", &prune_subcells, gsi::arg ("cell_index"), gsi::arg ("levels"), + gsi::method_ext ("delete_cells", &delete_cells_ptr, gsi::arg ("cell_list"), + "@brief Deletes multiple cells\n" + "\n" + "This deletes the cells but not the sub cells of these cells.\n" + "These subcells will likely become new top cells unless they are used\n" + "otherwise.\n" + "All instances of these cells are deleted as well.\n" + "\n" + "@param cell_list An list of cells to delete\n" + "\n" + "This convenience variant taking a list of cell objects has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_subcells", &prune_subcells, gsi::arg ("cell_index"), gsi::arg ("levels", -1), "@brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy\n" "\n" "This deletes all sub cells of the cell which are not used otherwise.\n" "All instances of the deleted cells are deleted as well.\n" "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" "\n" + "A variant exists that takes a list of cell indexes and which is more efficient than calling\n" + "'prune_subcells' multiple times on a single cell.\n" + "\n" "@param cell_index The root cell from which to delete a sub cells\n" "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" "\n" "This method has been introduced in version 0.20.\n" ) + - gsi::method_ext ("prune_cell", &prune_cell, gsi::arg ("cell_index"), gsi::arg ("levels"), + gsi::method_ext ("prune_subcells", &prune_subcells_ptr, gsi::arg ("cell"), gsi::arg ("levels", -1), + "@brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy\n" + "\n" + "This deletes all sub cells of the cell which are not used otherwise.\n" + "All instances of the deleted cells are deleted as well.\n" + "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" + "\n" + "A variant exists that takes a list of cells and which is more efficient than calling\n" + "'prune_subcells' multiple times on a single cell.\n" + "\n" + "@param cell The root cell from which to delete a sub cells\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This convenience variant taking a list of cell objects has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_subcells", &prune_subcells_many, gsi::arg ("cell_index_list"), gsi::arg ("levels", -1), + "@brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy\n" + "\n" + "This deletes all sub cells of the given cells which are not used otherwise.\n" + "All instances of the deleted cells are deleted as well.\n" + "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" + "\n" + "@param cell_index_list The root cells from which to delete the sub cells\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_subcells", &prune_subcells_many_ptr, gsi::arg ("cell_list"), gsi::arg ("levels", -1), + "@brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy\n" + "\n" + "This deletes all sub cells of the given cells which are not used otherwise.\n" + "All instances of the deleted cells are deleted as well.\n" + "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" + "\n" + "@param cell_list The root cells from which to delete the sub cells\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cell", &prune_cell, gsi::arg ("cell_index"), gsi::arg ("levels", -1), "@brief Deletes a cell plus subcells not used otherwise\n" "\n" "This deletes a cell and also all sub cells of the cell which are not used otherwise.\n" @@ -1687,10 +1844,53 @@ Class decl_Layout ("db", "Layout", "only the direct children of the cell are deleted with the cell itself.\n" "All instances of this cell are deleted as well.\n" "\n" + "A version that allows pruning multiple cells in one call is \\prune_cells.\n" + "\n" "@param cell_index The index of the cell to delete\n" "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" "\n" - "This method has been introduced in version 0.20.\n" + "This method has been introduced in version 0.20. The 'levels' argument was made optional in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cell", &prune_cell_ptr, gsi::arg ("cell"), gsi::arg ("levels", -1), + "@brief Deletes a cell plus subcells not used otherwise\n" + "\n" + "This deletes a cell and also all sub cells of the cell which are not used otherwise.\n" + "The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that " + "only the direct children of the cell are deleted with the cell itself.\n" + "All instances of this cell are deleted as well.\n" + "\n" + "A version that allows pruning multiple cells in one call is \\prune_cells.\n" + "\n" + "@param cell The cell to delete\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cells", &prune_cells, gsi::arg ("cell_indexes"), gsi::arg ("levels", -1), + "@brief Deletes cells plus subcells not used otherwise\n" + "\n" + "This deletes the given cells and also all sub cells of the cells which are not used otherwise.\n" + "The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that " + "only the direct children of the cell are deleted with the cell itself.\n" + "All instances of the pruned cells are deleted as well.\n" + "\n" + "@param cell_indexes The indexes of the cells to delete\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cells", &prune_cells_ptr, gsi::arg ("cells"), gsi::arg ("levels", -1), + "@brief Deletes cells plus subcells not used otherwise\n" + "\n" + "This deletes the given cells and also all sub cells of the cells which are not used otherwise.\n" + "The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that " + "only the direct children of the cell are deleted with the cell itself.\n" + "All instances of the pruned cells are deleted as well.\n" + "\n" + "@param cells The cells to delete\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" ) + gsi::method_ext ("delete_cell_rec", &delete_cell_rec, gsi::arg ("cell_index"), "@brief Deletes a cell plus all subcells\n" @@ -1702,6 +1902,16 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + + gsi::method_ext ("delete_cell_rec", &delete_cell_rec_ptr, gsi::arg ("cell"), + "@brief Deletes a cell plus all subcells\n" + "\n" + "This deletes a cell and also all sub cells of the cell.\n" + "In contrast to \\prune_cell, all cells are deleted together with their instances even if they are used otherwise.\n" + "\n" + "@param cell The cell to delete\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("insert", &insert_region, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Inserts a region into the given cell and layer\n" @@ -1750,7 +1960,7 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.27.\n" ) + - gsi::method_ext ("flatten", &flatten, gsi::arg ("cell_index"), gsi::arg ("levels"), gsi::arg ("prune"), + gsi::method_ext ("flatten", &flatten, gsi::arg ("cell_index"), gsi::arg ("levels", -1), gsi::arg ("prune", true), "@brief Flattens the given cell\n" "\n" "This method propagates all shapes and instances from the specified number of hierarchy levels below into the given cell.\n" @@ -1761,9 +1971,22 @@ Class decl_Layout ("db", "Layout", "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" "@param prune Set to true to remove orphan cells.\n" "\n" - "This method has been introduced in version 0.20.\n" + "This method has been introduced in version 0.20. The 'levels' and 'prune' arguments have been made optional in version 0.30.9.\n" + ) + + gsi::method_ext ("flatten", &flatten_ptr, gsi::arg ("cell"), gsi::arg ("levels", -1), gsi::arg ("prune", true), + "@brief Flattens the given cell\n" + "\n" + "This method propagates all shapes and instances from the specified number of hierarchy levels below into the given cell.\n" + "It also removes the instances of the cells from which the shapes came from, but does not remove the cells themselves if prune is set to false.\n" + "If prune is set to true, these cells are removed if not used otherwise.\n" + "\n" + "@param cell The cell which should be flattened\n" + "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" + "@param prune Set to true to remove orphan cells.\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" ) + - gsi::method_ext ("flatten_into", &flatten_into, gsi::arg ("source_cell_index"), gsi::arg ("target_cell_index"), gsi::arg ("trans"), gsi::arg ("levels"), + gsi::method_ext ("flatten_into", &flatten_into, gsi::arg ("source_cell_index"), gsi::arg ("target_cell_index"), gsi::arg ("trans", db::ICplxTrans (), "unity"), gsi::arg ("levels", -1), "@brief Flattens the given cell into another cell\n" "\n" "This method works like 'flatten', but allows specification of a target cell which can be different from the source cell plus " @@ -1776,7 +1999,22 @@ Class decl_Layout ("db", "Layout", "@param trans The transformation to apply on the output shapes and instances\n" "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" "\n" - "This method has been introduced in version 0.24.\n" + "This method has been introduced in version 0.24. The 'trans' and 'levels' arguments have been made optional is version 0.30.9.\n" + ) + + gsi::method_ext ("flatten_into", &flatten_into_ptr, gsi::arg ("source_cell"), gsi::arg ("target_cell"), gsi::arg ("trans", db::ICplxTrans (), "unity"), gsi::arg ("levels", -1), + "@brief Flattens the given cell into another cell\n" + "\n" + "This method works like 'flatten', but allows specification of a target cell which can be different from the source cell plus " + "a transformation which is applied for all shapes and instances in the target cell.\n" + "\n" + "In contrast to the 'flatten' method, the source cell is not modified.\n" + "\n" + "@param source_cell The source cell which should be flattened\n" + "@param target_cell The target cell into which the resulting objects are written\n" + "@param trans The transformation to apply on the output shapes and instances\n" + "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This convenience variant taking a cell objects has been introduced in version 0.30.9.\n" ) + gsi::method ("start_changes", &db::Layout::start_changes, "@brief Signals the start of an operation bringing the layout into invalid state\n" diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 9b6178ca8b..c1dee66d1f 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -1040,3 +1040,134 @@ TEST(101_CopyTreeDoesNotModifyPolygons) EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {TOP}\nboundary 1 0 {0 0} {0 1000} {500 1000} {1500 1000} {1000 1000} {1000 0} {0 0}\nend_cell\nend_lib\n"); } +namespace +{ + +class LIBT_L + : public db::Library +{ +public: + LIBT_L (tl::TestBase *_this) + : Library () + { + set_name("L"); + set_description("A test library."); + + layout ().dbu (0.001); + + db::LayerProperties p; + + p.layer = 23; + p.datatype = 0; + unsigned int l_cont = layout ().insert_layer (p); + + p.layer = 16; + p.datatype = 0; + unsigned int l_gate = layout ().insert_layer (p); + + db::Cell &cell_a = layout ().cell (layout ().add_cell ("A")); + cell_a.shapes(l_cont).insert(db::Box (50, 50, 150, 150)); + cell_a.shapes(l_gate).insert(db::Box (0, 0, 200, 1000)); + + db::Cell &top = layout ().cell (layout ().add_cell ("TOP")); + top.insert (db::CellInstArray (db::CellInst (cell_a.cell_index ()), db::Trans (db::Vector (0, 0)))); + } + + ~LIBT_L() + { + // .. nothing yet .. + } +}; + +} + +static std::string cells2string (const db::Layout &layout, bool top = true) +{ + std::string s; + auto cto = top ? layout.end_top_cells () : layout.end_top_down (); + for (auto c = layout.begin_top_down (); c != cto; ++c) { + if (! s.empty ()) { + s += ","; + } + if (layout.cell (*c).is_proxy ()) { + s += "*"; + } + s += layout.cell_name (*c); + } + return s; +} + +// issue #2350 +TEST(101_CleanupBehavior) +{ + std::unique_ptr lib (new LIBT_L (_this)); + db::LibraryManager::instance ().register_lib (lib.get ()); + + db::Manager m (true); + db::Layout layout (&m); + layout.do_cleanup (true); + layout.dbu (0.001); + + db::Cell &top = layout.cell (layout.add_cell ("TOPTOP")); + + db::cell_index_type lib_top = lib->layout ().cell_by_name ("TOP").second; + db::cell_index_type lp1 = layout.get_lib_proxy (lib.get (), lib_top); + + db::Instance i1 = top.insert (db::CellInstArray (db::CellInst (lp1), db::Trans (db::Vector (0, 0)))); + + db::Layout layout2 = layout; + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + layout2.cleanup (); + + // cleanup does not change anything as the top cell is not a proxy + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + top.erase (i1); + + layout2 = layout; + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP,*TOP"); + + layout2.cleanup (); + + // cleanup removes the proxy and subcell + EXPECT_EQ (cells2string (layout2, false), "TOPTOP"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + // now we borrow *A (LIB.A) and place it in TOPTOP + db::cell_index_type ci_a = layout.cell_by_name ("A").second; + EXPECT_EQ (layout.cell (ci_a).is_proxy (), true); + top.insert (db::CellInstArray (db::CellInst (ci_a), db::Trans (db::Vector (0, 0)))); + + layout2 = layout; + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP,*TOP"); + + layout2.cleanup (); + + // cleanup removes the *TOP proxy, but not *A as it is used by TOPTOP + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + layout2 = layout; + layout2.delete_cell (top.cell_index ()); // layout and layout2 use the same cell indexes + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "*TOP"); + + layout2.cleanup (); + + // cleanup does not remove the proxy as it is the only top cell + EXPECT_EQ (cells2string (layout2, false), "*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "*TOP"); +} diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index afee536b77..aca1a28e7e 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -574,7 +574,9 @@ in connect, soft_connect,

If layers are not named, they will be given a name made from the name_prefix and an incremental number when the layer is used for the -first time. +first time. As the default name prefix is "l", you may can name conflicts +with auto-named layers, if you register explicit layer names like "l5". +Consider changing the name prefix in that case to some prefix you are not using.

name can only be used once on a layer and the layer names must be unique (not taken by another layer). diff --git a/src/doc/doc/manual/ruler_properties.xml b/src/doc/doc/manual/ruler_properties.xml index 8a23920d43..184c02f414 100644 --- a/src/doc/doc/manual/ruler_properties.xml +++ b/src/doc/doc/manual/ruler_properties.xml @@ -33,14 +33,14 @@ just being horizontal. By default, the ruler uses the global setting. It can however be configured to provide its own constraint. -

  • Object snapping: each ruler can be configure to snap to the closest object edge or vertex. - By default, the rulers use the global setting. It may be disabled however for each ruler. -
  • Mode: in normal mode, two clicks are required to define a ruler: to set the first point and to set the second one. In "Single click" mode, a single click will set both points to the same. In "Auto measure" mode, the points will be determined by looking for edges in the vicinity of the click point and adjusting the points accordingly.
  • +
  • Never snap to objects: if this option is on, the ruler never snaps to drawing + objects, even if that global option is enabled. +
  • diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 7b803db766..1eeb20541c 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -142,8 +142,8 @@ class PluginDeclaration } } - virtual std::vector additional_editor_options_pages () const - { + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *) const + { std::vector names; names.push_back ("GenericEditorOptions"); return names; @@ -415,7 +415,7 @@ class PartialPluginDeclaration return true; } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *) const { std::vector names; names.push_back ("GenericEditorOptions"); diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index 4f6d97cf3d..f823a31988 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -47,6 +47,65 @@ namespace img { +// -------------------------------------------------------------------------------------- + +namespace +{ + +struct compare_first_of_node +{ + bool operator() (const std::pair > &a, const std::pair > &b) const + { + return a.first < b.first; + } +}; + +} + +static tl::Color +interpolated_color2 (const std::pair > &n1, const std::pair > &n2, double x) +{ + double x1 = n1.first; + double x2 = n2.first; + + unsigned int h1 = 0, s1 = 0, v1 = 0; + n1.second.second.get_hsv (h1, s1, v1); + + unsigned int h2 = 0, s2 = 0, v2 = 0; + n2.second.first.get_hsv (h2, s2, v2); + + int h = int (0.5 + h1 + double(x - x1) * double (int (h2) - int (h1)) / double(x2 - x1)); + int s = int (0.5 + s1 + double(x - x1) * double (int (s2) - int (s1)) / double(x2 - x1)); + int v = int (0.5 + v1 + double(x - x1) * double (int (v2) - int (v1)) / double(x2 - x1)); + + return tl::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v); +} + +tl::Color +interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x) +{ + if (nodes.size () < 1) { + + return tl::Color (); + + } else if (nodes.size () < 2) { + + return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second; + + } else { + + std::vector > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (tl::Color (), tl::Color ())), compare_first_of_node ()); + if (p == nodes.end ()) { + return nodes.back ().second.second; + } else if (p == nodes.begin ()) { + return nodes.front ().second.first; + } else { + return interpolated_color2 (p[-1], *p, x); + } + + } +} + // -------------------------------------------------------------------------------------- // img::DataMapping implementation @@ -153,6 +212,21 @@ DataMapping::operator< (const DataMapping &d) const return false; } +static inline double color_to_channel_value (const tl::Color &c, unsigned int channel) +{ + double y = 0.0; + + if (channel == 0) { + y = c.red (); + } else if (channel == 1) { + y = c.green (); + } else if (channel == 2) { + y = c.blue (); + } + + return y / 255.0; +} + tl::DataMappingBase * DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, unsigned int channel) const { @@ -191,6 +265,8 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns if (monochrome && false_color_nodes.size () > 1) { tl::TableDataMapping *gray_to_color = new tl::TableDataMapping (); + double xfence = false_color_nodes.front ().first - 1.0; + double xeps = 1e-5; for (unsigned int i = 1; i < false_color_nodes.size (); ++i) { @@ -212,35 +288,35 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns for (int j = 0; j < n; ++j) { - tl::Color c = interpolated_color (false_color_nodes, x); + tl::Color c = interpolated_color2 (false_color_nodes [i - 1], false_color_nodes [i], x); - double y = 0.0; - if (channel == 0) { - y = c.red (); - } else if (channel == 1) { - y = c.green (); - } else if (channel == 2) { - y = c.blue (); - } - - gray_to_color->push_back (x, y / 255.0); + double xx = std::max (xfence * (1.0 + xeps), x); + xfence = xx; + double y = color_to_channel_value (c, channel); + gray_to_color->push_back (xx, y); x += dx; } - } + // on discontinuous colors add another entry that manifests the discontinuity + if (i + 1 < false_color_nodes.size () && false_color_nodes [i].second.first != false_color_nodes [i].second.second) { + + double x = false_color_nodes [i].first; + double xx = std::max (xfence * (1.0 + xeps), x); + xfence = xx; + double y = color_to_channel_value (false_color_nodes [i].second.first, channel); + gray_to_color->push_back (xx, y); + + } - double ylast = 0.0; - if (channel == 0) { - ylast = false_color_nodes.back ().second.second.red (); - } else if (channel == 1) { - ylast = false_color_nodes.back ().second.second.green (); - } else if (channel == 2) { - ylast = false_color_nodes.back ().second.second.blue (); } - gray_to_color->push_back (false_color_nodes.back ().first, ylast / 255.0); + double x = false_color_nodes.back ().first; + double xx = std::max (xfence * (1.0 + xeps), x); + xfence = xx; + double ylast = color_to_channel_value (false_color_nodes.back ().second.first, channel); + gray_to_color->push_back (xx, ylast); dm = new tl::CombinedDataMapping ( to_pixel, @@ -266,57 +342,6 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns return dm; } -// -------------------------------------------------------------------------------------- - -namespace -{ - -struct compare_first_of_node -{ - bool operator() (const std::pair > &a, const std::pair > &b) const - { - return a.first < b.first; - } -}; - -} - -tl::Color -interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x) -{ - if (nodes.size () < 1) { - return tl::Color (); - } else if (nodes.size () < 2) { - return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second; - } else { - - std::vector > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (tl::Color (), tl::Color ())), compare_first_of_node ()); - if (p == nodes.end ()) { - return nodes.back ().second.second; - } else if (p == nodes.begin ()) { - return nodes.front ().second.first; - } else { - - double x1 = p[-1].first; - double x2 = p->first; - - unsigned int h1 = 0, s1 = 0, v1 = 0; - p[-1].second.second.get_hsv (h1, s1, v1); - - unsigned int h2 = 0, s2 = 0, v2 = 0; - p->second.first.get_hsv (h2, s2, v2); - - int h = int (0.5 + h1 + double(x - x1) * double (int (h2) - int (h1)) / double(x2 - x1)); - int s = int (0.5 + s1 + double(x - x1) * double (int (s2) - int (s1)) / double(x2 - x1)); - int v = int (0.5 + v1 + double(x - x1) * double (int (v2) - int (v1)) / double(x2 - x1)); - - return tl::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v); - - } - - } -} - // -------------------------------------------------------------------------------------- // img::DataHeader definition and implementation diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index 6658d2ae96..8d254ee516 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -145,7 +145,7 @@ struct IMG_PUBLIC DataMapping /** * @brief A helper function to interpolate a color in the color bar at a given x */ -tl::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x); +IMG_PUBLIC tl::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x); /** * @brief A image object diff --git a/src/img/img/imgPlugin.cc b/src/img/img/imgPlugin.cc index 3438f73430..ea435d66a5 100644 --- a/src/img/img/imgPlugin.cc +++ b/src/img/img/imgPlugin.cc @@ -62,15 +62,6 @@ PluginDeclaration::get_options (std::vector < std::pair (cfg_images_visible, "true")); } -std::vector -PluginDeclaration::additional_editor_options_pages () const -{ - std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); - return names; -} - static tl::RegisteredClass config_decl (new img::PluginDeclaration (), 4000, "img::Plugin"); } diff --git a/src/img/img/imgPlugin.h b/src/img/img/imgPlugin.h index 0214662fca..3302cbc3cb 100644 --- a/src/img/img/imgPlugin.h +++ b/src/img/img/imgPlugin.h @@ -39,7 +39,6 @@ class PluginDeclaration virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *, lay::LayoutViewBase *view) const; virtual bool implements_editable (std::string &title) const; virtual void get_options (std::vector < std::pair > &options) const; - virtual std::vector additional_editor_options_pages () const; }; } diff --git a/src/img/unit_tests/imgObject.cc b/src/img/unit_tests/imgObject.cc index bd4d5d7e96..30d1ca625f 100644 --- a/src/img/unit_tests/imgObject.cc +++ b/src/img/unit_tests/imgObject.cc @@ -328,3 +328,46 @@ TEST(3) EXPECT_EQ (image.mask (1, 2), false); } +// color interpolation +TEST(4) +{ + img::DataMapping::false_color_nodes_type nodes; + + nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000)))); + nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0xffffff)))); + nodes.push_back (std::make_pair (2.0, std::make_pair (tl::color_t (0x000000), tl::color_t (0xffffff)))); + + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.0)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.001)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.5)).to_string (), "#00ff00"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.999)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.0)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.001)).to_string (), "#ffffff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.5)).to_string (), "#808080"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.999)).to_string (), "#000000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 2.0)).to_string (), "#000000"); + + nodes.clear (); + + nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000)))); + nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0x0000ff)))); + nodes.push_back (std::make_pair (2.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x0000ff)))); + + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.0)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.5)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.999)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.0)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.001)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.5)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.999)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 2.0)).to_string (), "#0000ff"); + + nodes.clear (); + + nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x0000ff)))); + nodes.push_back (std::make_pair (0.275591, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x000000)))); + nodes.push_back (std::make_pair (0.643701, std::make_pair (tl::color_t (0xffffff), tl::color_t (0xff0000)))); + nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000)))); + + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.7)).to_string (), "#ff0000"); +} diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index 86850a4e2b..7ef712ab22 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -56,6 +56,7 @@ static const std::string cfg_show_hierarchy_panel ("show-hierarchy-panel"); static const std::string cfg_show_libraries_view ("show-libraries-view"); static const std::string cfg_show_bookmarks_view ("show-bookmarks-view"); static const std::string cfg_show_layer_panel ("show-layer-panel"); +static const std::string cfg_show_tool_options ("show-tool-options"); static const std::string cfg_window_state ("window-state"); static const std::string cfg_layout_file_watcher_enabled ("layout-file-watcher-enabled"); static const std::string cfg_window_geometry ("window-geometry"); diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index c56f483576..ffe99975bf 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -74,6 +74,7 @@ class MainPluginDeclaration options.push_back (std::pair (cfg_show_libraries_view, "true")); options.push_back (std::pair (cfg_show_bookmarks_view, "false")); options.push_back (std::pair (cfg_show_layer_panel, "true")); + options.push_back (std::pair (cfg_show_tool_options, "true")); options.push_back (std::pair (cfg_layout_file_watcher_enabled, "true")); options.push_back (std::pair (cfg_window_state, "")); options.push_back (std::pair (cfg_window_geometry, "")); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index f63026e95d..6a25826431 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -274,6 +274,34 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (action, SIGNAL (triggered ()), this, SLOT (clone ())); mp_tab_bar->addAction (action); + mp_view_stack = new ViewWidgetStack (mp_main_frame); + mp_view_stack->setObjectName (QString::fromUtf8 ("view_stack")); + vbl->addWidget (mp_view_stack); + + mp_navigator_dock_widget = new QDockWidget (QObject::tr ("Navigator"), this); + mp_navigator_dock_widget->setObjectName (QString::fromUtf8 ("navigator_dock_widget")); + mp_navigator = new Navigator (this); + mp_navigator_dock_widget->setWidget (mp_navigator); + mp_navigator_dock_widget->setFocusProxy (mp_navigator); + connect (mp_navigator_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_navigator_visible = true; + + mp_lp_dock_widget = new QDockWidget (QObject::tr ("Layers"), this); + mp_lp_dock_widget->setObjectName (QString::fromUtf8 ("lp_dock_widget")); + mp_lp_stack = new ControlWidgetStack (mp_lp_dock_widget, "lp_stack"); + mp_lp_dock_widget->setWidget (mp_lp_stack); + mp_lp_dock_widget->setFocusProxy (mp_lp_stack); + connect (mp_lp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_lp_visible = true; + + mp_layer_toolbox_dock_widget = new QDockWidget (QObject::tr ("Layer Toolbox"), this); + mp_layer_toolbox_dock_widget->setObjectName (QString::fromUtf8 ("lt_dock_widget")); + mp_layer_toolbox_stack = new ControlWidgetStack (mp_layer_toolbox_dock_widget, "layer_toolbox_stack", true); + mp_layer_toolbox_dock_widget->setWidget (mp_layer_toolbox_stack); + mp_layer_toolbox_dock_widget->setFocusProxy (mp_layer_toolbox_stack); + connect (mp_layer_toolbox_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_layer_toolbox_visible = true; + mp_hp_dock_widget = new QDockWidget (QObject::tr ("Cells"), this); mp_hp_dock_widget->setObjectName (QString::fromUtf8 ("hp_dock_widget")); mp_hp_stack = new ControlWidgetStack (mp_hp_dock_widget, "hp_stack"); @@ -290,15 +318,6 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_libs_visible = true; - mp_eo_dock_widget = new QDockWidget (QObject::tr ("Editor Options"), this); - mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); - mp_eo_dock_widget->setMinimumHeight (150); - mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); - mp_eo_dock_widget->setWidget (mp_eo_stack); - mp_eo_dock_widget->setFocusProxy (mp_eo_stack); - connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_eo_visible = true; - mp_bm_dock_widget = new QDockWidget (QObject::tr ("Bookmarks"), this); mp_bm_dock_widget->setObjectName (QString::fromUtf8 ("bookmarks_dock_widget")); mp_bm_stack = new ControlWidgetStack (mp_bm_dock_widget, "bookmarks_stack"); @@ -307,33 +326,14 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (mp_bm_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_bm_visible = true; - mp_view_stack = new ViewWidgetStack (mp_main_frame); - mp_view_stack->setObjectName (QString::fromUtf8 ("view_stack")); - vbl->addWidget (mp_view_stack); - - mp_layer_toolbox_dock_widget = new QDockWidget (QObject::tr ("Layer Toolbox"), this); - mp_layer_toolbox_dock_widget->setObjectName (QString::fromUtf8 ("lt_dock_widget")); - mp_layer_toolbox_stack = new ControlWidgetStack (mp_layer_toolbox_dock_widget, "layer_toolbox_stack", true); - mp_layer_toolbox_dock_widget->setWidget (mp_layer_toolbox_stack); - mp_layer_toolbox_dock_widget->setFocusProxy (mp_layer_toolbox_stack); - connect (mp_layer_toolbox_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_layer_toolbox_visible = true; - - mp_lp_dock_widget = new QDockWidget (QObject::tr ("Layers"), this); - mp_lp_dock_widget->setObjectName (QString::fromUtf8 ("lp_dock_widget")); - mp_lp_stack = new ControlWidgetStack (mp_lp_dock_widget, "lp_stack"); - mp_lp_dock_widget->setWidget (mp_lp_stack); - mp_lp_dock_widget->setFocusProxy (mp_lp_stack); - connect (mp_lp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_lp_visible = true; - - mp_navigator_dock_widget = new QDockWidget (QObject::tr ("Navigator"), this); - mp_navigator_dock_widget->setObjectName (QString::fromUtf8 ("navigator_dock_widget")); - mp_navigator = new Navigator (this); - mp_navigator_dock_widget->setWidget (mp_navigator); - mp_navigator_dock_widget->setFocusProxy (mp_navigator); - connect (mp_navigator_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_navigator_visible = true; + mp_eo_dock_widget = new QDockWidget (QObject::tr ("Tool Options"), this); + mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); + mp_eo_dock_widget->setMinimumHeight (150); + mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); + mp_eo_dock_widget->setWidget (mp_eo_stack); + mp_eo_dock_widget->setFocusProxy (mp_eo_stack); + connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_eo_visible = true; // Add dock widgets #if QT_VERSION >= 0x040500 @@ -567,7 +567,7 @@ MainWindow::technology_changed () } void -MainWindow::dock_widget_visibility_changed (bool visible) +MainWindow::dock_widget_visibility_changed (bool /*visible*/) { if (sender () == mp_lp_dock_widget) { dispatcher ()->config_set (cfg_show_layer_panel, tl::to_string (!mp_lp_dock_widget->isHidden ())); @@ -582,7 +582,7 @@ MainWindow::dock_widget_visibility_changed (bool visible) } else if (sender () == mp_layer_toolbox_dock_widget) { dispatcher ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); } else if (sender () == mp_eo_dock_widget) { - m_eo_visible = visible; + dispatcher ()->config_set (cfg_show_tool_options, tl::to_string (!mp_eo_dock_widget->isHidden ())); } } @@ -1133,6 +1133,17 @@ MainWindow::configure (const std::string &name, const std::string &value) return true; + } else if (name == cfg_show_tool_options) { + + tl::from_string (value, m_eo_visible); + if (m_eo_visible) { + mp_eo_dock_widget->show (); + } else { + mp_eo_dock_widget->hide (); + } + + return true; + } else if (name == cfg_navigator_show_images) { bool flag = false; @@ -1321,6 +1332,7 @@ MainWindow::read_dock_widget_state () dispatcher ()->config_set (cfg_show_bookmarks_view, tl::to_string (!mp_bm_dock_widget->isHidden ())); dispatcher ()->config_set (cfg_show_navigator, tl::to_string (!mp_navigator_dock_widget->isHidden ())); dispatcher ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); + dispatcher ()->config_set (cfg_show_tool_options, tl::to_string (!mp_eo_dock_widget->isHidden ())); } void @@ -1699,38 +1711,6 @@ MainWindow::select_mode (int m) } } - update_editor_options_dock (); - - } -} - -void -MainWindow::update_editor_options_dock () -{ - // if the current mode supports editing, show the editor options panel - - const lay::PluginDeclaration *pd_sel = 0; - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const lay::PluginDeclaration *pd = cls.operator-> (); - if (pd->id () == m_mode) { - pd_sel = pd; - } - } - - bool eo_visible = false; - if (mp_eo_stack && pd_sel) { - eo_visible = pd_sel->editable_enabled (); - } - if (current_view () && eo_visible) { - lay::EditorOptionsPageCollection *eo_pages = current_view ()->editor_options_pages (); - if (! eo_pages || ! eo_pages->has_content ()) { - eo_visible = false; - } - } - - if (eo_visible != m_eo_visible) { - m_eo_visible = eo_visible; - show_dock_widget (mp_eo_dock_widget, m_eo_visible); } } @@ -2499,7 +2479,6 @@ MainWindow::select_view (int index) current_view_changed (); - update_editor_options_dock (); clear_current_pos (); edits_enabled_changed (); clear_messages (); @@ -4502,7 +4481,6 @@ class MainWindowPluginDeclaration at = "edit_menu.end"; menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); - menu_entries.push_back (lay::menu_item ("cm_edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)")); at = "file_menu.end"; menu_entries.push_back (lay::menu_item ("cm_new_layout", "new_layout:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layout")))); @@ -4568,6 +4546,7 @@ class MainWindowPluginDeclaration menu_entries.push_back (lay::config_menu_item ("show_hierarchy_panel", at, tl::to_string (QObject::tr ("Cells")), cfg_show_hierarchy_panel, "?")); menu_entries.push_back (lay::config_menu_item ("show_libraries_view", at, tl::to_string (QObject::tr ("Libraries")), cfg_show_libraries_view, "?")); menu_entries.push_back (lay::config_menu_item ("show_bookmarks_view", at, tl::to_string (QObject::tr ("Bookmarks")), cfg_show_bookmarks_view, "?")); + menu_entries.push_back (lay::config_menu_item ("show_tool_options", at, tl::to_string (QObject::tr ("Tool Options(F3)")), cfg_show_tool_options, "?")); menu_entries.push_back (lay::menu_item ("cm_reset_window_state", "reset_window_state", at, tl::to_string (QObject::tr ("Restore Window")))), menu_entries.push_back (lay::separator ("selection_group", at)); menu_entries.push_back (lay::config_menu_item ("transient_selection", at, tl::to_string (QObject::tr ("Highlight Object Under Mouse")), cfg_sel_transient_mode, "?")); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index a22ac7bc9f..ade27bd248 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -859,7 +859,6 @@ protected slots: void interactive_close_view (int from, int to, bool invert_range, bool all_cellviews); void call_on_current_view (void (lay::LayoutView::*func) (), const std::string &op_desc); void current_view_changed (); - void update_editor_options_dock (); void update_window_title (); void update_tab_title (int i); void add_view (LayoutViewWidget *view); diff --git a/src/lay/lay/layNavigator.cc b/src/lay/lay/layNavigator.cc index 1de9b1a2ba..31f571560e 100644 --- a/src/lay/lay/layNavigator.cc +++ b/src/lay/lay/layNavigator.cc @@ -576,6 +576,7 @@ Navigator::showEvent (QShowEvent *) void Navigator::closeEvent (QCloseEvent *) { + // TODO: this is most likely never called mp_main_window->dispatcher ()->config_set (cfg_show_navigator, "false"); mp_main_window->dispatcher ()->config_end (); } diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc index 637f9ab8fd..3ab6d1c06f 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -1280,31 +1280,138 @@ AbstractMenu::extra_menu_name () void AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar) { - if (tbar) { - tbar->clear (); - } - - std::set > present_actions; + // Build the menu bar if (mbar) { + std::set > present_actions; + QList a = mbar->actions (); for (QList::const_iterator i = a.begin (); i != a.end (); ++i) { present_actions.insert (std::make_pair (id_from_action (*i), *i)); } - } + // NOTE: on MacOS, once the top level menu is created, we should not + // modify it and place menu items into the "Extras" menu instead. + bool menu_frozen = ! s_can_move_menu && ! present_actions.empty (); + + // NOTE: the "extras" menu is relevant on MacOS only + QMenu *extras_menu = find_extras_menu (mbar); + if (extras_menu) { + extras_menu->clear (); + } + + QAction *prev_action = 0; + + for (std::list::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) { + + if (c->has_submenu ()) { + + if (c->name ().find ("@") == 0) { + + // done later. + + } else { + + if (c->menu () == 0) { + + // NOTE: we intentionally do not make the item owner of the menu action + // as implicitly deleting it might cause trouble on MacOS. Instead we + // explicitly delete below or keep the action. + QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ()); + menu->setTitle (tl::to_qstring (c->action ()->get_title ())); + c->set_action (new Action (menu, false), true); + + // This case happens when we dynamically create menus. + // MacOS does not like generating top-level menus dynamically, so + // we put them into the "_extra" top level one. + if (menu_frozen) { + if (extras_menu) { + extras_menu->addMenu (menu); + } + } else { + prev_action = insert_action_after (mbar, prev_action, menu->menuAction ()); + } + + } else { + + // Move the action to the end if present in the menu already + std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ())); + if (a != present_actions.end ()) { + if (s_can_move_menu) { + mbar->removeAction (a->second); + insert_action_after (mbar, prev_action, a->second); + } + prev_action = a->second; + present_actions.erase (*a); + } else if (menu_frozen) { + if (extras_menu) { + extras_menu->addMenu (c->menu ()); + } + } else { + prev_action = insert_action_after (mbar, prev_action, c->menu ()->menuAction ()); + } + + } + + // NOTE: the "extras" menu is built implicitly. You cannot put anything there. + if (c->menu () != extras_menu) { + build (c->menu (), c->children); + } + + } + + } else { + + // Move the action to the end if present in the menu already + std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ()->qaction ()), c->action ()->qaction ())); + if (a != present_actions.end ()) { + if (s_can_move_menu) { + mbar->removeAction (a->second); + insert_action_after (mbar, prev_action, a->second); + } + prev_action = a->second; + present_actions.erase (*a); + } else if (menu_frozen) { + QMenu *extras_menu = find_extras_menu (mbar); + if (extras_menu) { + extras_menu->addAction (c->action ()->qaction ()); + } + } else { + prev_action = insert_action_after (mbar, prev_action, c->action ()->qaction ()); + } + + } + + } + + // Disable the (maybe new) "extras" menu if empty + extras_menu = find_extras_menu (mbar); + if (extras_menu) { + extras_menu->setEnabled (! extras_menu->isEmpty ()); + } - // NOTE: on MacOS, once the top level menu is created, we should not - // modify it and place menu items into the "Extras" menu instead. - bool menu_frozen = ! s_can_move_menu && ! present_actions.empty (); + // Remove all actions that have vanished + for (std::set >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) { + if (s_can_move_menu) { + mbar->removeAction (a->second); + delete a->second; + } else { + if (a->second->menu ()) { + a->second->menu ()->clear (); + } + a->second->setEnabled (false); + } + } - // NOTE: the "extras" menu is relevant on MacOS only - QMenu *extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->clear (); } - QAction *prev_action = 0; + // Finally build the context menus and toolbar. + // NOTE: this is done afterwards, as generating the submenus, specifically for the toolbar menu buttons + // messes with the QActions stored in "present_actions". + + if (tbar) { + tbar->clear (); + } for (std::list::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) { @@ -1338,100 +1445,11 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar) // prepare a detached menu which can be used as context menus build (c->menu (), c->children); - } else if (mbar) { - - if (c->menu () == 0) { - - // NOTE: we intentionally do not make the item owner of the menu action - // as implicitly deleting it might cause trouble on MacOS. Instead we - // explicitly delete below or keep the action. - QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ()); - menu->setTitle (tl::to_qstring (c->action ()->get_title ())); - c->set_action (new Action (menu, false), true); - - // This case happens when we dynamically create menus. - // MacOS does not like generating top-level menus dynamically, so - // we put them into the "_extra" top level one. - if (menu_frozen) { - if (extras_menu) { - extras_menu->addMenu (menu); - } - } else { - prev_action = insert_action_after (mbar, prev_action, menu->menuAction ()); - } - - } else { - - // Move the action to the end if present in the menu already - std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ())); - if (a != present_actions.end ()) { - if (s_can_move_menu) { - mbar->removeAction (a->second); - insert_action_after (mbar, prev_action, a->second); - } - prev_action = a->second; - present_actions.erase (*a); - } else if (menu_frozen) { - if (extras_menu) { - extras_menu->addMenu (c->menu ()); - } - } else { - prev_action = insert_action_after (mbar, prev_action, c->menu ()->menuAction ()); - } - - } - - // NOTE: the "extras" menu is built implicitly. You cannot put anything there. - if (c->menu () != extras_menu) { - build (c->menu (), c->children); - } - - } - - } else if (mbar) { - - // Move the action to the end if present in the menu already - std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ()->qaction ()), c->action ()->qaction ())); - if (a != present_actions.end ()) { - if (s_can_move_menu) { - mbar->removeAction (a->second); - insert_action_after (mbar, prev_action, a->second); - } - prev_action = a->second; - present_actions.erase (*a); - } else if (menu_frozen) { - QMenu *extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->addAction (c->action ()->qaction ()); - } - } else { - prev_action = insert_action_after (mbar, prev_action, c->action ()->qaction ()); } } } - - // Disable the (maybe new) "extras" menu if empty - extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->setEnabled (! extras_menu->isEmpty ()); - } - - // Remove all actions that have vanished - if (mbar) { - for (std::set >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) { - if (s_can_move_menu) { - mbar->removeAction (a->second); - delete a->second; - } else { - if (a->second->menu ()) { - a->second->menu ()->clear (); - } - a->second->setEnabled (false); - } - } - } } void diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index b270ab5858..f5eb6489a5 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -482,11 +482,15 @@ class MoveServiceDeclaration return new MoveService (view); } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *view) const { std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); + if (view->is_editable ()) { + // TODO: provide in a central place instead of borrowing from the edt module + names.push_back ("GenericEditorOptions"); + } + // TODO: provide in a central place instead of borrowing from the ant module + names.push_back ("ant::RulerOptions"); return names; } }; diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index 8b8812bed7..c2a1407d2d 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -354,7 +354,7 @@ Q_OBJECT * In addition to providing pages through "get_editor_options_pages", the plugin can request pages * from globally registered factories by name. */ - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const { return std::vector (); } diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc index 241b9f4968..287dab404a 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -333,11 +333,13 @@ class SelectionServiceDeclaration return new SelectionService (view); } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *view) const { std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); + if (view->is_editable ()) { + // TODO: provide in a central place instead of borrowing from the edt module + names.push_back ("GenericEditorOptions"); + } return names; } }; diff --git a/src/laybasic/laybasic/laySnap.cc b/src/laybasic/laybasic/laySnap.cc index 7088b5fe98..e53b257942 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -554,7 +554,7 @@ class ContourFinder } } - if (! any_point) { + if (! any_point && ! m_projection_constraint) { // no certain direction to look into: // compute the projection of the point and if within a diff --git a/src/layview/layview/gsiDeclLayPluginFactory.cc b/src/layview/layview/gsiDeclLayPluginFactory.cc index ce4f81afd0..bc72cd6565 100644 --- a/src/layview/layview/gsiDeclLayPluginFactory.cc +++ b/src/layview/layview/gsiDeclLayPluginFactory.cc @@ -318,7 +318,7 @@ class PluginFactoryBase m_additional_editor_options_pages.push_back (name); } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *) const { return m_additional_editor_options_pages; } diff --git a/src/layview/layview/layEditorOptionsFrame.cc b/src/layview/layview/layEditorOptionsFrame.cc index 2d6fab3912..ab527000a1 100644 --- a/src/layview/layview/layEditorOptionsFrame.cc +++ b/src/layview/layview/layEditorOptionsFrame.cc @@ -56,7 +56,7 @@ EditorOptionsFrame::populate (LayoutViewBase *view) for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { cls->get_editor_options_pages (editor_options_pages, view, view->dispatcher ()); - std::vector ap = cls->additional_editor_options_pages (); + std::vector ap = cls->additional_editor_options_pages (view); for (auto i = ap.begin (); i != ap.end (); ++i) { additional_pages [*i].push_back (cls.operator-> ()); } diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index 6e3dd2d6a9..3bc397f9be 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -482,7 +482,7 @@ EditorOptionsModalPages::update_title () if (mp_single_page) { setWindowTitle (tl::to_qstring (mp_single_page->title ())); } else { - setWindowTitle (tr ("Editor Options")); + setWindowTitle (tr ("Tool Options")); } } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 712ea2bd81..ce62968e29 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -582,7 +582,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // don't write ghost cells unless they are not empty (any more) // also don't write proxy cells which are not employed - if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) { + if (! cref.is_real_ghost_cell ()) { try { bool skip_body = options.write_context_info () && cref.can_skip_replica (); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 93e0cafd09..bebf4d1752 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1437,12 +1437,6 @@ OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vec end_table (layernames_table_pos); } -static bool must_write_cell (const db::Cell &cref) -{ - // Don't write proxy cells which are not employed - return ! cref.is_proxy () || ! cref.is_top (); -} - void OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set &cell_set) { @@ -1511,13 +1505,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save cells_by_index.reserve (cell_set.size ()); for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) { - if (cell_set.find (*cell) != cell_set.end () && must_write_cell (layout.cell (*cell))) { + if (cell_set.find (*cell) != cell_set.end ()) { cells.push_back (*cell); } } for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) { - if (cell_set.find (cell->cell_index ()) != cell_set.end () && must_write_cell (layout.cell (cell->cell_index ()))) { + if (cell_set.find (cell->cell_index ()) != cell_set.end ()) { cells_by_index.push_back (cell->cell_index ()); } } diff --git a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui index ded9b81ce8..3b1bcc021f 100644 --- a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui +++ b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui @@ -57,31 +57,47 @@ - - - - Pixel size - - - - - + + 0 0 + + QComboBox::AdjustToContents + + + + Periodic (repeat region seamlessly) + + + + + Use zero density outside + + + + + Use 100% density outside + + + + + Use average density outside + + - - + + - µm + Boundary mode - + @@ -101,13 +117,6 @@ - - - - Threads - - - @@ -128,15 +137,46 @@ + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Threads + + + + + + + Pixel size + + + - + - Boundary mode + Averaging mode - + 0 @@ -144,26 +184,16 @@ - QComboBox::AdjustToContents + QComboBox::AdjustToContentsOnFirstShow - Periodic (repeat region seamlessly) + Square hat - Use zero density outside - - - - - Use 100% density outside - - - - - Use average density outside + Gaussian hat (σ = window size / 2) @@ -552,6 +582,8 @@ le_pixel_size + le_window_size + cb_boundary_mode sb_threads layer_cb cb_source_layer diff --git a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc index b6e280797b..baba18a87d 100644 --- a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc +++ b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc @@ -66,10 +66,17 @@ static const std::string boundary_mode_average ("boundary-mode-average"); // items in boundary_mode_cb static const std::vector boundary_modes = { boundary_mode_periodic, boundary_mode_zero, boundary_mode_one, boundary_mode_average }; +static const std::string averaging_mode_square ("averaging-mode-square"); +static const std::string averaging_mode_gaussian ("averaging-mode-gaussian"); + +// items in average_mode_cb +static const std::vector averaging_modes = { averaging_mode_square, averaging_mode_gaussian }; + static const std::string cfg_density_map_region_mode ("density-map-region-mode"); static const std::string cfg_density_map_layer_mode ("density-map-layer-mode"); static const std::string cfg_density_map_pixel_size ("density-map-pixel-size"); static const std::string cfg_density_map_window_size ("density-map-window-size"); +static const std::string cfg_density_map_averaging_mode ("density-map-averaging-mode"); static const std::string cfg_density_map_boundary_mode ("density-map-boundary-mode"); static const std::string cfg_density_map_threads ("density-map-threads"); static const std::string cfg_density_map_source_layer ("density-map-source-layer"); @@ -90,6 +97,7 @@ class DensityMapDialogPluginDeclaration options.push_back (std::make_pair (cfg_density_map_pixel_size, "100")); options.push_back (std::make_pair (cfg_density_map_window_size, "0")); options.push_back (std::make_pair (cfg_density_map_boundary_mode, boundary_mode_periodic)); + options.push_back (std::make_pair (cfg_density_map_averaging_mode, averaging_mode_square)); options.push_back (std::make_pair (cfg_density_map_threads, "1")); } @@ -307,6 +315,18 @@ DensityMapDialog::configure (const std::string &name, const std::string &value) cb_boundary_mode->setCurrentIndex (mode); return true; + } else if (name == cfg_density_map_averaging_mode) { + + int mode = 0; + for (size_t i = 0; i < averaging_modes.size (); ++i) { + if (averaging_modes[i] == value) { + mode = int (i); + } + } + + cb_averaging_mode->setCurrentIndex (mode); + return true; + } else if (name == cfg_density_map_threads) { int thr = 1; @@ -390,6 +410,12 @@ DensityMapDialog::make_density_map () } par.boundary_mode = boundary_modes [boundary_mode]; + int averaging_mode = cb_averaging_mode->currentIndex (); + if (averaging_mode < 0 || averaging_mode >= int (averaging_modes.size ())) { + return; + } + par.averaging_mode = averaging_modes [averaging_mode]; + int region_mode = region_cb->currentIndex (); if (region_mode < 0 || region_mode >= int (region_modes.size ())) { return; @@ -514,6 +540,7 @@ DensityMapDialog::make_density_map () dispatcher ()->config_set (cfg_density_map_layer_mode, lm); dispatcher ()->config_set (cfg_density_map_region_mode, rm); dispatcher ()->config_set (cfg_density_map_boundary_mode, par.boundary_mode); + dispatcher ()->config_set (cfg_density_map_averaging_mode, par.averaging_mode); dispatcher ()->config_set (cfg_density_map_threads, tl::to_string (par.threads)); dispatcher ()->config_set (cfg_density_map_pixel_size, tl::to_string (par.pixel_size)); dispatcher ()->config_set (cfg_density_map_window_size, tl::to_string (par.window_size)); @@ -644,7 +671,39 @@ DensityMapDialog::compute_density_map (const DensityMapParameters &par) unsigned int nw = (unsigned int) (std::max (0.0, std::min (1000.0, floor (par.window_size / par.pixel_size + 0.5)))); if (nw > 1) { - average_window (*img_object, par.boundary_mode, nw); + + if (par.averaging_mode == averaging_mode_square) { + + std::vector weights; + + int wh = nw / 2; + weights.resize (wh * 2 + 1, 1.0); + if (nw % 2 == 0) { + weights.front () = weights.back () = 0.5; + } + + average_window (*img_object, par.boundary_mode, wh, weights); + + } else if (par.averaging_mode == averaging_mode_gaussian) { + + std::vector weights; + + int wh = nw * 3 / 2; + + // Note: the averaging kernel (f = exp(-r^2) = exp(-(x^2+y^2)) can be decomposed + // into f = f(x)*f(y); f(x) = exp(-x^2); f(y) = exp(-y^2). + // Hence we can use a 1d-kernel here and average vertically first, then horizontally. + + weights.reserve (wh * 2 + 1); + for (int i = -wh; i <= wh; ++i) { + double x = double (i) / (nw * 0.5); + weights.push_back (exp (-x * x)); + } + + average_window (*img_object, par.boundary_mode, wh, weights); + + } + } } @@ -658,8 +717,15 @@ inline int safe_mod (int a, int b) } void -DensityMapDialog::average_window (img::Object &img_object, const std::string boundary_mode, int nw) +DensityMapDialog::average_window (img::Object &img_object, const std::string boundary_mode, int wh, const std::vector &weights) { + tl_assert (weights.size () == size_t (wh * 2 + 1)); + + double ws = 0.0; + for (auto i = weights.begin (); i != weights.end (); ++i) { + ws += *i; + } + bool periodic = (boundary_mode == boundary_mode_periodic); int nx = img_object.width (); @@ -673,7 +739,6 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou } else if (boundary_mode == boundary_mode_average) { - double outside = 0.0; const float *d = img_object.float_data (); for (size_t i = size_t (nx) * size_t (ny); i > 0; --i) { outside += *d++; @@ -689,13 +754,10 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou for (int y = 0; y < ny; ++y) { - int wh = nw / 2; - double fb = (nw % 2 == 0) ? 0.5 : 1.0; - for (int dy = -wh; dy <= wh; ++dy) { // top and bottom row count half in case of even nw - double f = (dy == -wh || dy == wh) ? fb : 1.0; + double f = weights [dy + wh]; std::vector::iterator d = vavg_data.begin () + y * nx; if (periodic || (y + dy >= 0 && y + dy < ny)) { @@ -715,7 +777,7 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou // horizontal sum - outside *= nw; // because we do normalization later + outside *= ws; // because we do normalization later // TODO: transposing the image would make things more efficient @@ -724,13 +786,10 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou for (int x = 0; x < nx; ++x) { - int wh = nw / 2; - double fb = (nw % 2 == 0) ? 0.5 : 1.0; - for (int dx = -wh; dx <= wh; ++dx) { // top and bottom row count half in case of even nw - double f = (dx == -wh || dx == wh) ? fb : 1.0; + double f = weights [dx + wh]; std::vector::iterator d = havg_data.begin () + x; @@ -753,7 +812,7 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou } // take the average - double s = 1.0 / (double (nw) * double (nw)); + double s = 1.0 / (ws * ws); for (auto i = havg_data.begin (); i != havg_data.end (); ++i) { *i *= s; } diff --git a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h index bd99bbbe8a..ebaa4752ff 100644 --- a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h +++ b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h @@ -76,6 +76,9 @@ public slots: // The boundary mode std::string boundary_mode; + // The averaging mode + std::string averaging_mode; + // The number of threads to use int threads; }; @@ -88,7 +91,7 @@ public slots: void make_density_map (); void compute_density_map (const DensityMapParameters &par); - void average_window (img::Object &img_object, const std::string boundary_mode, int nw); + void average_window (img::Object &img_object, const std::string boundary_mode, int wh, const std::vector &weights); }; diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 3f94bc341c..1592a72603 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -73,6 +73,15 @@ class Box: @brief Sets the top coordinate of the box """ @classmethod + def from_bytes(cls, s: bytes) -> Box: + r""" + @brief Creates a box object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dbox(cls, dbox: DBox) -> Box: r""" @brief Creates an integer coordinate box from a floating-point coordinate box @@ -728,6 +737,15 @@ class Box: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this box + + This string can be turned into a box again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DBox: r""" @brief Converts the box to a floating-point coordinate box @@ -809,6 +827,24 @@ class BoxWithProperties(Box): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> BoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> BoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, box: Box, properties: Dict[Any, Any]) -> BoxWithProperties: @@ -844,7 +880,7 @@ class BoxWithProperties(Box): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> BoxWithProperties: @@ -856,7 +892,7 @@ class BoxWithProperties(Box): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Box) -> None: @@ -1010,9 +1046,18 @@ class BoxWithProperties(Box): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -2183,6 +2228,23 @@ class Cell: It has been added in version 0.16. """ ... + def is_cold_proxy(self) -> bool: + r""" + @brief Returns true, if the cell is a 'cold proxy' + Cold proxies are cells that refer to a library cell or PCell variant, but can temporarily not be resolved - + for example, because the library is not installed. Such cells are basically placeholders + for library references and also carry PCell parameter information needed to establish + the link to the library PCell, once the library is available again. + + You can use \library_name to obtain the name of the library the proxy points to, \library_cell_name to obtain the cell name in that library, \pcell_name to obtain the PCell name if it is a PCell proxy, and \pcell_parameter or \pcell_parameters_by_name to obtain the PCell parameters. + + Cold proxies cannot be created or modified. Cold proxies are basically error indicators and should be fixed by installing the respective library. Their layout state + reflects the last version of the layout when the cell was functional and properly + linked to a library. Still, they can be used in read-only applications. + + This method has been introduced in version 0.30.9. + """ + ... def is_const_object(self) -> bool: r""" @brief Returns a value indicating whether the reference is a const reference @@ -2271,7 +2333,7 @@ class Cell: ... def is_proxy(self) -> bool: r""" - @brief Returns true, if the cell presents some external entity + @brief Returns true, if the cell presents some external entity A cell may represent some data which is imported from some other source, i.e. a library. Such cells are called "proxy cells". For a library reference, the proxy cell is some kind of pointer to the library and the cell within the library. @@ -2279,8 +2341,9 @@ class Cell: For PCells, this data can even be computed through some script. A PCell proxy represents all instances with a given set of parameters. - Proxy cells cannot be modified, except that pcell parameters can be modified - and PCell instances can be recomputed. + Proxy cells should not be modified directly - i.e. the shapes or instances should not + be touched. However, you can change PCell parameters (\change_pcell_parameter, \change_pcell_parameters) + or change the library reference (\change_ref). This method has been introduced in version 0.22. """ @@ -2318,7 +2381,7 @@ class Cell: def library(self) -> LibraryBase: r""" @brief Returns a reference to the library from which the cell is imported - if the cell is not imported from a library, this reference is nil. + If the cell is not imported from a library, this reference is nil. This method has been introduced in version 0.22. """ @@ -2340,6 +2403,31 @@ class Cell: This method has been introduced in version 0.22. """ ... + def library_cell_name(self) -> str: + r""" + @brief Returns the cell name inside the library from which the cell is imported + If the cell is not imported from a library, the return value is an empty string. + This method is basically a convenience function, equivalent to taking the name + from \library and \library_cell_index. + Note that for PCells, 'library_cell_name' is the name of the PCell proxy cell inside the library, not the name of the PCell. + + However, this method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the name of cell inside the (missing) library. + + This method has been introduced in version 0.30.8. + """ + ... + def library_name(self) -> str: + r""" + @brief Returns the name of the library from which the cell is imported + If the cell is not imported from a library, the return value is an empty string. + This method is basically a convenience function, equivalent to taking the name + from \library. However, this method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the name of the (missing) library. + + This method has been introduced in version 0.30.8. + """ + ... def merge_meta_info(self, other: Cell) -> None: r""" @brief Merges the meta information from the other cell into this cell @@ -2541,6 +2629,17 @@ class Cell: This method has been introduced in version 0.22. """ ... + def pcell_name(self) -> str: + r""" + @brief Returns the PCell name if the cell is a PCell variant + If this cell is not a PCell variant, this method returns an empty string. + This method is basically a convenience function, equivalent to taking the name + from \pcell_declaration. However, this method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the name of the (missing) PCell. + + This method has been introduced in version 0.30.9. + """ + ... @overload def pcell_parameter(self, instance: Instance, name: str) -> Any: r""" @@ -2560,6 +2659,9 @@ class Cell: If the cell is a PCell variant, this method returns the parameter with the given name. If the cell is not a PCell variant or the name is not a valid PCell parameter name, the return value is nil. + This method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the value of the given stored PCell parameter. + This method has been introduced in version 0.25. """ ... @@ -2595,6 +2697,9 @@ class Cell: method returns an empty dictionary. This method also returns the PCell parameters if the cell is a PCell imported from a library. + This method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the names and values of the stored PCell parameters. + This method has been introduced in version 0.24. """ ... @@ -6314,6 +6419,15 @@ class CplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> CplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dtrans(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> CplxTrans: r""" @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour @@ -7253,6 +7367,15 @@ class CplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> ICplxTrans: r""" @brief Converts the transformation to another transformation with integer input and output coordinates @@ -7556,6 +7679,15 @@ class DBox: @brief Sets the top coordinate of the box """ @classmethod + def from_bytes(cls, s: bytes) -> DBox: + r""" + @brief Creates a box object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ibox(cls, box: Box) -> DBox: r""" @brief Creates a floating-point coordinate box from an integer coordinate box @@ -8211,6 +8343,15 @@ class DBox: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this box + + This string can be turned into a box again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Box: r""" @brief Converts the box to an integer coordinate box @@ -8292,6 +8433,24 @@ class DBoxWithProperties(DBox): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DBoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DBoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, box: DBox, properties: Dict[Any, Any]) -> DBoxWithProperties: @@ -8327,7 +8486,7 @@ class DBoxWithProperties(DBox): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DBoxWithProperties: @@ -8339,7 +8498,7 @@ class DBoxWithProperties(DBox): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DBox) -> None: @@ -8493,9 +8652,18 @@ class DBoxWithProperties(DBox): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -9275,6 +9443,15 @@ class DCplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> DCplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_itrans(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour @@ -10203,6 +10380,15 @@ class DCplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> ICplxTrans: r""" @brief Converts the transformation to another transformation with integer input and output coordinates @@ -10506,6 +10692,15 @@ class DEdge: This method has been added in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> DEdge: + r""" + @brief Creates an edge object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_iedge(cls, edge: Edge) -> DEdge: r""" @brief Creates a floating-point coordinate edge from an integer coordinate edge @@ -11210,6 +11405,15 @@ class DEdge: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge + + This string can be turned into an edge again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Edge: r""" @brief Converts the edge to an integer coordinate edge @@ -11318,6 +11522,15 @@ class DEdgePair: Symmetric edge pairs have been introduced in version 0.27. """ @classmethod + def from_bytes(cls, s: bytes) -> DEdgePair: + r""" + @brief Creates an edge pair object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> DEdgePair: r""" @brief Creates an object from a string @@ -11613,6 +11826,15 @@ class DEdgePair: @param e The enlargement (set to zero for exact representation) """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge pair + + This string can be turned into an edge pair again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> EdgePair: r""" @brief Converts the edge pair to an integer coordinate edge pair @@ -11685,6 +11907,24 @@ class DEdgePairWithProperties(DEdgePair): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DEdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DEdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge_pair: DEdgePair, properties: Dict[Any, Any]) -> DEdgePairWithProperties: @@ -11720,7 +11960,7 @@ class DEdgePairWithProperties(DEdgePair): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DEdgePairWithProperties: @@ -11732,7 +11972,7 @@ class DEdgePairWithProperties(DEdgePair): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DEdgePair) -> None: @@ -11886,9 +12126,18 @@ class DEdgePairWithProperties(DEdgePair): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -11944,6 +12193,24 @@ class DEdgeWithProperties(DEdge): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DEdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DEdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge: DEdge, properties: Dict[Any, Any]) -> DEdgeWithProperties: @@ -11979,7 +12246,7 @@ class DEdgeWithProperties(DEdge): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DEdgeWithProperties: @@ -11991,7 +12258,7 @@ class DEdgeWithProperties(DEdge): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DEdge) -> None: @@ -12145,9 +12412,18 @@ class DEdgeWithProperties(DEdge): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -12240,6 +12516,15 @@ class DPath: @brief Set the width """ @classmethod + def from_bytes(cls, s: bytes) -> DPath: + r""" + @brief Creates a path object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipath(cls, path: Path) -> DPath: r""" @brief Creates a floating-point coordinate path from an integer coordinate path @@ -12683,6 +12968,15 @@ class DPath: The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps itself or contains very short segments. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this path + + This string can be turned into a path again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Path: r""" @brief Converts the path to an integer coordinate path @@ -12764,6 +13058,24 @@ class DPathWithProperties(DPath): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DPathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DPathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, path: DPath, properties: Dict[Any, Any]) -> DPathWithProperties: @@ -12799,7 +13111,7 @@ class DPathWithProperties(DPath): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPathWithProperties: @@ -12811,7 +13123,7 @@ class DPathWithProperties(DPath): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DPath) -> None: @@ -12965,9 +13277,18 @@ class DPathWithProperties(DPath): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -13032,6 +13353,15 @@ class DPoint: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> DPoint: + r""" + @brief Creates a point object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipoint(cls, point: Point) -> DPoint: r""" @brief Creates a floating-point coordinate point from an integer coordinate point @@ -13442,6 +13772,15 @@ class DPoint: @param d The other point to compute the distance to. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this point + + This string can be turned into a point again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Point: r""" @brief Converts the point to an integer coordinate point @@ -13529,6 +13868,15 @@ class DPolygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> DPolygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipoly(cls, polygon: Polygon) -> DPolygon: r""" @brief Creates a floating-point coordinate polygon from an integer coordinate polygon @@ -14277,6 +14625,15 @@ class DPolygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Polygon: r""" @brief Converts the polygon to an integer coordinate polygon @@ -14424,6 +14781,24 @@ class DPolygonWithProperties(DPolygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DPolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DPolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: DPolygon, properties: Dict[Any, Any]) -> DPolygonWithProperties: @@ -14459,7 +14834,7 @@ class DPolygonWithProperties(DPolygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPolygonWithProperties: @@ -14471,7 +14846,7 @@ class DPolygonWithProperties(DPolygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DPolygon) -> None: @@ -14625,9 +15000,18 @@ class DPolygonWithProperties(DPolygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -14709,6 +15093,15 @@ class DSimplePolygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> DSimplePolygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipoly(cls, polygon: SimplePolygon) -> DSimplePolygon: r""" @brief Creates a floating-point coordinate polygon from an integer coordinate polygon @@ -15242,6 +15635,15 @@ class DSimplePolygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> SimplePolygon: r""" @brief Converts the polygon to an integer coordinate polygon @@ -15387,6 +15789,24 @@ class DSimplePolygonWithProperties(DSimplePolygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DSimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DSimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: DSimplePolygon, properties: Dict[Any, Any]) -> DSimplePolygonWithProperties: @@ -15422,7 +15842,7 @@ class DSimplePolygonWithProperties(DSimplePolygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DSimplePolygonWithProperties: @@ -15434,7 +15854,7 @@ class DSimplePolygonWithProperties(DSimplePolygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DSimplePolygon) -> None: @@ -15588,9 +16008,18 @@ class DSimplePolygonWithProperties(DSimplePolygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -15759,6 +16188,15 @@ class DText: This method has been introduced in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> DText: + r""" + @brief Creates a text object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> DText: r""" @brief Creates an object from a string @@ -16099,6 +16537,15 @@ class DText: This convenience method has been added in version 0.28. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this text object + + This string can be turned into a text object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Text: r""" @brief Converts the text to an integer coordinate text @@ -16165,6 +16612,24 @@ class DTextWithProperties(DText): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DTextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DTextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, text: DText, properties: Dict[Any, Any]) -> DTextWithProperties: @@ -16200,7 +16665,7 @@ class DTextWithProperties(DText): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DTextWithProperties: @@ -16212,7 +16677,7 @@ class DTextWithProperties(DText): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DText) -> None: @@ -16366,9 +16831,18 @@ class DTextWithProperties(DText): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -16522,6 +16996,15 @@ class DTrans: This method was introduced in version 0.20. """ @classmethod + def from_bytes(cls, s: bytes) -> DTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_itrans(cls, trans: Trans) -> DTrans: r""" @brief Creates a floating-point coordinate transformation from an integer coordinate transformation @@ -17367,6 +17850,15 @@ class DTrans: If this property is true, the transformation is composed of a mirroring at the x-axis followed by a rotation by the angle given by the \angle property. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Trans: r""" @brief Converts the transformation to an integer coordinate transformation @@ -17623,6 +18115,15 @@ class DVector: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> DVector: + r""" + @brief Creates a vector object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> DVector: r""" @brief Creates an object from a string @@ -17977,6 +18478,15 @@ class DVector: 'sq_abs' is an alias provided for compatibility with the former point type. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this vector + + This string can be turned into a vector again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Vector: r""" @brief Converts the point to an integer coordinate point @@ -22058,6 +22568,15 @@ class Edge: This method has been added in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> Edge: + r""" + @brief Creates an edge object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dedge(cls, dedge: DEdge) -> Edge: r""" @brief Creates an integer coordinate edge from a floating-point coordinate edge @@ -22762,6 +23281,15 @@ class Edge: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge + + This string can be turned into an edge again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DEdge: r""" @brief Converts the edge to a floating-point coordinate edge @@ -23860,6 +24388,15 @@ class EdgePair: Symmetric edge pairs have been introduced in version 0.27. """ @classmethod + def from_bytes(cls, s: bytes) -> EdgePair: + r""" + @brief Creates an edge pair object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> EdgePair: r""" @brief Creates an object from a string @@ -24155,6 +24692,15 @@ class EdgePair: @param e The enlargement (set to zero for exact representation) """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge pair + + This string can be turned into an edge pair again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DEdgePair: r""" @brief Converts the edge pair to a floating-point coordinate edge pair @@ -25421,6 +25967,24 @@ class EdgePairWithProperties(EdgePair): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> EdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> EdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge_pair: EdgePair, properties: Dict[Any, Any]) -> EdgePairWithProperties: @@ -25456,7 +26020,7 @@ class EdgePairWithProperties(EdgePair): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> EdgePairWithProperties: @@ -25468,7 +26032,7 @@ class EdgePairWithProperties(EdgePair): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: EdgePair) -> None: @@ -25622,9 +26186,18 @@ class EdgePairWithProperties(EdgePair): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -29222,6 +29795,24 @@ class EdgeWithProperties(Edge): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> EdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> EdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge: Edge, properties: Dict[Any, Any]) -> EdgeWithProperties: @@ -29257,7 +29848,7 @@ class EdgeWithProperties(Edge): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DEdgeWithProperties: @@ -29269,7 +29860,7 @@ class EdgeWithProperties(Edge): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Edge) -> None: @@ -29423,9 +30014,18 @@ class EdgeWithProperties(Edge): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -33467,6 +34067,15 @@ class ICplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> ICplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dtrans(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" @brief Creates an integer coordinate transformation from another coordinate flavour @@ -34412,6 +35021,15 @@ class ICplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Converts the transformation to another transformation with floating-point input and output coordinates @@ -35882,11 +36500,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'a' axis + @brief Sets the displacement vector for the 'a' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ b: Vector r""" @@ -35895,11 +36513,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'b' axis + @brief Sets the displacement vector for the 'b' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \b= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ cell: Cell r""" @@ -35942,10 +36560,9 @@ class Instance: @brief Gets the complex transformation of the instance or the first instance in the array This method is always valid compared to \trans, since simple transformations can be expressed as complex transformations as well. Setter: - @brief Sets the complex transformation of the instance or the first instance in the array (in micrometer units) - This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. + @brief Sets the complex transformation of the instance or the first instance in the array - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ da: DVector r""" @@ -36440,7 +37057,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ ... @overload @@ -36448,7 +37065,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ ... def pcell_declaration(self) -> PCellDeclaration_Native: @@ -39833,6 +40450,24 @@ class Layout: This method has been introduce in version 0.24. """ ... + @overload + def delete_cell(self, cell: Cell) -> None: + r""" + @brief Deletes a cell + + This deletes a cell but not the sub cells of the cell. + These subcells will likely become new top cells unless they are used + otherwise. + All instances of this cell are deleted as well. + Hint: to delete multiple cells, use "delete_cells" which is + far more efficient in this case. + + @param cell The cell to delete + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload def delete_cell(self, cell_index: int) -> None: r""" @brief Deletes a cell @@ -39849,6 +40484,20 @@ class Layout: This method has been introduced in version 0.20. """ ... + @overload + def delete_cell_rec(self, cell: Cell) -> None: + r""" + @brief Deletes a cell plus all subcells + + This deletes a cell and also all sub cells of the cell. + In contrast to \prune_cell, all cells are deleted together with their instances even if they are used otherwise. + + @param cell The cell to delete + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload def delete_cell_rec(self, cell_index: int) -> None: r""" @brief Deletes a cell plus all subcells @@ -39861,6 +40510,7 @@ class Layout: This method has been introduced in version 0.20. """ ... + @overload def delete_cells(self, cell_index_list: Sequence[int]) -> None: r""" @brief Deletes multiple cells @@ -39876,6 +40526,21 @@ class Layout: """ ... @overload + def delete_cells(self, cell_list: Sequence[Cell]) -> None: + r""" + @brief Deletes multiple cells + + This deletes the cells but not the sub cells of these cells. + These subcells will likely become new top cells unless they are used + otherwise. + All instances of these cells are deleted as well. + + @param cell_list An list of cells to delete + + This convenience variant taking a list of cell objects has been introduced in version 0.30.9. + """ + ... + @overload def delete_layer(self, layer: LayerInfo) -> None: r""" @brief Deletes a layer @@ -40046,7 +40711,24 @@ class Layout: This method has been introduced in version 0.23 and has been extended to name queries in version 0.28.11. """ ... - def flatten(self, cell_index: int, levels: int, prune: bool) -> None: + @overload + def flatten(self, cell: Cell, levels: Optional[int] = ..., prune: Optional[bool] = ...) -> None: + r""" + @brief Flattens the given cell + + This method propagates all shapes and instances from the specified number of hierarchy levels below into the given cell. + It also removes the instances of the cells from which the shapes came from, but does not remove the cells themselves if prune is set to false. + If prune is set to true, these cells are removed if not used otherwise. + + @param cell The cell which should be flattened + @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) + @param prune Set to true to remove orphan cells. + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload + def flatten(self, cell_index: int, levels: Optional[int] = ..., prune: Optional[bool] = ...) -> None: r""" @brief Flattens the given cell @@ -40058,10 +40740,28 @@ class Layout: @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) @param prune Set to true to remove orphan cells. - This method has been introduced in version 0.20. + This method has been introduced in version 0.20. The 'levels' and 'prune' arguments have been made optional in version 0.30.9. + """ + ... + @overload + def flatten_into(self, source_cell: Cell, target_cell: Cell, trans: Optional[ICplxTrans] = ..., levels: Optional[int] = ...) -> None: + r""" + @brief Flattens the given cell into another cell + + This method works like 'flatten', but allows specification of a target cell which can be different from the source cell plus a transformation which is applied for all shapes and instances in the target cell. + + In contrast to the 'flatten' method, the source cell is not modified. + + @param source_cell The source cell which should be flattened + @param target_cell The target cell into which the resulting objects are written + @param trans The transformation to apply on the output shapes and instances + @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) + + This convenience variant taking a cell objects has been introduced in version 0.30.9. """ ... - def flatten_into(self, source_cell_index: int, target_cell_index: int, trans: ICplxTrans, levels: int) -> None: + @overload + def flatten_into(self, source_cell_index: int, target_cell_index: int, trans: Optional[ICplxTrans] = ..., levels: Optional[int] = ...) -> None: r""" @brief Flattens the given cell into another cell @@ -40074,7 +40774,7 @@ class Layout: @param trans The transformation to apply on the output shapes and instances @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) - This method has been introduced in version 0.24. + This method has been introduced in version 0.24. The 'trans' and 'levels' arguments have been made optional is version 0.30.9. """ ... def get_info(self, index: int) -> LayerInfo: @@ -40598,7 +41298,8 @@ class Layout: This method has been introduced in version 0.24. """ ... - def prune_cell(self, cell_index: int, levels: int) -> None: + @overload + def prune_cell(self, cell: Cell, levels: Optional[int] = ...) -> None: r""" @brief Deletes a cell plus subcells not used otherwise @@ -40606,13 +41307,81 @@ class Layout: The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. All instances of this cell are deleted as well. + A version that allows pruning multiple cells in one call is \prune_cells. + + @param cell The cell to delete + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload + def prune_cell(self, cell_index: int, levels: Optional[int] = ...) -> None: + r""" + @brief Deletes a cell plus subcells not used otherwise + + This deletes a cell and also all sub cells of the cell which are not used otherwise. + The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. + All instances of this cell are deleted as well. + + A version that allows pruning multiple cells in one call is \prune_cells. + @param cell_index The index of the cell to delete @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) - This method has been introduced in version 0.20. + This method has been introduced in version 0.20. The 'levels' argument was made optional in version 0.30.9. + """ + ... + @overload + def prune_cells(self, cell_indexes: Sequence[int], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes cells plus subcells not used otherwise + + This deletes the given cells and also all sub cells of the cells which are not used otherwise. + The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. + All instances of the pruned cells are deleted as well. + + @param cell_indexes The indexes of the cells to delete + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload + def prune_cells(self, cells: Sequence[Cell], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes cells plus subcells not used otherwise + + This deletes the given cells and also all sub cells of the cells which are not used otherwise. + The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. + All instances of the pruned cells are deleted as well. + + @param cells The cells to delete + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload + def prune_subcells(self, cell: Cell, levels: Optional[int] = ...) -> None: + r""" + @brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy + + This deletes all sub cells of the cell which are not used otherwise. + All instances of the deleted cells are deleted as well. + It is possible to specify how many levels of hierarchy below the given root cell are considered. + + A variant exists that takes a list of cells and which is more efficient than calling + 'prune_subcells' multiple times on a single cell. + + @param cell The root cell from which to delete a sub cells + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This convenience variant taking a list of cell objects has been introduced in version 0.30.9. """ ... - def prune_subcells(self, cell_index: int, levels: int) -> None: + @overload + def prune_subcells(self, cell_index: int, levels: Optional[int] = ...) -> None: r""" @brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy @@ -40620,6 +41389,9 @@ class Layout: All instances of the deleted cells are deleted as well. It is possible to specify how many levels of hierarchy below the given root cell are considered. + A variant exists that takes a list of cell indexes and which is more efficient than calling + 'prune_subcells' multiple times on a single cell. + @param cell_index The root cell from which to delete a sub cells @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) @@ -40627,6 +41399,36 @@ class Layout: """ ... @overload + def prune_subcells(self, cell_index_list: Sequence[int], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy + + This deletes all sub cells of the given cells which are not used otherwise. + All instances of the deleted cells are deleted as well. + It is possible to specify how many levels of hierarchy below the given root cell are considered. + + @param cell_index_list The root cells from which to delete the sub cells + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload + def prune_subcells(self, cell_list: Sequence[Cell], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy + + This deletes all sub cells of the given cells which are not used otherwise. + All instances of the deleted cells are deleted as well. + It is possible to specify how many levels of hierarchy below the given root cell are considered. + + @param cell_list The root cells from which to delete the sub cells + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload def read(self, filename: str) -> LayerMap: r""" @brief Load the layout from the given file @@ -48146,17 +48948,17 @@ class NetTerminalRef: @overload def device(self) -> Device: r""" - @brief Gets the device reference. + @brief Gets the device reference (non-const version). Gets the device object that this connection is made to. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def device(self) -> Device: r""" - @brief Gets the device reference (non-const version). + @brief Gets the device reference. Gets the device object that this connection is made to. - - This constness variant has been introduced in version 0.26.8 """ ... def device_class(self) -> DeviceClass: @@ -49152,17 +49954,17 @@ class Netlist: @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index. + @brief Gets the circuit object for a given cell index (const version). If the cell index is not valid or no circuit is registered with this index, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index (const version). + @brief Gets the circuit object for a given cell index. If the cell index is not valid or no circuit is registered with this index, nil is returned. - - This constness variant has been introduced in version 0.26.8. """ ... @overload @@ -49446,7 +50248,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit (const version). + @brief Gets the top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -49455,7 +50257,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit. + @brief Gets the top circuit (const version). This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -53335,6 +54137,15 @@ class Path: @brief Set the width """ @classmethod + def from_bytes(cls, s: bytes) -> Path: + r""" + @brief Creates a path object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpath(cls, dpath: DPath) -> Path: r""" @brief Creates an integer coordinate path from a floating-point coordinate path @@ -53775,6 +54586,15 @@ class Path: The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps itself or contains very short segments. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this path + + This string can be turned into a path again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DPath: r""" @brief Converts the path to a floating-point coordinate path @@ -53859,6 +54679,24 @@ class PathWithProperties(Path): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> PathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> PathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, path: Path, properties: Dict[Any, Any]) -> PathWithProperties: @@ -53894,7 +54732,7 @@ class PathWithProperties(Path): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPathWithProperties: @@ -53906,7 +54744,7 @@ class PathWithProperties(Path): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Path) -> None: @@ -54060,9 +54898,18 @@ class PathWithProperties(Path): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -54218,6 +55065,15 @@ class Point: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> Point: + r""" + @brief Creates a point object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpoint(cls, dpoint: DPoint) -> Point: r""" @brief Creates an integer coordinate point from a floating-point coordinate point @@ -54628,6 +55484,15 @@ class Point: @param d The other point to compute the distance to. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this point + + This string can be turned into a point again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DPoint: r""" @brief Converts the point to a floating-point coordinate point @@ -54762,6 +55627,15 @@ class Polygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> Polygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpoly(cls, dpolygon: DPolygon) -> Polygon: r""" @brief Creates an integer coordinate polygon from a floating-point coordinate polygon @@ -55693,6 +56567,15 @@ class Polygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DPolygon: r""" @brief Converts the polygon to a floating-point coordinate polygon @@ -57404,6 +58287,24 @@ class PolygonWithProperties(Polygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> PolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> PolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: Polygon, properties: Dict[Any, Any]) -> PolygonWithProperties: @@ -57439,7 +58340,7 @@ class PolygonWithProperties(Polygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPolygonWithProperties: @@ -57451,7 +58352,7 @@ class PolygonWithProperties(Polygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Polygon) -> None: @@ -57605,9 +58506,18 @@ class PolygonWithProperties(Polygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -68559,6 +69469,15 @@ class SimplePolygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> SimplePolygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpoly(cls, dpolygon: DSimplePolygon) -> SimplePolygon: r""" @brief Creates an integer coordinate polygon from a floating-point coordinate polygon @@ -69206,6 +70125,15 @@ class SimplePolygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DSimplePolygon: r""" @brief Converts the polygon to a floating-point coordinate polygon @@ -69356,6 +70284,24 @@ class SimplePolygonWithProperties(SimplePolygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> SimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> SimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: SimplePolygon, properties: Dict[Any, Any]) -> SimplePolygonWithProperties: @@ -69391,7 +70337,7 @@ class SimplePolygonWithProperties(SimplePolygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DSimplePolygonWithProperties: @@ -69403,7 +70349,7 @@ class SimplePolygonWithProperties(SimplePolygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: SimplePolygon) -> None: @@ -69557,9 +70503,18 @@ class SimplePolygonWithProperties(SimplePolygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -69704,32 +70659,32 @@ class SubCircuit(NetlistObject): @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in (non-const version). + @brief Gets the circuit the subcircuit lives in. This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 """ ... @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in. + @brief Gets the circuit the subcircuit lives in (non-const version). This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def circuit_ref(self) -> Circuit: r""" - @brief Gets the circuit referenced by the subcircuit. + @brief Gets the circuit referenced by the subcircuit (non-const version). + + + This constness variant has been introduced in version 0.26.8 """ ... @overload def circuit_ref(self) -> Circuit: r""" - @brief Gets the circuit referenced by the subcircuit (non-const version). - - - This constness variant has been introduced in version 0.26.8 + @brief Gets the circuit referenced by the subcircuit. """ ... @overload @@ -69775,17 +70730,17 @@ class SubCircuit(NetlistObject): @overload def net_for_pin(self, pin_id: int) -> Net: r""" - @brief Gets the net connected to the specified pin of the subcircuit (non-const version). + @brief Gets the net connected to the specified pin of the subcircuit. If the pin is not connected, nil is returned for the net. - - This constness variant has been introduced in version 0.26.8 """ ... @overload def net_for_pin(self, pin_id: int) -> Net: r""" - @brief Gets the net connected to the specified pin of the subcircuit. + @brief Gets the net connected to the specified pin of the subcircuit (non-const version). If the pin is not connected, nil is returned for the net. + + This constness variant has been introduced in version 0.26.8 """ ... ... @@ -70435,8 +71390,7 @@ class Text: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: int r""" @@ -70463,6 +71417,15 @@ class Text: This method has been introduced in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> Text: + r""" + @brief Creates a text object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> Text: r""" @brief Creates an object from a string @@ -70801,6 +71764,15 @@ class Text: This convenience method has been added in version 0.28. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this text object + + This string can be turned into a text object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DText: r""" @brief Converts the text to a floating-point coordinate text @@ -72145,6 +73117,24 @@ class TextWithProperties(Text): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> TextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> TextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, text: Text, properties: Dict[Any, Any]) -> TextWithProperties: @@ -72180,7 +73170,7 @@ class TextWithProperties(Text): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DTextWithProperties: @@ -72192,7 +73182,7 @@ class TextWithProperties(Text): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Text) -> None: @@ -72346,9 +73336,18 @@ class TextWithProperties(Text): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -74192,6 +75191,15 @@ class Trans: This method was introduced in version 0.20. """ @classmethod + def from_bytes(cls, s: bytes) -> Trans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dtrans(cls, dtrans: DTrans) -> Trans: r""" @brief Creates an integer coordinate transformation from a floating-point coordinate transformation @@ -75037,6 +76045,15 @@ class Trans: If this property is true, the transformation is composed of a mirroring at the x-axis followed by a rotation by the angle given by the \angle property. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DTrans: r""" @brief Converts the transformation to a floating-point coordinate transformation @@ -76006,6 +77023,15 @@ class VCplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> VCplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> VCplxTrans: r""" @brief Creates an object from a string @@ -76942,6 +77968,15 @@ class VCplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Converts the transformation to another transformation with floating-point output coordinates @@ -77452,6 +78487,15 @@ class Vector: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> Vector: + r""" + @brief Creates a vector object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> Vector: r""" @brief Creates an object from a string @@ -77806,6 +78850,15 @@ class Vector: 'sq_abs' is an alias provided for compatibility with the former point type. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this vector + + This string can be turned into a vector again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DVector: r""" @brief Converts the vector to a floating-point coordinate vector diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 1d547f2775..4d1cf22e0a 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -1270,17 +1270,17 @@ class RdbReference: @overload def database(self) -> ReportDatabase: r""" - @brief Gets the database object that category is associated with (non-const version) + @brief Gets the database object that category is associated with - This method has been introduced in version 0.29. + This method has been introduced in version 0.23. """ ... @overload def database(self) -> ReportDatabase: r""" - @brief Gets the database object that category is associated with + @brief Gets the database object that category is associated with (non-const version) - This method has been introduced in version 0.23. + This method has been introduced in version 0.29. """ ... def destroy(self) -> None: diff --git a/testdata/lstream/pcells_au.oas b/testdata/lstream/pcells_au.oas index 88943799d8..3554c35e72 100644 Binary files a/testdata/lstream/pcells_au.oas and b/testdata/lstream/pcells_au.oas differ diff --git a/testdata/lvs/split_substrate.l2n.1 b/testdata/lvs/split_substrate.l2n.1 new file mode 100644 index 0000000000..0fe18254e5 --- /dev/null +++ b/testdata/lvs/split_substrate.l2n.1 @@ -0,0 +1,215 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l3 '1/0') +L(l4 '3/0') +L(l15 '3/1') +L(l8 '4/0') +L(l11 '5/0') +L(l12 '6/0') +L(l16 '6/1') +L(l13 '7/0') +L(l14 '8/0') +L(l17) +L(l22 '10/0') +L(l7) +L(l10) +L(l2) +L(l9) +L(l6) +L(l18) +L(l21) +L(l20) +L(l19) +C(l3 l3 l10) +C(l4 l4 l15 l11) +C(l15 l4 l15) +C(l8 l8 l12 l10 l2 l9 l6) +CS(l8 l10 l2 l9 l6) +C(l11 l4 l11 l12) +CS(l11 l4) +C(l12 l8 l11 l12 l16 l13) +C(l16 l12 l16) +C(l13 l12 l13 l14) +C(l14 l13 l14 l17) +C(l17 l14 l17) +C(l22 l22 l21 l20) +C(l7 l7 l18 l20) +C(l10 l3 l8 l10) +CS(l10 l3) +C(l2 l8 l2) +C(l9 l8 l9 l21 l19) +C(l6 l8 l6) +C(l18 l7 l18) +C(l21 l22 l9 l21) +CS(l21 l22) +C(l20 l22 l7 l20) +C(l19 l9 l19) +G(l22 IOSUB) +G(l18 SUBSTRATE) +G(l19 SUBSTRATE) +GS(l19 SUBSTRATE) +H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check')) +H(B('\tPartial net #1: TOP/INV[r0 3,1.6]:$1 - $2') C(TOP) Q('(1.5,3.95;1.5,4.85;5.3,4.85;5.3,3.95)')) +H(B('\tPartial net #2: TOP/INV[r0 7.7,1.6]:$2 - $2') C(TOP) Q('(6.2,3.95;6.2,4.85;10,4.85;10,3.95)')) +K(PMOS MOS4) +K(NMOS MOS4) +D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l4 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + T(B + R(l3 (-125 -475) (250 950)) + ) +) +D(D$NMOS NMOS + T(S + R(l6 (-900 -475) (775 950)) + ) + T(G + R(l4 (-125 -475) (250 950)) + ) + T(D + R(l6 (125 -475) (775 950)) + ) + T(B + R(l7 (-125 -475) (250 950)) + ) +) +X(INV + R((-1500 -800) (3800 4600)) + N(1 I($1) + R(l8 (1700 100) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (-1610 -210) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (890 -760) (800 900)) + R(l12 (-1980 -830) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l9 (-700 -950) (400 1000)) + R(l6 (-1875 -975) (775 950)) + ) + N(2 I($2) + R(l3 (-1500 1800) (3000 2000)) + R(l3 (-200 -2000) (1000 2000)) + R(l8 (-2010 -1310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (1190 -210) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-1680 -280) (360 760)) + R(l12 (820 -830) (800 900)) + R(l13 (-1925 -775) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l10 (-700 -950) (400 1000)) + R(l2 (-1875 -975) (775 950)) + ) + N(3 I($4) + R(l4 (-125 -250) (250 2500)) + R(l4 (-250 -3050) (250 1600)) + R(l4 (-250 1200) (250 1600)) + ) + N(4 I($5) + R(l8 (-510 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (-220 2180) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -3530) (360 2840)) + R(l12 (-360 -2800) (360 760)) + R(l12 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l6 (-775 -3750) (775 950)) + ) + N(5 I($I11)) + P(2) + P(3) + P(4) + P(1) + P(5) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + T(B 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + T(B 5) + ) +) +X(TOP + R((1334 621) (9246 5073)) + N(1 I($1) + R(l4 (2920 2600) (3980 400)) + R(l11 (-300 -300) (200 200)) + R(l12 (-300 -300) (690 400)) + ) + N(2 I(A) + R(l4 (7700 2600) (2880 400)) + R(l15 (-2380 -200) (0 0)) + ) + N(3 I(Q) + R(l12 (1810 2600) (690 400)) + R(l16 (-400 -200) (0 0)) + ) + N(4 I(IOSUB) + R(l22 (1334 621) (4230 5073)) + R(l21 (-964 -4594) (400 1000)) + R(l20 (-2125 -975) (250 950)) + ) + N(5 I($7) + R(l3 (4000 3400) (2700 2000)) + ) + N(6 I(SUBSTRATE) + R(l18 (7575 1125) (250 950)) + R(l19 (1475 -975) (400 1000)) + ) + P(2 I(A)) + P(3 I(Q)) + P(4 I(IOSUB)) + P(6 I(SUBSTRATE)) + X(1 INV Y(3000 1600) + P(0 5) + P(1 1) + P(2 3) + P(3 4) + P(4 4) + ) + X(2 INV Y(7700 1600) + P(0 5) + P(1 2) + P(2 1) + P(3 6) + P(4 6) + ) +) diff --git a/testdata/lvs/split_substrate.l2n b/testdata/lvs/split_substrate.l2n.2 similarity index 100% rename from testdata/lvs/split_substrate.l2n rename to testdata/lvs/split_substrate.l2n.2 diff --git a/testdata/ruby/dbLayoutTests1.rb b/testdata/ruby/dbLayoutTests1.rb index ecf2fa19bf..97c903762a 100644 --- a/testdata/ruby/dbLayoutTests1.rb +++ b/testdata/ruby/dbLayoutTests1.rb @@ -335,34 +335,21 @@ def test_5a c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) assert_equal(c0.is_empty?, false) + c0_index = c0.cell_index + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)"); - c0_index = c0.cell_index + ll = l.dup assert_equal(l.is_valid_cell_index?(c0_index), true) - l.delete_cell(c0.cell_index) + l.delete_cell(c0_index) assert_equal(l.is_valid_cell_index?(c0_index), false) assert_equal(collect_hier(l), "[c1](P=)(C=)/[c2](P=)(C=c3)/[c3](P=c2)(C=)"); assert_equal(c3.is_empty?, true) - l = RBA::Layout.new - l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) - c0 = l.cell(l.add_cell("c0")) - c1 = l.cell(l.add_cell("c1")) - c2 = l.cell(l.add_cell("c2")) - c3 = l.cell(l.add_cell("c3")) - assert_equal(c0.is_empty?, true) - - tt = RBA::Trans.new - c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) - c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) - c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) - c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) - assert_equal(c0.is_empty?, false) - - assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)"); + l = ll + ll = l.dup - c0_index = c0.cell_index assert_equal(l.is_valid_cell_index?(c0_index), true) l.cell(c0_index).delete assert_equal(l.is_valid_cell_index?(c0_index), false) @@ -370,6 +357,16 @@ def test_5a assert_equal(collect_hier(l), "[c1](P=)(C=)/[c2](P=)(C=c3)/[c3](P=c2)(C=)"); assert_equal(c3.is_empty?, true) + l = ll + ll = l.dup + + assert_equal(l.is_valid_cell_index?(c0_index), true) + # with cell object + l.delete_cell(l.cell(c0_index)) + assert_equal(l.is_valid_cell_index?(c0_index), false) + assert_equal(collect_hier(l), "[c1](P=)(C=)/[c2](P=)(C=c3)/[c3](P=c2)(C=)"); + assert_equal(c3.is_empty?, true) + end def test_5b @@ -413,10 +410,17 @@ def test_5b assert_equal(collect_hier(l), "[c1](P=)(C=)/[c3](P=)(C=)"); l = ll + ll = l.dup # Hint: even though we deleted c0 and c2, their indices are still valid l.delete_cells([c2_index, c0_index]) assert_equal(collect_hier(l), "[c1](P=)(C=)/[c3](P=)(C=)"); + l = ll + ll = l.dup + # with cell objects + l.delete_cells([l.cell(c2_index), l.cell(c0_index)]) + assert_equal(collect_hier(l), "[c1](P=)(C=)/[c3](P=)(C=)"); + end def test_5d @@ -487,6 +491,7 @@ def test_5d c0_index = c0.cell_index c2_index = c2.cell_index + c3_index = c3.cell_index ll = l.dup @@ -499,6 +504,42 @@ def test_5d l.cell(c2_index).prune_cell(-1) assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + l = ll + ll = l.dup + # As method of Layout + l.prune_cell(c2_index, -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with default argument + l.prune_cell(c2_index) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with cell object + l.prune_cell(l.cell(c2_index), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with cell object and default argument + l.prune_cell(l.cell(c2_index), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # prune c2 and c3 + l.prune_cells([ c2_index, c3_index ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + + l = ll + ll = l.dup + # prune c2 and c3 with cell objects + l.prune_cells([ l.cell(c2_index), l.cell(c3_index) ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + end def test_5e @@ -546,6 +587,12 @@ def test_5e l.delete_cell_rec(c2_index) assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + l = ll + ll = l.dup + # with cell object + l.delete_cell_rec(l.cell(c2_index)) + assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + end def test_5f @@ -589,12 +636,38 @@ def test_5f l = ll ll = l.dup - l.flatten(c0_index, -1, true) + l.flatten(c0_index) assert_equal(collect_hier(l), "[c0](P=)(C=)"); ii = l.begin_shapes(c0_index, 0); assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c0](0,100;1000,1200)/[c0](100,0;1100,1100)/[c0](1200,0;2200,1100)/[c0](-1200,0;-100,1000)"); + l = ll + ll = l.dup + c0flat = l.create_cell("c0flat") + l.flatten_into(c0_index, c0flat.cell_index) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)/[c0flat](P=)(C=)"); + + ii = l.begin_shapes(c0flat, 0); + assert_equal(collect(ii, l), "[c0flat](0,100;1000,1200)/[c0flat](0,100;1000,1200)/[c0flat](100,0;1100,1100)/[c0flat](1200,0;2200,1100)/[c0flat](-1200,0;-100,1000)"); + + l = ll + ll = l.dup + l.flatten(l.cell(c0_index), -1, true) # with cell object + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + ii = l.begin_shapes(c0_index, 0); + assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c0](0,100;1000,1200)/[c0](100,0;1100,1100)/[c0](1200,0;2200,1100)/[c0](-1200,0;-100,1000)"); + + l = ll + ll = l.dup + c0flat = l.create_cell("c0flat") + l.flatten_into(l.cell(c0_index), c0flat, RBA::ICplxTrans::new(2.0), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)/[c0flat](P=)(C=)"); + + ii = l.begin_shapes(c0flat, 0); + assert_equal(collect(ii, l), "[c0flat](0,200;2000,2400)/[c0flat](0,200;2000,2400)/[c0flat](200,0;2200,2200)/[c0flat](2400,0;4400,2200)/[c0flat](-2400,0;-200,2000)"); + l = ll ll = l.dup l.flatten(c0_index, 0, false) @@ -748,10 +821,59 @@ def test_5g assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=)/[c3](P=c0)(C=)"); l = ll + ll = l.dup # Hint: even though we deleted c0 and c2, their indices are still valid l.cell(c0_index).prune_subcells(1) assert_equal(collect_hier(l), "[c0](P=)(C=)"); + l = ll + ll = l.dup + # As method of Layout + l.prune_subcells(c0_index, -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with default argument + l.prune_subcells(c0_index) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with Cell object + l.prune_subcells(l.cell(c0_index), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with Cell object and default argument + l.prune_subcells(l.cell(c0_index)) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells + l.prune_subcells([ c0_index ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells and default argument + l.prune_subcells([ c0_index ]) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells and Cell object + l.prune_subcells([ l.cell(c0_index) ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells and Cell object and default argument + l.prune_subcells([ l.cell(c0_index) ]) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + end def test_6 @@ -2365,7 +2487,7 @@ def test_issue1549 end # Properties IDs - def test_issue1549 + def test_propertyIDs ly = RBA::Layout::new diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index 6ba044e78e..864b82ead1 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -277,7 +277,7 @@ def norm_hash(hash) class DBPCellAPI_TestClass < TestBase - def _test_1 + def test_1 # PCellParameterDeclaration @@ -351,7 +351,7 @@ def _test_1 class DBPCell_TestClass < TestBase - def _test_1 + def test_1 # instantiate and register the library tl = PCellTestLib::new @@ -593,7 +593,7 @@ def test_1a end - def _test_2 + def test_2 # instantiate and register the library tl = PCellTestLib::new @@ -632,7 +632,7 @@ def _test_2 end - def _test_3 + def test_3 # instantiate and register the library tl = PCellTestLib::new @@ -657,7 +657,7 @@ def _test_3 end - def _test_4 + def test_4 # instantiate and register the library tl = PCellTestLib::new @@ -674,7 +674,7 @@ def _test_4 end - def _test_5 + def test_5 # instantiate and register the library tl = PCellTestLib::new @@ -691,7 +691,7 @@ def _test_5 end - def _test_6 + def test_6 # instantiate and register the library tl = PCellTestLib::new @@ -707,7 +707,7 @@ def _test_6 end - def _test_7 + def test_7 # instantiate and register the library tl = PCellTestLib::new @@ -725,7 +725,7 @@ def _test_7 end - def _test_8 + def test_8 # instantiate and register the library tl = PCellTestLib::new @@ -747,7 +747,7 @@ def _test_8 end - def _test_9 + def test_9 layout = RBA::Layout::new @@ -813,7 +813,7 @@ def reregister_pcell end - def _test_10 + def test_10 lib = CircleLib1782::new("CircleLib") @@ -864,7 +864,7 @@ def _test_10 end - def _test_11 + def test_11 lib = CircleLib1782::new("CircleLib") @@ -899,7 +899,7 @@ def _test_11 end - def _test_12 + def test_12 if !RBA.constants.member?(:PCellDeclarationHelper) return @@ -924,7 +924,7 @@ def _test_12 end # convert to static cell - def _test_13 + def test_13 if !RBA.constants.member?(:PCellDeclarationHelper) return @@ -954,11 +954,69 @@ def _test_13 end + # cold proxies + def test_14 + + # instantiate and register the library + tl = PCellTestLib::new + + ly = RBA::Layout::new(true) + ly.dbu = 0.01 + + li1 = ly.layer(1, 0) + + scell = ly.create_cell("A") + + param = { "w" => 4.0, "h" => 8.0, "l" => RBA::LayerInfo::new(1, 0) } + cell = ly.create_cell("Box", "PCellTestLib", param) + cell.name = "B" + + assert_equal(scell.is_proxy?, false) + assert_equal(scell.is_library_cell?, false) + assert_equal(scell.is_cold_proxy?, false) + assert_equal(scell.pcell_parameter("h").to_s, "") + assert_equal(scell.pcell_parameter("x").to_s, "") + assert_equal(scell.pcell_parameters_by_name.to_s, "{}") + assert_equal(scell.pcell_name, "") + assert_equal(scell.library_name, "") + assert_equal(scell.library_cell_name, "") + + assert_equal(cell.is_proxy?, true) + assert_equal(cell.is_library_cell?, true) + assert_equal(cell.is_cold_proxy?, false) + assert_equal(cell.pcell_parameter("h").to_s, "8.0") + assert_equal(cell.pcell_parameter("x").to_s, "") + assert_equal(cell.pcell_parameters_by_name.to_s, "{\"h\"=>8.0, \"l\"=><1/0>, \"w\"=>4.0}") + assert_equal(cell.pcell_name, "Box") + assert_equal(cell.library_cell_name, "Box_L1d0_W4p000_H8p000") # it's the library's PCell proxy + assert_equal(cell.library_name, "PCellTestLib") + assert_equal(cell.display_title, "PCellTestLib.Box(L=1/0,W=4.000,H=8.000)") + + tl._destroy + + # the cell got destroyed and replaced by a cold proxy + assert_equal(cell._destroyed?, true) + + cell = ly.cell("B") + + assert_equal(cell.is_proxy?, true) + assert_equal(cell.is_library_cell?, false) + assert_equal(cell.is_cold_proxy?, true) + assert_equal(cell.pcell_parameter("h").to_s, "8.0") + assert_equal(cell.pcell_parameter("x").to_s, "") + assert_equal(cell.pcell_parameters_by_name.to_s, "{\"h\"=>8.0, \"l\"=><1/0>, \"w\"=>4.0}") + assert_equal(cell.pcell_name, "Box") + assert_equal(cell.library_cell_name, "") # it's not a static library cell + assert_equal(cell.library_name, "PCellTestLib") + assert_equal(cell.display_title, "PCellTestLib.Box") + + end + end class DBPCellParameterStates_TestClass < TestBase - def _test_1 + def test_1 ps = RBA::PCellParameterState::new diff --git a/version.sh b/version.sh index c7782a9d79..664c35ca28 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.30.8" +KLAYOUT_VERSION="0.30.9" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.30.8" +KLAYOUT_PYPI_VERSION="0.30.9" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")