mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' into dev-polygon-graph
This commit is contained in:
commit
9917978566
|
|
@ -34,6 +34,9 @@ jobs:
|
|||
- os: "ubuntu-latest"
|
||||
cibuild: "*musllinux*"
|
||||
cibw_arch: "musllinux"
|
||||
- os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner
|
||||
cibuild: "*manylinux*"
|
||||
cibw_arch: "aarch64"
|
||||
steps:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
|
@ -48,29 +51,44 @@ jobs:
|
|||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
- uses: actions/checkout@v4
|
||||
- name: ccache
|
||||
if: matrix.os != 'ubuntu-24.04-arm'
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS
|
||||
max-size: "5G"
|
||||
- name: Install dependencies
|
||||
if: matrix.os != 'ubuntu-24.04-arm'
|
||||
run: |
|
||||
env
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
HOST_CCACHE_DIR="$(ccache -k cache_dir)"
|
||||
mkdir -p $HOST_CCACHE_DIR
|
||||
- name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions
|
||||
uses: pypa/cibuildwheel@v2.23.0
|
||||
# to supply options, put them in 'env', like:
|
||||
# env:
|
||||
# CIBW_SOME_OPTION: value
|
||||
- name: Build wheels (ARM)
|
||||
if: matrix.os == 'ubuntu-24.04-arm'
|
||||
uses: pypa/cibuildwheel@v2.23.3
|
||||
env:
|
||||
# override the default CentOS “yum install … ccache” and drop ccache
|
||||
CIBW_BEFORE_ALL_LINUX: |
|
||||
yum install -y \
|
||||
zlib-devel \
|
||||
curl-devel \
|
||||
expat-devel \
|
||||
libpng-devel
|
||||
CIBW_BEFORE_BUILD_LINUX: "true"
|
||||
CIBW_BUILD: ${{ matrix.cibuild }}
|
||||
CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }}
|
||||
|
||||
- name: Build wheels (all other platforms)
|
||||
if: matrix.os != 'ubuntu-24.04-arm'
|
||||
uses: pypa/cibuildwheel@v2.23.3
|
||||
env:
|
||||
CIBW_BUILD: ${{ matrix.cibuild }}
|
||||
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}
|
||||
CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt
|
||||
|
||||
- name: Download Cache from Docker (linux only)
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm'
|
||||
# hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed
|
||||
run: |
|
||||
env
|
||||
|
|
|
|||
20
Changelog
20
Changelog
|
|
@ -1,3 +1,23 @@
|
|||
0.30.2 (2025-xx-xx):
|
||||
* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties
|
||||
|
||||
0.30.1 (2025-04-27):
|
||||
* Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts
|
||||
* Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed
|
||||
* Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader
|
||||
* Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9
|
||||
* Bugfix: %GITHUB%/issues/2020 Strict weak ordering issue fixed in edge processor
|
||||
* Enhancement: %GITHUB%/issues/2024 Option to configure grid density
|
||||
* Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field
|
||||
* Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view
|
||||
* Bugfix/Enhancement: some updates of "strmxor" tool
|
||||
- strmxor was giving wrong results if cell variants are
|
||||
present where one variant is covered entirely by a large shape
|
||||
- Parallelization now happens on a per-layer basis (same as for
|
||||
XOR tool in KLayout)
|
||||
- Shape count was not consistent in deep mode
|
||||
- All buddy tools print total runtime with -d11
|
||||
|
||||
0.30.0 (2025-03-25):
|
||||
* Bug: %GITHUB%/issues/1996 More robust triangulation
|
||||
* Bug: %GITHUB%/issues/2002 Path to polygon conversion issue
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
klayout (0.30.1-1) unstable; urgency=low
|
||||
|
||||
* New features and bugfixes
|
||||
- See changelog
|
||||
|
||||
-- Matthias Köfferlein <matthias@koefferlein.de> Sun, 27 Apr 2025 14:26:50 +0200
|
||||
|
||||
klayout (0.30.0-1) unstable; urgency=low
|
||||
|
||||
* New features and bugfixes
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ RubySequoia = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions
|
|||
# install with 'sudo port install ruby33'
|
||||
# [Key Type Name] = 'MP33'
|
||||
Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3',
|
||||
'inc': '/opt/local/include/ruby-3.3.7',
|
||||
'inc': '/opt/local/include/ruby-3.3.8',
|
||||
'lib': '/opt/local/lib/libruby.3.3.dylib'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ def CheckPkgDirectory():
|
|||
#------------------------------------------------------
|
||||
# [5] Check the occupied disk space
|
||||
#------------------------------------------------------
|
||||
command = "\du -sm %s" % DefaultBundleName
|
||||
command = r"\du -sm %s" % DefaultBundleName
|
||||
sizeApp = int( os.popen(command).read().strip("\n").split("\t")[0] )
|
||||
|
||||
#------------------------------------------------------
|
||||
|
|
@ -671,14 +671,14 @@ def MakeTargetDMGFile(msg=""):
|
|||
imageDest = "%s/.background" % MountDir
|
||||
if not os.path.isdir(imageDest):
|
||||
os.mkdir(imageDest)
|
||||
command = "\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG)
|
||||
command = r"\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG)
|
||||
os.system(command)
|
||||
|
||||
#--------------------------------------------------------
|
||||
# (6) Create a symbolic link to /Applications
|
||||
#--------------------------------------------------------
|
||||
print( ">>> (6) Creating a symbolic link to /Applications..." )
|
||||
command = "\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications)
|
||||
command = r"\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications)
|
||||
os.system(command)
|
||||
|
||||
#--------------------------------------------------------
|
||||
|
|
@ -702,7 +702,7 @@ def MakeTargetDMGFile(msg=""):
|
|||
print( ">>> (8) Copying the volume icon..." )
|
||||
iconsSrc = "macbuild/Resources/%s" % VolumeIcons
|
||||
iconsDest = "%s/.VolumeIcon.icns" % MountDir
|
||||
command1 = "\cp -p %s %s" % (iconsSrc, iconsDest)
|
||||
command1 = r"\cp -p %s %s" % (iconsSrc, iconsDest)
|
||||
command2 = "SetFile -c icnC %s" % iconsDest
|
||||
os.system(command1)
|
||||
sleep(2)
|
||||
|
|
@ -713,7 +713,7 @@ def MakeTargetDMGFile(msg=""):
|
|||
# (9) Change the permission
|
||||
#--------------------------------------------------------
|
||||
print( ">>> (9) Changing permission to 755..." )
|
||||
command = "\chmod -Rf 755 %s &> /dev/null" % MountDir
|
||||
command = r"\chmod -Rf 755 %s &> /dev/null" % MountDir
|
||||
os.system(command)
|
||||
|
||||
#--------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.cibuildwheel]
|
||||
build-verbosity = "3"
|
||||
test-command = [
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m
|
|||
drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?)
|
||||
drop_method "QNoDebug", /QNoDebug::operator<</ # nothing usable (TODO: how to map?)
|
||||
|
||||
# No longer supported operator== and operator!= in Qt 6.7/6.8
|
||||
# No longer supported operator== and operator!= in Qt 6.7/6.8/6.9
|
||||
add_native_operator_neq(self, "QEasingCurve")
|
||||
add_native_operator_neq(self, "QTimeZone")
|
||||
add_native_operator_neq(self, "QDir")
|
||||
|
|
@ -569,6 +569,7 @@ add_native_operator_neq(self, "QProcessEnvironment")
|
|||
add_native_operator_neq(self, "QRegularExpression")
|
||||
add_native_operator_neqlt(self, "QUrl")
|
||||
add_native_operator_neq(self, "QUrlQuery")
|
||||
add_native_operator_neq(self, "QDomNodeList")
|
||||
add_native_operator_neq(self, "QXmlStreamAttribute")
|
||||
add_native_operator_neq(self, "QXmlStreamEntityDeclaration")
|
||||
add_native_operator_neq(self, "QXmlStreamNamespaceDeclaration")
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -1028,7 +1028,7 @@ if __name__ == "__main__":
|
|||
setup(
|
||||
name=config.root,
|
||||
version=config.version(),
|
||||
license="GNU GPLv3",
|
||||
license="GPL-3.0-or-later",
|
||||
description="KLayout standalone Python package",
|
||||
long_description="This package is a standalone distribution of KLayout's Python API.\n\nFor more details see here: https://www.klayout.org/klayout-pypi",
|
||||
author="Matthias Koefferlein",
|
||||
|
|
@ -1037,7 +1037,6 @@ if __name__ == "__main__":
|
|||
# Recommended classifiers
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ PropertiesPage::readonly ()
|
|||
}
|
||||
|
||||
void
|
||||
PropertiesPage::apply ()
|
||||
PropertiesPage::apply (bool /*commit*/)
|
||||
{
|
||||
ant::Object obj;
|
||||
get_object (obj);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public:
|
|||
virtual void update ();
|
||||
virtual void leave ();
|
||||
virtual bool readonly ();
|
||||
virtual void apply ();
|
||||
virtual void apply (bool commit);
|
||||
|
||||
private slots:
|
||||
void swap_points_clicked ();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "dbReader.h"
|
||||
#include "dbWriter.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace bd
|
||||
{
|
||||
|
|
@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format)
|
|||
|
||||
db::Layout layout;
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
{
|
||||
db::LoadLayoutOptions load_options;
|
||||
generic_reader_options.configure (load_options);
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions ()
|
|||
m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool ();
|
||||
m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool ();
|
||||
m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string ();
|
||||
m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
|
||||
// Don't take the default, as in practice, it's more common to substitute LEF macros by layouts
|
||||
// m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
|
||||
m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always"
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbSaveLayoutOptions.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
|
||||
struct ClipData
|
||||
|
|
@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
|
|||
|
||||
cmd.parse (argc, argv);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
clip (data);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "dbLayoutDiff.h"
|
||||
#include "dbReader.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
BD_PUBLIC int strmcmp (int argc, char *argv[])
|
||||
{
|
||||
|
|
@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
|
|||
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given");
|
||||
}
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
db::Layout layout_a;
|
||||
db::Layout layout_b;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "tlLog.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlTimer.h"
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
#include "gsi.h"
|
||||
|
|
@ -98,5 +99,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[])
|
|||
lym::Macro macro;
|
||||
macro.load_from (script);
|
||||
macro.set_file_path (script);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
return macro.run ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include "gsiExpression.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlThreads.h"
|
||||
#include "tlThreadedWorkers.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -319,7 +321,8 @@ struct XORData
|
|||
dont_summarize_missing_layers (false), silent (false), no_summary (false),
|
||||
threads (0),
|
||||
tile_size (0.0), heal_results (false),
|
||||
output_layout (0), output_cell (0)
|
||||
output_layout (0), output_cell (0),
|
||||
layers_missing (0)
|
||||
{ }
|
||||
|
||||
db::Layout *layout_a, *layout_b;
|
||||
|
|
@ -336,6 +339,8 @@ struct XORData
|
|||
db::cell_index_type output_cell;
|
||||
std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> l2l_map;
|
||||
std::map<std::pair<int, db::LayerProperties>, ResultDescriptor> *results;
|
||||
mutable int layers_missing;
|
||||
mutable tl::Mutex lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -455,6 +460,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
db::Layout layout_a;
|
||||
db::Layout layout_b;
|
||||
|
||||
|
|
@ -572,14 +579,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
if (! silent && ! no_summary) {
|
||||
|
||||
if (result) {
|
||||
tl::info << "No differences found";
|
||||
tl::info << tl::to_string (tr ("No differences found"));
|
||||
} else {
|
||||
|
||||
const char *line_format = " %-10s %-12s %s";
|
||||
const char *sep = " -------------------------------------------------------";
|
||||
|
||||
tl::info << "Result summary (layers without differences are not shown):" << tl::endl;
|
||||
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
|
||||
std::string headline;
|
||||
if (deep) {
|
||||
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)")));
|
||||
} else {
|
||||
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)")));
|
||||
}
|
||||
|
||||
const char *sep = " ----------------------------------------------------------------";
|
||||
|
||||
tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl;
|
||||
tl::info << headline << tl::endl << sep;
|
||||
|
||||
int ti = -1;
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) {
|
||||
|
|
@ -587,17 +602,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
if (r->first.first != ti) {
|
||||
ti = r->first.first;
|
||||
if (tolerances[ti] > db::epsilon) {
|
||||
tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
|
||||
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
|
||||
tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
|
||||
tl::info << headline << tl::endl << sep;
|
||||
}
|
||||
}
|
||||
|
||||
std::string out ("-");
|
||||
std::string value;
|
||||
if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) {
|
||||
value = "(no such layer in first layout)";
|
||||
value = tl::to_string (tr ("(no such layer in first layout)"));
|
||||
} else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) {
|
||||
value = "(no such layer in second layout)";
|
||||
value = tl::to_string (tr ("(no such layer in second layout)"));
|
||||
} else if (! r->second.is_empty ()) {
|
||||
if (r->second.layer_output >= 0 && r->second.layout) {
|
||||
out = r->second.layout->get_properties (r->second.layer_output).to_string ();
|
||||
|
|
@ -758,15 +773,174 @@ bool run_tiled_xor (const XORData &xor_data)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool run_deep_xor (const XORData &xor_data)
|
||||
{
|
||||
db::DeepShapeStore dss;
|
||||
dss.set_threads (xor_data.threads);
|
||||
|
||||
class XORJob
|
||||
: public tl::JobBase
|
||||
{
|
||||
public:
|
||||
XORJob (int nworkers)
|
||||
: tl::JobBase (nworkers)
|
||||
{
|
||||
}
|
||||
|
||||
virtual tl::Worker *create_worker ();
|
||||
};
|
||||
|
||||
class XORWorker
|
||||
: public tl::Worker
|
||||
{
|
||||
public:
|
||||
XORWorker (XORJob *job);
|
||||
void perform_task (tl::Task *task);
|
||||
|
||||
db::DeepShapeStore &dss ()
|
||||
{
|
||||
return m_dss;
|
||||
}
|
||||
|
||||
private:
|
||||
XORJob *mp_job;
|
||||
db::DeepShapeStore m_dss;
|
||||
};
|
||||
|
||||
class XORTask
|
||||
: public tl::Task
|
||||
{
|
||||
public:
|
||||
XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu)
|
||||
: mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void run (XORWorker *worker) const
|
||||
{
|
||||
if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) {
|
||||
|
||||
if (m_la < 0) {
|
||||
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second";
|
||||
} else {
|
||||
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first";
|
||||
}
|
||||
|
||||
tl::MutexLocker locker (&mp_xor_data->lock);
|
||||
|
||||
mp_xor_data->layers_missing += 1;
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
|
||||
|
||||
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
|
||||
result.layer_a = m_la;
|
||||
result.layer_b = m_lb;
|
||||
result.layout = mp_xor_data->output_layout;
|
||||
result.top_cell = mp_xor_data->output_cell;
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ());
|
||||
|
||||
db::RecursiveShapeIterator ri_a, ri_b;
|
||||
|
||||
if (m_la >= 0) {
|
||||
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
|
||||
} else {
|
||||
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector<unsigned int> ());
|
||||
}
|
||||
ri_a.set_for_merged_input (true);
|
||||
|
||||
if (m_lb >= 0) {
|
||||
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
|
||||
} else {
|
||||
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector<unsigned int> ());
|
||||
}
|
||||
ri_b.set_for_merged_input (true);
|
||||
|
||||
db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
|
||||
db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
|
||||
|
||||
db::Region xor_res;
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
}
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
|
||||
|
||||
db::LayerProperties lp = m_layer_props;
|
||||
if (lp.layer >= 0) {
|
||||
lp.layer += tol_index * mp_xor_data->tolerance_bump;
|
||||
}
|
||||
|
||||
if (*t > db::epsilon) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ());
|
||||
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
|
||||
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
|
||||
}
|
||||
|
||||
{
|
||||
tl::MutexLocker locker (&mp_xor_data->lock);
|
||||
|
||||
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
|
||||
result.layer_a = m_la;
|
||||
result.layer_b = m_lb;
|
||||
result.layout = mp_xor_data->output_layout;
|
||||
result.top_cell = mp_xor_data->output_cell;
|
||||
|
||||
if (mp_xor_data->output_layout) {
|
||||
result.layer_output = result.layout->insert_layer (lp);
|
||||
xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output);
|
||||
} else {
|
||||
result.shape_count = xor_res.hier_count ();
|
||||
}
|
||||
}
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const XORData *mp_xor_data;
|
||||
const db::LayerProperties &m_layer_props;
|
||||
int m_la;
|
||||
int m_lb;
|
||||
double m_dbu;
|
||||
};
|
||||
|
||||
XORWorker::XORWorker (XORJob *job)
|
||||
: tl::Worker (), mp_job (job)
|
||||
{
|
||||
// TODO: this conflicts with the "set_for_merged_input" optimization below.
|
||||
// It seems not to be very effective then. Why?
|
||||
dss.set_wants_all_cells (true); // saves time for less cell mapping operations
|
||||
m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations
|
||||
}
|
||||
|
||||
void
|
||||
XORWorker::perform_task (tl::Task *task)
|
||||
{
|
||||
XORTask *xor_task = dynamic_cast <XORTask *> (task);
|
||||
if (xor_task) {
|
||||
xor_task->run (this);
|
||||
}
|
||||
}
|
||||
|
||||
tl::Worker *
|
||||
XORJob::create_worker ()
|
||||
{
|
||||
return new XORWorker (this);
|
||||
}
|
||||
|
||||
|
||||
bool run_deep_xor (const XORData &xor_data)
|
||||
{
|
||||
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
|
|
@ -779,98 +953,18 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
xor_data.output_layout->dbu (dbu);
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
int index = 1;
|
||||
XORJob job (xor_data.threads);
|
||||
|
||||
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) {
|
||||
|
||||
if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) {
|
||||
|
||||
if (ll->second.first < 0) {
|
||||
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second";
|
||||
} else {
|
||||
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first";
|
||||
}
|
||||
|
||||
result = false;
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
|
||||
|
||||
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
|
||||
result.layer_a = ll->second.first;
|
||||
result.layer_b = ll->second.second;
|
||||
result.layout = xor_data.output_layout;
|
||||
result.top_cell = xor_data.output_cell;
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
|
||||
|
||||
db::RecursiveShapeIterator ri_a, ri_b;
|
||||
|
||||
if (ll->second.first >= 0) {
|
||||
ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first);
|
||||
ri_a.set_for_merged_input (true);
|
||||
}
|
||||
|
||||
if (ll->second.second >= 0) {
|
||||
ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second);
|
||||
ri_b.set_for_merged_input (true);
|
||||
}
|
||||
|
||||
db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu));
|
||||
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
|
||||
|
||||
db::Region xor_res;
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
}
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
|
||||
|
||||
db::LayerProperties lp = ll->first;
|
||||
if (lp.layer >= 0) {
|
||||
lp.layer += tol_index * xor_data.tolerance_bump;
|
||||
}
|
||||
|
||||
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
|
||||
result.layer_a = ll->second.first;
|
||||
result.layer_b = ll->second.second;
|
||||
result.layout = xor_data.output_layout;
|
||||
result.top_cell = xor_data.output_cell;
|
||||
|
||||
if (*t > db::epsilon) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
|
||||
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
|
||||
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
|
||||
}
|
||||
|
||||
if (xor_data.output_layout) {
|
||||
result.layer_output = result.layout->insert_layer (lp);
|
||||
xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output);
|
||||
} else {
|
||||
result.shape_count = xor_res.count ();
|
||||
}
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu));
|
||||
}
|
||||
|
||||
// Determines the output status
|
||||
job.start ();
|
||||
job.wait ();
|
||||
|
||||
// Determine the output status
|
||||
|
||||
bool result = (xor_data.layers_missing == 0);
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) {
|
||||
result = r->second.is_empty ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ TEST(1A_Flat)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 30\n"
|
||||
" 6/0 6/0 41\n"
|
||||
" 8/1 8/1 1\n"
|
||||
|
|
@ -146,8 +146,8 @@ TEST(1A_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 3\n"
|
||||
" 6/0 6/0 314\n"
|
||||
" 8/1 8/1 1\n"
|
||||
|
|
@ -177,7 +177,7 @@ TEST(1B_Flat)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" 6/0 - 41\n"
|
||||
" 8/1 - 1\n"
|
||||
|
|
@ -206,9 +206,9 @@ TEST(1B_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 3\n"
|
||||
" 6/0 - 314\n"
|
||||
" 8/1 - 1\n"
|
||||
" 10/0 - (no such layer in first layout)\n"
|
||||
|
|
@ -417,7 +417,7 @@ TEST(3_FlatCount)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 31\n"
|
||||
" 6/0 - 217\n"
|
||||
" 8/1 - 168\n"
|
||||
|
|
@ -483,7 +483,7 @@ TEST(3_FlatCountHeal)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" 6/0 - 41\n"
|
||||
" 8/1 - 1\n"
|
||||
|
|
@ -756,3 +756,42 @@ TEST(6_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(7_OptimizeDeep)
|
||||
{
|
||||
tl::CaptureChannel cap;
|
||||
|
||||
std::string input_a = tl::testdata ();
|
||||
input_a += "/bd/strmxor_covered1.gds";
|
||||
|
||||
std::string input_b = tl::testdata ();
|
||||
input_b += "/bd/strmxor_covered2.gds";
|
||||
|
||||
std::string au = tl::testdata ();
|
||||
au += "/bd/strmxor_au7d.oas";
|
||||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "-u", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
||||
db::Layout layout;
|
||||
|
||||
{
|
||||
tl::InputStream stream (output);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
|
||||
EXPECT_EQ (cap.captured_text (),
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 2/0 2/0 1\n"
|
||||
" 3/0 3/0 8\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1571,6 +1571,17 @@ struct array_iterator
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the iterator is a synthetic one
|
||||
*
|
||||
* "is_singular" is true, if the iterator was default-created or with a single
|
||||
* transformation.
|
||||
*/
|
||||
bool is_singular () const
|
||||
{
|
||||
return mp_base == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
trans_type m_trans;
|
||||
basic_array_iterator <Coord> *mp_base;
|
||||
|
|
|
|||
|
|
@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
std::vector<db::cell_index_type> &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int);
|
||||
std::vector<db::cell_index_type> new_cells_b;
|
||||
|
||||
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > all_a2b;
|
||||
for (std::vector<db::cell_index_type>::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) {
|
||||
auto m = m_b2a_mapping.find (*b);
|
||||
tl_assert (m != m_b2a_mapping.end ());
|
||||
all_a2b.push_back (std::make_pair (m->second, *b));
|
||||
}
|
||||
|
||||
std::set<db::cell_index_type> called_b;
|
||||
for (std::vector<db::cell_index_type>::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) {
|
||||
layout_b.cell (*i).collect_called_cells (called_b);
|
||||
|
|
@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b);
|
||||
new_cells.push_back (new_cell);
|
||||
new_cells_b.push_back (*b);
|
||||
all_a2b.push_back (std::make_pair (new_cell, *b));
|
||||
|
||||
if (mapped_pairs) {
|
||||
mapped_pairs->push_back (std::make_pair (*b, new_cell));
|
||||
|
|
@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
}
|
||||
}
|
||||
|
||||
if (! new_cells.empty ()) {
|
||||
if (all_a2b.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
|
||||
db::LayoutLocker locker (&layout_a);
|
||||
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
|
||||
db::LayoutLocker locker (&layout_a);
|
||||
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (size_t i = 0; i < new_cells.size (); ++i) {
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) {
|
||||
|
||||
const db::Cell &b = layout_b.cell (new_cells_b [i]);
|
||||
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
|
||||
const db::Cell &b = layout_b.cell (i->second);
|
||||
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
|
||||
|
||||
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
|
||||
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
|
||||
|
||||
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
|
||||
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
|
||||
|
||||
db::Instance bi = pb->child_inst ();
|
||||
db::Instance bi = pb->child_inst ();
|
||||
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (new_cells [i]);
|
||||
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
|
||||
|
||||
if (bi.has_prop_id ()) {
|
||||
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
|
||||
} else {
|
||||
pa.insert (bci);
|
||||
}
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (i->first);
|
||||
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
|
||||
|
||||
if (bi.has_prop_id ()) {
|
||||
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
|
||||
} else {
|
||||
pa.insert (bci);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,6 +311,11 @@ const db::EdgePair *DeepEdgePairs::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type DeepEdgePairs::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
|
||||
}
|
||||
|
||||
bool DeepEdgePairs::has_valid_edge_pairs () const
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public:
|
|||
virtual Box bbox () const;
|
||||
virtual bool empty () const;
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
virtual void apply_property_translator (const db::PropertiesTranslator &pt);
|
||||
|
|
|
|||
|
|
@ -438,6 +438,12 @@ DeepEdges::nth (size_t /*n*/) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat edge collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
DeepEdges::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
DeepEdges::has_valid_edges () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public:
|
|||
virtual bool is_merged () const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
|
|||
{
|
||||
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
|
||||
|
||||
if (empty ()) {
|
||||
|
||||
// Nothing to do
|
||||
return other.delegate ()->clone ();
|
||||
|
||||
} else if (other.empty ()) {
|
||||
if (other.empty ()) {
|
||||
|
||||
// Nothing to do
|
||||
return clone ();
|
||||
|
|
@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
|
|||
|
||||
return AsIfFlatRegion::xor_with (other, property_constraint);
|
||||
|
||||
} else if (empty ()) {
|
||||
|
||||
// Nothing to do, but to maintain the normal behavior, we have to map the other
|
||||
// input to our layout if neccessary
|
||||
if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) {
|
||||
return other.delegate ()->clone ();
|
||||
} else {
|
||||
std::unique_ptr<DeepRegion> other_deep_mapped (dynamic_cast<DeepRegion *> (clone ()));
|
||||
other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ());
|
||||
return other_deep_mapped.release ();
|
||||
}
|
||||
|
||||
} else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) {
|
||||
|
||||
return new DeepRegion (deep_layer ().derived ());
|
||||
|
|
|
|||
|
|
@ -332,6 +332,12 @@ const db::Text *DeepTexts::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat text collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
DeepTexts::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool DeepTexts::has_valid_texts () const
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public:
|
|||
virtual Box bbox () const;
|
||||
virtual bool empty () const;
|
||||
virtual const db::Text *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_texts () const;
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
virtual void apply_property_translator (const db::PropertiesTranslator &pt);
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@ public:
|
|||
/**
|
||||
* @brief Returns the nth edge pair
|
||||
*
|
||||
* This operation is available only for flat regions - i.e. such for which
|
||||
* This operation is available only for flat edge pair collections - i.e. such for which
|
||||
* "has_valid_edge_pairs" is true.
|
||||
*/
|
||||
const db::EdgePair *nth (size_t n) const
|
||||
|
|
@ -686,6 +686,17 @@ public:
|
|||
return mp_delegate->nth (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the nth edge pair's property ID
|
||||
*
|
||||
* This operation is available only for flat edge pair collections - i.e. such for which
|
||||
* "has_valid_edge_pairs" is true.
|
||||
*/
|
||||
db::properties_id_type nth_prop_id (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth_prop_id (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces flattening of the edge pair collection
|
||||
*
|
||||
|
|
|
|||
|
|
@ -273,6 +273,7 @@ public:
|
|||
virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const = 0;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
|
||||
virtual bool has_valid_edge_pairs () const = 0;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const = 0;
|
||||
|
|
|
|||
|
|
@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare
|
|||
{
|
||||
if (edge_xmax (a) < edge_xmin (b)) {
|
||||
return true;
|
||||
} else if (edge_xmin (a) >= edge_xmax (b)) {
|
||||
} else if (edge_xmin (a) > edge_xmax (b)) {
|
||||
return false;
|
||||
} else {
|
||||
C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2);
|
||||
|
|
|
|||
|
|
@ -1440,13 +1440,24 @@ public:
|
|||
/**
|
||||
* @brief Returns the nth edge
|
||||
*
|
||||
* This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true.
|
||||
* This operation is available only for flat edge collections - i.e. such for which "has_valid_edges" is true.
|
||||
*/
|
||||
const db::Edge *nth (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the nth edge's property ID
|
||||
*
|
||||
* This operation is available only for flat edge collections - i.e. such for which
|
||||
* "has_valid_edges" is true.
|
||||
*/
|
||||
db::properties_id_type nth_prop_id (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth_prop_id (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces flattening of the edge collection
|
||||
*
|
||||
|
|
|
|||
|
|
@ -280,6 +280,7 @@ public:
|
|||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> in_and_out (const Edges &) const = 0;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const = 0;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
|
||||
virtual bool has_valid_edges () const = 0;
|
||||
virtual bool has_valid_merged_edges () const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -250,8 +250,8 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start
|
|||
include_angle_start = true;
|
||||
}
|
||||
|
||||
m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ());
|
||||
m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ());
|
||||
m_t_start = db::ICplxTrans (1.0, angle_start, false, db::Vector ());
|
||||
m_t_end = db::ICplxTrans (1.0, angle_end, false, db::Vector ());
|
||||
|
||||
m_include_start = include_angle_start;
|
||||
m_include_end = include_angle_end;
|
||||
|
|
@ -266,10 +266,10 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start
|
|||
bool
|
||||
EdgeAngleChecker::check (const db::Vector &a, const db::Vector &b) const
|
||||
{
|
||||
db::DVector vout (b);
|
||||
db::Vector vout (b);
|
||||
|
||||
db::DVector v1 = m_t_start * a;
|
||||
db::DVector v2 = m_t_end * a;
|
||||
db::Vector v1 = m_t_start * a;
|
||||
db::Vector v2 = m_t_end * a;
|
||||
|
||||
int vps1 = db::vprod_sign (v1, vout);
|
||||
int vps2 = db::vprod_sign (v2, vout);
|
||||
|
|
@ -306,12 +306,14 @@ EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse, bool absol
|
|||
bool
|
||||
EdgeOrientationFilter::selected (const db::Edge &edge, db::properties_id_type) const
|
||||
{
|
||||
db::Vector en = db::Vector (std::max (edge.dx_abs (), edge.dy_abs ()), 0);
|
||||
|
||||
// NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded).
|
||||
// A horizontal edge has 0 degree, a vertical one has 90 degree.
|
||||
if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) {
|
||||
return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ());
|
||||
return m_checker (en, -edge.d ());
|
||||
} else {
|
||||
return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ());
|
||||
return m_checker (en, edge.d ());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -363,7 +365,7 @@ SpecialEdgeOrientationFilter::selected (const db::Edge &edge, properties_id_type
|
|||
}
|
||||
|
||||
db::Vector en, ev;
|
||||
en = db::Vector (edge.ortho_length (), 0);
|
||||
en = db::Vector (std::max (edge.dx_abs (), edge.dy_abs ()), 0);
|
||||
|
||||
// NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded).
|
||||
// A horizontal edge has 0 degree, a vertical one has 90 degree.
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
db::CplxTrans m_t_start, m_t_end;
|
||||
db::ICplxTrans m_t_start, m_t_end;
|
||||
bool m_include_start, m_include_end;
|
||||
bool m_big_angle, m_all;
|
||||
bool m_inverse, m_absolute;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public:
|
|||
virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); }
|
||||
|
||||
virtual const db::EdgePair *nth (size_t) const { tl_assert (false); }
|
||||
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_edge_pairs () const { return true; }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ public:
|
|||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> in_and_out (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
|
||||
|
||||
virtual const db::Edge *nth (size_t) const { tl_assert (false); }
|
||||
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_edges () const { return true; }
|
||||
virtual bool has_valid_merged_edges () const { return true; }
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public:
|
|||
virtual TextsDelegate *in (const Texts &, bool) const { return new EmptyTexts (); }
|
||||
|
||||
virtual const db::Text *nth (size_t) const { tl_assert (false); }
|
||||
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_texts () const { return true; }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
|
|
|||
|
|
@ -173,7 +173,46 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
|
|||
|
||||
const db::EdgePair *FlatEdgePairs::nth (size_t n) const
|
||||
{
|
||||
return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
// NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties
|
||||
|
||||
if (n >= mp_edge_pairs->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::EdgePair, db::unstable_layer_tag> &l = mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return &l.begin () [n];
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgePairWithProperties, db::unstable_layer_tag> &lp = mp_edge_pairs->get_layer<db::EdgePairWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return &lp.begin () [n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::properties_id_type FlatEdgePairs::nth_prop_id (size_t n) const
|
||||
{
|
||||
// NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties
|
||||
|
||||
if (n >= mp_edge_pairs->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::EdgePair, db::unstable_layer_tag> &l = mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return 0;
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgePairWithProperties, db::unstable_layer_tag> &lp = mp_edge_pairs->get_layer<db::EdgePairWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return lp.begin () [n].properties_id ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FlatEdgePairs::has_valid_edge_pairs () const
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public:
|
|||
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -361,7 +361,46 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other)
|
|||
|
||||
const db::Edge *FlatEdges::nth (size_t n) const
|
||||
{
|
||||
return n < mp_edges->size () ? &mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
// NOTE: this assumes that we iterate over non-property edges first and then over edges with properties
|
||||
|
||||
if (n >= mp_edges->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Edge, db::unstable_layer_tag> &l = mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return &l.begin () [n];
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgeWithProperties, db::unstable_layer_tag> &lp = mp_edges->get_layer<db::EdgeWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return &lp.begin () [n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::properties_id_type FlatEdges::nth_prop_id (size_t n) const
|
||||
{
|
||||
// NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties
|
||||
|
||||
if (n >= mp_edges->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Edge, db::unstable_layer_tag> &l = mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return 0;
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgeWithProperties, db::unstable_layer_tag> &lp = mp_edges->get_layer<db::EdgeWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return lp.begin () [n].properties_id ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FlatEdges::has_valid_edges () const
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public:
|
|||
virtual EdgesDelegate *add (const Edges &other) const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,46 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other)
|
|||
|
||||
const db::Text *FlatTexts::nth (size_t n) const
|
||||
{
|
||||
return n < mp_texts->size () ? &mp_texts->get_layer<db::Text, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
// NOTE: this assumes that we iterate over non-property texts first and then over texts with properties
|
||||
|
||||
if (n >= mp_texts->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Text, db::unstable_layer_tag> &l = mp_texts->get_layer<db::Text, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return &l.begin () [n];
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::TextWithProperties, db::unstable_layer_tag> &lp = mp_texts->get_layer<db::TextWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return &lp.begin () [n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::properties_id_type FlatTexts::nth_prop_id (size_t n) const
|
||||
{
|
||||
// NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties
|
||||
|
||||
if (n >= mp_texts->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Text, db::unstable_layer_tag> &l = mp_texts->get_layer<db::Text, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return 0;
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::TextWithProperties, db::unstable_layer_tag> &lp = mp_texts->get_layer<db::TextWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return lp.begin () [n].properties_id ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FlatTexts::has_valid_texts () const
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public:
|
|||
virtual TextsDelegate *add (const Texts &other) const;
|
||||
|
||||
virtual const db::Text *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_texts () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -152,15 +152,17 @@ static std::pair<bool, std::set<db::Box> > compute_clip_variant (const db::Box &
|
|||
}
|
||||
|
||||
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
|
||||
: mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
|
||||
{
|
||||
set_shape_receiver (pipe);
|
||||
reset ();
|
||||
}
|
||||
|
||||
HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
|
||||
: mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
|
||||
{
|
||||
set_shape_receiver (pipe);
|
||||
reset ();
|
||||
}
|
||||
|
||||
HierarchyBuilder::~HierarchyBuilder ()
|
||||
|
|
@ -178,6 +180,8 @@ void
|
|||
HierarchyBuilder::reset ()
|
||||
{
|
||||
m_initial_pass = true;
|
||||
m_cm_new_entry = false;
|
||||
|
||||
mp_initial_cell = 0;
|
||||
|
||||
m_cells_to_be_filled.clear ();
|
||||
|
|
@ -186,7 +190,6 @@ HierarchyBuilder::reset ()
|
|||
m_cells_seen.clear ();
|
||||
m_cell_stack.clear ();
|
||||
m_cm_entry = null_iterator;
|
||||
m_cm_new_entry = false;
|
||||
}
|
||||
|
||||
const std::pair<db::cell_index_type, std::string> &
|
||||
|
|
@ -351,7 +354,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
|
|||
if (! key.clip_region.empty ()) {
|
||||
cn += "$CLIP_VAR";
|
||||
description += "CLIP";
|
||||
|
||||
}
|
||||
if (key.inactive) {
|
||||
cn += "$DIS";
|
||||
|
|
@ -383,7 +385,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
|
|||
}
|
||||
|
||||
HierarchyBuilder::new_inst_mode
|
||||
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes)
|
||||
{
|
||||
if (all) {
|
||||
|
||||
|
|
@ -402,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
|
|||
}
|
||||
|
||||
// To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array.
|
||||
return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
|
||||
return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -413,7 +415,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
|
|||
}
|
||||
|
||||
bool
|
||||
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
if (all) {
|
||||
|
||||
|
|
@ -441,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
|
|||
}
|
||||
}
|
||||
|
||||
return (m_cells_seen.find (key) == m_cells_seen.end ());
|
||||
return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,8 +294,8 @@ public:
|
|||
virtual void end (const RecursiveShapeIterator *iter);
|
||||
virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell);
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all);
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all);
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes);
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes);
|
||||
virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "dbLayoutVsSchematic.h"
|
||||
#include "dbLayoutVsSchematicWriter.h"
|
||||
#include "dbLayoutVsSchematicReader.h"
|
||||
#include "dbNetlistCompareUtils.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -86,6 +87,53 @@ db::NetlistCrossReference *LayoutVsSchematic::make_cross_ref ()
|
|||
return mp_cross_ref.get ();
|
||||
}
|
||||
|
||||
bool
|
||||
LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit)
|
||||
{
|
||||
if (! mp_cross_ref.get ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db::NetlistCrossReference::PerCircuitData *pcd = const_cast<db::NetlistCrossReference::PerCircuitData *> (mp_cross_ref->per_circuit_data_for (std::make_pair (circuit, circuit)));
|
||||
if (! pcd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
bool any = false;
|
||||
|
||||
for (auto n = pcd->nets.begin (); n != pcd->nets.end (); ++n) {
|
||||
|
||||
const db::Net *schem = n->pair.second;
|
||||
const db::Net *layout = n->pair.first;
|
||||
|
||||
if (schem && layout && schem->begin_pins () != schem->end_pins ()) {
|
||||
|
||||
any = true;
|
||||
|
||||
if (db::name_compare (layout, schem) != 0) {
|
||||
|
||||
std::string msg = tl::sprintf (tl::to_string (tr ("Port mismatch '%s' vs. '%s'")), layout->expanded_name (), schem->expanded_name ());
|
||||
db::LogEntryData entry (db::Error, msg);
|
||||
pcd->log_entries.push_back (entry);
|
||||
|
||||
error = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! any) {
|
||||
|
||||
std::string msg = tl::to_string (tr ("No pins found in circuit during 'flag_missing_ports'"));
|
||||
db::LogEntryData entry (db::Warning, msg);
|
||||
pcd->log_entries.push_back (entry);
|
||||
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
void LayoutVsSchematic::save (const std::string &path, bool short_format)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -144,6 +144,22 @@ public:
|
|||
*/
|
||||
db::NetlistCrossReference *make_cross_ref ();
|
||||
|
||||
/**
|
||||
* @brief Checks top-level port names
|
||||
*
|
||||
* This method checks that every top-level pin has a corresponding
|
||||
* schematic pin and their names are equivalent. This verifies that
|
||||
* all pins are labelles properly.
|
||||
*
|
||||
* Errors are placed in the log file. The return values indicates
|
||||
* if there are no errors.
|
||||
*
|
||||
* The circuit is either a schematic or layout circuit.
|
||||
*
|
||||
* See issue #2055.
|
||||
*/
|
||||
bool flag_missing_ports (const db::Circuit *circuit);
|
||||
|
||||
/**
|
||||
* @brief Saves the database to the given path
|
||||
*
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db
|
|||
void PolygonRefToShapesGenerator::put (const db::Polygon &polygon)
|
||||
{
|
||||
tl::MutexLocker locker (&mp_layout->lock ());
|
||||
if (m_prop_id != 0) {
|
||||
if (polygon.is_empty ()) {
|
||||
// ignore empty polygons
|
||||
} else if (m_prop_id != 0) {
|
||||
mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id));
|
||||
} else {
|
||||
mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
|
||||
|
|
@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size
|
|||
void
|
||||
PolygonSplitter::put (const db::Polygon &poly)
|
||||
{
|
||||
if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
|
||||
if (poly.is_empty ()) {
|
||||
// ignore empty polygons
|
||||
} else if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
|
||||
|
||||
std::vector <db::Polygon> split_polygons;
|
||||
db::split_polygon (poly, split_polygons);
|
||||
|
|
|
|||
|
|
@ -190,6 +190,12 @@ OriginalLayerEdgePairs::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
OriginalLayerEdgePairs::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
OriginalLayerEdgePairs::has_valid_edge_pairs () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public:
|
|||
virtual bool empty () const;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -233,6 +233,12 @@ OriginalLayerEdges::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
OriginalLayerEdges::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
OriginalLayerEdges::has_valid_edges () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public:
|
|||
virtual bool is_merged () const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -190,6 +190,12 @@ OriginalLayerTexts::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
OriginalLayerTexts::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
OriginalLayerTexts::has_valid_texts () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public:
|
|||
virtual bool empty () const;
|
||||
|
||||
virtual const db::Text *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_texts () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -1771,6 +1771,14 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating that the polygon is an empty one
|
||||
*/
|
||||
bool is_empty () const
|
||||
{
|
||||
return m_ctrs.size () == size_t (1) && m_ctrs[0].size () == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of points in the polygon
|
||||
*/
|
||||
|
|
@ -1879,6 +1887,7 @@ public:
|
|||
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
|
||||
h->transform (db::unit_trans<C> (), true /*compress*/, remove_reflected);
|
||||
}
|
||||
m_bbox = m_ctrs [0].bbox ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -2804,6 +2813,7 @@ public:
|
|||
{
|
||||
// compress the polygon by employing the transform method
|
||||
m_hull.transform (db::unit_trans<C> (), true, remove_reflected);
|
||||
m_bbox = m_hull.bbox ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -3022,6 +3032,14 @@ public:
|
|||
return m_hull.is_halfmanhattan ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating that the polygon is an empty one
|
||||
*/
|
||||
bool is_empty () const
|
||||
{
|
||||
return m_hull.size () == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The number of holes
|
||||
*
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
|
|||
m_layer = d.m_layer;
|
||||
mp_cell = d.mp_cell;
|
||||
m_current_layer = d.m_current_layer;
|
||||
m_skip_shapes = d.m_skip_shapes;
|
||||
m_skip_shapes_member = d.m_skip_shapes_member;
|
||||
m_shape = d.m_shape;
|
||||
m_trans = d.m_trans;
|
||||
m_global_trans = d.m_global_trans;
|
||||
|
|
@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
|
|||
m_local_complex_region_stack = d.m_local_complex_region_stack;
|
||||
m_local_region_stack = d.m_local_region_stack;
|
||||
m_skip_shapes_stack = d.m_skip_shapes_stack;
|
||||
m_skip_shapes_member_stack = d.m_skip_shapes_member_stack;
|
||||
m_needs_reinit = d.m_needs_reinit;
|
||||
m_inst_quad_id = d.m_inst_quad_id;
|
||||
m_inst_quad_id_stack = d.m_inst_quad_id_stack;
|
||||
|
|
@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
|
|||
m_local_region_stack.push_back (m_global_trans.inverted () * m_region);
|
||||
m_skip_shapes_stack.clear ();
|
||||
m_skip_shapes_stack.push_back (false);
|
||||
m_skip_shapes_member_stack.clear ();
|
||||
m_skip_shapes_member_stack.push_back (false);
|
||||
|
||||
m_local_complex_region_stack.clear ();
|
||||
if (mp_complex_region.get ()) {
|
||||
|
|
@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
|
|||
bool
|
||||
RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
|
||||
{
|
||||
bool skip_shapes = false;
|
||||
|
||||
if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) {
|
||||
|
||||
// Try some optimization: if the instance we're looking at is entirely covered
|
||||
// by a rectangle (other objects are too expensive to check), then we skip it
|
||||
//
|
||||
// We check 10 shapes max.
|
||||
|
||||
box_type inst_bx;
|
||||
if (m_inst->size () == 1) {
|
||||
inst_bx = m_inst->bbox (m_box_convert);
|
||||
} else {
|
||||
inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ());
|
||||
}
|
||||
|
||||
unsigned int l = m_has_layers ? m_layers.front () : m_layer;
|
||||
auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
|
||||
size_t nmax = 10;
|
||||
while (! si.at_end () && nmax-- > 0) {
|
||||
if (inst_bx.inside (si->rectangle ())) {
|
||||
skip_shapes = true;
|
||||
break;
|
||||
}
|
||||
++si;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tl_assert (mp_layout);
|
||||
|
||||
m_trans_stack.push_back (m_trans);
|
||||
|
|
@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
|
|||
}
|
||||
|
||||
m_local_region_stack.push_back (new_region);
|
||||
m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes);
|
||||
m_skip_shapes_stack.push_back (m_skip_shapes);
|
||||
m_skip_shapes_member_stack.push_back (m_skip_shapes_member);
|
||||
|
||||
if (! m_local_complex_region_stack.empty ()) {
|
||||
|
||||
|
|
@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const
|
|||
m_inst = m_inst_iterators.back ();
|
||||
m_inst_array = m_inst_array_iterators.back ();
|
||||
m_inst_quad_id = m_inst_quad_id_stack.back ();
|
||||
m_skip_shapes = m_skip_shapes_stack.back ();
|
||||
m_skip_shapes_member = m_skip_shapes_member_stack.back ();
|
||||
m_inst_iterators.pop_back ();
|
||||
m_inst_array_iterators.pop_back ();
|
||||
m_inst_quad_id_stack.pop_back ();
|
||||
|
|
@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const
|
|||
m_cells.pop_back ();
|
||||
m_local_region_stack.pop_back ();
|
||||
m_skip_shapes_stack.pop_back ();
|
||||
m_skip_shapes_member_stack.pop_back ();
|
||||
if (! m_local_complex_region_stack.empty ()) {
|
||||
m_local_complex_region_stack.pop_back ();
|
||||
}
|
||||
|
|
@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const
|
|||
void
|
||||
RecursiveShapeIterator::new_layer () const
|
||||
{
|
||||
if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
|
||||
if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
|
||||
m_shape = shape_iterator ();
|
||||
} else if (! m_overlapping) {
|
||||
m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
|
||||
|
|
@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
|
|||
new_inst (receiver);
|
||||
}
|
||||
|
||||
bool
|
||||
RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const
|
||||
{
|
||||
// Try some optimization: if the instance we're looking at is entirely covered
|
||||
// by a rectangle (other objects are too expensive to check), then we skip it
|
||||
//
|
||||
// We check 10 shapes max.
|
||||
|
||||
auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
|
||||
size_t nmax = 10;
|
||||
while (! si.at_end () && nmax-- > 0) {
|
||||
if (inst_bx.inside (si->rectangle ())) {
|
||||
return true;
|
||||
}
|
||||
++si;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RecursiveShapeIterator::skip_shapes () const
|
||||
{
|
||||
return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back ();
|
||||
}
|
||||
|
||||
void
|
||||
RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
|
||||
{
|
||||
|
|
@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
|
|||
all_of_instance = m_local_complex_region_stack.empty ();
|
||||
}
|
||||
|
||||
m_skip_shapes = skip_shapes ();
|
||||
m_skip_shapes_member = false;
|
||||
|
||||
if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
|
||||
box_type inst_bx = m_inst->bbox (m_box_convert);
|
||||
m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer);
|
||||
}
|
||||
|
||||
RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all;
|
||||
if (receiver) {
|
||||
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance);
|
||||
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes);
|
||||
} else if (m_skip_shapes) {
|
||||
ni = RecursiveShapeReceiver::NI_skip;
|
||||
}
|
||||
|
||||
if (ni == RecursiveShapeReceiver::NI_skip) {
|
||||
|
|
@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
// skip instance array members not part of the complex region
|
||||
while (! m_inst_array.at_end ()) {
|
||||
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
if (! is_outside_complex_region (ia_box)) {
|
||||
break;
|
||||
} else {
|
||||
|
|
@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
}
|
||||
|
||||
while (! m_inst_array.at_end () && receiver) {
|
||||
if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) {
|
||||
break;
|
||||
} else {
|
||||
++m_inst_array;
|
||||
m_skip_shapes_member = false;
|
||||
|
||||
while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) {
|
||||
|
||||
m_skip_shapes_member = m_skip_shapes;
|
||||
if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
|
||||
|
||||
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer);
|
||||
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
if (receiver) {
|
||||
skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance (), m_skip_shapes_member);
|
||||
} else {
|
||||
skip = m_skip_shapes_member;
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
++m_inst_array;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -868,6 +868,7 @@ private:
|
|||
mutable unsigned int m_layer;
|
||||
mutable const cell_type *mp_cell;
|
||||
mutable size_t m_current_layer;
|
||||
mutable bool m_skip_shapes, m_skip_shapes_member;
|
||||
mutable shape_iterator m_shape;
|
||||
mutable cplx_trans_type m_trans;
|
||||
mutable std::vector<cplx_trans_type> m_trans_stack;
|
||||
|
|
@ -876,7 +877,7 @@ private:
|
|||
mutable std::vector<const cell_type *> m_cells;
|
||||
mutable std::vector<box_tree_type> m_local_complex_region_stack;
|
||||
mutable std::vector<box_type> m_local_region_stack;
|
||||
mutable std::vector<bool> m_skip_shapes_stack;
|
||||
mutable std::vector<bool> m_skip_shapes_stack, m_skip_shapes_member_stack;
|
||||
mutable bool m_needs_reinit;
|
||||
mutable size_t m_inst_quad_id;
|
||||
mutable std::vector<size_t> m_inst_quad_id_stack;
|
||||
|
|
@ -899,6 +900,8 @@ private:
|
|||
bool down (RecursiveShapeReceiver *receiver) const;
|
||||
void pop () const;
|
||||
|
||||
bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const;
|
||||
bool skip_shapes () const;
|
||||
bool is_outside_complex_region (const db::Box &box) const;
|
||||
|
||||
void set_inactive (bool a) const
|
||||
|
|
@ -1013,8 +1016,11 @@ public:
|
|||
* - NI_all: iterate all members through "new_inst_member"
|
||||
* - NI_single: iterate a single member (the first one)
|
||||
* - NI_skip: skips the whole array (not a single instance is iterated)
|
||||
*
|
||||
* The "skip_shapes" parameter indicates that the instance is visited with the
|
||||
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
|
||||
*/
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; }
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; }
|
||||
|
||||
/**
|
||||
* @brief Enters a new array member of the instance
|
||||
|
|
@ -1026,8 +1032,11 @@ public:
|
|||
* "all" is true, if an instance array is iterated in "all" mode (see new_inst).
|
||||
*
|
||||
* If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered.
|
||||
*
|
||||
* The "skip_shapes" parameter indicates that the instance member is visited with the
|
||||
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
|
||||
*/
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; }
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; }
|
||||
|
||||
/**
|
||||
* @brief Delivers a shape
|
||||
|
|
|
|||
|
|
@ -1618,18 +1618,34 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
|
|||
}
|
||||
}
|
||||
|
||||
db::polygon_ref_generator<TR> pr (layout, result);
|
||||
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
|
||||
|
||||
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
const TR &subject = interactions.subject_shape (i->first);
|
||||
if (others.find (subject) != others.end ()) {
|
||||
|
||||
// shortcut (and: keep, not: drop)
|
||||
// Note that we still normalize and split the polygon, so we get a uniform
|
||||
// behavior.
|
||||
if (m_is_and) {
|
||||
result.insert (subject);
|
||||
db::Polygon poly;
|
||||
subject.instantiate (poly);
|
||||
splitter.put (poly);
|
||||
}
|
||||
|
||||
} else if (i->second.empty ()) {
|
||||
|
||||
// shortcut (not: keep, and: drop)
|
||||
// Note that we still normalize and split the polygon, so we get a uniform
|
||||
// behavior.
|
||||
if (! m_is_and) {
|
||||
result.insert (subject);
|
||||
db::Polygon poly;
|
||||
subject.instantiate (poly);
|
||||
splitter.put (poly);
|
||||
}
|
||||
|
||||
} else {
|
||||
for (auto e = subject.begin_edge (); ! e.at_end(); ++e) {
|
||||
ep.insert (*e, p1);
|
||||
|
|
@ -1649,8 +1665,6 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
|
|||
}
|
||||
|
||||
db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
|
||||
db::polygon_ref_generator<TR> pr (layout, result);
|
||||
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
|
||||
db::PolygonGenerator pg (splitter, true, true);
|
||||
ep.set_base_verbosity (50);
|
||||
ep.process (pg, op);
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ public:
|
|||
/**
|
||||
* @brief Returns the nth text
|
||||
*
|
||||
* This operation is available only for flat regions - i.e. such for which
|
||||
* This operation is available only for flat text collections - i.e. such for which
|
||||
* "has_valid_texts" is true.
|
||||
*/
|
||||
const db::Text *nth (size_t n) const
|
||||
|
|
@ -503,6 +503,17 @@ public:
|
|||
return mp_delegate->nth (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the nth text's property ID
|
||||
*
|
||||
* This operation is available only for flat text collections - i.e. such for which
|
||||
* "has_valid_texts" is true.
|
||||
*/
|
||||
db::properties_id_type nth_prop_id (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth_prop_id (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces flattening of the text collection
|
||||
*
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public:
|
|||
virtual TextsDelegate *in (const Texts &other, bool invert) const = 0;
|
||||
|
||||
virtual const db::Text *nth (size_t n) const = 0;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
|
||||
virtual bool has_valid_texts () const = 0;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const = 0;
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ static db::DEdgePairWithProperties *new_dedge_pair_with_properties2 (const db::D
|
|||
return new db::DEdgePairWithProperties (edge_pair, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ())));
|
||||
}
|
||||
|
||||
Class<db::DEdgePairWithProperties> decl_DEdgePairWithProperties (decl_EdgePair, "db", "DEdgePairWithProperties",
|
||||
Class<db::DEdgePairWithProperties> decl_DEdgePairWithProperties (decl_DEdgePair, "db", "DEdgePairWithProperties",
|
||||
gsi::properties_support_methods<db::DEdgePairWithProperties> () +
|
||||
constructor ("new", &new_dedge_pair_with_properties, gsi::arg ("edge_pair"), gsi::arg ("properties_id", db::properties_id_type (0)),
|
||||
"@brief Creates a new object from a property-less object and a properties ID."
|
||||
|
|
|
|||
|
|
@ -783,6 +783,16 @@ static std::vector<db::EdgePairs> split_with_area2 (const db::EdgePairs *r, db::
|
|||
return as_2edge_pairs_vector (r->split_filter (f));
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n)
|
||||
{
|
||||
const db::EdgePair *ep = edge_pairs->nth (n);
|
||||
if (! ep) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::EdgePairWithProperties> begin_edge_pairs (const db::EdgePairs *edge_pairs)
|
||||
{
|
||||
return db::generic_shape_iterator<db::EdgePairWithProperties> (db::make_wp_iter (edge_pairs->delegate ()->begin ()));
|
||||
|
|
@ -1855,13 +1865,15 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
|
|||
"\n"
|
||||
"Starting with version 0.30, the iterator delivers EdgePairWithProperties objects."
|
||||
) +
|
||||
method ("[]", &db::EdgePairs::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth edge pair\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. "
|
||||
"those for which \\has_valid_edge_pairs? is true. Use \\flatten to explicitly flatten an edge pair collection.\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the edge pairs."
|
||||
"The \\each iterator is the more general approach to access the edge pairs.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns a \\EdgePairWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::EdgePairs::flatten,
|
||||
"@brief Explicitly flattens an edge pair collection\n"
|
||||
|
|
|
|||
|
|
@ -848,6 +848,16 @@ static std::vector<db::Edges> split_interacting_with_region (const db::Edges *r,
|
|||
return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count));
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::Edges *edges, size_t n)
|
||||
{
|
||||
const db::Edge *e = edges->nth (n);
|
||||
if (! e) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::EdgeWithProperties> begin_edges (const db::Edges *edges)
|
||||
{
|
||||
return db::generic_shape_iterator<db::EdgeWithProperties> (db::make_wp_iter (edges->delegate ()->begin ()));
|
||||
|
|
@ -2439,14 +2449,16 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"This method has been introduced in version 0.25."
|
||||
"Starting with version 0.30, the iterator delivers an EdgeWithProperties object."
|
||||
) +
|
||||
method ("[]", &db::Edges::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth edge of the collection\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat edge collections only - i.e. "
|
||||
"those for which \\has_valid_edges? is true. Use \\flatten to explicitly flatten an edge collection.\n"
|
||||
"This method returns the raw edge (not merged edges, even if merged semantics is enabled).\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the edges."
|
||||
"The \\each iterator is the more general approach to access the edges.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns an \\EdgeWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::Edges::flatten,
|
||||
"@brief Explicitly flattens an edge collection\n"
|
||||
|
|
|
|||
|
|
@ -110,6 +110,18 @@ Class<db::LayoutVsSchematic> decl_dbLayoutVsSchematic (decl_dbLayoutToNetlist, "
|
|||
"\n"
|
||||
"See \\NetlistCrossReference for more details.\n"
|
||||
) +
|
||||
gsi::method ("flag_missing_ports", &db::LayoutVsSchematic::flag_missing_ports, gsi::arg ("circuit"),
|
||||
"@brief Flags inconsistent port labels in the given circuit\n"
|
||||
"@param circuit Either a layout or schematic circuit\n"
|
||||
"@return True, if no errors were found\n"
|
||||
"This method will check all schematic nets which have pins and tests whether the corresponding layout net "
|
||||
"has the same name. This way, it is checked if the pins are properly labelled.\n"
|
||||
"\n"
|
||||
"The method must be called after the compare step was successful. Error messages will be added "
|
||||
"to the log entries. If an error occured or the cross reference is not value, 'false' is returned.\n"
|
||||
"\n"
|
||||
"This method was introduced in version 0.30.2."
|
||||
) +
|
||||
gsi::method_ext ("write_l2n", &save_l2n, gsi::arg ("path"), gsi::arg ("short_format", false),
|
||||
"@brief Writes the \\LayoutToNetlist part of the object to a file.\n"
|
||||
"This method employs the native format of KLayout.\n"
|
||||
|
|
|
|||
|
|
@ -1379,6 +1379,16 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector
|
|||
return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny);
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::Region *region, size_t n)
|
||||
{
|
||||
const db::Polygon *poly = region->nth (n);
|
||||
if (! poly) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::PolygonWithProperties (*poly, region->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::PolygonWithProperties> begin_region (const db::Region *region)
|
||||
{
|
||||
return db::generic_shape_iterator<db::PolygonWithProperties> (db::make_wp_iter (region->delegate ()->begin ()));
|
||||
|
|
@ -4103,14 +4113,16 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n"
|
||||
"Starting with version 0.30, the iterator delivers a RegionWithProperties object."
|
||||
) +
|
||||
method ("[]", &db::Region::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth polygon of the region\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat regions only - i.e. "
|
||||
"those for which \\has_valid_polygons? is true. Use \\flatten to explicitly flatten a region.\n"
|
||||
"This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the polygons."
|
||||
"The \\each iterator is the more general approach to access the polygons.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns a \\PolygonWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::Region::flatten,
|
||||
"@brief Explicitly flattens a region\n"
|
||||
|
|
|
|||
|
|
@ -455,6 +455,16 @@ static db::Region pull_interacting (const db::Texts *r, const db::Region &other)
|
|||
return out;
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::Texts *texts, size_t n)
|
||||
{
|
||||
const db::Text *t = texts->nth (n);
|
||||
if (! t) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::TextWithProperties (*t, texts->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::TextWithProperties> begin_texts (const db::Texts *texts)
|
||||
{
|
||||
return db::generic_shape_iterator<db::TextWithProperties> (db::make_wp_iter (texts->delegate ()->begin ()));
|
||||
|
|
@ -846,13 +856,15 @@ Class<db::Texts> decl_Texts (decl_dbShapeCollection, "db", "Texts",
|
|||
"\n"
|
||||
"Starting with version 0.30, the iterator delivers TextWithProperties objects."
|
||||
) +
|
||||
method ("[]", &db::Texts::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth text\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat texts only - i.e. "
|
||||
"those for which \\has_valid_texts? is true. Use \\flatten to explicitly flatten an text collection.\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the texts."
|
||||
"The \\each iterator is the more general approach to access the texts.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns a \\TextWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::Texts::flatten,
|
||||
"@brief Explicitly flattens an text collection\n"
|
||||
|
|
|
|||
|
|
@ -485,8 +485,41 @@ TEST(7)
|
|||
cib.push_back (b1.cell_index ());
|
||||
cib.push_back (b2.cell_index ());
|
||||
cm.create_multi_mapping_full (h, cib, *g, cia);
|
||||
EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5");
|
||||
EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5");
|
||||
|
||||
EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:");
|
||||
}
|
||||
|
||||
// Issue #2014
|
||||
TEST(8)
|
||||
{
|
||||
std::unique_ptr<db::Layout> g (new db::Layout ());
|
||||
db::Cell &a (g->cell (g->add_cell ("a")));
|
||||
db::Cell &b (g->cell (g->add_cell ("b")));
|
||||
db::Cell &b1 (g->cell (g->add_cell ("b1")));
|
||||
db::Cell &b2 (g->cell (g->add_cell ("b2")));
|
||||
db::Cell &c (g->cell (g->add_cell ("c")));
|
||||
|
||||
b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ()));
|
||||
b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ()));
|
||||
b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ()));
|
||||
b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ()));
|
||||
|
||||
db::Layout h;
|
||||
db::Cell &ha (h.cell (h.add_cell ("a")));
|
||||
db::Cell &hb (h.cell (h.add_cell ("b")));
|
||||
db::Cell &hc (h.cell (h.add_cell ("c")));
|
||||
|
||||
db::CellMapping cm;
|
||||
std::vector<db::cell_index_type> cib, cia;
|
||||
cia.push_back (a.cell_index ());
|
||||
cia.push_back (b.cell_index ());
|
||||
cia.push_back (c.cell_index ());
|
||||
cib.push_back (ha.cell_index ());
|
||||
cib.push_back (hb.cell_index ());
|
||||
cib.push_back (hc.cell_index ());
|
||||
cm.create_multi_mapping_full (h, cib, *g, cia);
|
||||
EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c");
|
||||
|
||||
EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,3 +279,24 @@ TEST(6_add_with_properties)
|
|||
EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}");
|
||||
}
|
||||
|
||||
TEST(7_properties)
|
||||
{
|
||||
db::PropertiesSet ps;
|
||||
|
||||
ps.insert (tl::Variant ("id"), 1);
|
||||
db::properties_id_type pid1 = db::properties_id (ps);
|
||||
|
||||
db::EdgePairs edge_pairs;
|
||||
db::Edge e1 (db::Point (0, 0), db::Point (10, 20));
|
||||
db::Edge e2 (db::Point (1, 2), db::Point (11, 22));
|
||||
edge_pairs.insert (db::EdgePairWithProperties (db::EdgePair (e1, e2), pid1));
|
||||
edge_pairs.insert (db::EdgePair (e1, e2));
|
||||
|
||||
EXPECT_EQ (edge_pairs.nth (0)->to_string (), "(0,0;10,20)/(1,2;11,22)");
|
||||
EXPECT_EQ (edge_pairs.nth (1)->to_string (), "(0,0;10,20)/(1,2;11,22)");
|
||||
EXPECT_EQ (edge_pairs.nth (2) == 0, true);
|
||||
|
||||
EXPECT_EQ (edge_pairs.nth_prop_id (0), db::properties_id_type (0));
|
||||
EXPECT_EQ (edge_pairs.nth_prop_id (1), pid1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,37 @@ TEST(4)
|
|||
db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false, false);
|
||||
EXPECT_EQ (r.filtered (f1).to_string (), "");
|
||||
}
|
||||
|
||||
// issue-2060
|
||||
{
|
||||
db::EdgeOrientationFilter f1 (90.0, true, false);
|
||||
db::EdgeOrientationFilter f2 (90.0, false, false);
|
||||
db::EdgeOrientationFilter f45 (45.0, false, false);
|
||||
db::SpecialEdgeOrientationFilter fs (db::SpecialEdgeOrientationFilter::Diagonal, false);
|
||||
|
||||
db::Edges rr;
|
||||
rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 4000000)));
|
||||
EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,4000000;1000,4000000)"), true);
|
||||
|
||||
rr.clear ();
|
||||
rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 400000)));
|
||||
EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,400000;1000,400000)"), true);
|
||||
|
||||
rr.clear ();
|
||||
rr.insert (db::Box (db::Point (0, -1000000000), db::Point (1000, 1000000000)));
|
||||
EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,-1000000000;0,-1000000000);(0,1000000000;1000,1000000000)"), true);
|
||||
|
||||
rr.clear ();
|
||||
rr.insert (db::Box (db::Point (0, -1000000000), db::Point (1000, 1000000000)));
|
||||
EXPECT_EQ (db::compare (rr.filtered (f2), "(0,-1000000000;0,1000000000);(1000,1000000000;1000,-1000000000)"), true);
|
||||
|
||||
EXPECT_EQ (f2.selected (db::Edge (db::Point (0, -1000000000), db::Point (0, 1000000000)), size_t (0)), true);
|
||||
EXPECT_EQ (f2.selected (db::Edge (db::Point (0, -1000000000), db::Point (1, 1000000000)), size_t (0)), false);
|
||||
EXPECT_EQ (f45.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000000)), size_t (0)), true);
|
||||
EXPECT_EQ (f45.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000001)), size_t (0)), false);
|
||||
EXPECT_EQ (fs.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000000)), size_t (0)), true);
|
||||
EXPECT_EQ (fs.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000001)), size_t (0)), false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(5)
|
||||
|
|
@ -1443,6 +1474,25 @@ TEST(32_add_with_properties)
|
|||
EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;40,60){net=>17};(-10,20;20,60){net=>17}");
|
||||
}
|
||||
|
||||
TEST(33_properties)
|
||||
{
|
||||
db::PropertiesSet ps;
|
||||
|
||||
ps.insert (tl::Variant ("id"), 1);
|
||||
db::properties_id_type pid1 = db::properties_id (ps);
|
||||
|
||||
db::Edges edges;
|
||||
edges.insert (db::EdgeWithProperties (db::Edge (db::Point (0, 0), db::Point (10, 20)), pid1));
|
||||
edges.insert (db::Edge (db::Point (0, 0), db::Point (10, 20)));
|
||||
|
||||
EXPECT_EQ (edges.nth (0)->to_string (), "(0,0;10,20)");
|
||||
EXPECT_EQ (edges.nth (1)->to_string (), "(0,0;10,20)");
|
||||
EXPECT_EQ (edges.nth (2) == 0, true);
|
||||
|
||||
EXPECT_EQ (edges.nth_prop_id (0), db::properties_id_type (0));
|
||||
EXPECT_EQ (edges.nth_prop_id (1), pid1);
|
||||
}
|
||||
|
||||
// GitHub issue #72 (Edges/Region NOT issue)
|
||||
TEST(100)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ TEST(1)
|
|||
|
||||
EXPECT_EQ (empty == p, true);
|
||||
EXPECT_EQ (p.is_box (), false);
|
||||
EXPECT_EQ (p.is_empty (), true);
|
||||
|
||||
std::vector <db::Point> c1, c2, c3;
|
||||
c1.push_back (db::Point (0, 0));
|
||||
|
|
@ -76,6 +77,7 @@ TEST(1)
|
|||
c1.push_back (db::Point (100, 1000));
|
||||
c1.push_back (db::Point (100, 0));
|
||||
p.assign_hull (c1.begin (), c1.end ());
|
||||
EXPECT_EQ (p.is_empty (), false);
|
||||
b = p.box ();
|
||||
EXPECT_EQ (p.holes (), size_t (0));
|
||||
EXPECT_EQ (p.area (), 1000*100);
|
||||
|
|
@ -1404,3 +1406,30 @@ TEST(28)
|
|||
db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000));
|
||||
EXPECT_EQ (b.perimeter (), 8000000000.0);
|
||||
}
|
||||
|
||||
TEST(29)
|
||||
{
|
||||
// Degenerated boxes and compress
|
||||
|
||||
db::Polygon b (db::Box (10, 20, 10, 20));
|
||||
EXPECT_EQ (b.is_empty (), false);
|
||||
EXPECT_EQ (b == db::Polygon (), false);
|
||||
EXPECT_EQ (b.to_string (), "(10,20;10,20;10,20;10,20)");
|
||||
EXPECT_EQ (double (b.area ()), 0.0);
|
||||
|
||||
b.compress (true);
|
||||
|
||||
EXPECT_EQ (b.is_empty (), true);
|
||||
EXPECT_EQ (b == db::Polygon (), true);
|
||||
|
||||
db::SimplePolygon sb (db::Box (10, 20, 10, 20));
|
||||
EXPECT_EQ (sb.is_empty (), false);
|
||||
EXPECT_EQ (sb == db::SimplePolygon (), false);
|
||||
EXPECT_EQ (sb.to_string (), "(10,20;10,20;10,20;10,20)");
|
||||
EXPECT_EQ (double (sb.area ()), 0.0);
|
||||
|
||||
sb.compress (true);
|
||||
|
||||
EXPECT_EQ (sb.is_empty (), true);
|
||||
EXPECT_EQ (sb == db::SimplePolygon (), true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -756,15 +756,25 @@ namespace {
|
|||
: public db::RecursiveShapeReceiver
|
||||
{
|
||||
public:
|
||||
FlatPusher (std::set<db::Box> *boxes) : mp_boxes (boxes) { }
|
||||
FlatPusher (std::set<db::Box> *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { }
|
||||
|
||||
void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
|
||||
{
|
||||
mp_boxes->insert (trans * shape.bbox ());
|
||||
}
|
||||
|
||||
std::string to_string () const
|
||||
{
|
||||
std::vector<std::string> s;
|
||||
for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) {
|
||||
s.push_back (i->to_string ());
|
||||
}
|
||||
return tl::join (s.begin (), s.end (), ";");
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<db::Box> *mp_boxes;
|
||||
std::set<db::Box> m_boxes;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1038,7 +1048,7 @@ public:
|
|||
m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n";
|
||||
}
|
||||
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
|
||||
{
|
||||
m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ());
|
||||
if (all) {
|
||||
|
|
@ -1048,7 +1058,7 @@ public:
|
|||
return NI_all;
|
||||
}
|
||||
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
|
||||
{
|
||||
m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans);
|
||||
if (all) {
|
||||
|
|
@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray
|
|||
public:
|
||||
ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { }
|
||||
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all);
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
|
||||
return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip;
|
||||
}
|
||||
|
||||
|
|
@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne
|
|||
public:
|
||||
ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { }
|
||||
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all);
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
|
||||
return inst.object ().cell_index () != m_rejected ? NI_all : NI_single;
|
||||
}
|
||||
|
||||
|
|
@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance
|
|||
public:
|
||||
ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { }
|
||||
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all);
|
||||
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes);
|
||||
return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected;
|
||||
}
|
||||
|
||||
|
|
@ -1586,49 +1596,174 @@ TEST(12_ForMerged)
|
|||
db::RecursiveShapeIterator i1 (*g, c0, 0);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
std::vector<unsigned int> lv;
|
||||
lv.push_back (0);
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, lv);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
lv.push_back (1); // empty, but kills "for merged" optimization
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, lv);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
// no longer optimized
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
|
||||
}
|
||||
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50));
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)");
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
|
||||
}
|
||||
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50));
|
||||
i1.set_overlapping (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(12b_ForMerged)
|
||||
{
|
||||
std::unique_ptr<db::Layout> g (new db::Layout ());
|
||||
g->insert_layer (0);
|
||||
g->insert_layer (1);
|
||||
db::Cell &c0 (g->cell (g->add_cell ()));
|
||||
db::Cell &c1 (g->cell (g->add_cell ()));
|
||||
|
||||
db::Box b (0, 100, 1000, 1200);
|
||||
c0.shapes (0).insert (db::Box (0, 0, 3000, 2200));
|
||||
c1.shapes (0).insert (b);
|
||||
|
||||
db::Trans tt;
|
||||
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt));
|
||||
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l));
|
||||
|
||||
std::string x;
|
||||
|
||||
db::RecursiveShapeIterator i1 (*g, c0, 0);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (false);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000))));
|
||||
|
||||
db::RecursiveShapeIterator i2 (*g, c0, 0);
|
||||
|
||||
x = collect(i2, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
|
||||
EXPECT_EQ (collect_with_copy(i2, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i2.reset ();
|
||||
i2.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
|
||||
i2.set_for_merged_input (true);
|
||||
x = collect(i2, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
|
||||
EXPECT_EQ (collect_with_copy(i2, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i2.reset ();
|
||||
i2.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(13_ForMergedPerformance)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -307,3 +307,22 @@ TEST(9_polygons)
|
|||
EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19){17=>ABC};(-11,-21;-11,-19;-9,-19;-9,-21){17=>XZY}");
|
||||
}
|
||||
|
||||
TEST(10_properties)
|
||||
{
|
||||
db::PropertiesSet ps;
|
||||
|
||||
ps.insert (tl::Variant ("id"), 1);
|
||||
db::properties_id_type pid1 = db::properties_id (ps);
|
||||
|
||||
db::Texts texts;
|
||||
texts.insert (db::TextWithProperties (db::Text ("string", db::Trans ()), pid1));
|
||||
texts.insert (db::Text ("abc", db::Trans ()));
|
||||
|
||||
EXPECT_EQ (texts.nth (0)->to_string (), "('abc',r0 0,0)");
|
||||
EXPECT_EQ (texts.nth (1)->to_string (), "('string',r0 0,0)");
|
||||
EXPECT_EQ (texts.nth (2) == 0, true);
|
||||
|
||||
EXPECT_EQ (texts.nth_prop_id (0), db::properties_id_type (0));
|
||||
EXPECT_EQ (texts.nth_prop_id (1), pid1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,15 @@ See <a href="/about/lvs_ref_netter.xml#enable_parameter">Netter#enable_parameter
|
|||
<p>
|
||||
See <a href="/about/lvs_ref_netter.xml#equivalent_pins">Netter#equivalent_pins</a> for a description of that function.
|
||||
</p>
|
||||
<a name="flag_missing_ports"/><h2>"flag_missing_ports" - Checks if all top level ports are properly labelled</h2>
|
||||
<keyword name="flag_missing_ports"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>flag_missing_ports</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
See <a href="/about/lvs_ref_netter.xml#flag_missing_ports">Netter#flag_missing_ports</a> for a description of that function.
|
||||
</p>
|
||||
<a name="ignore_parameter"/><h2>"ignore_parameter" - Specifies whether to ignore a parameter from a given device class for the compare</h2>
|
||||
<keyword name="ignore_parameter"/>
|
||||
<p>Usage:</p>
|
||||
|
|
|
|||
|
|
@ -186,6 +186,27 @@ case pin names for SPICE netlists.
|
|||
</p><p>
|
||||
Use this method andwhere in the script before the <a href="#compare">compare</a> call.
|
||||
</p>
|
||||
<a name="flag_missing_ports"/><h2>"flag_missing_ports" - Flags inconsistently labelled or missing ports in the current top circuit</h2>
|
||||
<keyword name="flag_missing_ports"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>flag_missing_ports</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
This method must be called after "compare" was executed successfully and will
|
||||
report errors if pins in the current top circuit's schematic are not labelled
|
||||
correspondingly in the layout. This prevents swapping of port labels or
|
||||
pads.
|
||||
</p><p>
|
||||
<pre>
|
||||
success = compare
|
||||
success && flag_missing_ports
|
||||
</pre>
|
||||
</p><p>
|
||||
Note that in order to use this method, the top circuit from the schematic netlist
|
||||
needs to have pins. This may not be always the case - for example, if the top
|
||||
level circuit is not a subcircuit in a Spice netlist.
|
||||
</p>
|
||||
<a name="ignore_parameter"/><h2>"ignore_parameter" - Skip a specific parameter for a given device class name during device compare</h2>
|
||||
<keyword name="ignore_parameter"/>
|
||||
<p>Usage:</p>
|
||||
|
|
|
|||
|
|
@ -296,6 +296,69 @@ tolerance("NMOS", "L", :absolute => 0.05, :relative => 0.01)</pre>
|
|||
|
||||
<pre>min_caps(1e-16)</pre>
|
||||
|
||||
<h2>Checking pin labels</h2>
|
||||
|
||||
<p>
|
||||
LVS is basically name-agnostic, so except for resolving ambiguities, net names are
|
||||
not considered. Topology matching has priority - if nets are not labelled
|
||||
properly, LVS by default does not care.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This may have adverse effects in the case of outbound connections - for example
|
||||
pads. It's a fatal error to connect the chip pads incorrectly. To mitigate this
|
||||
issue, the "flag_missing_ports" function is provided.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You need to call this function after the compare step, i.e.
|
||||
</p>
|
||||
|
||||
<pre>compare
|
||||
flag_missing_ports</pre>
|
||||
|
||||
<p>
|
||||
Or, if you want to quench pseudo errors, only in case of successful compare:
|
||||
</p>
|
||||
|
||||
<pre>success = compare
|
||||
success && flag_missing_ports</pre>
|
||||
|
||||
<p>
|
||||
This function takes the schematic top circuit and investigates all
|
||||
nets that are connected to a pin. It will check the name (label) of the
|
||||
corresponding layout net and if names do not match, an error is written
|
||||
into the log section of the LVS report.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When you use this feature while working yourself bottom-up in the design,
|
||||
it will make sure that all pins are properly labelled. If you use pins
|
||||
in the top level circuit to describe the chip pads, this feature will make
|
||||
sure that the correct nets are connected to the pads with the corresponding labels
|
||||
on them.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that it is possible to have SPICE netlists which do not have pins
|
||||
at the top level circuit - e.g. if the top level circuit is not a SUBCKT.
|
||||
In that case, the function will not report errors as there are not pin-carrying
|
||||
nets. Only a warning is issues saying that no top level pins have been found.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can use
|
||||
</p>
|
||||
|
||||
<pre>schematic.make_top_level_pins</pre>
|
||||
|
||||
<p>
|
||||
to create pins if none are provided. However, this method will turn every net into a pin
|
||||
and force you to label every net in the top circuit then.
|
||||
Hence, it is better to provide pins inside the schematic netlist.
|
||||
Also note, that "make_top_level_pins" is implicitly included in "schematic.simplify".
|
||||
</p>
|
||||
|
||||
<h2>Compare and netlist hierarchy</h2>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -1585,7 +1585,8 @@ module DRC
|
|||
self._context("report") do
|
||||
|
||||
# finish what we got so far
|
||||
_finish(false)
|
||||
view = RBA::LayoutView::current
|
||||
@def_output && @def_output.finish(false, view)
|
||||
|
||||
@def_output = nil
|
||||
@def_output = _make_report(description, filename, cellname)
|
||||
|
|
|
|||
|
|
@ -1694,6 +1694,16 @@ TEST(93d_withAngle)
|
|||
run_test (_this, "93", true);
|
||||
}
|
||||
|
||||
TEST(94_texts_in_region_xor)
|
||||
{
|
||||
run_test (_this, "94", false);
|
||||
}
|
||||
|
||||
TEST(94d_texts_in_region_xor)
|
||||
{
|
||||
run_test (_this, "94", true);
|
||||
}
|
||||
|
||||
TEST(100_edge_interaction_with_count)
|
||||
{
|
||||
run_test (_this, "100", false);
|
||||
|
|
|
|||
|
|
@ -925,7 +925,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative)
|
|||
}
|
||||
|
||||
void
|
||||
InstPropertiesPage::apply ()
|
||||
InstPropertiesPage::apply (bool /*commit*/)
|
||||
{
|
||||
do_apply (true, false);
|
||||
}
|
||||
|
|
@ -937,7 +937,7 @@ InstPropertiesPage::can_apply_to_all () const
|
|||
}
|
||||
|
||||
void
|
||||
InstPropertiesPage::apply_to_all (bool relative)
|
||||
InstPropertiesPage::apply_to_all (bool relative, bool /*commit*/)
|
||||
{
|
||||
do_apply (false, relative);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ protected:
|
|||
edt::PCellParametersPage *mp_pcell_parameters;
|
||||
|
||||
virtual bool readonly ();
|
||||
virtual void apply ();
|
||||
virtual void apply_to_all (bool relative);
|
||||
virtual void apply (bool commit);
|
||||
virtual void apply_to_all (bool relative, bool commit);
|
||||
virtual bool can_apply_to_all () const;
|
||||
void do_apply (bool current_only, bool relative);
|
||||
virtual ChangeApplicator *create_applicator (db::Cell &cell, const db::Instance &inst, double dbu);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,31 @@
|
|||
namespace edt
|
||||
{
|
||||
|
||||
static std::string variant_list_to_string (const tl::Variant &value)
|
||||
{
|
||||
if (! value.is_list ()) {
|
||||
tl::Variant v = tl::Variant::empty_list ();
|
||||
v.push (value);
|
||||
return v.to_parsable_string ();
|
||||
}
|
||||
|
||||
for (auto i = value.begin (); i != value.end (); ++i) {
|
||||
if (! i->is_a_string () || std::string (i->to_string ()).find (",") != std::string::npos) {
|
||||
return value.to_parsable_string ();
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise we can plainly combine the strings with ","
|
||||
std::string res;
|
||||
for (auto i = value.begin (); i != value.end (); ++i) {
|
||||
if (i != value.begin ()) {
|
||||
res += ",";
|
||||
}
|
||||
res += i->to_string ();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, const tl::Variant &value)
|
||||
{
|
||||
if (p.get_choices ().empty ()) {
|
||||
|
|
@ -91,7 +116,7 @@ static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget,
|
|||
QLineEdit *le = dynamic_cast<QLineEdit *> (widget);
|
||||
if (le) {
|
||||
le->blockSignals (true);
|
||||
le->setText (value.to_qstring ());
|
||||
le->setText (tl::to_qstring (variant_list_to_string (value)));
|
||||
le->blockSignals (false);
|
||||
}
|
||||
}
|
||||
|
|
@ -905,8 +930,29 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool
|
|||
{
|
||||
QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
|
||||
if (le) {
|
||||
std::vector<std::string> values = tl::split (tl::to_string (le->text ()), ",");
|
||||
|
||||
std::string s = tl::to_string (le->text ());
|
||||
|
||||
// try parsing a bracketed expression
|
||||
tl::Extractor ex (s.c_str ());
|
||||
if (*ex.skip () == '(') {
|
||||
tl::Variant v;
|
||||
try {
|
||||
ex.read (v);
|
||||
ps.set_value (v);
|
||||
break;
|
||||
} catch (...) {
|
||||
// ignore errors
|
||||
}
|
||||
} else if (ex.at_end ()) {
|
||||
ps.set_value (tl::Variant::empty_list ());
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise: plain splitting at comma
|
||||
std::vector<std::string> values = tl::split (s, ",");
|
||||
ps.set_value (tl::Variant (values.begin (), values.end ()));
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector<lay::ObjectInst
|
|||
}
|
||||
|
||||
void
|
||||
ShapePropertiesPage::do_apply (bool current_only, bool relative)
|
||||
ShapePropertiesPage::do_apply (bool current_only, bool relative, bool commit)
|
||||
{
|
||||
if (m_indexes.empty ()) {
|
||||
return;
|
||||
|
|
@ -321,7 +321,7 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative)
|
|||
}
|
||||
|
||||
// handle the case of guiding shape updates
|
||||
std::pair<bool, lay::ObjectInstPath> gs = mp_service->handle_guiding_shape_changes (new_sel[index]);
|
||||
std::pair<bool, lay::ObjectInstPath> gs = mp_service->handle_guiding_shape_changes (new_sel[index], commit);
|
||||
if (gs.first) {
|
||||
|
||||
new_sel[index] = gs.second;
|
||||
|
|
@ -350,9 +350,9 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative)
|
|||
}
|
||||
|
||||
void
|
||||
ShapePropertiesPage::apply ()
|
||||
ShapePropertiesPage::apply (bool commit)
|
||||
{
|
||||
do_apply (true, false);
|
||||
do_apply (true, false, commit);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -362,9 +362,9 @@ ShapePropertiesPage::can_apply_to_all () const
|
|||
}
|
||||
|
||||
void
|
||||
ShapePropertiesPage::apply_to_all (bool relative)
|
||||
ShapePropertiesPage::apply_to_all (bool relative, bool commit)
|
||||
{
|
||||
do_apply (false, relative);
|
||||
do_apply (false, relative, commit);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ protected:
|
|||
|
||||
private:
|
||||
virtual void update ();
|
||||
virtual void apply ();
|
||||
virtual void apply_to_all (bool relative);
|
||||
virtual void apply (bool commit);
|
||||
virtual void apply_to_all (bool relative, bool commit);
|
||||
virtual bool can_apply_to_all () const;
|
||||
virtual void do_apply (bool current_only, bool relative);
|
||||
virtual void do_apply (bool current_only, bool relative, bool commit);
|
||||
void recompute_selection_ptrs (const std::vector<lay::ObjectInstPath> &new_sel);
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter
|
|||
m_snap_to_objects (true),
|
||||
m_snap_objects_to_grid (true),
|
||||
m_top_level_sel (false), m_show_shapes_of_instances (true), m_max_shapes_of_instances (1000),
|
||||
m_pcell_lazy_evaluation (0),
|
||||
m_hier_copy_mode (-1),
|
||||
m_indicate_secondary_selection (false),
|
||||
m_seq (0),
|
||||
|
|
@ -391,6 +392,10 @@ Service::configure (const std::string &name, const std::string &value)
|
|||
tl::from_string (value, m_hier_copy_mode);
|
||||
service_configuration_changed ();
|
||||
|
||||
} else if (name == cfg_edit_pcell_lazy_eval_mode) {
|
||||
|
||||
tl::from_string (value, m_pcell_lazy_evaluation);
|
||||
|
||||
} else {
|
||||
lay::EditorServiceBase::configure (name, value);
|
||||
}
|
||||
|
|
@ -598,7 +603,7 @@ Service::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type ac)
|
|||
transform (db::DCplxTrans (m_move_trans));
|
||||
move_cancel (); // formally this functionality fits here
|
||||
// accept changes to guiding shapes
|
||||
handle_guiding_shape_changes ();
|
||||
handle_guiding_shape_changes (true);
|
||||
}
|
||||
m_alt_ac = lay::AC_Global;
|
||||
}
|
||||
|
|
@ -846,7 +851,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
|
|||
|
||||
}
|
||||
|
||||
handle_guiding_shape_changes ();
|
||||
handle_guiding_shape_changes (true);
|
||||
selection_to_view ();
|
||||
}
|
||||
|
||||
|
|
@ -1860,7 +1865,7 @@ Service::add_selection (const lay::ObjectInstPath &sel)
|
|||
}
|
||||
|
||||
std::pair<bool, lay::ObjectInstPath>
|
||||
Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const
|
||||
Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool commit) const
|
||||
{
|
||||
unsigned int cv_index = obj.cv_index ();
|
||||
lay::CellView cv = view ()->cellview (cv_index);
|
||||
|
|
@ -1874,10 +1879,22 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const
|
|||
return std::make_pair (false, lay::ObjectInstPath ());
|
||||
}
|
||||
|
||||
if (! layout->is_pcell_instance (obj.cell_index ()).first) {
|
||||
auto pcell_decl = layout->pcell_declaration_for_pcell_variant (obj.cell_index ());
|
||||
if (! pcell_decl) {
|
||||
return std::make_pair (false, lay::ObjectInstPath ());
|
||||
}
|
||||
|
||||
// Don't update unless we're committing or not in lazy PCell update mode
|
||||
if (! commit) {
|
||||
if (m_pcell_lazy_evaluation < 0) {
|
||||
if (pcell_decl->wants_lazy_evaluation ()) {
|
||||
return std::make_pair (false, lay::ObjectInstPath ());
|
||||
}
|
||||
} else if (m_pcell_lazy_evaluation > 0) {
|
||||
return std::make_pair (false, lay::ObjectInstPath ());
|
||||
}
|
||||
}
|
||||
|
||||
db::cell_index_type top_cell = std::numeric_limits<db::cell_index_type>::max ();
|
||||
db::cell_index_type parent_cell = std::numeric_limits<db::cell_index_type>::max ();
|
||||
db::Instance parent_inst;
|
||||
|
|
@ -1944,7 +1961,7 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const
|
|||
}
|
||||
|
||||
bool
|
||||
Service::handle_guiding_shape_changes ()
|
||||
Service::handle_guiding_shape_changes (bool commit)
|
||||
{
|
||||
EditableSelectionIterator s = begin_selection ();
|
||||
|
||||
|
|
@ -1953,7 +1970,7 @@ Service::handle_guiding_shape_changes ()
|
|||
return false;
|
||||
}
|
||||
|
||||
std::pair<bool, lay::ObjectInstPath> gs = handle_guiding_shape_changes (*s);
|
||||
std::pair<bool, lay::ObjectInstPath> gs = handle_guiding_shape_changes (*s, commit);
|
||||
if (gs.first) {
|
||||
|
||||
// remove superfluous proxies
|
||||
|
|
|
|||
|
|
@ -398,17 +398,19 @@ public:
|
|||
*
|
||||
* @return true, if PCells have been updated, indicating that our selection is no longer valid
|
||||
*
|
||||
* @param commit If true, changes are "final" (and PCells are updated also in lazy evaluation mode)
|
||||
*
|
||||
* This version assumes there is only one guiding shape selected and will update the selection.
|
||||
* It will also call layout.cleanup() if required.
|
||||
*/
|
||||
bool handle_guiding_shape_changes ();
|
||||
bool handle_guiding_shape_changes (bool commit);
|
||||
|
||||
/**
|
||||
* @brief Handle changes in a specific guiding shape, i.e. create new PCell variants if required
|
||||
*
|
||||
* @return A pair of bool (indicating that the object path has changed) and the new guiding shape path
|
||||
*/
|
||||
std::pair<bool, lay::ObjectInstPath> handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const;
|
||||
std::pair<bool, lay::ObjectInstPath> handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool commit) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether a move operation is ongoing
|
||||
|
|
@ -672,9 +674,12 @@ private:
|
|||
bool m_snap_to_objects;
|
||||
bool m_snap_objects_to_grid;
|
||||
db::DVector m_global_grid;
|
||||
|
||||
// Other attributes
|
||||
bool m_top_level_sel;
|
||||
bool m_show_shapes_of_instances;
|
||||
unsigned int m_max_shapes_of_instances;
|
||||
int m_pcell_lazy_evaluation;
|
||||
|
||||
// Hierarchical copy mode (-1: ask, 0: shallow, 1: deep)
|
||||
int m_hier_copy_mode;
|
||||
|
|
|
|||
|
|
@ -952,9 +952,11 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context
|
|||
const tl::Variant *arg = i >= int (args.size ()) ? get_kwarg (*a, kwargs) : &args[i];
|
||||
if (! arg) {
|
||||
is_valid = a->spec ()->has_default ();
|
||||
} else if (gsi::test_arg (*a, *arg, false /*strict*/)) {
|
||||
} else if (gsi::test_arg (*a, *arg, false /*strict*/, false /*no object substitution*/)) {
|
||||
sc += 100;
|
||||
} else if (gsi::test_arg (*a, *arg, true /*loose*/, false /*no object substitution*/)) {
|
||||
++sc;
|
||||
} else if (test_arg (*a, *arg, true /*loose*/)) {
|
||||
} else if (gsi::test_arg (*a, *arg, true /*loose*/, true /*with object substitution*/)) {
|
||||
// non-scoring match
|
||||
} else {
|
||||
is_valid = false;
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ inline void *get_object (tl::Variant &var)
|
|||
// -------------------------------------------------------------------
|
||||
// Test if an argument can be converted to the given type
|
||||
|
||||
bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose);
|
||||
bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution);
|
||||
|
||||
template <class R>
|
||||
struct test_arg_func
|
||||
{
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/)
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/)
|
||||
{
|
||||
*ret = arg.can_convert_to<R> ();
|
||||
}
|
||||
|
|
@ -60,7 +60,16 @@ struct test_arg_func
|
|||
template <>
|
||||
struct test_arg_func<gsi::VoidType>
|
||||
{
|
||||
void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/)
|
||||
void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/)
|
||||
{
|
||||
*ret = true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct test_arg_func<gsi::VariantType>
|
||||
{
|
||||
void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/)
|
||||
{
|
||||
*ret = true;
|
||||
}
|
||||
|
|
@ -69,7 +78,7 @@ struct test_arg_func<gsi::VoidType>
|
|||
template <>
|
||||
struct test_arg_func<gsi::ObjectType>
|
||||
{
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool object_substitution)
|
||||
{
|
||||
// allow nil of pointers
|
||||
if ((atype.is_ptr () || atype.is_cptr ()) && arg.is_nil ()) {
|
||||
|
|
@ -77,7 +86,7 @@ struct test_arg_func<gsi::ObjectType>
|
|||
return;
|
||||
}
|
||||
|
||||
if (arg.is_list ()) {
|
||||
if (object_substitution && arg.is_list ()) {
|
||||
|
||||
// we may implicitly convert an array into a constructor call of a target object -
|
||||
// for now we only check whether the number of arguments is compatible with the array given.
|
||||
|
|
@ -104,9 +113,9 @@ struct test_arg_func<gsi::ObjectType>
|
|||
const tl::VariantUserClassBase *cls = arg.user_cls ();
|
||||
if (! cls) {
|
||||
*ret = false;
|
||||
} else if (! cls->gsi_cls ()->is_derived_from (atype.cls ()) && (! loose || ! cls->gsi_cls ()->can_convert_to(atype.cls ()))) {
|
||||
*ret = false;
|
||||
} else if ((atype.is_ref () || atype.is_ptr ()) && cls->is_const ()) {
|
||||
} else if (! (cls->gsi_cls () == atype.cls () ||
|
||||
(loose && (cls->gsi_cls ()->is_derived_from (atype.cls ()) ||
|
||||
(object_substitution && cls->gsi_cls ()->can_convert_to (atype.cls ())))))) {
|
||||
*ret = false;
|
||||
} else {
|
||||
*ret = true;
|
||||
|
|
@ -117,7 +126,7 @@ struct test_arg_func<gsi::ObjectType>
|
|||
template <>
|
||||
struct test_arg_func<gsi::VectorType>
|
||||
{
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/)
|
||||
{
|
||||
if (! arg.is_list ()) {
|
||||
*ret = false;
|
||||
|
|
@ -129,7 +138,7 @@ struct test_arg_func<gsi::VectorType>
|
|||
|
||||
*ret = true;
|
||||
for (tl::Variant::const_iterator v = arg.begin (); v != arg.end () && *ret; ++v) {
|
||||
if (! test_arg (ainner, *v, loose)) {
|
||||
if (! test_arg (ainner, *v, loose, true)) {
|
||||
*ret = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -139,7 +148,7 @@ struct test_arg_func<gsi::VectorType>
|
|||
template <>
|
||||
struct test_arg_func<gsi::MapType>
|
||||
{
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
|
||||
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/)
|
||||
{
|
||||
// Note: delegating that to the function avoids "injected class name used as template template expression" warning
|
||||
if (! arg.is_array ()) {
|
||||
|
|
@ -152,16 +161,11 @@ struct test_arg_func<gsi::MapType>
|
|||
const ArgType &ainner = *atype.inner ();
|
||||
const ArgType &ainner_k = *atype.inner_k ();
|
||||
|
||||
if (! arg.is_list ()) {
|
||||
*ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
*ret = true;
|
||||
for (tl::Variant::const_array_iterator a = arg.begin_array (); a != arg.end_array () && *ret; ++a) {
|
||||
if (! test_arg (ainner_k, a->first, loose)) {
|
||||
if (! test_arg (ainner_k, a->first, loose, true)) {
|
||||
*ret = false;
|
||||
} else if (! test_arg (ainner, a->second, loose)) {
|
||||
} else if (! test_arg (ainner, a->second, loose, true)) {
|
||||
*ret = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +173,7 @@ struct test_arg_func<gsi::MapType>
|
|||
};
|
||||
|
||||
bool
|
||||
test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose)
|
||||
test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution)
|
||||
{
|
||||
// for const X * or X *, nil is an allowed value
|
||||
if ((atype.is_cptr () || atype.is_ptr ()) && arg.is_nil ()) {
|
||||
|
|
@ -177,7 +181,7 @@ test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose)
|
|||
}
|
||||
|
||||
bool ret = false;
|
||||
gsi::do_on_type<test_arg_func> () (atype.type (), &ret, arg, atype, loose);
|
||||
gsi::do_on_type<test_arg_func> () (atype.type (), &ret, arg, atype, loose, object_substitution);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,10 +70,11 @@ GSI_PUBLIC void pull_arg (gsi::SerialArgs &retlist, const gsi::ArgType &atype, t
|
|||
* @param atype The argument type
|
||||
* @param arg The value to pass to it
|
||||
* @param loose true for loose checking
|
||||
* @param object_substitution true to substitute object arguments by lists (using constructor) or employing conversion constructors
|
||||
*
|
||||
* @return True, if the argument can be passed
|
||||
*/
|
||||
GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose);
|
||||
GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -863,3 +863,38 @@ TEST(16)
|
|||
}
|
||||
}
|
||||
|
||||
// implicit conversions
|
||||
TEST(17)
|
||||
{
|
||||
tl::Eval e;
|
||||
tl::Variant v;
|
||||
|
||||
// smoke test
|
||||
v = e.parse ("var rdb=ReportDatabase.new();"
|
||||
"var cat=rdb.create_category('name');"
|
||||
"var cell=rdb.create_cell('TOP');"
|
||||
"var it=rdb.create_item(cell,cat);"
|
||||
"var bwp=BoxWithProperties.new(Box.new(0,0,1,2), {1=>'value'});"
|
||||
"it.add_value(bwp)").execute ();
|
||||
|
||||
v = e.parse ("var rdb=ReportDatabase.new();"
|
||||
"var cat=rdb.create_category('name');"
|
||||
"var cell=rdb.create_cell('TOP');"
|
||||
"var it=rdb.create_item(cell,cat);"
|
||||
"var bwp=DBoxWithProperties.new(DBox.new(0,0,1,2), {1=>'value'});"
|
||||
"it.add_value(bwp)").execute ();
|
||||
|
||||
v = e.parse ("var rdb=ReportDatabase.new();"
|
||||
"var cat=rdb.create_category('name');"
|
||||
"var cell=rdb.create_cell('TOP');"
|
||||
"var it=rdb.create_item(cell,cat);"
|
||||
"var b=DBox.new(0,0,1,2);"
|
||||
"it.add_value(b)").execute ();
|
||||
|
||||
v = e.parse ("var rdb=ReportDatabase.new();"
|
||||
"var cat=rdb.create_category('name');"
|
||||
"var cell=rdb.create_cell('TOP');"
|
||||
"var it=rdb.create_item(cell,cat);"
|
||||
"it.add_value(17.5)").execute ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@
|
|||
|
||||
// -----------------------------------------------------------------------
|
||||
// class QDomNodeList
|
||||
static bool QDomNodeList_operator_eq(const QDomNodeList *a, const QDomNodeList &b) {
|
||||
return *a == b;
|
||||
}
|
||||
static bool QDomNodeList_operator_ne(const QDomNodeList *a, const QDomNodeList &b) {
|
||||
return !(*a == b);
|
||||
}
|
||||
|
||||
// Constructor QDomNodeList::QDomNodeList()
|
||||
|
||||
|
|
@ -153,25 +159,6 @@ static void _call_f_length_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls
|
|||
}
|
||||
|
||||
|
||||
// bool QDomNodeList::operator!=(const QDomNodeList &)
|
||||
|
||||
|
||||
static void _init_f_operator_excl__eq__c2484 (qt_gsi::GenericMethod *decl)
|
||||
{
|
||||
static gsi::ArgSpecBase argspec_0 ("arg1");
|
||||
decl->add_arg<const QDomNodeList & > (argspec_0);
|
||||
decl->set_return<bool > ();
|
||||
}
|
||||
|
||||
static void _call_f_operator_excl__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
|
||||
{
|
||||
__SUPPRESS_UNUSED_WARNING(args);
|
||||
tl::Heap heap;
|
||||
const QDomNodeList &arg1 = gsi::arg_reader<const QDomNodeList & >() (args, heap);
|
||||
ret.write<bool > ((bool)((QDomNodeList *)cls)->operator!= (arg1));
|
||||
}
|
||||
|
||||
|
||||
// QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)
|
||||
|
||||
|
||||
|
|
@ -191,25 +178,6 @@ static void _call_f_operator_eq__2484 (const qt_gsi::GenericMethod * /*decl*/, v
|
|||
}
|
||||
|
||||
|
||||
// bool QDomNodeList::operator==(const QDomNodeList &)
|
||||
|
||||
|
||||
static void _init_f_operator_eq__eq__c2484 (qt_gsi::GenericMethod *decl)
|
||||
{
|
||||
static gsi::ArgSpecBase argspec_0 ("arg1");
|
||||
decl->add_arg<const QDomNodeList & > (argspec_0);
|
||||
decl->set_return<bool > ();
|
||||
}
|
||||
|
||||
static void _call_f_operator_eq__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
|
||||
{
|
||||
__SUPPRESS_UNUSED_WARNING(args);
|
||||
tl::Heap heap;
|
||||
const QDomNodeList &arg1 = gsi::arg_reader<const QDomNodeList & >() (args, heap);
|
||||
ret.write<bool > ((bool)((QDomNodeList *)cls)->operator== (arg1));
|
||||
}
|
||||
|
||||
|
||||
// int QDomNodeList::size()
|
||||
|
||||
|
||||
|
|
@ -238,14 +206,15 @@ static gsi::Methods methods_QDomNodeList () {
|
|||
methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QDomNodeList::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0);
|
||||
methods += new qt_gsi::GenericMethod ("item", "@brief Method QDomNode QDomNodeList::item(int index)\n", true, &_init_f_item_c767, &_call_f_item_c767);
|
||||
methods += new qt_gsi::GenericMethod ("length", "@brief Method int QDomNodeList::length()\n", true, &_init_f_length_c0, &_call_f_length_c0);
|
||||
methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &)\n", true, &_init_f_operator_excl__eq__c2484, &_call_f_operator_excl__eq__c2484);
|
||||
methods += new qt_gsi::GenericMethod ("assign", "@brief Method QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)\n", false, &_init_f_operator_eq__2484, &_call_f_operator_eq__2484);
|
||||
methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QDomNodeList::operator==(const QDomNodeList &)\n", true, &_init_f_operator_eq__eq__c2484, &_call_f_operator_eq__eq__c2484);
|
||||
methods += new qt_gsi::GenericMethod ("size", "@brief Method int QDomNodeList::size()\n", true, &_init_f_size_c0, &_call_f_size_c0);
|
||||
return methods;
|
||||
}
|
||||
|
||||
gsi::Class<QDomNodeList> decl_QDomNodeList ("QtXml", "QDomNodeList",
|
||||
gsi::method_ext("==", &QDomNodeList_operator_eq, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator==(const QDomNodeList &) const") +
|
||||
gsi::method_ext("!=", &QDomNodeList_operator_ne, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &) const")
|
||||
+
|
||||
methods_QDomNodeList (),
|
||||
"@qt\n@brief Binding of QDomNodeList");
|
||||
|
||||
|
|
|
|||
|
|
@ -784,7 +784,7 @@ PropertiesPage::reverse_color_order ()
|
|||
}
|
||||
|
||||
void
|
||||
PropertiesPage::apply ()
|
||||
PropertiesPage::apply (bool /*commit*/)
|
||||
{
|
||||
bool has_error = false;
|
||||
|
||||
|
|
@ -915,7 +915,7 @@ PropertiesPage::browse ()
|
|||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
apply ();
|
||||
apply (true);
|
||||
|
||||
lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Load Image File")), tl::to_string (QObject::tr ("All files (*)")));
|
||||
|
||||
|
|
@ -941,7 +941,7 @@ PropertiesPage::save_pressed ()
|
|||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
apply ();
|
||||
apply (true);
|
||||
|
||||
lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Save As KLayout Image File")), tl::to_string (QObject::tr ("KLayout image files (*.lyimg);;All files (*)")));
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public:
|
|||
virtual void update ();
|
||||
virtual void leave ();
|
||||
virtual bool readonly ();
|
||||
virtual void apply ();
|
||||
virtual void apply (bool commit);
|
||||
|
||||
void set_direct_image (img::Object *image);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public:
|
|||
BEGIN_PROTECTED
|
||||
|
||||
properties_frame->set_direct_image (mp_image_object);
|
||||
properties_frame->apply ();
|
||||
properties_frame->apply (true);
|
||||
|
||||
if (mp_image_object->is_empty ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("No data loaded for that image")));
|
||||
|
|
|
|||
|
|
@ -154,8 +154,10 @@ public:
|
|||
* Apply any changes to the current objects. If nothing was
|
||||
* changed, the object may be left untouched.
|
||||
* The dialog will start a transaction on the manager object.
|
||||
*
|
||||
* @param commit Is true for the "final" changes (i.e. not during editing)
|
||||
*/
|
||||
virtual void apply ()
|
||||
virtual void apply (bool /*commit*/)
|
||||
{
|
||||
// default implementation is empty.
|
||||
}
|
||||
|
|
@ -174,8 +176,11 @@ public:
|
|||
* Apply any changes to the current object plus all other objects of the same kind.
|
||||
* If nothing was changed, the objects may be left untouched.
|
||||
* The dialog will start a transaction on the manager object.
|
||||
*
|
||||
* @param relative Is true if relative mode is selected
|
||||
* @param commit Is true for the "final" changes (i.e. not during editing)
|
||||
*/
|
||||
virtual void apply_to_all (bool /*relative*/)
|
||||
virtual void apply_to_all (bool /*relative*/, bool /*commit*/)
|
||||
{
|
||||
// default implementation is empty.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ static const std::string cfg_grid_grid_color ("grid-grid-color");
|
|||
static const std::string cfg_grid_style0 ("grid-style0");
|
||||
static const std::string cfg_grid_style1 ("grid-style1");
|
||||
static const std::string cfg_grid_style2 ("grid-style2");
|
||||
static const std::string cfg_grid_density ("grid-density");
|
||||
static const std::string cfg_grid_visible ("grid-visible");
|
||||
static const std::string cfg_grid_micron ("grid-micron");
|
||||
static const std::string cfg_grid_show_ruler ("grid-show-ruler");
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager,
|
|||
}
|
||||
for (size_t i = 0; i < mp_properties_pages.size (); ++i) {
|
||||
mp_stack->addWidget (mp_properties_pages [i]);
|
||||
connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (apply ()));
|
||||
connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (properties_edited ()));
|
||||
}
|
||||
|
||||
// Necessary to maintain the page order for UI regression testing of 0.18 vs. 0.19 (because tl::Collection has changed to order) ..
|
||||
|
|
@ -314,7 +314,7 @@ PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelI
|
|||
|
||||
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
|
||||
|
||||
mp_properties_pages [m_index]->apply ();
|
||||
mp_properties_pages [m_index]->apply (true);
|
||||
|
||||
if (! t.is_empty ()) {
|
||||
m_transaction_id = t.id ();
|
||||
|
|
@ -437,7 +437,7 @@ BEGIN_PROTECTED
|
|||
|
||||
if (! mp_properties_pages [m_index]->readonly ()) {
|
||||
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
|
||||
mp_properties_pages [m_index]->apply ();
|
||||
mp_properties_pages [m_index]->apply (true);
|
||||
if (! t.is_empty ()) {
|
||||
m_transaction_id = t.id ();
|
||||
}
|
||||
|
|
@ -485,7 +485,7 @@ BEGIN_PROTECTED
|
|||
|
||||
if (! mp_properties_pages [m_index]->readonly ()) {
|
||||
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
|
||||
mp_properties_pages [m_index]->apply ();
|
||||
mp_properties_pages [m_index]->apply (true);
|
||||
if (! t.is_empty ()) {
|
||||
m_transaction_id = t.id ();
|
||||
}
|
||||
|
|
@ -567,7 +567,7 @@ PropertiesDialog::any_prev () const
|
|||
}
|
||||
|
||||
void
|
||||
PropertiesDialog::apply ()
|
||||
PropertiesDialog::properties_edited ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
|
|
@ -580,9 +580,9 @@ BEGIN_PROTECTED
|
|||
try {
|
||||
|
||||
if (mp_ui->apply_to_all_cbx->isChecked () && mp_properties_pages [m_index]->can_apply_to_all ()) {
|
||||
mp_properties_pages [m_index]->apply_to_all (mp_ui->relative_cbx->isChecked ());
|
||||
mp_properties_pages [m_index]->apply_to_all (mp_ui->relative_cbx->isChecked (), false);
|
||||
} else {
|
||||
mp_properties_pages [m_index]->apply ();
|
||||
mp_properties_pages [m_index]->apply (false);
|
||||
}
|
||||
mp_properties_pages [m_index]->update ();
|
||||
|
||||
|
|
@ -632,7 +632,7 @@ BEGIN_PROTECTED
|
|||
|
||||
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
|
||||
|
||||
mp_properties_pages [m_index]->apply ();
|
||||
mp_properties_pages [m_index]->apply (true);
|
||||
mp_properties_pages [m_index]->update ();
|
||||
|
||||
if (! t.is_empty ()) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ private:
|
|||
void update_controls ();
|
||||
|
||||
public slots:
|
||||
void apply ();
|
||||
void properties_edited ();
|
||||
void next_pressed ();
|
||||
void prev_pressed ();
|
||||
void cancel_pressed ();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>483</width>
|
||||
<height>341</height>
|
||||
<height>361</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
|
@ -59,6 +59,23 @@
|
|||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Far style</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="2" colspan="2">
|
||||
<widget class="QCheckBox" name="show_ruler">
|
||||
<property name="text">
|
||||
<string>Show ruler</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" colspan="2">
|
||||
<widget class="QComboBox" name="style1_cbx">
|
||||
<item>
|
||||
|
|
@ -108,7 +125,122 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="2" column="4">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2">
|
||||
<widget class="QPushButton" name="grid_axis_color_pb">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Grid </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="5">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="grid_net_color_pb">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Ruler</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="2">
|
||||
<widget class="QPushButton" name="grid_ruler_color_pb">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QPushButton" name="grid_grid_color_pb">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="Close style">
|
||||
<property name="text">
|
||||
<string>Close style</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Color (all)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
|
|
@ -118,14 +250,17 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="5">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2" colspan="2">
|
||||
<item row="7" column="2" colspan="2">
|
||||
<widget class="QComboBox" name="style0_cbx">
|
||||
<item>
|
||||
<property name="text">
|
||||
|
|
@ -169,30 +304,23 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="10" column="2">
|
||||
<widget class="QPushButton" name="grid_ruler_color_pb">
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string/>
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -245,141 +373,37 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Far style</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="Close style">
|
||||
<property name="text">
|
||||
<string>Close style</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Color (all)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QPushButton" name="grid_grid_color_pb">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Grid </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="2" colspan="2">
|
||||
<widget class="QCheckBox" name="show_ruler">
|
||||
<property name="text">
|
||||
<string>Show Ruler</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QPushButton" name="grid_axis_color_pb">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="grid_net_color_pb">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Ruler</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="5">
|
||||
<item row="9" column="0" colspan="5">
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QSpinBox" name="grid_density_sb">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Min. spacing</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="3" colspan="2">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>(Font height units)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ namespace lay
|
|||
// ------------------------------------------------------------
|
||||
// Helper functions to get and set the configuration
|
||||
|
||||
int default_density = 4;
|
||||
|
||||
static struct {
|
||||
lay::GridNet::GridStyle style;
|
||||
const char *string;
|
||||
|
|
@ -79,6 +81,20 @@ GridNetStyleConverter::to_string (lay::GridNet::GridStyle style)
|
|||
return "";
|
||||
}
|
||||
|
||||
void
|
||||
GridNetDensityConverter::from_string (const std::string &value, int &density)
|
||||
{
|
||||
density = default_density; // original default
|
||||
tl::Extractor ex (value.c_str ());
|
||||
ex.try_read (density);
|
||||
}
|
||||
|
||||
std::string
|
||||
GridNetDensityConverter::to_string (int density)
|
||||
{
|
||||
return tl::to_string (density);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Implementation of the GridNetPluginDeclaration
|
||||
|
||||
|
|
@ -92,6 +108,7 @@ GridNetPluginDeclaration::get_options (std::vector < std::pair<std::string, std:
|
|||
options.push_back (std::pair<std::string, std::string> (cfg_grid_style0, GridNetStyleConverter ().to_string (lay::GridNet::Invisible)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_grid_style1, GridNetStyleConverter ().to_string (lay::GridNet::Dots)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_grid_style2, GridNetStyleConverter ().to_string (lay::GridNet::TenthDottedLines)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_grid_density, ""));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_grid_visible, tl::to_string (true)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_grid_show_ruler, tl::to_string (true)));
|
||||
// grid-micron is not configured here since some other entity is supposed to do this.
|
||||
|
|
@ -122,7 +139,8 @@ GridNet::GridNet (LayoutViewBase *view)
|
|||
lay::Plugin (view),
|
||||
mp_view (view),
|
||||
m_visible (false), m_show_ruler (true), m_grid (1.0),
|
||||
m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible)
|
||||
m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible),
|
||||
m_density (default_density)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -175,6 +193,12 @@ GridNet::configure (const std::string &name, const std::string &value)
|
|||
GridNetStyleConverter ().from_string (value, style);
|
||||
need_update = test_and_set (m_style2, style);
|
||||
|
||||
} else if (name == cfg_grid_density) {
|
||||
|
||||
int density = 0;
|
||||
GridNetDensityConverter ().from_string (value, density);
|
||||
need_update = test_and_set (m_density, density);
|
||||
|
||||
} else if (name == cfg_grid_show_ruler) {
|
||||
|
||||
bool sr = false;
|
||||
|
|
@ -246,13 +270,14 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas)
|
|||
|
||||
// fw is the basic unit of the ruler geometry
|
||||
int fwr = lay::FixedFont::get_font (bmp_canvas->font_resolution ()).width ();
|
||||
int threshold = std::min (1000, m_density * fwr);
|
||||
|
||||
double dgrid = trans.ctrans (m_grid);
|
||||
GridStyle style = m_style1;
|
||||
|
||||
// compute major grid and switch to secondary style if necessary
|
||||
int s = 0;
|
||||
while (dgrid < fwr * 4) {
|
||||
while (dgrid < threshold) {
|
||||
if (s == 0) {
|
||||
dgrid *= 2.0;
|
||||
} else if (s == 1) {
|
||||
|
|
@ -279,56 +304,6 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas)
|
|||
int nx = int (dbworld.width () / grid + eps) + 2;
|
||||
int ny = int (dbworld.height () / grid + eps) + 2;
|
||||
|
||||
if (m_show_ruler && dgrid < vp.width () * 0.2) {
|
||||
|
||||
int rh = int (floor (0.5 + fwr * 0.8));
|
||||
int xoffset = int (floor (0.5 + fwr * 2.5));
|
||||
int yoffset = int (floor (0.5 + fwr * 2.5));
|
||||
|
||||
painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2),
|
||||
db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2),
|
||||
ruler_color);
|
||||
|
||||
painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2),
|
||||
db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2),
|
||||
ruler_color);
|
||||
|
||||
painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (),
|
||||
db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2),
|
||||
ruler_color, -1, 1);
|
||||
|
||||
if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) {
|
||||
|
||||
// draw a small "F" indicating any global transformation
|
||||
db::Point pts[] = {
|
||||
db::Point (-4, -5),
|
||||
db::Point (-4, 5),
|
||||
db::Point (4, 5),
|
||||
db::Point (4, 3),
|
||||
db::Point (-2, 3),
|
||||
db::Point (-2, 1),
|
||||
db::Point (3, 1),
|
||||
db::Point (3, -1),
|
||||
db::Point (-2, -1),
|
||||
db::Point (-2, -5),
|
||||
db::Point (-4, -5)
|
||||
};
|
||||
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0])));
|
||||
poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ()));
|
||||
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) {
|
||||
db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5);
|
||||
db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4)));
|
||||
db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4)));
|
||||
painter.draw_line (p1, p2, ruler_color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// draw grid
|
||||
if (style == Dots || style == TenthDottedLines ||
|
||||
style == DottedLines || style == LightDottedLines) {
|
||||
|
|
@ -549,6 +524,71 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas)
|
|||
|
||||
}
|
||||
|
||||
if (m_show_ruler && dgrid < vp.width () * 0.4) {
|
||||
|
||||
int rh = int (floor (0.5 + fwr * 0.8));
|
||||
int xoffset = int (floor (0.5 + fwr * 2.5));
|
||||
int yoffset = int (floor (0.5 + fwr * 2.5));
|
||||
|
||||
painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2),
|
||||
db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2),
|
||||
ruler_color);
|
||||
|
||||
painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2),
|
||||
db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2),
|
||||
ruler_color);
|
||||
|
||||
double grid_value = grid * 2;
|
||||
std::string fmt = "%g \265m";
|
||||
if (grid_value < 0.1 * (1 + db::epsilon)) {
|
||||
grid_value *= 1000.0;
|
||||
fmt = "%g nm";
|
||||
} else if (grid_value < 100.0 * (1 + db::epsilon)) {
|
||||
fmt = "%g \265m";
|
||||
} else if (grid_value < 100000.0 * (1 + db::epsilon)) {
|
||||
grid_value *= 1e-3;
|
||||
fmt = "%g mm";
|
||||
} else {
|
||||
grid_value *= 1e-6;
|
||||
fmt = "%g m";
|
||||
}
|
||||
|
||||
painter.draw_text (tl::sprintf (fmt, grid_value).c_str (),
|
||||
db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2),
|
||||
ruler_color, -1, 1);
|
||||
|
||||
if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) {
|
||||
|
||||
// draw a small "F" indicating any global transformation
|
||||
db::Point pts[] = {
|
||||
db::Point (-4, -5),
|
||||
db::Point (-4, 5),
|
||||
db::Point (4, 5),
|
||||
db::Point (4, 3),
|
||||
db::Point (-2, 3),
|
||||
db::Point (-2, 1),
|
||||
db::Point (3, 1),
|
||||
db::Point (3, -1),
|
||||
db::Point (-2, -1),
|
||||
db::Point (-2, -5),
|
||||
db::Point (-4, -5)
|
||||
};
|
||||
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0])));
|
||||
poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ()));
|
||||
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) {
|
||||
db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5);
|
||||
db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4)));
|
||||
db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4)));
|
||||
painter.draw_line (p1, p2, ruler_color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ private:
|
|||
GridStyle m_style0;
|
||||
GridStyle m_style1;
|
||||
GridStyle m_style2;
|
||||
int m_density;
|
||||
};
|
||||
|
||||
class GridNetStyleConverter
|
||||
|
|
@ -96,6 +97,13 @@ public:
|
|||
std::string to_string (lay::GridNet::GridStyle style);
|
||||
};
|
||||
|
||||
class GridNetDensityConverter
|
||||
{
|
||||
public:
|
||||
void from_string (const std::string &value, int &density);
|
||||
std::string to_string (int density);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ GridNetConfigPage::setup (lay::Dispatcher *root)
|
|||
style = lay::GridNet::Invisible;
|
||||
root->config_get (cfg_grid_style2, style, GridNetStyleConverter ());
|
||||
mp_ui->style2_cbx->setCurrentIndex (int (style));
|
||||
|
||||
int density = 0;
|
||||
root->config_get (cfg_grid_density, density, GridNetDensityConverter ());
|
||||
mp_ui->grid_density_sb->setValue (density);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -108,6 +112,7 @@ GridNetConfigPage::commit (lay::Dispatcher *root)
|
|||
root->config_set (cfg_grid_style0, lay::GridNet::GridStyle (mp_ui->style0_cbx->currentIndex ()), GridNetStyleConverter ());
|
||||
root->config_set (cfg_grid_style1, lay::GridNet::GridStyle (mp_ui->style1_cbx->currentIndex ()), GridNetStyleConverter ());
|
||||
root->config_set (cfg_grid_style2, lay::GridNet::GridStyle (mp_ui->style2_cbx->currentIndex ()), GridNetStyleConverter ());
|
||||
root->config_set (cfg_grid_density, mp_ui->grid_density_sb->value (), GridNetDensityConverter ());
|
||||
}
|
||||
|
||||
} // namespace lay
|
||||
|
|
|
|||
|
|
@ -225,9 +225,16 @@ module LVS
|
|||
# @synopsis lvs_data
|
||||
# See \Netter#lvs_data for a description of that function.
|
||||
|
||||
# %LVS%
|
||||
# @name flag_missing_ports
|
||||
# @brief Checks if all top level ports are properly labelled
|
||||
# @synopsis flag_missing_ports
|
||||
# See \Netter#flag_missing_ports for a description of that function.
|
||||
|
||||
%w(schematic compare split_gates join_symmetric_nets tolerance ignore_parameter enable_parameter disable_parameter
|
||||
blank_circuit align same_nets same_nets! same_circuits same_device_classes equivalent_pins
|
||||
min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data no_lvs_hints).each do |f|
|
||||
min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data no_lvs_hints
|
||||
flag_missing_ports).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}(*args)
|
||||
_netter.#{f}(*args)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue