Refactoring

This commit is contained in:
Matthias Koefferlein 2026-05-30 22:53:49 +02:00
commit cb96b8c169
173 changed files with 9488 additions and 3852 deletions

View File

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

View File

@ -1,3 +1,10 @@
klayout (0.30.9-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Mon, 25 May 2026 19:46:05 +0200
klayout (0.30.8-1) unstable; urgency=low
* New features and bugfixes

View File

@ -594,12 +594,12 @@
</widget>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="t_snap_cbx">
<widget class="QCheckBox" name="t_never_snap_cbx">
<property name="toolTip">
<string>If checked, snap to edges or vertices of objects unless disabled above</string>
</property>
<property name="text">
<string>Snap to objects</string>
<string>Never snap to objects</string>
</property>
</widget>
</item>
@ -893,7 +893,7 @@
<tabstop>style_cb</tabstop>
<tabstop>outline_cb</tabstop>
<tabstop>t_angle_cb</tabstop>
<tabstop>t_snap_cbx</tabstop>
<tabstop>t_never_snap_cbx</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>

242
src/ant/ant/RulerOptions.ui Normal file
View File

@ -0,0 +1,242 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RulerOptions</class>
<widget class="QWidget" name="RulerOptions">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>446</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>446</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Snapping</string>
</property>
<layout class="QGridLayout" name="_5">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="snap_to_grid_cbx">
<property name="text">
<string>Snap to grid</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="snap_to_objects_cbx">
<property name="text">
<string>Snap to edge/vertex</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Angle Constraints</string>
</property>
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="angle_cb">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Any Angle</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal only</string>
</property>
</item>
<item>
<property name="text">
<string>Orthogonal</string>
</property>
</item>
<item>
<property name="text">
<string>Horizontal only</string>
</property>
</item>
<item>
<property name="text">
<string>Vertical only</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Ruler direction</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Note: certain rulers may ignore these options</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>121</width>
<height>70</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>snap_to_grid_cbx</tabstop>
<tabstop>snap_to_objects_cbx</tabstop>
<tabstop>angle_cb</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

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

View File

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

View File

@ -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<std::string>
PluginDeclaration::additional_editor_options_pages () const
PluginDeclaration::additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const
{
std::vector<std::string> 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;
}

View File

@ -54,7 +54,7 @@ public:
virtual void uninitialize (lay::Dispatcher *);
virtual bool menu_activated (const std::string &symbol) const;
virtual std::vector<std::string> additional_editor_options_pages () const;
virtual std::vector<std::string> 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);

View File

@ -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<lay::angle_constraint_type> 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<lay::EditorOptionsPageFactoryBase> s_factory_ruler_options (new lay::EditorOptionsPageFactory<RulerOptionsPage> ("ant::RulerOptions"), 0);
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -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<class C>
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<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::point<C> &pt)
{
return write_coord (write_coord (s, pt.x ()), pt.y ());
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::vector<C> &v)
{
return write_coord (write_coord (s, v.x ()), v.y ());
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::point<C> &pt)
{
s << (uint16_t) 1; // version
return write_binary (s, pt);
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::vector<C> &v)
{
s << (uint16_t) 1; // version
return write_binary (s, v);
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::edge<C> &e)
{
write_binary (s, e.p1 ());
write_binary (s, e.p2 ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge<C> &e)
{
s << (uint16_t) 1; // version
return write_binary (s, e);
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::box<C> &b)
{
s << (uint16_t) 1; // version
write_binary (s, b.p1 ());
write_binary (s, b.p2 ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge_pair<C> &ep)
{
s << (uint16_t) 1; // version
write_binary (s, ep.first ());
write_binary (s, ep.second ());
return s;
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::polygon_contour<C> &c)
{
s << (uint64_t) c.size ();
for (auto i = c.begin (); i != c.end (); ++i) {
write_binary (s, *i);
}
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::polygon<C> &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<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_polygon<C> &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<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::unit_trans<C> &)
{
s << (uint16_t) 1; // version
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::disp_trans<C> &t)
{
s << (uint16_t) 1; // version
write_binary (s, t.disp ());
return s;
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::simple_trans<C> &t)
{
s << (uint16_t) t.rot ();
write_binary (s, t.disp ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_trans<C> &t)
{
s << (uint16_t) 1; // version
return write_binary (s, t);
}
template<class C, class D, class R>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::complex_trans<C, D, R> &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<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::text<C> &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<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::path<C> &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<class T>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::object_with_properties<T> &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<class C>
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<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::point<C> &pt)
{
C x = 0, y = 0;
read_coord (s, x);
read_coord (s, y);
pt = db::point<C> (x, y);
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::vector<C> &v)
{
C x = 0, y = 0;
read_coord (s, x);
read_coord (s, y);
v = db::vector<C> (x, y);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::point<C> &pt)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, pt);
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::vector<C> &v)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, v);
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::edge<C> &v)
{
db::point<C> p1, p2;
read_binary (s, p1);
read_binary (s, p2);
v = db::edge<C> (p1, p2);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge<C> &e)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, e);
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::box<C> &b)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::point<C> p1, p2;
read_binary (s, p1);
read_binary (s, p2);
b = db::box<C> (p1, p2);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge_pair<C> &ep)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::edge<C> e1, e2;
read_binary (s, e1);
read_binary (s, e2);
ep = db::edge_pair<C> (e1, e2);
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contour<C> &c, bool hole)
{
uint64_t n = 0;
s >> n;
std::vector<db::point<C> > pts;
pts.reserve (n);
while (n-- > 0) {
pts.push_back (db::point<C> ());
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<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::polygon<C> &p)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
uint64_t n = 0;
s >> n;
p = db::polygon<C> ();
if (n > 0) {
p.reserve_holes (n - 1);
}
db::polygon_contour<C> 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<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_polygon<C> &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<C> ctr;
read_binary (s, ctr, false);
p.assign_hull (ctr);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::unit_trans<C> &)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::disp_trans<C> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::vector<C> d;
read_binary (s, d);
t = db::disp_trans<C> (d);
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::simple_trans<C> &t)
{
uint16_t r = 0;
s >> r;
db::vector<C> d;
read_binary (s, d);
t = db::simple_trans<C> (r, d);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_trans<C> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, t);
}
template<class C, class D, class R>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::complex_trans<C, D, R> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::vector<R> d;
read_binary (s, d);
double asin, acos, mag;
s >> asin >> acos >> mag;
t = db::complex_trans<C, D, R> (d, asin, acos, mag);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::text<C> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
std::string txt;
s >> txt;
db::simple_trans<C> tr;
read_binary (s, tr);
C size;
read_coord (s, size);
int32_t font, halign, valign;
s >> font >> halign >> valign;
t = db::text<C> (txt, tr, size, db::Font (font), db::HAlign (halign), db::VAlign (valign));
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::path<C> &p)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
uint64_t n = 0;
s >> n;
std::vector<db::point<C> > pts;
pts.reserve (n);
while (n-- > 0) {
pts.push_back (db::point<C> ());
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<C> (pts.begin (), pts.end (), width, bgn_ext, end_ext, round);
return s;
}
template<class T>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::object_with_properties<T> &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<class T>
std::vector<char> to_bytes (const T &t)
{
tl::OutputMemoryStream osm;
{
tl::BinaryOutputStream os (osm);
os << t;
}
return std::vector<char> (osm.data (), osm.data () + osm.size ());
}
template<class T>
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<class T>
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<class T>
void from_bytes (const std::vector<char> &s, T &t)
{
tl::InputMemoryStream ism (s.begin ().operator-> (), s.size ());
tl::InputStream is (ism);
tl::BinaryInputStream bis (is);
bis >> t;
}
}
#endif

View File

@ -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<db::cell_index_type> 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 <const db::ColdProxy *> (cptr) && keep.empty ()) {
keep.insert (*c);
}
} else {
keep.clear ();
break;
}
}
}
layout.cleanup (keep);
layout.cleanup ();
return layer_map_out ();
}

View File

@ -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<bool, db::property_names_id_type> m_text_annot_name_id;
const db::Layout *mp_layout;
};
}

View File

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

View File

@ -119,7 +119,6 @@ public:
virtual bool wants_variants () const { return false; }
private:
bool m_inverted;
db::EdgeAngleChecker m_checker;
};

View File

@ -244,7 +244,6 @@ struct DB_PUBLIC EdgeOrientationFilter
}
private:
bool m_inverse;
db::MagnificationAndOrientationReducer m_vars;
EdgeAngleChecker m_checker;
};

View File

@ -1723,6 +1723,84 @@ Layout::refresh ()
}
}
std::set<db::cell_index_type>
Layout::cells_to_cleanup (const std::set<db::cell_index_type> &keep_always) const
{
std::set<db::cell_index_type> 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<db::cell_index_type> 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 <const db::ColdProxy *> (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 <db::cell_index_type> 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 <db::cell_index_type> 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<db::cell_index_type> &keep)
{
@ -1801,28 +1879,17 @@ Layout::cleanup (const std::set<db::cell_index_type> &keep)
}
std::set<cell_index_type> 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<cell_index_type> 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

View File

@ -1682,6 +1682,19 @@ public:
*/
void cleanup (const std::set<db::cell_index_type> &keep = std::set<db::cell_index_type> ());
/**
* @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<db::cell_index_type> cells_to_cleanup (const std::set<db::cell_index_type> &keep = std::set<db::cell_index_type> ()) const;
/**
* @brief Calls "update" on all cells of the layout
*

View File

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

View File

@ -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<db::Net *> 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);
}
}
}

View File

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

View File

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

View File

@ -372,12 +372,19 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index
if (m_all_cells) {
// issue #2350: don't write cells that are to be cleanup, but in a sensitive manner
// - this does a kind of virtual cleanup, but with the same rules as applied
// during the normal cleanup.
std::set<db::cell_index_type> 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 <db::cell_index
if (has_skipped_replica) {
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) {
if (cell->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 <db::cell_index
} else {
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end () && ! has_skipped_replica; ++cell) {
cells.insert (cell->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 ());
}
}
}

View File

@ -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<R> &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
*

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"
);
}
};

View File

@ -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<db::cell_index_type> caller_cells (const db::Cell *c)
return std::vector<db::cell_index_type> (ids.begin (), ids.end ());
}
static bool is_cold_proxy (const db::Cell *cell)
{
return dynamic_cast<const db::ColdProxy *> (cell) != 0;
}
static bool is_library_cell (const db::Cell *cell)
{
return dynamic_cast<const db::LibraryProxy *> (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<const db::ColdProxy *> (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<const db::ColdProxy *> (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<db::LibraryProxy *> (cell);
@ -1173,13 +1209,25 @@ static const std::vector<tl::Variant> &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 <const db::ColdProxy *> (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<std::string, tl::Variant> 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 <const db::ColdProxy *> (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 <const db::ColdProxy *> (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<db::Cell> 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<db::Cell> 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<db::Cell> 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<db::Cell> 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<db::Cell> 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"

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"
) +

View File

@ -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<db::cell_index_type> &cell_indices)
delete_cells (db::Layout *layout, const std::vector<db::cell_index_type> &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<db::Cell *> &cells)
{
std::set<db::cell_index_type> 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<db::cell_index_type> &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<db::Cell *> &cells, int levels)
{
std::set<db::cell_index_type> 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<db::cell_index_type> &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<db::Cell *> &cells, int levels)
{
std::set<db::cell_index_type> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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"

File diff suppressed because it is too large Load Diff

View File

@ -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<db::Net *>
nets_non_const (const std::vector<const db::Net *> &nc)
{
std::vector<db::Net *> n;
n.reserve (nc.size ());
for (auto i = nc.begin (); i != nc.end (); ++i) {
n.push_back (const_cast<db::Net *> (*i));
}
return n;
}
static std::vector<const db::Net *>
nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs)
{
std::vector<const db::Net *> 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<db::Net *>
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<db::NetlistObject> decl_dbNetlistObject;
Class<db::Circuit> 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."
);
}

View File

@ -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<db::DeviceReconnectedTerminal> 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<db::DeviceAbstractRef> 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<db::DeviceReconnectedTerminal> empty;
static std::vector<db::DeviceReconnectedTerminal>::const_iterator begin_reconnected_terminals_for (const db::Device *device, size_t terminal_id)
{
const std::vector<db::DeviceReconnectedTerminal> *ti = device->reconnected_terminals_for ((unsigned int) terminal_id);
if (! ti) {
return empty.begin ();
} else {
return ti->begin ();
}
}
static std::vector<db::DeviceReconnectedTerminal>::const_iterator end_reconnected_terminals_for (const db::Device *device, size_t terminal_id)
{
const std::vector<db::DeviceReconnectedTerminal> *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<db::DeviceAbstractRef>::const_iterator begin_other_abstracts (const db::Device *device)
{
return device->other_abstracts ().begin ();
}
static std::vector<db::DeviceAbstractRef>::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<db::NetlistObject> decl_dbNetlistObject;
Class<db::Device> 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<db::DeviceAbstract> 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."
);
}

View File

@ -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<db::DeviceTerminalDefinition> 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<db::DeviceParameterDefinition> 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, bool, const db::Device &, const db::Device &> (&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, bool, db::Device *, db::Device *> (&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<db::EqualDeviceParameters> 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<GenericDeviceParameterCompare> 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<GenericDeviceCombiner> 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<std::string> &get_terminal_order (const db::DeviceClass::SpiceProfile *profile)
{
return profile->terminal_order;
}
static void set_terminal_order (db::DeviceClass::SpiceProfile *profile, const std::vector<std::string> &terminal_order)
{
profile->terminal_order = terminal_order;
}
Class<db::DeviceClass::SpiceProfile> 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<db::EqualDeviceParameters *> (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<GenericDeviceCombiner *> (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<db::DeviceClass> 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", &parameter_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."
);
}

View File

@ -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<db::NetTerminalRef> 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<db::NetPinRef> 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<db::NetSubcircuitPinRef> 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<db::NetlistObject> decl_dbNetlistObject;
Class<db::Net> 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."
);
}

View File

@ -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<tl::Variant> property_keys (const db::NetlistObject *object)
{
std::vector<tl::Variant> v;
for (db::NetlistObject::property_iterator p = object->begin_properties (); p != object->end_properties (); ++p) {
v.push_back (p->first);
}
return v;
}
Class<db::NetlistObject> 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"
);
}

View File

@ -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<db::NetlistObject> decl_dbNetlistObject;
Class<db::Pin> 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."
);
}

View File

@ -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 ("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<std::string> &net_names () const { return m_net_names; }
std::vector<std::string> &net_names_nc () { return m_net_names; }
void set_net_names (const std::vector<std::string> &nn) { m_net_names = nn; }
const db::NetlistSpiceReader::parameters_type &parameters () const { return m_parameters; }
db::NetlistSpiceReader::parameters_type &parameters_nc () { return m_parameters; }
void set_parameters (const db::NetlistSpiceReader::parameters_type &parameters) { m_parameters = parameters; }
private:
std::string m_model, m_element;
double m_value;
std::vector<std::string> 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<std::string> &strings () const { return m_strings; }
std::vector<std::string> &strings_nc () { return m_strings; }
void set_strings (const std::vector<std::string> &nn) { m_strings = nn; }
const db::NetlistSpiceReader::parameters_type &parameters () const { return m_parameters; }
db::NetlistSpiceReader::parameters_type &parameters_nc () { return m_parameters; }
void set_parameters (const db::NetlistSpiceReader::parameters_type &parameters) { m_parameters = parameters; }
private:
std::vector<std::string> 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, db::Netlist *> (&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, db::Netlist *> (&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, bool, const std::string &> (&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, bool, const std::string &> (&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, std::string, const std::string &> (&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<std::string> &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, ParseElementData, const std::string &, const std::string &> (&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<db::Net *> &nets, const db::NetlistSpiceReader::parameters_type &params)
{
try {
m_error.clear ();
if (cb_element.can_issue ()) {
return cb_element.issue<db::NetlistSpiceReaderDelegate, bool, db::Circuit *, const std::string &, const std::string &, const std::string &, double, const std::vector<db::Net *> &, 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<db::Net *> &nets, const db::NetlistSpiceReader::parameters_type &params)
{
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<ParseElementComponentsData> 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<ParseElementData> 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<NetlistSpiceReaderDelegateImpl> 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<NetlistSpiceReaderDelegateImpl> 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_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."
);
}

View File

@ -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> (&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<db::DeviceClass &> (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, db::DeviceClass &> (&NetlistSpiceWriterDelegateImpl::org_write_device_intro, const_cast<db::DeviceClass &> (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<db::Device &> (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, db::Device &> (&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<NetlistSpiceWriterDelegateImpl> 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<NetlistSpiceWriterDelegateImpl> 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 ("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_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."
);
}

View File

@ -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<db::NetlistObject> decl_dbNetlistObject;
Class<db::SubCircuit> 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."
);
}

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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 "

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"
);
}

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"

View File

@ -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 <class T>
static T *from_string (const std::string &s)
{
tl::Extractor ex (s);
std::unique_ptr<T> t (new T ());
ex.read (*t.get ());
return t.release ();
}
template <class T>
static T *from_bytes (const std::vector<char> &s)
{
std::unique_ptr<T> t (new T ());
db::from_bytes (s, *t);
return t.release ();
}
template <class T>
static std::vector<char> to_bytes (const T *t)
{
return db::to_bytes (*t);
}
template <class T>
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<T>, 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<T>, 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<T>,
"@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<T>,
"@brief Gets the user properties\n"

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"
);
}
};

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"
) +

View File

@ -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<char> &s)
{
std::unique_ptr<C> c (new C ());
db::from_bytes (s, *c);
return c.release ();
}
static std::vector<char> 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"
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -574,7 +574,9 @@ in <a href="#connect">connect</a>, <a href="#soft_connect">soft_connect</a>, <a
</p><p>
If layers are not named, they will be given a name made from the
<a href="#name_prefix">name_prefix</a> 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.
</p><p>
<a href="#name">name</a> can only be used once on a layer and the layer names must be
unique (not taken by another layer).

View File

@ -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.
</li>
<li><b>Object snapping:</b> 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.
</li>
<li><b>Mode:</b> 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.
</li>
<li><b>Never snap to objects:</b> if this option is on, the ruler never snaps to drawing
objects, even if that global option is enabled.
</li>
</ul>
<p>

View File

@ -142,8 +142,8 @@ public:
}
}
virtual std::vector<std::string> additional_editor_options_pages () const
{
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase *) const
{
std::vector<std::string> names;
names.push_back ("GenericEditorOptions");
return names;
@ -415,7 +415,7 @@ public:
return true;
}
virtual std::vector<std::string> additional_editor_options_pages () const
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase *) const
{
std::vector<std::string> names;
names.push_back ("GenericEditorOptions");

View File

@ -47,6 +47,65 @@
namespace img
{
// --------------------------------------------------------------------------------------
namespace
{
struct compare_first_of_node
{
bool operator() (const std::pair <double, std::pair<tl::Color, tl::Color> > &a, const std::pair <double, std::pair<tl::Color, tl::Color> > &b) const
{
return a.first < b.first;
}
};
}
static tl::Color
interpolated_color2 (const std::pair<double, std::pair<tl::Color, tl::Color> > &n1, const std::pair<double, std::pair<tl::Color, tl::Color> > &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<std::pair<double, std::pair<tl::Color, tl::Color> > >::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 <double, std::pair<tl::Color, tl::Color> > &a, const std::pair <double, std::pair<tl::Color, tl::Color> > &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<std::pair<double, std::pair<tl::Color, tl::Color> > >::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

View File

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

View File

@ -62,15 +62,6 @@ PluginDeclaration::get_options (std::vector < std::pair<std::string, std::string
options.push_back (std::pair<std::string, std::string> (cfg_images_visible, "true"));
}
std::vector<std::string>
PluginDeclaration::additional_editor_options_pages () const
{
std::vector<std::string> names;
// TODO: provide in a central place instead of borrowing from the edt module
names.push_back ("GenericEditorOptions");
return names;
}
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new img::PluginDeclaration (), 4000, "img::Plugin");
}

View File

@ -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<std::string, std::string> > &options) const;
virtual std::vector<std::string> additional_editor_options_pages () const;
};
}

View File

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

View File

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

View File

@ -74,6 +74,7 @@ public:
options.push_back (std::pair<std::string, std::string> (cfg_show_libraries_view, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_show_bookmarks_view, "false"));
options.push_back (std::pair<std::string, std::string> (cfg_show_layer_panel, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_show_tool_options, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_layout_file_watcher_enabled, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_window_state, ""));
options.push_back (std::pair<std::string, std::string> (cfg_window_geometry, ""));

View File

@ -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<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::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, "?"));

View File

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

View File

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

View File

@ -1280,32 +1280,139 @@ AbstractMenu::extra_menu_name ()
void
AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
{
if (tbar) {
tbar->clear ();
}
std::set<std::pair<size_t, QAction *> > present_actions;
// Build the menu bar
if (mbar) {
std::set<std::pair<size_t, QAction *> > present_actions;
QList<QAction *> a = mbar->actions ();
for (QList<QAction *>::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<AbstractMenuItem>::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<std::pair<size_t, QAction *> >::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<std::pair<size_t, QAction *> >::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<std::pair<size_t, QAction *> >::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<AbstractMenuItem>::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<std::pair<size_t, QAction *> >::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<std::pair<size_t, QAction *> >::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<std::pair<size_t, QAction *> >::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

View File

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

View File

@ -482,11 +482,15 @@ public:
return new MoveService (view);
}
virtual std::vector<std::string> additional_editor_options_pages () const
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase *view) const
{
std::vector<std::string> 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;
}
};

View File

@ -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<std::string> additional_editor_options_pages () const
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const
{
return std::vector<std::string> ();
}

View File

@ -333,11 +333,13 @@ public:
return new SelectionService (view);
}
virtual std::vector<std::string> additional_editor_options_pages () const
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase *view) const
{
std::vector<std::string> 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;
}
};

View File

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

View File

@ -318,7 +318,7 @@ public:
m_additional_editor_options_pages.push_back (name);
}
virtual std::vector<std::string> additional_editor_options_pages () const
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase *) const
{
return m_additional_editor_options_pages;
}

View File

@ -56,7 +56,7 @@ EditorOptionsFrame::populate (LayoutViewBase *view)
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
cls->get_editor_options_pages (editor_options_pages, view, view->dispatcher ());
std::vector<std::string> ap = cls->additional_editor_options_pages ();
std::vector<std::string> ap = cls->additional_editor_options_pages (view);
for (auto i = ap.begin (); i != ap.end (); ++i) {
additional_pages [*i].push_back (cls.operator-> ());
}

View File

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

View File

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

View File

@ -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 <db::cell_index_type> &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 ());
}
}

View File

@ -57,85 +57,7 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Pixel size</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="le_window_size">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="sb_threads">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Averaging window size</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Threads</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>353</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2" colspan="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>µm (a multiple of the pixel size or empty)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Boundary mode</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<item row="3" column="1" colspan="3">
<widget class="QComboBox" name="cb_boundary_mode">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -168,6 +90,114 @@
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Boundary mode</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="sb_threads">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Averaging window size</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>353</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2" colspan="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>µm (a multiple of the pixel size or empty)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="le_window_size">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Threads</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Pixel size</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Averaging mode</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QComboBox" name="cb_averaging_mode">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
<item>
<property name="text">
<string>Square hat</string>
</property>
</item>
<item>
<property name="text">
<string>Gaussian hat (σ = window size / 2)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -66,10 +66,17 @@ static const std::string boundary_mode_average ("boundary-mode-average");
// items in boundary_mode_cb
static const std::vector<std::string> 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<std::string> 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<double> 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<double> 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<double> &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<double>::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<double>::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;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

691
src/tl/tl/tlBinaryStream.cc Normal file
View File

@ -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 <limits>
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<char> &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<const double *> (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<const float *> (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<const uint16_t *> (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<const int16_t *> (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<const uint32_t *> (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<const int32_t *> (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<const uint64_t *> (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<const int64_t *> (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<long>::min () && vv <= (int64_t) std::numeric_limits<long>::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<unsigned long>::min () && vv <= (uint64_t) std::numeric_limits<unsigned long>::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<char> 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<char> 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;
}
}
}

207
src/tl/tl/tlBinaryStream.h Normal file
View File

@ -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 <class T>
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<char> &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

View File

@ -20,8 +20,6 @@
*/
#include <stddef.h>
#include <ctype.h>
#include <fcntl.h>

View File

@ -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 <class T>
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 &);

