diff --git a/Changelog b/Changelog index 959aa1868..4003ed0b7 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,70 @@ +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: %GITHUB%/issues/2360 Sort ports by name in "make_top_level_pins" +* 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 3b572f5f4..44cba48fc 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 1f5ec4a29..c732cebde 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 000000000..151f8f658 --- /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 1d1286c73..dbd46c374 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 f92c2c58e..9706615ef 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 0fc87fed9..8235a563f 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 334a46002..25a13a7a5 100644 --- a/src/ant/ant/antPlugin.h +++ b/src/ant/ant/antPlugin.h @@ -54,7 +54,7 @@ public: 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 000000000..199525ad3 --- /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 000000000..4c20b573a --- /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 fe45fbecb..faed89a27 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/db.pro b/src/db/db/db.pro index bcae249bf..bcd16b00e 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY SOURCES = \ dbArray.cc \ + dbBinarySerialize.cc \ dbBox.cc \ dbBoxConvert.cc \ dbBoxScanner.cc \ @@ -145,6 +146,15 @@ SOURCES = \ gsiDeclDbMatrix.cc \ gsiDeclDbMeasureHelpers.cc \ gsiDeclDbMetaInfo.cc \ + gsiDeclDbNetlistCircuit.cc \ + gsiDeclDbNetlistDevice.cc \ + gsiDeclDbNetlistDeviceClass.cc \ + gsiDeclDbNetlistNet.cc \ + gsiDeclDbNetlistObject.cc \ + gsiDeclDbNetlistPin.cc \ + gsiDeclDbNetlistSpiceReader.cc \ + gsiDeclDbNetlistSpiceWriter.cc \ + gsiDeclDbNetlistSubcircuit.cc \ gsiDeclDbPath.cc \ gsiDeclDbPoint.cc \ gsiDeclDbPolygon.cc \ @@ -248,6 +258,7 @@ SOURCES = \ HEADERS = \ dbArray.h \ + dbBinarySerialize.h \ dbBoxConvert.h \ dbBox.h \ dbBoxScanner.h \ diff --git a/src/db/db/dbBinarySerialize.cc b/src/db/db/dbBinarySerialize.cc new file mode 100644 index 000000000..ea92b8ac4 --- /dev/null +++ b/src/db/db/dbBinarySerialize.cc @@ -0,0 +1,31 @@ + +/* + + 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 + +*/ + + +#include "dbBinarySerialize.h" + +namespace db +{ + + // .. nothing yet .. + +} diff --git a/src/db/db/dbBinarySerialize.h b/src/db/db/dbBinarySerialize.h new file mode 100644 index 000000000..5512228ea --- /dev/null +++ b/src/db/db/dbBinarySerialize.h @@ -0,0 +1,623 @@ + +/* + + 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 + +*/ + + +#ifndef HDR_dbBinarySerialize +#define HDR_dbBinarySerialize + +#include "dbCommon.h" + +#include "dbPoint.h" +#include "dbVector.h" +#include "dbEdge.h" +#include "dbEdgePair.h" +#include "dbPolygon.h" +#include "dbBox.h" +#include "dbPath.h" +#include "dbTrans.h" +#include "dbText.h" +#include "dbObjectWithProperties.h" + +#include "tlBinaryStream.h" +#include "tlException.h" + +namespace db +{ + +// ----------------------------------------------------------------------------------------- +// Stream inserters + +template +tl::BinaryOutputStream &write_coord (tl::BinaryOutputStream &s, C c) +{ + return s << c; +} + +inline tl::BinaryOutputStream &write_coord (tl::BinaryOutputStream &s, db::Coord c) +{ + // NOTE: for compatibility across different builds we use 64 bit coordinates always + return s << (int64_t) c; +} + +template +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::point &pt) +{ + return write_coord (write_coord (s, pt.x ()), pt.y ()); +} + +template +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::vector &v) +{ + return write_coord (write_coord (s, v.x ()), v.y ()); +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::point &pt) +{ + s << (uint16_t) 1; // version + return write_binary (s, pt); +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::vector &v) +{ + s << (uint16_t) 1; // version + return write_binary (s, v); +} + +template +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::edge &e) +{ + write_binary (s, e.p1 ()); + write_binary (s, e.p2 ()); + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge &e) +{ + s << (uint16_t) 1; // version + return write_binary (s, e); +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::box &b) +{ + s << (uint16_t) 1; // version + write_binary (s, b.p1 ()); + write_binary (s, b.p2 ()); + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge_pair &ep) +{ + s << (uint16_t) 1; // version + write_binary (s, ep.first ()); + write_binary (s, ep.second ()); + return s; +} + +template +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::polygon_contour &c) +{ + s << (uint64_t) c.size (); + for (auto i = c.begin (); i != c.end (); ++i) { + write_binary (s, *i); + } + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::polygon &p) +{ + s << (uint16_t) 1; // version + s << (uint64_t) (p.holes () + 1); + for (size_t h = 0; h < p.holes () + 1; ++h) { + write_binary (s, p.contour (h)); + } + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_polygon &p) +{ + // NOTE: the format of polygon and simple polygon are compatible + s << (uint16_t) 1; // version + s << (uint64_t) 1; // number of contours + write_binary (s, p.hull ()); + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::unit_trans &) +{ + s << (uint16_t) 1; // version + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::disp_trans &t) +{ + s << (uint16_t) 1; // version + write_binary (s, t.disp ()); + return s; +} + +template +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::simple_trans &t) +{ + s << (uint16_t) t.rot (); + write_binary (s, t.disp ()); + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_trans &t) +{ + s << (uint16_t) 1; // version + return write_binary (s, t); +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::complex_trans &t) +{ + s << (uint16_t) 1; // version + write_binary (s, t.disp ()); + return s << (double) t.msin () << (double) t.mcos () << (double) (t.is_mirror () ? -t.mag () : t.mag ()); +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::text &t) +{ + s << (uint16_t) 1; // version + s << t.string (); + write_binary (s, t.trans ()); + write_coord (s, t.size ()); + return s << (int32_t) t.font () << (int32_t) t.halign () << (int32_t) t.valign (); +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::path &p) +{ + s << (uint16_t) 1; // version + s << (uint64_t) p.points (); + for (auto i = p.begin (); i != p.end (); ++i) { + write_binary (s, *i); + } + write_coord (s, p.width ()); + write_coord (s, p.bgn_ext ()); + write_coord (s, p.end_ext ()); + s << p.round (); + return s; +} + +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::object_with_properties &o) +{ + s << (uint16_t) 1; // version + + s << (const T &) o; + + const auto &ps = db::properties (o.properties_id ()); + + s << (uint64_t) ps.size (); + for (auto i = ps.begin (); i != ps.end (); ++i) { + s << db::property_name (i->first) << db::property_value (i->second); + } + + return s; +} + +// ----------------------------------------------------------------------------------------- +// Stream extractors + +class FutureBinarySerializationFormatException + : public tl::Exception +{ +public: + FutureBinarySerializationFormatException () + : tl::Exception (tl::to_string (tr ("Binary serialization format version is too new for this build"))) + { } +}; + +template +tl::BinaryInputStream &read_coord (tl::BinaryInputStream &s, C &c) +{ + return s >> c; +} + +inline tl::BinaryInputStream &read_coord (tl::BinaryInputStream &s, db::Coord &c) +{ + // NOTE: for compatibility across different builds we use 64 bit coordinates always + int64_t cc = 0; + s >> cc; + c = cc; + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::point &pt) +{ + C x = 0, y = 0; + read_coord (s, x); + read_coord (s, y); + pt = db::point (x, y); + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::vector &v) +{ + C x = 0, y = 0; + read_coord (s, x); + read_coord (s, y); + v = db::vector (x, y); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::point &pt) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + return read_binary (s, pt); +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::vector &v) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + return read_binary (s, v); +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::edge &v) +{ + db::point p1, p2; + read_binary (s, p1); + read_binary (s, p2); + v = db::edge (p1, p2); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge &e) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + return read_binary (s, e); +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::box &b) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::point p1, p2; + read_binary (s, p1); + read_binary (s, p2); + b = db::box (p1, p2); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge_pair &ep) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::edge e1, e2; + read_binary (s, e1); + read_binary (s, e2); + ep = db::edge_pair (e1, e2); + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contour &c, bool hole) +{ + uint64_t n = 0; + s >> n; + + std::vector > pts; + pts.reserve (n); + while (n-- > 0) { + pts.push_back (db::point ()); + read_binary (s, pts.back ()); + } + + // NOTE: not normalization or modification is applied + c.assign (pts.begin (), pts.end (), hole, false, false, false); + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::polygon &p) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + uint64_t n = 0; + s >> n; + + p = db::polygon (); + if (n > 0) { + p.reserve_holes (n - 1); + } + + db::polygon_contour ctr; + for (uint64_t h = 0; h < n; ++h) { + read_binary (s, ctr, h > 0); + if (h == 0) { + p.assign_hull (ctr); + } else { + p.insert_hole (ctr); + } + } + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_polygon &p) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + uint64_t n = 0; + s >> n; + tl_assert (n == (uint64_t) 1); + + db::polygon_contour ctr; + read_binary (s, ctr, false); + p.assign_hull (ctr); + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::unit_trans &) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::disp_trans &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::vector d; + read_binary (s, d); + t = db::disp_trans (d); + + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::simple_trans &t) +{ + uint16_t r = 0; + s >> r; + + db::vector d; + read_binary (s, d); + + t = db::simple_trans (r, d); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_trans &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + return read_binary (s, t); +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::complex_trans &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::vector d; + read_binary (s, d); + + double asin, acos, mag; + s >> asin >> acos >> mag; + + t = db::complex_trans (d, asin, acos, mag); + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::text &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + std::string txt; + s >> txt; + + db::simple_trans tr; + read_binary (s, tr); + + C size; + read_coord (s, size); + + int32_t font, halign, valign; + s >> font >> halign >> valign; + + t = db::text (txt, tr, size, db::Font (font), db::HAlign (halign), db::VAlign (valign)); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::path &p) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + uint64_t n = 0; + s >> n; + + std::vector > pts; + pts.reserve (n); + while (n-- > 0) { + pts.push_back (db::point ()); + read_binary (s, pts.back ()); + } + + C width, bgn_ext, end_ext; + read_coord (s, width); + read_coord (s, bgn_ext); + read_coord (s, end_ext); + + bool round; + s >> round; + + p = db::path (pts.begin (), pts.end (), width, bgn_ext, end_ext, round); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::object_with_properties &o) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + s >> (T &) o; + + uint64_t n = 0; + s >> n; + + db::PropertiesSet ps; + + while (n-- > 0) { + tl::Variant name, value; + s >> name >> value; + ps.insert (name, value); + } + + o.properties_id (db::properties_id (ps)); + + return s; +} + +// ----------------------------------------------------------------------------------------- +// Converters of objects to a binary string + +template +std::vector to_bytes (const T &t) +{ + tl::OutputMemoryStream osm; + { + tl::BinaryOutputStream os (osm); + os << t; + } + return std::vector (osm.data (), osm.data () + osm.size ()); +} + +template +std::string to_bytes_str (const T &t) +{ + tl::OutputMemoryStream osm; + { + tl::BinaryOutputStream os (osm); + os << t; + } + return std::string (osm.data (), osm.size ()); +} + +template +void from_bytes (const char *data, size_t n, T &t) +{ + tl::InputMemoryStream ism (data, n); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + bis >> t; +} + +template +void from_bytes (const std::vector &s, T &t) +{ + tl::InputMemoryStream ism (s.begin ().operator-> (), s.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + bis >> t; +} + +} + +#endif diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 617ea3f1f..15cb0fb2e 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 5ded121e1..3ff11ccce 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/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index 7f32f2d3b..9dc93f930 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -330,7 +330,6 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); } -// @@@ GSI bindings for SPICE profile void DeviceClass::set_spice_profile (const std::string &name, const SpiceProfile &profile) { m_spice_profiles [name] = profile; diff --git a/src/db/db/dbEdgePairFilters.h b/src/db/db/dbEdgePairFilters.h index 3d8342b93..0881b2649 100644 --- a/src/db/db/dbEdgePairFilters.h +++ b/src/db/db/dbEdgePairFilters.h @@ -119,7 +119,6 @@ public: 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 ad6acd0b7..b2c02005a 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 fb9cbbc04..a69e8b4bb 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); - } + { + std::set cells_to_delete = cells_to_cleanup (keep); + if (! cells_to_delete.empty ()) { + delete_cells (cells_to_delete); } - - if (cells_to_delete.empty ()) { - break; - } - - 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 b9bc0f1c9..36ec43dc5 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1682,6 +1682,19 @@ public: */ 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 8ce32e6d6..0925960ad 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -544,6 +544,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo std::string param_name; read_word_or_quoted (param_name); int primary = read_int (); + make_parameter (dc, param_name, primary, tl::Variant ()); br.done (); diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 8e13ab793..fbe959837 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -626,7 +626,26 @@ void Netlist::purge_devices () } } -void Netlist::make_top_level_pins () +namespace { + + struct CompareNetByName + { + CompareNetByName (const db::Netlist *netlist) + : mp_netlist (netlist) + { } + + bool operator() (const db::Net *a, const db::Net *b) + { + return mp_netlist->normalize_name (a->name ()) < mp_netlist->normalize_name (b->name ()); + } + + private: + const db::Netlist *mp_netlist; + }; + +} + +void Netlist::make_top_level_pins (bool sort_by_name) { size_t ntop = top_circuit_count (); for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down () && ntop > 0; ++c, --ntop) { @@ -635,14 +654,25 @@ void Netlist::make_top_level_pins () if (circuit->pin_count () == 0) { + std::vector nets_for_pins; + // create pins for the named nets and connect them for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { if (! n->name ().empty () && n->terminal_count () + n->subcircuit_pin_count () > 0) { - Pin pin = circuit->add_pin (n->name ()); - circuit->connect_pin (pin.id (), n.operator-> ()); + nets_for_pins.push_back (n.operator-> ()); } } + if (sort_by_name) { + std::stable_sort (nets_for_pins.begin (), nets_for_pins.end (), CompareNetByName (this)); + } + + // create pins for the named nets and connect them + for (auto n = nets_for_pins.begin (); n != nets_for_pins.end (); ++n) { + Pin pin = circuit->add_pin ((*n)->name ()); + circuit->connect_pin (pin.id (), *n); + } + } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 958df8b38..73848ec92 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -523,7 +523,7 @@ public: * referenced by subcircuits) into pins. This method can be used before purge to * avoid that purge will remove nets which are directly connecting to subcircuits. */ - void make_top_level_pins (); + void make_top_level_pins (bool sort_by_name = true); /** * @brief Purge unused nets, circuits and subcircuits diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index 8b2cd1baf..d19bce192 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -271,7 +271,7 @@ public: { std::string s = Obj::to_string (); s += " props="; - s += db::properties (properties_id ()).to_dict_var ().to_string (); + s += db::properties (properties_id ()).to_dict_var ().to_parsable_string (); return s; } diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 236a3f38c..33c14497b 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/dbTrans.h b/src/db/db/dbTrans.h index 9c93a54bc..cf50a7cb4 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -1625,6 +1625,17 @@ public: m_mag = s.is_mirror () ? -1.0 : 1.0; } + /** + * @brief Native constructor + * + * This constructor creates the objects from the native parameters (max, acos, asin and displacement) + */ + complex_trans (const vector &disp, R asin, R acos, R mag) + : m_u (disp), m_sin (asin), m_cos (acos), m_mag (mag) + { + // .. nothing yet .. + } + /** * @brief Migration constructor from a simple transformation to a complex transformation * diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 80e3bdfbb..f5572fa16 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -26,6 +26,7 @@ #include "dbPoint.h" #include "dbBox.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -50,6 +51,18 @@ struct box_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C world () { return C::world (); @@ -533,6 +546,19 @@ struct box_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a box object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this box\n" + "\n" + "This string can be turned into a box again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } }; diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 6e0718fe3..07ca93e64 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/gsiDeclDbEdge.cc b/src/db/db/gsiDeclDbEdge.cc index c7b348f69..c3387a5c4 100644 --- a/src/db/db/gsiDeclDbEdge.cc +++ b/src/db/db/gsiDeclDbEdge.cc @@ -26,6 +26,7 @@ #include "dbPoint.h" #include "dbEdge.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -53,6 +54,18 @@ struct edge_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -446,6 +459,19 @@ struct edge_defs "\n" "The DBU argument has been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an edge object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this edge\n" + "\n" + "This string can be turned into an edge again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method ("is_parallel?", &C::parallel, gsi::arg ("e"), "@brief Test for being parallel\n" "\n" diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index 2d9e7a86c..8c7ac6595 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -26,6 +26,7 @@ #include "dbEdgePair.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -50,6 +51,18 @@ struct edge_pair_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -167,6 +180,19 @@ struct edge_pair_defs "\n" "The DBU argument has been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an edge pair object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this edge pair\n" + "\n" + "This string can be turned into an edge pair again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method ("bbox", &C::bbox, "@brief Gets the bounding box of the edge pair\n" ) + diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 9d76ae5e9..46329ec13 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_into", &flatten_into, gsi::arg ("source_cell_index"), gsi::arg ("target_cell_index"), gsi::arg ("trans"), gsi::arg ("levels"), + 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", 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/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index e5f03e725..b8dedf12e 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -23,1998 +23,13 @@ #include "gsiDecl.h" #include "dbNetlist.h" #include "dbNetlistWriter.h" -#include "dbNetlistSpiceWriter.h" #include "dbNetlistReader.h" -#include "dbNetlistSpiceReader.h" -#include "dbNetlistSpiceReaderDelegate.h" -#include "tlException.h" -#include "tlInternational.h" #include "tlStream.h" #include "tlGlobPattern.h" namespace gsi { -static std::vector property_keys (const db::NetlistObject *object) -{ - std::vector v; - for (db::NetlistObject::property_iterator p = object->begin_properties (); p != object->end_properties (); ++p) { - v.push_back (p->first); - } - return v; -} - -Class decl_dbNetlistObject ("db", "NetlistObject", - gsi::method ("property", &db::NetlistObject::property, gsi::arg ("key"), - "@brief Gets the property value for the given key or nil if there is no value with this key." - ) + - gsi::method ("set_property", &db::NetlistObject::set_property, gsi::arg ("key"), gsi::arg ("value"), - "@brief Sets the property value for the given key.\n" - "Use a nil value to erase the property with this key." - ) + - gsi::method_ext ("property_keys", &property_keys, - "@brief Gets the keys for the properties stored in this object." - ), - "@brief The base class for some netlist objects.\n" - "The main purpose of this class is to supply user properties for netlist objects.\n" - "\n" - "This class has been introduced in version 0.26.2" -); - -Class decl_dbPin (decl_dbNetlistObject, "db", "Pin", - gsi::method ("id", &db::Pin::id, - "@brief Gets the ID of the pin.\n" - ) + - gsi::method ("name", &db::Pin::name, - "@brief Gets the name of the pin.\n" - ) + - gsi::method ("expanded_name", &db::Pin::expanded_name, - "@brief Gets the expanded name of the pin.\n" - "The expanded name is the name or a generic identifier made from the ID if the name is empty." - ), - "@brief A pin of a circuit.\n" - "Pin objects are used to describe the outgoing pins of " - "a circuit. To create a new pin of a circuit, use \\Circuit#create_pin.\n" - "\n" - "This class has been added in version 0.26." -); - -static void device_connect_terminal_by_name (db::Device *device, const std::string &terminal_name, db::Net *net) -{ - if (! device->device_class ()) { - throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); - } - size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); - device->connect_terminal (terminal_id, net); -} - -static void device_disconnect_terminal (db::Device *device, size_t terminal_id) -{ - device->connect_terminal (terminal_id, 0); -} - -static void device_disconnect_terminal_by_name (db::Device *device, const std::string &terminal_name) -{ - device_connect_terminal_by_name (device, terminal_name, 0); -} - -static size_t get_device_index (const db::DeviceReconnectedTerminal *obj) -{ - return obj->device_index; -} - -static void set_device_index (db::DeviceReconnectedTerminal *obj, size_t device_index) -{ - obj->device_index = device_index; -} - -static size_t get_other_terminal_id (const db::DeviceReconnectedTerminal *obj) -{ - return obj->other_terminal_id; -} - -static void set_other_terminal_id (db::DeviceReconnectedTerminal *obj, unsigned int other_terminal_id) -{ - obj->other_terminal_id = other_terminal_id; -} - -Class decl_dbDeviceReconnectedTerminal ("db", "DeviceReconnectedTerminal", - gsi::method_ext ("device_index=", &set_device_index, gsi::arg ("device_index"), - "@brief The device abstract index setter.\n" - "See the class description for details." - ) + - gsi::method_ext ("device_index", &get_device_index, - "@brief The device abstract index getter.\n" - "See the class description for details." - ) + - gsi::method_ext ("other_terminal_id=", &set_other_terminal_id, gsi::arg ("other_terminal_id"), - "@brief The setter for the abstract's connected terminal.\n" - "See the class description for details." - ) + - gsi::method_ext ("other_terminal_id", &get_other_terminal_id, - "@brief The getter for the abstract's connected terminal.\n" - "See the class description for details." - ), - "@brief Describes a terminal rerouting in combined devices.\n" - "Combined devices are implemented as a generalization of the device abstract concept in \\Device. For " - "combined devices, multiple \\DeviceAbstract references are present. To support different combination schemes, " - "device-to-abstract routing is supported. Parallel combinations will route all outer terminals to corresponding " - "terminals of all device abstracts (because of terminal swapping these may be different ones).\n" - "\n" - "This object describes one route to an abstract's terminal. The device index is 0 for the main device abstract and " - "1 for the first combined device abstract.\n" - "\n" - "This class has been introduced in version 0.26.\n" -); - -static const db::DeviceAbstract *get_device_abstract (const db::DeviceAbstractRef *obj) -{ - return obj->device_abstract; -} - -static void set_device_abstract (db::DeviceAbstractRef *obj, const db::DeviceAbstract *device_abstract) -{ - obj->device_abstract = device_abstract; -} - -static db::DCplxTrans get_trans (const db::DeviceAbstractRef *obj) -{ - return obj->trans; -} - -static void set_trans (db::DeviceAbstractRef *obj, const db::DCplxTrans &trans) -{ - obj->trans = trans; -} - -Class decl_dbDeviceAbstractRef ("db", "DeviceAbstractRef", - gsi::method_ext ("device_abstract=", &set_device_abstract, gsi::arg ("device_abstract"), - "@brief The setter for the device abstract reference.\n" - "See the class description for details." - ) + - gsi::method_ext ("device_abstract", &get_device_abstract, - "@brief The getter for the device abstract reference.\n" - "See the class description for details." - ) + - gsi::method_ext ("trans=", &set_trans, gsi::arg ("tr"), - "@brief The setter for the relative transformation of the instance.\n" - "See the class description for details." - ) + - gsi::method_ext ("trans", &get_trans, - "@brief The getter for the relative transformation of the instance.\n" - "See the class description for details." - ), - "@brief Describes an additional device abstract reference for combined devices.\n" - "Combined devices are implemented as a generalization of the device abstract concept in \\Device. For " - "combined devices, multiple \\DeviceAbstract references are present. This class describes such an " - "additional reference. A reference is a pointer to an abstract plus a transformation by which the abstract " - "is transformed geometrically as compared to the first (initial) abstract.\n" - "\n" - "This class has been introduced in version 0.26.\n" -); - -static bool is_combined_device (const db::Device *device) -{ - return ! device->reconnected_terminals ().empty (); -} - -static std::vector empty; - -static std::vector::const_iterator begin_reconnected_terminals_for (const db::Device *device, size_t terminal_id) -{ - const std::vector *ti = device->reconnected_terminals_for ((unsigned int) terminal_id); - if (! ti) { - return empty.begin (); - } else { - return ti->begin (); - } -} - -static std::vector::const_iterator end_reconnected_terminals_for (const db::Device *device, size_t terminal_id) -{ - const std::vector *ti = device->reconnected_terminals_for ((unsigned int) terminal_id); - if (! ti) { - return empty.end (); - } else { - return ti->end (); - } -} - -static void clear_reconnected_terminals (db::Device *device) -{ - device->reconnected_terminals ().clear (); -} - -static void add_reconnected_terminals (db::Device *device, size_t outer_terminal, const db::DeviceReconnectedTerminal &t) -{ - device->reconnected_terminals () [(unsigned int) outer_terminal].push_back (t); -} - -static std::vector::const_iterator begin_other_abstracts (const db::Device *device) -{ - return device->other_abstracts ().begin (); -} - -static std::vector::const_iterator end_other_abstracts (const db::Device *device) -{ - return device->other_abstracts ().end (); -} - -static void clear_other_abstracts (db::Device *device) -{ - device->other_abstracts ().clear (); -} - -static void add_other_abstracts (db::Device *device, const db::DeviceAbstractRef &ref) -{ - device->other_abstracts ().push_back (ref); -} - -static const db::Net *net_for_terminal_by_name_const (const db::Device *device, const std::string &name) -{ - if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { - return 0; - } else { - return device->net_for_terminal (device->device_class ()->terminal_id_for_name (name)); - } -} - -static db::Net *net_for_terminal_by_name (db::Device *device, const std::string &name) -{ - if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { - return 0; - } else { - return device->net_for_terminal (device->device_class ()->terminal_id_for_name (name)); - } -} - -static const db::NetTerminalRef *terminal_ref_by_name_const (const db::Device *device, const std::string &name) -{ - if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { - return 0; - } else { - return device->terminal_ref_for_terminal (device->device_class ()->terminal_id_for_name (name)); - } -} - -static db::NetTerminalRef *terminal_ref_by_name (db::Device *device, const std::string &name) -{ - if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { - return 0; - } else { - return device->terminal_ref_for_terminal (device->device_class ()->terminal_id_for_name (name)); - } -} - -Class decl_dbDevice (decl_dbNetlistObject, "db", "Device", - gsi::method ("device_class", &db::Device::device_class, - "@brief Gets the device class the device belongs to.\n" - ) + - gsi::method ("device_abstract", &db::Device::device_abstract, - "@brief Gets the device abstract for this device instance.\n" - "See \\DeviceAbstract for more details.\n" - ) + - gsi::method ("device_abstract=", &db::Device::set_device_abstract, gsi::arg ("device_abstract"), - "@hide\n" - "Provided for test purposes mainly. Be careful with pointers!" - ) + - gsi::method_ext ("is_combined_device?", &is_combined_device, - "@brief Returns true, if the device is a combined device.\n" - "Combined devices feature multiple device abstracts and device-to-abstract terminal connections.\n" - "See \\each_reconnected_terminal and \\each_combined_abstract for more details.\n" - ) + - gsi::iterator_ext ("each_reconnected_terminal_for", &begin_reconnected_terminals_for, &end_reconnected_terminals_for, gsi::arg ("terminal_id"), - "@brief Iterates over the reconnected terminal specifications for a given outer terminal.\n" - "This feature applies to combined devices. This iterator will deliver all device-to-abstract terminal reroutings.\n" - ) + - gsi::method_ext ("clear_reconnected_terminals", &clear_reconnected_terminals, - "@hide\n" - "Provided for test purposes mainly." - ) + - gsi::method_ext ("add_reconnected_terminal_for", &add_reconnected_terminals, gsi::arg ("outer_terminal"), gsi::arg ("descriptor"), - "@hide\n" - "Provided for test purposes mainly." - ) + - gsi::iterator_ext ("each_combined_abstract", &begin_other_abstracts, &end_other_abstracts, - "@brief Iterates over the combined device specifications.\n" - "This feature applies to combined devices. This iterator will deliver all device abstracts present in addition to the default device abstract.\n" - ) + - gsi::method_ext ("clear_combined_abstracts", &clear_other_abstracts, - "@hide\n" - "Provided for test purposes mainly." - ) + - gsi::method_ext ("add_combined_abstract", &add_other_abstracts, gsi::arg ("ref"), - "@hide\n" - "Provided for test purposes mainly." - ) + - gsi::method ("circuit", (const db::Circuit *(db::Device::*) () const) &db::Device::circuit, - "@brief Gets the circuit the device lives in." - ) + - gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit, - "@brief Gets the circuit the device lives in (non-const version)." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("id", &db::Device::id, - "@brief Gets the device ID.\n" - "The ID is a unique integer which identifies the device.\n" - "It can be used to retrieve the device from the circuit using \\Circuit#device_by_id.\n" - "When assigned, the device ID is not 0.\n" - ) + - gsi::method ("name=", &db::Device::set_name, gsi::arg ("name"), - "@brief Sets the name of the device.\n" - "Device names are used to name a device inside a netlist file. " - "Device names should be unique within a circuit." - ) + - gsi::method ("name", &db::Device::name, - "@brief Gets the name of the device.\n" - ) + - gsi::method ("trans=", &db::Device::set_trans, gsi::arg ("t"), - "@brief Sets the location of the device.\n" - "The device location is essentially describing the position of the device. The position is typically the center of some " - "recognition shape. In this case the transformation is a plain displacement to the center of this shape." - ) + - gsi::method ("trans", &db::Device::trans, - "@brief Gets the location of the device.\n" - "See \\trans= for details about this method." - ) + - gsi::method ("expanded_name", &db::Device::expanded_name, - "@brief Gets the expanded name of the device.\n" - "The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. " - ) + - gsi::method ("net_for_terminal", (const db::Net *(db::Device::*) (size_t) const) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), - "@brief Gets the net connected to the specified terminal.\n" - "If the terminal is not connected, nil is returned for the net." - ) + - gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), - "@brief Gets the net connected to the specified terminal (non-const version).\n" - "If the terminal is not connected, nil is returned for the net." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method_ext ("net_for_terminal", &net_for_terminal_by_name_const, gsi::arg ("terminal_name"), - "@brief Gets the net connected to the specified terminal.\n" - "If the terminal is not connected, nil is returned for the net." - "\n\n" - "This convenience method has been introduced in version 0.27.3.\n" - ) + - gsi::method_ext ("net_for_terminal", &net_for_terminal_by_name, gsi::arg ("terminal_name"), - "@brief Gets the net connected to the specified terminal (non-const version).\n" - "If the terminal is not connected, nil is returned for the net." - "\n\n" - "This convenience method has been introduced in version 0.27.3.\n" - ) + - gsi::method ("terminal_ref", (const db::NetTerminalRef *(db::Device::*) (size_t) const) &db::Device::terminal_ref_for_terminal, gsi::arg ("terminal_id"), - "@brief Gets the terminal refeference for a specific terminal.\n" - "The terminal ref is the connector between a net and a device terminal. " - "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " - "If the terminal is not connected, nil is returned for the net.\n" - "\n" - "This method has been introduced in version 0.30." - ) + - gsi::method ("terminal_ref", (db::NetTerminalRef *(db::Device::*) (size_t)) &db::Device::terminal_ref_for_terminal, gsi::arg ("terminal_id"), - "@brief Gets the terminal refeference for a specific terminal (non-const version).\n" - "The terminal ref is the connector between a net and a device terminal. " - "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " - "If the terminal is not connected, nil is returned for the net.\n" - "\n" - "This method has been introduced in version 0.30." - ) + - gsi::method_ext ("terminal_ref", &terminal_ref_by_name_const, gsi::arg ("terminal_name"), - "@brief Gets the terminal refeference for a specific terminal where the terminal is given by name.\n" - "The terminal ref is the connector between a net and a device terminal. " - "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " - "If the terminal is not connected, nil is returned for the net.\n" - "\n" - "This method has been introduced in version 0.30." - ) + - gsi::method_ext ("terminal_ref", &terminal_ref_by_name, gsi::arg ("terminal_name"), - "@brief Gets the terminal refeference for a specific terminal where the terminal is given by name (non-const version).\n" - "The terminal ref is the connector between a net and a device terminal. " - "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " - "If the terminal is not connected, nil is returned for the net.\n" - "\n" - "This method has been introduced in version 0.30." - ) + - gsi::method ("connect_terminal", &db::Device::connect_terminal, gsi::arg ("terminal_id"), gsi::arg ("net"), - "@brief Connects the given terminal to the specified net.\n" - ) + - gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), - "@brief Disconnects the given terminal from any net.\n" - "If the terminal has been connected to a global, this connection will be disconnected too." - ) + - gsi::method_ext ("connect_terminal", &device_connect_terminal_by_name, gsi::arg ("terminal_name"), gsi::arg ("net"), - "@brief Connects the given terminal to the specified net.\n" - "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised.\n" - "If the terminal has been connected to a global net, it will be disconnected from there." - ) + - gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal_by_name, gsi::arg ("terminal_name"), - "@brief Disconnects the given terminal from any net.\n" - "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." - ) + - gsi::method ("parameter", (const tl::Variant &(db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), - "@brief Gets the parameter value for the given parameter ID." - ) + - gsi::method ("set_parameter", (void (db::Device::*) (size_t, const tl::Variant &)) &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"), - "@brief Sets the parameter value for the given parameter ID." - ) + - gsi::method ("parameter", (const tl::Variant &(db::Device::*) (const std::string &) const) &db::Device::parameter_value, gsi::arg ("param_name"), - "@brief Gets the parameter value for the given parameter name.\n" - "If the parameter name is not valid, an exception is thrown." - ) + - gsi::method ("set_parameter", (void (db::Device::*) (const std::string &, const tl::Variant &)) &db::Device::set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"), - "@brief Sets the parameter value for the given parameter name.\n" - "If the parameter name is not valid, an exception is thrown." - ) + - gsi::method ("set_parameter_create", (void (db::Device::*) (const std::string &, const tl::Variant &, bool, const tl::Variant &)) &db::Device::set_parameter_value_create, gsi::arg ("param_name"), gsi::arg ("value"), gsi::arg ("primary", true), gsi::arg ("default_value", tl::Variant (0.0), "none"), - "@brief Sets the parameter value for the given parameter name and creates a new parameter if no parameter is present with this name.\n" - "If no parameter with the given name exists in the device class's parameter table, a new parameter is created. " - "\\primary indicates that this parameter will be a primary one and \\default_value is the default value for this parameter. " - "The default value also implicitly defines the type of the parameter. A float value will create a float-type parameter, a string value for default will create a string-type parameter.\n" - "\n" - "This method has been introduced in version 0.31.0." - ), - "@brief A device inside a circuit.\n" - "Device object represent atomic devices such as resistors, diodes or transistors. " - "The \\Device class represents a particular device with specific parameters. " - "The type of device is represented by a \\DeviceClass object. Device objects " - "live in \\Circuit objects, the device class objects live in the \\Netlist object.\n" - "\n" - "Devices connect to nets through terminals. Terminals are described by a terminal ID which is " - "essentially the zero-based index of the terminal. Terminal definitions can be " - "obtained from the device class using the \\DeviceClass#terminal_definitions method.\n" - "\n" - "Devices connect to nets through the \\Device#connect_terminal method. " - "Device terminals can be disconnected using \\Device#disconnect_terminal.\n" - "\n" - "Device objects are created inside a circuit with \\Circuit#create_device.\n" - "\n" - "This class has been added in version 0.26." -); - -Class decl_dbDeviceAbstract ("db", "DeviceAbstract", - gsi::method ("netlist", (const db::Netlist *(db::DeviceAbstract::*) () const) &db::DeviceAbstract::netlist, - "@brief Gets the netlist the device abstract lives in." - ) + - gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist, - "@brief Gets the netlist the device abstract lives in (non-const version)." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("device_class", &db::DeviceAbstract::device_class, - "@brief Gets the device class of the device." - ) + - gsi::method ("name=", &db::DeviceAbstract::set_name, gsi::arg ("name"), - "@brief Sets the name of the device abstract.\n" - "Device names are used to name a device abstract inside a netlist file. " - "Device names should be unique within a netlist." - ) + - gsi::method ("name", &db::DeviceAbstract::name, - "@brief Gets the name of the device abstract.\n" - ) + - gsi::method ("cell_index", &db::DeviceAbstract::cell_index, - "@brief Gets the cell index of the device abstract.\n" - "This is the cell that represents the device." - ) + - gsi::method ("cluster_id_for_terminal", &db::DeviceAbstract::cluster_id_for_terminal, gsi::arg ("terminal_id"), - "@brief Gets the cluster ID for the given terminal.\n" - "The cluster ID links the terminal to geometrical shapes within the clusters of the cell (see \\cell_index)" - ), - "@brief A geometrical device abstract\n" - "This class represents the geometrical model for the device. It links into the extracted layout " - "to a cell which holds the terminal shapes for the device.\n" - "\n" - "This class has been added in version 0.26." -); - -static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) -{ - if (pin) { - subcircuit->connect_pin (pin->id (), net); - } -} - -static void subcircuit_disconnect_pin (db::SubCircuit *subcircuit, size_t pin_id) -{ - subcircuit->connect_pin (pin_id, 0); -} - -static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin) -{ - if (pin) { - subcircuit->connect_pin (pin->id (), 0); - } -} - -Class decl_dbSubCircuit (decl_dbNetlistObject, "db", "SubCircuit", - gsi::method ("circuit_ref", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit_ref, - "@brief Gets the circuit referenced by the subcircuit.\n" - ) + - gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref, - "@brief Gets the circuit referenced by the subcircuit (non-const version).\n" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("circuit", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit, - "@brief Gets the circuit the subcircuit lives in.\n" - "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." - ) + - gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, - "@brief Gets the circuit the subcircuit lives in (non-const version).\n" - "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("id", &db::SubCircuit::id, - "@brief Gets the subcircuit ID.\n" - "The ID is a unique integer which identifies the subcircuit.\n" - "It can be used to retrieve the subcircuit from the circuit using \\Circuit#subcircuit_by_id.\n" - "When assigned, the subcircuit ID is not 0.\n" - ) + - gsi::method ("name=", &db::SubCircuit::set_name, gsi::arg ("name"), - "@brief Sets the name of the subcircuit.\n" - "SubCircuit names are used to name a subcircuits inside a netlist file. " - "SubCircuit names should be unique within a circuit." - ) + - gsi::method ("name", &db::SubCircuit::name, - "@brief Gets the name of the subcircuit.\n" - ) + - gsi::method ("expanded_name", &db::SubCircuit::expanded_name, - "@brief Gets the expanded name of the subcircuit.\n" - "The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. " - ) + - gsi::method ("net_for_pin", (const db::Net *(db::SubCircuit::*) (size_t) const) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), - "@brief Gets the net connected to the specified pin of the subcircuit.\n" - "If the pin is not connected, nil is returned for the net." - ) + - gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), - "@brief Gets the net connected to the specified pin of the subcircuit (non-const version).\n" - "If the pin is not connected, nil is returned for the net." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("connect_pin", &db::SubCircuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), - "@brief Connects the given pin to the specified net.\n" - ) + - gsi::method_ext ("connect_pin", &gsi::subcircuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), - "@brief Connects the given pin to the specified net.\n" - "This version takes a \\Pin reference instead of a pin ID." - ) + - gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin, gsi::arg ("pin_id"), - "@brief Disconnects the given pin from any net.\n" - ) + - gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"), - "@brief Disconnects the given pin from any net.\n" - "This version takes a \\Pin reference instead of a pin ID." - ) + - gsi::method ("trans", &db::SubCircuit::trans, - "@brief Gets the physical transformation for the subcircuit.\n" - "\n" - "This property applies to subcircuits derived from a layout. It specifies the " - "placement of the respective cell.\n" - "\n" - "This property has been introduced in version 0.27." - ) + - gsi::method ("trans=", &db::SubCircuit::set_trans, gsi::arg ("trans"), - "@brief Sets the physical transformation for the subcircuit.\n" - "\n" - "See \\trans for details about this property.\n" - "\n" - "This property has been introduced in version 0.27." - ), - "@brief A subcircuit inside a circuit.\n" - "Circuits may instantiate other circuits as subcircuits similar to cells " - "in layouts. Such an instance is a subcircuit. A subcircuit refers to a " - "circuit implementation (a \\Circuit object), and presents connections through " - "pins. The pins of a subcircuit can be connected to nets. The subcircuit pins " - "are identical to the outgoing pins of the circuit the subcircuit refers to.\n" - "\n" - "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " - "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n" - "\n" - "Subcircuit objects are created inside a circuit with \\Circuit#create_subcircuit.\n" - "\n" - "This class has been added in version 0.26." -); - -Class decl_dbNetTerminalRef ("db", "NetTerminalRef", - gsi::method ("terminal_id", &db::NetTerminalRef::terminal_id, - "@brief Gets the ID of the terminal of the device the connection is made to." - ) + - gsi::method ("device", (const db::Device *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device, - "@brief Gets the device reference.\n" - "Gets the device object that this connection is made to." - ) + - gsi::method ("device", (db::Device *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device, - "@brief Gets the device reference (non-const version).\n" - "Gets the device object that this connection is made to." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("net", (const db::Net *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::net, - "@brief Gets the net this terminal reference is attached to." - ) + - gsi::method ("net", (db::Net *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::net, - "@brief Gets the net this terminal reference is attached to (non-const version)." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("device_class", (const db::DeviceClass *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device_class, - "@brief Gets the class of the device which is addressed." - ) + - gsi::method ("terminal_def", (const db::DeviceTerminalDefinition *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::terminal_def, - "@brief Gets the terminal definition of the terminal that is connected" - ), - "@brief A connection to a terminal of a device.\n" - "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" - "\n" - "This class has been added in version 0.26." -); - -Class decl_dbNetPinRef ("db", "NetPinRef", - gsi::method ("pin_id", &db::NetPinRef::pin_id, - "@brief Gets the ID of the pin the connection is made to." - ) + - gsi::method ("pin", &db::NetPinRef::pin, - "@brief Gets the \\Pin object of the pin the connection is made to." - ) + - gsi::method ("net", (const db::Net *(db::NetPinRef::*) () const) &db::NetPinRef::net, - "@brief Gets the net this pin reference is attached to." - ) + - gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net, - "@brief Gets the net this pin reference is attached to (non-const version)." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ), - "@brief A connection to an outgoing pin of the circuit.\n" - "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" - "\n" - "This class has been added in version 0.26." -); - -Class decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitPinRef", - gsi::method ("pin_id", &db::NetSubcircuitPinRef::pin_id, - "@brief Gets the ID of the pin the connection is made to." - ) + - gsi::method ("pin", &db::NetSubcircuitPinRef::pin, - "@brief Gets the \\Pin object of the pin the connection is made to." - ) + - gsi::method ("subcircuit", (const db::SubCircuit *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::subcircuit, - "@brief Gets the subcircuit reference.\n" - "This attribute indicates the subcircuit the net attaches to. The " - "subcircuit lives in the same circuit than the net. " - ) + - gsi::method ("subcircuit", (db::SubCircuit *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::subcircuit, - "@brief Gets the subcircuit reference (non-const version).\n" - "This attribute indicates the subcircuit the net attaches to. The " - "subcircuit lives in the same circuit than the net. " - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("net", (const db::Net *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::net, - "@brief Gets the net this pin reference is attached to." - ) + - gsi::method ("net", (db::Net *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::net, - "@brief Gets the net this pin reference is attached to (non-const version)." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ), - "@brief A connection to a pin of a subcircuit.\n" - "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" - "\n" - "This class has been added in version 0.26." -); - -Class decl_dbNet (decl_dbNetlistObject, "db", "Net", - gsi::method ("circuit", (db::Circuit *(db::Net::*) ()) &db::Net::circuit, - "@brief Gets the circuit the net lives in." - ) + - gsi::method ("clear", &db::Net::clear, - "@brief Clears the net." - ) + - gsi::method ("name=", &db::Net::set_name, gsi::arg ("name"), - "@brief Sets the name of the net.\n" - "The name of the net is used for naming the net in schematic files for example. " - "The name of the net has to be unique." - ) + - gsi::method ("name", &db::Net::name, - "@brief Gets the name of the net.\n" - "See \\name= for details about the name." - ) + - gsi::method ("qname|to_s", &db::Net::qname, - "@brief Gets the qualified name.\n" - "The qualified name is like the expanded name, but the circuit's name is preceded\n" - "(i.e. 'CIRCUIT:NET') if available.\n" - ) + - gsi::method ("expanded_name", &db::Net::expanded_name, - "@brief Gets the expanded name of the net.\n" - "The expanded name takes the name of the net. If the name is empty, the cluster ID will be used to build a name. " - ) + - gsi::method ("cluster_id=", &db::Net::set_cluster_id, gsi::arg ("id"), - "@brief Sets the cluster ID of the net.\n" - "The cluster ID connects the net with a layout cluster. It is set when " - "the net is extracted from a layout." - ) + - gsi::method ("cluster_id", &db::Net::cluster_id, - "@brief Gets the cluster ID of the net.\n" - "See \\cluster_id= for details about the cluster ID." - ) + - gsi::iterator ("each_pin", gsi::return_reference (), (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::begin_pins, (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::end_pins, - "@brief Iterates over all outgoing pins the net connects.\n" - "Pin connections are described by \\NetPinRef objects. Pin connections " - "are connections to outgoing pins of the circuit the net lives in." - ) + - gsi::iterator ("each_pin", gsi::return_reference (), (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins, - "@brief Iterates over all outgoing pins the net connects (non-const version).\n" - "Pin connections are described by \\NetPinRef objects. Pin connections " - "are connections to outgoing pins of the circuit the net lives in." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::iterator ("each_subcircuit_pin", gsi::return_reference (), (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::begin_subcircuit_pins, (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::end_subcircuit_pins, - "@brief Iterates over all subcircuit pins the net connects.\n" - "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " - "connections to specific pins of subcircuits." - ) + - gsi::iterator ("each_subcircuit_pin", gsi::return_reference (), (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::begin_subcircuit_pins, (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::end_subcircuit_pins, - "@brief Iterates over all subcircuit pins the net connects (non-const version).\n" - "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " - "connections to specific pins of subcircuits." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::iterator ("each_terminal", gsi::return_reference (), (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::begin_terminals, (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::end_terminals, - "@brief Iterates over all terminals the net connects.\n" - "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " - "objects." - ) + - gsi::iterator ("each_terminal", gsi::return_reference (), (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals, - "@brief Iterates over all terminals the net connects (non-const version).\n" - "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " - "objects." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("is_floating?", &db::Net::is_floating, - "@brief Returns true, if the net is floating.\n" - "Floating nets are those which don't have any device or subcircuit on it and are not connected through a pin." - ) + - gsi::method ("is_passive?", &db::Net::is_passive, - "@brief Returns true, if the net is passive.\n" - "Passive nets don't have devices or subcircuits on it. They can be exposed through a pin.\n" - "\\is_floating? implies \\is_passive?.\n" - "\n" - "This method has been introduced in version 0.26.1.\n" - ) + - gsi::method ("is_internal?", &db::Net::is_internal, - "@brief Returns true, if the net is an internal net.\n" - "Internal nets are those which connect exactly two terminals and nothing else (pin_count = 0 and terminal_count == 2)." - ) + - gsi::method ("pin_count", &db::Net::pin_count, - "@brief Returns the number of outgoing pins connected by this net.\n" - ) + - gsi::method ("subcircuit_pin_count", &db::Net::subcircuit_pin_count, - "@brief Returns the number of subcircuit pins connected by this net.\n" - ) + - gsi::method ("terminal_count", &db::Net::terminal_count, - "@brief Returns the number of terminals connected by this net.\n" - ), - "@brief A single net.\n" - "A net connects multiple pins or terminals together. Pins are either " - "pin or subcircuits of outgoing pins of the circuit the net lives in. " - "Terminals are connections made to specific terminals of devices.\n" - "\n" - "Net objects are created inside a circuit with \\Circuit#create_net.\n" - "\n" - "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " - "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " - "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " - "disconnect a net from a pin of a subcircuit, use \\SubCircuit#disconnect_pin. " - "To connect a net to a terminal of a device, use \\Device#connect_terminal, to " - "disconnect a net from a terminal of a device, use \\Device#disconnect_terminal.\n" - "\n" - "This class has been added in version 0.26." -); - -static db::DeviceTerminalDefinition *new_terminal_definition (const std::string &name, const std::string &description) -{ - return new db::DeviceTerminalDefinition (name, description); -} - -Class decl_dbDeviceTerminalDefinition ("db", "DeviceTerminalDefinition", - gsi::constructor ("new", &gsi::new_terminal_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), - "@brief Creates a new terminal definition." - ) + - gsi::method ("name", &db::DeviceTerminalDefinition::name, - "@brief Gets the name of the terminal." - ) + - gsi::method ("name=", &db::DeviceTerminalDefinition::set_name, gsi::arg ("name"), - "@brief Sets the name of the terminal." - ) + - gsi::method ("description", &db::DeviceTerminalDefinition::description, - "@brief Gets the description of the terminal." - ) + - gsi::method ("description=", &db::DeviceTerminalDefinition::set_description, gsi::arg ("description"), - "@brief Sets the description of the terminal." - ) + - gsi::method ("id", &db::DeviceTerminalDefinition::id, - "@brief Gets the ID of the terminal.\n" - "The ID of the terminal is used in some places to refer to a specific terminal (e.g. in " - "the \\NetTerminalRef object)." - ), - "@brief A terminal descriptor\n" - "This class is used inside the \\DeviceClass class to describe a terminal of the device.\n" - "\n" - "This class has been added in version 0.26." -); - -static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling, double geo_scaling_exponent) -{ - return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling, geo_scaling_exponent); -} - -Class decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition", - gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0), gsi::arg ("geo_scaling_exponent", 0.0), - "@brief Creates a new parameter definition.\n" - "@param name The name of the parameter\n" - "@param description The human-readable description\n" - "@param default_value The initial value\n" - "@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\n" - "@param si_scaling The scaling factor to SI units\n" - "@param geo_scaling_exponent Indicates how the parameter scales with geometrical scaling (0: no scaling, 1.0: linear, 2.0: quadratic)\n" - ) + - gsi::method ("name", &db::DeviceParameterDefinition::name, - "@brief Gets the name of the parameter." - ) + - gsi::method ("name=", &db::DeviceParameterDefinition::set_name, gsi::arg ("name"), - "@brief Sets the name of the parameter." - ) + - gsi::method ("description", &db::DeviceParameterDefinition::description, - "@brief Gets the description of the parameter." - ) + - gsi::method ("description=", &db::DeviceParameterDefinition::set_description, gsi::arg ("description"), - "@brief Sets the description of the parameter." - ) + - gsi::method ("default_value", &db::DeviceParameterDefinition::default_value, - "@brief Gets the default value of the parameter." - ) + - gsi::method ("default_value=", &db::DeviceParameterDefinition::set_default_value, gsi::arg ("default_value"), - "@brief Sets the default value of the parameter.\n" - "The default value is used to initialize parameters of \\Device objects." - ) + - gsi::method ("is_primary?", &db::DeviceParameterDefinition::is_primary, - "@brief Gets a value indicating whether the parameter is a primary parameter\n" - "See \\is_primary= for details about this predicate." - ) + - gsi::method ("is_primary=", &db::DeviceParameterDefinition::set_is_primary, gsi::arg ("primary"), - "@brief Sets a value indicating whether the parameter is a primary parameter\n" - "If this flag is set to true (the default), the parameter is considered a primary parameter.\n" - "Only primary parameters are compared by default.\n" - ) + - gsi::method ("si_scaling", &db::DeviceParameterDefinition::si_scaling, - "@brief Gets the scaling factor to SI units.\n" - "For parameters in micrometers - for example W and L of MOS devices - this factor can be set to 1e-6 to reflect " - "the unit. SI unit scaling is only applied to parameters of the 'float' type. It is not applied to integer-type " - "or string parameters." - ) + - gsi::method ("si_scaling=", &db::DeviceParameterDefinition::set_si_scaling, gsi::arg ("flag"), - "@brief Sets the scaling factor to SI units.\n" - "See \\si_scaling for details.\n" - "\n" - "This setter has been added in version 0.28.6." - ) + - gsi::method ("geo_scaling_exponent", &db::DeviceParameterDefinition::geo_scaling_exponent, - "@brief Gets the geometry scaling exponent.\n" - "This value is used when applying '.options scale' in the SPICE reader for example. " - "It is zero for 'no scaling', 1.0 for linear scaling and 2.0 for quadratic scaling.\n" - "Geometric scaling is only applied to parameters of the 'float' type. It is not applied to integer-type " - "or string parameters.\n" - "\n" - "This attribute has been added in version 0.28.6." - ) + - gsi::method ("geo_scaling_exponent=", &db::DeviceParameterDefinition::set_geo_scaling_exponent, gsi::arg ("expo"), - "@brief Sets the geometry scaling exponent.\n" - "See \\geo_scaling_exponent for details.\n" - "\n" - "This attribute has been added in version 0.28.6." - ) + - gsi::method ("id", &db::DeviceParameterDefinition::id, - "@brief Gets the ID of the parameter.\n" - "The ID of the parameter is used in some places to refer to a specific parameter (e.g. in " - "the \\NetParameterRef object)." - ), - "@brief A parameter descriptor\n" - "This class is used inside the \\DeviceClass class to describe a parameter of the device.\n" - "\n" - "This class has been added in version 0.26." -); - -namespace -{ - -/** - * @brief A DeviceParameterCompare implementation that allows reimplementation of the virtual methods - */ -class GenericDeviceParameterCompare - : public db::EqualDeviceParameters -{ -public: - GenericDeviceParameterCompare () - : db::EqualDeviceParameters () - { - // .. nothing yet .. - } - - virtual bool less (const db::Device &a, const db::Device &b) const - { - if (cb_less.can_issue ()) { - return cb_less.issue (&db::EqualDeviceParameters::less, a, b); - } else { - return db::EqualDeviceParameters::less (a, b); - } - } - - gsi::Callback cb_less; -}; - -/** - * @brief A DeviceCombiner implementation that allows reimplementation of the virtual methods - */ -class GenericDeviceCombiner - : public db::DeviceCombiner -{ -public: - GenericDeviceCombiner () - : db::DeviceCombiner () - { - // .. nothing yet .. - } - - virtual bool combine_devices (db::Device *a, db::Device *b) const - { - if (cb_combine.can_issue ()) { - return cb_combine.issue (&db::DeviceCombiner::combine_devices, a, b); - } else { - return false; - } - } - - gsi::Callback cb_combine; -}; - -} - -db::EqualDeviceParameters *make_equal_dp (size_t param_id, double absolute, double relative) -{ - return new db::EqualDeviceParameters (param_id, absolute, relative); -} - -db::EqualDeviceParameters *make_ignore_dp (size_t param_id) -{ - return new db::EqualDeviceParameters (param_id, true); -} - -Class decl_dbEqualDeviceParameters ("db", "EqualDeviceParameters", - gsi::constructor ("new", &make_equal_dp, gsi::arg ("param_id"), gsi::arg ("absolute", 0.0), gsi::arg ("relative", 0.0), - "@brief Creates a device parameter comparer for a single parameter.\n" - "'absolute' is the absolute deviation allowed for the parameter values. " - "'relative' is the relative deviation allowed for the parameter values (a value between 0 and 1).\n" - "\n" - "A value of 0 for both absolute and relative deviation means the parameters have to match exactly.\n" - "\n" - "If 'absolute' and 'relative' are both given, their deviations will add to the allowed difference between " - "two parameter values. The relative deviation will be applied to the mean value of both parameter values. " - "For example, when comparing parameter values of 40 and 60, a relative deviation of 0.35 means an absolute " - "deviation of 17.5 (= 0.35 * average of 40 and 60) which does not make both values match." - ) + - gsi::constructor ("ignore", &make_ignore_dp, gsi::arg ("param_id"), - "@brief Creates a device parameter comparer which ignores the parameter.\n" - "\n" - "This specification can be used to make a parameter ignored. Starting with version 0.27.4, all primary parameters " - "are compared. Before 0.27.4, giving a tolerance meant only those parameters are compared. To exclude a primary " - "parameter from the compare, use the 'ignore' specification for that parameter.\n" - "\n" - "This constructor has been introduced in version 0.27.4.\n" - ) + - gsi::method ("+", &db::EqualDeviceParameters::operator+, gsi::arg ("other"), - "@brief Combines two parameters for comparison.\n" - "The '+' operator will join the parameter comparers and produce one that checks the combined parameters.\n" - ) + - gsi::method ("+=", &db::EqualDeviceParameters::operator+, gsi::arg ("other"), - "@brief Combines two parameters for comparison (in-place).\n" - "The '+=' operator will join the parameter comparers and produce one that checks the combined parameters.\n" - ) + - gsi::method ("to_string", &db::EqualDeviceParameters::to_string, "@hide"), - "@brief A device parameter equality comparer.\n" - "Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device " - "class use this comparer:\n" - "\n" - "@code\n" - "# 20nm tolerance for length:\n" - "equal_device_parameters = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_L, 0.02, 0.0)\n" - "# one percent tolerance for width:\n" - "equal_device_parameters += RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_W, 0.0, 0.01)\n" - "# applies the compare delegate:\n" - "netlist.device_class_by_name(\"NMOS\").equal_parameters = equal_device_parameters\n" - "@/code\n" - "\n" - "You can use this class to specify fuzzy equality criteria for the comparison of device parameters in " - "netlist verification or to confine the equality of devices to certain parameters only.\n" - "\n" - "This class has been added in version 0.26." -); - -Class decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare", - gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"), - "@brief Compares the parameters of two devices for a begin less than b.\n" - "Returns true, if the parameters of device a are considered less than those of device b." - "The 'less' implementation needs to ensure strict weak ordering. Specifically, less(a,b) == false and less(b,a) implies that a is equal to b and " - "less(a,b) == true implies that less(b,a) is false and vice versa. If not, an internal error " - "will be encountered on netlist compare." - ), - "@brief A class implementing the comparison of device parameters.\n" - "Reimplement this class to provide a custom device parameter compare scheme.\n" - "Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device " - "class use this comparer.\n" - "\n" - "This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of " - "implementing a custom comparer class.\n" - "\n" - "This class has been added in version 0.26. The 'equal' method has been dropped in 0.27.1 as it can be expressed as !less(a,b) && !less(b,a)." -); - -Class decl_GenericDeviceCombiner ("db", "GenericDeviceCombiner", - gsi::callback ("combine_devices", &GenericDeviceCombiner::combine_devices, &GenericDeviceCombiner::cb_combine, gsi::arg ("device_a"), gsi::arg ("device_b"), - "@brief Combines two devices if possible.\n" - "This method needs to test, whether the two devices can be combined. Both devices " - "are guaranteed to share the same device class. " - "If they cannot be combined, this method shall do nothing and return false. " - "If they can be combined, this method shall reconnect the nets of the first " - "device and entirely disconnect the nets of the second device. " - "The second device will be deleted afterwards. " - ), - "@brief A class implementing the combination of two devices (parallel or serial mode).\n" - "Reimplement this class to provide a custom device combiner.\n" - "Device combination requires 'supports_paralell_combination' or 'supports_serial_combination' to be set " - "to true for the device class. In the netlist device combination step, the algorithm will try to identify " - "devices which can be combined into single devices and use the combiner object to implement the actual " - "joining of such devices.\n" - "\n" - "Attach this object to a device class with \\DeviceClass#combiner= to make the device " - "class use this combiner.\n" - "\n" - "This class has been added in version 0.27.3." -); - -static tl::id_type id_of_device_class (const db::DeviceClass *cls) -{ - return tl::id_of (cls); -} - -static void equal_parameters (db::DeviceClass *cls, db::EqualDeviceParameters *comparer) -{ - cls->set_parameter_compare_delegate (comparer); -} - -static db::EqualDeviceParameters *get_equal_parameters (db::DeviceClass *cls) -{ - return dynamic_cast (cls->parameter_compare_delegate ()); -} - -static void set_combiner (db::DeviceClass *cls, GenericDeviceCombiner *combiner) -{ - cls->set_device_combiner (combiner); -} - -static GenericDeviceCombiner *get_combiner (db::DeviceClass *cls) -{ - return dynamic_cast (cls->device_combiner ()); -} - -static void enable_parameter (db::DeviceClass *cls, size_t id, bool en) -{ - db::DeviceParameterDefinition *pd = cls->parameter_definition_non_const (id); - if (pd) { - pd->set_is_primary (en); - } -} - -static void enable_parameter2 (db::DeviceClass *cls, const std::string &name, bool en) -{ - if (! cls->has_parameter_with_name (name)) { - return; - } - - size_t id = cls->parameter_id_for_name (name); - db::DeviceParameterDefinition *pd = cls->parameter_definition_non_const (id); - if (pd) { - pd->set_is_primary (en); - } -} - -static const db::DeviceParameterDefinition *parameter_definition2 (const db::DeviceClass *cls, const std::string &name) -{ - if (! cls->has_parameter_with_name (name)) { - return 0; - } else { - return cls->parameter_definition (cls->parameter_id_for_name (name)); - } -} - -static void dc_add_terminal_definition (db::DeviceClass *cls, db::DeviceTerminalDefinition *terminal_def) -{ - if (terminal_def) { - *terminal_def = cls->add_terminal_definition (*terminal_def); - } -} - -static void dc_add_parameter_definition (db::DeviceClass *cls, db::DeviceParameterDefinition *parameter_def) -{ - if (parameter_def) { - *parameter_def = cls->add_parameter_definition (*parameter_def); - } -} - -static bool dc_has_spice_profile (const db::DeviceClass *cls, const std::string &name) -{ - return cls->has_spice_profile (name); -} - -static void dc_remove_spice_profile (db::DeviceClass *cls, const std::string &name) -{ - cls->remove_spice_profile (name); -} - -static void dc_clear_spice_profiles (db::DeviceClass *cls) -{ - cls->clear_spice_profiles (); -} - -static std::string dc_get_element_name (const db::DeviceClass *cls, const std::string &name) -{ - return cls->spice_profile (name).element; -} - -static void dc_set_element_name (db::DeviceClass *cls, const std::string &name, const std::string &element) -{ - db::DeviceClass::SpiceProfile profile = cls->spice_profile (name); - profile.element = element; - cls->set_spice_profile (name, profile); -} - -static std::vector dc_get_terminal_order (const db::DeviceClass *cls, const std::string &name) -{ - return cls->spice_profile (name).terminal_order; -} - -static void dc_set_terminal_order (db::DeviceClass *cls, const std::string &name, const std::vector &to) -{ - db::DeviceClass::SpiceProfile profile = cls->spice_profile (name); - profile.terminal_order = to; - cls->set_spice_profile (name, profile); -} - -Class decl_dbDeviceClass ("db", "DeviceClass", - gsi::method ("name", &db::DeviceClass::name, - "@brief Gets the name of the device class." - ) + - gsi::method ("name=", &db::DeviceClass::set_name, gsi::arg ("name"), - "@brief Sets the name of the device class." - ) + - gsi::method_ext ("has_spice_profile?", &dc_has_spice_profile, gsi::arg ("name"), - "@brief Gets a value indicating whether a device class supports a specific SPICE profile.\n" - "SPICE profiles are a way to declare SPICE representations for a specific device. " - "Each device class can support multiple profiles. An empty name declares the default profile, " - "'*' is the fallback profile used when there is no profile with a requested name. Profiles " - "are requested by the SPICE reader or writer, unless they use delegates to implement " - "a customized SPICE representation.\n" - "\n" - "SPICE profiles can declare element names for example or specify the terminal count and order in SPICE files.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method_ext ("remove_spice_profile", &dc_remove_spice_profile, gsi::arg ("name"), - "@brief Removes a SPICE profile with the given name.\n" - "See \\has_spice_profile? for a description of the SPICE profile concept.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method_ext ("clear_spice_profiles", &dc_clear_spice_profiles, - "@brief Removes all SPICE profiles from the device class.\n" - "See \\has_spice_profile? for a description of the SPICE profile concept.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method_ext ("set_terminal_order", &dc_set_terminal_order, gsi::arg ("profile_name"), gsi::arg ("terminal_names"), - "@brief Defines the order the terminals are written to SPICE or read from SPICE.\n" - "See \\has_spice_profile? for a description of the SPICE profile concept.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method_ext ("terminal_order", &dc_get_terminal_order, gsi::arg ("profile_name"), - "@brief Gets a list of strings, defining the order the terminals are written to SPICE or read from SPICE.\n" - "See \\has_spice_profile? for a description of the SPICE profile concept.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method_ext ("set_element_name", &dc_set_element_name, gsi::arg ("profile_name"), gsi::arg ("element_name"), - "@brief Sets the element by which this device is represented in SPICE.\n" - "See \\has_spice_profile? for a description of the SPICE profile concept.\n" - "The element name is 'X' or 'M' for example.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method_ext ("element_name", &dc_get_element_name, gsi::arg ("profile_name"), - "@brief Gets the element by which this device is represented in SPICE.\n" - "See \\has_spice_profile? for a description of the SPICE profile concept.\n" - "\n" - "SPICE profiles have been introduced in version 0.31.0." - ) + - gsi::method ("strict?", &db::DeviceClass::is_strict, - "@brief Gets a value indicating whether this class performs strict terminal mapping\n" - "See \\strict= for details about this attribute." - ) + - gsi::method ("strict=", &db::DeviceClass::set_strict, gsi::arg ("s"), - "@brief Sets a value indicating whether this class performs strict terminal mapping\n" - "\n" - "Classes with this flag set never allow terminal swapping, even if the device symmetry supports that. " - "If two classes are involved in a netlist compare,\n" - "terminal swapping will be disabled if one of the classes is in strict mode.\n" - "\n" - "By default, device classes are not strict and terminal swapping is allowed as far as the " - "device symmetry supports that." - ) + - gsi::method ("description", &db::DeviceClass::description, - "@brief Gets the description text of the device class." - ) + - gsi::method ("description=", &db::DeviceClass::set_description, gsi::arg ("description"), - "@brief Sets the description of the device class." - ) + - gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist, - "@brief Gets the netlist the device class lives in." - ) + - gsi::method_ext ("id", &gsi::id_of_device_class, - "@brief Gets the unique ID of the device class\n" - "The ID is a unique integer that identifies the device class. Use the ID " - "to check for object identity - i.e. to determine whether two devices share the " - "same device class." - ) + - gsi::method ("terminal_definitions", &db::DeviceClass::terminal_definitions, - "@brief Gets the list of terminal definitions of the device.\n" - "See the \\DeviceTerminalDefinition class description for details." - ) + - gsi::method ("terminal_definition", &db::DeviceClass::terminal_definition, gsi::arg ("terminal_id"), - "@brief Gets the terminal definition object for a given ID.\n" - "Terminal definition IDs are used in some places to reference a specific terminal of a device. " - "This method obtains the corresponding definition object." - ) + - gsi::method ("parameter_definitions", &db::DeviceClass::parameter_definitions, - "@brief Gets the list of parameter definitions of the device.\n" - "See the \\DeviceParameterDefinition class description for details." - ) + - gsi::method ("parameter_definition", &db::DeviceClass::parameter_definition, gsi::arg ("parameter_id"), - "@brief Gets the parameter definition object for a given ID.\n" - "Parameter definition IDs are used in some places to reference a specific parameter of a device. " - "This method obtains the corresponding definition object." - ) + - gsi::method_ext ("parameter_definition", ¶meter_definition2, gsi::arg ("parameter_name"), - "@brief Gets the parameter definition object for a given ID.\n" - "Parameter definition IDs are used in some places to reference a specific parameter of a device. " - "This method obtains the corresponding definition object." - "\n" - "This version accepts a parameter name.\n" - "\n" - "This method has been introduced in version 0.27.3.\n" - ) + - gsi::method_ext ("enable_parameter", &enable_parameter, gsi::arg ("parameter_id"), gsi::arg ("enable"), - "@brief Enables or disables a parameter.\n" - "Some parameters are 'secondary' parameters which are extracted but not handled in device compare and are not shown in the netlist browser. " - "For example, the 'W' parameter of the resistor is such a secondary parameter. This method allows turning a parameter in a primary one ('enable') or " - "into a secondary one ('disable').\n" - "\n" - "This method has been introduced in version 0.27.3.\n" - ) + - gsi::method_ext ("enable_parameter", &enable_parameter2, gsi::arg ("parameter_name"), gsi::arg ("enable"), - "@brief Enables or disables a parameter.\n" - "Some parameters are 'secondary' parameters which are extracted but not handled in device compare and are not shown in the netlist browser. " - "For example, the 'W' parameter of the resistor is such a secondary parameter. This method allows turning a parameter in a primary one ('enable') or " - "into a secondary one ('disable').\n" - "\n" - "This version accepts a parameter name.\n" - "\n" - "This method has been introduced in version 0.27.3.\n" - ) + - gsi::method ("has_parameter?", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"), - "@brief Returns true, if the device class has a parameter with the given name.\n" - ) + - gsi::method ("parameter_id", &db::DeviceClass::parameter_id_for_name, gsi::arg ("name"), - "@brief Returns the parameter ID of the parameter with the given name.\n" - "An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check " - "whether the name is a valid parameter name." - ) + - gsi::method ("has_terminal?", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"), - "@brief Returns true, if the device class has a terminal with the given name.\n" - ) + - gsi::method ("terminal_id", &db::DeviceClass::terminal_id_for_name, gsi::arg ("name"), - "@brief Returns the terminal ID of the terminal with the given name.\n" - "An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check " - "whether the name is a valid terminal name." - ) + - gsi::method_ext ("equal_parameters", &get_equal_parameters, - "@brief Gets the device parameter comparer for netlist verification or nil if no comparer is registered.\n" - "See \\equal_parameters= for the setter.\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method_ext ("equal_parameters=", &equal_parameters, gsi::arg ("comparer"), - "@brief Specifies a device parameter comparer for netlist verification.\n" - "By default, all devices are compared with all parameters. If you want to select only certain parameters " - "for comparison or use a fuzzy compare criterion, use an \\EqualDeviceParameters object and assign it " - "to the device class of one netlist. You can also chain multiple \\EqualDeviceParameters objects with the '+' operator " - "for specifying multiple parameters in the equality check.\n" - "\n" - "You can assign nil for the parameter comparer to remove it.\n" - "\n" - "In special cases, you can even implement a custom compare scheme by deriving your own comparer from the \\GenericDeviceParameterCompare class.\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method_ext ("add_terminal", &gsi::dc_add_terminal_definition, gsi::arg ("terminal_def"), - "@brief Adds the given terminal definition to the device class\n" - "This method will define a new terminal. The new terminal is added at the end of existing terminals. " - "The terminal definition object passed as the argument is modified to contain the " - "new ID of the terminal.\n" - "\n" - "The terminal is copied into the device class. Modifying the terminal object later " - "does not have the effect of changing the terminal definition.\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method ("clear_terminals", &db::DeviceClass::clear_terminal_definitions, - "@brief Clears the list of terminals\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method_ext ("add_parameter", &gsi::dc_add_parameter_definition, gsi::arg ("parameter_def"), - "@brief Adds the given parameter definition to the device class\n" - "This method will define a new parameter. The new parameter is added at the end of existing parameters. " - "The parameter definition object passed as the argument is modified to contain the " - "new ID of the parameter." - "\n" - "The parameter is copied into the device class. Modifying the parameter object later " - "does not have the effect of changing the parameter definition.\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method ("clear_parameters", &db::DeviceClass::clear_parameter_definitions, - "@brief Clears the list of parameters\n" - "\n" - "This method has been added in version 0.27.3.\n" - ) + - gsi::method_ext ("combiner=", &set_combiner, gsi::arg ("combiner"), - "@brief Specifies a device combiner (parallel or serial device combination).\n" - "\n" - "You can assign nil for the combiner to remove it.\n" - "\n" - "In special cases, you can even implement a custom combiner by deriving your own comparer from the \\GenericDeviceCombiner class.\n" - "\n" - "This method has been added in version 0.27.3.\n" - ) + - gsi::method_ext ("combiner", &get_combiner, - "@brief Gets a device combiner or nil if none is registered.\n" - "\n" - "This method has been added in version 0.27.3.\n" - ) + - gsi::method ("supports_parallel_combination=", &db::DeviceClass::set_supports_parallel_combination, gsi::arg ("f"), - "@brief Specifies whether the device supports parallel device combination.\n" - "Parallel device combination means that all terminals of two combination candidates are connected to the same nets. " - "If the device does not support this combination mode, this predicate can be set to false. This will make the device " - "extractor skip the combination test in parallel mode and improve performance somewhat.\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method ("supports_serial_combination=", &db::DeviceClass::set_supports_serial_combination, gsi::arg ("f"), - "@brief Specifies whether the device supports serial device combination.\n" - "Serial device combination means that the devices are connected by internal nodes. " - "If the device does not support this combination mode, this predicate can be set to false. This will make the device " - "extractor skip the combination test in serial mode and improve performance somewhat.\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method ("equivalent_terminal_id", &db::DeviceClass::equivalent_terminal_id, gsi::arg ("original_id"), gsi::arg ("equivalent_id"), - "@brief Specifies a terminal to be equivalent to another.\n" - "Use this method to specify two terminals to be exchangeable. For example to make S and D of a MOS transistor equivalent, " - "call this method with S and D terminal IDs. In netlist matching, S will be translated to D and thus made equivalent to D.\n" - "\n" - "Note that terminal equivalence is not effective if the device class operates in strict mode (see \\DeviceClass#strict=).\n" - "\n" - "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" - ) + - gsi::method ("clear_equivalent_terminal_ids", &db::DeviceClass::clear_equivalent_terminal_ids, - "@brief Clears all equivalent terminal ids\n" - "\n" - "This method has been added in version 0.27.3.\n" - ), - "@brief A class describing a specific type of device.\n" - "Device class objects live in the context of a \\Netlist object. After a " - "device class is created, it must be added to the netlist using \\Netlist#add. " - "The netlist will own the device class object. When the netlist is destroyed, the " - "device class object will become invalid.\n" - "\n" - "The \\DeviceClass class is the base class for other device classes.\n" - "\n" - "This class has been added in version 0.26. In version 0.27.3, the 'GenericDeviceClass' has been integrated with \\DeviceClass " - "and the device class was made writeable in most respects. This enables manipulating built-in device classes." -); - -namespace { - -/** - * @brief A DeviceClass implementation that allows reimplementation of the virtual methods - * - * NOTE: cloning of the generic device class is not supported currently. Hence when the - * netlist is copied, the device class attributes will remain, but the functionality is lost. - */ -class GenericDeviceClass - : public db::DeviceClass -{ -public: - GenericDeviceClass () - : db::DeviceClass (), m_supports_parallel_combination (true), m_supports_serial_combination (true) - { - // .. nothing yet .. - } - - virtual bool combine_devices (db::Device *a, db::Device *b) const - { - if (cb_combine_devices.can_issue ()) { - return cb_combine_devices.issue (&db::DeviceClass::combine_devices, a, b); - } else { - return db::DeviceClass::combine_devices (a, b); - } - } - - virtual bool supports_parallel_combination () const - { - return m_supports_parallel_combination; - } - - virtual bool supports_serial_combination () const - { - return m_supports_serial_combination; - } - - void set_supports_parallel_combination (bool f) - { - m_supports_parallel_combination = f; - } - - void set_supports_serial_combination (bool f) - { - m_supports_serial_combination = f; - } - - void equivalent_terminal_id (size_t tid, size_t equiv_tid) - { - m_equivalent_terminal_ids.insert (std::make_pair (tid, equiv_tid)); - } - - virtual size_t normalize_terminal_id (size_t tid) const - { - std::map::const_iterator ntid = m_equivalent_terminal_ids.find (tid); - if (ntid != m_equivalent_terminal_ids.end ()) { - return ntid->second; - } else { - return tid; - } - } - - gsi::Callback cb_combine_devices; - -private: - bool m_supports_parallel_combination; - bool m_supports_serial_combination; - std::map m_equivalent_terminal_ids; -}; - -} - -static db::Net *create_net (db::Circuit *c, const std::string &name) -{ - db::Net *n = new db::Net (); - c->add_net (n); - n->set_name (name); - return n; -} - -static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const std::string &name) -{ - db::Device *d = new db::Device (dc, name); - c->add_device (d); - return d; -} - -static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, const std::string &name) -{ - db::SubCircuit *sc = new db::SubCircuit (cc, name); - c->add_subcircuit (sc); - return sc; -} - -static db::Net *circuit_net_for_pin (db::Circuit *c, const db::Pin *pin) -{ - return pin ? c->net_for_pin (pin->id ()) : 0; -} - -static const db::Net *circuit_net_for_pin_const (const db::Circuit *c, const db::Pin *pin) -{ - return pin ? c->net_for_pin (pin->id ()) : 0; -} - -static void circuit_connect_pin1 (db::Circuit *c, const db::Pin *pin, db::Net *net) -{ - if (pin) { - c->connect_pin (pin->id (), net); - } -} - -static void circuit_disconnect_pin (db::Circuit *c, size_t pin_id) -{ - c->connect_pin (pin_id, 0); -} - -static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin) -{ - if (pin) { - c->connect_pin (pin->id (), 0); - } -} - -static db::Pin *create_pin (db::Circuit *circuit, const std::string &name) -{ - return & circuit->add_pin (name); -} - -static std::vector -nets_non_const (const std::vector &nc) -{ - std::vector n; - n.reserve (nc.size ()); - for (auto i = nc.begin (); i != nc.end (); ++i) { - n.push_back (const_cast (*i)); - } - - return n; -} - -static std::vector -nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs) -{ - std::vector res; - if (! circuit) { - return res; - } - - tl::GlobPattern glob (name_pattern); - if (! cs.is_nil ()) { - glob.set_case_sensitive (cs.to_bool ()); - } else if (circuit->netlist ()) { - glob.set_case_sensitive (circuit->netlist ()->is_case_sensitive ()); - } - for (db::Circuit::const_net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { - const db::Net *net = n.operator-> (); - if (!net->name ().empty () && glob.match (net->name ())) { - res.push_back (net); - } - } - - return res; -} - -static std::vector -nets_by_name (db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs) -{ - return nets_non_const (nets_by_name_const (circuit, name_pattern, cs)); -} - -static std::vector -nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) -{ - std::vector res; - if (! netlist) { - return res; - } - - tl::GlobPattern glob (name_pattern); - glob.set_case_sensitive (cs.is_nil () ? netlist->is_case_sensitive () : cs.to_bool ()); - for (auto c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { - bool is_top = (c->begin_parents () == c->end_parents ()); - for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { - const db::Net *net = n.operator-> (); - // NOTE: we only pick root nets (pin_count == 0 or in top cell) - if ((is_top || net->pin_count () == 0) && !net->name ().empty () && glob.match (net->name ())) { - res.push_back (net); - } - } - } - - return res; -} - -static std::vector -nets_by_name_from_netlist (db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) -{ - return nets_non_const (nets_by_name_const_from_netlist (netlist, name_pattern, cs)); -} - -Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", - gsi::method_ext ("create_pin", &create_pin, gsi::arg ("name"), - "@brief Creates a new \\Pin object inside the circuit\n" - "This object will describe a pin of the circuit. A circuit connects " - "to the outside through such a pin. The pin is added after all existing " - "pins. For more details see the \\Pin class." - "\n\n" - "Starting with version 0.26.8, this method returns a reference to a \\Pin object rather than a copy." - ) + - gsi::method ("remove_pin", &db::Circuit::remove_pin, gsi::arg ("id"), - "@brief Removes the pin with the given ID from the circuit\n" - "\n" - "This method has been introduced in version 0.26.2.\n" - ) + - gsi::method ("rename_pin", &db::Circuit::rename_pin, gsi::arg ("id"), gsi::arg ("new_name"), - "@brief Renames the pin with the given ID to 'new_name'\n" - "\n" - "This method has been introduced in version 0.26.8.\n" - ) + - gsi::iterator ("each_child", (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_children, (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_children, - "@brief Iterates over the child circuits of this circuit\n" - "Child circuits are the ones that are referenced from this circuit via subcircuits." - ) + - gsi::iterator ("each_child", (db::Circuit::const_child_circuit_iterator (db::Circuit::*) () const) &db::Circuit::begin_children, (db::Circuit::const_child_circuit_iterator (db::Circuit::*) () const) &db::Circuit::end_children, - "@brief Iterates over the child circuits of this circuit (const version)\n" - "Child circuits are the ones that are referenced from this circuit via subcircuits." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::iterator ("each_parent", (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_parents, (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_parents, - "@brief Iterates over the parent circuits of this circuit\n" - "Child circuits are the ones that are referencing this circuit via subcircuits." - ) + - gsi::iterator ("each_parent", (db::Circuit::const_parent_circuit_iterator (db::Circuit::*) () const) &db::Circuit::begin_parents, (db::Circuit::const_parent_circuit_iterator (db::Circuit::*) () const) &db::Circuit::end_parents, - "@brief Iterates over the parent circuits of this circuit (const version)\n" - "Child circuits are the ones that are referencing this circuit via subcircuits." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("has_refs?", &db::Circuit::has_refs, - "@brief Returns a value indicating whether the circuit has references\n" - "A circuit has references if there is at least one subcircuit referring to it." - ) + - gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs, - "@brief Iterates over the subcircuit objects referencing this circuit\n" - ) + - gsi::iterator ("each_ref", (db::Circuit::const_refs_iterator (db::Circuit::*) () const) &db::Circuit::begin_refs, (db::Circuit::const_refs_iterator (db::Circuit::*) () const) &db::Circuit::end_refs, - "@brief Iterates over the subcircuit objects referencing this circuit (const version)\n" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins, - "@brief Iterates over the pins of the circuit" - ) + - gsi::iterator ("each_pin", (db::Circuit::const_pin_iterator (db::Circuit::*) () const) &db::Circuit::begin_pins, (db::Circuit::const_pin_iterator (db::Circuit::*) () const) &db::Circuit::end_pins, - "@brief Iterates over the pins of the circuit (const version)" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("device_by_id", (db::Device *(db::Circuit::*) (size_t)) &db::Circuit::device_by_id, gsi::arg ("id"), - "@brief Gets the device object for a given ID.\n" - "If the ID is not a valid device ID, nil is returned." - ) + - gsi::method ("device_by_id", (const db::Device *(db::Circuit::*) (size_t) const) &db::Circuit::device_by_id, gsi::arg ("id"), - "@brief Gets the device object for a given ID (const version).\n" - "If the ID is not a valid device ID, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("device_by_name", (db::Device *(db::Circuit::*) (const std::string &)) &db::Circuit::device_by_name, gsi::arg ("name"), - "@brief Gets the device object for a given name.\n" - "If the ID is not a valid device name, nil is returned." - ) + - gsi::method ("device_by_name", (const db::Device *(db::Circuit::*) (const std::string &) const) &db::Circuit::device_by_name, gsi::arg ("name"), - "@brief Gets the device object for a given name (const version).\n" - "If the ID is not a valid device name, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("subcircuit_by_id", (db::SubCircuit *(db::Circuit::*) (size_t)) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), - "@brief Gets the subcircuit object for a given ID.\n" - "If the ID is not a valid subcircuit ID, nil is returned." - ) + - gsi::method ("subcircuit_by_id", (const db::SubCircuit *(db::Circuit::*) (size_t) const) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), - "@brief Gets the subcircuit object for a given ID (const version).\n" - "If the ID is not a valid subcircuit ID, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("subcircuit_by_name", (db::SubCircuit *(db::Circuit::*) (const std::string &)) &db::Circuit::subcircuit_by_name, gsi::arg ("name"), - "@brief Gets the subcircuit object for a given name.\n" - "If the ID is not a valid subcircuit name, nil is returned." - ) + - gsi::method ("subcircuit_by_name", (const db::SubCircuit *(db::Circuit::*) (const std::string &) const) &db::Circuit::subcircuit_by_name, gsi::arg ("name"), - "@brief Gets the subcircuit object for a given name (const version).\n" - "If the ID is not a valid subcircuit name, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("net_by_cluster_id", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_by_cluster_id, gsi::arg ("cluster_id"), - "@brief Gets the net object corresponding to a specific cluster ID\n" - "If the ID is not a valid pin cluster ID, nil is returned." - ) + - gsi::method ("net_by_name", (db::Net *(db::Circuit::*) (const std::string &)) &db::Circuit::net_by_name, gsi::arg ("name"), - "@brief Gets the net object for a given name.\n" - "If the ID is not a valid net name, nil is returned." - ) + - gsi::method ("net_by_name", (const db::Net *(db::Circuit::*) (const std::string &) const) &db::Circuit::net_by_name, gsi::arg ("name"), - "@brief Gets the net object for a given name (const version).\n" - "If the ID is not a valid net name, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method_ext ("nets_by_name", &nets_by_name, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), - "@brief Gets the net objects for a given name filter.\n" - "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" - "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " - "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " - "default, case sensitivity is taken from the netlist.\n" - "\n" - "This method has been introduced in version 0.27.3.\n" - "The 'case_sensitive' argument has been added in version 0.30.2." - ) + - gsi::method_ext ("nets_by_name", &nets_by_name_const, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), - "@brief Gets the net objects for a given name filter (const version).\n" - "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" - "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " - "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " - "default, case sensitivity is taken from the netlist.\n" - "\n" - "This constness variant has been introduced in version 0.27.3.\n" - "The 'case_sensitive' argument has been added in version 0.30.2." - ) + - gsi::method ("pin_by_id", (db::Pin *(db::Circuit::*) (size_t)) &db::Circuit::pin_by_id, gsi::arg ("id"), - "@brief Gets the \\Pin object corresponding to a specific ID\n" - "If the ID is not a valid pin ID, nil is returned." - ) + - gsi::method ("pin_by_id", (const db::Pin *(db::Circuit::*) (size_t) const) &db::Circuit::pin_by_id, gsi::arg ("id"), - "@brief Gets the \\Pin object corresponding to a specific ID (const version)\n" - "If the ID is not a valid pin ID, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("pin_by_name", (db::Pin *(db::Circuit::*) (const std::string &)) &db::Circuit::pin_by_name, gsi::arg ("name"), - "@brief Gets the \\Pin object corresponding to a specific name\n" - "If the ID is not a valid pin name, nil is returned." - ) + - gsi::method ("pin_by_name", (const db::Pin *(db::Circuit::*) (const std::string &) const) &db::Circuit::pin_by_name, gsi::arg ("name"), - "@brief Gets the \\Pin object corresponding to a specific name (const version)\n" - "If the ID is not a valid pin name, nil is returned." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("pin_count", &db::Circuit::pin_count, - "@brief Gets the number of pins in the circuit" - ) + - gsi::method_ext ("create_net", &gsi::create_net, gsi::arg ("name", std::string ()), - "@brief Creates a new \\Net object inside the circuit\n" - "This object will describe a net of the circuit. The nets are basically " - "connections between the different components of the circuit (subcircuits, " - "devices and pins).\n" - "\n" - "A net needs to be filled with references to connect to specific objects. " - "See the \\Net class for more details." - ) + - gsi::method ("remove_net", &db::Circuit::remove_net, gsi::arg ("net"), - "@brief Removes the given net from the circuit\n" - ) + - gsi::method ("join_nets", &db::Circuit::join_nets, gsi::arg ("net"), gsi::arg ("with"), - "@brief Joins (connects) two nets into one\n" - "This method will connect the 'with' net with 'net' and remove 'with'.\n" - "\n" - "This method has been introduced in version 0.26.4. Starting with version 0.28.13, " - "net names will be formed from both input names, combining them with as a comma-separated list." - ) + - gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, - "@brief Iterates over the nets of the circuit" - ) + - gsi::iterator ("each_net", (db::Circuit::const_net_iterator (db::Circuit::*) () const) &db::Circuit::begin_nets, (db::Circuit::const_net_iterator (db::Circuit::*) () const) &db::Circuit::end_nets, - "@brief Iterates over the nets of the circuit (const version)" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method_ext ("create_device", &gsi::create_device1, gsi::arg ("device_class"), gsi::arg ("name", std::string ()), - "@brief Creates a new bound \\Device object inside the circuit\n" - "This object describes a device of the circuit. The device is already attached " - "to the device class. The name is optional and is used to identify the device in a " - "netlist file.\n" - "\n" - "For more details see the \\Device class." - ) + - gsi::method ("remove_device", &db::Circuit::remove_device, gsi::arg ("device"), - "@brief Removes the given device from the circuit\n" - ) + - gsi::iterator ("each_device", (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::begin_devices, (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::end_devices, - "@brief Iterates over the devices of the circuit" - ) + - gsi::iterator ("each_device", (db::Circuit::const_device_iterator (db::Circuit::*) () const) &db::Circuit::begin_devices, (db::Circuit::const_device_iterator (db::Circuit::*) () const) &db::Circuit::end_devices, - "@brief Iterates over the devices of the circuit (const version)" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit1, gsi::arg ("circuit"), gsi::arg ("name", std::string ()), - "@brief Creates a new bound \\SubCircuit object inside the circuit\n" - "This object describes an instance of another circuit inside the circuit. The subcircuit is already attached " - "to the other circuit. The name is optional and is used to identify the subcircuit in a " - "netlist file.\n" - "\n" - "For more details see the \\SubCircuit class." - ) + - gsi::method ("remove_subcircuit", &db::Circuit::remove_subcircuit, gsi::arg ("subcircuit"), - "@brief Removes the given subcircuit from the circuit\n" - ) + - gsi::method ("flatten_subcircuit", &db::Circuit::flatten_subcircuit, gsi::arg ("subcircuit"), - "@brief Flattens a subcircuit\n" - "This method will substitute the given subcircuit by its contents. The subcircuit is removed " - "after this." - ) + - gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, - "@brief Iterates over the subcircuits of the circuit" - ) + - gsi::iterator ("each_subcircuit", (db::Circuit::const_subcircuit_iterator (db::Circuit::*) () const) &db::Circuit::begin_subcircuits, (db::Circuit::const_subcircuit_iterator (db::Circuit::*) () const) &db::Circuit::end_subcircuits, - "@brief Iterates over the subcircuits of the circuit (const version)" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("blank", &db::Circuit::blank, - "@brief Blanks out the circuit\n" - "This method will remove all the innards of the circuit and just leave the pins. " - "The pins won't be connected to inside nets anymore, but the circuit can still be " - "called by subcircuit references. " - "This method will eventually create a 'circuit abstract' (or black box). It will " - "set the \\dont_purge flag to mark this circuit as 'intentionally empty'." - ) + - gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist, - "@brief Gets the netlist object the circuit lives in" - ) + - gsi::method ("netlist", (const db::Netlist *(db::Circuit::*) () const) &db::Circuit::netlist, - "@brief Gets the netlist object the circuit lives in (const version)" - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("name=", &db::Circuit::set_name, gsi::arg ("name"), - "@brief Sets the name of the circuit" - ) + - gsi::method ("name", &db::Circuit::name, - "@brief Gets the name of the circuit" - ) + - gsi::method ("boundary=", &db::Circuit::set_boundary, gsi::arg ("boundary"), - "@brief Sets the boundary of the circuit" - ) + - gsi::method ("boundary", &db::Circuit::boundary, - "@brief Gets the boundary of the circuit" - ) + - gsi::method ("dont_purge", &db::Circuit::dont_purge, - "@brief Gets a value indicating whether the circuit can be purged on \\Netlist#purge.\n" - ) + - gsi::method ("dont_purge=", &db::Circuit::set_dont_purge, gsi::arg ("f"), - "@brief Sets a value indicating whether the circuit can be purged on \\Netlist#purge.\n" - "If this attribute is set to true, \\Netlist#purge will never delete this circuit.\n" - "This flag therefore marks this circuit as 'precious'." - ) + - gsi::method ("cell_index=", &db::Circuit::set_cell_index, gsi::arg ("cell_index"), - "@brief Sets the cell index\n" - "The cell index relates a circuit with a cell from a layout. It's intended to " - "hold a cell index number if the netlist was extracted from a layout.\n" - ) + - gsi::method ("cell_index", &db::Circuit::cell_index, - "@brief Gets the cell index of the circuit\n" - "See \\cell_index= for details.\n" - ) + - gsi::method ("net_for_pin", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), - "@brief Gets the net object attached to a specific pin.\n" - "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" - "This method returns nil if the pin is not connected or the pin ID is invalid." - ) + - gsi::method ("net_for_pin", (const db::Net *(db::Circuit::*) (size_t) const) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), - "@brief Gets the net object attached to a specific pin (const version).\n" - "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" - "This method returns nil if the pin is not connected or the pin ID is invalid." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method_ext ("net_for_pin", &gsi::circuit_net_for_pin, gsi::arg ("pin"), - "@brief Gets the net object attached to a specific pin.\n" - "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" - "This method returns nil if the pin is not connected or the pin object is nil." - ) + - gsi::method_ext ("net_for_pin", &gsi::circuit_net_for_pin_const, gsi::arg ("pin"), - "@brief Gets the net object attached to a specific pin (const version).\n" - "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" - "This method returns nil if the pin is not connected or the pin object is nil." - "\n\n" - "This constness variant has been introduced in version 0.26.8" - ) + - gsi::method ("connect_pin", &db::Circuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), - "@brief Connects the given pin with the given net.\n" - "The net must be one inside the circuit. Any previous connected is resolved before this " - "connection is made. A pin can only be connected to one net at a time." - ) + - gsi::method_ext ("connect_pin", &gsi::circuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), - "@brief Connects the given pin with the given net.\n" - "The net and the pin must be objects from inside the circuit. Any previous connected is resolved before this " - "connection is made. A pin can only be connected to one net at a time." - ) + - gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin, gsi::arg ("pin_id"), - "@brief Disconnects the given pin from any net.\n" - ) + - gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin1, gsi::arg ("pin"), - "@brief Disconnects the given pin from any net.\n" - ) + - gsi::method ("clear", &db::Circuit::clear, - "@brief Clears the circuit\n" - "This method removes all objects and clears the other attributes." - ) + - gsi::method ("combine_devices", &db::Circuit::combine_devices, - "@brief Combines devices where possible\n" - "This method will combine devices that can be combined according " - "to their device classes 'combine_devices' method.\n" - "For example, serial or parallel resistors can be combined into " - "a single resistor.\n" - ) + - gsi::method ("purge_devices", &db::Circuit::purge_devices, - "@brief Purges invalid devices.\n" - "Purges devices which are considered invalid. Such devices are for example those whose terminals are all connected to a single net.\n" - "\n" - "This method has been added in version 0.29.7." - ) + - gsi::method ("purge_nets", &db::Circuit::purge_nets, - "@brief Purges floating nets.\n" - "Floating nets are nets with no device or subcircuit attached to. Such floating " - "nets are removed in this step. If these nets are connected outward to a circuit pin, this " - "circuit pin is also removed." - ) + - gsi::method ("purge_nets_keep_pins", &db::Circuit::purge_nets_keep_pins, - "@brief Purges floating nets but keep pins.\n" - "This method will remove floating nets like \\purge_nets, but if these nets are attached " - "to a pin, the pin will be left disconnected from any net.\n" - "\n" - "This method has been introduced in version 0.26.2.\n" - ), - "@brief Circuits are the basic building blocks of the netlist\n" - "A circuit has pins by which it can connect to the outside. Pins are " - "created using \\create_pin and are represented by the \\Pin class.\n" - "\n" - "Furthermore, a circuit manages the components of the netlist. " - "Components are devices (class \\Device) and subcircuits (class \\SubCircuit). " - "Devices are basic devices such as resistors or transistors. Subcircuits " - "are other circuits to which nets from this circuit connect. " - "Devices are created using the \\create_device method. Subcircuits are " - "created using the \\create_subcircuit method.\n" - "\n" - "Devices are connected through 'terminals', subcircuits are connected through " - "their pins. Terminals and pins are described by integer ID's in the context of " - "most methods.\n" - "\n" - "Finally, the circuit consists of the nets. Nets connect terminals of devices " - "and pins of subcircuits or the circuit itself. Nets are created using " - "\\create_net and are represented by objects of the \\Net class.\n" - "See there for more about nets.\n" - "\n" - "The Circuit object is only valid if the netlist object is alive. " - "Circuits must be added to a netlist using \\Netlist#add to become " - "part of the netlist.\n" - "\n" - "The Circuit class has been introduced in version 0.26." -); - static void add_circuit (db::Netlist *nl, db::Circuit *c) { tl_assert (c != 0); @@ -2115,6 +130,48 @@ circuits_by_name_const (const db::Netlist *netlist, const std::string &name_patt return res; } +static std::vector +nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) +{ + std::vector res; + if (! netlist) { + return res; + } + + tl::GlobPattern glob (name_pattern); + glob.set_case_sensitive (cs.is_nil () ? netlist->is_case_sensitive () : cs.to_bool ()); + for (auto c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { + bool is_top = (c->begin_parents () == c->end_parents ()); + for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { + const db::Net *net = n.operator-> (); + // NOTE: we only pick root nets (pin_count == 0 or in top cell) + if ((is_top || net->pin_count () == 0) && !net->name ().empty () && glob.match (net->name ())) { + res.push_back (net); + } + } + } + + return res; +} + +static std::vector +nets_non_const (const std::vector &nc) +{ + std::vector n; + n.reserve (nc.size ()); + for (auto i = nc.begin (); i != nc.end (); ++i) { + n.push_back (const_cast (*i)); + } + + return n; +} + +static std::vector +nets_by_name_from_netlist (db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) +{ + return nets_non_const (nets_by_name_const_from_netlist (netlist, name_pattern, cs)); +} + Class decl_dbNetlist ("db", "Netlist", gsi::method ("is_case_sensitive?", &db::Netlist::is_case_sensitive, "@brief Returns a value indicating whether the netlist names are case sensitive\n" @@ -2340,11 +397,14 @@ Class decl_dbNetlist ("db", "Netlist", "For example, serial or parallel resistors can be combined into " "a single resistor.\n" ) + - gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, + gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, gsi::arg ("sorted_by_name", true), "@brief Creates pins for top-level circuits.\n" "This method will turn all named nets of top-level circuits (such that are not " "referenced by subcircuits) into pins. This method can be used before purge to " - "avoid that purge will remove nets which are directly connecting to subcircuits." + "avoid that purge will remove nets which are directly connecting to subcircuits.\n" + "\n" + "Starting from version 0.30.9, the pins will be sorted by name by default. Sorting can be " + "disabled by setting \\sorted_by_name to false for backward compatibility." ) + gsi::method ("purge", &db::Netlist::purge, "@brief Purge unused nets, circuits and subcircuits.\n" @@ -2389,952 +449,4 @@ Class decl_dbNetlist ("db", "Netlist", "The netlist class has been introduced with version 0.26." ); -/** - * @brief A SPICE writer delegate base class for reimplementation - */ -class NetlistSpiceWriterDelegateImpl - : public db::NetlistSpiceWriterDelegate, public gsi::ObjectBase -{ -public: - NetlistSpiceWriterDelegateImpl () - : db::NetlistSpiceWriterDelegate () - { - // .. nothing yet .. - } - - virtual void write_header () const - { - if (cb_write_header.can_issue ()) { - cb_write_header.issue (&db::NetlistSpiceWriterDelegate::write_header); - } else { - db::NetlistSpiceWriterDelegate::write_header (); - } - } - - virtual void write_device_intro (const db::DeviceClass &ccls) const - { - reimpl_write_device_intro (const_cast (ccls)); - } - - // NOTE: we pass non-const refs to Ruby/Python - everything else is a bit of a nightmare. - // Still that's not really clean. Just say, the implementation promises not to change the objects. - void reimpl_write_device_intro (db::DeviceClass &cls) const - { - if (cb_write_device_intro.can_issue ()) { - cb_write_device_intro.issue (&NetlistSpiceWriterDelegateImpl::org_write_device_intro, const_cast (cls)); - } else { - org_write_device_intro (cls); - } - } - - void org_write_device_intro (db::DeviceClass &cls) const - { - db::NetlistSpiceWriterDelegate::write_device_intro (cls); - } - - virtual void write_device (const db::Device &cdev) const - { - reimpl_write_device (const_cast (cdev)); - } - - // NOTE: we pass non-const refs to Ruby/Python - everthing else is a bit of a nightmare. - // Still that's not really clean. Just say, the implementation promises not to change the objects. - void reimpl_write_device (db::Device &dev) const - { - if (cb_write_device.can_issue ()) { - cb_write_device.issue (&NetlistSpiceWriterDelegateImpl::org_write_device, dev); - } else { - org_write_device (dev); - } - } - - void org_write_device (db::Device &dev) const - { - db::NetlistSpiceWriterDelegate::write_device (dev); - } - - void org_write_header () const - { - db::NetlistSpiceWriterDelegate::write_header (); - } - - gsi::Callback cb_write_header; - gsi::Callback cb_write_device_intro; - gsi::Callback cb_write_device; -}; - -Class db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate", - gsi::method ("write_header", &NetlistSpiceWriterDelegateImpl::org_write_header, "@hide") + - gsi::callback ("write_header", &NetlistSpiceWriterDelegateImpl::write_header, &NetlistSpiceWriterDelegateImpl::cb_write_header, - "@brief Writes the text at the beginning of the SPICE netlist\n" - "Reimplement this method to insert your own text at the beginning of the file" - ) + - gsi::method ("write_device_intro", &NetlistSpiceWriterDelegateImpl::org_write_device_intro, "@hide") + - gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::reimpl_write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"), - "@brief Inserts a text for the given device class\n" - "Reimplement this method to insert your own text at the beginning of the file for the given device class" - ) + - gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"), "@hide") + - gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::reimpl_write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"), - "@brief Inserts a text for the given device\n" - "Reimplement this method to write the given device in the desired way. " - "The default implementation will utilize the device class information to write native SPICE " - "elements for the devices." - ) + - gsi::method ("emit_comment", &NetlistSpiceWriterDelegateImpl::emit_comment, gsi::arg ("comment"), - "@brief Writes the given comment into the file" - ) + - gsi::method ("emit_line", &NetlistSpiceWriterDelegateImpl::emit_line, gsi::arg ("line"), - "@brief Writes the given line into the file" - ) + - gsi::method ("net_to_string", &NetlistSpiceWriterDelegateImpl::net_to_string, gsi::arg ("net"), - "@brief Gets the node ID for the given net\n" - "The node ID is a numeric string instead of the full name of the net. Numeric IDs are used within " - "SPICE netlist because they are usually shorter.\n" - ) + - gsi::method ("format_name", &NetlistSpiceWriterDelegateImpl::format_name, gsi::arg ("name"), - "@brief Formats the given name in a SPICE-compatible way" - ), - "@brief Provides a delegate for the SPICE writer for doing special formatting for devices\n" - "Supply a customized class to provide a specialized writing scheme for devices. " - "You need a customized class if you want to implement special devices or you want to use " - "subcircuits rather than the built-in devices.\n" - "\n" - "See \\NetlistSpiceWriter for more details.\n" - "\n" - "This class has been introduced in version 0.26." -); - -namespace { - -class NetlistSpiceWriterWithOwnership - : public db::NetlistSpiceWriter -{ -public: - NetlistSpiceWriterWithOwnership (NetlistSpiceWriterDelegateImpl *delegate) - : db::NetlistSpiceWriter (delegate), m_ownership (delegate) - { - if (delegate) { - delegate->keep (); - } - } - -private: - tl::shared_ptr m_ownership; -}; - -} - -db::NetlistSpiceWriter *new_spice_writer (const std::string &profile) -{ - auto *writer = new db::NetlistSpiceWriter (); - writer->set_profile (profile); - return writer; -} - -db::NetlistSpiceWriter *new_spice_writer2 (NetlistSpiceWriterDelegateImpl *delegate, const std::string &profile) -{ - auto *writer = new NetlistSpiceWriterWithOwnership (delegate); - writer->set_profile (profile); - return writer; -} - -Class db_NetlistWriter ("db", "NetlistWriter", - gsi::Methods (), - "@brief Base class for netlist writers\n" - "This class is provided as a base class for netlist writers. It is not intended for reimplementation on script level, but used internally as an interface.\n" - "\n" - "This class has been introduced in version 0.26." -); - -Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "NetlistSpiceWriter", - gsi::constructor ("new", &new_spice_writer, gsi::arg ("profile", std::string ()), - "@brief Creates a new writer without delegate.\n" - "The profile string gives the name of the SPICE profile to use, when taking the SPICE representation " - "from the device classes in the netlist.\n" - "\n" - "The profile argument has been added in version 0.31.0." - ) + - gsi::constructor ("new", &new_spice_writer2, gsi::arg ("delegate"), gsi::arg ("profile", std::string ()), - "@brief Creates a new writer with a delegate.\n" - "The profile string gives the name of the SPICE profile to use, when taking the SPICE representation " - "from the device classes in the netlist.\n" - "\n" - "The profile argument has been added in version 0.31.0." - ) + - gsi::method ("not_connect_prefix", &db::NetlistSpiceWriter::not_connect_prefix, - "@brief Gets the prefix used for terminals or pins which are not connected.\n" - "See \\not_connect_prefix= for details.\n" - "\n" - "This attribute has been introduced in version 0.31.0." - ) + - gsi::method ("not_connect_prefix=", &db::NetlistSpiceWriter::set_not_connect_prefix, gsi::arg ("s"), - "@brief Sets the prefix used for terminals or pins which are not connected.\n" - "By default, the prefix is 'nc_'." - "\n" - "This attribute has been introduced in version 0.31.0." - ) + - gsi::method ("allowed_name_characters", &db::NetlistSpiceWriter::allowed_name_chars, - "@brief Gets a string listing the allowed characters for names (beside alphanumeric).\n" - "See \\allowed_name_characters= for details.\n" - "\n" - "This attribute has been introduced in version 0.31.0." - ) + - gsi::method ("allowed_name_characters=", &db::NetlistSpiceWriter::set_allowed_name_chars, gsi::arg ("s"), - "@brief Sets a string listing the allowed characters for names (beside alphanumeric).\n" - "\n" - "This attribute has been introduced in version 0.31.0." - ) + - gsi::method ("use_net_names?", &db::NetlistSpiceWriter::use_net_names, - "@brief Gets a value indicating whether to use net names (true) or net numbers (false).\n" - ) + - gsi::method ("use_net_names=", &db::NetlistSpiceWriter::set_use_net_names, gsi::arg ("f"), - "@brief Sets a value indicating whether to use net names (true) or net numbers (false).\n" - "The default is to use net numbers." - ) + - gsi::method ("use_net_names?", &db::NetlistSpiceWriter::use_net_names, - "@brief Gets a value indicating whether to use net names (true) or net numbers (false).\n" - ) + - gsi::method ("with_comments=", &db::NetlistSpiceWriter::set_with_comments, gsi::arg ("f"), - "@brief Sets a value indicating whether to embed comments for position etc. (true) or not (false).\n" - "The default is to embed comments." - ) + - gsi::method ("with_comments?", &db::NetlistSpiceWriter::with_comments, - "@brief Gets a value indicating whether to embed comments for position etc. (true) or not (false).\n" - ), - "@brief Implements a netlist writer for the SPICE format.\n" - "Provide a delegate for customizing the way devices are written.\n" - "\n" - "Use the SPICE writer like this:\n" - "\n" - "@code\n" - "writer = RBA::NetlistSpiceWriter::new\n" - "netlist.write(path, writer)\n" - "@/code\n" - "\n" - "You can give a custom description for the headline:\n" - "\n" - "@code\n" - "writer = RBA::NetlistSpiceWriter::new\n" - "netlist.write(path, writer, \"A custom description\")\n" - "@/code\n" - "\n" - "To customize the output, you can use a device writer delegate.\n" - "The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which " - "reimplements several methods to customize the following parts:\n" - "\n" - "@ul\n" - "@li A global header (\\NetlistSpiceWriterDelegate#write_header): this method is called to print the part right after the headline @/li\n" - "@li A per-device class header (\\NetlistSpiceWriterDelegate#write_device_intro): this method is called for every device class and may print device-class specific headers (e.g. model definitions) @/li\n" - "@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way. @/li\n" - "@/ul\n" - "\n" - "The delegate must use \\NetlistSpiceWriterDelegate#emit_line to print a line, \\NetlistSpiceWriterDelegate#emit_comment to print a comment etc.\n" - "For more method see \\NetlistSpiceWriterDelegate.\n" - "\n" - "A sample with a delegate is this:\n" - "\n" - "@code\n" - "class MyDelegate < RBA::NetlistSpiceWriterDelegate\n" - "\n" - " def write_header\n" - " emit_line(\"*** My special header\")\n" - " end\n" - "\n" - " def write_device_intro(cls)\n" - " emit_comment(\"My intro for class \" + cls.name)\n" - " end\n" - "\n" - " def write_device(dev)\n" - " if dev.device_class.name != \"MYDEVICE\"\n" - " emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n" - " emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n" - " super(dev)\n" - " emit_comment(\"After device \" + dev.expanded_name)\n" - " else\n" - " super(dev)\n" - " end\n" - " end\n" - "\n" - "end\n" - "\n" - "# write the netlist with delegate:\n" - "writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n" - "netlist.write(path, writer)\n" - "@/code\n" - "\n" - "This class has been introduced in version 0.26. " - "SPICE profiles have been added to device classes in version 0.31.0." -); - -Class db_NetlistReader ("db", "NetlistReader", - gsi::Methods (), - "@brief Base class for netlist readers\n" - "This class is provided as a base class for netlist readers. It is not intended for reimplementation on script level, but used internally as an interface.\n" - "\n" - "This class has been introduced in version 0.26." -); - -/** - * @brief A helper class wrapping the return values for NetlistSpiceReaderDelegateImpl::parse_element - */ -class ParseElementData -{ -public: - ParseElementData () : m_value (0.0) { } - - const std::string &element_name () const { return m_element; } - std::string &element_name_nc () { return m_element; } - void set_element_name (const std::string &element) { m_element = element; } - const std::string &model_name () const { return m_model; } - std::string &model_name_nc () { return m_model; } - void set_model_name (const std::string &model) { m_model = model; } - double value () const { return m_value; } - double &value_nc () { return m_value; } - void set_value (double value) { m_value = value; } - const std::vector &net_names () const { return m_net_names; } - std::vector &net_names_nc () { return m_net_names; } - void set_net_names (const std::vector &nn) { m_net_names = nn; } - const db::NetlistSpiceReader::parameters_type ¶meters () const { return m_parameters; } - db::NetlistSpiceReader::parameters_type ¶meters_nc () { return m_parameters; } - void set_parameters (const db::NetlistSpiceReader::parameters_type ¶meters) { m_parameters = parameters; } - -private: - std::string m_model, m_element; - double m_value; - std::vector m_net_names; - db::NetlistSpiceReader::parameters_type m_parameters; -}; - -/** - * @brief A helper class for the return values of NetlistSpiceReaderDelegateImpl::parse_element_components - */ -class ParseElementComponentsData -{ -public: - ParseElementComponentsData () { } - - const std::vector &strings () const { return m_strings; } - std::vector &strings_nc () { return m_strings; } - void set_strings (const std::vector &nn) { m_strings = nn; } - const db::NetlistSpiceReader::parameters_type ¶meters () const { return m_parameters; } - db::NetlistSpiceReader::parameters_type ¶meters_nc () { return m_parameters; } - void set_parameters (const db::NetlistSpiceReader::parameters_type ¶meters) { m_parameters = parameters; } - -private: - std::vector m_strings; - db::NetlistSpiceReader::parameters_type m_parameters; -}; - -/** - * @brief A SPICE reader delegate base class for reimplementation - */ -class NetlistSpiceReaderDelegateImpl - : public db::NetlistSpiceReaderDelegate, public gsi::ObjectBase -{ -public: - NetlistSpiceReaderDelegateImpl () - : db::NetlistSpiceReaderDelegate (), mp_variables (0) - { - // .. nothing yet .. - } - - virtual void error (const std::string &msg) - { - // doing this avoids passing exceptions through script code which spoils the message - // (the exception will be decorated with a stack trace). TODO: a better solution was - // to define a specific exception type for "raw exception". - m_error = msg; - db::NetlistSpiceReaderDelegate::error (msg); - } - - virtual void start (db::Netlist *netlist) - { - try { - m_error.clear (); - if (cb_start.can_issue ()) { - cb_start.issue (&db::NetlistSpiceReaderDelegate::start, netlist); - } else { - db::NetlistSpiceReaderDelegate::start (netlist); - } - } catch (tl::Exception &) { - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - } - } - - virtual void finish (db::Netlist *netlist) - { - try { - m_error.clear (); - if (cb_finish.can_issue ()) { - cb_finish.issue (&db::NetlistSpiceReaderDelegate::finish, netlist); - } else { - db::NetlistSpiceReaderDelegate::finish (netlist); - } - } catch (tl::Exception &) { - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - } - } - - virtual bool control_statement (const std::string &line) - { - try { - m_error.clear (); - if (cb_control_statement.can_issue ()) { - return cb_control_statement.issue (&db::NetlistSpiceReaderDelegate::control_statement, line); - } else { - return db::NetlistSpiceReaderDelegate::control_statement (line); - } - } catch (tl::Exception &) { - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - return false; - } - } - - virtual bool wants_subcircuit (const std::string &circuit_name) - { - try { - m_error.clear (); - if (cb_wants_subcircuit.can_issue ()) { - return cb_wants_subcircuit.issue (&db::NetlistSpiceReaderDelegate::wants_subcircuit, circuit_name); - } else { - return db::NetlistSpiceReaderDelegate::wants_subcircuit (circuit_name); - } - } catch (tl::Exception &) { - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - return false; - } - } - - virtual std::string translate_net_name (const std::string &nn) - { - try { - m_error.clear (); - if (cb_translate_net_name.can_issue ()) { - return cb_translate_net_name.issue (&db::NetlistSpiceReaderDelegate::translate_net_name, nn); - } else { - return db::NetlistSpiceReaderDelegate::translate_net_name (nn); - } - } catch (tl::Exception &) { - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - return std::string (); - } - } - - ParseElementData parse_element_helper (const std::string &s, const std::string &element) - { - // NOTE: this way of treating the element name is compatible with the old way and - // the new way in which the element name can be modified by "parse_element". - ParseElementData data; - data.set_element_name (element); - db::NetlistSpiceReaderDelegate::parse_element (s, data.element_name_nc (), data.model_name_nc (), data.value_nc (), data.net_names_nc (), data.parameters_nc (), variables ()); - return data; - } - - virtual void parse_element (const std::string &s, std::string &element, std::string &model, double &value, std::vector &nn, db::NetlistSpiceReader::parameters_type &pv, const db::NetlistSpiceReader::parameters_type &variables) - { - try { - - m_error.clear (); - mp_variables = &variables; - - ParseElementData data; - if (cb_parse_element.can_issue ()) { - data = cb_parse_element.issue (&NetlistSpiceReaderDelegateImpl::parse_element_helper, s, element); - } else { - data = parse_element_helper (s, element); - } - - element = data.element_name (); - model = data.model_name (); - value = data.value (); - nn = data.net_names (); - pv = data.parameters (); - - mp_variables = 0; - - } catch (tl::Exception &) { - mp_variables = 0; - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - } catch (...) { - mp_variables = 0; - throw; - } - } - - virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector &nets, const db::NetlistSpiceReader::parameters_type ¶ms) - { - try { - m_error.clear (); - if (cb_element.can_issue ()) { - return cb_element.issue &, const db::NetlistSpiceReader::parameters_type &> (&db::NetlistSpiceReaderDelegate::element, circuit, element, name, model, value, nets, params); - } else { - return db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params); - } - } catch (tl::Exception &) { - if (! m_error.empty ()) { - db::NetlistSpiceReaderDelegate::error (m_error); - } else { - throw; - } - return false; - } - } - - const db::NetlistSpiceReader::parameters_type &variables () const - { - static db::NetlistSpiceReader::parameters_type empty; - return mp_variables ? *mp_variables : empty; - } - - gsi::Callback cb_start; - gsi::Callback cb_finish; - gsi::Callback cb_control_statement; - gsi::Callback cb_wants_subcircuit; - gsi::Callback cb_translate_net_name; - gsi::Callback cb_element; - gsi::Callback cb_parse_element; - -private: - std::string m_error; - const db::NetlistSpiceReader::parameters_type *mp_variables; -}; - -static void start_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist) -{ - delegate->db::NetlistSpiceReaderDelegate::start (netlist); -} - -static void finish_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist) -{ - delegate->db::NetlistSpiceReaderDelegate::finish (netlist); -} - -static bool wants_subcircuit_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &model) -{ - return delegate->db::NetlistSpiceReaderDelegate::wants_subcircuit (model); -} - -static bool control_statement_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &line) -{ - return delegate->db::NetlistSpiceReaderDelegate::control_statement (line); -} - -static std::string translate_net_name_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &name) -{ - return delegate->db::NetlistSpiceReaderDelegate::translate_net_name (name); -} - -static bool element_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector &nets, const db::NetlistSpiceReader::parameters_type ¶ms) -{ - return delegate->db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params); -} - -static ParseElementData parse_element_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const std::string &element) -{ - return delegate->parse_element_helper (s, element); -} - -static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables) -{ - tl::Variant res; - double v = 0.0; - if (db::NetlistSpiceReaderDelegate::try_read_value (s, v, variables)) { - res = v; - } - return res; -} - -static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables) -{ - ParseElementComponentsData data; - delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables); - return data; -} - -Class db_ParseElementComponentsData ("db", "ParseElementComponentsData", - gsi::method ("strings", &ParseElementComponentsData::strings, - "@brief Gets the (unnamed) string parameters\n" - "These parameters are typically net names or model name." - ) + - gsi::method ("strings=", &ParseElementComponentsData::set_strings, gsi::arg ("list"), - "@brief Sets the (unnamed) string parameters\n" - ) + - gsi::method ("parameters", &ParseElementComponentsData::parameters, - "@brief Gets the (named) parameters\n" - "Named parameters are typically (but not neccessarily) numerical, like 'w=0.15u'." - ) + - gsi::method ("parameters=", &ParseElementComponentsData::set_parameters, gsi::arg ("dict"), - "@brief Sets the (named) parameters\n" - ), - "@brief Supplies the return value for \\NetlistSpiceReaderDelegate#parse_element_components.\n" - "This is a structure with two members: 'strings' for the string arguments and 'parameters' for the " - "named arguments.\n" - "\n" - "This helper class has been introduced in version 0.27.1. Starting with version 0.28.6, named parameters can be string types too.\n" -); - -Class db_ParseElementData ("db", "ParseElementData", - gsi::method ("value", &ParseElementData::value, - "@brief Gets the value\n" - ) + - gsi::method ("value=", &ParseElementData::set_value, gsi::arg ("v"), - "@brief Sets the value\n" - ) + - gsi::method ("model_name", &ParseElementData::model_name, - "@brief Gets the model name\n" - ) + - gsi::method ("model_name=", &ParseElementData::set_model_name, gsi::arg ("m"), - "@brief Sets the model name\n" - ) + - gsi::method ("element_name", &ParseElementData::element_name, - "@brief Gets the element name\n" - "This attribute is available since version 0.31.0.\n" - ) + - gsi::method ("element_name=", &ParseElementData::set_element_name, gsi::arg ("e"), - "@brief Sets the element name\n" - "This attribute is available since version 0.31.0.\n" - ) + - gsi::method ("net_names", &ParseElementData::net_names, - "@brief Gets the net names\n" - ) + - gsi::method ("net_names=", &ParseElementData::set_net_names, gsi::arg ("list"), - "@brief Sets the net names\n" - ) + - gsi::method ("parameters", &ParseElementData::parameters, - "@brief Gets the (named) parameters\n" - ) + - gsi::method ("parameters=", &ParseElementData::set_parameters, gsi::arg ("dict"), - "@brief Sets the (named) parameters\n" - ), - "@brief Supplies the return value for \\NetlistSpiceReaderDelegate#parse_element.\n" - "This is a structure with four members: 'model_name' for the model name, 'value' for the default numerical value, 'net_names' for the net names and 'parameters' for the " - "named parameters.\n" - "\n" - "This helper class has been introduced in version 0.27.1. Starting with version 0.28.6, named parameters can be string types too.\n" -); - -static double get_delegate_scale (const db::NetlistSpiceReaderDelegate *delegate) -{ - return delegate->options ().scale; -} - -static void apply_parameter_scaling (const db::NetlistSpiceReaderDelegate *delegate, db::Device *device) -{ - delegate->apply_parameter_scaling (device); -} - -Class db_NetlistSpiceReaderDelegate ("db", "NetlistSpiceReaderDelegate", - gsi::method_ext ("start", &start_fb, "@hide") + - gsi::method_ext ("finish", &finish_fb, "@hide") + - gsi::method_ext ("wants_subcircuit", &wants_subcircuit_fb, "@hide") + - gsi::method_ext ("element", &element_fb, "@hide") + - gsi::method_ext ("parse_element", &parse_element_fb, "@hide") + - gsi::method_ext ("control_statement", &control_statement_fb, "@hide") + - gsi::method_ext ("translate_net_name", &translate_net_name_fb, "@hide") + - gsi::callback ("start", &NetlistSpiceReaderDelegateImpl::start, &NetlistSpiceReaderDelegateImpl::cb_start, gsi::arg ("netlist"), - "@brief This method is called when the reader starts reading a netlist\n" - ) + - gsi::callback ("finish", &NetlistSpiceReaderDelegateImpl::finish, &NetlistSpiceReaderDelegateImpl::cb_finish, gsi::arg ("netlist"), - "@brief This method is called when the reader is done reading a netlist successfully\n" - ) + - gsi::callback ("wants_subcircuit", &NetlistSpiceReaderDelegateImpl::wants_subcircuit, &NetlistSpiceReaderDelegateImpl::cb_wants_subcircuit, gsi::arg ("circuit_name"), - "@brief Returns true, if the delegate wants subcircuit elements with this name\n" - "The name is always upper case.\n" - ) + - gsi::callback ("control_statement", &NetlistSpiceReaderDelegateImpl::control_statement, &NetlistSpiceReaderDelegateImpl::cb_control_statement, gsi::arg ("line"), - "@brief Receives control statements not understood by the standard reader\n" - "When the reader encounters a control statement not understood by the parser, it will pass the line to the delegate using this method.\n" - "The delegate can decide if it wants to read this statement. It should return true in this case.\n" - "\n" - "This method has been introduced in version 0.27.1\n" - ) + - gsi::callback ("translate_net_name", &NetlistSpiceReaderDelegateImpl::translate_net_name, &NetlistSpiceReaderDelegateImpl::cb_translate_net_name, gsi::arg ("net_name"), - "@brief Translates a net name from the raw net name to the true net name\n" - "The default implementation will replace backslash sequences by the corresponding character.\n" - "'translate_net_name' is called before a net name is turned into a net object.\n" - "The method can be reimplemented to supply a different translation scheme for net names. For example, to translate special characters.\n" - "\n" - "This method has been introduced in version 0.27.1\n" - ) + - gsi::method ("variables", &NetlistSpiceReaderDelegateImpl::variables, - "@brief Gets the variables defined inside the SPICE file during execution of 'parse_element'\n" - "In order to evaluate formulas, this method allows accessing the variables that are " - "present during the execution of the SPICE reader.\n" - "\n" - "This method has been introduced in version 0.28.6." - ) + - gsi::callback ("parse_element", &NetlistSpiceReaderDelegateImpl::parse_element_helper, &NetlistSpiceReaderDelegateImpl::cb_parse_element, - gsi::arg ("s"), gsi::arg ("element"), - "@brief Parses an element card\n" - "@param s The specification part of the element line (the part after element code and name).\n" - "@param element The upper-case element code (\"M\", \"R\", ...).\n" - "@return A \\ParseElementData object with the parts of the element.\n" - "\n" - "This method receives a string with the element specification and the element code. It is supposed to " - "parse the element line and return a model name, a value, a list of net names and a parameter value dictionary.\n" - "\n" - "'parse_element' is called on every element card. The results of this call go into the \\element method " - "to actually create the device. This method can be reimplemented to support other flavors of SPICE.\n" - "\n" - "This method has been introduced in version 0.27.1\n" - ) + - gsi::callback ("element", &NetlistSpiceReaderDelegateImpl::element, &NetlistSpiceReaderDelegateImpl::cb_element, - gsi::arg ("circuit"), gsi::arg ("element"), gsi::arg ("name"), gsi::arg ("model"), gsi::arg ("value"), gsi::arg ("nets"), gsi::arg ("parameters"), - "@brief Makes a device from an element line\n" - "@param circuit The circuit that is currently read.\n" - "@param element The upper-case element code (\"M\", \"R\", ...).\n" - "@param name The element's name.\n" - "@param model The upper-case model name (may be empty).\n" - "@param value The default value (e.g. resistance for resistors) and may be zero.\n" - "@param nets The nets given in the element line.\n" - "@param parameters The parameters of the element statement (parameter names are upper case).\n" - "\n" - "The default implementation will create corresponding devices for\n" - "some known elements using the Spice writer's parameter conventions.\n" - "\n" - "The method must return true, if the element was was understood and false otherwise.\n" - "\n" - "Starting with version 0.28.6, the parameter values can be strings too." - ) + - gsi::method ("error", &NetlistSpiceReaderDelegateImpl::error, gsi::arg ("msg"), - "@brief Issues an error with the given message.\n" - "Use this method to generate an error." - ) + - gsi::method_ext ("get_scale", &get_delegate_scale, - "@brief Gets the scale factor set with '.options scale=...'\n" - "This method has been introduced in version 0.28.6." - ) + - gsi::method_ext ("apply_parameter_scaling", &apply_parameter_scaling, gsi::arg ("device"), - "@brief Applies parameter scaling to the given device\n" - "Applies SI scaling (according to the parameter's si_scaling attribute) and " - "geometry scaling (according to the parameter's geo_scale_exponent attribute) to " - "the device parameters. Use this method of finish the device when you have created " - "a custom device yourself.\n" - "\n" - "The geometry scale is taken from the '.options scale=...' control statement.\n" - "\n" - "This method has been introduced in version 0.28.6." - ) + - gsi::method_ext ("value_from_string", &value_from_string, gsi::arg ("s"), gsi::arg ("variables", db::NetlistSpiceReader::parameters_type (), "{}"), - "@brief Translates a string into a value\n" - "This function simplifies the implementation of SPICE readers by providing a translation of a unit-annotated string " - "into double values. For example, '1k' is translated to 1000.0. In addition, simple formula evaluation is supported, e.g " - "'(1+3)*2' is translated into 8.0.\n" - "\n" - "The variables dictionary defines named variables with the given values.\n" - "\n" - "This method has been introduced in version 0.27.1. The variables argument has been added in version 0.28.6.\n" - ) + - gsi::method_ext ("parse_element_components", &parse_element_components, gsi::arg ("s"), gsi::arg ("variables", db::NetlistSpiceReader::parameters_type (), "{}"), - "@brief Parses a string into string and parameter components.\n" - "This method is provided to simplify the implementation of 'parse_element'. It takes a string and splits it into " - "string arguments and parameter values. For example, 'a b c=6' renders two string arguments in 'nn' and one parameter ('C'->6.0). " - "It returns data \\ParseElementComponentsData object with the strings and parameters.\n" - "The parameter names are already translated to upper case.\n" - "\n" - "The variables dictionary defines named variables with the given values.\n" - "\n" - "This method has been introduced in version 0.27.1. The variables argument has been added in version 0.28.6.\n" - ), - "@brief Provides a delegate for the SPICE reader for translating device statements\n" - "Supply a customized class to provide a specialized reading scheme for devices. " - "You need a customized class if you want to implement device reading from model subcircuits or to " - "translate device parameters.\n" - "\n" - "See \\NetlistSpiceReader for more details.\n" - "\n" - "This class has been introduced in version 0.26. Profiles have been added to the device classes in version 0.31.0." -); - -namespace { - -class NetlistSpiceReaderWithOwnership - : public db::NetlistSpiceReader -{ -public: - NetlistSpiceReaderWithOwnership (NetlistSpiceReaderDelegateImpl *delegate) - : db::NetlistSpiceReader (delegate), m_ownership (delegate) - { - if (delegate) { - delegate->keep (); - } - } - -private: - tl::shared_ptr m_ownership; -}; - -} - -db::NetlistSpiceReader *new_spice_reader (const std::string &profile) -{ - auto *reader = new db::NetlistSpiceReader (); - reader->set_profile (profile); - return reader; -} - -db::NetlistSpiceReader *new_spice_reader2 (NetlistSpiceReaderDelegateImpl *delegate, const std::string &profile) -{ - auto *reader = new NetlistSpiceReaderWithOwnership (delegate); - reader->set_profile (profile); - return reader; -} - -static bool read_all_parameters (const db::NetlistSpiceReader *reader) -{ - return reader->delegate () ? reader->delegate ()->read_all_parameters () : false; -} - -static void set_read_all_parameters (db::NetlistSpiceReader *reader, bool f) -{ - if (reader->delegate ()) { - reader->delegate ()->set_read_all_parameters (f); - } -} - - -Class db_NetlistSpiceReader (db_NetlistReader, "db", "NetlistSpiceReader", - gsi::constructor ("new", &new_spice_reader, gsi::arg ("profile", std::string ()), - "@brief Creates a new reader.\n" - "The profile string defines the SPICE profile to use for picking default SPICE mappings from the " - "device classes declared in the netlist to read.\n" - "\n" - "The profile string argument has been added in version 0.31.0." - ) + - gsi::constructor ("new", &new_spice_reader2, gsi::arg ("delegate"), gsi::arg ("profile", std::string ()), - "@brief Creates a new reader with a delegate.\n" - "The profile string defines the SPICE profile to use for picking default SPICE mappings from the " - "device classes declared in the netlist to read.\n" - "\n" - "The profile string argument has been added in version 0.31.0." - ) + - gsi::method_ext ("read_all_parameters", &read_all_parameters, - "@brief Gets a flag indicating whether all parameters shall be read\n" - "\n" - "With this flag set to false, only those parameters which\n" - "are declared in the device classes of the netlist are read.\n" - "If set to true (the default), additional parameters are added to the device classes\n" - "and their values are stored in the device objects.\n" - "Note that this behavior can be changed in reimplementations of the\n" - "SPICE reader delegate class.\n" - "\n" - "This attribute has been introduced in version 0.31.0." - ) + - gsi::method_ext ("read_all_parameters=", &set_read_all_parameters, gsi::arg ("f"), - "@brief Sets a flag indicating whether all parameters shall be read\n" - "See \\read_all_parameters for details.\n" - "\n" - "This attribute has been introduced in version 0.31.0." - ), - "@brief Implements a netlist Reader for the SPICE format.\n" - "Use the SPICE reader like this:\n" - "\n" - "@code\n" - "reader = RBA::NetlistSpiceReader::new\n" - "netlist = RBA::Netlist::new\n" - "netlist.read(path, reader)\n" - "@/code\n" - "\n" - "The translation of SPICE elements can be tailored by providing a \\NetlistSpiceReaderDelegate class or by " - "supplying device classes in the netlist that declare their SPICe representation in a SPICE profile.\n" - // @@@ TODO: example for profiles in device classes - "\n" - "The following example is a delegate that turns subcircuits called HVNMOS and HVPMOS into " - "MOS4 devices with the parameters scaled by 1.5:\n" - "\n" - "@code\n" - "class MyDelegate < RBA::NetlistSpiceReaderDelegate\n" - "\n" - " # says we want to catch these subcircuits as devices\n" - " def wants_subcircuit(name)\n" - " name == \"HVNMOS\" || name == \"HVPMOS\"\n" - " end\n" - "\n" - " # translate the element\n" - " def element(circuit, el, name, model, value, nets, params)\n" - "\n" - " if el != \"X\"\n" - " # all other elements are left to the standard implementation\n" - " return super\n" - " end\n" - "\n" - " if nets.size != 4\n" - " error(\"Subcircuit #{model} needs four nodes\")\n" - " end\n" - "\n" - " # provide a device class\n" - " cls = circuit.netlist.device_class_by_name(model)\n" - " if ! cls\n" - " cls = RBA::DeviceClassMOS4Transistor::new\n" - " cls.name = model\n" - " circuit.netlist.add(cls)\n" - " end\n" - "\n" - " # create a device\n" - " device = circuit.create_device(cls, name)\n" - "\n" - " # and configure the device\n" - " [ \"S\", \"G\", \"D\", \"B\" ].each_with_index do |t,index|\n" - " device.connect_terminal(t, nets[index])\n" - " end\n" - " params.each do |p,value|\n" - " device.set_parameter(p, value * 1.5)\n" - " end\n" - "\n" - " end\n" - "\n" - "end\n" - "\n" - "# usage:\n" - "\n" - "mydelegate = MyDelegate::new\n" - "reader = RBA::NetlistSpiceReader::new(mydelegate)\n" - "\n" - "nl = RBA::Netlist::new\n" - "nl.read(input_file, reader)\n" - "@/code\n" - "\n" - "A somewhat contrived example for using the delegate to translate net names is this:\n" - "\n" - "@code\n" - "class MyDelegate < RBA::NetlistSpiceReaderDelegate\n" - "\n" - " # translates 'VDD' to 'VXX' and leave all other net names as is:\n" - " alias translate_net_name_org translate_net_name\n" - " def translate_net_name(n)\n" - " return n == \"VDD\" ? \"VXX\" : translate_net_name_org(n)}\n" - " end\n" - "\n" - "end\n" - "@/code\n" - "\n" - "This class has been introduced in version 0.26. It has been extended in version 0.27.1. " - "Profiles have been added to the device classes in version 0.31.0." -); - } diff --git a/src/db/db/gsiDeclDbNetlistCircuit.cc b/src/db/db/gsiDeclDbNetlistCircuit.cc new file mode 100644 index 000000000..a112235f5 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistCircuit.cc @@ -0,0 +1,499 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" +#include "tlGlobPattern.h" + +namespace gsi +{ + +static db::Net *create_net (db::Circuit *c, const std::string &name) +{ + db::Net *n = new db::Net (); + c->add_net (n); + n->set_name (name); + return n; +} + +static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const std::string &name) +{ + db::Device *d = new db::Device (dc, name); + c->add_device (d); + return d; +} + +static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, const std::string &name) +{ + db::SubCircuit *sc = new db::SubCircuit (cc, name); + c->add_subcircuit (sc); + return sc; +} + +static db::Net *circuit_net_for_pin (db::Circuit *c, const db::Pin *pin) +{ + return pin ? c->net_for_pin (pin->id ()) : 0; +} + +static const db::Net *circuit_net_for_pin_const (const db::Circuit *c, const db::Pin *pin) +{ + return pin ? c->net_for_pin (pin->id ()) : 0; +} + +static void circuit_connect_pin1 (db::Circuit *c, const db::Pin *pin, db::Net *net) +{ + if (pin) { + c->connect_pin (pin->id (), net); + } +} + +static void circuit_disconnect_pin (db::Circuit *c, size_t pin_id) +{ + c->connect_pin (pin_id, 0); +} + +static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin) +{ + if (pin) { + c->connect_pin (pin->id (), 0); + } +} + +static db::Pin *create_pin (db::Circuit *circuit, const std::string &name) +{ + return & circuit->add_pin (name); +} + +static std::vector +nets_non_const (const std::vector &nc) +{ + std::vector n; + n.reserve (nc.size ()); + for (auto i = nc.begin (); i != nc.end (); ++i) { + n.push_back (const_cast (*i)); + } + + return n; +} + +static std::vector +nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs) +{ + std::vector res; + if (! circuit) { + return res; + } + + tl::GlobPattern glob (name_pattern); + if (! cs.is_nil ()) { + glob.set_case_sensitive (cs.to_bool ()); + } else if (circuit->netlist ()) { + glob.set_case_sensitive (circuit->netlist ()->is_case_sensitive ()); + } + for (db::Circuit::const_net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { + const db::Net *net = n.operator-> (); + if (!net->name ().empty () && glob.match (net->name ())) { + res.push_back (net); + } + } + + return res; +} + +static std::vector +nets_by_name (db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs) +{ + return nets_non_const (nets_by_name_const (circuit, name_pattern, cs)); +} + +extern Class decl_dbNetlistObject; + +Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", + gsi::method_ext ("create_pin", &create_pin, gsi::arg ("name"), + "@brief Creates a new \\Pin object inside the circuit\n" + "This object will describe a pin of the circuit. A circuit connects " + "to the outside through such a pin. The pin is added after all existing " + "pins. For more details see the \\Pin class." + "\n\n" + "Starting with version 0.26.8, this method returns a reference to a \\Pin object rather than a copy." + ) + + gsi::method ("remove_pin", &db::Circuit::remove_pin, gsi::arg ("id"), + "@brief Removes the pin with the given ID from the circuit\n" + "\n" + "This method has been introduced in version 0.26.2.\n" + ) + + gsi::method ("rename_pin", &db::Circuit::rename_pin, gsi::arg ("id"), gsi::arg ("new_name"), + "@brief Renames the pin with the given ID to 'new_name'\n" + "\n" + "This method has been introduced in version 0.26.8.\n" + ) + + gsi::iterator ("each_child", (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_children, (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_children, + "@brief Iterates over the child circuits of this circuit\n" + "Child circuits are the ones that are referenced from this circuit via subcircuits." + ) + + gsi::iterator ("each_child", (db::Circuit::const_child_circuit_iterator (db::Circuit::*) () const) &db::Circuit::begin_children, (db::Circuit::const_child_circuit_iterator (db::Circuit::*) () const) &db::Circuit::end_children, + "@brief Iterates over the child circuits of this circuit (const version)\n" + "Child circuits are the ones that are referenced from this circuit via subcircuits." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::iterator ("each_parent", (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_parents, (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_parents, + "@brief Iterates over the parent circuits of this circuit\n" + "Child circuits are the ones that are referencing this circuit via subcircuits." + ) + + gsi::iterator ("each_parent", (db::Circuit::const_parent_circuit_iterator (db::Circuit::*) () const) &db::Circuit::begin_parents, (db::Circuit::const_parent_circuit_iterator (db::Circuit::*) () const) &db::Circuit::end_parents, + "@brief Iterates over the parent circuits of this circuit (const version)\n" + "Child circuits are the ones that are referencing this circuit via subcircuits." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("has_refs?", &db::Circuit::has_refs, + "@brief Returns a value indicating whether the circuit has references\n" + "A circuit has references if there is at least one subcircuit referring to it." + ) + + gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs, + "@brief Iterates over the subcircuit objects referencing this circuit\n" + ) + + gsi::iterator ("each_ref", (db::Circuit::const_refs_iterator (db::Circuit::*) () const) &db::Circuit::begin_refs, (db::Circuit::const_refs_iterator (db::Circuit::*) () const) &db::Circuit::end_refs, + "@brief Iterates over the subcircuit objects referencing this circuit (const version)\n" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins, + "@brief Iterates over the pins of the circuit" + ) + + gsi::iterator ("each_pin", (db::Circuit::const_pin_iterator (db::Circuit::*) () const) &db::Circuit::begin_pins, (db::Circuit::const_pin_iterator (db::Circuit::*) () const) &db::Circuit::end_pins, + "@brief Iterates over the pins of the circuit (const version)" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("device_by_id", (db::Device *(db::Circuit::*) (size_t)) &db::Circuit::device_by_id, gsi::arg ("id"), + "@brief Gets the device object for a given ID.\n" + "If the ID is not a valid device ID, nil is returned." + ) + + gsi::method ("device_by_id", (const db::Device *(db::Circuit::*) (size_t) const) &db::Circuit::device_by_id, gsi::arg ("id"), + "@brief Gets the device object for a given ID (const version).\n" + "If the ID is not a valid device ID, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("device_by_name", (db::Device *(db::Circuit::*) (const std::string &)) &db::Circuit::device_by_name, gsi::arg ("name"), + "@brief Gets the device object for a given name.\n" + "If the ID is not a valid device name, nil is returned." + ) + + gsi::method ("device_by_name", (const db::Device *(db::Circuit::*) (const std::string &) const) &db::Circuit::device_by_name, gsi::arg ("name"), + "@brief Gets the device object for a given name (const version).\n" + "If the ID is not a valid device name, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("subcircuit_by_id", (db::SubCircuit *(db::Circuit::*) (size_t)) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), + "@brief Gets the subcircuit object for a given ID.\n" + "If the ID is not a valid subcircuit ID, nil is returned." + ) + + gsi::method ("subcircuit_by_id", (const db::SubCircuit *(db::Circuit::*) (size_t) const) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), + "@brief Gets the subcircuit object for a given ID (const version).\n" + "If the ID is not a valid subcircuit ID, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("subcircuit_by_name", (db::SubCircuit *(db::Circuit::*) (const std::string &)) &db::Circuit::subcircuit_by_name, gsi::arg ("name"), + "@brief Gets the subcircuit object for a given name.\n" + "If the ID is not a valid subcircuit name, nil is returned." + ) + + gsi::method ("subcircuit_by_name", (const db::SubCircuit *(db::Circuit::*) (const std::string &) const) &db::Circuit::subcircuit_by_name, gsi::arg ("name"), + "@brief Gets the subcircuit object for a given name (const version).\n" + "If the ID is not a valid subcircuit name, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("net_by_cluster_id", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_by_cluster_id, gsi::arg ("cluster_id"), + "@brief Gets the net object corresponding to a specific cluster ID\n" + "If the ID is not a valid pin cluster ID, nil is returned." + ) + + gsi::method ("net_by_name", (db::Net *(db::Circuit::*) (const std::string &)) &db::Circuit::net_by_name, gsi::arg ("name"), + "@brief Gets the net object for a given name.\n" + "If the ID is not a valid net name, nil is returned." + ) + + gsi::method ("net_by_name", (const db::Net *(db::Circuit::*) (const std::string &) const) &db::Circuit::net_by_name, gsi::arg ("name"), + "@brief Gets the net object for a given name (const version).\n" + "If the ID is not a valid net name, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method_ext ("nets_by_name", &nets_by_name, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), + "@brief Gets the net objects for a given name filter.\n" + "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + "The 'case_sensitive' argument has been added in version 0.30.2." + ) + + gsi::method_ext ("nets_by_name", &nets_by_name_const, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), + "@brief Gets the net objects for a given name filter (const version).\n" + "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" + "\n" + "This constness variant has been introduced in version 0.27.3.\n" + "The 'case_sensitive' argument has been added in version 0.30.2." + ) + + gsi::method ("pin_by_id", (db::Pin *(db::Circuit::*) (size_t)) &db::Circuit::pin_by_id, gsi::arg ("id"), + "@brief Gets the \\Pin object corresponding to a specific ID\n" + "If the ID is not a valid pin ID, nil is returned." + ) + + gsi::method ("pin_by_id", (const db::Pin *(db::Circuit::*) (size_t) const) &db::Circuit::pin_by_id, gsi::arg ("id"), + "@brief Gets the \\Pin object corresponding to a specific ID (const version)\n" + "If the ID is not a valid pin ID, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("pin_by_name", (db::Pin *(db::Circuit::*) (const std::string &)) &db::Circuit::pin_by_name, gsi::arg ("name"), + "@brief Gets the \\Pin object corresponding to a specific name\n" + "If the ID is not a valid pin name, nil is returned." + ) + + gsi::method ("pin_by_name", (const db::Pin *(db::Circuit::*) (const std::string &) const) &db::Circuit::pin_by_name, gsi::arg ("name"), + "@brief Gets the \\Pin object corresponding to a specific name (const version)\n" + "If the ID is not a valid pin name, nil is returned." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("pin_count", &db::Circuit::pin_count, + "@brief Gets the number of pins in the circuit" + ) + + gsi::method_ext ("create_net", &gsi::create_net, gsi::arg ("name", std::string ()), + "@brief Creates a new \\Net object inside the circuit\n" + "This object will describe a net of the circuit. The nets are basically " + "connections between the different components of the circuit (subcircuits, " + "devices and pins).\n" + "\n" + "A net needs to be filled with references to connect to specific objects. " + "See the \\Net class for more details." + ) + + gsi::method ("remove_net", &db::Circuit::remove_net, gsi::arg ("net"), + "@brief Removes the given net from the circuit\n" + ) + + gsi::method ("join_nets", &db::Circuit::join_nets, gsi::arg ("net"), gsi::arg ("with"), + "@brief Joins (connects) two nets into one\n" + "This method will connect the 'with' net with 'net' and remove 'with'.\n" + "\n" + "This method has been introduced in version 0.26.4. Starting with version 0.28.13, " + "net names will be formed from both input names, combining them with as a comma-separated list." + ) + + gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, + "@brief Iterates over the nets of the circuit" + ) + + gsi::iterator ("each_net", (db::Circuit::const_net_iterator (db::Circuit::*) () const) &db::Circuit::begin_nets, (db::Circuit::const_net_iterator (db::Circuit::*) () const) &db::Circuit::end_nets, + "@brief Iterates over the nets of the circuit (const version)" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method_ext ("create_device", &gsi::create_device1, gsi::arg ("device_class"), gsi::arg ("name", std::string ()), + "@brief Creates a new bound \\Device object inside the circuit\n" + "This object describes a device of the circuit. The device is already attached " + "to the device class. The name is optional and is used to identify the device in a " + "netlist file.\n" + "\n" + "For more details see the \\Device class." + ) + + gsi::method ("remove_device", &db::Circuit::remove_device, gsi::arg ("device"), + "@brief Removes the given device from the circuit\n" + ) + + gsi::iterator ("each_device", (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::begin_devices, (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::end_devices, + "@brief Iterates over the devices of the circuit" + ) + + gsi::iterator ("each_device", (db::Circuit::const_device_iterator (db::Circuit::*) () const) &db::Circuit::begin_devices, (db::Circuit::const_device_iterator (db::Circuit::*) () const) &db::Circuit::end_devices, + "@brief Iterates over the devices of the circuit (const version)" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit1, gsi::arg ("circuit"), gsi::arg ("name", std::string ()), + "@brief Creates a new bound \\SubCircuit object inside the circuit\n" + "This object describes an instance of another circuit inside the circuit. The subcircuit is already attached " + "to the other circuit. The name is optional and is used to identify the subcircuit in a " + "netlist file.\n" + "\n" + "For more details see the \\SubCircuit class." + ) + + gsi::method ("remove_subcircuit", &db::Circuit::remove_subcircuit, gsi::arg ("subcircuit"), + "@brief Removes the given subcircuit from the circuit\n" + ) + + gsi::method ("flatten_subcircuit", &db::Circuit::flatten_subcircuit, gsi::arg ("subcircuit"), + "@brief Flattens a subcircuit\n" + "This method will substitute the given subcircuit by its contents. The subcircuit is removed " + "after this." + ) + + gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, + "@brief Iterates over the subcircuits of the circuit" + ) + + gsi::iterator ("each_subcircuit", (db::Circuit::const_subcircuit_iterator (db::Circuit::*) () const) &db::Circuit::begin_subcircuits, (db::Circuit::const_subcircuit_iterator (db::Circuit::*) () const) &db::Circuit::end_subcircuits, + "@brief Iterates over the subcircuits of the circuit (const version)" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("blank", &db::Circuit::blank, + "@brief Blanks out the circuit\n" + "This method will remove all the innards of the circuit and just leave the pins. " + "The pins won't be connected to inside nets anymore, but the circuit can still be " + "called by subcircuit references. " + "This method will eventually create a 'circuit abstract' (or black box). It will " + "set the \\dont_purge flag to mark this circuit as 'intentionally empty'." + ) + + gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist, + "@brief Gets the netlist object the circuit lives in" + ) + + gsi::method ("netlist", (const db::Netlist *(db::Circuit::*) () const) &db::Circuit::netlist, + "@brief Gets the netlist object the circuit lives in (const version)" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("name=", &db::Circuit::set_name, gsi::arg ("name"), + "@brief Sets the name of the circuit" + ) + + gsi::method ("name", &db::Circuit::name, + "@brief Gets the name of the circuit" + ) + + gsi::method ("boundary=", &db::Circuit::set_boundary, gsi::arg ("boundary"), + "@brief Sets the boundary of the circuit" + ) + + gsi::method ("boundary", &db::Circuit::boundary, + "@brief Gets the boundary of the circuit" + ) + + gsi::method ("dont_purge", &db::Circuit::dont_purge, + "@brief Gets a value indicating whether the circuit can be purged on \\Netlist#purge.\n" + ) + + gsi::method ("dont_purge=", &db::Circuit::set_dont_purge, gsi::arg ("f"), + "@brief Sets a value indicating whether the circuit can be purged on \\Netlist#purge.\n" + "If this attribute is set to true, \\Netlist#purge will never delete this circuit.\n" + "This flag therefore marks this circuit as 'precious'." + ) + + gsi::method ("cell_index=", &db::Circuit::set_cell_index, gsi::arg ("cell_index"), + "@brief Sets the cell index\n" + "The cell index relates a circuit with a cell from a layout. It's intended to " + "hold a cell index number if the netlist was extracted from a layout.\n" + ) + + gsi::method ("cell_index", &db::Circuit::cell_index, + "@brief Gets the cell index of the circuit\n" + "See \\cell_index= for details.\n" + ) + + gsi::method ("net_for_pin", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net object attached to a specific pin.\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin ID is invalid." + ) + + gsi::method ("net_for_pin", (const db::Net *(db::Circuit::*) (size_t) const) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net object attached to a specific pin (const version).\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin ID is invalid." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method_ext ("net_for_pin", &gsi::circuit_net_for_pin, gsi::arg ("pin"), + "@brief Gets the net object attached to a specific pin.\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin object is nil." + ) + + gsi::method_ext ("net_for_pin", &gsi::circuit_net_for_pin_const, gsi::arg ("pin"), + "@brief Gets the net object attached to a specific pin (const version).\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin object is nil." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("connect_pin", &db::Circuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), + "@brief Connects the given pin with the given net.\n" + "The net must be one inside the circuit. Any previous connected is resolved before this " + "connection is made. A pin can only be connected to one net at a time." + ) + + gsi::method_ext ("connect_pin", &gsi::circuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), + "@brief Connects the given pin with the given net.\n" + "The net and the pin must be objects from inside the circuit. Any previous connected is resolved before this " + "connection is made. A pin can only be connected to one net at a time." + ) + + gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin, gsi::arg ("pin_id"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin1, gsi::arg ("pin"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method ("clear", &db::Circuit::clear, + "@brief Clears the circuit\n" + "This method removes all objects and clears the other attributes." + ) + + gsi::method ("combine_devices", &db::Circuit::combine_devices, + "@brief Combines devices where possible\n" + "This method will combine devices that can be combined according " + "to their device classes 'combine_devices' method.\n" + "For example, serial or parallel resistors can be combined into " + "a single resistor.\n" + ) + + gsi::method ("purge_devices", &db::Circuit::purge_devices, + "@brief Purges invalid devices.\n" + "Purges devices which are considered invalid. Such devices are for example those whose terminals are all connected to a single net.\n" + "\n" + "This method has been added in version 0.29.7." + ) + + gsi::method ("purge_nets", &db::Circuit::purge_nets, + "@brief Purges floating nets.\n" + "Floating nets are nets with no device or subcircuit attached to. Such floating " + "nets are removed in this step. If these nets are connected outward to a circuit pin, this " + "circuit pin is also removed." + ) + + gsi::method ("purge_nets_keep_pins", &db::Circuit::purge_nets_keep_pins, + "@brief Purges floating nets but keep pins.\n" + "This method will remove floating nets like \\purge_nets, but if these nets are attached " + "to a pin, the pin will be left disconnected from any net.\n" + "\n" + "This method has been introduced in version 0.26.2.\n" + ), + "@brief Circuits are the basic building blocks of the netlist\n" + "A circuit has pins by which it can connect to the outside. Pins are " + "created using \\create_pin and are represented by the \\Pin class.\n" + "\n" + "Furthermore, a circuit manages the components of the netlist. " + "Components are devices (class \\Device) and subcircuits (class \\SubCircuit). " + "Devices are basic devices such as resistors or transistors. Subcircuits " + "are other circuits to which nets from this circuit connect. " + "Devices are created using the \\create_device method. Subcircuits are " + "created using the \\create_subcircuit method.\n" + "\n" + "Devices are connected through 'terminals', subcircuits are connected through " + "their pins. Terminals and pins are described by integer ID's in the context of " + "most methods.\n" + "\n" + "Finally, the circuit consists of the nets. Nets connect terminals of devices " + "and pins of subcircuits or the circuit itself. Nets are created using " + "\\create_net and are represented by objects of the \\Net class.\n" + "See there for more about nets.\n" + "\n" + "The Circuit object is only valid if the netlist object is alive. " + "Circuits must be added to a netlist using \\Netlist#add to become " + "part of the netlist.\n" + "\n" + "The Circuit class has been introduced in version 0.26." +); + +} + diff --git a/src/db/db/gsiDeclDbNetlistDevice.cc b/src/db/db/gsiDeclDbNetlistDevice.cc new file mode 100644 index 000000000..75d9558c6 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistDevice.cc @@ -0,0 +1,460 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +static void device_connect_terminal_by_name (db::Device *device, const std::string &terminal_name, db::Net *net) +{ + if (! device->device_class ()) { + throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); + } + size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); + device->connect_terminal (terminal_id, net); +} + +static void device_disconnect_terminal (db::Device *device, size_t terminal_id) +{ + device->connect_terminal (terminal_id, 0); +} + +static void device_disconnect_terminal_by_name (db::Device *device, const std::string &terminal_name) +{ + device_connect_terminal_by_name (device, terminal_name, 0); +} + +static size_t get_device_index (const db::DeviceReconnectedTerminal *obj) +{ + return obj->device_index; +} + +static void set_device_index (db::DeviceReconnectedTerminal *obj, size_t device_index) +{ + obj->device_index = device_index; +} + +static size_t get_other_terminal_id (const db::DeviceReconnectedTerminal *obj) +{ + return obj->other_terminal_id; +} + +static void set_other_terminal_id (db::DeviceReconnectedTerminal *obj, unsigned int other_terminal_id) +{ + obj->other_terminal_id = other_terminal_id; +} + +Class decl_dbDeviceReconnectedTerminal ("db", "DeviceReconnectedTerminal", + gsi::method_ext ("device_index=", &set_device_index, gsi::arg ("device_index"), + "@brief The device abstract index setter.\n" + "See the class description for details." + ) + + gsi::method_ext ("device_index", &get_device_index, + "@brief The device abstract index getter.\n" + "See the class description for details." + ) + + gsi::method_ext ("other_terminal_id=", &set_other_terminal_id, gsi::arg ("other_terminal_id"), + "@brief The setter for the abstract's connected terminal.\n" + "See the class description for details." + ) + + gsi::method_ext ("other_terminal_id", &get_other_terminal_id, + "@brief The getter for the abstract's connected terminal.\n" + "See the class description for details." + ), + "@brief Describes a terminal rerouting in combined devices.\n" + "Combined devices are implemented as a generalization of the device abstract concept in \\Device. For " + "combined devices, multiple \\DeviceAbstract references are present. To support different combination schemes, " + "device-to-abstract routing is supported. Parallel combinations will route all outer terminals to corresponding " + "terminals of all device abstracts (because of terminal swapping these may be different ones).\n" + "\n" + "This object describes one route to an abstract's terminal. The device index is 0 for the main device abstract and " + "1 for the first combined device abstract.\n" + "\n" + "This class has been introduced in version 0.26.\n" +); + +static const db::DeviceAbstract *get_device_abstract (const db::DeviceAbstractRef *obj) +{ + return obj->device_abstract; +} + +static void set_device_abstract (db::DeviceAbstractRef *obj, const db::DeviceAbstract *device_abstract) +{ + obj->device_abstract = device_abstract; +} + +static db::DCplxTrans get_trans (const db::DeviceAbstractRef *obj) +{ + return obj->trans; +} + +static void set_trans (db::DeviceAbstractRef *obj, const db::DCplxTrans &trans) +{ + obj->trans = trans; +} + +Class decl_dbDeviceAbstractRef ("db", "DeviceAbstractRef", + gsi::method_ext ("device_abstract=", &set_device_abstract, gsi::arg ("device_abstract"), + "@brief The setter for the device abstract reference.\n" + "See the class description for details." + ) + + gsi::method_ext ("device_abstract", &get_device_abstract, + "@brief The getter for the device abstract reference.\n" + "See the class description for details." + ) + + gsi::method_ext ("trans=", &set_trans, gsi::arg ("tr"), + "@brief The setter for the relative transformation of the instance.\n" + "See the class description for details." + ) + + gsi::method_ext ("trans", &get_trans, + "@brief The getter for the relative transformation of the instance.\n" + "See the class description for details." + ), + "@brief Describes an additional device abstract reference for combined devices.\n" + "Combined devices are implemented as a generalization of the device abstract concept in \\Device. For " + "combined devices, multiple \\DeviceAbstract references are present. This class describes such an " + "additional reference. A reference is a pointer to an abstract plus a transformation by which the abstract " + "is transformed geometrically as compared to the first (initial) abstract.\n" + "\n" + "This class has been introduced in version 0.26.\n" +); + +static bool is_combined_device (const db::Device *device) +{ + return ! device->reconnected_terminals ().empty (); +} + +static std::vector empty; + +static std::vector::const_iterator begin_reconnected_terminals_for (const db::Device *device, size_t terminal_id) +{ + const std::vector *ti = device->reconnected_terminals_for ((unsigned int) terminal_id); + if (! ti) { + return empty.begin (); + } else { + return ti->begin (); + } +} + +static std::vector::const_iterator end_reconnected_terminals_for (const db::Device *device, size_t terminal_id) +{ + const std::vector *ti = device->reconnected_terminals_for ((unsigned int) terminal_id); + if (! ti) { + return empty.end (); + } else { + return ti->end (); + } +} + +static void clear_reconnected_terminals (db::Device *device) +{ + device->reconnected_terminals ().clear (); +} + +static void add_reconnected_terminals (db::Device *device, size_t outer_terminal, const db::DeviceReconnectedTerminal &t) +{ + device->reconnected_terminals () [(unsigned int) outer_terminal].push_back (t); +} + +static std::vector::const_iterator begin_other_abstracts (const db::Device *device) +{ + return device->other_abstracts ().begin (); +} + +static std::vector::const_iterator end_other_abstracts (const db::Device *device) +{ + return device->other_abstracts ().end (); +} + +static void clear_other_abstracts (db::Device *device) +{ + device->other_abstracts ().clear (); +} + +static void add_other_abstracts (db::Device *device, const db::DeviceAbstractRef &ref) +{ + device->other_abstracts ().push_back (ref); +} + +static const db::Net *net_for_terminal_by_name_const (const db::Device *device, const std::string &name) +{ + if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { + return 0; + } else { + return device->net_for_terminal (device->device_class ()->terminal_id_for_name (name)); + } +} + +static db::Net *net_for_terminal_by_name (db::Device *device, const std::string &name) +{ + if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { + return 0; + } else { + return device->net_for_terminal (device->device_class ()->terminal_id_for_name (name)); + } +} + +static const db::NetTerminalRef *terminal_ref_by_name_const (const db::Device *device, const std::string &name) +{ + if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { + return 0; + } else { + return device->terminal_ref_for_terminal (device->device_class ()->terminal_id_for_name (name)); + } +} + +static db::NetTerminalRef *terminal_ref_by_name (db::Device *device, const std::string &name) +{ + if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { + return 0; + } else { + return device->terminal_ref_for_terminal (device->device_class ()->terminal_id_for_name (name)); + } +} + +extern Class decl_dbNetlistObject; + +Class decl_dbDevice (decl_dbNetlistObject, "db", "Device", + gsi::method ("device_class", &db::Device::device_class, + "@brief Gets the device class the device belongs to.\n" + ) + + gsi::method ("device_abstract", &db::Device::device_abstract, + "@brief Gets the device abstract for this device instance.\n" + "See \\DeviceAbstract for more details.\n" + ) + + gsi::method ("device_abstract=", &db::Device::set_device_abstract, gsi::arg ("device_abstract"), + "@hide\n" + "Provided for test purposes mainly. Be careful with pointers!" + ) + + gsi::method_ext ("is_combined_device?", &is_combined_device, + "@brief Returns true, if the device is a combined device.\n" + "Combined devices feature multiple device abstracts and device-to-abstract terminal connections.\n" + "See \\each_reconnected_terminal and \\each_combined_abstract for more details.\n" + ) + + gsi::iterator_ext ("each_reconnected_terminal_for", &begin_reconnected_terminals_for, &end_reconnected_terminals_for, gsi::arg ("terminal_id"), + "@brief Iterates over the reconnected terminal specifications for a given outer terminal.\n" + "This feature applies to combined devices. This iterator will deliver all device-to-abstract terminal reroutings.\n" + ) + + gsi::method_ext ("clear_reconnected_terminals", &clear_reconnected_terminals, + "@hide\n" + "Provided for test purposes mainly." + ) + + gsi::method_ext ("add_reconnected_terminal_for", &add_reconnected_terminals, gsi::arg ("outer_terminal"), gsi::arg ("descriptor"), + "@hide\n" + "Provided for test purposes mainly." + ) + + gsi::iterator_ext ("each_combined_abstract", &begin_other_abstracts, &end_other_abstracts, + "@brief Iterates over the combined device specifications.\n" + "This feature applies to combined devices. This iterator will deliver all device abstracts present in addition to the default device abstract.\n" + ) + + gsi::method_ext ("clear_combined_abstracts", &clear_other_abstracts, + "@hide\n" + "Provided for test purposes mainly." + ) + + gsi::method_ext ("add_combined_abstract", &add_other_abstracts, gsi::arg ("ref"), + "@hide\n" + "Provided for test purposes mainly." + ) + + gsi::method ("circuit", (const db::Circuit *(db::Device::*) () const) &db::Device::circuit, + "@brief Gets the circuit the device lives in." + ) + + gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit, + "@brief Gets the circuit the device lives in (non-const version)." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("id", &db::Device::id, + "@brief Gets the device ID.\n" + "The ID is a unique integer which identifies the device.\n" + "It can be used to retrieve the device from the circuit using \\Circuit#device_by_id.\n" + "When assigned, the device ID is not 0.\n" + ) + + gsi::method ("name=", &db::Device::set_name, gsi::arg ("name"), + "@brief Sets the name of the device.\n" + "Device names are used to name a device inside a netlist file. " + "Device names should be unique within a circuit." + ) + + gsi::method ("name", &db::Device::name, + "@brief Gets the name of the device.\n" + ) + + gsi::method ("trans=", &db::Device::set_trans, gsi::arg ("t"), + "@brief Sets the location of the device.\n" + "The device location is essentially describing the position of the device. The position is typically the center of some " + "recognition shape. In this case the transformation is a plain displacement to the center of this shape." + ) + + gsi::method ("trans", &db::Device::trans, + "@brief Gets the location of the device.\n" + "See \\trans= for details about this method." + ) + + gsi::method ("expanded_name", &db::Device::expanded_name, + "@brief Gets the expanded name of the device.\n" + "The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. " + ) + + gsi::method ("net_for_terminal", (const db::Net *(db::Device::*) (size_t) const) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the net connected to the specified terminal.\n" + "If the terminal is not connected, nil is returned for the net." + ) + + gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the net connected to the specified terminal (non-const version).\n" + "If the terminal is not connected, nil is returned for the net." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method_ext ("net_for_terminal", &net_for_terminal_by_name_const, gsi::arg ("terminal_name"), + "@brief Gets the net connected to the specified terminal.\n" + "If the terminal is not connected, nil is returned for the net." + "\n\n" + "This convenience method has been introduced in version 0.27.3.\n" + ) + + gsi::method_ext ("net_for_terminal", &net_for_terminal_by_name, gsi::arg ("terminal_name"), + "@brief Gets the net connected to the specified terminal (non-const version).\n" + "If the terminal is not connected, nil is returned for the net." + "\n\n" + "This convenience method has been introduced in version 0.27.3.\n" + ) + + gsi::method ("terminal_ref", (const db::NetTerminalRef *(db::Device::*) (size_t) const) &db::Device::terminal_ref_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the terminal refeference for a specific terminal.\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method ("terminal_ref", (db::NetTerminalRef *(db::Device::*) (size_t)) &db::Device::terminal_ref_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the terminal refeference for a specific terminal (non-const version).\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method_ext ("terminal_ref", &terminal_ref_by_name_const, gsi::arg ("terminal_name"), + "@brief Gets the terminal refeference for a specific terminal where the terminal is given by name.\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method_ext ("terminal_ref", &terminal_ref_by_name, gsi::arg ("terminal_name"), + "@brief Gets the terminal refeference for a specific terminal where the terminal is given by name (non-const version).\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method ("connect_terminal", &db::Device::connect_terminal, gsi::arg ("terminal_id"), gsi::arg ("net"), + "@brief Connects the given terminal to the specified net.\n" + ) + + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), + "@brief Disconnects the given terminal from any net.\n" + "If the terminal has been connected to a global, this connection will be disconnected too." + ) + + gsi::method_ext ("connect_terminal", &device_connect_terminal_by_name, gsi::arg ("terminal_name"), gsi::arg ("net"), + "@brief Connects the given terminal to the specified net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised.\n" + "If the terminal has been connected to a global net, it will be disconnected from there." + ) + + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal_by_name, gsi::arg ("terminal_name"), + "@brief Disconnects the given terminal from any net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." + ) + + gsi::method ("parameter", (const tl::Variant &(db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), + "@brief Gets the parameter value for the given parameter ID." + ) + + gsi::method ("set_parameter", (void (db::Device::*) (size_t, const tl::Variant &)) &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"), + "@brief Sets the parameter value for the given parameter ID." + ) + + gsi::method ("parameter", (const tl::Variant &(db::Device::*) (const std::string &) const) &db::Device::parameter_value, gsi::arg ("param_name"), + "@brief Gets the parameter value for the given parameter name.\n" + "If the parameter name is not valid, an exception is thrown." + ) + + gsi::method ("set_parameter", (void (db::Device::*) (const std::string &, const tl::Variant &)) &db::Device::set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"), + "@brief Sets the parameter value for the given parameter name.\n" + "If the parameter name is not valid, an exception is thrown." + ) + + gsi::method ("set_parameter_create", (void (db::Device::*) (const std::string &, const tl::Variant &, bool, const tl::Variant &)) &db::Device::set_parameter_value_create, gsi::arg ("param_name"), gsi::arg ("value"), gsi::arg ("primary", true), gsi::arg ("default_value", tl::Variant (0.0), "none"), + "@brief Sets the parameter value for the given parameter name and creates a new parameter if no parameter is present with this name.\n" + "If no parameter with the given name exists in the device class's parameter table, a new parameter is created. " + "\\primary indicates that this parameter will be a primary one and \\default_value is the default value for this parameter. " + "The default value also implicitly defines the type of the parameter. A float value will create a float-type parameter, a string value for default will create a string-type parameter.\n" + "\n" + "This method has been introduced in version 0.31.0." + ), + "@brief A device inside a circuit.\n" + "Device object represent atomic devices such as resistors, diodes or transistors. " + "The \\Device class represents a particular device with specific parameters. " + "The type of device is represented by a \\DeviceClass object. Device objects " + "live in \\Circuit objects, the device class objects live in the \\Netlist object.\n" + "\n" + "Devices connect to nets through terminals. Terminals are described by a terminal ID which is " + "essentially the zero-based index of the terminal. Terminal definitions can be " + "obtained from the device class using the \\DeviceClass#terminal_definitions method.\n" + "\n" + "Devices connect to nets through the \\Device#connect_terminal method. " + "Device terminals can be disconnected using \\Device#disconnect_terminal.\n" + "\n" + "Device objects are created inside a circuit with \\Circuit#create_device.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbDeviceAbstract ("db", "DeviceAbstract", + gsi::method ("netlist", (const db::Netlist *(db::DeviceAbstract::*) () const) &db::DeviceAbstract::netlist, + "@brief Gets the netlist the device abstract lives in." + ) + + gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist, + "@brief Gets the netlist the device abstract lives in (non-const version)." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("device_class", &db::DeviceAbstract::device_class, + "@brief Gets the device class of the device." + ) + + gsi::method ("name=", &db::DeviceAbstract::set_name, gsi::arg ("name"), + "@brief Sets the name of the device abstract.\n" + "Device names are used to name a device abstract inside a netlist file. " + "Device names should be unique within a netlist." + ) + + gsi::method ("name", &db::DeviceAbstract::name, + "@brief Gets the name of the device abstract.\n" + ) + + gsi::method ("cell_index", &db::DeviceAbstract::cell_index, + "@brief Gets the cell index of the device abstract.\n" + "This is the cell that represents the device." + ) + + gsi::method ("cluster_id_for_terminal", &db::DeviceAbstract::cluster_id_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the cluster ID for the given terminal.\n" + "The cluster ID links the terminal to geometrical shapes within the clusters of the cell (see \\cell_index)" + ), + "@brief A geometrical device abstract\n" + "This class represents the geometrical model for the device. It links into the extracted layout " + "to a cell which holds the terminal shapes for the device.\n" + "\n" + "This class has been added in version 0.26." +); + +} + diff --git a/src/db/db/gsiDeclDbNetlistDeviceClass.cc b/src/db/db/gsiDeclDbNetlistDeviceClass.cc new file mode 100644 index 000000000..213c4ab54 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistDeviceClass.cc @@ -0,0 +1,671 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +static db::DeviceTerminalDefinition *new_terminal_definition (const std::string &name, const std::string &description) +{ + return new db::DeviceTerminalDefinition (name, description); +} + +Class decl_dbDeviceTerminalDefinition ("db", "DeviceTerminalDefinition", + gsi::constructor ("new", &gsi::new_terminal_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), + "@brief Creates a new terminal definition." + ) + + gsi::method ("name", &db::DeviceTerminalDefinition::name, + "@brief Gets the name of the terminal." + ) + + gsi::method ("name=", &db::DeviceTerminalDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the terminal." + ) + + gsi::method ("description", &db::DeviceTerminalDefinition::description, + "@brief Gets the description of the terminal." + ) + + gsi::method ("description=", &db::DeviceTerminalDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the terminal." + ) + + gsi::method ("id", &db::DeviceTerminalDefinition::id, + "@brief Gets the ID of the terminal.\n" + "The ID of the terminal is used in some places to refer to a specific terminal (e.g. in " + "the \\NetTerminalRef object)." + ), + "@brief A terminal descriptor\n" + "This class is used inside the \\DeviceClass class to describe a terminal of the device.\n" + "\n" + "This class has been added in version 0.26." +); + +static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling, double geo_scaling_exponent) +{ + return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling, geo_scaling_exponent); +} + +Class decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition", + gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0), gsi::arg ("geo_scaling_exponent", 0.0), + "@brief Creates a new parameter definition.\n" + "@param name The name of the parameter\n" + "@param description The human-readable description\n" + "@param default_value The initial value\n" + "@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\n" + "@param si_scaling The scaling factor to SI units\n" + "@param geo_scaling_exponent Indicates how the parameter scales with geometrical scaling (0: no scaling, 1.0: linear, 2.0: quadratic)\n" + ) + + gsi::method ("name", &db::DeviceParameterDefinition::name, + "@brief Gets the name of the parameter." + ) + + gsi::method ("name=", &db::DeviceParameterDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the parameter." + ) + + gsi::method ("description", &db::DeviceParameterDefinition::description, + "@brief Gets the description of the parameter." + ) + + gsi::method ("description=", &db::DeviceParameterDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the parameter." + ) + + gsi::method ("default_value", &db::DeviceParameterDefinition::default_value, + "@brief Gets the default value of the parameter." + ) + + gsi::method ("default_value=", &db::DeviceParameterDefinition::set_default_value, gsi::arg ("default_value"), + "@brief Sets the default value of the parameter.\n" + "The default value is used to initialize parameters of \\Device objects." + ) + + gsi::method ("is_primary?", &db::DeviceParameterDefinition::is_primary, + "@brief Gets a value indicating whether the parameter is a primary parameter\n" + "See \\is_primary= for details about this predicate." + ) + + gsi::method ("is_primary=", &db::DeviceParameterDefinition::set_is_primary, gsi::arg ("primary"), + "@brief Sets a value indicating whether the parameter is a primary parameter\n" + "If this flag is set to true (the default), the parameter is considered a primary parameter.\n" + "Only primary parameters are compared by default.\n" + ) + + gsi::method ("si_scaling", &db::DeviceParameterDefinition::si_scaling, + "@brief Gets the scaling factor to SI units.\n" + "For parameters in micrometers - for example W and L of MOS devices - this factor can be set to 1e-6 to reflect " + "the unit. SI unit scaling is only applied to parameters of the 'float' type. It is not applied to integer-type " + "or string parameters." + ) + + gsi::method ("si_scaling=", &db::DeviceParameterDefinition::set_si_scaling, gsi::arg ("flag"), + "@brief Sets the scaling factor to SI units.\n" + "See \\si_scaling for details.\n" + "\n" + "This setter has been added in version 0.28.6." + ) + + gsi::method ("geo_scaling_exponent", &db::DeviceParameterDefinition::geo_scaling_exponent, + "@brief Gets the geometry scaling exponent.\n" + "This value is used when applying '.options scale' in the SPICE reader for example. " + "It is zero for 'no scaling', 1.0 for linear scaling and 2.0 for quadratic scaling.\n" + "Geometric scaling is only applied to parameters of the 'float' type. It is not applied to integer-type " + "or string parameters.\n" + "\n" + "This attribute has been added in version 0.28.6." + ) + + gsi::method ("geo_scaling_exponent=", &db::DeviceParameterDefinition::set_geo_scaling_exponent, gsi::arg ("expo"), + "@brief Sets the geometry scaling exponent.\n" + "See \\geo_scaling_exponent for details.\n" + "\n" + "This attribute has been added in version 0.28.6." + ) + + gsi::method ("id", &db::DeviceParameterDefinition::id, + "@brief Gets the ID of the parameter.\n" + "The ID of the parameter is used in some places to refer to a specific parameter (e.g. in " + "the \\NetParameterRef object)." + ), + "@brief A parameter descriptor\n" + "This class is used inside the \\DeviceClass class to describe a parameter of the device.\n" + "\n" + "This class has been added in version 0.26." +); + +namespace +{ + +/** + * @brief A DeviceParameterCompare implementation that allows reimplementation of the virtual methods + */ +class GenericDeviceParameterCompare + : public db::EqualDeviceParameters +{ +public: + GenericDeviceParameterCompare () + : db::EqualDeviceParameters () + { + // .. nothing yet .. + } + + virtual bool less (const db::Device &a, const db::Device &b) const + { + if (cb_less.can_issue ()) { + return cb_less.issue (&db::EqualDeviceParameters::less, a, b); + } else { + return db::EqualDeviceParameters::less (a, b); + } + } + + gsi::Callback cb_less; +}; + +/** + * @brief A DeviceCombiner implementation that allows reimplementation of the virtual methods + */ +class GenericDeviceCombiner + : public db::DeviceCombiner +{ +public: + GenericDeviceCombiner () + : db::DeviceCombiner () + { + // .. nothing yet .. + } + + virtual bool combine_devices (db::Device *a, db::Device *b) const + { + if (cb_combine.can_issue ()) { + return cb_combine.issue (&db::DeviceCombiner::combine_devices, a, b); + } else { + return false; + } + } + + gsi::Callback cb_combine; +}; + +} + +db::EqualDeviceParameters *make_equal_dp (size_t param_id, double absolute, double relative) +{ + return new db::EqualDeviceParameters (param_id, absolute, relative); +} + +db::EqualDeviceParameters *make_ignore_dp (size_t param_id) +{ + return new db::EqualDeviceParameters (param_id, true); +} + +Class decl_dbEqualDeviceParameters ("db", "EqualDeviceParameters", + gsi::constructor ("new", &make_equal_dp, gsi::arg ("param_id"), gsi::arg ("absolute", 0.0), gsi::arg ("relative", 0.0), + "@brief Creates a device parameter comparer for a single parameter.\n" + "'absolute' is the absolute deviation allowed for the parameter values. " + "'relative' is the relative deviation allowed for the parameter values (a value between 0 and 1).\n" + "\n" + "A value of 0 for both absolute and relative deviation means the parameters have to match exactly.\n" + "\n" + "If 'absolute' and 'relative' are both given, their deviations will add to the allowed difference between " + "two parameter values. The relative deviation will be applied to the mean value of both parameter values. " + "For example, when comparing parameter values of 40 and 60, a relative deviation of 0.35 means an absolute " + "deviation of 17.5 (= 0.35 * average of 40 and 60) which does not make both values match." + ) + + gsi::constructor ("ignore", &make_ignore_dp, gsi::arg ("param_id"), + "@brief Creates a device parameter comparer which ignores the parameter.\n" + "\n" + "This specification can be used to make a parameter ignored. Starting with version 0.27.4, all primary parameters " + "are compared. Before 0.27.4, giving a tolerance meant only those parameters are compared. To exclude a primary " + "parameter from the compare, use the 'ignore' specification for that parameter.\n" + "\n" + "This constructor has been introduced in version 0.27.4.\n" + ) + + gsi::method ("+", &db::EqualDeviceParameters::operator+, gsi::arg ("other"), + "@brief Combines two parameters for comparison.\n" + "The '+' operator will join the parameter comparers and produce one that checks the combined parameters.\n" + ) + + gsi::method ("+=", &db::EqualDeviceParameters::operator+, gsi::arg ("other"), + "@brief Combines two parameters for comparison (in-place).\n" + "The '+=' operator will join the parameter comparers and produce one that checks the combined parameters.\n" + ) + + gsi::method ("to_string", &db::EqualDeviceParameters::to_string, "@hide"), + "@brief A device parameter equality comparer.\n" + "Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device " + "class use this comparer:\n" + "\n" + "@code\n" + "# 20nm tolerance for length:\n" + "equal_device_parameters = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_L, 0.02, 0.0)\n" + "# one percent tolerance for width:\n" + "equal_device_parameters += RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_W, 0.0, 0.01)\n" + "# applies the compare delegate:\n" + "netlist.device_class_by_name(\"NMOS\").equal_parameters = equal_device_parameters\n" + "@/code\n" + "\n" + "You can use this class to specify fuzzy equality criteria for the comparison of device parameters in " + "netlist verification or to confine the equality of devices to certain parameters only.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare", + gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"), + "@brief Compares the parameters of two devices for a begin less than b.\n" + "Returns true, if the parameters of device a are considered less than those of device b." + "The 'less' implementation needs to ensure strict weak ordering. Specifically, less(a,b) == false and less(b,a) implies that a is equal to b and " + "less(a,b) == true implies that less(b,a) is false and vice versa. If not, an internal error " + "will be encountered on netlist compare." + ), + "@brief A class implementing the comparison of device parameters.\n" + "Reimplement this class to provide a custom device parameter compare scheme.\n" + "Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device " + "class use this comparer.\n" + "\n" + "This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of " + "implementing a custom comparer class.\n" + "\n" + "This class has been added in version 0.26. The 'equal' method has been dropped in 0.27.1 as it can be expressed as !less(a,b) && !less(b,a)." +); + +Class decl_GenericDeviceCombiner ("db", "GenericDeviceCombiner", + gsi::callback ("combine_devices", &GenericDeviceCombiner::combine_devices, &GenericDeviceCombiner::cb_combine, gsi::arg ("device_a"), gsi::arg ("device_b"), + "@brief Combines two devices if possible.\n" + "This method needs to test, whether the two devices can be combined. Both devices " + "are guaranteed to share the same device class. " + "If they cannot be combined, this method shall do nothing and return false. " + "If they can be combined, this method shall reconnect the nets of the first " + "device and entirely disconnect the nets of the second device. " + "The second device will be deleted afterwards. " + ), + "@brief A class implementing the combination of two devices (parallel or serial mode).\n" + "Reimplement this class to provide a custom device combiner.\n" + "Device combination requires 'supports_paralell_combination' or 'supports_serial_combination' to be set " + "to true for the device class. In the netlist device combination step, the algorithm will try to identify " + "devices which can be combined into single devices and use the combiner object to implement the actual " + "joining of such devices.\n" + "\n" + "Attach this object to a device class with \\DeviceClass#combiner= to make the device " + "class use this combiner.\n" + "\n" + "This class has been added in version 0.27.3." +); + +static const std::string &get_element (const db::DeviceClass::SpiceProfile *profile) +{ + return profile->element; +} + +static void set_element (db::DeviceClass::SpiceProfile *profile, const std::string &element) +{ + profile->element = element; +} + +static const std::vector &get_terminal_order (const db::DeviceClass::SpiceProfile *profile) +{ + return profile->terminal_order; +} + +static void set_terminal_order (db::DeviceClass::SpiceProfile *profile, const std::vector &terminal_order) +{ + profile->terminal_order = terminal_order; +} + +Class decl_dbDeviceClassSpiceProfile ("db", "DeviceClassSpiceProfile", + gsi::method_ext ("element", &get_element, + "@brief Gets the SPICE element to use for this device.\n" + "The element is the SPICE code for the element - i.e. 'X', 'M', 'R' etc.\n" + ) + + gsi::method_ext ("element=", &set_element, + "@brief Sets the SPICE element to use for this device.\n" + "See \\element for a description of this attribute.\n" + ) + + gsi::method_ext ("terminal_order", &get_terminal_order, + "@brief Gets the terminal order to use for this device.\n" + "The terminal order is a list of terminal names that specifies which device terminal to tie to the " + "nets given in the SPICE statement. The list items must be strings corresponding to valid terminal " + "names of the device." + ) + + gsi::method_ext ("terminal_order=", &set_terminal_order, + "@brief Sets the terminal order to use for this device.\n" + "See \\terminal_order for a description of this attribute.\n" + ), + "@brief Declares a SPICE profile of a device\n" + "Objects of this type are used inside the \\DeviceClass object to specify how the device " + "is written to and read from SPICE files. See \\DeviceClass#set_spice_profile for a " + "description of that concept.\n" + "\n" + "This class has been added in version 0.31.0." +); + +static tl::id_type id_of_device_class (const db::DeviceClass *cls) +{ + return tl::id_of (cls); +} + +static void equal_parameters (db::DeviceClass *cls, db::EqualDeviceParameters *comparer) +{ + cls->set_parameter_compare_delegate (comparer); +} + +static db::EqualDeviceParameters *get_equal_parameters (db::DeviceClass *cls) +{ + return dynamic_cast (cls->parameter_compare_delegate ()); +} + +static void set_combiner (db::DeviceClass *cls, GenericDeviceCombiner *combiner) +{ + cls->set_device_combiner (combiner); +} + +static GenericDeviceCombiner *get_combiner (db::DeviceClass *cls) +{ + return dynamic_cast (cls->device_combiner ()); +} + +static void enable_parameter (db::DeviceClass *cls, size_t id, bool en) +{ + db::DeviceParameterDefinition *pd = cls->parameter_definition_non_const (id); + if (pd) { + pd->set_is_primary (en); + } +} + +static void enable_parameter2 (db::DeviceClass *cls, const std::string &name, bool en) +{ + if (! cls->has_parameter_with_name (name)) { + return; + } + + size_t id = cls->parameter_id_for_name (name); + db::DeviceParameterDefinition *pd = cls->parameter_definition_non_const (id); + if (pd) { + pd->set_is_primary (en); + } +} + +static const db::DeviceParameterDefinition *parameter_definition2 (const db::DeviceClass *cls, const std::string &name) +{ + if (! cls->has_parameter_with_name (name)) { + return 0; + } else { + return cls->parameter_definition (cls->parameter_id_for_name (name)); + } +} + +static void dc_add_terminal_definition (db::DeviceClass *cls, db::DeviceTerminalDefinition *terminal_def) +{ + if (terminal_def) { + *terminal_def = cls->add_terminal_definition (*terminal_def); + } +} + +static void dc_add_parameter_definition (db::DeviceClass *cls, db::DeviceParameterDefinition *parameter_def) +{ + if (parameter_def) { + *parameter_def = cls->add_parameter_definition (*parameter_def); + } +} + +Class decl_dbDeviceClass ("db", "DeviceClass", + gsi::method ("name", &db::DeviceClass::name, + "@brief Gets the name of the device class." + ) + + gsi::method ("name=", &db::DeviceClass::set_name, gsi::arg ("name"), + "@brief Sets the name of the device class." + ) + + gsi::method ("has_spice_profile?", &db::DeviceClass::has_spice_profile, gsi::arg ("name"), + "@brief Gets a value indicating whether a device class supports a specific SPICE profile.\n" + "See \\set_spice_profile for a description of the SPICE profile concept.\n" + "\n" + "SPICE profiles have been introduced in version 0.31.0." + ) + + gsi::method ("remove_spice_profile", &db::DeviceClass::remove_spice_profile, gsi::arg ("name"), + "@brief Removes a SPICE profile with the given name.\n" + "See \\set_spice_profile for a description of the SPICE profile concept.\n" + "\n" + "SPICE profiles have been introduced in version 0.31.0." + ) + + gsi::method ("clear_spice_profiles", &db::DeviceClass::clear_spice_profiles, + "@brief Removes all SPICE profiles from the device class.\n" + "See \\set_spice_profile for a description of the SPICE profile concept.\n" + "\n" + "SPICE profiles have been introduced in version 0.31.0." + ) + + gsi::method ("set_spice_profile", &db::DeviceClass::set_spice_profile, gsi::arg ("profile_name"), gsi::arg ("spice_profile"), + "@brief Defines a SPICE profile.\n" + "SPICE profiles are a way to declare SPICE representations for a specific device. " + "Each device class can support multiple profiles. An empty name declares the default profile, " + "'*' is the fallback profile used when there is no profile with a requested name. Profiles " + "are requested by the SPICE reader or writer, unless they use delegates to implement " + "a customized SPICE representation.\n" + "\n" + "SPICE profiles can declare element names for example or specify the terminal count and order in SPICE files.\n" + "See \\DeviceClassSpiceProfile for the available attributes.\n" + "\n" + "Note that this method stores a copy of the profile object, so changing at afterwards will not have any effect.\n" + "\n" + "SPICE profiles have been introduced in version 0.31.0." + ) + + gsi::method ("spice_profile", &db::DeviceClass::spice_profile, gsi::arg ("profile_name"), + "@brief Gets a SPICE profile with the given name.\n" + "If the name is not corresponding to a valid profile and a fallback profile '*' exists, the latter is returned. " + "Otherwise, a default profile is returned, which triggers the default serialization.\n" + "See \\set_spice_profile for a description of the SPICE profile concept.\n" + "\n" + "Note that this method returns a copy of the profile object. If you want to manipulate a SPICE profile, " + "you have to fetch the current profile, change it and store it again:\n" + "\n" + "@code\n" + "dc = ... # some DeviceClass\n" + "profile = dc.spice_profile(\"\") # default profile\n" + "profile.element = \"X\"\n" + "dc.set_spice_profile(\"\", profile)\n" + "@/code\n" + "\n" + "SPICE profiles have been introduced in version 0.31.0." + ) + + gsi::method ("strict?", &db::DeviceClass::is_strict, + "@brief Gets a value indicating whether this class performs strict terminal mapping\n" + "See \\strict= for details about this attribute." + ) + + gsi::method ("strict=", &db::DeviceClass::set_strict, gsi::arg ("s"), + "@brief Sets a value indicating whether this class performs strict terminal mapping\n" + "\n" + "Classes with this flag set never allow terminal swapping, even if the device symmetry supports that. " + "If two classes are involved in a netlist compare,\n" + "terminal swapping will be disabled if one of the classes is in strict mode.\n" + "\n" + "By default, device classes are not strict and terminal swapping is allowed as far as the " + "device symmetry supports that." + ) + + gsi::method ("description", &db::DeviceClass::description, + "@brief Gets the description text of the device class." + ) + + gsi::method ("description=", &db::DeviceClass::set_description, gsi::arg ("description"), + "@brief Sets the description of the device class." + ) + + gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist, + "@brief Gets the netlist the device class lives in." + ) + + gsi::method_ext ("id", &gsi::id_of_device_class, + "@brief Gets the unique ID of the device class\n" + "The ID is a unique integer that identifies the device class. Use the ID " + "to check for object identity - i.e. to determine whether two devices share the " + "same device class." + ) + + gsi::method ("terminal_definitions", &db::DeviceClass::terminal_definitions, + "@brief Gets the list of terminal definitions of the device.\n" + "See the \\DeviceTerminalDefinition class description for details." + ) + + gsi::method ("terminal_definition", &db::DeviceClass::terminal_definition, gsi::arg ("terminal_id"), + "@brief Gets the terminal definition object for a given ID.\n" + "Terminal definition IDs are used in some places to reference a specific terminal of a device. " + "This method obtains the corresponding definition object." + ) + + gsi::method ("parameter_definitions", &db::DeviceClass::parameter_definitions, + "@brief Gets the list of parameter definitions of the device.\n" + "See the \\DeviceParameterDefinition class description for details." + ) + + gsi::method ("parameter_definition", &db::DeviceClass::parameter_definition, gsi::arg ("parameter_id"), + "@brief Gets the parameter definition object for a given ID.\n" + "Parameter definition IDs are used in some places to reference a specific parameter of a device. " + "This method obtains the corresponding definition object." + ) + + gsi::method_ext ("parameter_definition", ¶meter_definition2, gsi::arg ("parameter_name"), + "@brief Gets the parameter definition object for a given ID.\n" + "Parameter definition IDs are used in some places to reference a specific parameter of a device. " + "This method obtains the corresponding definition object." + "\n" + "This version accepts a parameter name.\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + ) + + gsi::method_ext ("enable_parameter", &enable_parameter, gsi::arg ("parameter_id"), gsi::arg ("enable"), + "@brief Enables or disables a parameter.\n" + "Some parameters are 'secondary' parameters which are extracted but not handled in device compare and are not shown in the netlist browser. " + "For example, the 'W' parameter of the resistor is such a secondary parameter. This method allows turning a parameter in a primary one ('enable') or " + "into a secondary one ('disable').\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + ) + + gsi::method_ext ("enable_parameter", &enable_parameter2, gsi::arg ("parameter_name"), gsi::arg ("enable"), + "@brief Enables or disables a parameter.\n" + "Some parameters are 'secondary' parameters which are extracted but not handled in device compare and are not shown in the netlist browser. " + "For example, the 'W' parameter of the resistor is such a secondary parameter. This method allows turning a parameter in a primary one ('enable') or " + "into a secondary one ('disable').\n" + "\n" + "This version accepts a parameter name.\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + ) + + gsi::method ("has_parameter?", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"), + "@brief Returns true, if the device class has a parameter with the given name.\n" + ) + + gsi::method ("parameter_id", &db::DeviceClass::parameter_id_for_name, gsi::arg ("name"), + "@brief Returns the parameter ID of the parameter with the given name.\n" + "An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check " + "whether the name is a valid parameter name." + ) + + gsi::method ("has_terminal?", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"), + "@brief Returns true, if the device class has a terminal with the given name.\n" + ) + + gsi::method ("terminal_id", &db::DeviceClass::terminal_id_for_name, gsi::arg ("name"), + "@brief Returns the terminal ID of the terminal with the given name.\n" + "An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check " + "whether the name is a valid terminal name." + ) + + gsi::method_ext ("equal_parameters", &get_equal_parameters, + "@brief Gets the device parameter comparer for netlist verification or nil if no comparer is registered.\n" + "See \\equal_parameters= for the setter.\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method_ext ("equal_parameters=", &equal_parameters, gsi::arg ("comparer"), + "@brief Specifies a device parameter comparer for netlist verification.\n" + "By default, all devices are compared with all parameters. If you want to select only certain parameters " + "for comparison or use a fuzzy compare criterion, use an \\EqualDeviceParameters object and assign it " + "to the device class of one netlist. You can also chain multiple \\EqualDeviceParameters objects with the '+' operator " + "for specifying multiple parameters in the equality check.\n" + "\n" + "You can assign nil for the parameter comparer to remove it.\n" + "\n" + "In special cases, you can even implement a custom compare scheme by deriving your own comparer from the \\GenericDeviceParameterCompare class.\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method_ext ("add_terminal", &gsi::dc_add_terminal_definition, gsi::arg ("terminal_def"), + "@brief Adds the given terminal definition to the device class\n" + "This method will define a new terminal. The new terminal is added at the end of existing terminals. " + "The terminal definition object passed as the argument is modified to contain the " + "new ID of the terminal.\n" + "\n" + "The terminal is copied into the device class. Modifying the terminal object later " + "does not have the effect of changing the terminal definition.\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method ("clear_terminals", &db::DeviceClass::clear_terminal_definitions, + "@brief Clears the list of terminals\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method_ext ("add_parameter", &gsi::dc_add_parameter_definition, gsi::arg ("parameter_def"), + "@brief Adds the given parameter definition to the device class\n" + "This method will define a new parameter. The new parameter is added at the end of existing parameters. " + "The parameter definition object passed as the argument is modified to contain the " + "new ID of the parameter." + "\n" + "The parameter is copied into the device class. Modifying the parameter object later " + "does not have the effect of changing the parameter definition.\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method ("clear_parameters", &db::DeviceClass::clear_parameter_definitions, + "@brief Clears the list of parameters\n" + "\n" + "This method has been added in version 0.27.3.\n" + ) + + gsi::method_ext ("combiner=", &set_combiner, gsi::arg ("combiner"), + "@brief Specifies a device combiner (parallel or serial device combination).\n" + "\n" + "You can assign nil for the combiner to remove it.\n" + "\n" + "In special cases, you can even implement a custom combiner by deriving your own comparer from the \\GenericDeviceCombiner class.\n" + "\n" + "This method has been added in version 0.27.3.\n" + ) + + gsi::method_ext ("combiner", &get_combiner, + "@brief Gets a device combiner or nil if none is registered.\n" + "\n" + "This method has been added in version 0.27.3.\n" + ) + + gsi::method ("supports_parallel_combination=", &db::DeviceClass::set_supports_parallel_combination, gsi::arg ("f"), + "@brief Specifies whether the device supports parallel device combination.\n" + "Parallel device combination means that all terminals of two combination candidates are connected to the same nets. " + "If the device does not support this combination mode, this predicate can be set to false. This will make the device " + "extractor skip the combination test in parallel mode and improve performance somewhat.\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method ("supports_serial_combination=", &db::DeviceClass::set_supports_serial_combination, gsi::arg ("f"), + "@brief Specifies whether the device supports serial device combination.\n" + "Serial device combination means that the devices are connected by internal nodes. " + "If the device does not support this combination mode, this predicate can be set to false. This will make the device " + "extractor skip the combination test in serial mode and improve performance somewhat.\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method ("equivalent_terminal_id", &db::DeviceClass::equivalent_terminal_id, gsi::arg ("original_id"), gsi::arg ("equivalent_id"), + "@brief Specifies a terminal to be equivalent to another.\n" + "Use this method to specify two terminals to be exchangeable. For example to make S and D of a MOS transistor equivalent, " + "call this method with S and D terminal IDs. In netlist matching, S will be translated to D and thus made equivalent to D.\n" + "\n" + "Note that terminal equivalence is not effective if the device class operates in strict mode (see \\DeviceClass#strict=).\n" + "\n" + "This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n" + ) + + gsi::method ("clear_equivalent_terminal_ids", &db::DeviceClass::clear_equivalent_terminal_ids, + "@brief Clears all equivalent terminal ids\n" + "\n" + "This method has been added in version 0.27.3.\n" + ), + "@brief A class describing a specific type of device.\n" + "Device class objects live in the context of a \\Netlist object. After a " + "device class is created, it must be added to the netlist using \\Netlist#add. " + "The netlist will own the device class object. When the netlist is destroyed, the " + "device class object will become invalid.\n" + "\n" + "The \\DeviceClass class is the base class for other device classes.\n" + "\n" + "This class has been added in version 0.26. In version 0.27.3, the 'GenericDeviceClass' has been integrated with \\DeviceClass " + "and the device class was made writeable in most respects. This enables manipulating built-in device classes." +); + +} + diff --git a/src/db/db/gsiDeclDbNetlistNet.cc b/src/db/db/gsiDeclDbNetlistNet.cc new file mode 100644 index 000000000..38f4a6061 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistNet.cc @@ -0,0 +1,230 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +Class decl_dbNetTerminalRef ("db", "NetTerminalRef", + gsi::method ("terminal_id", &db::NetTerminalRef::terminal_id, + "@brief Gets the ID of the terminal of the device the connection is made to." + ) + + gsi::method ("device", (const db::Device *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device, + "@brief Gets the device reference.\n" + "Gets the device object that this connection is made to." + ) + + gsi::method ("device", (db::Device *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device, + "@brief Gets the device reference (non-const version).\n" + "Gets the device object that this connection is made to." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("net", (const db::Net *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::net, + "@brief Gets the net this terminal reference is attached to." + ) + + gsi::method ("net", (db::Net *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::net, + "@brief Gets the net this terminal reference is attached to (non-const version)." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("device_class", (const db::DeviceClass *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device_class, + "@brief Gets the class of the device which is addressed." + ) + + gsi::method ("terminal_def", (const db::DeviceTerminalDefinition *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::terminal_def, + "@brief Gets the terminal definition of the terminal that is connected" + ), + "@brief A connection to a terminal of a device.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNetPinRef ("db", "NetPinRef", + gsi::method ("pin_id", &db::NetPinRef::pin_id, + "@brief Gets the ID of the pin the connection is made to." + ) + + gsi::method ("pin", &db::NetPinRef::pin, + "@brief Gets the \\Pin object of the pin the connection is made to." + ) + + gsi::method ("net", (const db::Net *(db::NetPinRef::*) () const) &db::NetPinRef::net, + "@brief Gets the net this pin reference is attached to." + ) + + gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net, + "@brief Gets the net this pin reference is attached to (non-const version)." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ), + "@brief A connection to an outgoing pin of the circuit.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitPinRef", + gsi::method ("pin_id", &db::NetSubcircuitPinRef::pin_id, + "@brief Gets the ID of the pin the connection is made to." + ) + + gsi::method ("pin", &db::NetSubcircuitPinRef::pin, + "@brief Gets the \\Pin object of the pin the connection is made to." + ) + + gsi::method ("subcircuit", (const db::SubCircuit *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::subcircuit, + "@brief Gets the subcircuit reference.\n" + "This attribute indicates the subcircuit the net attaches to. The " + "subcircuit lives in the same circuit than the net. " + ) + + gsi::method ("subcircuit", (db::SubCircuit *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::subcircuit, + "@brief Gets the subcircuit reference (non-const version).\n" + "This attribute indicates the subcircuit the net attaches to. The " + "subcircuit lives in the same circuit than the net. " + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("net", (const db::Net *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::net, + "@brief Gets the net this pin reference is attached to." + ) + + gsi::method ("net", (db::Net *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::net, + "@brief Gets the net this pin reference is attached to (non-const version)." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ), + "@brief A connection to a pin of a subcircuit.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +extern Class decl_dbNetlistObject; + +Class decl_dbNet (decl_dbNetlistObject, "db", "Net", + gsi::method ("circuit", (db::Circuit *(db::Net::*) ()) &db::Net::circuit, + "@brief Gets the circuit the net lives in." + ) + + gsi::method ("clear", &db::Net::clear, + "@brief Clears the net." + ) + + gsi::method ("name=", &db::Net::set_name, gsi::arg ("name"), + "@brief Sets the name of the net.\n" + "The name of the net is used for naming the net in schematic files for example. " + "The name of the net has to be unique." + ) + + gsi::method ("name", &db::Net::name, + "@brief Gets the name of the net.\n" + "See \\name= for details about the name." + ) + + gsi::method ("qname|to_s", &db::Net::qname, + "@brief Gets the qualified name.\n" + "The qualified name is like the expanded name, but the circuit's name is preceded\n" + "(i.e. 'CIRCUIT:NET') if available.\n" + ) + + gsi::method ("expanded_name", &db::Net::expanded_name, + "@brief Gets the expanded name of the net.\n" + "The expanded name takes the name of the net. If the name is empty, the cluster ID will be used to build a name. " + ) + + gsi::method ("cluster_id=", &db::Net::set_cluster_id, gsi::arg ("id"), + "@brief Sets the cluster ID of the net.\n" + "The cluster ID connects the net with a layout cluster. It is set when " + "the net is extracted from a layout." + ) + + gsi::method ("cluster_id", &db::Net::cluster_id, + "@brief Gets the cluster ID of the net.\n" + "See \\cluster_id= for details about the cluster ID." + ) + + gsi::iterator ("each_pin", gsi::return_reference (), (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::begin_pins, (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::end_pins, + "@brief Iterates over all outgoing pins the net connects.\n" + "Pin connections are described by \\NetPinRef objects. Pin connections " + "are connections to outgoing pins of the circuit the net lives in." + ) + + gsi::iterator ("each_pin", gsi::return_reference (), (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins, + "@brief Iterates over all outgoing pins the net connects (non-const version).\n" + "Pin connections are described by \\NetPinRef objects. Pin connections " + "are connections to outgoing pins of the circuit the net lives in." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::iterator ("each_subcircuit_pin", gsi::return_reference (), (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::begin_subcircuit_pins, (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::end_subcircuit_pins, + "@brief Iterates over all subcircuit pins the net connects.\n" + "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " + "connections to specific pins of subcircuits." + ) + + gsi::iterator ("each_subcircuit_pin", gsi::return_reference (), (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::begin_subcircuit_pins, (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::end_subcircuit_pins, + "@brief Iterates over all subcircuit pins the net connects (non-const version).\n" + "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " + "connections to specific pins of subcircuits." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::iterator ("each_terminal", gsi::return_reference (), (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::begin_terminals, (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::end_terminals, + "@brief Iterates over all terminals the net connects.\n" + "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " + "objects." + ) + + gsi::iterator ("each_terminal", gsi::return_reference (), (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals, + "@brief Iterates over all terminals the net connects (non-const version).\n" + "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " + "objects." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("is_floating?", &db::Net::is_floating, + "@brief Returns true, if the net is floating.\n" + "Floating nets are those which don't have any device or subcircuit on it and are not connected through a pin." + ) + + gsi::method ("is_passive?", &db::Net::is_passive, + "@brief Returns true, if the net is passive.\n" + "Passive nets don't have devices or subcircuits on it. They can be exposed through a pin.\n" + "\\is_floating? implies \\is_passive?.\n" + "\n" + "This method has been introduced in version 0.26.1.\n" + ) + + gsi::method ("is_internal?", &db::Net::is_internal, + "@brief Returns true, if the net is an internal net.\n" + "Internal nets are those which connect exactly two terminals and nothing else (pin_count = 0 and terminal_count == 2)." + ) + + gsi::method ("pin_count", &db::Net::pin_count, + "@brief Returns the number of outgoing pins connected by this net.\n" + ) + + gsi::method ("subcircuit_pin_count", &db::Net::subcircuit_pin_count, + "@brief Returns the number of subcircuit pins connected by this net.\n" + ) + + gsi::method ("terminal_count", &db::Net::terminal_count, + "@brief Returns the number of terminals connected by this net.\n" + ), + "@brief A single net.\n" + "A net connects multiple pins or terminals together. Pins are either " + "pin or subcircuits of outgoing pins of the circuit the net lives in. " + "Terminals are connections made to specific terminals of devices.\n" + "\n" + "Net objects are created inside a circuit with \\Circuit#create_net.\n" + "\n" + "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " + "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " + "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " + "disconnect a net from a pin of a subcircuit, use \\SubCircuit#disconnect_pin. " + "To connect a net to a terminal of a device, use \\Device#connect_terminal, to " + "disconnect a net from a terminal of a device, use \\Device#disconnect_terminal.\n" + "\n" + "This class has been added in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbNetlistObject.cc b/src/db/db/gsiDeclDbNetlistObject.cc new file mode 100644 index 000000000..3d890509d --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistObject.cc @@ -0,0 +1,55 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +static std::vector property_keys (const db::NetlistObject *object) +{ + std::vector v; + for (db::NetlistObject::property_iterator p = object->begin_properties (); p != object->end_properties (); ++p) { + v.push_back (p->first); + } + return v; +} + +Class decl_dbNetlistObject ("db", "NetlistObject", + gsi::method ("property", &db::NetlistObject::property, gsi::arg ("key"), + "@brief Gets the property value for the given key or nil if there is no value with this key." + ) + + gsi::method ("set_property", &db::NetlistObject::set_property, gsi::arg ("key"), gsi::arg ("value"), + "@brief Sets the property value for the given key.\n" + "Use a nil value to erase the property with this key." + ) + + gsi::method_ext ("property_keys", &property_keys, + "@brief Gets the keys for the properties stored in this object." + ), + "@brief The base class for some netlist objects.\n" + "The main purpose of this class is to supply user properties for netlist objects.\n" + "\n" + "This class has been introduced in version 0.26.2" +); + +} diff --git a/src/db/db/gsiDeclDbNetlistPin.cc b/src/db/db/gsiDeclDbNetlistPin.cc new file mode 100644 index 000000000..79de02a3b --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistPin.cc @@ -0,0 +1,50 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +extern Class decl_dbNetlistObject; + +Class decl_dbPin (decl_dbNetlistObject, "db", "Pin", + gsi::method ("id", &db::Pin::id, + "@brief Gets the ID of the pin.\n" + ) + + gsi::method ("name", &db::Pin::name, + "@brief Gets the name of the pin.\n" + ) + + gsi::method ("expanded_name", &db::Pin::expanded_name, + "@brief Gets the expanded name of the pin.\n" + "The expanded name is the name or a generic identifier made from the ID if the name is empty." + ), + "@brief A pin of a circuit.\n" + "Pin objects are used to describe the outgoing pins of " + "a circuit. To create a new pin of a circuit, use \\Circuit#create_pin.\n" + "\n" + "This class has been added in version 0.26." +); + +} + diff --git a/src/db/db/gsiDeclDbNetlistSpiceReader.cc b/src/db/db/gsiDeclDbNetlistSpiceReader.cc new file mode 100644 index 000000000..56f58e739 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistSpiceReader.cc @@ -0,0 +1,703 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" +#include "dbNetlistReader.h" +#include "dbNetlistSpiceReader.h" +#include "dbNetlistSpiceReaderDelegate.h" + +namespace gsi +{ + +Class db_NetlistReader ("db", "NetlistReader", + gsi::Methods (), + "@brief Base class for netlist readers\n" + "This class is provided as a base class for netlist readers. It is not intended for reimplementation on script level, but used internally as an interface.\n" + "\n" + "This class has been introduced in version 0.26." +); + +/** + * @brief A helper class wrapping the return values for NetlistSpiceReaderDelegateImpl::parse_element + */ +class ParseElementData +{ +public: + ParseElementData () : m_value (0.0) { } + + const std::string &element_name () const { return m_element; } + std::string &element_name_nc () { return m_element; } + void set_element_name (const std::string &element) { m_element = element; } + const std::string &model_name () const { return m_model; } + std::string &model_name_nc () { return m_model; } + void set_model_name (const std::string &model) { m_model = model; } + double value () const { return m_value; } + double &value_nc () { return m_value; } + void set_value (double value) { m_value = value; } + const std::vector &net_names () const { return m_net_names; } + std::vector &net_names_nc () { return m_net_names; } + void set_net_names (const std::vector &nn) { m_net_names = nn; } + const db::NetlistSpiceReader::parameters_type ¶meters () const { return m_parameters; } + db::NetlistSpiceReader::parameters_type ¶meters_nc () { return m_parameters; } + void set_parameters (const db::NetlistSpiceReader::parameters_type ¶meters) { m_parameters = parameters; } + +private: + std::string m_model, m_element; + double m_value; + std::vector m_net_names; + db::NetlistSpiceReader::parameters_type m_parameters; +}; + +/** + * @brief A helper class for the return values of NetlistSpiceReaderDelegateImpl::parse_element_components + */ +class ParseElementComponentsData +{ +public: + ParseElementComponentsData () { } + + const std::vector &strings () const { return m_strings; } + std::vector &strings_nc () { return m_strings; } + void set_strings (const std::vector &nn) { m_strings = nn; } + const db::NetlistSpiceReader::parameters_type ¶meters () const { return m_parameters; } + db::NetlistSpiceReader::parameters_type ¶meters_nc () { return m_parameters; } + void set_parameters (const db::NetlistSpiceReader::parameters_type ¶meters) { m_parameters = parameters; } + +private: + std::vector m_strings; + db::NetlistSpiceReader::parameters_type m_parameters; +}; + +/** + * @brief A SPICE reader delegate base class for reimplementation + */ +class NetlistSpiceReaderDelegateImpl + : public db::NetlistSpiceReaderDelegate, public gsi::ObjectBase +{ +public: + NetlistSpiceReaderDelegateImpl () + : db::NetlistSpiceReaderDelegate (), mp_variables (0) + { + // .. nothing yet .. + } + + virtual void error (const std::string &msg) + { + // doing this avoids passing exceptions through script code which spoils the message + // (the exception will be decorated with a stack trace). TODO: a better solution was + // to define a specific exception type for "raw exception". + m_error = msg; + db::NetlistSpiceReaderDelegate::error (msg); + } + + virtual void start (db::Netlist *netlist) + { + try { + m_error.clear (); + if (cb_start.can_issue ()) { + cb_start.issue (&db::NetlistSpiceReaderDelegate::start, netlist); + } else { + db::NetlistSpiceReaderDelegate::start (netlist); + } + } catch (tl::Exception &) { + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + } + } + + virtual void finish (db::Netlist *netlist) + { + try { + m_error.clear (); + if (cb_finish.can_issue ()) { + cb_finish.issue (&db::NetlistSpiceReaderDelegate::finish, netlist); + } else { + db::NetlistSpiceReaderDelegate::finish (netlist); + } + } catch (tl::Exception &) { + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + } + } + + virtual bool control_statement (const std::string &line) + { + try { + m_error.clear (); + if (cb_control_statement.can_issue ()) { + return cb_control_statement.issue (&db::NetlistSpiceReaderDelegate::control_statement, line); + } else { + return db::NetlistSpiceReaderDelegate::control_statement (line); + } + } catch (tl::Exception &) { + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + return false; + } + } + + virtual bool wants_subcircuit (const std::string &circuit_name) + { + try { + m_error.clear (); + if (cb_wants_subcircuit.can_issue ()) { + return cb_wants_subcircuit.issue (&db::NetlistSpiceReaderDelegate::wants_subcircuit, circuit_name); + } else { + return db::NetlistSpiceReaderDelegate::wants_subcircuit (circuit_name); + } + } catch (tl::Exception &) { + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + return false; + } + } + + virtual std::string translate_net_name (const std::string &nn) + { + try { + m_error.clear (); + if (cb_translate_net_name.can_issue ()) { + return cb_translate_net_name.issue (&db::NetlistSpiceReaderDelegate::translate_net_name, nn); + } else { + return db::NetlistSpiceReaderDelegate::translate_net_name (nn); + } + } catch (tl::Exception &) { + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + return std::string (); + } + } + + ParseElementData parse_element_helper (const std::string &s, const std::string &element) + { + // NOTE: this way of treating the element name is compatible with the old way and + // the new way in which the element name can be modified by "parse_element". + ParseElementData data; + data.set_element_name (element); + db::NetlistSpiceReaderDelegate::parse_element (s, data.element_name_nc (), data.model_name_nc (), data.value_nc (), data.net_names_nc (), data.parameters_nc (), variables ()); + return data; + } + + virtual void parse_element (const std::string &s, std::string &element, std::string &model, double &value, std::vector &nn, db::NetlistSpiceReader::parameters_type &pv, const db::NetlistSpiceReader::parameters_type &variables) + { + try { + + m_error.clear (); + mp_variables = &variables; + + ParseElementData data; + if (cb_parse_element.can_issue ()) { + data = cb_parse_element.issue (&NetlistSpiceReaderDelegateImpl::parse_element_helper, s, element); + } else { + data = parse_element_helper (s, element); + } + + element = data.element_name (); + model = data.model_name (); + value = data.value (); + nn = data.net_names (); + pv = data.parameters (); + + mp_variables = 0; + + } catch (tl::Exception &) { + mp_variables = 0; + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + } catch (...) { + mp_variables = 0; + throw; + } + } + + virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector &nets, const db::NetlistSpiceReader::parameters_type ¶ms) + { + try { + m_error.clear (); + if (cb_element.can_issue ()) { + return cb_element.issue &, const db::NetlistSpiceReader::parameters_type &> (&db::NetlistSpiceReaderDelegate::element, circuit, element, name, model, value, nets, params); + } else { + return db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params); + } + } catch (tl::Exception &) { + if (! m_error.empty ()) { + db::NetlistSpiceReaderDelegate::error (m_error); + } else { + throw; + } + return false; + } + } + + const db::NetlistSpiceReader::parameters_type &variables () const + { + static db::NetlistSpiceReader::parameters_type empty; + return mp_variables ? *mp_variables : empty; + } + + gsi::Callback cb_start; + gsi::Callback cb_finish; + gsi::Callback cb_control_statement; + gsi::Callback cb_wants_subcircuit; + gsi::Callback cb_translate_net_name; + gsi::Callback cb_element; + gsi::Callback cb_parse_element; + +private: + std::string m_error; + const db::NetlistSpiceReader::parameters_type *mp_variables; +}; + +static void start_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist) +{ + delegate->db::NetlistSpiceReaderDelegate::start (netlist); +} + +static void finish_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist) +{ + delegate->db::NetlistSpiceReaderDelegate::finish (netlist); +} + +static bool wants_subcircuit_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &model) +{ + return delegate->db::NetlistSpiceReaderDelegate::wants_subcircuit (model); +} + +static bool control_statement_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &line) +{ + return delegate->db::NetlistSpiceReaderDelegate::control_statement (line); +} + +static std::string translate_net_name_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &name) +{ + return delegate->db::NetlistSpiceReaderDelegate::translate_net_name (name); +} + +static bool element_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector &nets, const db::NetlistSpiceReader::parameters_type ¶ms) +{ + return delegate->db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params); +} + +static ParseElementData parse_element_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const std::string &element) +{ + return delegate->parse_element_helper (s, element); +} + +static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables) +{ + tl::Variant res; + double v = 0.0; + if (db::NetlistSpiceReaderDelegate::try_read_value (s, v, variables)) { + res = v; + } + return res; +} + +static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables) +{ + ParseElementComponentsData data; + delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables); + return data; +} + +Class db_ParseElementComponentsData ("db", "ParseElementComponentsData", + gsi::method ("strings", &ParseElementComponentsData::strings, + "@brief Gets the (unnamed) string parameters\n" + "These parameters are typically net names or model name." + ) + + gsi::method ("strings=", &ParseElementComponentsData::set_strings, gsi::arg ("list"), + "@brief Sets the (unnamed) string parameters\n" + ) + + gsi::method ("parameters", &ParseElementComponentsData::parameters, + "@brief Gets the (named) parameters\n" + "Named parameters are typically (but not neccessarily) numerical, like 'w=0.15u'." + ) + + gsi::method ("parameters=", &ParseElementComponentsData::set_parameters, gsi::arg ("dict"), + "@brief Sets the (named) parameters\n" + ), + "@brief Supplies the return value for \\NetlistSpiceReaderDelegate#parse_element_components.\n" + "This is a structure with two members: 'strings' for the string arguments and 'parameters' for the " + "named arguments.\n" + "\n" + "This helper class has been introduced in version 0.27.1. Starting with version 0.28.6, named parameters can be string types too.\n" +); + +Class db_ParseElementData ("db", "ParseElementData", + gsi::method ("value", &ParseElementData::value, + "@brief Gets the value\n" + ) + + gsi::method ("value=", &ParseElementData::set_value, gsi::arg ("v"), + "@brief Sets the value\n" + ) + + gsi::method ("model_name", &ParseElementData::model_name, + "@brief Gets the model name\n" + ) + + gsi::method ("model_name=", &ParseElementData::set_model_name, gsi::arg ("m"), + "@brief Sets the model name\n" + ) + + gsi::method ("element_name", &ParseElementData::element_name, + "@brief Gets the element name\n" + "This attribute is available since version 0.31.0.\n" + ) + + gsi::method ("element_name=", &ParseElementData::set_element_name, gsi::arg ("e"), + "@brief Sets the element name\n" + "This attribute is available since version 0.31.0.\n" + ) + + gsi::method ("net_names", &ParseElementData::net_names, + "@brief Gets the net names\n" + ) + + gsi::method ("net_names=", &ParseElementData::set_net_names, gsi::arg ("list"), + "@brief Sets the net names\n" + ) + + gsi::method ("parameters", &ParseElementData::parameters, + "@brief Gets the (named) parameters\n" + ) + + gsi::method ("parameters=", &ParseElementData::set_parameters, gsi::arg ("dict"), + "@brief Sets the (named) parameters\n" + ), + "@brief Supplies the return value for \\NetlistSpiceReaderDelegate#parse_element.\n" + "This is a structure with four members: 'model_name' for the model name, 'value' for the default numerical value, 'net_names' for the net names and 'parameters' for the " + "named parameters.\n" + "\n" + "This helper class has been introduced in version 0.27.1. Starting with version 0.28.6, named parameters can be string types too.\n" +); + +static double get_delegate_scale (const db::NetlistSpiceReaderDelegate *delegate) +{ + return delegate->options ().scale; +} + +static void apply_parameter_scaling (const db::NetlistSpiceReaderDelegate *delegate, db::Device *device) +{ + delegate->apply_parameter_scaling (device); +} + +Class db_NetlistSpiceReaderDelegate ("db", "NetlistSpiceReaderDelegate", + gsi::method_ext ("start", &start_fb, "@hide") + + gsi::method_ext ("finish", &finish_fb, "@hide") + + gsi::method_ext ("wants_subcircuit", &wants_subcircuit_fb, "@hide") + + gsi::method_ext ("element", &element_fb, "@hide") + + gsi::method_ext ("parse_element", &parse_element_fb, "@hide") + + gsi::method_ext ("control_statement", &control_statement_fb, "@hide") + + gsi::method_ext ("translate_net_name", &translate_net_name_fb, "@hide") + + gsi::callback ("start", &NetlistSpiceReaderDelegateImpl::start, &NetlistSpiceReaderDelegateImpl::cb_start, gsi::arg ("netlist"), + "@brief This method is called when the reader starts reading a netlist\n" + ) + + gsi::callback ("finish", &NetlistSpiceReaderDelegateImpl::finish, &NetlistSpiceReaderDelegateImpl::cb_finish, gsi::arg ("netlist"), + "@brief This method is called when the reader is done reading a netlist successfully\n" + ) + + gsi::callback ("wants_subcircuit", &NetlistSpiceReaderDelegateImpl::wants_subcircuit, &NetlistSpiceReaderDelegateImpl::cb_wants_subcircuit, gsi::arg ("circuit_name"), + "@brief Returns true, if the delegate wants subcircuit elements with this name\n" + "The name is always upper case.\n" + ) + + gsi::callback ("control_statement", &NetlistSpiceReaderDelegateImpl::control_statement, &NetlistSpiceReaderDelegateImpl::cb_control_statement, gsi::arg ("line"), + "@brief Receives control statements not understood by the standard reader\n" + "When the reader encounters a control statement not understood by the parser, it will pass the line to the delegate using this method.\n" + "The delegate can decide if it wants to read this statement. It should return true in this case.\n" + "\n" + "This method has been introduced in version 0.27.1\n" + ) + + gsi::callback ("translate_net_name", &NetlistSpiceReaderDelegateImpl::translate_net_name, &NetlistSpiceReaderDelegateImpl::cb_translate_net_name, gsi::arg ("net_name"), + "@brief Translates a net name from the raw net name to the true net name\n" + "The default implementation will replace backslash sequences by the corresponding character.\n" + "'translate_net_name' is called before a net name is turned into a net object.\n" + "The method can be reimplemented to supply a different translation scheme for net names. For example, to translate special characters.\n" + "\n" + "This method has been introduced in version 0.27.1\n" + ) + + gsi::method ("variables", &NetlistSpiceReaderDelegateImpl::variables, + "@brief Gets the variables defined inside the SPICE file during execution of 'parse_element'\n" + "In order to evaluate formulas, this method allows accessing the variables that are " + "present during the execution of the SPICE reader.\n" + "\n" + "This method has been introduced in version 0.28.6." + ) + + gsi::callback ("parse_element", &NetlistSpiceReaderDelegateImpl::parse_element_helper, &NetlistSpiceReaderDelegateImpl::cb_parse_element, + gsi::arg ("s"), gsi::arg ("element"), + "@brief Parses an element card\n" + "@param s The specification part of the element line (the part after element code and name).\n" + "@param element The upper-case element code (\"M\", \"R\", ...).\n" + "@return A \\ParseElementData object with the parts of the element.\n" + "\n" + "This method receives a string with the element specification and the element code. It is supposed to " + "parse the element line and return a model name, a value, a list of net names and a parameter value dictionary.\n" + "\n" + "'parse_element' is called on every element card. The results of this call go into the \\element method " + "to actually create the device. This method can be reimplemented to support other flavors of SPICE.\n" + "\n" + "This method has been introduced in version 0.27.1\n" + ) + + gsi::callback ("element", &NetlistSpiceReaderDelegateImpl::element, &NetlistSpiceReaderDelegateImpl::cb_element, + gsi::arg ("circuit"), gsi::arg ("element"), gsi::arg ("name"), gsi::arg ("model"), gsi::arg ("value"), gsi::arg ("nets"), gsi::arg ("parameters"), + "@brief Makes a device from an element line\n" + "@param circuit The circuit that is currently read.\n" + "@param element The upper-case element code (\"M\", \"R\", ...).\n" + "@param name The element's name.\n" + "@param model The upper-case model name (may be empty).\n" + "@param value The default value (e.g. resistance for resistors) and may be zero.\n" + "@param nets The nets given in the element line.\n" + "@param parameters The parameters of the element statement (parameter names are upper case).\n" + "\n" + "The default implementation will create corresponding devices for\n" + "some known elements using the Spice writer's parameter conventions.\n" + "\n" + "The method must return true, if the element was was understood and false otherwise.\n" + "\n" + "Starting with version 0.28.6, the parameter values can be strings too." + ) + + gsi::method ("error", &NetlistSpiceReaderDelegateImpl::error, gsi::arg ("msg"), + "@brief Issues an error with the given message.\n" + "Use this method to generate an error." + ) + + gsi::method_ext ("get_scale", &get_delegate_scale, + "@brief Gets the scale factor set with '.options scale=...'\n" + "This method has been introduced in version 0.28.6." + ) + + gsi::method_ext ("apply_parameter_scaling", &apply_parameter_scaling, gsi::arg ("device"), + "@brief Applies parameter scaling to the given device\n" + "Applies SI scaling (according to the parameter's si_scaling attribute) and " + "geometry scaling (according to the parameter's geo_scale_exponent attribute) to " + "the device parameters. Use this method of finish the device when you have created " + "a custom device yourself.\n" + "\n" + "The geometry scale is taken from the '.options scale=...' control statement.\n" + "\n" + "This method has been introduced in version 0.28.6." + ) + + gsi::method_ext ("value_from_string", &value_from_string, gsi::arg ("s"), gsi::arg ("variables", db::NetlistSpiceReader::parameters_type (), "{}"), + "@brief Translates a string into a value\n" + "This function simplifies the implementation of SPICE readers by providing a translation of a unit-annotated string " + "into double values. For example, '1k' is translated to 1000.0. In addition, simple formula evaluation is supported, e.g " + "'(1+3)*2' is translated into 8.0.\n" + "\n" + "The variables dictionary defines named variables with the given values.\n" + "\n" + "This method has been introduced in version 0.27.1. The variables argument has been added in version 0.28.6.\n" + ) + + gsi::method_ext ("parse_element_components", &parse_element_components, gsi::arg ("s"), gsi::arg ("variables", db::NetlistSpiceReader::parameters_type (), "{}"), + "@brief Parses a string into string and parameter components.\n" + "This method is provided to simplify the implementation of 'parse_element'. It takes a string and splits it into " + "string arguments and parameter values. For example, 'a b c=6' renders two string arguments in 'nn' and one parameter ('C'->6.0). " + "It returns data \\ParseElementComponentsData object with the strings and parameters.\n" + "The parameter names are already translated to upper case.\n" + "\n" + "The variables dictionary defines named variables with the given values.\n" + "\n" + "This method has been introduced in version 0.27.1. The variables argument has been added in version 0.28.6.\n" + ), + "@brief Provides a delegate for the SPICE reader for translating device statements\n" + "Supply a customized class to provide a specialized reading scheme for devices. " + "You need a customized class if you want to implement device reading from model subcircuits or to " + "translate device parameters.\n" + "\n" + "See \\NetlistSpiceReader for more details.\n" + "\n" + "This class has been introduced in version 0.26. Profiles have been added to the device classes in version 0.31.0." +); + +namespace { + +class NetlistSpiceReaderWithOwnership + : public db::NetlistSpiceReader +{ +public: + NetlistSpiceReaderWithOwnership (NetlistSpiceReaderDelegateImpl *delegate) + : db::NetlistSpiceReader (delegate), m_ownership (delegate) + { + if (delegate) { + delegate->keep (); + } + } + +private: + tl::shared_ptr m_ownership; +}; + +} + +db::NetlistSpiceReader *new_spice_reader (const std::string &profile) +{ + auto *reader = new db::NetlistSpiceReader (); + reader->set_profile (profile); + return reader; +} + +db::NetlistSpiceReader *new_spice_reader2 (NetlistSpiceReaderDelegateImpl *delegate, const std::string &profile) +{ + auto *reader = new NetlistSpiceReaderWithOwnership (delegate); + reader->set_profile (profile); + return reader; +} + +static bool read_all_parameters (const db::NetlistSpiceReader *reader) +{ + return reader->delegate () ? reader->delegate ()->read_all_parameters () : false; +} + +static void set_read_all_parameters (db::NetlistSpiceReader *reader, bool f) +{ + if (reader->delegate ()) { + reader->delegate ()->set_read_all_parameters (f); + } +} + + +Class db_NetlistSpiceReader (db_NetlistReader, "db", "NetlistSpiceReader", + gsi::constructor ("new", &new_spice_reader, gsi::arg ("profile", std::string ()), + "@brief Creates a new reader.\n" + "The profile string defines the SPICE profile to use for picking default SPICE mappings from the " + "device classes declared in the netlist to read.\n" + "\n" + "The profile string argument has been added in version 0.31.0." + ) + + gsi::constructor ("new", &new_spice_reader2, gsi::arg ("delegate"), gsi::arg ("profile", std::string ()), + "@brief Creates a new reader with a delegate.\n" + "The profile string defines the SPICE profile to use for picking default SPICE mappings from the " + "device classes declared in the netlist to read.\n" + "\n" + "The profile string argument has been added in version 0.31.0." + ) + + gsi::method_ext ("read_all_parameters", &read_all_parameters, + "@brief Gets a flag indicating whether all parameters shall be read\n" + "\n" + "With this flag set to false, only those parameters which\n" + "are declared in the device classes of the netlist are read.\n" + "If set to true (the default), additional parameters are added to the device classes\n" + "and their values are stored in the device objects.\n" + "Note that this behavior can be changed in reimplementations of the\n" + "SPICE reader delegate class.\n" + "\n" + "This attribute has been introduced in version 0.31.0." + ) + + gsi::method_ext ("read_all_parameters=", &set_read_all_parameters, gsi::arg ("f"), + "@brief Sets a flag indicating whether all parameters shall be read\n" + "See \\read_all_parameters for details.\n" + "\n" + "This attribute has been introduced in version 0.31.0." + ), + "@brief Implements a netlist Reader for the SPICE format.\n" + "Use the SPICE reader like this:\n" + "\n" + "@code\n" + "reader = RBA::NetlistSpiceReader::new\n" + "netlist = RBA::Netlist::new\n" + "netlist.read(path, reader)\n" + "@/code\n" + "\n" + "The translation of SPICE elements can be tailored by providing a \\NetlistSpiceReaderDelegate class or by " + "supplying device classes in the netlist that declare their SPICe representation in a SPICE profile.\n" + // @@@ TODO: example for profiles in device classes + "\n" + "The following example is a delegate that turns subcircuits called HVNMOS and HVPMOS into " + "MOS4 devices with the parameters scaled by 1.5:\n" + "\n" + "@code\n" + "class MyDelegate < RBA::NetlistSpiceReaderDelegate\n" + "\n" + " # says we want to catch these subcircuits as devices\n" + " def wants_subcircuit(name)\n" + " name == \"HVNMOS\" || name == \"HVPMOS\"\n" + " end\n" + "\n" + " # translate the element\n" + " def element(circuit, el, name, model, value, nets, params)\n" + "\n" + " if el != \"X\"\n" + " # all other elements are left to the standard implementation\n" + " return super\n" + " end\n" + "\n" + " if nets.size != 4\n" + " error(\"Subcircuit #{model} needs four nodes\")\n" + " end\n" + "\n" + " # provide a device class\n" + " cls = circuit.netlist.device_class_by_name(model)\n" + " if ! cls\n" + " cls = RBA::DeviceClassMOS4Transistor::new\n" + " cls.name = model\n" + " circuit.netlist.add(cls)\n" + " end\n" + "\n" + " # create a device\n" + " device = circuit.create_device(cls, name)\n" + "\n" + " # and configure the device\n" + " [ \"S\", \"G\", \"D\", \"B\" ].each_with_index do |t,index|\n" + " device.connect_terminal(t, nets[index])\n" + " end\n" + " params.each do |p,value|\n" + " device.set_parameter(p, value * 1.5)\n" + " end\n" + "\n" + " end\n" + "\n" + "end\n" + "\n" + "# usage:\n" + "\n" + "mydelegate = MyDelegate::new\n" + "reader = RBA::NetlistSpiceReader::new(mydelegate)\n" + "\n" + "nl = RBA::Netlist::new\n" + "nl.read(input_file, reader)\n" + "@/code\n" + "\n" + "A somewhat contrived example for using the delegate to translate net names is this:\n" + "\n" + "@code\n" + "class MyDelegate < RBA::NetlistSpiceReaderDelegate\n" + "\n" + " # translates 'VDD' to 'VXX' and leave all other net names as is:\n" + " alias translate_net_name_org translate_net_name\n" + " def translate_net_name(n)\n" + " return n == \"VDD\" ? \"VXX\" : translate_net_name_org(n)}\n" + " end\n" + "\n" + "end\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.26. It has been extended in version 0.27.1. " + "Profiles have been added to the device classes in version 0.31.0." +); + +} + diff --git a/src/db/db/gsiDeclDbNetlistSpiceWriter.cc b/src/db/db/gsiDeclDbNetlistSpiceWriter.cc new file mode 100644 index 000000000..f7206d5d0 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistSpiceWriter.cc @@ -0,0 +1,310 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" +#include "dbNetlistSpiceWriter.h" +#include "dbNetlistWriter.h" + +namespace gsi +{ + +/** + * @brief A SPICE writer delegate base class for reimplementation + */ +class NetlistSpiceWriterDelegateImpl + : public db::NetlistSpiceWriterDelegate, public gsi::ObjectBase +{ +public: + NetlistSpiceWriterDelegateImpl () + : db::NetlistSpiceWriterDelegate () + { + // .. nothing yet .. + } + + virtual void write_header () const + { + if (cb_write_header.can_issue ()) { + cb_write_header.issue (&db::NetlistSpiceWriterDelegate::write_header); + } else { + db::NetlistSpiceWriterDelegate::write_header (); + } + } + + virtual void write_device_intro (const db::DeviceClass &ccls) const + { + reimpl_write_device_intro (const_cast (ccls)); + } + + // NOTE: we pass non-const refs to Ruby/Python - everything else is a bit of a nightmare. + // Still that's not really clean. Just say, the implementation promises not to change the objects. + void reimpl_write_device_intro (db::DeviceClass &cls) const + { + if (cb_write_device_intro.can_issue ()) { + cb_write_device_intro.issue (&NetlistSpiceWriterDelegateImpl::org_write_device_intro, const_cast (cls)); + } else { + org_write_device_intro (cls); + } + } + + void org_write_device_intro (db::DeviceClass &cls) const + { + db::NetlistSpiceWriterDelegate::write_device_intro (cls); + } + + virtual void write_device (const db::Device &cdev) const + { + reimpl_write_device (const_cast (cdev)); + } + + // NOTE: we pass non-const refs to Ruby/Python - everthing else is a bit of a nightmare. + // Still that's not really clean. Just say, the implementation promises not to change the objects. + void reimpl_write_device (db::Device &dev) const + { + if (cb_write_device.can_issue ()) { + cb_write_device.issue (&NetlistSpiceWriterDelegateImpl::org_write_device, dev); + } else { + org_write_device (dev); + } + } + + void org_write_device (db::Device &dev) const + { + db::NetlistSpiceWriterDelegate::write_device (dev); + } + + void org_write_header () const + { + db::NetlistSpiceWriterDelegate::write_header (); + } + + gsi::Callback cb_write_header; + gsi::Callback cb_write_device_intro; + gsi::Callback cb_write_device; +}; + +Class db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate", + gsi::method ("write_header", &NetlistSpiceWriterDelegateImpl::org_write_header, "@hide") + + gsi::callback ("write_header", &NetlistSpiceWriterDelegateImpl::write_header, &NetlistSpiceWriterDelegateImpl::cb_write_header, + "@brief Writes the text at the beginning of the SPICE netlist\n" + "Reimplement this method to insert your own text at the beginning of the file" + ) + + gsi::method ("write_device_intro", &NetlistSpiceWriterDelegateImpl::org_write_device_intro, "@hide") + + gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::reimpl_write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"), + "@brief Inserts a text for the given device class\n" + "Reimplement this method to insert your own text at the beginning of the file for the given device class" + ) + + gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"), "@hide") + + gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::reimpl_write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"), + "@brief Inserts a text for the given device\n" + "Reimplement this method to write the given device in the desired way. " + "The default implementation will utilize the device class information to write native SPICE " + "elements for the devices." + ) + + gsi::method ("emit_comment", &NetlistSpiceWriterDelegateImpl::emit_comment, gsi::arg ("comment"), + "@brief Writes the given comment into the file" + ) + + gsi::method ("emit_line", &NetlistSpiceWriterDelegateImpl::emit_line, gsi::arg ("line"), + "@brief Writes the given line into the file" + ) + + gsi::method ("net_to_string", &NetlistSpiceWriterDelegateImpl::net_to_string, gsi::arg ("net"), + "@brief Gets the node ID for the given net\n" + "The node ID is a numeric string instead of the full name of the net. Numeric IDs are used within " + "SPICE netlist because they are usually shorter.\n" + ) + + gsi::method ("format_name", &NetlistSpiceWriterDelegateImpl::format_name, gsi::arg ("name"), + "@brief Formats the given name in a SPICE-compatible way" + ), + "@brief Provides a delegate for the SPICE writer for doing special formatting for devices\n" + "Supply a customized class to provide a specialized writing scheme for devices. " + "You need a customized class if you want to implement special devices or you want to use " + "subcircuits rather than the built-in devices.\n" + "\n" + "See \\NetlistSpiceWriter for more details.\n" + "\n" + "This class has been introduced in version 0.26." +); + +namespace { + +class NetlistSpiceWriterWithOwnership + : public db::NetlistSpiceWriter +{ +public: + NetlistSpiceWriterWithOwnership (NetlistSpiceWriterDelegateImpl *delegate) + : db::NetlistSpiceWriter (delegate), m_ownership (delegate) + { + if (delegate) { + delegate->keep (); + } + } + +private: + tl::shared_ptr m_ownership; +}; + +} + +db::NetlistSpiceWriter *new_spice_writer (const std::string &profile) +{ + auto *writer = new db::NetlistSpiceWriter (); + writer->set_profile (profile); + return writer; +} + +db::NetlistSpiceWriter *new_spice_writer2 (NetlistSpiceWriterDelegateImpl *delegate, const std::string &profile) +{ + auto *writer = new NetlistSpiceWriterWithOwnership (delegate); + writer->set_profile (profile); + return writer; +} + +Class db_NetlistWriter ("db", "NetlistWriter", + gsi::Methods (), + "@brief Base class for netlist writers\n" + "This class is provided as a base class for netlist writers. It is not intended for reimplementation on script level, but used internally as an interface.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "NetlistSpiceWriter", + gsi::constructor ("new", &new_spice_writer, gsi::arg ("profile", std::string ()), + "@brief Creates a new writer without delegate.\n" + "The profile string gives the name of the SPICE profile to use, when taking the SPICE representation " + "from the device classes in the netlist.\n" + "\n" + "The profile argument has been added in version 0.31.0." + ) + + gsi::constructor ("new", &new_spice_writer2, gsi::arg ("delegate"), gsi::arg ("profile", std::string ()), + "@brief Creates a new writer with a delegate.\n" + "The profile string gives the name of the SPICE profile to use, when taking the SPICE representation " + "from the device classes in the netlist.\n" + "\n" + "The profile argument has been added in version 0.31.0." + ) + + gsi::method ("not_connect_prefix", &db::NetlistSpiceWriter::not_connect_prefix, + "@brief Gets the prefix used for terminals or pins which are not connected.\n" + "See \\not_connect_prefix= for details.\n" + "\n" + "This attribute has been introduced in version 0.31.0." + ) + + gsi::method ("not_connect_prefix=", &db::NetlistSpiceWriter::set_not_connect_prefix, gsi::arg ("s"), + "@brief Sets the prefix used for terminals or pins which are not connected.\n" + "By default, the prefix is 'nc_'." + "\n" + "This attribute has been introduced in version 0.31.0." + ) + + gsi::method ("allowed_name_characters", &db::NetlistSpiceWriter::allowed_name_chars, + "@brief Gets a string listing the allowed characters for names (beside alphanumeric).\n" + "See \\allowed_name_characters= for details.\n" + "\n" + "This attribute has been introduced in version 0.31.0." + ) + + gsi::method ("allowed_name_characters=", &db::NetlistSpiceWriter::set_allowed_name_chars, gsi::arg ("s"), + "@brief Sets a string listing the allowed characters for names (beside alphanumeric).\n" + "\n" + "This attribute has been introduced in version 0.31.0." + ) + + gsi::method ("use_net_names?", &db::NetlistSpiceWriter::use_net_names, + "@brief Gets a value indicating whether to use net names (true) or net numbers (false).\n" + ) + + gsi::method ("use_net_names=", &db::NetlistSpiceWriter::set_use_net_names, gsi::arg ("f"), + "@brief Sets a value indicating whether to use net names (true) or net numbers (false).\n" + "The default is to use net numbers." + ) + + gsi::method ("use_net_names?", &db::NetlistSpiceWriter::use_net_names, + "@brief Gets a value indicating whether to use net names (true) or net numbers (false).\n" + ) + + gsi::method ("with_comments=", &db::NetlistSpiceWriter::set_with_comments, gsi::arg ("f"), + "@brief Sets a value indicating whether to embed comments for position etc. (true) or not (false).\n" + "The default is to embed comments." + ) + + gsi::method ("with_comments?", &db::NetlistSpiceWriter::with_comments, + "@brief Gets a value indicating whether to embed comments for position etc. (true) or not (false).\n" + ), + "@brief Implements a netlist writer for the SPICE format.\n" + "Provide a delegate for customizing the way devices are written.\n" + "\n" + "Use the SPICE writer like this:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceWriter::new\n" + "netlist.write(path, writer)\n" + "@/code\n" + "\n" + "You can give a custom description for the headline:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceWriter::new\n" + "netlist.write(path, writer, \"A custom description\")\n" + "@/code\n" + "\n" + "To customize the output, you can use a device writer delegate.\n" + "The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which " + "reimplements several methods to customize the following parts:\n" + "\n" + "@ul\n" + "@li A global header (\\NetlistSpiceWriterDelegate#write_header): this method is called to print the part right after the headline @/li\n" + "@li A per-device class header (\\NetlistSpiceWriterDelegate#write_device_intro): this method is called for every device class and may print device-class specific headers (e.g. model definitions) @/li\n" + "@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way. @/li\n" + "@/ul\n" + "\n" + "The delegate must use \\NetlistSpiceWriterDelegate#emit_line to print a line, \\NetlistSpiceWriterDelegate#emit_comment to print a comment etc.\n" + "For more method see \\NetlistSpiceWriterDelegate.\n" + "\n" + "A sample with a delegate is this:\n" + "\n" + "@code\n" + "class MyDelegate < RBA::NetlistSpiceWriterDelegate\n" + "\n" + " def write_header\n" + " emit_line(\"*** My special header\")\n" + " end\n" + "\n" + " def write_device_intro(cls)\n" + " emit_comment(\"My intro for class \" + cls.name)\n" + " end\n" + "\n" + " def write_device(dev)\n" + " if dev.device_class.name != \"MYDEVICE\"\n" + " emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n" + " emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n" + " super(dev)\n" + " emit_comment(\"After device \" + dev.expanded_name)\n" + " else\n" + " super(dev)\n" + " end\n" + " end\n" + "\n" + "end\n" + "\n" + "# write the netlist with delegate:\n" + "writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n" + "netlist.write(path, writer)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.26. " + "SPICE profiles have been added to device classes in version 0.31.0." +); + +} + diff --git a/src/db/db/gsiDeclDbNetlistSubcircuit.cc b/src/db/db/gsiDeclDbNetlistSubcircuit.cc new file mode 100644 index 000000000..d9cd5d6e7 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistSubcircuit.cc @@ -0,0 +1,142 @@ + +/* + + 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 + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" + +namespace gsi +{ + +static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) +{ + if (pin) { + subcircuit->connect_pin (pin->id (), net); + } +} + +static void subcircuit_disconnect_pin (db::SubCircuit *subcircuit, size_t pin_id) +{ + subcircuit->connect_pin (pin_id, 0); +} + +static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin) +{ + if (pin) { + subcircuit->connect_pin (pin->id (), 0); + } +} + +extern Class decl_dbNetlistObject; + +Class decl_dbSubCircuit (decl_dbNetlistObject, "db", "SubCircuit", + gsi::method ("circuit_ref", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit_ref, + "@brief Gets the circuit referenced by the subcircuit.\n" + ) + + gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref, + "@brief Gets the circuit referenced by the subcircuit (non-const version).\n" + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("circuit", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit, + "@brief Gets the circuit the subcircuit lives in.\n" + "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." + ) + + gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, + "@brief Gets the circuit the subcircuit lives in (non-const version).\n" + "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("id", &db::SubCircuit::id, + "@brief Gets the subcircuit ID.\n" + "The ID is a unique integer which identifies the subcircuit.\n" + "It can be used to retrieve the subcircuit from the circuit using \\Circuit#subcircuit_by_id.\n" + "When assigned, the subcircuit ID is not 0.\n" + ) + + gsi::method ("name=", &db::SubCircuit::set_name, gsi::arg ("name"), + "@brief Sets the name of the subcircuit.\n" + "SubCircuit names are used to name a subcircuits inside a netlist file. " + "SubCircuit names should be unique within a circuit." + ) + + gsi::method ("name", &db::SubCircuit::name, + "@brief Gets the name of the subcircuit.\n" + ) + + gsi::method ("expanded_name", &db::SubCircuit::expanded_name, + "@brief Gets the expanded name of the subcircuit.\n" + "The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. " + ) + + gsi::method ("net_for_pin", (const db::Net *(db::SubCircuit::*) (size_t) const) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net connected to the specified pin of the subcircuit.\n" + "If the pin is not connected, nil is returned for the net." + ) + + gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net connected to the specified pin of the subcircuit (non-const version).\n" + "If the pin is not connected, nil is returned for the net." + "\n\n" + "This constness variant has been introduced in version 0.26.8" + ) + + gsi::method ("connect_pin", &db::SubCircuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), + "@brief Connects the given pin to the specified net.\n" + ) + + gsi::method_ext ("connect_pin", &gsi::subcircuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), + "@brief Connects the given pin to the specified net.\n" + "This version takes a \\Pin reference instead of a pin ID." + ) + + gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin, gsi::arg ("pin_id"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"), + "@brief Disconnects the given pin from any net.\n" + "This version takes a \\Pin reference instead of a pin ID." + ) + + gsi::method ("trans", &db::SubCircuit::trans, + "@brief Gets the physical transformation for the subcircuit.\n" + "\n" + "This property applies to subcircuits derived from a layout. It specifies the " + "placement of the respective cell.\n" + "\n" + "This property has been introduced in version 0.27." + ) + + gsi::method ("trans=", &db::SubCircuit::set_trans, gsi::arg ("trans"), + "@brief Sets the physical transformation for the subcircuit.\n" + "\n" + "See \\trans for details about this property.\n" + "\n" + "This property has been introduced in version 0.27." + ), + "@brief A subcircuit inside a circuit.\n" + "Circuits may instantiate other circuits as subcircuits similar to cells " + "in layouts. Such an instance is a subcircuit. A subcircuit refers to a " + "circuit implementation (a \\Circuit object), and presents connections through " + "pins. The pins of a subcircuit can be connected to nets. The subcircuit pins " + "are identical to the outgoing pins of the circuit the subcircuit refers to.\n" + "\n" + "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " + "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n" + "\n" + "Subcircuit objects are created inside a circuit with \\Circuit#create_subcircuit.\n" + "\n" + "This class has been added in version 0.26." +); + +} + diff --git a/src/db/db/gsiDeclDbPath.cc b/src/db/db/gsiDeclDbPath.cc index b2b6dae33..e804e16af 100644 --- a/src/db/db/gsiDeclDbPath.cc +++ b/src/db/db/gsiDeclDbPath.cc @@ -26,6 +26,7 @@ #include "dbPoint.h" #include "dbPath.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -59,6 +60,18 @@ struct path_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -284,6 +297,19 @@ struct path_defs method ("to_s", (std::string (C::*) () const) &C::to_string, "@brief Convert to a string\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a path object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this path\n" + "\n" + "This string can be turned into a path again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method ("simple_polygon", &C::simple_polygon, "@brief Convert the path to a simple polygon\n" "The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps " diff --git a/src/db/db/gsiDeclDbPoint.cc b/src/db/db/gsiDeclDbPoint.cc index 80b65c4b1..de391726c 100644 --- a/src/db/db/gsiDeclDbPoint.cc +++ b/src/db/db/gsiDeclDbPoint.cc @@ -25,6 +25,7 @@ #include "dbPoint.h" #include "dbBox.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -46,6 +47,18 @@ struct point_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -320,6 +333,19 @@ struct point_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a point object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this point\n" + "\n" + "This string can be turned into a point again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 41b6b205a..5894f9393 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -30,6 +30,7 @@ #include "dbHash.h" #include "dbPLCTriangulation.h" #include "dbPLCConvexDecomposition.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -307,6 +308,18 @@ struct simple_polygon_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C; @@ -702,6 +715,19 @@ struct simple_polygon_defs method ("to_s", (std::string (C::*) () const) &C::to_string, "@brief Returns a string representing the polygon\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a polygon object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this polygon\n" + "\n" + "This string can be turned into a polygon again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"), "@brief Rounds the corners of the polygon\n" "\n" @@ -1298,6 +1324,18 @@ struct polygon_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C; @@ -1854,6 +1892,19 @@ struct polygon_defs method ("to_s", (std::string (C::*) () const) &C::to_string, "@brief Returns a string representing the polygon\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a polygon object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this polygon\n" + "\n" + "This string can be turned into a polygon again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"), "@brief Rounds the corners of the polygon\n" "\n" diff --git a/src/db/db/gsiDeclDbPropertiesSupport.h b/src/db/db/gsiDeclDbPropertiesSupport.h index 8016117c5..9abebee9c 100644 --- a/src/db/db/gsiDeclDbPropertiesSupport.h +++ b/src/db/db/gsiDeclDbPropertiesSupport.h @@ -27,12 +27,36 @@ #include "tlTypeTraits.h" #include "dbPropertiesRepository.h" #include "dbObjectWithProperties.h" +#include "dbBinarySerialize.h" #include "dbTrans.h" #include "dbEdge.h" namespace gsi { +template +static T *from_string (const std::string &s) +{ + tl::Extractor ex (s); + std::unique_ptr t (new T ()); + ex.read (*t.get ()); + return t.release (); +} + +template +static T *from_bytes (const std::vector &s) +{ + std::unique_ptr t (new T ()); + db::from_bytes (s, *t); + return t.release (); +} + +template +static std::vector to_bytes (const T *t) +{ + return db::to_bytes (*t); +} + template static void delete_property_meth_impl (T *s, const tl::Variant &key) { @@ -250,8 +274,27 @@ static gsi::Methods properties_support_methods () "If no property with that key does not exist, it will return nil. Using that method is more " "convenient than using the layout object and the properties ID to retrieve the property value. " ) + + gsi::constructor ("from_s", &from_string, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + gsi::method ("to_s", (std::string (T::*) () const) &T::to_string, - "@brief Returns a string representing the polygon\n" + "@brief Returns a string representing the object\n" + ) + + gsi::constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + gsi::method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this object\n" + "\n" + "This string can be turned into an object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ) + gsi::method_ext ("properties", &get_properties_meth_impl, "@brief Gets the user properties\n" diff --git a/src/db/db/gsiDeclDbText.cc b/src/db/db/gsiDeclDbText.cc index 118e7474c..7f88d763e 100644 --- a/src/db/db/gsiDeclDbText.cc +++ b/src/db/db/gsiDeclDbText.cc @@ -27,6 +27,7 @@ #include "dbPoint.h" #include "dbText.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -52,6 +53,18 @@ struct text_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -397,6 +410,19 @@ struct text_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a text object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this text object\n" + "\n" + "This string can be turned into a text object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } }; diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index 917fa60fe..7e07d6739 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -29,6 +29,7 @@ #include "dbPath.h" #include "dbText.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -98,6 +99,18 @@ struct trans_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -518,6 +531,19 @@ struct trans_defs "\n" "The DBU argument has been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this object\n" + "\n" + "This string can be turned into an object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method ("disp", (const vector_type &(C::*) () const) &C::disp, "@brief Gets to the displacement vector\n" "\n" @@ -736,6 +762,18 @@ struct cplx_trans_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -1139,6 +1177,19 @@ struct cplx_trans_defs "\n" "The lazy and DBU arguments have been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this object\n" + "\n" + "This string can be turned into an object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + method ("disp", (displacement_type (C::*)() const) &C::disp, "@brief Gets the displacement\n" ) + diff --git a/src/db/db/gsiDeclDbVector.cc b/src/db/db/gsiDeclDbVector.cc index 6946f5ac8..25b22fd4d 100644 --- a/src/db/db/gsiDeclDbVector.cc +++ b/src/db/db/gsiDeclDbVector.cc @@ -25,6 +25,7 @@ #include "dbVector.h" #include "dbPoint.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -45,6 +46,18 @@ struct vector_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -275,6 +288,19 @@ struct vector_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a vector object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this vector\n" + "\n" + "This string can be turned into a vector again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 9b6178ca8..c1dee66d1 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/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 5c9ea6012..67648a5b0 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -163,7 +163,7 @@ TEST(1_WriterBasic) rpoly_lbl.reset (0); l2n.extract_netlist (); - l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->make_top_level_pins (false); l2n.netlist ()->purge (); std::string path = tmp_file ("tmp_l2nwriter_1.txt"); diff --git a/src/db/unit_tests/dbObjectWithPropertiesTests.cc b/src/db/unit_tests/dbObjectWithPropertiesTests.cc index 751b1f34e..187cfe7f3 100644 --- a/src/db/unit_tests/dbObjectWithPropertiesTests.cc +++ b/src/db/unit_tests/dbObjectWithPropertiesTests.cc @@ -34,7 +34,7 @@ TEST(1) db::PolygonWithProperties pwp (db::Polygon (db::Box (0, 0, 100, 200)), db::properties_id (ps)); - EXPECT_EQ (pwp.to_string (), "(0,0;0,200;100,200;100,0) props={1=>one,key=>42}"); + EXPECT_EQ (pwp.to_string (), "(0,0;0,200;100,200;100,0) props={#1=>'one','key'=>##42}"); db::PolygonWithProperties pwp2; @@ -43,11 +43,11 @@ TEST(1) EXPECT_EQ (ex.try_read (pwp2), false); - s = " (0,0;0,200;100,200;100,0) props= {1 => \"one\", key => 42} "; + s = " (0,0;0,200;100,200;100,0) props= {#1 => \"one\", 'key' => ##42} "; ex = tl::Extractor (s.c_str ()); EXPECT_EQ (ex.try_read (pwp2), true); - EXPECT_EQ (pwp2.to_string (), "(0,0;0,200;100,200;100,0) props={1=>one,key=>42}"); + EXPECT_EQ (pwp2.to_string (), "(0,0;0,200;100,200;100,0) props={#1=>'one','key'=>##42}"); } } diff --git a/src/db/unit_tests/dbRegionProcessorTests.cc b/src/db/unit_tests/dbRegionProcessorTests.cc index c0e9cfb40..fcf6a3a2e 100644 --- a/src/db/unit_tests/dbRegionProcessorTests.cc +++ b/src/db/unit_tests/dbRegionProcessorTests.cc @@ -84,5 +84,5 @@ TEST(1_RegionToEdgesProcessor) result.clear (); db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepIn).process (poly, result); - EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000) props={net=>17};(2000,2000;2000,1000) props={net=>17}"); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000) props={'net'=>#17};(2000,2000;2000,1000) props={'net'=>#17}"); } diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index fb114a32b..e6730cc59 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2253,7 +2253,7 @@ TEST(52_PropertiesDeep) EXPECT_EQ (s.at_end (), false); // polygons are merged with "maximum" property value - EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={id=>42}"); + EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={'id'=>#42}"); ++s; EXPECT_EQ (s.at_end (), true); @@ -2395,7 +2395,7 @@ TEST(53_PropertiesDeepFromLayout) EXPECT_EQ (s.at_end (), false); // polygons are merged with "maximum" property value - EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={VALUE=>42}"); + EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={'VALUE'=>#42}"); ++s; EXPECT_EQ (s.at_end (), true); diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index afee536b7..aca1a28e7 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 8a23920d4..184c02f41 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 7b803db76..1eeb20541 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -142,8 +142,8 @@ public: } } - 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 @@ public: 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 4f6d97cf3..f823a3198 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 6658d2ae9..8d254ee51 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -145,7 +145,7 @@ public: /** * @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 3438f7343..ea435d66a 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 0214662fc..3302cbc3c 100644 --- a/src/img/img/imgPlugin.h +++ b/src/img/img/imgPlugin.h @@ -39,7 +39,6 @@ public: 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 bd4d5d7e9..30d1ca625 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 86850a4e2..7ef712ab2 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 c56f48357..ffe99975b 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -74,6 +74,7 @@ public: 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 f63026e95..6a2582643 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 @@ public: 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 @@ public: 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 a22ac7bc9..ade27bd24 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -859,7 +859,6 @@ private: 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 1de9b1a2b..31f571560 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 637f9ab8f..3ab6d1c06 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -1280,32 +1280,139 @@ 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 ()); + } + + // 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: 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 (); + // 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". - // NOTE: the "extras" menu is relevant on MacOS only - QMenu *extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->clear (); + if (tbar) { + tbar->clear (); } - QAction *prev_action = 0; - for (std::list::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) { if (c->has_submenu ()) { @@ -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/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index c17ffda7e..d919bd0b2 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -298,7 +298,7 @@ Bitmap::fill_pattern (int y, int x, const uint32_t *pp, unsigned int stride, uns uint32_t p = *pp; int x1 = x + s * 32; - if (x1 <= -32 || x1 >= m_width) { + if (x1 <= -32 || x1 >= (int)m_width) { continue; } else if (x1 < 0) { p >>= (unsigned int)-x1; diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index b270ab585..f5eb6489a 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -482,11 +482,15 @@ public: 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 8b8812bed..c2a1407d2 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -354,7 +354,7 @@ public: * 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 241b9f496..287dab404 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -333,11 +333,13 @@ public: 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 7088b5fe9..e53b25794 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -554,7 +554,7 @@ private: } } - 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 ce4f81afd..bc72cd656 100644 --- a/src/layview/layview/gsiDeclLayPluginFactory.cc +++ b/src/layview/layview/gsiDeclLayPluginFactory.cc @@ -318,7 +318,7 @@ public: 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 2d6fab391..ab527000a 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 6e3dd2d6a..3bc397f9b 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 712ea2bd8..ce62968e2 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 93e0cafd0..bebf4d175 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 b099be6a8..3b1bcc021 100644 --- a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui +++ b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui @@ -57,85 +57,7 @@ - - - - Pixel size - - - - - - - - 0 - 0 - - - - - - - - µm - - - - - - - - 0 - 0 - - - - 1 - - - - - - - Averaging window size - - - - - - - Threads - - - - - - - Qt::Horizontal - - - - 353 - 20 - - - - - - - - µm (a multiple of the pixel size or empty) - - - - - - - Boundary mode - - - - + @@ -168,6 +90,114 @@ + + + + Boundary mode + + + + + + + + 0 + 0 + + + + 1 + + + + + + + Averaging window size + + + + + + + Qt::Horizontal + + + + 353 + 20 + + + + + + + + µm (a multiple of the pixel size or empty) + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Threads + + + + + + + Pixel size + + + + + + + Averaging mode + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContentsOnFirstShow + + + + Square hat + + + + + Gaussian hat (σ = window size / 2) + + + + diff --git a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc index b6e280797..baba18a87 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 @@ public: 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 bd99bbbe8..ebaa4752f 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 @@ private: // 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 @@ private: 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 3f94bc341..f18782fc3 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 @@ -15695,7 +16124,8 @@ class DText: Setter: @brief Sets the horizontal alignment - This is the version accepting integer values. It's provided for backward compatibility. + 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. """ size: float r""" @@ -15759,6 +16189,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 +16538,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 +16613,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 +16666,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 +16678,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 +16832,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 +16997,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 +17851,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 +18116,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 +18479,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 +22569,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 +23282,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 +24389,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 +24693,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 +25968,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 +26021,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 +26033,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 +26187,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 +29796,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 +29849,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 +29861,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 +30015,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 +34068,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 +35022,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 +36501,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 +36514,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 +36561,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 +37058,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 +37066,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 +40451,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 +40485,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 +40511,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 +40527,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 +40712,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 +40741,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. """ ... - def flatten_into(self, source_cell_index: int, target_cell_index: int, trans: ICplxTrans, levels: int) -> None: + @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. + """ + ... + @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 +40775,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 +41299,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 +41308,63 @@ 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. """ ... - def prune_subcells(self, cell_index: int, levels: int) -> None: + @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 @@ -40620,6 +41372,27 @@ 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 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. + """ + ... + @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 + + 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 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 +41400,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 +48949,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 +49955,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 @@ -49357,10 +50160,12 @@ class Netlist: In that case, only const methods may be called on self. """ ... - def make_top_level_pins(self) -> None: + def make_top_level_pins(self, sorted_by_name: Optional[bool] = ...) -> None: r""" @brief Creates pins for top-level circuits. This method will turn all named nets of top-level circuits (such that are not referenced by subcircuits) into pins. This method can be used before purge to avoid that purge will remove nets which are directly connecting to subcircuits. + + Starting from version 0.30.9, the pins will be sorted by name by default. Sorting can be disabled by setting \sorted_by_name to false for backward compatibility. """ ... @overload @@ -49446,7 +50251,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 +50260,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 +54140,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 +54589,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 +54682,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 +54735,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 +54747,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 +54901,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 +55068,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 +55487,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 +55630,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 +56570,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 +58290,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 +58343,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 +58355,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 +58509,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 +69472,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 +70128,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 +70287,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 +70340,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 +70352,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 +70506,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 @@ -69702,15 +70660,6 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit(self) -> Circuit: - r""" - @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(self) -> Circuit: r""" @brief Gets the circuit the subcircuit lives in. @@ -69718,9 +70667,12 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit_ref(self) -> Circuit: + def circuit(self) -> Circuit: r""" - @brief Gets the circuit referenced by the subcircuit. + @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 @@ -69733,6 +70685,12 @@ class SubCircuit(NetlistObject): """ ... @overload + def circuit_ref(self) -> Circuit: + r""" + @brief Gets the circuit referenced by the subcircuit. + """ + ... + @overload def connect_pin(self, pin: Pin, net: Net) -> None: r""" @brief Connects the given pin to the specified net. @@ -69775,17 +70733,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 """ ... ... @@ -70463,6 +71421,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 +71768,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 +73121,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 +73174,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 +73186,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 +73340,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 +75195,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 +76049,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 +77027,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 +77972,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 +78491,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 +78854,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 1d547f277..4d1cf22e0 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/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index b3386179b..51061b1b3 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -11,6 +11,7 @@ FORMS = SOURCES = \ tlAssert.cc \ tlBase64.cc \ + tlBinaryStream.cc \ tlColor.cc \ tlClassRegistry.cc \ tlCopyOnWrite.cc \ @@ -62,6 +63,7 @@ HEADERS = \ tlAlgorithm.h \ tlAssert.h \ tlBase64.h \ + tlBinaryStream.h \ tlColor.h \ tlClassRegistry.h \ tlCopyOnWrite.h \ diff --git a/src/tl/tl/tlBinaryStream.cc b/src/tl/tl/tlBinaryStream.cc new file mode 100644 index 000000000..4de710814 --- /dev/null +++ b/src/tl/tl/tlBinaryStream.cc @@ -0,0 +1,691 @@ + +/* + + 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 + +*/ + +#include "tlBinaryStream.h" + +#include + +namespace tl +{ + +// --------------------------------------------------------------- +// Type codes for Variant serialization +// These type codes are intentionally separated from tl::Variant's +// type codes to allow a backward-compatible serialization scheme. + +enum VariantTypeCode +{ + var_type_nil = 0, + var_type_bool = 1, + var_type_char = 2, + var_type_schar = 3, + var_type_uchar = 4, + var_type_short = 5, + var_type_ushort = 6, + var_type_int = 7, + var_type_uint = 8, + var_type_long = 9, + var_type_ulong = 10, + var_type_longlong = 11, + var_type_ulonglong = 12, + var_type_id = 13, + var_type_float = 14, + var_type_double = 15, + var_type_string = 16, + var_type_bytes = 17, + var_type_list = 18, + var_type_array = 19, + + var_type_other = -1 +}; + +// --------------------------------------------------------------- + +class UnexpectedEndOfFileException + : public tl::Exception +{ +public: + UnexpectedEndOfFileException (const std::string &f) + : tl::Exception (tl::to_string (tr ("Unexpected end of file in: %s")), f) + { } +}; + +class InvalidVariantTypeCode + : public tl::Exception +{ +public: + InvalidVariantTypeCode (const std::string &f, int tc) + : tl::Exception (tl::to_string (tr ("Invalid variant type code %d in file: %s - maybe file is too new for this build?")), tc, f) + { } +}; + +// --------------------------------------------------------------- + +BinaryInputStream::BinaryInputStream (InputStream &stream) + : m_stream (stream) +{ + // .. nothing yet .. +} + +void +BinaryInputStream::reset () +{ + m_stream.reset (); +} + +BinaryInputStream & +BinaryInputStream::operator>> (std::string &v) +{ + uint64_t l = 0; + *this >> l; + + v.clear (); + v.reserve (l); + + size_t chunk_size = 1024, n = l; + while (n > 0) { + size_t chunk_len = std::min (n, chunk_size); + const char *chunk_data = m_stream.get (chunk_len); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v.append (chunk_data, chunk_len); + n -= chunk_len; + } + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (std::vector &v) +{ + uint64_t l = 0; + *this >> l; + + v.clear (); + v.reserve (l); + + size_t chunk_size = 1024, n = l; + while (n > 0) { + size_t chunk_len = std::min (n, chunk_size); + const char *chunk_data = m_stream.get (chunk_len); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v.insert (v.end (), chunk_data, chunk_data + chunk_len); + n -= chunk_len; + } + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (double &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (float &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (bool &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = *chunk_data != 0; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint8_t &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = (uint8_t) *chunk_data; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int8_t &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = (int8_t) *chunk_data; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint16_t &v) +{ + const char *chunk_data = m_stream.get (2); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int16_t &v) +{ + const char *chunk_data = m_stream.get (2); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint32_t &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int32_t &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint64_t &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int64_t &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (tl::Variant &v) +{ + int16_t var_type = -1; + *this >> var_type; + + switch (var_type) { + case VariantTypeCode::var_type_nil: + v = tl::Variant (); + break; + case VariantTypeCode::var_type_bool: + { + bool f = false; + *this >> f; + v = tl::Variant (f); + } + break; + case VariantTypeCode::var_type_char: + { + uint8_t vv = 0; + *this >> vv; + v = tl::Variant ((char) vv); + } + break; + case VariantTypeCode::var_type_schar: + { + int8_t vv = 0; + *this >> vv; + v = tl::Variant ((signed char) vv); + } + break; + case VariantTypeCode::var_type_uchar: + { + uint8_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned char) vv); + } + break; + case VariantTypeCode::var_type_short: + { + int16_t vv = 0; + *this >> vv; + v = tl::Variant ((short) vv); + } + break; + case VariantTypeCode::var_type_ushort: + { + uint16_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned short) vv); + } + break; + case VariantTypeCode::var_type_int: + { + int32_t vv = 0; + *this >> vv; + v = tl::Variant ((int) vv); + } + break; + case VariantTypeCode::var_type_uint: + { + uint32_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned int) vv); + } + break; + case VariantTypeCode::var_type_long: + { + int64_t vv = 0; + *this >> vv; + if (vv >= (int64_t) std::numeric_limits::min () && vv <= (int64_t) std::numeric_limits::max ()) { + v = tl::Variant ((long) vv); + } else { + // Linux to Windows migration + v = tl::Variant ((long long) vv); + } + } + break; + case VariantTypeCode::var_type_ulong: + { + uint64_t vv = 0; + *this >> vv; + if (vv >= (uint64_t) std::numeric_limits::min () && vv <= (uint64_t) std::numeric_limits::max ()) { + v = tl::Variant ((unsigned long) vv); + } else { + // Linux to Windows migration + v = tl::Variant ((unsigned long long) vv); + } + } + break; + break; + case VariantTypeCode::var_type_longlong: + { + int64_t vv = 0; + *this >> vv; + v = tl::Variant ((long long) vv); + } + break; + case VariantTypeCode::var_type_ulonglong: + { + uint64_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned long long) vv); + } + break; + case VariantTypeCode::var_type_id: + { + uint64_t vv = 0; + *this >> vv; + v = tl::Variant ((size_t) vv, true /*as id*/); + } + break; + case VariantTypeCode::var_type_float: + { + float vv = 0; + *this >> vv; + v = tl::Variant (vv); + } + break; + case VariantTypeCode::var_type_double: + { + double vv = 0; + *this >> vv; + v = tl::Variant (vv); + } + break; + case VariantTypeCode::var_type_string: + { + std::string vv; + *this >> vv; + v = tl::Variant (std::move (vv)); + } + break; + case VariantTypeCode::var_type_bytes: + { + std::vector vv; + *this >> vv; + v = tl::Variant (std::move (vv)); + } + break; + case VariantTypeCode::var_type_list: + { + uint64_t n = 0; + *this >> n; + v = tl::Variant::empty_list (); + v.reserve (n); + while (n-- > 0) { + tl::Variant vv; + *this >> vv; + v.push (std::move (vv)); + } + } + break; + case VariantTypeCode::var_type_array: + { + uint64_t n = 0; + *this >> n; + v = tl::Variant::empty_array (); + while (n-- > 0) { + tl::Variant vk, vv; + *this >> vk; + *this >> vv; + v.insert (std::move (vk), std::move (vv)); + } + } + break; + case VariantTypeCode::var_type_other: + { + std::string s; + *this >> s; + tl::Extractor ex (s.c_str ()); + v = tl::Variant (); + ex.read (v); + } + break; + default: + throw InvalidVariantTypeCode (source (), int (var_type)); + } + + return *this; +} + +// --------------------------------------------------------------- +// BinaryOutputStream implementation + +BinaryOutputStream::BinaryOutputStream (OutputStreamBase &delegate) + : OutputStream (delegate, false) +{ + // .. nothing yet .. +} + +BinaryOutputStream::BinaryOutputStream (OutputStreamBase *delegate) + : OutputStream (delegate, false) +{ + // .. nothing yet .. +} + +BinaryOutputStream::BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om, int keep_backups) + : OutputStream (abstract_path, om, false, keep_backups) +{ + // .. nothing yet .. +} + +void +BinaryOutputStream::put_native (const char *s, size_t n) +{ + // the native format for a string is a length field (uint64_t) and the bytes + // TODO: for now we assume that the memory layout is the same for all platforms + uint64_t len = uint64_t (n); + put_raw ((const char *) &len, 8); + put_raw (s, n); +} + +void +BinaryOutputStream::put_native (const std::string &s) +{ + // the native format for a string is a length field (uint64_t) and the bytes + // TODO: for now we assume that the memory layout is the same for all platforms + uint64_t len = uint64_t (s.size ()); + put_raw ((const char *) &len, 8); + put_raw (s.c_str (), s.size ()); +} + +void +BinaryOutputStream::put_native (double v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +BinaryOutputStream::put_native (float v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +BinaryOutputStream::put_native (bool v) +{ + char c = v ? 1 : 0; + put_raw (&c, 1); +} + +void +BinaryOutputStream::put_native (uint8_t v) +{ + put_raw ((const char *) &v, 1); +} + +void +BinaryOutputStream::put_native (int8_t v) +{ + put_raw ((const char *) &v, 1); +} + +void +BinaryOutputStream::put_native (uint16_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 2); +} + +void +BinaryOutputStream::put_native (int16_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 2); +} + +void +BinaryOutputStream::put_native (uint32_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +BinaryOutputStream::put_native (int32_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +BinaryOutputStream::put_native (uint64_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +BinaryOutputStream::put_native (int64_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +BinaryOutputStream::put_native (const tl::Variant &v) +{ + switch (v.type_code ()) { + case tl::Variant::t_nil: + put_native ((int16_t) VariantTypeCode::var_type_nil); + break; + case tl::Variant::t_bool: + put_native ((int16_t) VariantTypeCode::var_type_bool); + put_native (v.to_bool ()); + break; + case tl::Variant::t_char: + put_native ((int16_t) VariantTypeCode::var_type_char); + put_native ((uint8_t) v.to_char ()); + break; + case tl::Variant::t_schar: + put_native ((int16_t) VariantTypeCode::var_type_schar); + put_native ((int8_t) v.to_schar ()); + break; + case tl::Variant::t_uchar: + put_native ((int16_t) VariantTypeCode::var_type_uchar); + put_native ((uint8_t) v.to_uchar ()); + break; + case tl::Variant::t_short: + put_native ((int16_t) VariantTypeCode::var_type_short); + put_native ((int16_t) v.to_short ()); + break; + case tl::Variant::t_ushort: + put_native ((int16_t) VariantTypeCode::var_type_ushort); + put_native ((uint16_t) v.to_ushort ()); + break; + case tl::Variant::t_int: + put_native ((int16_t) VariantTypeCode::var_type_int); + put_native ((int32_t) v.to_int ()); + break; + case tl::Variant::t_uint: + put_native ((int16_t) VariantTypeCode::var_type_uint); + put_native ((uint32_t) v.to_uint ()); + break; + case tl::Variant::t_long: + put_native ((int16_t) VariantTypeCode::var_type_long); + // NOTE: "long" is always encoded as 64 bit for compatibility of Windows + Linux + put_native ((int64_t) v.to_long ()); + break; + case tl::Variant::t_ulong: + put_native ((int16_t) VariantTypeCode::var_type_ulong); + // NOTE: "ulong" is always encoded as 64 bit for compatibility of Windows + Linux + put_native ((uint64_t) v.to_ulong ()); + break; + case tl::Variant::t_longlong: + put_native ((int16_t) VariantTypeCode::var_type_longlong); + put_native ((int64_t) v.to_longlong ()); + break; + case tl::Variant::t_ulonglong: + put_native ((int16_t) VariantTypeCode::var_type_ulonglong); + put_native ((uint64_t) v.to_ulonglong ()); + break; + case tl::Variant::t_id: + put_native ((int16_t) VariantTypeCode::var_type_id); + // NOTE: "id" is always encoded as 64 bit + put_native ((uint64_t) v.to_id ()); + break; + case tl::Variant::t_float: + put_native ((int16_t) VariantTypeCode::var_type_float); + put_native (v.to_float ()); + break; + case tl::Variant::t_double: + put_native ((int16_t) VariantTypeCode::var_type_double); + put_native (v.to_double ()); + break; + case tl::Variant::t_string: + case tl::Variant::t_stdstring: + put_native ((int16_t) VariantTypeCode::var_type_string); + put_native (v.to_stdstring ()); + break; + case tl::Variant::t_bytearray: +#if defined(HAVE_QT) + case tl::Variant::t_qstring: + case tl::Variant::t_qbytearray: +#endif + { + put_native ((int16_t) VariantTypeCode::var_type_bytes); + std::vector bytes = v.to_bytearray (); + put_native (bytes.begin ().operator-> (), bytes.size ()); + } + break; + case tl::Variant::t_list: + { + put_native ((int16_t) VariantTypeCode::var_type_list); + put_native ((uint64_t) v.size ()); + for (auto i = v.begin (); i != v.end (); ++i) { + put_native (*i); + } + } + break; + case tl::Variant::t_array: + { + put_native ((int16_t) VariantTypeCode::var_type_array); + put_native ((uint64_t) v.array_size ()); + for (auto i = v.begin_array (); i != v.end_array (); ++i) { + put_native (i->first); + put_native (i->second); + } + } + break; + default: + put_native ((int16_t) VariantTypeCode::var_type_other); + put_native (v.to_parsable_string ()); + break; + } +} + +} diff --git a/src/tl/tl/tlBinaryStream.h b/src/tl/tl/tlBinaryStream.h new file mode 100644 index 000000000..4b6596ced --- /dev/null +++ b/src/tl/tl/tlBinaryStream.h @@ -0,0 +1,207 @@ + +/* + + 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 + +*/ + + +#ifndef HDR_tlBinaryStream +#define HDR_tlBinaryStream + +#include "tlCommon.h" +#include "tlStream.h" +#include "tlVariant.h" + +namespace tl +{ + +// --------------------------------------------------------------------------------- + +/** + * @brief An output stream specialized on binary representation + */ + +class TL_PUBLIC BinaryOutputStream + : public OutputStream +{ +public: + /** + * @brief Default constructor + * + * This constructor takes a delegate object. + */ + BinaryOutputStream (OutputStreamBase &delegate); + + /** + * @brief Default constructor + * + * This constructor takes a delegate object. The stream will own the delegate. + */ + BinaryOutputStream (OutputStreamBase *delegate); + + /** + * @brief Open an output stream with the given path and stream mode + * + * This will automatically create a delegate object and delete it later. + */ + BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om = OM_Auto, int keep_backups = 0); + + /** + * @brief << operator: inserts character + */ + OutputStream &operator<< (char s) + { + put (&s, 1); + return *this; + } + + /** + * @brief << operator: inserts a character + */ + OutputStream &operator<< (unsigned char s) + { + put ((const char *) &s, 1); + return *this; + } + + /** + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. + */ + BinaryOutputStream &operator<< (const char *s) + { + put_native (s, strlen (s)); + return *this; + } + + /** + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. + */ + BinaryOutputStream &operator<< (const std::string &s) + { + put_native (s); + return *this; + } + + /** + * @brief << operator: inserts an object supported by "put_native". + */ + template + BinaryOutputStream &operator<< (const T &t) + { + put_native (t); + return *this; + } + +private: + void put_native (const std::string &s); + void put_native (const char *s, size_t n); + void put_native (double v); + void put_native (float v); + void put_native (bool v); + void put_native (uint8_t v); + void put_native (int8_t v); + void put_native (uint16_t v); + void put_native (int16_t v); + void put_native (uint32_t v); + void put_native (int32_t v); + void put_native (uint64_t v); + void put_native (int64_t v); + void put_native (const tl::Variant &v); + + void set_as_text (bool f); + + // No copying currently + BinaryOutputStream (const BinaryOutputStream &); + BinaryOutputStream &operator= (const BinaryOutputStream &); +}; + +// --------------------------------------------------------------------------------- + +/** + * @brief A binary input stream + * + * This class is put in front of a InputStream to retrieve binary primitives from the stream. + * The binary format corresponds to binary mode of OutputStream. + */ +class TL_PUBLIC BinaryInputStream +{ +public: + /** + * @brief Default constructor + * + * This constructor takes a delegate object. + */ + BinaryInputStream (InputStream &stream); + + /** + * @brief Gets the raw stream + */ + InputStream &raw_stream () + { + return m_stream; + } + + /** + * @brief Get the source specification + */ + std::string source () const + { + return m_stream.source (); + } + + /** + * @brief Gets a value of a specific type + */ + BinaryInputStream &operator>> (std::string &v); + BinaryInputStream &operator>> (std::vector &v); + BinaryInputStream &operator>> (double &v); + BinaryInputStream &operator>> (float &v); + BinaryInputStream &operator>> (bool &v); + BinaryInputStream &operator>> (uint8_t &v); + BinaryInputStream &operator>> (int8_t &v); + BinaryInputStream &operator>> (uint16_t &v); + BinaryInputStream &operator>> (int16_t &v); + BinaryInputStream &operator>> (uint32_t &v); + BinaryInputStream &operator>> (int32_t &v); + BinaryInputStream &operator>> (uint64_t &v); + BinaryInputStream &operator>> (int64_t &v); + BinaryInputStream &operator>> (tl::Variant &v); + + /** + * @brief Resets to the initial position + */ + void reset (); + +private: + InputStream &m_stream; + + // no copying + BinaryInputStream (const BinaryInputStream &); + BinaryInputStream &operator= (const BinaryInputStream &); +}; + +} + +#endif diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 4e52db055..fce7a43ae 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -20,8 +20,6 @@ */ - - #include #include #include diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 70fac833c..0b9764611 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -730,7 +730,7 @@ public: } /** - * @brief Get a single line (presumably UTF8 encoded) + * @brief Gets a single line (presumably UTF8 encoded) */ const std::string &get_line (); @@ -747,7 +747,7 @@ public: std::string read_all (size_t max_count); /** - * @brief Get a single character + * @brief Gets a single character */ char get_char (); @@ -757,14 +757,14 @@ public: char peek_char (); /** - * @brief Skip blanks, newlines etc. + * @brief Skips blanks, newlines etc. * * Returns the following character without getting it. */ char skip (); /** - * @brief Get the source specification + * @brief Gets the source specification */ std::string source () const { @@ -772,7 +772,7 @@ public: } /** - * @brief Get the current line number + * @brief Gets the current line number */ size_t line_number () { @@ -780,7 +780,7 @@ public: } /** - * @brief Return false, if no more characters can be obtained + * @brief Returns false, if no more characters can be obtained */ bool at_end () const { @@ -788,7 +788,7 @@ public: } /** - * @brief Reset to the initial position + * @brief Resets to the initial position */ void reset (); @@ -880,9 +880,9 @@ public: /** * @brief Create a string writer */ - OutputMemoryStream () + OutputMemoryStream (size_t initial_alloc = 65536) { - m_buffer.reserve (65336); + m_buffer.reserve (initial_alloc); } /** @@ -1312,13 +1312,20 @@ public: void close (); /** - * @brief This is the outer write method to call + * @brief Puts a string into the stream * - * This implementation writes data through the - * protected write call. + * In text mode, this handles line separator conversion. + * In binary mode, this method is equivalent to "put_raw". */ void put (const char *b, size_t n); + /** + * @brief Puts the raw bytes into the stream + * + * This method bypasses the line feed translation. + */ + void put_raw (const char *b, size_t n); + /** * @brief Puts a C string (UTF-8) to the output */ @@ -1336,7 +1343,7 @@ public: } /** - * @brief << operator + * @brief << operator: inserts character */ OutputStream &operator<< (char s) { @@ -1345,7 +1352,7 @@ public: } /** - * @brief << operator + * @brief << operator: inserts a character */ OutputStream &operator<< (unsigned char s) { @@ -1354,16 +1361,22 @@ public: } /** - * @brief << operator + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. */ OutputStream &operator<< (const char *s) { - put (s); + put (s, strlen (s)); return *this; } /** - * @brief << operator + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. */ OutputStream &operator<< (const std::string &s) { @@ -1372,7 +1385,7 @@ public: } /** - * @brief << operator + * @brief << operator: inserts an object supported by "put_native". */ template OutputStream &operator<< (const T &t) @@ -1456,8 +1469,6 @@ private: size_t m_buffer_capacity, m_buffer_pos; std::string m_path; - void put_raw (const char *b, size_t n); - // No copying currently OutputStream (const OutputStream &); OutputStream &operator= (const OutputStream &); diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 251568511..9242d99c7 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -651,7 +651,7 @@ to_quoted_string (const std::string &s) std::string r; r.reserve (s.size () + 2); r += '\''; - for (const char *c = s.c_str (); *c; ++c) { + for (auto c = s.begin (); c != s.end (); ++c) { if (*c == '\'' || *c == '\\') { r += '\\'; r += *c; diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 3633ef37b..aef08085a 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -315,6 +315,12 @@ Variant::Variant (const std::vector &ba) m_var.m_bytearray = new std::vector (ba); } +Variant::Variant (std::vector &&ba) + : m_type (t_bytearray), m_string (0) +{ + m_var.m_bytearray = new std::vector (ba); +} + #if defined(HAVE_QT) Variant::Variant (const QByteArray &qba) @@ -530,7 +536,13 @@ Variant::Variant (const std::string &s) m_var.m_stdstring = new std::string (s); } -Variant::Variant (const char *s) +Variant::Variant (std::string &&s) + : m_type (t_stdstring), m_string (0) +{ + m_var.m_stdstring = new std::string (s); +} + +Variant::Variant (const char *s) : m_type (s != 0 ? t_string : t_nil) { if (s) { @@ -645,6 +657,12 @@ Variant::Variant (const Variant &v) operator= (v); } +Variant::Variant (Variant &&v) + : m_type (t_nil), m_string (0) +{ + swap (v); +} + Variant::~Variant () { reset (); @@ -713,6 +731,20 @@ Variant::operator= (const std::string &s) return *this; } +Variant & +Variant::operator= (std::string &&s) +{ + if (m_type == t_stdstring && &s == m_var.m_stdstring) { + // we are assigning to ourselves + } else { + std::string *snew = new std::string (s); + reset (); + m_type = t_stdstring; + m_var.m_stdstring = snew; + } + return *this; +} + Variant & Variant::operator= (const std::vector &s) { @@ -727,6 +759,20 @@ Variant::operator= (const std::vector &s) return *this; } +Variant & +Variant::operator= (std::vector &&s) +{ + if (m_type == t_bytearray && &s == m_var.m_bytearray) { + // we are assigning to ourselves + } else { + std::vector *snew = new std::vector (s); + reset (); + m_type = t_bytearray; + m_var.m_bytearray = snew; + } + return *this; +} + #if defined(HAVE_QT) Variant & @@ -900,6 +946,13 @@ Variant::operator= (__int128 l) } #endif +Variant & +Variant::operator= (Variant &&v) +{ + swap (v); + return *this; +} + Variant & Variant::operator= (const Variant &v) { @@ -1958,7 +2011,7 @@ Variant::to_string () const } else if (m_type == t_float) { r = tl::to_string (m_var.m_float, 7); } else if (m_type == t_char) { - r = tl::to_string ((int) m_var.m_char); + r += m_var.m_char; } else if (m_type == t_schar) { r = tl::to_string ((int) m_var.m_schar); } else if (m_type == t_uchar) { @@ -2602,12 +2655,10 @@ Variant::to_parsable_string () const return "nil"; } else if (is_stdstring ()) { return tl::to_quoted_string (*m_var.m_stdstring); -#if defined(HAVE_QT) - } else if (is_cstring () || is_qstring () || is_qbytearray () || is_bytearray ()) { -#else - } else if (is_cstring () || is_bytearray ()) { -#endif - return tl::to_quoted_string (to_string ()); + } else if (is_a_string ()) { + return tl::to_quoted_string (to_stdstring ()); + } else if (is_a_bytearray ()) { + return tl::to_quoted_string (to_stdstring ()) + "b"; } else if (is_list ()) { std::string r = "("; for (tl::Variant::const_iterator l = begin (); l != end (); ++l) { @@ -2630,6 +2681,10 @@ Variant::to_parsable_string () const } r += "}"; return r; + } else if (is_char ()) { + std::string s; + s += to_char (); + return tl::to_quoted_string (s) + "c"; } else if (is_id ()) { return "[id" + tl::to_string (m_var.m_id) + "]"; } else if (is_user ()) { @@ -3049,7 +3104,15 @@ TL_PUBLIC bool test_extractor_impl (tl::Extractor &ex, tl::Variant &v) } else if (ex.try_read_word_or_quoted (s)) { - v = tl::Variant (s); + if (ex.test ("c") || ex.test ("C")) { + v = s.empty () ? tl::Variant () : tl::Variant ((char) s.front ()); + } else if (ex.test ("b") || ex.test ("B")) { + std::vector cv (s.begin (), s.end ()); + v = tl::Variant (std::move (cv)); + } else { + v = tl::Variant (s); + } + return true; } else { diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index 5abaa1c45..0d2c28d96 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -205,11 +205,21 @@ public: */ Variant (const tl::Variant &d); + /** + * @brief Move ctor + */ + Variant (tl::Variant &&d); + /** * @brief Initialize the Variant with a std::vector */ Variant (const std::vector &s); + /** + * @brief Initialize the Variant with a std::vector (move semantics) + */ + Variant (std::vector &&s); + #if defined(HAVE_QT) /** * @brief Initialize the Variant with a QByteArray @@ -234,6 +244,11 @@ public: */ Variant (const std::string &s); + /** + * @brief Initialize the Variant with "string" (move semantics) + */ + Variant (std::string &&s); + /** * @brief Initialize the Variant with "string" */ @@ -466,6 +481,15 @@ public: m_var.m_list = new std::vector (list); } + /** + * @brief Initialize the Variant with an explicit vector of variants (move semantics) + */ + Variant (std::vector &&list) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (list); + } + /** * @brief Initialize the Variant with an explicit map of variants */ @@ -475,6 +499,15 @@ public: m_var.m_array = new std::map (map); } + /** + * @brief Initialize the Variant with an explicit map of variants (move semantics) + */ + Variant (std::map &&map) + : m_type (t_array), m_string (0) + { + m_var.m_array = new std::map (map); + } + /** * @brief Initialize the Variant with a list */ @@ -546,6 +579,11 @@ public: */ Variant &operator= (const Variant &v); + /** + * @brief Assignment (move) + */ + Variant &operator= (Variant &&v); + /** * @brief Assignment of a string */ @@ -568,11 +606,21 @@ public: */ Variant &operator= (const std::string &v); + /** + * @brief Assignment of a string (move) + */ + Variant &operator= (std::string &&v); + /** * @brief Assignment of a STL byte array */ Variant &operator= (const std::vector &v); + /** + * @brief Assignment of a STL byte array (move) + */ + Variant &operator= (std::vector &&v); + /** * @brief Assignment of a double */ @@ -1173,6 +1221,15 @@ public: m_var.m_list->push_back (v); } + /** + * @brief Add a element to the list (move semantics) + */ + void push (tl::Variant &&v) + { + tl_assert (m_type == t_list); + m_var.m_list->push_back (v); + } + /** * @brief Get the back element of the list */ @@ -1280,6 +1337,15 @@ public: m_var.m_array->insert (std::make_pair (k, v)); } + /** + * @brief Insert an element into the array (move semantics) + */ + void insert (tl::Variant &&k, tl::Variant &&v) + { + tl_assert (m_type == t_array); + m_var.m_array->insert (std::make_pair (k, v)); + } + /** * @brief Returns the value for the given key or 0 if the variant is not an array or does not contain the key */ diff --git a/src/tl/unit_tests/tlBinaryStreamTests.cc b/src/tl/unit_tests/tlBinaryStreamTests.cc new file mode 100644 index 000000000..3a3c77779 --- /dev/null +++ b/src/tl/unit_tests/tlBinaryStreamTests.cc @@ -0,0 +1,425 @@ + +/* + + 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 + +*/ + +#include "tlBinaryStream.h" +#include "tlUnitTest.h" + +std::string s2string (tl::OutputMemoryStream &osm) +{ + std::string res; + size_t n = osm.size (); + const char *d = osm.data (); + for (size_t i = 0; i < n; ++i, ++d) { + if (i > 0) { + res += ","; + } + res += tl::sprintf ("0x%02x", int ((unsigned char) *d)); + } + return res; +} + +TEST(BinaryStreams1) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << (double) 0.17; + + os.flush (); + EXPECT_EQ (s2string (osm), "0xc3,0xf5,0x28,0x5c,0x8f,0xc2,0xc5,0x3f"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + double x = 0.0; + bis >> x; + + EXPECT_EQ (tl::to_string (x), "0.17"); +} + +TEST(BinaryStreams2) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << (float) 0.17; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x7b,0x14,0x2e,0x3e"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + float x = 0.0; + bis >> x; + + EXPECT_EQ (tl::to_string (x), "0.17"); +} + +TEST(BinaryStreams3) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << std::string ("ABC"); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string x; + bis >> x; + + EXPECT_EQ (x, "ABC"); +} + +TEST(BinaryStreams4) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << "ABC"; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string x; + bis >> x; + + EXPECT_EQ (x, "ABC"); +} + +TEST(BinaryStreams5) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint8_t (17); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x11"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint8_t x; + bis >> x; + + EXPECT_EQ (x, 17); +} + +TEST(BinaryStreams6) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int8_t (17); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x11"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int8_t x; + bis >> x; + + EXPECT_EQ (x, 17); +} + +TEST(BinaryStreams7) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint16_t (1742); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xce,0x06"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint16_t x; + bis >> x; + + EXPECT_EQ (x, 1742); +} + +TEST(BinaryStreams8) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int16_t (1742); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xce,0x06"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int16_t x; + bis >> x; + + EXPECT_EQ (x, 1742); +} + +TEST(BinaryStreams9) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint32_t (17420000); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint32_t x; + bis >> x; + + EXPECT_EQ (x, 17420000u); +} + +TEST(BinaryStreams10) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int32_t (17420000); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int32_t x; + bis >> x; + + EXPECT_EQ (x, 17420000); +} + +TEST(BinaryStreams11) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint64_t (174200000000l); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint64_t x; + bis >> x; + + EXPECT_EQ (x, 174200000000lu); +} + +TEST(BinaryStreams12) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int64_t (174200000000l); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int64_t x; + bis >> x; + + EXPECT_EQ (x, 174200000000l); +} + +TEST(BinaryStreams13) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << true; + os << false; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x01,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + bool x = false, y = false; + bis >> x >> y; + + EXPECT_EQ (x, true); + EXPECT_EQ (y, false); +} + +TEST(BinaryStreamsCombined) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << "ABC" << 17.0 << "XUV" << (int32_t) 42; + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string a, c; + double b = 0.0; + int32_t d = 0; + + bis >> a >> b >> c >> d; + + EXPECT_EQ (a, "ABC"); + EXPECT_EQ (b, 17.0); + EXPECT_EQ (c, "XUV"); + EXPECT_EQ (d, 42); +} + +TEST(BinaryStreamsVariants) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + std::vector bytes = { 'X', 'U', 'V' }; + + os << tl::Variant ("ABC") + << tl::Variant () + << tl::Variant (bytes) + << tl::Variant ((float) 42.5) + << tl::Variant ((double) -17.5) + << tl::Variant ((char) 'x') + << tl::Variant ((unsigned char) 'u') + << tl::Variant ((signed char) 'v') + << tl::Variant ((short) 17) + << tl::Variant ((unsigned short) 42) + << tl::Variant ((int) 18) + << tl::Variant ((unsigned int) 43) + << tl::Variant ((long) 19) + << tl::Variant ((unsigned long) 44) + << tl::Variant ((long long) 20) + << tl::Variant ((unsigned long long) 45) + << tl::Variant ((size_t) 202, true /*id*/); + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + tl::Variant v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17; + + bis >> v1 >> v2 >> v3 >> v4 >> v5 >> v6 >> v7 >> v8 >> v9 >> v10 >> v11 >> v12 >> v13 >> v14 >> v15 >> v16 >> v17; + + EXPECT_EQ (v1.to_parsable_string (), "'ABC'"); + EXPECT_EQ (v2.to_parsable_string (), "nil"); + EXPECT_EQ (v3.to_parsable_string (), "'XUV'b"); + EXPECT_EQ (v3.type_code (), tl::Variant::t_bytearray); + EXPECT_EQ (v4.to_parsable_string (), "##42.5"); + EXPECT_EQ (v5.to_parsable_string (), "##-17.5"); + EXPECT_EQ (v6.to_parsable_string (), "'x'c"); + EXPECT_EQ (v7.to_parsable_string (), "#u117"); + EXPECT_EQ (v8.to_parsable_string (), "#118"); + EXPECT_EQ (v9.to_parsable_string (), "#17"); + EXPECT_EQ (v10.to_parsable_string (), "#u42"); + EXPECT_EQ (v11.to_parsable_string (), "#18"); + EXPECT_EQ (v12.to_parsable_string (), "#u43"); + EXPECT_EQ (v13.to_parsable_string (), "#19"); + EXPECT_EQ (v14.to_parsable_string (), "#u44"); + EXPECT_EQ (v15.to_parsable_string (), "#l20"); + EXPECT_EQ (v16.to_parsable_string (), "#lu45"); + EXPECT_EQ (v17.to_parsable_string (), "[id202]"); +} + +TEST(BinaryStreamsVariantList) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + std::vector list = { 5.5, -17, "ABC" }; + + os << tl::Variant (list); + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + tl::Variant v1; + + bis >> v1; + + EXPECT_EQ (v1.to_parsable_string (), "(##5.5,#-17,'ABC')"); +} + +TEST(BinaryStreamsVariantArray) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + std::vector > a = { { 1, 5.5 }, { 2, -17 }, { "id", "ABC" } }; + + std::map array (a.begin (), a.end ()); + os << tl::Variant (array); + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + tl::Variant v1; + + bis >> v1; + + EXPECT_EQ (v1.to_parsable_string (), "{#1=>##5.5,#2=>#-17,'id'=>'ABC'}"); +} diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 4396a102f..71f85030e 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -66,7 +66,7 @@ TEST(OutputPipe1) { tl::OutputPipe pipe ("cat >" + tf); tl::OutputStream str (pipe); - str << "Hello, world!"; + str.put ("Hello, world!"); } { @@ -82,7 +82,7 @@ TEST(TextOutputStream) { tl::OutputStream os (fn, tl::OutputStream::OM_Auto, false); - os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."; + os.put ("Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."); } { @@ -140,7 +140,7 @@ TEST(TextInputStream) { tl::OutputStream os (fn, tl::OutputStream::OM_Auto, false); - os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."; + os.put ("Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."); } { @@ -242,7 +242,7 @@ TEST(SafeOutput) { tl::OutputStream os (tp); - os << "blabla\n"; + os.put ("blabla\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -252,7 +252,7 @@ TEST(SafeOutput) tl::OutputStream os (tp); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hello, world!\n"; + os.put ("Hello, world!\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -268,7 +268,7 @@ TEST(SafeOutput) tl::OutputStream os (broken); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hi!\n"; + os.put ("Hi!\n"); os.flush (); // raises the exception EXPECT_EQ (true, false); } catch (...) { @@ -291,7 +291,7 @@ TEST(SafeOutput) tl::OutputStream os (broken); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hi!\n"; + os.put ("Hi!\n"); os.flush (); // raises the exception EXPECT_EQ (true, false); } catch (...) { @@ -323,7 +323,7 @@ TEST(SafeOutput2) { tl::OutputStream os (tp); - os << "blabla\n"; + os.put ("blabla\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -333,7 +333,7 @@ TEST(SafeOutput2) tl::OutputStream os (tp); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hello, world!\n"; + os.put ("Hello, world!\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -358,7 +358,7 @@ TEST(Backups) { tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); - os << "1\n"; + os.put ("1\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -376,7 +376,7 @@ TEST(Backups) tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "2\n"; + os.put ("2\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -399,7 +399,7 @@ TEST(Backups) tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "3\n"; + os.put ("3\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -427,7 +427,7 @@ TEST(Backups) tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "4\n"; + os.put ("4\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -456,7 +456,7 @@ TEST(Backups) tl::OutputStream os (broken); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "5!\n"; + os.put ("5!\n"); os.flush (); // raises the exception EXPECT_EQ (true, false); } catch (...) { diff --git a/src/tl/unit_tests/tlVariantTests.cc b/src/tl/unit_tests/tlVariantTests.cc index 28c71258c..15e354c7d 100644 --- a/src/tl/unit_tests/tlVariantTests.cc +++ b/src/tl/unit_tests/tlVariantTests.cc @@ -97,6 +97,7 @@ TEST(1) EXPECT_EQ (v.is_ulong (), false); EXPECT_EQ (v.is_ulonglong (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "nil"); EXPECT_EQ (v.to_parsable_string (), "nil"); vv = v; EXPECT_EQ (vv == v, true); @@ -109,6 +110,43 @@ TEST(1) EXPECT_EQ (vx == v, true); } + { + tl::Variant v ((char) '\033'); +#if defined(HAVE_QT) + EXPECT_EQ (tl::to_string (v.to_qvariant ().toString ()), "27"); +#endif + EXPECT_EQ (v.is_nil (), false); + EXPECT_EQ (v.is_list (), false); + EXPECT_EQ (v.is_cstring (), false); + EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is_char (), true); + EXPECT_EQ (v.is_long (), false); + EXPECT_EQ (v.is_longlong (), false); + EXPECT_EQ (v.is_ulong (), false); + EXPECT_EQ (v.is_ulonglong (), false); + EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "\033"); + EXPECT_EQ (v.to_parsable_string (), "'\\033'c"); + vv = v; + EXPECT_EQ (vv == v, true); + EXPECT_EQ (vv != v, false); + tl::Variant vx; + std::string s (v.to_parsable_string ()); + tl::Extractor ex (s.c_str ()); + ex.read (vx); + ex.expect_end (); + EXPECT_EQ (vx == v, true); + } + { tl::Variant v (1ul); #if defined(HAVE_QT) @@ -123,6 +161,7 @@ TEST(1) EXPECT_EQ (v.is_long (), false); EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "1"); EXPECT_EQ (v.to_parsable_string (), "#u1"); EXPECT_EQ (v.to_long (), 1l); EXPECT_EQ (v.to_longlong (), 1l); @@ -161,6 +200,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_id (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#u2"); EXPECT_EQ (v.to_long (), 2l); EXPECT_EQ (v.to_longlong (), 2l); @@ -196,6 +236,7 @@ TEST(1) EXPECT_EQ (v.is (), true); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "1"); EXPECT_EQ (v.to_parsable_string (), "#1"); EXPECT_EQ (v.to_long (), 1l); EXPECT_EQ (v.to_longlong (), 1l); @@ -236,6 +277,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#2"); EXPECT_EQ (v.to_long (), 2l); EXPECT_EQ (v.to_longlong (), 2l); @@ -279,6 +321,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.to_string (), "5"); EXPECT_EQ (v.to_parsable_string (), "##5"); EXPECT_EQ (v.to_double (), 5.0); EXPECT_EQ (v.to_float (), 5.0); @@ -327,6 +370,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.to_string (), "5"); EXPECT_EQ (v.to_parsable_string (), "##5"); EXPECT_EQ (v.to_double (), 5.0); EXPECT_EQ (v.to_long (), 5); @@ -386,6 +430,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_ulonglong (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#2"); vv = v; EXPECT_EQ (vv == v, true); @@ -419,6 +464,8 @@ TEST(1) EXPECT_EQ (v.is_nil (), false); EXPECT_EQ (v.is_list (), false); EXPECT_EQ (v.is_cstring (), false); + EXPECT_EQ (v.is_bytearray (), false); + EXPECT_EQ (v.is_a_bytearray (), false); EXPECT_EQ (v.is_id (), false); EXPECT_EQ (v.is_char (), false); EXPECT_EQ (v.is_long (), false); @@ -432,6 +479,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#u2"); vv = v; EXPECT_EQ (vv == v, true); @@ -454,6 +502,39 @@ TEST(1) EXPECT_EQ (*(long *)v.native_ptr(), 2); } + { + std::vector bytes = { '"', 'h', 0, '\033', 'a', 'l', 'l', 'O', '"' }; + tl::Variant v (std::move (bytes)); +#if defined(HAVE_QT) + EXPECT_EQ (tl::Variant (v.to_qvariant ()).to_parsable_string (), "'\"h\\000\\033allO\"'b"); +#endif + EXPECT_EQ (v.is_nil (), false); + EXPECT_EQ (v.is_list (), false); + EXPECT_EQ (v.is_cstring (), false); + EXPECT_EQ (v.is_bytearray (), true); + EXPECT_EQ (v.is_a_bytearray (), true); + EXPECT_EQ (v.is_long (), false); + EXPECT_EQ (v.is_ulong (), false); + EXPECT_EQ (v.is_longlong (), false); + EXPECT_EQ (v.is_ulonglong (), false); + EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.to_parsable_string (), "'\"h\\000\\033allO\"'b"); + EXPECT_EQ (v.to_stdstring (), std::string ("\"h\000\033allO\"", 9)); + EXPECT_EQ (vv == v, false); + EXPECT_EQ (vv != v, true); + vv = v; + EXPECT_EQ (vv == v, true); + EXPECT_EQ (vv != v, false); + tl::Variant vx; + std::string s (v.to_parsable_string ()); + tl::Extractor ex (s.c_str ()); + ex.read (vx); + ex.expect_end (); + EXPECT_EQ (vx.is_bytearray (), true); + EXPECT_EQ (vx == v, true); + } + { tl::Variant v ("hal'l\"o"); #if defined(HAVE_QT) @@ -534,6 +615,7 @@ TEST(1) EXPECT_EQ (v.is_id (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "(1,5,25)"); EXPECT_EQ (v.to_parsable_string (), "(#1,#5,#25)"); EXPECT_EQ (v.get_list ().size (), size_t (3)); EXPECT_EQ (v.begin ()->is_long (), true); @@ -571,6 +653,7 @@ TEST(1) EXPECT_EQ (v.is_ulonglong (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "17"); EXPECT_EQ (v.to_parsable_string (), "#l17"); tl::Variant vx; std::string s (v.to_parsable_string ()); @@ -595,6 +678,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "17"); EXPECT_EQ (v.to_parsable_string (), "#lu17"); tl::Variant vx; std::string s (v.to_parsable_string ()); @@ -619,6 +703,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "[id17]"); EXPECT_EQ (v.to_parsable_string (), "[id17]"); } @@ -642,6 +727,7 @@ TEST(1) v.insert (tl::Variant (1), tl::Variant ("A")); EXPECT_EQ (v.to_parsable_string (), "{#1=>\'A\'}"); v.insert (tl::Variant ("B"), tl::Variant (17)); + EXPECT_EQ (v.to_string (), "{1=>A,B=>17}"); EXPECT_EQ (v.to_parsable_string (), "{#1=>\'A\',\'B\'=>#17}"); #if defined(HAVE_QT) EXPECT_EQ (tl::Variant (v.to_qvariant ()).to_parsable_string (), "{\'1\'=>\'A\',\'B\'=>#17}"); @@ -1138,7 +1224,7 @@ TEST(8) v = tl::Variant (QByteArray ()); EXPECT_EQ (v.to_parsable_string (), "nil"); v = tl::Variant (QByteArray ("abc")); - EXPECT_EQ (v.to_parsable_string (), "'abc'"); + EXPECT_EQ (v.to_parsable_string (), "'abc'b"); v = QByteArray (); EXPECT_EQ (v.to_parsable_string (), "nil"); #endif diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 3d8b06676..5ea1fe9cf 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ tlBase64Tests.cc \ + tlBinaryStreamTests.cc \ tlClassRegistryTests.cc \ tlCommandLineParserTests.cc \ tlColorTests.cc \ diff --git a/testdata/lstream/pcells_au.oas b/testdata/lstream/pcells_au.oas index 88943799d..3554c35e7 100644 Binary files a/testdata/lstream/pcells_au.oas and b/testdata/lstream/pcells_au.oas differ diff --git a/testdata/lvs/bbdevices1b.lvs b/testdata/lvs/bbdevices1b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices1b.lvs +++ b/testdata/lvs/bbdevices1b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices2b.lvs b/testdata/lvs/bbdevices2b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices2b.lvs +++ b/testdata/lvs/bbdevices2b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices4b.lvs b/testdata/lvs/bbdevices4b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices4b.lvs +++ b/testdata/lvs/bbdevices4b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices5b.lvs b/testdata/lvs/bbdevices5b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices5b.lvs +++ b/testdata/lvs/bbdevices5b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices6b.lvs b/testdata/lvs/bbdevices6b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices6b.lvs +++ b/testdata/lvs/bbdevices6b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/custom_compare.lvs b/testdata/lvs/custom_compare.lvs index 25e60364c..9b58b6bd0 100644 --- a/testdata/lvs/custom_compare.lvs +++ b/testdata/lvs/custom_compare.lvs @@ -63,6 +63,7 @@ end netlist.device_class_by_name("RES").equal_parameters = ResistorComparator::new() # Netlist normalization +netlist.make_top_level_pins(false) netlist.simplify # Hierarchy alignment (flatten out unmatched cells) diff --git a/testdata/lvs/double_height.cir b/testdata/lvs/double_height.cir index 6c6f92e17..1e727a252 100644 --- a/testdata/lvs/double_height.cir +++ b/testdata/lvs/double_height.cir @@ -1,6 +1,6 @@ * Extracted by KLayout -.SUBCKT INVCHAIN IN OUT VSS VDD +.SUBCKT INVCHAIN IN OUT VDD VSS X$1 VDD IN \$1 \$1 OUT VSS INV2 .ENDS INVCHAIN diff --git a/testdata/lvs/double_height.lvs b/testdata/lvs/double_height.lvs index ec154d59f..eb5e89129 100644 --- a/testdata/lvs/double_height.lvs +++ b/testdata/lvs/double_height.lvs @@ -123,6 +123,7 @@ connect_implicit("INV2", "VSS") # Compare section +# NOTE: indirectly tests "make_top_level_pins" with name sorting (#2360) netlist.simplify align diff --git a/testdata/lvs/double_height.lvsdb b/testdata/lvs/double_height.lvsdb index f9b66a23d..849f93776 100644 --- a/testdata/lvs/double_height.lvsdb +++ b/testdata/lvs/double_height.lvsdb @@ -185,8 +185,8 @@ J( ) P(2 I(IN)) P(3 I(OUT)) - P(4 I(VSS)) P(5 I(VDD)) + P(4 I(VSS)) X(1 INV2 Y(0 0) P(0 5) P(1 2) @@ -318,8 +318,8 @@ Z( N(4 2 1) P(0 () 1) P(1 () 1) - P(3 () 1) P(2 () 1) + P(3 () 1) X(1 1 1) ) ) diff --git a/testdata/lvs/double_height2.lvs b/testdata/lvs/double_height2.lvs index 71e32a05c..1060dec05 100644 --- a/testdata/lvs/double_height2.lvs +++ b/testdata/lvs/double_height2.lvs @@ -130,6 +130,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/double_height2_texts.lvs b/testdata/lvs/double_height2_texts.lvs index c3d169a9d..a2882395e 100644 --- a/testdata/lvs/double_height2_texts.lvs +++ b/testdata/lvs/double_height2_texts.lvs @@ -130,6 +130,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/enable_wl1.lvs b/testdata/lvs/enable_wl1.lvs index a58903723..c97bbda95 100644 --- a/testdata/lvs/enable_wl1.lvs +++ b/testdata/lvs/enable_wl1.lvs @@ -47,6 +47,7 @@ connect(contact, metal1_not_res) connect(metal1_not_res, metal1_lbl) align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/enable_wl2.lvs b/testdata/lvs/enable_wl2.lvs index 945a89294..7ca716ee3 100644 --- a/testdata/lvs/enable_wl2.lvs +++ b/testdata/lvs/enable_wl2.lvs @@ -28,6 +28,7 @@ connect(contact, metal1_not_res) connect(metal1_not_res, metal1_lbl) align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/enable_wl3.lvs b/testdata/lvs/enable_wl3.lvs index 8f5405307..552524f62 100644 --- a/testdata/lvs/enable_wl3.lvs +++ b/testdata/lvs/enable_wl3.lvs @@ -28,6 +28,7 @@ connect(contact, metal1_not_res) connect(metal1_not_res, metal1_lbl) align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/flag_missing_ports.lvs b/testdata/lvs/flag_missing_ports.lvs index 4c35142a3..cf98f6dd7 100644 --- a/testdata/lvs/flag_missing_ports.lvs +++ b/testdata/lvs/flag_missing_ports.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/floating.lvs b/testdata/lvs/floating.lvs index 8a3c6417e..d0e6c1e09 100644 --- a/testdata/lvs/floating.lvs +++ b/testdata/lvs/floating.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/invchain_cheat.lvs b/testdata/lvs/invchain_cheat.lvs index 93c6206e7..379a0c601 100644 --- a/testdata/lvs/invchain_cheat.lvs +++ b/testdata/lvs/invchain_cheat.lvs @@ -123,6 +123,7 @@ connect_global(bulk, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/layer_names.lvs b/testdata/lvs/layer_names.lvs index 9ea2cf9ca..071e511fd 100644 --- a/testdata/lvs/layer_names.lvs +++ b/testdata/lvs/layer_names.lvs @@ -96,6 +96,7 @@ ok || raise("ptie layer already used - this is an error") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect1.lvs b/testdata/lvs/must_connect1.lvs index 799fa0364..0981cde4c 100644 --- a/testdata/lvs/must_connect1.lvs +++ b/testdata/lvs/must_connect1.lvs @@ -132,6 +132,7 @@ connect_implicit("INVCHAIN", "VDD") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect1_tl.lvs b/testdata/lvs/must_connect1_tl.lvs index 4e9cc35ed..60379dc81 100644 --- a/testdata/lvs/must_connect1_tl.lvs +++ b/testdata/lvs/must_connect1_tl.lvs @@ -136,6 +136,7 @@ connect_implicit("INVCHAIN", "VDD") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect2.lvs b/testdata/lvs/must_connect2.lvs index ac34742e7..22ead7473 100644 --- a/testdata/lvs/must_connect2.lvs +++ b/testdata/lvs/must_connect2.lvs @@ -134,6 +134,7 @@ connect_global(bulk, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect3.lvs b/testdata/lvs/must_connect3.lvs index ac34742e7..22ead7473 100644 --- a/testdata/lvs/must_connect3.lvs +++ b/testdata/lvs/must_connect3.lvs @@ -134,6 +134,7 @@ connect_global(bulk, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/nand2_split_gate.lvs b/testdata/lvs/nand2_split_gate.lvs index 1c4a795bf..ca4ce42f6 100644 --- a/testdata/lvs/nand2_split_gate.lvs +++ b/testdata/lvs/nand2_split_gate.lvs @@ -76,6 +76,7 @@ connect_global(ptie, "SUBSTRATE") netlist join_symmetric_nets("*") +netlist.make_top_level_pins(false) netlist.simplify # Compare section diff --git a/testdata/lvs/nand2_split_gate_early.lvs b/testdata/lvs/nand2_split_gate_early.lvs index bf48ae960..e128d1693 100644 --- a/testdata/lvs/nand2_split_gate_early.lvs +++ b/testdata/lvs/nand2_split_gate_early.lvs @@ -77,6 +77,7 @@ connect_global(ptie, "SUBSTRATE") # Extract, simplify netlist +netlist.make_top_level_pins(false) netlist.simplify # Compare section diff --git a/testdata/lvs/res_combine1.lvs b/testdata/lvs/res_combine1.lvs index 3dba3b639..db30261d1 100644 --- a/testdata/lvs/res_combine1.lvs +++ b/testdata/lvs/res_combine1.lvs @@ -93,6 +93,7 @@ schematic.simplify # Netlist vs. netlist align +netlist.make_top_level_pins(false) netlist.simplify no_lvs_hints compare diff --git a/testdata/lvs/res_combine2.lvs b/testdata/lvs/res_combine2.lvs index 395f84765..012d2b4cf 100644 --- a/testdata/lvs/res_combine2.lvs +++ b/testdata/lvs/res_combine2.lvs @@ -92,6 +92,7 @@ schematic.simplify # Netlist vs. netlist align +netlist.make_top_level_pins(false) netlist.simplify no_lvs_hints compare diff --git a/testdata/lvs/res_combine3.lvs b/testdata/lvs/res_combine3.lvs index c609c0824..0aabff3ab 100644 --- a/testdata/lvs/res_combine3.lvs +++ b/testdata/lvs/res_combine3.lvs @@ -87,6 +87,7 @@ schematic.simplify # Netlist vs. netlist align +netlist.make_top_level_pins(false) netlist.simplify no_lvs_hints compare diff --git a/testdata/lvs/ringo_device_subcircuits.lvs b/testdata/lvs/ringo_device_subcircuits.lvs index f12f62d8d..8a9f32ecb 100644 --- a/testdata/lvs/ringo_device_subcircuits.lvs +++ b/testdata/lvs/ringo_device_subcircuits.lvs @@ -120,6 +120,7 @@ same_device_classes("PMOS", $change_case ? "xpMos" : "XPMOS") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_layout_var.lvs b/testdata/lvs/ringo_layout_var.lvs index afe79c3e2..ca04cf5fd 100644 --- a/testdata/lvs/ringo_layout_var.lvs +++ b/testdata/lvs/ringo_layout_var.lvs @@ -72,6 +72,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_mixed_hierarchy.lvs b/testdata/lvs/ringo_mixed_hierarchy.lvs index cba9caa86..c1149c265 100644 --- a/testdata/lvs/ringo_mixed_hierarchy.lvs +++ b/testdata/lvs/ringo_mixed_hierarchy.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/ringo_simple.lvs b/testdata/lvs/ringo_simple.lvs index 057f39c25..44b535f51 100644 --- a/testdata/lvs/ringo_simple.lvs +++ b/testdata/lvs/ringo_simple.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_blackboxing.lvs b/testdata/lvs/ringo_simple_blackboxing.lvs index 2fe02fd83..e464f5c00 100644 --- a/testdata/lvs/ringo_simple_blackboxing.lvs +++ b/testdata/lvs/ringo_simple_blackboxing.lvs @@ -78,7 +78,7 @@ schematic.blank_circuit("INVX1") schematic.blank_circuit("INVX2") schematic.blank_circuit("ND2X1") -netlist.make_top_level_pins +netlist.make_top_level_pins(false) netlist.purge netlist.combine_devices netlist.purge_nets diff --git a/testdata/lvs/ringo_simple_blackboxing_netter.lvs b/testdata/lvs/ringo_simple_blackboxing_netter.lvs index 4cbbc0591..55f4d4fd5 100644 --- a/testdata/lvs/ringo_simple_blackboxing_netter.lvs +++ b/testdata/lvs/ringo_simple_blackboxing_netter.lvs @@ -76,7 +76,7 @@ connect_global(ptie, "SUBSTRATE") netlist.flatten_circuit("INVCHAIN") -netlist.make_top_level_pins +netlist.make_top_level_pins(false) netlist.purge netlist.combine_devices netlist.purge_nets diff --git a/testdata/lvs/ringo_simple_compare2.lvs b/testdata/lvs/ringo_simple_compare2.lvs index abaeaff92..1b56db53f 100644 --- a/testdata/lvs/ringo_simple_compare2.lvs +++ b/testdata/lvs/ringo_simple_compare2.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_device_scaling.lvs b/testdata/lvs/ringo_simple_device_scaling.lvs index afb3caebb..e248bcd12 100644 --- a/testdata/lvs/ringo_simple_device_scaling.lvs +++ b/testdata/lvs/ringo_simple_device_scaling.lvs @@ -70,6 +70,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_dmos.lvs b/testdata/lvs/ringo_simple_dmos.lvs index 0879f5e1b..1af75b266 100644 --- a/testdata/lvs/ringo_simple_dmos.lvs +++ b/testdata/lvs/ringo_simple_dmos.lvs @@ -75,6 +75,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_dmos_fixed.lvs b/testdata/lvs/ringo_simple_dmos_fixed.lvs index 5f375e95f..279ee200e 100644 --- a/testdata/lvs/ringo_simple_dmos_fixed.lvs +++ b/testdata/lvs/ringo_simple_dmos_fixed.lvs @@ -77,6 +77,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_dummy_device.lvs b/testdata/lvs/ringo_simple_dummy_device.lvs index a33340a2d..9c1219cd4 100644 --- a/testdata/lvs/ringo_simple_dummy_device.lvs +++ b/testdata/lvs/ringo_simple_dummy_device.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvs b/testdata/lvs/ringo_simple_implicit_connections.lvs index 056cf97d8..4e583d46c 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvs +++ b/testdata/lvs/ringo_simple_implicit_connections.lvs @@ -72,6 +72,7 @@ connect_implicit("VDD") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_io.lvs b/testdata/lvs/ringo_simple_io.lvs index 215b556ce..92a30d54b 100644 --- a/testdata/lvs/ringo_simple_io.lvs +++ b/testdata/lvs/ringo_simple_io.lvs @@ -69,6 +69,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_io2.lvs b/testdata/lvs/ringo_simple_io2.lvs index bea93496f..8042382aa 100644 --- a/testdata/lvs/ringo_simple_io2.lvs +++ b/testdata/lvs/ringo_simple_io2.lvs @@ -102,6 +102,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs index 02724522a..0e8e2d201 100644 --- a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs +++ b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs @@ -76,6 +76,7 @@ same_circuits("INV", $change_case ? "invX1" : "INVX1") same_circuits("DOESNOTEXIST", "DOESNOTEXIST2") same_nets("DOESNOTEXIST", "ENABLE", "DOESNOTEXIST2", "ENABLE") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_pin_swapping.lvs b/testdata/lvs/ringo_simple_pin_swapping.lvs index 166fa320f..4b9fd5a04 100644 --- a/testdata/lvs/ringo_simple_pin_swapping.lvs +++ b/testdata/lvs/ringo_simple_pin_swapping.lvs @@ -73,6 +73,7 @@ connect_global(ptie, "SUBSTRATE") equivalent_pins("DOESNOTEXIST", 4, 5) +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_same_device_classes.lvs b/testdata/lvs/ringo_simple_same_device_classes.lvs index 8005d70cc..b2d0c4053 100644 --- a/testdata/lvs/ringo_simple_same_device_classes.lvs +++ b/testdata/lvs/ringo_simple_same_device_classes.lvs @@ -91,6 +91,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify same_device_classes("NM", "NMOS") diff --git a/testdata/lvs/ringo_simple_simplification.lvs b/testdata/lvs/ringo_simple_simplification.lvs index 9579e11cd..1833b7a87 100644 --- a/testdata/lvs/ringo_simple_simplification.lvs +++ b/testdata/lvs/ringo_simple_simplification.lvs @@ -70,7 +70,7 @@ connect_global(ptie, "SUBSTRATE") netlist.flatten_circuit("INVCHAIN") -netlist.make_top_level_pins +netlist.make_top_level_pins(false) netlist.purge netlist.combine_devices netlist.purge_nets diff --git a/testdata/lvs/ringo_simple_simplification_with_align.lvs b/testdata/lvs/ringo_simple_simplification_with_align.lvs index 4591cf3e6..73ae01353 100644 --- a/testdata/lvs/ringo_simple_simplification_with_align.lvs +++ b/testdata/lvs/ringo_simple_simplification_with_align.lvs @@ -70,6 +70,7 @@ connect_global(ptie, "SUBSTRATE") align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_with_tol.lvs b/testdata/lvs/ringo_simple_with_tol.lvs index 5b2f6dcbd..0d234e6b9 100644 --- a/testdata/lvs/ringo_simple_with_tol.lvs +++ b/testdata/lvs/ringo_simple_with_tol.lvs @@ -73,6 +73,7 @@ tolerance("PMOS", "W", 0.01, 0.1) # relative + absolute tolerance("NMOS", "L", :absolute => 0.01) tolerance("NMOS", "W", :relative => 0.07) +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_with_tol_early.lvs b/testdata/lvs/ringo_simple_with_tol_early.lvs index 7f8fcef72..25b79e53a 100644 --- a/testdata/lvs/ringo_simple_with_tol_early.lvs +++ b/testdata/lvs/ringo_simple_with_tol_early.lvs @@ -73,6 +73,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/soft_connect1.lvs b/testdata/lvs/soft_connect1.lvs index a4ed6551d..b41b177b8 100644 --- a/testdata/lvs/soft_connect1.lvs +++ b/testdata/lvs/soft_connect1.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect1a.lvs b/testdata/lvs/soft_connect1a.lvs index 4063000dc..9b490f4fe 100644 --- a/testdata/lvs/soft_connect1a.lvs +++ b/testdata/lvs/soft_connect1a.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect2.lvs b/testdata/lvs/soft_connect2.lvs index a4ed6551d..b41b177b8 100644 --- a/testdata/lvs/soft_connect2.lvs +++ b/testdata/lvs/soft_connect2.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect3.lvs b/testdata/lvs/soft_connect3.lvs index a4ed6551d..b41b177b8 100644 --- a/testdata/lvs/soft_connect3.lvs +++ b/testdata/lvs/soft_connect3.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect4.lvs b/testdata/lvs/soft_connect4.lvs index fcf0d3d22..ca1a4fa64 100644 --- a/testdata/lvs/soft_connect4.lvs +++ b/testdata/lvs/soft_connect4.lvs @@ -88,6 +88,7 @@ soft_connect_global(ptie, "SUBSTRATE") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect5.lvs b/testdata/lvs/soft_connect5.lvs index fcf0d3d22..ca1a4fa64 100644 --- a/testdata/lvs/soft_connect5.lvs +++ b/testdata/lvs/soft_connect5.lvs @@ -88,6 +88,7 @@ soft_connect_global(ptie, "SUBSTRATE") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect6.lvs b/testdata/lvs/soft_connect6.lvs index fcf0d3d22..ca1a4fa64 100644 --- a/testdata/lvs/soft_connect6.lvs +++ b/testdata/lvs/soft_connect6.lvs @@ -88,6 +88,7 @@ soft_connect_global(ptie, "SUBSTRATE") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/split_substrate.l2n.1 b/testdata/lvs/split_substrate.l2n.1 new file mode 100644 index 000000000..0fe18254e --- /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/lvs/split_substrate.lvs b/testdata/lvs/split_substrate.lvs index 43204067d..2bf709152 100644 --- a/testdata/lvs/split_substrate.lvs +++ b/testdata/lvs/split_substrate.lvs @@ -102,6 +102,7 @@ connect_global(iosub, "IOSUB") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/python/dbEdgeNeighborhoodWithNets.py b/testdata/python/dbEdgeNeighborhoodWithNets.py index ddd4a37a4..758ec0b75 100644 --- a/testdata/python/dbEdgeNeighborhoodWithNets.py +++ b/testdata/python/dbEdgeNeighborhoodWithNets.py @@ -147,160 +147,160 @@ class DBEdgeNeighborhoodWithNets(unittest.TestCase): self.maxDiff = None self.assertEqual("\n" + visitor.dump(), """ -Polygon: (-14000,0;-14000,15000;-11000,15000;-11000,0) props={net=>net1} - Edge: (-11000,0;-14000,0) props={net=>net1} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - Edge: (-11000,15000;-11000,0) props={net=>net1} - 0.0,500.0 -> 0: (0,-11;0,0;500,0;500,-11) props={net=>net1} - 0.0,500.0 -> 1: (0,1500;0,6001;500,6001;500,1500) props={net=>net1} - 0.0,500.0 -> 3: (0,-11;0,4500;500,4500;500,-11) props={net=>net1} - 500.0,2500.0 -> 0: (500,-11;500,0;2500,0;2500,-11) props={net=>net1} - 500.0,2500.0 -> 1: (500,1500;500,6001;2500,6001;2500,1500) props={net=>net1} - 500.0,2500.0 -> 2: (500,2000;500,4000;2500,4000;2500,2000) props={net=>net1} - 500.0,2500.0 -> 3: (500,-11;500,4500;2500,4500;2500,-11) props={net=>net1} - 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1} - 2500.0,3000.0 -> 1: (2500,1500;2500,6001;3000,6001;3000,1500) props={net=>net1} - 2500.0,3000.0 -> 3: (2500,-11;2500,4500;3000,4500;3000,-11) props={net=>net1} - 3000.0,8500.0 -> 0: (3000,-11;3000,0;8500,0;8500,-11) props={net=>net1} - 3000.0,8500.0 -> 1: (3000,1500;3000,4500;8500,4500;8500,1500) props={net=>net1}/(3000,6000;3000,6001;8500,6001;8500,6000) props={net=>net1} - 3000.0,8500.0 -> 3: (3000,-11;3000,0;8500,0;8500,-11) props={net=>net1} - 8500.0,15000.0 -> 0: (8500,-11;8500,0;15000,0;15000,-11) props={net=>net1} - 8500.0,15000.0 -> 1: (8500,1500;8500,4500;15000,4500;15000,1500) props={net=>net1}/(8500,6000;8500,6001;15000,6001;15000,6000) props={net=>net1} - Edge: (-14000,0;-14000,15000) props={net=>net1} - 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={net=>net1} - 6500.0,15000.0 -> 0: (6500,-11;6500,0;15000,0;15000,-11) props={net=>net1} - 6500.0,15000.0 -> 3: (6500,-11;6500,0;15000,0;15000,-11) props={net=>net1} - Edge: (-14000,15000;-11000,15000) props={net=>net1} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 0.0,3000.0 -> 1: (0,1500;0,5000;3000,5000;3000,1500) props={net=>net3} - 0.0,3000.0 -> 3: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 3000.0,3001.0 -> 1: (3000,1500;3000,5000;3001,5000;3001,1500) props={net=>net3} - 3000.0,3001.0 -> 3: (3000,-11;3000,0;3001,0;3001,-11) props={net=>net1} -Polygon: (-14000,16500;-14000,20000;0,20000;0,16500) props={net=>net3} - Edge: (-14000,16500;-14000,20000) props={net=>net3} - 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={net=>net3} - Edge: (-14000,20000;0,20000) props={net=>net3} - 0.0,7000.0 -> 0: (0,-11;0,0;7000,0;7000,-11) props={net=>net3} - 7000.0,14000.0 -> 0: (7000,-11;7000,0;14000,0;14000,-11) props={net=>net3} - 7000.0,14000.0 -> 3: (7000,-11;7000,0;14000,0;14000,-11) props={net=>net2} - 14000.0,14001.0 -> 3: (14000,-11;14000,0;14001,0;14001,-11) props={net=>net2} - Edge: (0,16500;-14000,16500) props={net=>net3} - -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={net=>net2} - 0.0,5000.0 -> 0: (0,-11;0,0;5000,0;5000,-11) props={net=>net3} - 0.0,5000.0 -> 1: (0,1500;0,6001;5000,6001;5000,1500) props={net=>net1} - 0.0,5000.0 -> 3: (0,-11;0,0;5000,0;5000,-11) props={net=>net2} - 5000.0,6500.0 -> 0: (5000,-11;5000,0;6500,0;6500,-11) props={net=>net3} - 5000.0,6500.0 -> 1: (5000,1500;5000,4500;6500,4500;6500,1500) props={net=>net1} - 5000.0,6500.0 -> 3: (5000,-11;5000,0;6500,0;6500,-11) props={net=>net2} - 6500.0,7000.0 -> 0: (6500,-11;6500,0;7000,0;7000,-11) props={net=>net3} - 6500.0,7000.0 -> 1: (6500,1500;6500,6001;7000,6001;7000,1500) props={net=>net1} - 6500.0,7000.0 -> 3: (6500,-11;6500,0;7000,0;7000,-11) props={net=>net2}/(6500,1500;6500,4500;7000,4500;7000,1500) props={net=>net1} - 7000.0,9000.0 -> 0: (7000,-11;7000,0;9000,0;9000,-11) props={net=>net3} - 7000.0,9000.0 -> 1: (7000,1500;7000,6001;9000,6001;9000,1500) props={net=>net1} - 7000.0,9000.0 -> 2: (7000,2000;7000,4000;9000,4000;9000,2000) props={net=>net1} - 7000.0,9000.0 -> 3: (7000,1500;7000,4500;9000,4500;9000,1500) props={net=>net1} - 9000.0,9500.0 -> 0: (9000,-11;9000,0;9500,0;9500,-11) props={net=>net3} - 9000.0,9500.0 -> 1: (9000,1500;9000,6001;9500,6001;9500,1500) props={net=>net1} - 9000.0,9500.0 -> 3: (9000,1500;9000,4500;9500,4500;9500,1500) props={net=>net1} - 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={net=>net3} - 9500.0,11000.0 -> 3: (9500,1500;9500,4500;11000,4500;11000,1500) props={net=>net1} - 11000.0,11500.0 -> 0: (11000,-11;11000,0;11500,0;11500,-11) props={net=>net3} - 11000.0,11500.0 -> 1: (11000,1500;11000,6001;11500,6001;11500,1500) props={net=>net1} - 11000.0,11500.0 -> 3: (11000,1500;11000,6001;11500,6001;11500,1500) props={net=>net1} - 11500.0,13500.0 -> 0: (11500,-11;11500,0;13500,0;13500,-11) props={net=>net3} - 11500.0,13500.0 -> 1: (11500,1500;11500,6001;13500,6001;13500,1500) props={net=>net1} - 11500.0,13500.0 -> 2: (11500,2000;11500,4000;13500,4000;13500,2000) props={net=>net1} - 11500.0,13500.0 -> 3: (11500,1500;11500,6001;13500,6001;13500,1500) props={net=>net1} - 13500.0,14000.0 -> 0: (13500,-11;13500,0;14000,0;14000,-11) props={net=>net3} - 13500.0,14000.0 -> 1: (13500,1500;13500,6001;14000,6001;14000,1500) props={net=>net1} - 13500.0,14000.0 -> 3: (13500,1500;13500,6001;14000,6001;14000,1500) props={net=>net1} - Edge: (0,20000;0,16500) props={net=>net3} - 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={net=>net3} - 0.0,3500.0 -> 3: (0,-11;0,4500;3500,4500;3500,-11) props={net=>net2} - 3500.0,3501.0 -> 3: (3500,1500;3500,4500;3501,4500;3501,1500) props={net=>net2} -Polygon: (-9500,-5500;-9500,-1500;5000,-1500;5000,-5500) props={net=>net2} - Edge: (-9500,-1500;5000,-1500) props={net=>net2} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net2} - 0.0,3000.0 -> 1: (0,1500;0,6001;3000,6001;3000,1500) props={net=>net1} - 3000.0,4500.0 -> 0: (3000,-11;3000,0;4500,0;4500,-11) props={net=>net2} - 4500.0,9500.0 -> 0: (4500,-11;4500,0;9500,0;9500,-11) props={net=>net2} - 4500.0,9500.0 -> 1: (4500,1500;4500,6001;9500,6001;9500,1500) props={net=>net1} - 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={net=>net2} - 11000.0,14000.0 -> 0: (11000,-11;11000,0;14000,0;14000,-11) props={net=>net2} - 11000.0,14000.0 -> 3: (11000,-11;11000,6001;14000,6001;14000,-11) props={net=>net2} - 14000.0,14500.0 -> 0: (14000,-11;14000,0;14500,0;14500,-11) props={net=>net2} - Edge: (-9500,-5500;-9500,-1500) props={net=>net2} - 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={net=>net2} - Edge: (5000,-1500;5000,-5500) props={net=>net2} - 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={net=>net2} - Edge: (5000,-5500;-9500,-5500) props={net=>net2} - 0.0,14500.0 -> 0: (0,-11;0,0;14500,0;14500,-11) props={net=>net2} -Polygon: (-9500,0;-9500,15000;0,15000;0,0;-5000,0;-5000,12000;-6500,12000;-6500,0) props={net=>net1} - Edge: (-5000,0;-5000,12000) props={net=>net1} - 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={net=>net1}/(0,1500;0,4500;6500,4500;6500,1500) props={net=>net1} - 0.0,6500.0 -> 1: (0,6000;0,6001;6500,6001;6500,6000) props={net=>net1} - 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={net=>net1}/(6500,1500;6500,4500;12000,4500;12000,1500) props={net=>net1} - 6500.0,12000.0 -> 1: (6500,6000;6500,6001;12000,6001;12000,6000) props={net=>net1} - 6500.0,12000.0 -> 3: (6500,6000;6500,6001;12000,6001;12000,6000) props={net=>net1} - 12000.0,12001.0 -> 0: (12000,-11;12000,4500;12001,4500;12001,-11) props={net=>net1} - 12000.0,12001.0 -> 1: (12000,6000;12000,6001;12001,6001;12001,6000) props={net=>net1} - 12000.0,12001.0 -> 3: (12000,1500;12000,6001;12001,6001;12001,1500) props={net=>net1} - Edge: (-5000,12000;-6500,12000) props={net=>net1} - -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={net=>net1} - 0.0,1500.0 -> 0: (0,-11;0,0;1500,0;1500,-11) props={net=>net1} - 1500.0,1501.0 -> 0: (1500,-11;1500,6001;1501,6001;1501,-11) props={net=>net1} - 1500.0,1501.0 -> 3: (1500,-11;1500,0;1501,0;1501,-11) props={net=>net1} - Edge: (-6500,0;-9500,0) props={net=>net1} - -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={net=>net2} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={net=>net2} - Edge: (-6500,12000;-6500,0) props={net=>net1} - -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={net=>net1} - -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={net=>net1} - 0.0,12000.0 -> 0: (0,-11;0,0;12000,0;12000,-11) props={net=>net1}/(0,1500;0,6001;12000,6001;12000,1500) props={net=>net1} - Edge: (-9500,0;-9500,15000) props={net=>net1} - 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={net=>net1} - 0.0,6500.0 -> 1: (0,1500;0,4500;6500,4500;6500,1500) props={net=>net1} - 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={net=>net1} - 6500.0,12000.0 -> 1: (6500,1500;6500,4500;12000,4500;12000,1500) props={net=>net1} - 6500.0,12000.0 -> 3: (6500,1500;6500,4500;12000,4500;12000,1500) props={net=>net1} - 12000.0,12500.0 -> 0: (12000,-11;12000,0;12500,0;12500,-11) props={net=>net1} - 12000.0,12500.0 -> 1: (12000,1500;12000,4500;12500,4500;12500,1500) props={net=>net1} - 12000.0,12500.0 -> 3: (12000,-11;12000,4500;12500,4500;12500,-11) props={net=>net1} - 12500.0,14500.0 -> 0: (12500,-11;12500,0;14500,0;14500,-11) props={net=>net1} - 12500.0,14500.0 -> 1: (12500,1500;12500,4500;14500,4500;14500,1500) props={net=>net1} - 12500.0,14500.0 -> 2: (12500,2000;12500,4000;14500,4000;14500,2000) props={net=>net1} - 12500.0,14500.0 -> 3: (12500,-11;12500,4500;14500,4500;14500,-11) props={net=>net1} - 14500.0,15000.0 -> 0: (14500,-11;14500,0;15000,0;15000,-11) props={net=>net1} - 14500.0,15000.0 -> 1: (14500,1500;14500,4500;15000,4500;15000,1500) props={net=>net1} - 14500.0,15000.0 -> 3: (14500,-11;14500,4500;15000,4500;15000,-11) props={net=>net1} - Edge: (-9500,15000;0,15000) props={net=>net1} - -1.0,0.0 -> 1: (-1,1500;-1,5000;0,5000;0,1500) props={net=>net3} - -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={net=>net1} - 0.0,2500.0 -> 0: (0,-11;0,0;2500,0;2500,-11) props={net=>net1} - 0.0,2500.0 -> 1: (0,1500;0,5000;2500,5000;2500,1500) props={net=>net3} - 0.0,2500.0 -> 3: (0,-11;0,0;2500,0;2500,-11) props={net=>net1} - 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1} - 2500.0,3000.0 -> 1: (2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net3} - 2500.0,3000.0 -> 3: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1}/(2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net2} - 3000.0,9500.0 -> 0: (3000,-11;3000,0;9500,0;9500,-11) props={net=>net1} - 3000.0,9500.0 -> 1: (3000,1500;3000,5000;9500,5000;9500,1500) props={net=>net3} - 3000.0,9500.0 -> 3: (3000,1500;3000,5000;9500,5000;9500,1500) props={net=>net2} - 9500.0,9501.0 -> 3: (9500,1500;9500,5000;9501,5000;9501,1500) props={net=>net2} - Edge: (0,0;-5000,0) props={net=>net1} - -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={net=>net2} - -1.0,0.0 -> 3: (-1,2000;-1,5000;0,5000;0,2000) props={net=>net2} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={net=>net2} - 0.0,3000.0 -> 3: (0,2000;0,5000;3000,5000;3000,2000) props={net=>net2} - 3000.0,5000.0 -> 0: (3000,-11;3000,0;5000,0;5000,-11) props={net=>net1} - 3000.0,5000.0 -> 1: (3000,1500;3000,5500;5000,5500;5000,1500) props={net=>net2} - 5000.0,5001.0 -> 1: (5000,1500;5000,5500;5001,5500;5001,1500) props={net=>net2} - Edge: (0,15000;0,0) props={net=>net1} - -1.0,0.0 -> 3: (-1,1500;-1,4500;0,4500;0,1500) props={net=>net2} - 0.0,15000.0 -> 0: (0,-11;0,0;15000,0;15000,-11) props={net=>net1} - 0.0,15000.0 -> 3: (0,1500;0,4500;15000,4500;15000,1500) props={net=>net2} - 15000.0,15001.0 -> 3: (15000,1500;15000,4500;15001,4500;15001,1500) props={net=>net2} +Polygon: (-14000,0;-14000,15000;-11000,15000;-11000,0) props={'net'=>'net1'} + Edge: (-11000,0;-14000,0) props={'net'=>'net1'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + Edge: (-11000,15000;-11000,0) props={'net'=>'net1'} + 0.0,500.0 -> 0: (0,-11;0,0;500,0;500,-11) props={'net'=>'net1'} + 0.0,500.0 -> 1: (0,1500;0,6001;500,6001;500,1500) props={'net'=>'net1'} + 0.0,500.0 -> 3: (0,-11;0,4500;500,4500;500,-11) props={'net'=>'net1'} + 500.0,2500.0 -> 0: (500,-11;500,0;2500,0;2500,-11) props={'net'=>'net1'} + 500.0,2500.0 -> 1: (500,1500;500,6001;2500,6001;2500,1500) props={'net'=>'net1'} + 500.0,2500.0 -> 2: (500,2000;500,4000;2500,4000;2500,2000) props={'net'=>'net1'} + 500.0,2500.0 -> 3: (500,-11;500,4500;2500,4500;2500,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 1: (2500,1500;2500,6001;3000,6001;3000,1500) props={'net'=>'net1'} + 2500.0,3000.0 -> 3: (2500,-11;2500,4500;3000,4500;3000,-11) props={'net'=>'net1'} + 3000.0,8500.0 -> 0: (3000,-11;3000,0;8500,0;8500,-11) props={'net'=>'net1'} + 3000.0,8500.0 -> 1: (3000,1500;3000,4500;8500,4500;8500,1500) props={'net'=>'net1'}/(3000,6000;3000,6001;8500,6001;8500,6000) props={'net'=>'net1'} + 3000.0,8500.0 -> 3: (3000,-11;3000,0;8500,0;8500,-11) props={'net'=>'net1'} + 8500.0,15000.0 -> 0: (8500,-11;8500,0;15000,0;15000,-11) props={'net'=>'net1'} + 8500.0,15000.0 -> 1: (8500,1500;8500,4500;15000,4500;15000,1500) props={'net'=>'net1'}/(8500,6000;8500,6001;15000,6001;15000,6000) props={'net'=>'net1'} + Edge: (-14000,0;-14000,15000) props={'net'=>'net1'} + 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={'net'=>'net1'} + 6500.0,15000.0 -> 0: (6500,-11;6500,0;15000,0;15000,-11) props={'net'=>'net1'} + 6500.0,15000.0 -> 3: (6500,-11;6500,0;15000,0;15000,-11) props={'net'=>'net1'} + Edge: (-14000,15000;-11000,15000) props={'net'=>'net1'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 0.0,3000.0 -> 1: (0,1500;0,5000;3000,5000;3000,1500) props={'net'=>'net3'} + 0.0,3000.0 -> 3: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 3000.0,3001.0 -> 1: (3000,1500;3000,5000;3001,5000;3001,1500) props={'net'=>'net3'} + 3000.0,3001.0 -> 3: (3000,-11;3000,0;3001,0;3001,-11) props={'net'=>'net1'} +Polygon: (-14000,16500;-14000,20000;0,20000;0,16500) props={'net'=>'net3'} + Edge: (-14000,16500;-14000,20000) props={'net'=>'net3'} + 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={'net'=>'net3'} + Edge: (-14000,20000;0,20000) props={'net'=>'net3'} + 0.0,7000.0 -> 0: (0,-11;0,0;7000,0;7000,-11) props={'net'=>'net3'} + 7000.0,14000.0 -> 0: (7000,-11;7000,0;14000,0;14000,-11) props={'net'=>'net3'} + 7000.0,14000.0 -> 3: (7000,-11;7000,0;14000,0;14000,-11) props={'net'=>'net2'} + 14000.0,14001.0 -> 3: (14000,-11;14000,0;14001,0;14001,-11) props={'net'=>'net2'} + Edge: (0,16500;-14000,16500) props={'net'=>'net3'} + -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={'net'=>'net2'} + 0.0,5000.0 -> 0: (0,-11;0,0;5000,0;5000,-11) props={'net'=>'net3'} + 0.0,5000.0 -> 1: (0,1500;0,6001;5000,6001;5000,1500) props={'net'=>'net1'} + 0.0,5000.0 -> 3: (0,-11;0,0;5000,0;5000,-11) props={'net'=>'net2'} + 5000.0,6500.0 -> 0: (5000,-11;5000,0;6500,0;6500,-11) props={'net'=>'net3'} + 5000.0,6500.0 -> 1: (5000,1500;5000,4500;6500,4500;6500,1500) props={'net'=>'net1'} + 5000.0,6500.0 -> 3: (5000,-11;5000,0;6500,0;6500,-11) props={'net'=>'net2'} + 6500.0,7000.0 -> 0: (6500,-11;6500,0;7000,0;7000,-11) props={'net'=>'net3'} + 6500.0,7000.0 -> 1: (6500,1500;6500,6001;7000,6001;7000,1500) props={'net'=>'net1'} + 6500.0,7000.0 -> 3: (6500,-11;6500,0;7000,0;7000,-11) props={'net'=>'net2'}/(6500,1500;6500,4500;7000,4500;7000,1500) props={'net'=>'net1'} + 7000.0,9000.0 -> 0: (7000,-11;7000,0;9000,0;9000,-11) props={'net'=>'net3'} + 7000.0,9000.0 -> 1: (7000,1500;7000,6001;9000,6001;9000,1500) props={'net'=>'net1'} + 7000.0,9000.0 -> 2: (7000,2000;7000,4000;9000,4000;9000,2000) props={'net'=>'net1'} + 7000.0,9000.0 -> 3: (7000,1500;7000,4500;9000,4500;9000,1500) props={'net'=>'net1'} + 9000.0,9500.0 -> 0: (9000,-11;9000,0;9500,0;9500,-11) props={'net'=>'net3'} + 9000.0,9500.0 -> 1: (9000,1500;9000,6001;9500,6001;9500,1500) props={'net'=>'net1'} + 9000.0,9500.0 -> 3: (9000,1500;9000,4500;9500,4500;9500,1500) props={'net'=>'net1'} + 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={'net'=>'net3'} + 9500.0,11000.0 -> 3: (9500,1500;9500,4500;11000,4500;11000,1500) props={'net'=>'net1'} + 11000.0,11500.0 -> 0: (11000,-11;11000,0;11500,0;11500,-11) props={'net'=>'net3'} + 11000.0,11500.0 -> 1: (11000,1500;11000,6001;11500,6001;11500,1500) props={'net'=>'net1'} + 11000.0,11500.0 -> 3: (11000,1500;11000,6001;11500,6001;11500,1500) props={'net'=>'net1'} + 11500.0,13500.0 -> 0: (11500,-11;11500,0;13500,0;13500,-11) props={'net'=>'net3'} + 11500.0,13500.0 -> 1: (11500,1500;11500,6001;13500,6001;13500,1500) props={'net'=>'net1'} + 11500.0,13500.0 -> 2: (11500,2000;11500,4000;13500,4000;13500,2000) props={'net'=>'net1'} + 11500.0,13500.0 -> 3: (11500,1500;11500,6001;13500,6001;13500,1500) props={'net'=>'net1'} + 13500.0,14000.0 -> 0: (13500,-11;13500,0;14000,0;14000,-11) props={'net'=>'net3'} + 13500.0,14000.0 -> 1: (13500,1500;13500,6001;14000,6001;14000,1500) props={'net'=>'net1'} + 13500.0,14000.0 -> 3: (13500,1500;13500,6001;14000,6001;14000,1500) props={'net'=>'net1'} + Edge: (0,20000;0,16500) props={'net'=>'net3'} + 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={'net'=>'net3'} + 0.0,3500.0 -> 3: (0,-11;0,4500;3500,4500;3500,-11) props={'net'=>'net2'} + 3500.0,3501.0 -> 3: (3500,1500;3500,4500;3501,4500;3501,1500) props={'net'=>'net2'} +Polygon: (-9500,-5500;-9500,-1500;5000,-1500;5000,-5500) props={'net'=>'net2'} + Edge: (-9500,-1500;5000,-1500) props={'net'=>'net2'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net2'} + 0.0,3000.0 -> 1: (0,1500;0,6001;3000,6001;3000,1500) props={'net'=>'net1'} + 3000.0,4500.0 -> 0: (3000,-11;3000,0;4500,0;4500,-11) props={'net'=>'net2'} + 4500.0,9500.0 -> 0: (4500,-11;4500,0;9500,0;9500,-11) props={'net'=>'net2'} + 4500.0,9500.0 -> 1: (4500,1500;4500,6001;9500,6001;9500,1500) props={'net'=>'net1'} + 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={'net'=>'net2'} + 11000.0,14000.0 -> 0: (11000,-11;11000,0;14000,0;14000,-11) props={'net'=>'net2'} + 11000.0,14000.0 -> 3: (11000,-11;11000,6001;14000,6001;14000,-11) props={'net'=>'net2'} + 14000.0,14500.0 -> 0: (14000,-11;14000,0;14500,0;14500,-11) props={'net'=>'net2'} + Edge: (-9500,-5500;-9500,-1500) props={'net'=>'net2'} + 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={'net'=>'net2'} + Edge: (5000,-1500;5000,-5500) props={'net'=>'net2'} + 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={'net'=>'net2'} + Edge: (5000,-5500;-9500,-5500) props={'net'=>'net2'} + 0.0,14500.0 -> 0: (0,-11;0,0;14500,0;14500,-11) props={'net'=>'net2'} +Polygon: (-9500,0;-9500,15000;0,15000;0,0;-5000,0;-5000,12000;-6500,12000;-6500,0) props={'net'=>'net1'} + Edge: (-5000,0;-5000,12000) props={'net'=>'net1'} + 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={'net'=>'net1'}/(0,1500;0,4500;6500,4500;6500,1500) props={'net'=>'net1'} + 0.0,6500.0 -> 1: (0,6000;0,6001;6500,6001;6500,6000) props={'net'=>'net1'} + 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={'net'=>'net1'}/(6500,1500;6500,4500;12000,4500;12000,1500) props={'net'=>'net1'} + 6500.0,12000.0 -> 1: (6500,6000;6500,6001;12000,6001;12000,6000) props={'net'=>'net1'} + 6500.0,12000.0 -> 3: (6500,6000;6500,6001;12000,6001;12000,6000) props={'net'=>'net1'} + 12000.0,12001.0 -> 0: (12000,-11;12000,4500;12001,4500;12001,-11) props={'net'=>'net1'} + 12000.0,12001.0 -> 1: (12000,6000;12000,6001;12001,6001;12001,6000) props={'net'=>'net1'} + 12000.0,12001.0 -> 3: (12000,1500;12000,6001;12001,6001;12001,1500) props={'net'=>'net1'} + Edge: (-5000,12000;-6500,12000) props={'net'=>'net1'} + -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={'net'=>'net1'} + 0.0,1500.0 -> 0: (0,-11;0,0;1500,0;1500,-11) props={'net'=>'net1'} + 1500.0,1501.0 -> 0: (1500,-11;1500,6001;1501,6001;1501,-11) props={'net'=>'net1'} + 1500.0,1501.0 -> 3: (1500,-11;1500,0;1501,0;1501,-11) props={'net'=>'net1'} + Edge: (-6500,0;-9500,0) props={'net'=>'net1'} + -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={'net'=>'net2'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={'net'=>'net2'} + Edge: (-6500,12000;-6500,0) props={'net'=>'net1'} + -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={'net'=>'net1'} + -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={'net'=>'net1'} + 0.0,12000.0 -> 0: (0,-11;0,0;12000,0;12000,-11) props={'net'=>'net1'}/(0,1500;0,6001;12000,6001;12000,1500) props={'net'=>'net1'} + Edge: (-9500,0;-9500,15000) props={'net'=>'net1'} + 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={'net'=>'net1'} + 0.0,6500.0 -> 1: (0,1500;0,4500;6500,4500;6500,1500) props={'net'=>'net1'} + 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={'net'=>'net1'} + 6500.0,12000.0 -> 1: (6500,1500;6500,4500;12000,4500;12000,1500) props={'net'=>'net1'} + 6500.0,12000.0 -> 3: (6500,1500;6500,4500;12000,4500;12000,1500) props={'net'=>'net1'} + 12000.0,12500.0 -> 0: (12000,-11;12000,0;12500,0;12500,-11) props={'net'=>'net1'} + 12000.0,12500.0 -> 1: (12000,1500;12000,4500;12500,4500;12500,1500) props={'net'=>'net1'} + 12000.0,12500.0 -> 3: (12000,-11;12000,4500;12500,4500;12500,-11) props={'net'=>'net1'} + 12500.0,14500.0 -> 0: (12500,-11;12500,0;14500,0;14500,-11) props={'net'=>'net1'} + 12500.0,14500.0 -> 1: (12500,1500;12500,4500;14500,4500;14500,1500) props={'net'=>'net1'} + 12500.0,14500.0 -> 2: (12500,2000;12500,4000;14500,4000;14500,2000) props={'net'=>'net1'} + 12500.0,14500.0 -> 3: (12500,-11;12500,4500;14500,4500;14500,-11) props={'net'=>'net1'} + 14500.0,15000.0 -> 0: (14500,-11;14500,0;15000,0;15000,-11) props={'net'=>'net1'} + 14500.0,15000.0 -> 1: (14500,1500;14500,4500;15000,4500;15000,1500) props={'net'=>'net1'} + 14500.0,15000.0 -> 3: (14500,-11;14500,4500;15000,4500;15000,-11) props={'net'=>'net1'} + Edge: (-9500,15000;0,15000) props={'net'=>'net1'} + -1.0,0.0 -> 1: (-1,1500;-1,5000;0,5000;0,1500) props={'net'=>'net3'} + -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={'net'=>'net1'} + 0.0,2500.0 -> 0: (0,-11;0,0;2500,0;2500,-11) props={'net'=>'net1'} + 0.0,2500.0 -> 1: (0,1500;0,5000;2500,5000;2500,1500) props={'net'=>'net3'} + 0.0,2500.0 -> 3: (0,-11;0,0;2500,0;2500,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 1: (2500,1500;2500,5000;3000,5000;3000,1500) props={'net'=>'net3'} + 2500.0,3000.0 -> 3: (2500,-11;2500,0;3000,0;3000,-11) props={'net'=>'net1'}/(2500,1500;2500,5000;3000,5000;3000,1500) props={'net'=>'net2'} + 3000.0,9500.0 -> 0: (3000,-11;3000,0;9500,0;9500,-11) props={'net'=>'net1'} + 3000.0,9500.0 -> 1: (3000,1500;3000,5000;9500,5000;9500,1500) props={'net'=>'net3'} + 3000.0,9500.0 -> 3: (3000,1500;3000,5000;9500,5000;9500,1500) props={'net'=>'net2'} + 9500.0,9501.0 -> 3: (9500,1500;9500,5000;9501,5000;9501,1500) props={'net'=>'net2'} + Edge: (0,0;-5000,0) props={'net'=>'net1'} + -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={'net'=>'net2'} + -1.0,0.0 -> 3: (-1,2000;-1,5000;0,5000;0,2000) props={'net'=>'net2'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={'net'=>'net2'} + 0.0,3000.0 -> 3: (0,2000;0,5000;3000,5000;3000,2000) props={'net'=>'net2'} + 3000.0,5000.0 -> 0: (3000,-11;3000,0;5000,0;5000,-11) props={'net'=>'net1'} + 3000.0,5000.0 -> 1: (3000,1500;3000,5500;5000,5500;5000,1500) props={'net'=>'net2'} + 5000.0,5001.0 -> 1: (5000,1500;5000,5500;5001,5500;5001,1500) props={'net'=>'net2'} + Edge: (0,15000;0,0) props={'net'=>'net1'} + -1.0,0.0 -> 3: (-1,1500;-1,4500;0,4500;0,1500) props={'net'=>'net2'} + 0.0,15000.0 -> 0: (0,-11;0,0;15000,0;15000,-11) props={'net'=>'net1'} + 0.0,15000.0 -> 3: (0,1500;0,4500;15000,4500;15000,1500) props={'net'=>'net2'} + 15000.0,15001.0 -> 3: (15000,1500;15000,4500;15001,4500;15001,1500) props={'net'=>'net2'} """) diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 537c8c047..3a4e023a2 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -41,6 +41,7 @@ class DBBox_TestClass < TestBase a = RBA::DBox::new( -10, 21, 11, 17 ) assert_equal( a.to_s, "(-10,17;11,21)" ) assert_equal( RBA::DBox::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DBox::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*0.5).to_s, "(-5,8.5;5.5,10.5)" ) b = a @@ -263,6 +264,7 @@ class DBBox_TestClass < TestBase a = RBA::Box::new( -10, 21, 11, 17 ) assert_equal( a.to_s, "(-10,17;11,21)" ) assert_equal( RBA::Box::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Box::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*2).to_s, "(-20,34;22,42)" ) b = a @@ -518,16 +520,16 @@ class DBBox_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) @@ -536,20 +538,40 @@ class DBBox_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) + # binary serialization + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::BoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::BoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DBoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DBoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + + # string serialization + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::BoxWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::BoxWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DBoxWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DBoxWithProperties::from_s(s.to_s).to_s, s.to_s) + end end diff --git a/testdata/ruby/dbEdgeNeighborhood.rb b/testdata/ruby/dbEdgeNeighborhood.rb index 33255bdfc..da05a0b08 100644 --- a/testdata/ruby/dbEdgeNeighborhood.rb +++ b/testdata/ruby/dbEdgeNeighborhood.rb @@ -123,15 +123,15 @@ class DBEdgeNeighborhood_TestClass < TestBase "edge = (-1100,0;-1100,1000) props={}\n" + "edge = (-1100,1000;-100,1000) props={}\n" + "edge = (-100,1000;-100,0) props={}\n" + - " 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100) props={1=>one}\n" + + " 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100) props={#1=>'one'}\n" + "edge = (-100,0;-1100,0) props={}\n" + "/Polygon\n" + - "Polygon: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + - "edge = (0,0;0,1000) props={1=>one}\n" + + "Polygon: (0,0;0,1000;1000,1000;1000,0) props={#1=>'one'}\n" + + "edge = (0,0;0,1000) props={#1=>'one'}\n" + " 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100) props={}\n" + - "edge = (0,1000;1000,1000) props={1=>one}\n" + - "edge = (1000,1000;1000,0) props={1=>one}\n" + - "edge = (1000,0;0,0) props={1=>one}\n" + + "edge = (0,1000;1000,1000) props={#1=>'one'}\n" + + "edge = (1000,1000;1000,0) props={#1=>'one'}\n" + + "edge = (1000,0;0,0) props={#1=>'one'}\n" + "/Polygon\n" ) diff --git a/testdata/ruby/dbEdgePairTest.rb b/testdata/ruby/dbEdgePairTest.rb index 2a02a05ad..71ee62828 100644 --- a/testdata/ruby/dbEdgePairTest.rb +++ b/testdata/ruby/dbEdgePairTest.rb @@ -40,6 +40,9 @@ class DBEdgePair_TestClass < TestBase assert_equal(ep.to_s, "(0,0;10,20)/(-10,0;-10,30)") assert_equal(ep.bbox.to_s, "(-10,0;10,30)") + assert_equal(RBA::EdgePair::from_s(ep.to_s).to_s, ep.to_s) + assert_equal(RBA::EdgePair::from_bytes(ep.to_bytes).to_s, ep.to_s) + assert_equal(RBA::EdgePair::new(ep.first, ep.second).to_s, "(0,0;10,20)/(-10,0;-10,30)") ep2 = RBA::EdgePair::new @@ -91,6 +94,9 @@ class DBEdgePair_TestClass < TestBase assert_equal(ep.to_s, "(0,0;10,20)/(-10,0;-10,30)") assert_equal(ep.bbox.to_s, "(-10,0;10,30)") + assert_equal(RBA::DEdgePair::from_s(ep.to_s).to_s, ep.to_s) + assert_equal(RBA::DEdgePair::from_bytes(ep.to_bytes).to_s, ep.to_s) + assert_equal(RBA::DEdgePair::new(ep.first, ep.second).to_s, "(0,0;10,20)/(-10,0;-10,30)") ep2 = RBA::DEdgePair::new @@ -252,16 +258,16 @@ class DBEdgePair_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0)/(0,0;0,0) props={}") s = RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(10, 10, 110, 210)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(10, 10, 110, 210)), pid) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2)/(0.01,0.01;0.11,0.21) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2)/(0.01,0.01;0.11,0.21) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={}") assert_equal(s.property(1), nil) @@ -270,16 +276,16 @@ class DBEdgePair_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0)/(0,0;0,0) props={}") s = RBA::DEdgePairWithProperties::new(RBA::DEdgePair::new(RBA::DEdge::new(0, 0, 100, 200), RBA::DEdge::new(10, 10, 110, 210)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DEdgePairWithProperties::new(RBA::DEdgePair::new(RBA::DEdge::new(0, 0, 100, 200), RBA::DEdge::new(10, 10, 110, 210)), pid) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500)/(25,25;275,525) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500)/(25,25;275,525) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 36dd8f40e..53e1b8a65 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -622,7 +622,7 @@ class DBEdgePairs_TestClass < TestBase r = RBA::EdgePairs::new([ RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") - assert_equal(r[0].to_s, "(0,0;100,100)/(200,300;200,500) props={1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100)/(200,300;200,500) props={#1=>'one'}") r = RBA::EdgePairs::new([]) assert_equal(r.to_s, "") @@ -638,7 +638,7 @@ class DBEdgePairs_TestClass < TestBase r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) r.insert(RBA::EdgePair::new(RBA::Edge::new(0, 10, 100, 110), RBA::Edge::new(220, 300, 220, 500))) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "(0,10;100,110)/(220,300;220,500) props={};(0,0;100,100)/(200,300;200,500) props={1=>one}") + assert_equal(s, "(0,10;100,110)/(220,300;220,500) props={};(0,0;100,100)/(200,300;200,500) props={#1=>'one'}") end diff --git a/testdata/ruby/dbEdgeTest.rb b/testdata/ruby/dbEdgeTest.rb index 09623dce3..f229bac1a 100644 --- a/testdata/ruby/dbEdgeTest.rb +++ b/testdata/ruby/dbEdgeTest.rb @@ -36,6 +36,7 @@ class DBEdge_TestClass < TestBase a = RBA::DEdge::new( -1, 2, 15, -7 ) assert_equal( a.to_s, "(-1,2;15,-7)" ) assert_equal( RBA::DEdge::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DEdge::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*0.5).to_s, "(-0.5,1;7.5,-3.5)" ) d = RBA::Edge::new( a ) assert_equal( d.to_s, "(-1,2;15,-7)" ) @@ -156,6 +157,7 @@ class DBEdge_TestClass < TestBase a = RBA::Edge::new( RBA::Point::new( -1, 2 ), RBA::Point::new( 15, -7 ) ) assert_equal( a.to_s, "(-1,2;15,-7)" ) assert_equal( RBA::Edge::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Edge::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*2).to_s, "(-2,4;30,-14)" ) d = RBA::DEdge::new( a ) assert_equal( d.to_s, "(-1,2;15,-7)" ) @@ -339,31 +341,31 @@ class DBEdge_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0) props={}") s = RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.transformed(RBA::CplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.transformed(RBA::ICplxTrans::new(2.5)).to_s, "(0,0;250,500) props={1=>one}") - assert_equal(s.transformed(RBA::Trans::R90).to_s, "(0,0;-200,100) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.transformed(RBA::CplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.transformed(RBA::ICplxTrans::new(2.5)).to_s, "(0,0;250,500) props={#1=>'one'}") + assert_equal(s.transformed(RBA::Trans::R90).to_s, "(0,0;-200,100) props={#1=>'one'}") s2 = s.dup s2.transform(RBA::Trans::R90) - assert_equal(s2.to_s, "(0,0;-200,100) props={1=>one}") - assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={1=>one}") - assert_equal(s.moved(RBA::Vector::new(10, 20)).to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(0,0;-200,100) props={#1=>'one'}") + assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={#1=>'one'}") + assert_equal(s.moved(RBA::Vector::new(10, 20)).to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(10, 20) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(RBA::Vector::new(10, 20)) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) @@ -372,31 +374,31 @@ class DBEdge_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0) props={}") s = RBA::DEdgeWithProperties::new(RBA::DEdge::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DEdgeWithProperties::new(RBA::DEdge::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={1=>one}") - assert_equal(s.transformed(RBA::DCplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.transformed(RBA::VCplxTrans::new(2.5)).to_s, "(0,0;250,500) props={1=>one}") - assert_equal(s.transformed(RBA::DTrans::R90).to_s, "(0,0;-200,100) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={#1=>'one'}") + assert_equal(s.transformed(RBA::DCplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.transformed(RBA::VCplxTrans::new(2.5)).to_s, "(0,0;250,500) props={#1=>'one'}") + assert_equal(s.transformed(RBA::DTrans::R90).to_s, "(0,0;-200,100) props={#1=>'one'}") s2 = s.dup s2.transform(RBA::DTrans::R90) - assert_equal(s2.to_s, "(0,0;-200,100) props={1=>one}") - assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={1=>one}") - assert_equal(s.moved(RBA::DVector::new(10, 20)).to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(0,0;-200,100) props={#1=>'one'}") + assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={#1=>'one'}") + assert_equal(s.moved(RBA::DVector::new(10, 20)).to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(10, 20) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(RBA::DVector::new(10, 20)) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 47ccf01df..50112c5ce 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -970,7 +970,7 @@ class DBEdges_TestClass < TestBase r = RBA::Edges::new([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100){1=>one}") - assert_equal(r[0].to_s, "(0,0;100,100) props={1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100) props={#1=>'one'}") r = RBA::Edges::new([]) assert_equal(r.to_s, "") @@ -1040,15 +1040,15 @@ class DBEdges_TestClass < TestBase r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 0), { 1 => "one" })) r.insert(RBA::Edge::new(10, 0, 110, 0)) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={1=>one}") + assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={#1=>'one'}") s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={1=>one}") + assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={#1=>'one'}") r = RBA::Edges::new r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 0), { 1 => "one" })) r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(10, 0, 110, 0), { 1 => "one" })) s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(0,0;110,0) props={1=>one}") + assert_equal(s, "(0,0;110,0) props={#1=>'one'}") end diff --git a/testdata/ruby/dbLayoutTests1.rb b/testdata/ruby/dbLayoutTests1.rb index ecf2fa19b..97c903762 100644 --- a/testdata/ruby/dbLayoutTests1.rb +++ b/testdata/ruby/dbLayoutTests1.rb @@ -335,34 +335,21 @@ class DBLayoutTests1_TestClass < TestBase 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) + l = ll + ll = l.dup - 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=)"); - - 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 @@ class DBLayoutTests1_TestClass < TestBase 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 @@ class DBLayoutTests1_TestClass < TestBase 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 @@ class DBLayoutTests1_TestClass < TestBase c0_index = c0.cell_index c2_index = c2.cell_index + c3_index = c3.cell_index ll = l.dup @@ -499,6 +504,42 @@ class DBLayoutTests1_TestClass < TestBase 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 @@ class DBLayoutTests1_TestClass < TestBase 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 @@ class DBLayoutTests1_TestClass < TestBase 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 @@ class DBLayoutTests1_TestClass < TestBase 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 @@ class DBLayoutTests1_TestClass < TestBase 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 6ba044e78..864b82ead 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -277,7 +277,7 @@ end class DBPCellAPI_TestClass < TestBase - def _test_1 + def test_1 # PCellParameterDeclaration @@ -351,7 +351,7 @@ end class DBPCell_TestClass < TestBase - def _test_1 + def test_1 # instantiate and register the library tl = PCellTestLib::new @@ -593,7 +593,7 @@ class DBPCell_TestClass < TestBase end - def _test_2 + def test_2 # instantiate and register the library tl = PCellTestLib::new @@ -632,7 +632,7 @@ class DBPCell_TestClass < TestBase end - def _test_3 + def test_3 # instantiate and register the library tl = PCellTestLib::new @@ -657,7 +657,7 @@ class DBPCell_TestClass < TestBase end - def _test_4 + def test_4 # instantiate and register the library tl = PCellTestLib::new @@ -674,7 +674,7 @@ class DBPCell_TestClass < TestBase end - def _test_5 + def test_5 # instantiate and register the library tl = PCellTestLib::new @@ -691,7 +691,7 @@ class DBPCell_TestClass < TestBase end - def _test_6 + def test_6 # instantiate and register the library tl = PCellTestLib::new @@ -707,7 +707,7 @@ class DBPCell_TestClass < TestBase end - def _test_7 + def test_7 # instantiate and register the library tl = PCellTestLib::new @@ -725,7 +725,7 @@ class DBPCell_TestClass < TestBase end - def _test_8 + def test_8 # instantiate and register the library tl = PCellTestLib::new @@ -747,7 +747,7 @@ class DBPCell_TestClass < TestBase end - def _test_9 + def test_9 layout = RBA::Layout::new @@ -813,7 +813,7 @@ class DBPCell_TestClass < TestBase end - def _test_10 + def test_10 lib = CircleLib1782::new("CircleLib") @@ -864,7 +864,7 @@ class DBPCell_TestClass < TestBase end - def _test_11 + def test_11 lib = CircleLib1782::new("CircleLib") @@ -899,7 +899,7 @@ class DBPCell_TestClass < TestBase end - def _test_12 + def test_12 if !RBA.constants.member?(:PCellDeclarationHelper) return @@ -924,7 +924,7 @@ class DBPCell_TestClass < TestBase end # convert to static cell - def _test_13 + def test_13 if !RBA.constants.member?(:PCellDeclarationHelper) return @@ -954,11 +954,69 @@ class DBPCell_TestClass < TestBase 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/testdata/ruby/dbPathTest.rb b/testdata/ruby/dbPathTest.rb index 199d808b2..6d66860a8 100644 --- a/testdata/ruby/dbPathTest.rb +++ b/testdata/ruby/dbPathTest.rb @@ -33,6 +33,7 @@ class DBPath_TestClass < TestBase assert_equal( a.area.to_s, "0.0" ) assert_equal( a.length.to_s, "0.0" ) assert_equal( RBA::DPath::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPath::from_bytes(a.to_bytes).to_s, a.to_s ) b = a.dup a = RBA::DPath::new( [ RBA::DPoint::new( 0, 1 ), RBA::DPoint::new( 1, 5 ) ], 2.5 ) @@ -40,6 +41,7 @@ class DBPath_TestClass < TestBase assert_equal( "%.3f" % a.area, "10.308" ) assert_equal( "%.3f" % a.length, "4.123" ) assert_equal( RBA::DPath::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPath::from_bytes(a.to_bytes).to_s, a.to_s ) c = a.dup assert_equal( a == b, false ) @@ -55,6 +57,7 @@ class DBPath_TestClass < TestBase a = RBA::DPath::new( [ RBA::DPoint::new( 0, 1 ), RBA::DPoint::new( 1, 5 ) ], 2.5, -0.5, 1.5 ) assert_equal( a.to_s, "(0,1;1,5) w=2.5 bx=-0.5 ex=1.5 r=false" ) assert_equal( RBA::DPath::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPath::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( "%.3f" % a.length, "5.123" ) assert_equal( RBA::Path::new(a).to_s, "(0,1;1,5) w=3 bx=-1 ex=2 r=false" ) @@ -122,6 +125,7 @@ class DBPath_TestClass < TestBase assert_equal( a.area.to_f.to_s, "0.0" ) assert_equal( a.length.to_s, "0" ) assert_equal( RBA::Path::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Path::from_bytes(a.to_bytes).to_s, a.to_s ) b = a.dup a = RBA::Path::new( [ RBA::Point::new( 0, 10 ), RBA::Point::new( 10, 50 ) ], 25 ) @@ -129,6 +133,7 @@ class DBPath_TestClass < TestBase assert_equal( a.area.to_f.to_s, "1025.0" ) assert_equal( a.length.to_s, "41" ) assert_equal( RBA::Path::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Path::from_bytes(a.to_bytes).to_s, a.to_s ) c = a.dup assert_equal( a == b, false ) @@ -144,6 +149,7 @@ class DBPath_TestClass < TestBase assert_equal( a.to_s, "(0,10;10,50) w=25 bx=-5 ex=15 r=false" ) assert_equal( a.length.to_s, "51" ) assert_equal( RBA::Path::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Path::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( RBA::DPath::new(a).to_s, "(0,10;10,50) w=25 bx=-5 ex=15 r=false" ) a.bgn_ext = 5 @@ -335,16 +341,16 @@ class DBPath_TestClass < TestBase assert_equal(s.to_s, "() w=0 bx=0 ex=0 r=false props={}") s = RBA::PathWithProperties::new(RBA::Path::new([ [0,0], [100, 0] ], 100), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::PathWithProperties::new(RBA::Path::new([ [0,0], [100, 0] ], 100), pid) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0) w=0.1 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0) w=0.1 bx=0 ex=0 r=false props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={}") assert_equal(s.property(1), nil) @@ -353,16 +359,16 @@ class DBPath_TestClass < TestBase assert_equal(s.to_s, "() w=0 bx=0 ex=0 r=false props={}") s = RBA::DPathWithProperties::new(RBA::DPath::new([ [0,0], [100, 0] ], 100), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DPathWithProperties::new(RBA::DPath::new([ [0,0], [100, 0] ], 100), pid) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,0) w=250 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,0) w=250 bx=0 ex=0 r=false props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbPointTest.rb b/testdata/ruby/dbPointTest.rb index 2aeea8a61..030475706 100644 --- a/testdata/ruby/dbPointTest.rb +++ b/testdata/ruby/dbPointTest.rb @@ -34,6 +34,7 @@ class DBPoint_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::DPoint::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPoint::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) @@ -84,6 +85,7 @@ class DBPoint_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::Point::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Point::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) diff --git a/testdata/ruby/dbPolygonNeighborhood.rb b/testdata/ruby/dbPolygonNeighborhood.rb index 27f944f7c..527a60544 100644 --- a/testdata/ruby/dbPolygonNeighborhood.rb +++ b/testdata/ruby/dbPolygonNeighborhood.rb @@ -122,8 +122,8 @@ class DBPolygonNeighborhood_TestClass < TestBase assert_equal(visitor.log, "Polygon: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" + - " 0: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + - "Polygon: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + + " 0: (0,0;0,1000;1000,1000;1000,0) props={#1=>'one'}\n" + + "Polygon: (0,0;0,1000;1000,1000;1000,0) props={#1=>'one'}\n" + " 0: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" ) diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index e3ed29a83..14e6f1983 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -31,6 +31,7 @@ class DBPolygon_TestClass < TestBase a = RBA::DPolygon::new assert_equal( a.to_s, "()" ) assert_equal( RBA::DPolygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPolygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.is_box?, false ) assert_equal( a.is_empty?, true ) assert_equal( a.is_rectilinear?, false ) @@ -41,6 +42,7 @@ class DBPolygon_TestClass < TestBase assert_equal( a.to_s, "(0,1;1,5;5,5)" ) assert_equal( (a * 2).to_s, "(0,2;2,10;10,10)" ) assert_equal( RBA::DPolygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPolygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.is_box?, false ) assert_equal( a.num_points_hull, 3 ) assert_equal( a.is_empty?, false ) @@ -101,6 +103,7 @@ class DBPolygon_TestClass < TestBase a.insert_hole( [ RBA::DPoint::new( 1, 2 ), RBA::DPoint::new( 2, 2 ), RBA::DPoint::new( 2, 6 ) ] ) assert_equal( a.to_s, "(0,1;1,5;1,1/1,2;2,2;2,6)" ) assert_equal( RBA::DPolygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPolygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.area, 0 ) assert_equal( a.num_points_hole(0), 3 ) assert_equal( a.holes, 1 ) @@ -172,6 +175,7 @@ class DBPolygon_TestClass < TestBase a = RBA::Polygon::new assert_equal( a.to_s, "()" ) assert_equal( RBA::Polygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Polygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.is_box?, false ) b = a.dup @@ -179,6 +183,7 @@ class DBPolygon_TestClass < TestBase assert_equal( a.to_s, "(0,1;1,5;5,5)" ) assert_equal( (a * 2).to_s, "(0,2;2,10;10,10)" ) assert_equal( RBA::Polygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Polygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.num_points_hull, 3 ) c = a.dup @@ -226,6 +231,7 @@ class DBPolygon_TestClass < TestBase a.insert_hole( [ RBA::Point::new( 1, 2 ), RBA::Point::new( 2, 2 ), RBA::Point::new( 2, 6 ) ] ) assert_equal( a.to_s, "(0,1;1,5;1,1/1,2;2,2;2,6)" ) assert_equal( RBA::Polygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Polygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.area, 0 ) assert_equal( a.num_points_hole(0), 3 ) assert_equal( a.holes, 1 ) @@ -900,16 +906,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -922,16 +928,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::DPolygonWithProperties::new(RBA::DPolygon::new(RBA::DBox::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DPolygonWithProperties::new(RBA::DPolygon::new(RBA::DBox::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -944,16 +950,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::SimplePolygonWithProperties::new(RBA::SimplePolygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::SimplePolygonWithProperties::new(RBA::SimplePolygon::new(RBA::Box::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -966,16 +972,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::DSimplePolygonWithProperties::new(RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DSimplePolygonWithProperties::new(RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -984,6 +990,42 @@ class DBPolygon_TestClass < TestBase assert_equal(s.class.to_s, "RBA::DSimplePolygonWithProperties") assert_equal(s.downcast.class.to_s, "RBA::DSimplePolygon") + # binary serialization + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::PolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::PolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DPolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DPolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::SimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::SimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DSimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DSimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + + # string serialization + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::PolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::PolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DPolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DPolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::SimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::SimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DSimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DSimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + end def test_triangulation diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 432429257..9a76b89db 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -235,7 +235,7 @@ class DBRegion_TestClass < TestBase r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) r.insert(RBA::Box::new(1, 2, 11, 22)) assert_equal(r[0].to_s, "(1,2;1,22;11,22;11,2) props={}") - assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") + assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={#1=>'value'}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") assert_equal(csort(r.to_s), csort("(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)")) @@ -1635,20 +1635,20 @@ class DBRegion_TestClass < TestBase r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) r.insert(RBA::Box::new(10, 20, 110, 220)) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={#1=>'one'}") rr = r.dup rr.join_properties_on_merge = true assert_equal(rr.join_properties_on_merge, true) s = rr.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={1=>one}") + assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={#1=>'one'}") s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={#1=>'one'}") r = RBA::Region::new r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) r.insert(RBA::BoxWithProperties::new(RBA::Box::new(10, 20, 110, 220), { 1 => "one" })) s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={1=>one}") + assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={#1=>'one'}") end diff --git a/testdata/ruby/dbTextTest.rb b/testdata/ruby/dbTextTest.rb index ac4ca0c38..a253ab67c 100644 --- a/testdata/ruby/dbTextTest.rb +++ b/testdata/ruby/dbTextTest.rb @@ -30,10 +30,12 @@ class DBText_TestClass < TestBase a = RBA::DText::new( "hallo", 10.0, -15.0 ) assert_equal( RBA::DText::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DText::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.to_s, "('hallo',r0 10,-15)" ) a = RBA::DText::new( RBA::Text::new( "itext", RBA::Trans::new( RBA::Trans::R270, RBA::Point::new( 100, -150 )))) assert_equal( RBA::DText::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DText::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.to_s, "('itext',r270 100,-150)" ) a = RBA::DText::new @@ -124,6 +126,7 @@ class DBText_TestClass < TestBase a = RBA::Text::new( "hallo", 10, -15 ) assert_equal( RBA::Text::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Text::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.to_s, "('hallo',r0 10,-15)" ) a = RBA::Text::new( RBA::DText::new( "dtext", RBA::DTrans::new( RBA::DTrans::R270, RBA::DPoint::new( 100.0, -150.0 )))) @@ -136,6 +139,7 @@ class DBText_TestClass < TestBase a = RBA::Text::new( "hallo", RBA::Trans::new( RBA::Trans::R90, RBA::Point::new( 10, -15 ))) assert_equal( a.to_s, "('hallo',r90 10,-15)" ) assert_equal( RBA::Text::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Text::from_bytes(a.to_bytes).to_s, a.to_s ) c = a.dup assert_equal( a == b, false ) @@ -260,16 +264,16 @@ class DBText_TestClass < TestBase assert_equal(s.to_s, "('',r0 0,0) props={}") s = RBA::TextWithProperties::new(RBA::Text::new("text", RBA::Trans::R90), { 1 => "one" }) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::TextWithProperties::new(RBA::Text::new("text", RBA::Trans::R90), pid) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "('text',r90 0,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "('text',r90 0,0) props={1=>xxx}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "('text',r90 0,0) props={}") assert_equal(s.property(1), nil) @@ -278,16 +282,16 @@ class DBText_TestClass < TestBase assert_equal(s.to_s, "('',r0 0,0) props={}") s = RBA::DTextWithProperties::new(RBA::DText::new("text", RBA::Trans::R90), { 1 => "one" }) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DTextWithProperties::new(RBA::DText::new("text", RBA::Trans::R90), pid) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "('text',r90 0,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "('text',r90 0,0) props={1=>xxx}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "('text',r90 0,0) props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 9c5eb2064..5a9336656 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -229,7 +229,7 @@ class DBTexts_TestClass < TestBase r = RBA::Texts::new r.insert(RBA::TextWithProperties::new(RBA::Text::new("string", RBA::Trans::new), { 1 => "value" })) - assert_equal(r[0].to_s, "('string',r0 0,0) props={1=>value}") + assert_equal(r[0].to_s, "('string',r0 0,0) props={#1=>'value'}") dss = RBA::DeepShapeStore::new r = RBA::Texts::new(ly.begin_shapes(c1.cell_index, l1), dss) @@ -477,7 +477,7 @@ class DBTexts_TestClass < TestBase r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) r.insert(RBA::Text::new("xuv", RBA::Trans::new)) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "('xuv',r0 0,0) props={};('abc',r0 0,0) props={1=>one}") + assert_equal(s, "('xuv',r0 0,0) props={};('abc',r0 0,0) props={#1=>'one'}") end diff --git a/testdata/ruby/dbTransTest.rb b/testdata/ruby/dbTransTest.rb index 71bc4d780..8385ddd9b 100644 --- a/testdata/ruby/dbTransTest.rb +++ b/testdata/ruby/dbTransTest.rb @@ -38,6 +38,7 @@ class DBTrans_TestClass < TestBase assert_equal( a.to_s, "r0 0,0" ) assert_equal( RBA::DTrans::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DTrans::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( b.to_s, "m135 17,5" ) assert_equal( c.to_s, "m135 17,5" ) assert_equal( d.to_s, "r0 17,5" ) @@ -47,6 +48,7 @@ class DBTrans_TestClass < TestBase assert_equal( RBA::DTrans::new( RBA::Trans::M135, RBA::DPoint::new( 1.2, 0.25 )).to_itype(0.001).to_s, "m135 1200,250" ) assert_equal( RBA::Trans::new( RBA::Trans::M135, RBA::Point::new( 1200, 250 )).to_dtype(0.001).to_s, "m135 1.2,0.25" ) assert_equal( RBA::DTrans::from_s(f.to_s).to_s, f.to_s ) + assert_equal( RBA::DTrans::from_bytes(f.to_bytes).to_s, f.to_s ) assert_equal( b.trans( RBA::DPoint::new( 1, 0 )).to_s, "17,4" ) @@ -139,6 +141,7 @@ class DBTrans_TestClass < TestBase assert_equal( i.to_s, "m135 *0.5 2.5,8.5" ) assert_equal( RBA::DCplxTrans::from_s(i.to_s).to_s, i.to_s ) + assert_equal( RBA::DCplxTrans::from_bytes(i.to_bytes).to_s, i.to_s ) assert_equal( i * mb == u, true ) assert_equal( mb * i == u, true ) @@ -244,6 +247,7 @@ class DBTrans_TestClass < TestBase assert_equal( a.to_s, "r0 0,0" ) assert_equal( RBA::Trans::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Trans::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( b.to_s, "m135 17,5" ) assert_equal( c.to_s, "m135 17,5" ) assert_equal( d.to_s, "r0 17,5" ) @@ -251,6 +255,7 @@ class DBTrans_TestClass < TestBase assert_equal( e2.to_s, "m135 0,0" ) assert_equal( f.to_s, "m135 17,5" ) assert_equal( RBA::Trans::from_s(f.to_s).to_s, f.to_s ) + assert_equal( RBA::Trans::from_bytes(f.to_bytes).to_s, f.to_s ) assert_equal( b.trans( RBA::Point::new( 1, 0 )).to_s, "17,4" ) @@ -340,6 +345,7 @@ class DBTrans_TestClass < TestBase c = RBA::CplxTrans::new( 5, -7 ) assert_equal( c.to_s, "r0 *1 5,-7" ) assert_equal( RBA::CplxTrans::from_s(c.to_s).to_s, c.to_s ) + assert_equal( RBA::CplxTrans::from_bytes(c.to_bytes).to_s, c.to_s ) c = RBA::CplxTrans::new( RBA::CplxTrans::M135 ) assert_equal( c.to_s, "m135 *1 0,0" ) @@ -367,6 +373,7 @@ class DBTrans_TestClass < TestBase c = RBA::CplxTrans::new( 0.75, 45, true, 2.5, -12.5 ) assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" ) assert_equal( RBA::CplxTrans::from_s(c.to_s).to_s, c.to_s ) + assert_equal( RBA::CplxTrans::from_bytes(c.to_bytes).to_s, c.to_s ) c = RBA::CplxTrans::new( 0.75, 45, true, RBA::DPoint::new( 2.5, -12.5 ) ) assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" ) assert_equal( c.is_unity?, false ) @@ -436,6 +443,7 @@ class DBTrans_TestClass < TestBase assert_equal( m.to_s, "r0 *1.1 0,0" ) assert_equal( RBA::DCplxTrans::from_s(m.to_s).to_s, m.to_s ) + assert_equal( RBA::DCplxTrans::from_bytes(m.to_bytes).to_s, m.to_s ) assert_equal( m.trans( RBA::Point::new( 5, -7 )).to_s, "5.5,-7.7" ) im = RBA::ICplxTrans::new( a, 0.5 ) @@ -443,6 +451,7 @@ class DBTrans_TestClass < TestBase assert_equal( im.to_s, "r0 *0.5 0,0" ) assert_equal( RBA::ICplxTrans::from_s(im.to_s).to_s, im.to_s ) + assert_equal( RBA::ICplxTrans::from_bytes(im.to_bytes).to_s, im.to_s ) assert_equal( im.trans( RBA::Point::new( 5, -7 )).to_s, "3,-4" ) im = RBA::ICplxTrans::new(m) diff --git a/testdata/ruby/dbVectorTest.rb b/testdata/ruby/dbVectorTest.rb index d279cb2bc..34bce3627 100644 --- a/testdata/ruby/dbVectorTest.rb +++ b/testdata/ruby/dbVectorTest.rb @@ -34,6 +34,7 @@ class DBVector_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::DVector::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DVector::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) @@ -88,6 +89,7 @@ class DBVector_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::Vector::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Vector::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) diff --git a/version.sh b/version.sh index c7782a9d7..664c35ca2 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")