mirror of https://github.com/KLayout/klayout.git
Refactoring
This commit is contained in:
commit
cb96b8c169
67
Changelog
67
Changelog
|
|
@ -1,3 +1,70 @@
|
|||
0.30.9 (2026-05-25):
|
||||
* Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true)
|
||||
* Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed
|
||||
* Enhancement: %GITHUB%/issues/2337 [Feature Request] Add to_bytes and from_bytes to shape classes
|
||||
* Bugfix: %GITHUB%/issues/2339 Ruby ReportDatabase merging can lead to internal error
|
||||
* Bugfix: %GITHUB%/issues/2343 copy_tree implicitly drops collinear points
|
||||
* Bugfix: %GITHUB%/issues/2344 Instance properties inside a PCell are not preserved in KLayout
|
||||
* Bugfix: %GITHUB%/issues/2345 Substrate sequestration
|
||||
Although this ticket was more a discussion, this enhancement fixes the behavior of
|
||||
empty layers in deep mode, which allows placing device terminals on them.
|
||||
This eliminates the need for the workaround mentioned in the ticket.
|
||||
* Bugfix: %GITHUB%/issues/2350 strm2oas klayout/testdata/gds/issue_1835.gds a.oas, a.oas is empty
|
||||
With this fix, it is possible to save and load layouts that use a single library references
|
||||
or PCell variants as top cell. This is not strictly a supported use model, but may be
|
||||
useful in some cases. The "strm2xxx" tools now allow translating such files from OASIS
|
||||
to GDS and back.
|
||||
* Bugfix: %GITHUB%/issues/2356 Edit options always shown, even for klayout -ne
|
||||
* Enhancement: because of issue #2356 and #2335, the "Editor Options" dock was overhauled
|
||||
* It is called "Tool Options" now as it hosts not just shape editor options
|
||||
* The Ruler tool got its own page as the original page was not functional and
|
||||
rulers have other options. This replicates some settings from the "Ruler
|
||||
and annotations setup" page.
|
||||
* The dock widget's visibility can be controlled in the same way than the other
|
||||
docks. The "Edit/Editor Options" entry moved to "View/Tool Options". It turns
|
||||
on or off the tool options dock, which is populated with pages depending on the tool.
|
||||
The default key binding (F3) will now show or hide this dock.
|
||||
* Enhancement: %GITHUB%/issues/2360 Sort ports by name in "make_top_level_pins"
|
||||
* Enhancement: A feature to compute a density map. It is found inside Tools/Density Map.
|
||||
The density map can be computed from one or multiple layers, the region can be
|
||||
specified in terms of visible region, rulers or bounding boxes of certain layers
|
||||
or the whole cell. The tool will generate an image overlay over the layout
|
||||
holding the density data.
|
||||
* Bugfix: using "xkill" on a KLayout instance ended in a crash
|
||||
* Enhancement: settings of search features are persisted (see discussion 2868)
|
||||
* Enhancement: when moving rulers with "snap to objects", all points of the
|
||||
rulers snap to edges or vertexes now.
|
||||
* Bugfix: false color maps in images where incorrectly interpolated in some
|
||||
cases.
|
||||
* Bugfix: small bugfix in L2N/LVSDB reader - default values of parameters were
|
||||
treated as int.
|
||||
* Bugfix: properties on objects with properties (such as BoxWithProperties) where
|
||||
not properly serialized. Specifically their representation did not include the
|
||||
type. Hence it was not possible to retrieve properties on deserialization with
|
||||
"from_s". The fix is to annotate the property values with type information.
|
||||
NOTE: this is a change that is not backward-compatible.
|
||||
* Bugfix: snapping rulers to edges parallel to the allowed ruler direction resulted
|
||||
in skew rulers.
|
||||
* Enhancement: renamed ruler option for more clarity - changed
|
||||
'snap to objects (unless disabled in template)' to 'never snap to object' (inverted)
|
||||
* Enhancement: enhancements in the script API
|
||||
* Cell#is_cold_proxy?: indicates if a cell is a "defunc" library reference
|
||||
* Cell#library_cell_name: gets the library cell name for a "defunc" reference
|
||||
* Cell#library_name: gets the library name for a "defunc" reference
|
||||
* Cell#pcell_name: gets the pcell name for a "defunct" reference
|
||||
* Layout#delete_cells: now available with a list of cell object
|
||||
* Layout#delete_cell_rec: now available with a cell object
|
||||
* Layout#prune_cell: now available with a cell object
|
||||
* Layout#prune_cells: for pruning multiple cells in one call
|
||||
* Layout#prune_subcells: now with a cell object
|
||||
* Layout#prune_subcells: now with multiple root cells
|
||||
* Layout#flatten: now with a cell object
|
||||
* Layout#flatten_into: now with cell objects
|
||||
* Layout#delete_cell: now with a cell object
|
||||
* Default value "all" for "levels" argument in "prune_subcells" and "prune_cell"
|
||||
* Default value "all" for "levels" and "true" for "prune" argument in "flatten"
|
||||
* Default value "all" for "levels" and "unity" for "trans" argument in "flatten_into"
|
||||
|
||||
0.30.8 (2026-04-14):
|
||||
* Enhancement: %GITHUB%/issues/2248 Switch layouts but without losing the handle
|
||||
* Bugfix: %GITHUB%/issues/2299 gf180mcu DRC performance regression.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 ..
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@ public:
|
|||
virtual bool wants_variants () const { return false; }
|
||||
|
||||
private:
|
||||
bool m_inverted;
|
||||
db::EdgeAngleChecker m_checker;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -244,7 +244,6 @@ struct DB_PUBLIC EdgeOrientationFilter
|
|||
}
|
||||
|
||||
private:
|
||||
bool m_inverse;
|
||||
db::MagnificationAndOrientationReducer m_vars;
|
||||
EdgeAngleChecker m_checker;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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", ¶meter_definition2, gsi::arg ("parameter_name"),
|
||||
"@brief Gets the parameter definition object for a given ID.\n"
|
||||
"Parameter definition IDs are used in some places to reference a specific parameter of a device. "
|
||||
"This method obtains the corresponding definition object."
|
||||
"\n"
|
||||
"This version accepts a parameter name.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("enable_parameter", &enable_parameter, gsi::arg ("parameter_id"), gsi::arg ("enable"),
|
||||
"@brief Enables or disables a parameter.\n"
|
||||
"Some parameters are 'secondary' parameters which are extracted but not handled in device compare and are not shown in the netlist browser. "
|
||||
"For example, the 'W' parameter of the resistor is such a secondary parameter. This method allows turning a parameter in a primary one ('enable') or "
|
||||
"into a secondary one ('disable').\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("enable_parameter", &enable_parameter2, gsi::arg ("parameter_name"), gsi::arg ("enable"),
|
||||
"@brief Enables or disables a parameter.\n"
|
||||
"Some parameters are 'secondary' parameters which are extracted but not handled in device compare and are not shown in the netlist browser. "
|
||||
"For example, the 'W' parameter of the resistor is such a secondary parameter. This method allows turning a parameter in a primary one ('enable') or "
|
||||
"into a secondary one ('disable').\n"
|
||||
"\n"
|
||||
"This version accepts a parameter name.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("has_parameter?", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"),
|
||||
"@brief Returns true, if the device class has a parameter with the given name.\n"
|
||||
) +
|
||||
gsi::method ("parameter_id", &db::DeviceClass::parameter_id_for_name, gsi::arg ("name"),
|
||||
"@brief Returns the parameter ID of the parameter with the given name.\n"
|
||||
"An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check "
|
||||
"whether the name is a valid parameter name."
|
||||
) +
|
||||
gsi::method ("has_terminal?", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"),
|
||||
"@brief Returns true, if the device class has a terminal with the given name.\n"
|
||||
) +
|
||||
gsi::method ("terminal_id", &db::DeviceClass::terminal_id_for_name, gsi::arg ("name"),
|
||||
"@brief Returns the terminal ID of the terminal with the given name.\n"
|
||||
"An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check "
|
||||
"whether the name is a valid terminal name."
|
||||
) +
|
||||
gsi::method_ext ("equal_parameters", &get_equal_parameters,
|
||||
"@brief Gets the device parameter comparer for netlist verification or nil if no comparer is registered.\n"
|
||||
"See \\equal_parameters= for the setter.\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("equal_parameters=", &equal_parameters, gsi::arg ("comparer"),
|
||||
"@brief Specifies a device parameter comparer for netlist verification.\n"
|
||||
"By default, all devices are compared with all parameters. If you want to select only certain parameters "
|
||||
"for comparison or use a fuzzy compare criterion, use an \\EqualDeviceParameters object and assign it "
|
||||
"to the device class of one netlist. You can also chain multiple \\EqualDeviceParameters objects with the '+' operator "
|
||||
"for specifying multiple parameters in the equality check.\n"
|
||||
"\n"
|
||||
"You can assign nil for the parameter comparer to remove it.\n"
|
||||
"\n"
|
||||
"In special cases, you can even implement a custom compare scheme by deriving your own comparer from the \\GenericDeviceParameterCompare class.\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("add_terminal", &gsi::dc_add_terminal_definition, gsi::arg ("terminal_def"),
|
||||
"@brief Adds the given terminal definition to the device class\n"
|
||||
"This method will define a new terminal. The new terminal is added at the end of existing terminals. "
|
||||
"The terminal definition object passed as the argument is modified to contain the "
|
||||
"new ID of the terminal.\n"
|
||||
"\n"
|
||||
"The terminal is copied into the device class. Modifying the terminal object later "
|
||||
"does not have the effect of changing the terminal definition.\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("clear_terminals", &db::DeviceClass::clear_terminal_definitions,
|
||||
"@brief Clears the list of terminals\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("add_parameter", &gsi::dc_add_parameter_definition, gsi::arg ("parameter_def"),
|
||||
"@brief Adds the given parameter definition to the device class\n"
|
||||
"This method will define a new parameter. The new parameter is added at the end of existing parameters. "
|
||||
"The parameter definition object passed as the argument is modified to contain the "
|
||||
"new ID of the parameter."
|
||||
"\n"
|
||||
"The parameter is copied into the device class. Modifying the parameter object later "
|
||||
"does not have the effect of changing the parameter definition.\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("clear_parameters", &db::DeviceClass::clear_parameter_definitions,
|
||||
"@brief Clears the list of parameters\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("combiner=", &set_combiner, gsi::arg ("combiner"),
|
||||
"@brief Specifies a device combiner (parallel or serial device combination).\n"
|
||||
"\n"
|
||||
"You can assign nil for the combiner to remove it.\n"
|
||||
"\n"
|
||||
"In special cases, you can even implement a custom combiner by deriving your own comparer from the \\GenericDeviceCombiner class.\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method_ext ("combiner", &get_combiner,
|
||||
"@brief Gets a device combiner or nil if none is registered.\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("supports_parallel_combination=", &db::DeviceClass::set_supports_parallel_combination, gsi::arg ("f"),
|
||||
"@brief Specifies whether the device supports parallel device combination.\n"
|
||||
"Parallel device combination means that all terminals of two combination candidates are connected to the same nets. "
|
||||
"If the device does not support this combination mode, this predicate can be set to false. This will make the device "
|
||||
"extractor skip the combination test in parallel mode and improve performance somewhat.\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("supports_serial_combination=", &db::DeviceClass::set_supports_serial_combination, gsi::arg ("f"),
|
||||
"@brief Specifies whether the device supports serial device combination.\n"
|
||||
"Serial device combination means that the devices are connected by internal nodes. "
|
||||
"If the device does not support this combination mode, this predicate can be set to false. This will make the device "
|
||||
"extractor skip the combination test in serial mode and improve performance somewhat.\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("equivalent_terminal_id", &db::DeviceClass::equivalent_terminal_id, gsi::arg ("original_id"), gsi::arg ("equivalent_id"),
|
||||
"@brief Specifies a terminal to be equivalent to another.\n"
|
||||
"Use this method to specify two terminals to be exchangeable. For example to make S and D of a MOS transistor equivalent, "
|
||||
"call this method with S and D terminal IDs. In netlist matching, S will be translated to D and thus made equivalent to D.\n"
|
||||
"\n"
|
||||
"Note that terminal equivalence is not effective if the device class operates in strict mode (see \\DeviceClass#strict=).\n"
|
||||
"\n"
|
||||
"This method has been moved from 'GenericDeviceClass' to 'DeviceClass' in version 0.27.3.\n"
|
||||
) +
|
||||
gsi::method ("clear_equivalent_terminal_ids", &db::DeviceClass::clear_equivalent_terminal_ids,
|
||||
"@brief Clears all equivalent terminal ids\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.27.3.\n"
|
||||
),
|
||||
"@brief A class describing a specific type of device.\n"
|
||||
"Device class objects live in the context of a \\Netlist object. After a "
|
||||
"device class is created, it must be added to the netlist using \\Netlist#add. "
|
||||
"The netlist will own the device class object. When the netlist is destroyed, the "
|
||||
"device class object will become invalid.\n"
|
||||
"\n"
|
||||
"The \\DeviceClass class is the base class for other device classes.\n"
|
||||
"\n"
|
||||
"This class has been added in version 0.26. In version 0.27.3, the 'GenericDeviceClass' has been integrated with \\DeviceClass "
|
||||
"and the device class was made writeable in most respects. This enables manipulating built-in device classes."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -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"
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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 ¶meters () const { return m_parameters; }
|
||||
db::NetlistSpiceReader::parameters_type ¶meters_nc () { return m_parameters; }
|
||||
void set_parameters (const db::NetlistSpiceReader::parameters_type ¶meters) { m_parameters = parameters; }
|
||||
|
||||
private:
|
||||
std::string m_model, m_element;
|
||||
double m_value;
|
||||
std::vector<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 ¶meters () const { return m_parameters; }
|
||||
db::NetlistSpiceReader::parameters_type ¶meters_nc () { return m_parameters; }
|
||||
void set_parameters (const db::NetlistSpiceReader::parameters_type ¶meters) { m_parameters = parameters; }
|
||||
|
||||
private:
|
||||
std::vector<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 ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
return delegate->db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params);
|
||||
}
|
||||
|
||||
static ParseElementData parse_element_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const std::string &element)
|
||||
{
|
||||
return delegate->parse_element_helper (s, element);
|
||||
}
|
||||
|
||||
static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables)
|
||||
{
|
||||
tl::Variant res;
|
||||
double v = 0.0;
|
||||
if (db::NetlistSpiceReaderDelegate::try_read_value (s, v, variables)) {
|
||||
res = v;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables)
|
||||
{
|
||||
ParseElementComponentsData data;
|
||||
delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables);
|
||||
return data;
|
||||
}
|
||||
|
||||
Class<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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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, ""));
|
||||
|
|
|
|||
|
|
@ -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, "?"));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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> ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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-> ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
|
|
|
|||
|
|
@ -730,7 +730,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Get a single line (presumably UTF8 encoded)
|
||||
* @brief Gets a single line (presumably UTF8 encoded)
|
||||
*/
|
||||
const std::string &get_line ();
|
||||
|
||||
|
|
@ -747,7 +747,7 @@ public:
|
|||
std::string read_all (size_t max_count);
|
||||
|
||||
/**
|
||||
* @brief Get a single character
|
||||
* @brief Gets a single character
|
||||
*/
|
||||
char get_char ();
|
||||
|
||||
|
|
@ -757,14 +757,14 @@ public:
|
|||
char peek_char ();
|
||||
|
||||
/**
|
||||
* @brief Skip blanks, newlines etc.
|
||||
* @brief Skips blanks, newlines etc.
|
||||
*
|
||||
* Returns the following character without getting it.
|
||||
*/
|
||||
char skip ();
|
||||
|
||||
/**
|
||||
* @brief Get the source specification
|
||||
* @brief Gets the source specification
|
||||
*/
|
||||
std::string source () const
|
||||
{
|
||||
|
|
@ -772,7 +772,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current line number
|
||||
* @brief Gets the current line number
|
||||
*/
|
||||
size_t line_number ()
|
||||
{
|
||||
|
|
@ -780,7 +780,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Return false, if no more characters can be obtained
|
||||
* @brief Returns false, if no more characters can be obtained
|
||||
*/
|
||||
bool at_end () const
|
||||
{
|
||||
|
|
@ -788,7 +788,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Reset to the initial position
|
||||
* @brief Resets to the initial position
|
||||
*/
|
||||
void reset ();
|
||||
|
||||
|
|
@ -880,9 +880,9 @@ public:
|
|||
/**
|
||||
* @brief Create a string writer
|
||||
*/
|
||||
OutputMemoryStream ()
|
||||
OutputMemoryStream (size_t initial_alloc = 65536)
|
||||
{
|
||||
m_buffer.reserve (65336);
|
||||
m_buffer.reserve (initial_alloc);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1312,13 +1312,20 @@ public:
|
|||
void close ();
|
||||
|
||||
/**
|
||||
* @brief This is the outer write method to call
|
||||
* @brief Puts a string into the stream
|
||||
*
|
||||
* This implementation writes data through the
|
||||
* protected write call.
|
||||
* In text mode, this handles line separator conversion.
|
||||
* In binary mode, this method is equivalent to "put_raw".
|
||||
*/
|
||||
void put (const char *b, size_t n);
|
||||
|
||||
/**
|
||||
* @brief Puts the raw bytes into the stream
|
||||
*
|
||||
* This method bypasses the line feed translation.
|
||||
*/
|
||||
void put_raw (const char *b, size_t n);
|
||||
|
||||
/**
|
||||
* @brief Puts a C string (UTF-8) to the output
|
||||
*/
|
||||
|
|
@ -1336,7 +1343,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief << operator
|
||||
* @brief << operator: inserts character
|
||||
*/
|
||||
OutputStream &operator<< (char s)
|
||||
{
|
||||
|
|
@ -1345,7 +1352,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief << operator
|
||||
* @brief << operator: inserts a character
|
||||
*/
|
||||
OutputStream &operator<< (unsigned char s)
|
||||
{
|
||||
|
|
@ -1354,16 +1361,22 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief << operator
|
||||
* @brief << operator: inserts a string
|
||||
*
|
||||
* In binary mode, the string is inserted as a length/data
|
||||
* combination. That matches the extraction in BinaryInputStream.
|
||||
*/
|
||||
OutputStream &operator<< (const char *s)
|
||||
{
|
||||
put (s);
|
||||
put (s, strlen (s));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief << operator
|
||||
* @brief << operator: inserts a string
|
||||
*
|
||||
* In binary mode, the string is inserted as a length/data
|
||||
* combination. That matches the extraction in BinaryInputStream.
|
||||
*/
|
||||
OutputStream &operator<< (const std::string &s)
|
||||
{
|
||||
|
|
@ -1372,7 +1385,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief << operator
|
||||
* @brief << operator: inserts an object supported by "put_native".
|
||||
*/
|
||||
template <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 &);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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'}");
|
||||
}
|
||||
|
|
@ -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 (...) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue