Merge branch 'master' into dev-polygon-graph

This commit is contained in:
Matthias Koefferlein 2025-05-28 19:01:42 +02:00
commit 9917978566
179 changed files with 6157 additions and 674 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[tool.cibuildwheel]
build-verbosity = "3"
test-command = [

View File

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

View File

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

View File

@ -517,7 +517,7 @@ PropertiesPage::readonly ()
}
void
PropertiesPage::apply ()
PropertiesPage::apply (bool /*commit*/)
{
ant::Object obj;
get_object (obj);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &region, 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 &region, 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 &region, 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 &region, 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 &region, 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 &region, const box_tree_type *complex_region);
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &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)
{
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 &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)
{
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 &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)
{
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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -105,7 +105,7 @@ private:
void update_controls ();
public slots:
void apply ();
void properties_edited ();
void next_pressed ();
void prev_pressed ();
void cancel_pressed ();

View File

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

View File

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

View File

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

View File

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

View File

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