View File

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

View File

@ -315,6 +315,12 @@ Variant::Variant (const std::vector<char> &ba)
m_var.m_bytearray = new std::vector<char> (ba);
}
Variant::Variant (std::vector<char> &&ba)
: m_type (t_bytearray), m_string (0)
{
m_var.m_bytearray = new std::vector<char> (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<char> &s)
{
@ -727,6 +759,20 @@ Variant::operator= (const std::vector<char> &s)
return *this;
}
Variant &
Variant::operator= (std::vector<char> &&s)
{
if (m_type == t_bytearray && &s == m_var.m_bytearray) {
// we are assigning to ourselves
} else {
std::vector<char> *snew = new std::vector<char> (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<char> cv (s.begin (), s.end ());
v = tl::Variant (std::move (cv));
} else {
v = tl::Variant (s);
}
return true;
} else {

View File

@ -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<char>
*/
Variant (const std::vector<char> &s);
/**
* @brief Initialize the Variant with a std::vector<char> (move semantics)
*/
Variant (std::vector<char> &&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<tl::Variant> (list);
}
/**
* @brief Initialize the Variant with an explicit vector of variants (move semantics)
*/
Variant (std::vector<tl::Variant> &&list)
: m_type (t_list), m_string (0)
{
m_var.m_list = new std::vector<tl::Variant> (list);
}
/**
* @brief Initialize the Variant with an explicit map of variants
*/
@ -475,6 +499,15 @@ public:
m_var.m_array = new std::map<tl::Variant, tl::Variant> (map);
}
/**
* @brief Initialize the Variant with an explicit map of variants (move semantics)
*/
Variant (std::map<tl::Variant, tl::Variant> &&map)
: m_type (t_array), m_string (0)
{
m_var.m_array = new std::map<tl::Variant, tl::Variant> (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<char> &v);
/**
* @brief Assignment of a STL byte array (move)
*/
Variant &operator= (std::vector<char> &&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
*/

View File

@ -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<char> 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<tl::Variant> 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<std::pair<tl::Variant, tl::Variant> > a = { { 1, 5.5 }, { 2, -17 }, { "id", "ABC" } };
std::map<tl::Variant, tl::Variant> 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'}");
}

View File

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

View File

@ -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<short> (), false);
EXPECT_EQ (v.is<unsigned short> (), false);
EXPECT_EQ (v.is<int> (), false);
EXPECT_EQ (v.is<unsigned int> (), false);
EXPECT_EQ (v.is<short> (), false);
EXPECT_EQ (v.is<unsigned short> (), false);
EXPECT_EQ (v.is<unsigned char> (), false);
EXPECT_EQ (v.is<signed char> (), false);
EXPECT_EQ (v.is<long> (), 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<int> (), true);
EXPECT_EQ (v.is<unsigned int> (), 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<unsigned int> (), false);
EXPECT_EQ (v.is<unsigned char> (), false);
EXPECT_EQ (v.is<signed char> (), 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<unsigned char> (), false);
EXPECT_EQ (v.is<signed char> (), 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<unsigned char> (), false);
EXPECT_EQ (v.is<signed char> (), 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<signed char> (), false);
EXPECT_EQ (v.is<long> (), false);
EXPECT_EQ (v.is<unsigned long> (), 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<char> 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

View File

@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri)
SOURCES = \
tlAlgorithmTests.cc \
tlBase64Tests.cc \
tlBinaryStreamTests.cc \
tlClassRegistryTests.cc \
tlCommandLineParserTests.cc \
tlColorTests.cc \

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More