mirror of https://github.com/KLayout/klayout.git
Compare commits
108 Commits
d60b296417
...
6cd4b97cd3
| Author | SHA1 | Date |
|---|---|---|
|
|
6cd4b97cd3 | |
|
|
a208c6d25d | |
|
|
e232bf8127 | |
|
|
56d91acc15 | |
|
|
0f39cef21f | |
|
|
6d871166f7 | |
|
|
38df5aa392 | |
|
|
92d79d46c1 | |
|
|
65a9465bc1 | |
|
|
cb649e6ee6 | |
|
|
e76e9b7d0b | |
|
|
6e7eff95e7 | |
|
|
43310e7f49 | |
|
|
9c7ccefc9f | |
|
|
9aa8d79bfc | |
|
|
c7514ba24d | |
|
|
efb3b3eb3d | |
|
|
345f316af5 | |
|
|
89d5bf0df1 | |
|
|
d8a13037a9 | |
|
|
09ad36e298 | |
|
|
c55a0757c1 | |
|
|
5077b22963 | |
|
|
5efcf83640 | |
|
|
ffa42653fe | |
|
|
2435e774f4 | |
|
|
2bd82af6fe | |
|
|
148498f840 | |
|
|
d317dc2fe3 | |
|
|
fa618a5b76 | |
|
|
09329442f0 | |
|
|
0ec8e18173 | |
|
|
3bd9a96f67 | |
|
|
a27fd3e0be | |
|
|
0542ef835a | |
|
|
3a752fd2c7 | |
|
|
e8d796aded | |
|
|
8150e732af | |
|
|
789e183be9 | |
|
|
41e9cb5893 | |
|
|
6b5268e5f7 | |
|
|
83e0c17291 | |
|
|
163c3b8edc | |
|
|
bcf14ede3e | |
|
|
449a9a968e | |
|
|
efe90cf29b | |
|
|
edcb283a87 | |
|
|
78e2074b4c | |
|
|
c656700b44 | |
|
|
0a587fa079 | |
|
|
a38bea3086 | |
|
|
40a0113ce5 | |
|
|
3aebf90ecd | |
|
|
a24d5388d7 | |
|
|
227203cdd1 | |
|
|
db66a6ee74 | |
|
|
e6ff30adee | |
|
|
db728dc68a | |
|
|
b748579bd7 | |
|
|
a5625c6d86 | |
|
|
b493be2dfa | |
|
|
8760ac4bcb | |
|
|
740f36a2c2 | |
|
|
a3593efa18 | |
|
|
efeb2c061b | |
|
|
a22f48d87a | |
|
|
f977973b85 | |
|
|
cf5e62a4ed | |
|
|
71644fa56c | |
|
|
8727c31d36 | |
|
|
16be7bf8c1 | |
|
|
593678e228 | |
|
|
986474d465 | |
|
|
73364ee406 | |
|
|
b8a6d0a1cd | |
|
|
21bfe7a632 | |
|
|
85bb9be5c0 | |
|
|
6228668fa1 | |
|
|
a727ed0b1d | |
|
|
2191febc38 | |
|
|
2668b42d29 | |
|
|
561a760881 | |
|
|
e6914c78b9 | |
|
|
ee60ae8146 | |
|
|
2aa88fe29c | |
|
|
283a9d0b22 | |
|
|
69a8f3ac11 | |
|
|
12b3cd011b | |
|
|
6f69efd427 | |
|
|
d9343ee530 | |
|
|
1f5c2b5132 | |
|
|
df631aa970 | |
|
|
6596008826 | |
|
|
cd62f62140 | |
|
|
4e65b96cb7 | |
|
|
f3037d11f3 | |
|
|
4aeb94d42e | |
|
|
477e2b5a31 | |
|
|
4369835e8b | |
|
|
f1f35ae2a4 | |
|
|
5c8e0539ee | |
|
|
54b5d9f5d6 | |
|
|
f136fdcde6 | |
|
|
1feffe7beb | |
|
|
82d2adeb05 | |
|
|
ed809952c3 | |
|
|
60f7c70f11 | |
|
|
c0f2bdd3d6 |
|
|
@ -60,7 +60,7 @@ jobs:
|
|||
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
|
||||
uses: pypa/cibuildwheel@v2.23.3
|
||||
# to supply options, put them in 'env', like:
|
||||
# env:
|
||||
# CIBW_SOME_OPTION: value
|
||||
|
|
@ -97,7 +97,7 @@ jobs:
|
|||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }}
|
||||
name: artifact-sdist
|
||||
path: dist/*.tar.gz
|
||||
|
||||
upload_to_test_pypy:
|
||||
|
|
@ -106,7 +106,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }}
|
||||
merge-multiple: true
|
||||
path: dist
|
||||
|
||||
- uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||
|
|
@ -123,7 +123,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }}
|
||||
merge-multiple: true
|
||||
path: dist
|
||||
|
||||
- uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||
|
|
|
|||
60
Changelog
60
Changelog
|
|
@ -1,3 +1,63 @@
|
|||
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
|
||||
* Enhancement: Better support for user properties
|
||||
The property management has been overhauled. Properties are not globally
|
||||
scoped. The "Layout" class now provides class methods to convert property IDs
|
||||
to property hashes and back. It is no longer required to know the origin of
|
||||
a property ID.
|
||||
Other changes are:
|
||||
- New classes "BoxWithProperties", "EdgeWithProperties", "PolygonWithProperties",
|
||||
"SimplePolygonWithProperties", "PathWithProperties", "EdgePairWithProperties",
|
||||
"TextWithProperties" and corresponding "D..." classes.
|
||||
Many functions accept these objects now in addition to the property-less "Box",
|
||||
"Edge" etc. objects. Many functions now deliver property-annotated objects
|
||||
instead of the plain ones.
|
||||
- "EdgePairFilter#property_glob", "EdgePairFilter#property_filter", "EdgePairFilter#property_filter_bounded"
|
||||
creates filters for edge pair objects, which can be used on "EdgePairs" containers to
|
||||
filter by properties.
|
||||
- The same is available for "EdgeFilter" (for filtering edges in "Edges" containers),
|
||||
"PolygonFilter" (for filtering polygons in "Region" containers), and
|
||||
"TextFilter" (for filtering texts in "Texts" containers)
|
||||
- New method "process_with_properties" for "PolygonOperator", "EdgeOperator",
|
||||
"EdgePairOperator" and "TextOperator"
|
||||
* Enhancement: New class "PolygonNeighborhood"
|
||||
This is a visitor to obtain all "neighboring" polygons for each polygon
|
||||
in a Region. Currently, neighborhood is defined by the bounding box of
|
||||
the primary polygons.
|
||||
* Enhancement: Layout queries can use "$_" to refer to the current cell now
|
||||
* Enhancement: New methods:
|
||||
- "LayoutToNetlist#build_net", "build_nets" and "build_all_nets" do not
|
||||
- "LayoutToNetlist#layer_index" from a name
|
||||
- "LayoutToNetlist#texts_by_index" and "LayoutToNetlist#texts_by_name"
|
||||
- "LayoutToNetlist#original_layout" and "LayoutToNetlist#original_top_cell"
|
||||
- "LayoutToNetlist#polygons_of_net" and "LayoutToNetlist#texts_of_net"
|
||||
- "Device#terminal_ref"
|
||||
- New "text_prop" argument in Texts#polygons (texts can transfer their
|
||||
string to the generated polygons as user properties)
|
||||
- New methods "Polygon#delaunay", "DPolygon#delaunay", "SimplePolygon#delaunay"
|
||||
and "DSimplePolygon#delaunay"
|
||||
|
||||
0.29.12 (2025-03-02):
|
||||
* Bug: %GITHUB%/issues/1976 Crash on cross mode, lw > 1 and oversampling
|
||||
* Bug: %GITHUB%/issues/1987 Build failure against Qt6.8
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
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
|
||||
- See changelog
|
||||
|
||||
-- Matthias Köfferlein <matthias@koefferlein.de> Tue, 25 Mar 2025 00:00:00 +0100
|
||||
|
||||
klayout (0.29.12-1) unstable; urgency=low
|
||||
|
||||
* New features and bugfixes
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ jobs:
|
|||
displayName: 'Download and Extract KLayout bits'
|
||||
|
||||
- script: |
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
# setuptools 67.0.0 is not working as of now (pypa/setuptools#4885)
|
||||
python -m pip install --upgrade pip "setuptools<76.0.0" wheel
|
||||
displayName: 'Update pip, setuptools and wheel'
|
||||
|
||||
- script: |
|
||||
|
|
|
|||
|
|
@ -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.6',
|
||||
'inc': '/opt/local/include/ruby-3.3.8',
|
||||
'lib': '/opt/local/lib/libruby.3.3.dylib'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ def CheckPkgDirectory():
|
|||
#------------------------------------------------------
|
||||
# [5] Check the occupied disk space
|
||||
#------------------------------------------------------
|
||||
command = "\du -sm %s" % DefaultBundleName
|
||||
command = r"\du -sm %s" % DefaultBundleName
|
||||
sizeApp = int( os.popen(command).read().strip("\n").split("\t")[0] )
|
||||
|
||||
#------------------------------------------------------
|
||||
|
|
@ -671,14 +671,14 @@ def MakeTargetDMGFile(msg=""):
|
|||
imageDest = "%s/.background" % MountDir
|
||||
if not os.path.isdir(imageDest):
|
||||
os.mkdir(imageDest)
|
||||
command = "\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG)
|
||||
command = r"\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG)
|
||||
os.system(command)
|
||||
|
||||
#--------------------------------------------------------
|
||||
# (6) Create a symbolic link to /Applications
|
||||
#--------------------------------------------------------
|
||||
print( ">>> (6) Creating a symbolic link to /Applications..." )
|
||||
command = "\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications)
|
||||
command = r"\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications)
|
||||
os.system(command)
|
||||
|
||||
#--------------------------------------------------------
|
||||
|
|
@ -702,7 +702,7 @@ def MakeTargetDMGFile(msg=""):
|
|||
print( ">>> (8) Copying the volume icon..." )
|
||||
iconsSrc = "macbuild/Resources/%s" % VolumeIcons
|
||||
iconsDest = "%s/.VolumeIcon.icns" % MountDir
|
||||
command1 = "\cp -p %s %s" % (iconsSrc, iconsDest)
|
||||
command1 = r"\cp -p %s %s" % (iconsSrc, iconsDest)
|
||||
command2 = "SetFile -c icnC %s" % iconsDest
|
||||
os.system(command1)
|
||||
sleep(2)
|
||||
|
|
@ -713,7 +713,7 @@ def MakeTargetDMGFile(msg=""):
|
|||
# (9) Change the permission
|
||||
#--------------------------------------------------------
|
||||
print( ">>> (9) Changing permission to 755..." )
|
||||
command = "\chmod -Rf 755 %s &> /dev/null" % MountDir
|
||||
command = r"\chmod -Rf 755 %s &> /dev/null" % MountDir
|
||||
os.system(command)
|
||||
|
||||
#--------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
[build-system]
|
||||
requires = ["setuptools >= 77.0.3"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.cibuildwheel]
|
||||
build-verbosity = "3"
|
||||
test-command = [
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m
|
|||
drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?)
|
||||
drop_method "QNoDebug", /QNoDebug::operator<</ # nothing usable (TODO: how to map?)
|
||||
|
||||
# No longer supported operator== and operator!= in Qt 6.7/6.8
|
||||
# No longer supported operator== and operator!= in Qt 6.7/6.8/6.9
|
||||
add_native_operator_neq(self, "QEasingCurve")
|
||||
add_native_operator_neq(self, "QTimeZone")
|
||||
add_native_operator_neq(self, "QDir")
|
||||
|
|
@ -569,6 +569,7 @@ add_native_operator_neq(self, "QProcessEnvironment")
|
|||
add_native_operator_neq(self, "QRegularExpression")
|
||||
add_native_operator_neqlt(self, "QUrl")
|
||||
add_native_operator_neq(self, "QUrlQuery")
|
||||
add_native_operator_neq(self, "QDomNodeList")
|
||||
add_native_operator_neq(self, "QXmlStreamAttribute")
|
||||
add_native_operator_neq(self, "QXmlStreamEntityDeclaration")
|
||||
add_native_operator_neq(self, "QXmlStreamNamespaceDeclaration")
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -987,7 +987,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",
|
||||
|
|
@ -996,7 +996,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",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "dbReader.h"
|
||||
#include "dbWriter.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace bd
|
||||
{
|
||||
|
|
@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format)
|
|||
|
||||
db::Layout layout;
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
{
|
||||
db::LoadLayoutOptions load_options;
|
||||
generic_reader_options.configure (load_options);
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions ()
|
|||
m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool ();
|
||||
m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool ();
|
||||
m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string ();
|
||||
m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
|
||||
// Don't take the default, as in practice, it's more common to substitute LEF macros by layouts
|
||||
// m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
|
||||
m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always"
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbSaveLayoutOptions.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
|
||||
struct ClipData
|
||||
|
|
@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
|
|||
|
||||
cmd.parse (argc, argv);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
clip (data);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "dbLayoutDiff.h"
|
||||
#include "dbReader.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
BD_PUBLIC int strmcmp (int argc, char *argv[])
|
||||
{
|
||||
|
|
@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
|
|||
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given");
|
||||
}
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
db::Layout layout_a;
|
||||
db::Layout layout_b;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "tlLog.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlTimer.h"
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
#include "gsi.h"
|
||||
|
|
@ -97,5 +98,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[])
|
|||
lym::Macro macro;
|
||||
macro.load_from (script);
|
||||
macro.set_file_path (script);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
return macro.run ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include "gsiExpression.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlThreads.h"
|
||||
#include "tlThreadedWorkers.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -319,7 +321,8 @@ struct XORData
|
|||
dont_summarize_missing_layers (false), silent (false), no_summary (false),
|
||||
threads (0),
|
||||
tile_size (0.0), heal_results (false),
|
||||
output_layout (0), output_cell (0)
|
||||
output_layout (0), output_cell (0),
|
||||
layers_missing (0)
|
||||
{ }
|
||||
|
||||
db::Layout *layout_a, *layout_b;
|
||||
|
|
@ -336,6 +339,8 @@ struct XORData
|
|||
db::cell_index_type output_cell;
|
||||
std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> l2l_map;
|
||||
std::map<std::pair<int, db::LayerProperties>, ResultDescriptor> *results;
|
||||
mutable int layers_missing;
|
||||
mutable tl::Mutex lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -455,6 +460,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
db::Layout layout_a;
|
||||
db::Layout layout_b;
|
||||
|
||||
|
|
@ -572,14 +579,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
if (! silent && ! no_summary) {
|
||||
|
||||
if (result) {
|
||||
tl::info << "No differences found";
|
||||
tl::info << tl::to_string (tr ("No differences found"));
|
||||
} else {
|
||||
|
||||
const char *line_format = " %-10s %-12s %s";
|
||||
const char *sep = " -------------------------------------------------------";
|
||||
|
||||
tl::info << "Result summary (layers without differences are not shown):" << tl::endl;
|
||||
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
|
||||
std::string headline;
|
||||
if (deep) {
|
||||
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)")));
|
||||
} else {
|
||||
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)")));
|
||||
}
|
||||
|
||||
const char *sep = " ----------------------------------------------------------------";
|
||||
|
||||
tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl;
|
||||
tl::info << headline << tl::endl << sep;
|
||||
|
||||
int ti = -1;
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) {
|
||||
|
|
@ -587,17 +602,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
if (r->first.first != ti) {
|
||||
ti = r->first.first;
|
||||
if (tolerances[ti] > db::epsilon) {
|
||||
tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
|
||||
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
|
||||
tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
|
||||
tl::info << headline << tl::endl << sep;
|
||||
}
|
||||
}
|
||||
|
||||
std::string out ("-");
|
||||
std::string value;
|
||||
if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) {
|
||||
value = "(no such layer in first layout)";
|
||||
value = tl::to_string (tr ("(no such layer in first layout)"));
|
||||
} else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) {
|
||||
value = "(no such layer in second layout)";
|
||||
value = tl::to_string (tr ("(no such layer in second layout)"));
|
||||
} else if (! r->second.is_empty ()) {
|
||||
if (r->second.layer_output >= 0 && r->second.layout) {
|
||||
out = r->second.layout->get_properties (r->second.layer_output).to_string ();
|
||||
|
|
@ -758,15 +773,174 @@ bool run_tiled_xor (const XORData &xor_data)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool run_deep_xor (const XORData &xor_data)
|
||||
{
|
||||
db::DeepShapeStore dss;
|
||||
dss.set_threads (xor_data.threads);
|
||||
|
||||
class XORJob
|
||||
: public tl::JobBase
|
||||
{
|
||||
public:
|
||||
XORJob (int nworkers)
|
||||
: tl::JobBase (nworkers)
|
||||
{
|
||||
}
|
||||
|
||||
virtual tl::Worker *create_worker ();
|
||||
};
|
||||
|
||||
class XORWorker
|
||||
: public tl::Worker
|
||||
{
|
||||
public:
|
||||
XORWorker (XORJob *job);
|
||||
void perform_task (tl::Task *task);
|
||||
|
||||
db::DeepShapeStore &dss ()
|
||||
{
|
||||
return m_dss;
|
||||
}
|
||||
|
||||
private:
|
||||
XORJob *mp_job;
|
||||
db::DeepShapeStore m_dss;
|
||||
};
|
||||
|
||||
class XORTask
|
||||
: public tl::Task
|
||||
{
|
||||
public:
|
||||
XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu)
|
||||
: mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void run (XORWorker *worker) const
|
||||
{
|
||||
if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) {
|
||||
|
||||
if (m_la < 0) {
|
||||
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second";
|
||||
} else {
|
||||
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first";
|
||||
}
|
||||
|
||||
tl::MutexLocker locker (&mp_xor_data->lock);
|
||||
|
||||
mp_xor_data->layers_missing += 1;
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
|
||||
|
||||
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
|
||||
result.layer_a = m_la;
|
||||
result.layer_b = m_lb;
|
||||
result.layout = mp_xor_data->output_layout;
|
||||
result.top_cell = mp_xor_data->output_cell;
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ());
|
||||
|
||||
db::RecursiveShapeIterator ri_a, ri_b;
|
||||
|
||||
if (m_la >= 0) {
|
||||
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
|
||||
} else {
|
||||
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector<unsigned int> ());
|
||||
}
|
||||
ri_a.set_for_merged_input (true);
|
||||
|
||||
if (m_lb >= 0) {
|
||||
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
|
||||
} else {
|
||||
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector<unsigned int> ());
|
||||
}
|
||||
ri_b.set_for_merged_input (true);
|
||||
|
||||
db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
|
||||
db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
|
||||
|
||||
db::Region xor_res;
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
}
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
|
||||
|
||||
db::LayerProperties lp = m_layer_props;
|
||||
if (lp.layer >= 0) {
|
||||
lp.layer += tol_index * mp_xor_data->tolerance_bump;
|
||||
}
|
||||
|
||||
if (*t > db::epsilon) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ());
|
||||
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
|
||||
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
|
||||
}
|
||||
|
||||
{
|
||||
tl::MutexLocker locker (&mp_xor_data->lock);
|
||||
|
||||
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
|
||||
result.layer_a = m_la;
|
||||
result.layer_b = m_lb;
|
||||
result.layout = mp_xor_data->output_layout;
|
||||
result.top_cell = mp_xor_data->output_cell;
|
||||
|
||||
if (mp_xor_data->output_layout) {
|
||||
result.layer_output = result.layout->insert_layer (lp);
|
||||
xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output);
|
||||
} else {
|
||||
result.shape_count = xor_res.hier_count ();
|
||||
}
|
||||
}
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const XORData *mp_xor_data;
|
||||
const db::LayerProperties &m_layer_props;
|
||||
int m_la;
|
||||
int m_lb;
|
||||
double m_dbu;
|
||||
};
|
||||
|
||||
XORWorker::XORWorker (XORJob *job)
|
||||
: tl::Worker (), mp_job (job)
|
||||
{
|
||||
// TODO: this conflicts with the "set_for_merged_input" optimization below.
|
||||
// It seems not to be very effective then. Why?
|
||||
dss.set_wants_all_cells (true); // saves time for less cell mapping operations
|
||||
m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations
|
||||
}
|
||||
|
||||
void
|
||||
XORWorker::perform_task (tl::Task *task)
|
||||
{
|
||||
XORTask *xor_task = dynamic_cast <XORTask *> (task);
|
||||
if (xor_task) {
|
||||
xor_task->run (this);
|
||||
}
|
||||
}
|
||||
|
||||
tl::Worker *
|
||||
XORJob::create_worker ()
|
||||
{
|
||||
return new XORWorker (this);
|
||||
}
|
||||
|
||||
|
||||
bool run_deep_xor (const XORData &xor_data)
|
||||
{
|
||||
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
|
|
@ -779,98 +953,18 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
xor_data.output_layout->dbu (dbu);
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
int index = 1;
|
||||
XORJob job (xor_data.threads);
|
||||
|
||||
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) {
|
||||
|
||||
if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) {
|
||||
|
||||
if (ll->second.first < 0) {
|
||||
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second";
|
||||
} else {
|
||||
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first";
|
||||
}
|
||||
|
||||
result = false;
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
|
||||
|
||||
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
|
||||
result.layer_a = ll->second.first;
|
||||
result.layer_b = ll->second.second;
|
||||
result.layout = xor_data.output_layout;
|
||||
result.top_cell = xor_data.output_cell;
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
|
||||
|
||||
db::RecursiveShapeIterator ri_a, ri_b;
|
||||
|
||||
if (ll->second.first >= 0) {
|
||||
ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first);
|
||||
ri_a.set_for_merged_input (true);
|
||||
}
|
||||
|
||||
if (ll->second.second >= 0) {
|
||||
ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second);
|
||||
ri_b.set_for_merged_input (true);
|
||||
}
|
||||
|
||||
db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu));
|
||||
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
|
||||
|
||||
db::Region xor_res;
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
}
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
|
||||
|
||||
db::LayerProperties lp = ll->first;
|
||||
if (lp.layer >= 0) {
|
||||
lp.layer += tol_index * xor_data.tolerance_bump;
|
||||
}
|
||||
|
||||
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
|
||||
result.layer_a = ll->second.first;
|
||||
result.layer_b = ll->second.second;
|
||||
result.layout = xor_data.output_layout;
|
||||
result.top_cell = xor_data.output_cell;
|
||||
|
||||
if (*t > db::epsilon) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
|
||||
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
|
||||
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
|
||||
}
|
||||
|
||||
if (xor_data.output_layout) {
|
||||
result.layer_output = result.layout->insert_layer (lp);
|
||||
xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output);
|
||||
} else {
|
||||
result.shape_count = xor_res.count ();
|
||||
}
|
||||
|
||||
++tol_index;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu));
|
||||
}
|
||||
|
||||
// Determines the output status
|
||||
job.start ();
|
||||
job.wait ();
|
||||
|
||||
// Determine the output status
|
||||
|
||||
bool result = (xor_data.layers_missing == 0);
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) {
|
||||
result = r->second.is_empty ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ TEST(1A_Flat)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 30\n"
|
||||
" 6/0 6/0 41\n"
|
||||
" 8/1 8/1 1\n"
|
||||
|
|
@ -146,8 +146,8 @@ TEST(1A_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 3\n"
|
||||
" 6/0 6/0 314\n"
|
||||
" 8/1 8/1 1\n"
|
||||
|
|
@ -177,7 +177,7 @@ TEST(1B_Flat)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" 6/0 - 41\n"
|
||||
" 8/1 - 1\n"
|
||||
|
|
@ -206,9 +206,9 @@ TEST(1B_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 3\n"
|
||||
" 6/0 - 314\n"
|
||||
" 8/1 - 1\n"
|
||||
" 10/0 - (no such layer in first layout)\n"
|
||||
|
|
@ -417,7 +417,7 @@ TEST(3_FlatCount)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 31\n"
|
||||
" 6/0 - 217\n"
|
||||
" 8/1 - 168\n"
|
||||
|
|
@ -483,7 +483,7 @@ TEST(3_FlatCountHeal)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" 6/0 - 41\n"
|
||||
" 8/1 - 1\n"
|
||||
|
|
@ -756,3 +756,42 @@ TEST(6_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(7_OptimizeDeep)
|
||||
{
|
||||
tl::CaptureChannel cap;
|
||||
|
||||
std::string input_a = tl::testdata ();
|
||||
input_a += "/bd/strmxor_covered1.gds";
|
||||
|
||||
std::string input_b = tl::testdata ();
|
||||
input_b += "/bd/strmxor_covered2.gds";
|
||||
|
||||
std::string au = tl::testdata ();
|
||||
au += "/bd/strmxor_au7d.oas";
|
||||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "-u", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
||||
db::Layout layout;
|
||||
|
||||
{
|
||||
tl::InputStream stream (output);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
|
||||
EXPECT_EQ (cap.captured_text (),
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 2/0 2/0 1\n"
|
||||
" 3/0 3/0 8\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ SOURCES = \
|
|||
dbPolygonGenerators.cc \
|
||||
dbPropertiesFilter.cc \
|
||||
dbPropertiesRepository.cc \
|
||||
dbQuadTree.cc \
|
||||
dbReader.cc \
|
||||
dbRecursiveInstanceIterator.cc \
|
||||
dbRecursiveShapeIterator.cc \
|
||||
|
|
@ -319,6 +320,7 @@ HEADERS = \
|
|||
dbPropertiesFilter.h \
|
||||
dbPropertiesRepository.h \
|
||||
dbPropertyConstraint.h \
|
||||
dbQuadTree.h \
|
||||
dbReader.h \
|
||||
dbRecursiveInstanceIterator.h \
|
||||
dbRecursiveShapeIterator.h \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ struct ResultCountingInserter
|
|||
{
|
||||
typedef db::Polygon value_type;
|
||||
|
||||
ResultCountingInserter (std::unordered_map<const db::Polygon *, size_t, std::ptr_hash_from_value<db::Polygon> > &result)
|
||||
ResultCountingInserter (std::unordered_map<const db::Polygon *, size_t, tl::ptr_hash_from_value<db::Polygon> > &result)
|
||||
: mp_result (&result)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
@ -70,7 +70,7 @@ struct ResultCountingInserter
|
|||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<const db::Polygon *, size_t, std::ptr_hash_from_value<db::Polygon> > *mp_result;
|
||||
std::unordered_map<const db::Polygon *, size_t, tl::ptr_hash_from_value<db::Polygon> > *mp_result;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,14 +233,32 @@ AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter)
|
|||
}
|
||||
|
||||
RegionDelegate *
|
||||
AsIfFlatTexts::polygons (db::Coord e) const
|
||||
AsIfFlatTexts::polygons (db::Coord e, const tl::Variant &text_prop) const
|
||||
{
|
||||
db::property_names_id_type key_id = 0;
|
||||
if (! text_prop.is_nil ()) {
|
||||
key_id = db::property_names_id (text_prop);
|
||||
}
|
||||
|
||||
std::map<std::string, db::properties_id_type> value_ids;
|
||||
|
||||
std::unique_ptr<FlatRegion> output (new FlatRegion ());
|
||||
|
||||
for (TextsIterator tp (begin ()); ! tp.at_end (); ++tp) {
|
||||
db::Box box = tp->box ();
|
||||
box.enlarge (db::Vector (e, e));
|
||||
output->insert (db::Polygon (box));
|
||||
if (key_id == 0) {
|
||||
output->insert (db::Polygon (box));
|
||||
} else {
|
||||
std::string value (tp->string ());
|
||||
auto v = value_ids.find (value);
|
||||
if (v == value_ids.end ()) {
|
||||
db::PropertiesSet ps;
|
||||
ps.insert_by_id (key_id, db::property_values_id (value));
|
||||
v = value_ids.insert (std::make_pair (value, db::properties_id (ps))).first;
|
||||
}
|
||||
output->insert (db::PolygonWithProperties (db::Polygon (box), v->second));
|
||||
}
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public:
|
|||
|
||||
virtual TextsDelegate *add (const Texts &other) const;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const;
|
||||
virtual EdgesDelegate *edges () const;
|
||||
|
||||
virtual TextsDelegate *in (const Texts &, bool) const;
|
||||
|
|
|
|||
|
|
@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
std::vector<db::cell_index_type> &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int);
|
||||
std::vector<db::cell_index_type> new_cells_b;
|
||||
|
||||
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > all_a2b;
|
||||
for (std::vector<db::cell_index_type>::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) {
|
||||
auto m = m_b2a_mapping.find (*b);
|
||||
tl_assert (m != m_b2a_mapping.end ());
|
||||
all_a2b.push_back (std::make_pair (m->second, *b));
|
||||
}
|
||||
|
||||
std::set<db::cell_index_type> called_b;
|
||||
for (std::vector<db::cell_index_type>::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) {
|
||||
layout_b.cell (*i).collect_called_cells (called_b);
|
||||
|
|
@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b);
|
||||
new_cells.push_back (new_cell);
|
||||
new_cells_b.push_back (*b);
|
||||
all_a2b.push_back (std::make_pair (new_cell, *b));
|
||||
|
||||
if (mapped_pairs) {
|
||||
mapped_pairs->push_back (std::make_pair (*b, new_cell));
|
||||
|
|
@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
}
|
||||
}
|
||||
|
||||
if (! new_cells.empty ()) {
|
||||
if (all_a2b.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
|
||||
db::LayoutLocker locker (&layout_a);
|
||||
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
|
||||
db::LayoutLocker locker (&layout_a);
|
||||
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (size_t i = 0; i < new_cells.size (); ++i) {
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) {
|
||||
|
||||
const db::Cell &b = layout_b.cell (new_cells_b [i]);
|
||||
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
|
||||
const db::Cell &b = layout_b.cell (i->second);
|
||||
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
|
||||
|
||||
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
|
||||
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
|
||||
|
||||
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
|
||||
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
|
||||
|
||||
db::Instance bi = pb->child_inst ();
|
||||
db::Instance bi = pb->child_inst ();
|
||||
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (new_cells [i]);
|
||||
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
|
||||
|
||||
if (bi.has_prop_id ()) {
|
||||
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
|
||||
} else {
|
||||
pa.insert (bci);
|
||||
}
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (i->first);
|
||||
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
|
||||
|
||||
if (bi.has_prop_id ()) {
|
||||
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
|
||||
} else {
|
||||
pa.insert (bci);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,6 +311,11 @@ const db::EdgePair *DeepEdgePairs::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type DeepEdgePairs::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
|
||||
}
|
||||
|
||||
bool DeepEdgePairs::has_valid_edge_pairs () const
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public:
|
|||
virtual Box bbox () const;
|
||||
virtual bool empty () const;
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
virtual void apply_property_translator (const db::PropertiesTranslator &pt);
|
||||
|
|
|
|||
|
|
@ -438,6 +438,12 @@ DeepEdges::nth (size_t /*n*/) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat edge collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
DeepEdges::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
DeepEdges::has_valid_edges () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public:
|
|||
virtual bool is_merged () const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
|
|||
{
|
||||
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
|
||||
|
||||
if (empty ()) {
|
||||
|
||||
// Nothing to do
|
||||
return other.delegate ()->clone ();
|
||||
|
||||
} else if (other.empty ()) {
|
||||
if (other.empty ()) {
|
||||
|
||||
// Nothing to do
|
||||
return clone ();
|
||||
|
|
@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
|
|||
|
||||
return AsIfFlatRegion::xor_with (other, property_constraint);
|
||||
|
||||
} else if (empty ()) {
|
||||
|
||||
// Nothing to do, but to maintain the normal behavior, we have to map the other
|
||||
// input to our layout if neccessary
|
||||
if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) {
|
||||
return other.delegate ()->clone ();
|
||||
} else {
|
||||
std::unique_ptr<DeepRegion> other_deep_mapped (dynamic_cast<DeepRegion *> (clone ()));
|
||||
other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ());
|
||||
return other_deep_mapped.release ();
|
||||
}
|
||||
|
||||
} else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) {
|
||||
|
||||
return new DeepRegion (deep_layer ().derived ());
|
||||
|
|
|
|||
|
|
@ -332,6 +332,12 @@ const db::Text *DeepTexts::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat text collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
DeepTexts::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool DeepTexts::has_valid_texts () const
|
||||
{
|
||||
return false;
|
||||
|
|
@ -462,7 +468,7 @@ DeepTexts::apply_filter (const TextFilterBase &filter, bool with_true, bool with
|
|||
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Texts); ! si.at_end (); ++si) {
|
||||
db::Text text;
|
||||
si->text (text);
|
||||
if (filter.selected (text.transformed (*v), si->prop_id ())) {
|
||||
if (filter.selected (text.transformed (tr), si->prop_id ())) {
|
||||
if (st_true) {
|
||||
st_true->insert (*si);
|
||||
}
|
||||
|
|
@ -528,8 +534,15 @@ DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) cons
|
|||
return shape_collection_processed_impl<db::Text, db::Polygon, db::DeepRegion> (deep_layer (), filter);
|
||||
}
|
||||
|
||||
RegionDelegate *DeepTexts::polygons (db::Coord e) const
|
||||
RegionDelegate *DeepTexts::polygons (db::Coord e, const tl::Variant &text_prop) const
|
||||
{
|
||||
db::property_names_id_type key_id = 0;
|
||||
if (! text_prop.is_nil ()) {
|
||||
key_id = db::property_names_id (text_prop);
|
||||
}
|
||||
|
||||
std::map<std::string, db::properties_id_type> value_ids;
|
||||
|
||||
db::DeepLayer new_layer = deep_layer ().derived ();
|
||||
db::Layout &layout = const_cast<db::Layout &> (deep_layer ().layout ());
|
||||
|
||||
|
|
@ -539,7 +552,18 @@ RegionDelegate *DeepTexts::polygons (db::Coord e) const
|
|||
db::Box box = s->bbox ();
|
||||
box.enlarge (db::Vector (e, e));
|
||||
db::Polygon poly (box);
|
||||
output.insert (db::PolygonRef (poly, layout.shape_repository ()));
|
||||
if (key_id == 0) {
|
||||
output.insert (db::PolygonRef (poly, layout.shape_repository ()));
|
||||
} else {
|
||||
std::string value (s->text_string ());
|
||||
auto v = value_ids.find (value);
|
||||
if (v == value_ids.end ()) {
|
||||
db::PropertiesSet ps;
|
||||
ps.insert_by_id (key_id, db::property_values_id (value));
|
||||
v = value_ids.insert (std::make_pair (value, db::properties_id (ps))).first;
|
||||
}
|
||||
output.insert (db::PolygonRefWithProperties (db::PolygonRef (poly, layout.shape_repository ()), v->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -86,7 +87,7 @@ public:
|
|||
virtual TextsDelegate *add_in_place (const Texts &other);
|
||||
virtual TextsDelegate *add (const Texts &other) const;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const;
|
||||
virtual EdgesDelegate *edges () const;
|
||||
|
||||
virtual TextsDelegate *in (const Texts &, bool) const;
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@ public:
|
|||
/**
|
||||
* @brief Returns the nth edge pair
|
||||
*
|
||||
* This operation is available only for flat regions - i.e. such for which
|
||||
* This operation is available only for flat edge pair collections - i.e. such for which
|
||||
* "has_valid_edge_pairs" is true.
|
||||
*/
|
||||
const db::EdgePair *nth (size_t n) const
|
||||
|
|
@ -686,6 +686,17 @@ public:
|
|||
return mp_delegate->nth (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the nth edge pair's property ID
|
||||
*
|
||||
* This operation is available only for flat edge pair collections - i.e. such for which
|
||||
* "has_valid_edge_pairs" is true.
|
||||
*/
|
||||
db::properties_id_type nth_prop_id (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth_prop_id (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces flattening of the edge pair collection
|
||||
*
|
||||
|
|
|
|||
|
|
@ -273,6 +273,7 @@ public:
|
|||
virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const = 0;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
|
||||
virtual bool has_valid_edge_pairs () const = 0;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const = 0;
|
||||
|
|
|
|||
|
|
@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare
|
|||
{
|
||||
if (edge_xmax (a) < edge_xmin (b)) {
|
||||
return true;
|
||||
} else if (edge_xmin (a) >= edge_xmax (b)) {
|
||||
} else if (edge_xmin (a) > edge_xmax (b)) {
|
||||
return false;
|
||||
} else {
|
||||
C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2);
|
||||
|
|
|
|||
|
|
@ -1440,13 +1440,24 @@ public:
|
|||
/**
|
||||
* @brief Returns the nth edge
|
||||
*
|
||||
* This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true.
|
||||
* This operation is available only for flat edge collections - i.e. such for which "has_valid_edges" is true.
|
||||
*/
|
||||
const db::Edge *nth (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the nth edge's property ID
|
||||
*
|
||||
* This operation is available only for flat edge collections - i.e. such for which
|
||||
* "has_valid_edges" is true.
|
||||
*/
|
||||
db::properties_id_type nth_prop_id (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth_prop_id (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces flattening of the edge collection
|
||||
*
|
||||
|
|
|
|||
|
|
@ -280,6 +280,7 @@ public:
|
|||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> in_and_out (const Edges &) const = 0;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const = 0;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
|
||||
virtual bool has_valid_edges () const = 0;
|
||||
virtual bool has_valid_merged_edges () const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public:
|
|||
virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); }
|
||||
|
||||
virtual const db::EdgePair *nth (size_t) const { tl_assert (false); }
|
||||
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_edge_pairs () const { return true; }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ public:
|
|||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> in_and_out (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
|
||||
|
||||
virtual const db::Edge *nth (size_t) const { tl_assert (false); }
|
||||
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_edges () const { return true; }
|
||||
virtual bool has_valid_merged_edges () const { return true; }
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ EmptyTexts::clone () const
|
|||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyTexts::polygons (db::Coord) const
|
||||
EmptyTexts::polygons (db::Coord, const tl::Variant &) const
|
||||
{
|
||||
return new EmptyRegion ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public:
|
|||
virtual TextsDelegate *processed (const TextProcessorBase &) const { return new EmptyTexts (); }
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &) const;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const;
|
||||
virtual EdgesDelegate *edges () const;
|
||||
|
||||
virtual TextsDelegate *add_in_place (const Texts &other);
|
||||
|
|
@ -71,6 +71,7 @@ public:
|
|||
virtual TextsDelegate *in (const Texts &, bool) const { return new EmptyTexts (); }
|
||||
|
||||
virtual const db::Text *nth (size_t) const { tl_assert (false); }
|
||||
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_texts () const { return true; }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
|
|
|||
|
|
@ -173,7 +173,46 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
|
|||
|
||||
const db::EdgePair *FlatEdgePairs::nth (size_t n) const
|
||||
{
|
||||
return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
// NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties
|
||||
|
||||
if (n >= mp_edge_pairs->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::EdgePair, db::unstable_layer_tag> &l = mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return &l.begin () [n];
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgePairWithProperties, db::unstable_layer_tag> &lp = mp_edge_pairs->get_layer<db::EdgePairWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return &lp.begin () [n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::properties_id_type FlatEdgePairs::nth_prop_id (size_t n) const
|
||||
{
|
||||
// NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties
|
||||
|
||||
if (n >= mp_edge_pairs->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::EdgePair, db::unstable_layer_tag> &l = mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return 0;
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgePairWithProperties, db::unstable_layer_tag> &lp = mp_edge_pairs->get_layer<db::EdgePairWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return lp.begin () [n].properties_id ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FlatEdgePairs::has_valid_edge_pairs () const
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public:
|
|||
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -361,7 +361,46 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other)
|
|||
|
||||
const db::Edge *FlatEdges::nth (size_t n) const
|
||||
{
|
||||
return n < mp_edges->size () ? &mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
// NOTE: this assumes that we iterate over non-property edges first and then over edges with properties
|
||||
|
||||
if (n >= mp_edges->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Edge, db::unstable_layer_tag> &l = mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return &l.begin () [n];
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgeWithProperties, db::unstable_layer_tag> &lp = mp_edges->get_layer<db::EdgeWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return &lp.begin () [n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::properties_id_type FlatEdges::nth_prop_id (size_t n) const
|
||||
{
|
||||
// NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties
|
||||
|
||||
if (n >= mp_edges->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Edge, db::unstable_layer_tag> &l = mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return 0;
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::EdgeWithProperties, db::unstable_layer_tag> &lp = mp_edges->get_layer<db::EdgeWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return lp.begin () [n].properties_id ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FlatEdges::has_valid_edges () const
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public:
|
|||
virtual EdgesDelegate *add (const Edges &other) const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,46 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other)
|
|||
|
||||
const db::Text *FlatTexts::nth (size_t n) const
|
||||
{
|
||||
return n < mp_texts->size () ? &mp_texts->get_layer<db::Text, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
// NOTE: this assumes that we iterate over non-property texts first and then over texts with properties
|
||||
|
||||
if (n >= mp_texts->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Text, db::unstable_layer_tag> &l = mp_texts->get_layer<db::Text, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return &l.begin () [n];
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::TextWithProperties, db::unstable_layer_tag> &lp = mp_texts->get_layer<db::TextWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return &lp.begin () [n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::properties_id_type FlatTexts::nth_prop_id (size_t n) const
|
||||
{
|
||||
// NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties
|
||||
|
||||
if (n >= mp_texts->size ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::layer<db::Text, db::unstable_layer_tag> &l = mp_texts->get_layer<db::Text, db::unstable_layer_tag> ();
|
||||
if (n < l.size ()) {
|
||||
return 0;
|
||||
}
|
||||
n -= l.size ();
|
||||
|
||||
const db::layer<db::TextWithProperties, db::unstable_layer_tag> &lp = mp_texts->get_layer<db::TextWithProperties, db::unstable_layer_tag> ();
|
||||
if (n < lp.size ()) {
|
||||
return lp.begin () [n].properties_id ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FlatTexts::has_valid_texts () const
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public:
|
|||
virtual TextsDelegate *add (const Texts &other) const;
|
||||
|
||||
virtual const db::Text *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_texts () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace db
|
|||
* for use with std::unordered_map and std::unordered_set
|
||||
*/
|
||||
|
||||
namespace std
|
||||
namespace tl
|
||||
{
|
||||
inline size_t hfunc_coord (db::DCoord d)
|
||||
{
|
||||
|
|
@ -89,15 +89,6 @@ namespace std
|
|||
return hfunc_coord (o.x (), hfunc_coord (o.y ()));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::point<C> >
|
||||
{
|
||||
size_t operator() (const db::point<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a vector
|
||||
*/
|
||||
|
|
@ -114,15 +105,6 @@ namespace std
|
|||
return hfunc_coord (o.x (), hfunc_coord (o.y ()));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::vector<C> >
|
||||
{
|
||||
size_t operator() (const db::vector<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a box
|
||||
*/
|
||||
|
|
@ -139,15 +121,6 @@ namespace std
|
|||
return hfunc (o.p1 (), hfunc (o.p2 ()));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::box<C> >
|
||||
{
|
||||
size_t operator() (const db::box<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for an edge
|
||||
*/
|
||||
|
|
@ -164,15 +137,6 @@ namespace std
|
|||
return hfunc (o.p1 (), hfunc (o.p2 ()));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::edge<C> >
|
||||
{
|
||||
size_t operator() (const db::edge<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for an edge pair
|
||||
*/
|
||||
|
|
@ -189,15 +153,6 @@ namespace std
|
|||
return hfunc (o.lesser (), hfunc (o.greater (), hfunc (int (o.is_symmetric ()))));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::edge_pair<C> >
|
||||
{
|
||||
size_t operator() (const db::edge_pair<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a text object
|
||||
*/
|
||||
|
|
@ -220,15 +175,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::text<C> >
|
||||
{
|
||||
size_t operator() (const db::text<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a path
|
||||
*/
|
||||
|
|
@ -259,15 +205,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::path<C> >
|
||||
{
|
||||
size_t operator() (const db::path<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a polygon contour
|
||||
*/
|
||||
|
|
@ -294,15 +231,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::polygon_contour<C> >
|
||||
{
|
||||
size_t operator() (const db::path<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a polygon
|
||||
*/
|
||||
|
|
@ -330,15 +258,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::polygon<C> >
|
||||
{
|
||||
size_t operator() (const db::polygon<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a simple polygon
|
||||
*/
|
||||
|
|
@ -355,15 +274,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::simple_polygon<C> >
|
||||
{
|
||||
size_t operator() (const db::simple_polygon<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a simple transformation
|
||||
*/
|
||||
|
|
@ -380,15 +290,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::simple_trans<C> >
|
||||
{
|
||||
size_t operator() (const db::simple_trans<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a displacement transformation
|
||||
*/
|
||||
|
|
@ -405,15 +306,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::disp_trans<C> >
|
||||
{
|
||||
size_t operator() (const db::disp_trans<C> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a complex transformation
|
||||
*/
|
||||
|
|
@ -434,15 +326,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class I, class F, class R>
|
||||
struct hash <db::complex_trans<I, F, R> >
|
||||
{
|
||||
size_t operator() (const db::complex_trans<I, F, R> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash value for a db::CellInstArray and db::DCellInstArray
|
||||
*/
|
||||
|
|
@ -482,15 +365,6 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct hash <db::array <db::CellInst, db::simple_trans<C> > >
|
||||
{
|
||||
size_t operator() (const db::array <db::CellInst, db::simple_trans<C> > &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for an object with properties
|
||||
*/
|
||||
|
|
@ -507,15 +381,6 @@ namespace std
|
|||
return hfunc ((const O &) o, db::hash_for_properties_id (o.properties_id ()));
|
||||
}
|
||||
|
||||
template <class O>
|
||||
struct hash <db::object_with_properties<O> >
|
||||
{
|
||||
size_t operator() (const db::object_with_properties<O> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a shape reference
|
||||
*/
|
||||
|
|
@ -532,15 +397,6 @@ namespace std
|
|||
return hfunc (*o.ptr (), hfunc (o.trans ()));
|
||||
}
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::shape_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::shape_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a polygon reference
|
||||
*/
|
||||
|
|
@ -557,15 +413,6 @@ namespace std
|
|||
return hfunc (*o.ptr (), hfunc (o.trans ()));
|
||||
}
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::polygon_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::polygon_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a path reference
|
||||
*/
|
||||
|
|
@ -582,15 +429,6 @@ namespace std
|
|||
return hfunc (*o.ptr (), hfunc (o.trans ()));
|
||||
}
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::path_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::path_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a text reference
|
||||
*/
|
||||
|
|
@ -607,15 +445,6 @@ namespace std
|
|||
return hfunc (*o.ptr (), hfunc (o.trans ()));
|
||||
}
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::text_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::text_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash value for a db::LayerProperties object
|
||||
*/
|
||||
|
|
@ -637,14 +466,191 @@ namespace std
|
|||
return hfunc (o, size_t (0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class C>
|
||||
struct hash <db::point<C> >
|
||||
{
|
||||
size_t operator() (const db::point<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::vector<C> >
|
||||
{
|
||||
size_t operator() (const db::vector<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::box<C> >
|
||||
{
|
||||
size_t operator() (const db::box<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::edge<C> >
|
||||
{
|
||||
size_t operator() (const db::edge<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::edge_pair<C> >
|
||||
{
|
||||
size_t operator() (const db::edge_pair<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::text<C> >
|
||||
{
|
||||
size_t operator() (const db::text<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::path<C> >
|
||||
{
|
||||
size_t operator() (const db::path<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::polygon_contour<C> >
|
||||
{
|
||||
size_t operator() (const db::path<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::polygon<C> >
|
||||
{
|
||||
size_t operator() (const db::polygon<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::simple_polygon<C> >
|
||||
{
|
||||
size_t operator() (const db::simple_polygon<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::simple_trans<C> >
|
||||
{
|
||||
size_t operator() (const db::simple_trans<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::disp_trans<C> >
|
||||
{
|
||||
size_t operator() (const db::disp_trans<C> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class I, class F, class R>
|
||||
struct hash <db::complex_trans<I, F, R> >
|
||||
{
|
||||
size_t operator() (const db::complex_trans<I, F, R> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
struct hash <db::array <db::CellInst, db::simple_trans<C> > >
|
||||
{
|
||||
size_t operator() (const db::array <db::CellInst, db::simple_trans<C> > &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class O>
|
||||
struct hash <db::object_with_properties<O> >
|
||||
{
|
||||
size_t operator() (const db::object_with_properties<O> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::shape_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::shape_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::polygon_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::polygon_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::path_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::path_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Shape, class Trans>
|
||||
struct hash <db::text_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::text_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash <db::LayerProperties>
|
||||
{
|
||||
size_t operator() (const db::LayerProperties &o) const
|
||||
{
|
||||
return hfunc (o);
|
||||
return tl::hfunc (o);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -152,15 +152,17 @@ static std::pair<bool, std::set<db::Box> > compute_clip_variant (const db::Box &
|
|||
}
|
||||
|
||||
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
|
||||
: mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
|
||||
{
|
||||
set_shape_receiver (pipe);
|
||||
reset ();
|
||||
}
|
||||
|
||||
HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
|
||||
: mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
|
||||
{
|
||||
set_shape_receiver (pipe);
|
||||
reset ();
|
||||
}
|
||||
|
||||
HierarchyBuilder::~HierarchyBuilder ()
|
||||
|
|
@ -178,6 +180,8 @@ void
|
|||
HierarchyBuilder::reset ()
|
||||
{
|
||||
m_initial_pass = true;
|
||||
m_cm_new_entry = false;
|
||||
|
||||
mp_initial_cell = 0;
|
||||
|
||||
m_cells_to_be_filled.clear ();
|
||||
|
|
@ -186,7 +190,6 @@ HierarchyBuilder::reset ()
|
|||
m_cells_seen.clear ();
|
||||
m_cell_stack.clear ();
|
||||
m_cm_entry = null_iterator;
|
||||
m_cm_new_entry = false;
|
||||
}
|
||||
|
||||
const std::pair<db::cell_index_type, std::string> &
|
||||
|
|
@ -351,7 +354,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
|
|||
if (! key.clip_region.empty ()) {
|
||||
cn += "$CLIP_VAR";
|
||||
description += "CLIP";
|
||||
|
||||
}
|
||||
if (key.inactive) {
|
||||
cn += "$DIS";
|
||||
|
|
@ -383,7 +385,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
|
|||
}
|
||||
|
||||
HierarchyBuilder::new_inst_mode
|
||||
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes)
|
||||
{
|
||||
if (all) {
|
||||
|
||||
|
|
@ -402,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
|
|||
}
|
||||
|
||||
// To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array.
|
||||
return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
|
||||
return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -413,7 +415,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
|
|||
}
|
||||
|
||||
bool
|
||||
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
if (all) {
|
||||
|
||||
|
|
@ -441,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
|
|||
}
|
||||
}
|
||||
|
||||
return (m_cells_seen.find (key) == m_cells_seen.end ());
|
||||
return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,8 +294,8 @@ public:
|
|||
virtual void end (const RecursiveShapeIterator *iter);
|
||||
virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell);
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all);
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all);
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes);
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes);
|
||||
virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2282,7 +2282,7 @@ private:
|
|||
// --------------------------------------------------------------------------------
|
||||
// LayoutQueryIterator implementation
|
||||
|
||||
LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, tl::Eval *parent_eval, tl::AbsoluteProgress *progress)
|
||||
LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, db::Cell *cell, tl::Eval *parent_eval, tl::AbsoluteProgress *progress)
|
||||
: mp_q (const_cast<db::LayoutQuery *> (&q)), mp_layout (layout), m_eval (parent_eval), m_layout_ctx (layout, true /*can modify*/), mp_progress (progress), m_initialized (false)
|
||||
{
|
||||
m_eval.set_ctx_handler (&m_layout_ctx);
|
||||
|
|
@ -2290,13 +2290,16 @@ LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layo
|
|||
for (unsigned int i = 0; i < mp_q->properties (); ++i) {
|
||||
m_eval.define_function (mp_q->property_name (i), new FilterStateFunction (i, &m_state));
|
||||
}
|
||||
if (cell && cell->layout ()) {
|
||||
m_eval.set_var ("_", cell->layout ()->cell_name (cell->cell_index ()));
|
||||
}
|
||||
|
||||
// Avoid update() calls while iterating in modifying mode
|
||||
mp_layout->update ();
|
||||
mp_layout->start_changes ();
|
||||
}
|
||||
|
||||
LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, tl::Eval *parent_eval, tl::AbsoluteProgress *progress)
|
||||
LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, const Cell *cell, tl::Eval *parent_eval, tl::AbsoluteProgress *progress)
|
||||
: mp_q (const_cast<db::LayoutQuery *> (&q)), mp_layout (const_cast <db::Layout *> (layout)), m_eval (parent_eval), m_layout_ctx (layout), mp_progress (progress), m_initialized (false)
|
||||
{
|
||||
// TODO: check whether the query is a modifying one (with .. do, delete)
|
||||
|
|
@ -2306,6 +2309,9 @@ LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout
|
|||
for (unsigned int i = 0; i < mp_q->properties (); ++i) {
|
||||
m_eval.define_function (mp_q->property_name (i), new FilterStateFunction (i, &m_state));
|
||||
}
|
||||
if (cell && cell->layout ()) {
|
||||
m_eval.set_var ("_", cell->layout ()->cell_name (cell->cell_index ()));
|
||||
}
|
||||
|
||||
// Avoid update() calls while iterating in modifying mode
|
||||
mp_layout->start_changes ();
|
||||
|
|
@ -2850,9 +2856,9 @@ LayoutQuery::dump () const
|
|||
}
|
||||
|
||||
void
|
||||
LayoutQuery::execute (db::Layout &layout, tl::Eval *context)
|
||||
LayoutQuery::execute (db::Layout &layout, db::Cell *cell, tl::Eval *context)
|
||||
{
|
||||
LayoutQueryIterator iq (*this, &layout, context);
|
||||
LayoutQueryIterator iq (*this, &layout, cell, context);
|
||||
while (! iq.at_end ()) {
|
||||
++iq;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ public:
|
|||
*
|
||||
* The context provides a way to define variables and functions.
|
||||
*/
|
||||
void execute (db::Layout &layout, tl::Eval *context = 0);
|
||||
void execute (db::Layout &layout, db::Cell *cell = 0, tl::Eval *context = 0);
|
||||
|
||||
/**
|
||||
* @brief A dump method (for debugging)
|
||||
|
|
@ -578,7 +578,7 @@ public:
|
|||
* @param q The query that this iterator walks over
|
||||
* @param layout The layout to which the query is applied
|
||||
*/
|
||||
LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0);
|
||||
LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, db::Cell *cell = 0, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0);
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
|
|
@ -586,7 +586,7 @@ public:
|
|||
* @param q The query that this iterator walks over
|
||||
* @param layout The layout to which the query is applied
|
||||
*/
|
||||
LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0);
|
||||
LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, const db::Cell *cell = 0, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
|
|
|
|||
|
|
@ -692,6 +692,8 @@ void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Net &net,
|
|||
*outp << tl::to_string (id);
|
||||
if (! net.name ().empty ()) {
|
||||
TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ());
|
||||
} else if (net.id () != id) {
|
||||
TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.expanded_name ());
|
||||
}
|
||||
|
||||
*outp << endl;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db
|
|||
void PolygonRefToShapesGenerator::put (const db::Polygon &polygon)
|
||||
{
|
||||
tl::MutexLocker locker (&mp_layout->lock ());
|
||||
if (m_prop_id != 0) {
|
||||
if (polygon.is_empty ()) {
|
||||
// ignore empty polygons
|
||||
} else if (m_prop_id != 0) {
|
||||
mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id));
|
||||
} else {
|
||||
mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
|
||||
|
|
@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size
|
|||
void
|
||||
PolygonSplitter::put (const db::Polygon &poly)
|
||||
{
|
||||
if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
|
||||
if (poly.is_empty ()) {
|
||||
// ignore empty polygons
|
||||
} else if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
|
||||
|
||||
std::vector <db::Polygon> split_polygons;
|
||||
db::split_polygon (poly, split_polygons);
|
||||
|
|
|
|||
|
|
@ -190,6 +190,12 @@ OriginalLayerEdgePairs::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
OriginalLayerEdgePairs::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
OriginalLayerEdgePairs::has_valid_edge_pairs () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public:
|
|||
virtual bool empty () const;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -233,6 +233,12 @@ OriginalLayerEdges::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
OriginalLayerEdges::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
OriginalLayerEdges::has_valid_edges () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public:
|
|||
virtual bool is_merged () const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -190,6 +190,12 @@ OriginalLayerTexts::nth (size_t) const
|
|||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
|
||||
}
|
||||
|
||||
db::properties_id_type
|
||||
OriginalLayerTexts::nth_prop_id (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
|
||||
}
|
||||
|
||||
bool
|
||||
OriginalLayerTexts::has_valid_texts () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public:
|
|||
virtual bool empty () const;
|
||||
|
||||
virtual const db::Text *nth (size_t n) const;
|
||||
virtual db::properties_id_type nth_prop_id (size_t n) const;
|
||||
virtual bool has_valid_texts () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
|
|
|||
|
|
@ -372,14 +372,7 @@ void path<C>::create_shifted_points (C start, C end, C width, bool forward, Iter
|
|||
double l1 = db::vprod (nnd - nd, eed) / dv;
|
||||
double l2 = db::vprod (nd - nnd, ed) / dv;
|
||||
|
||||
if ((l1 < -db::epsilon) != (l2 < -db::epsilon)) {
|
||||
|
||||
// No well-formed intersection (reflecting edge) ->
|
||||
// create a direct connection
|
||||
*pts++ = *pp + vector<C> (nd);
|
||||
*pts++ = *pp + vector<C> (nnd);
|
||||
|
||||
} else if (l1 < l1min - db::epsilon || l2 < l2min - db::epsilon) {
|
||||
if (l1 < l1min - db::epsilon || l2 < l2min - db::epsilon) {
|
||||
|
||||
// Segments are too short - the won't intersect: In this case we create a loop of three
|
||||
// points which define the area in self-overlapping way but confined to the path within
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ PropertiesSet::hash () const
|
|||
tl::MutexLocker locker (&lock);
|
||||
|
||||
if (! m_hash) {
|
||||
m_hash = std::hfunc (to_map ());
|
||||
m_hash = tl::hfunc (to_map ());
|
||||
if (! m_hash) {
|
||||
// avoid 0 value as this is reserved for "not computed yet"
|
||||
m_hash = size_t (1);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2025 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "dbQuadTree.h"
|
||||
|
||||
|
|
@ -0,0 +1,825 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2025 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_dbQuadTree
|
||||
#define HDR_dbQuadTree
|
||||
|
||||
#include "dbBox.h"
|
||||
#include "tlLog.h"
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The quad tree node implementation class
|
||||
*/
|
||||
template <class T, class BC, size_t thr, class CMP>
|
||||
class quad_tree_node
|
||||
{
|
||||
public:
|
||||
typedef typename BC::box_type box_type;
|
||||
typedef typename box_type::point_type point_type;
|
||||
typedef typename box_type::vector_type vector_type;
|
||||
typedef std::vector<T> objects_vector;
|
||||
typedef db::coord_traits<typename box_type::coord_type> coord_traits;
|
||||
|
||||
quad_tree_node (const point_type ¢er)
|
||||
: m_center (center)
|
||||
{
|
||||
init (true);
|
||||
}
|
||||
|
||||
~quad_tree_node ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
quad_tree_node (const quad_tree_node &other)
|
||||
: m_center (center)
|
||||
{
|
||||
init (true);
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
quad_tree_node &operator= (const quad_tree_node &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
clear ();
|
||||
m_center = other.m_center;
|
||||
m_objects = other.m_objects;
|
||||
if (! other.is_leaf ()) {
|
||||
init (false);
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (other.m_q[i]) {
|
||||
m_q[i] = other.m_q[i]->clone ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
quad_tree_node (quad_tree_node &&other)
|
||||
{
|
||||
init (true);
|
||||
swap (other);
|
||||
}
|
||||
|
||||
quad_tree_node &operator= (quad_tree_node &&other)
|
||||
{
|
||||
swap (other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap (quad_tree_node &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
std::swap (m_center, other.m_center);
|
||||
m_objects.swap (other.m_objects);
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
std::swap (m_q[i], other.m_q[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quad_tree_node *clone () const
|
||||
{
|
||||
quad_tree_node *node = new quad_tree_node (m_center);
|
||||
*node = *this;
|
||||
return node;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
m_objects.clear ();
|
||||
if (! is_leaf ()) {
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (m_q[i]) {
|
||||
delete m_q[i];
|
||||
}
|
||||
m_q[i] = 0;
|
||||
}
|
||||
init (true);
|
||||
}
|
||||
}
|
||||
|
||||
const point_type ¢er () const
|
||||
{
|
||||
return m_center;
|
||||
}
|
||||
|
||||
void insert_top (const T &value, const box_type &total_box, const box_type &b)
|
||||
{
|
||||
insert (value, propose_ucenter (total_box), b);
|
||||
}
|
||||
|
||||
bool erase (const T &value, const box_type &b)
|
||||
{
|
||||
int n = quad_for (b);
|
||||
|
||||
if (is_leaf () || n < 0) {
|
||||
|
||||
for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
|
||||
if (CMP () (*i, value)) {
|
||||
m_objects.erase (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (m_q[n]) {
|
||||
|
||||
if (m_q[n]->erase (value, b)) {
|
||||
if (m_q[n]->empty ()) {
|
||||
delete m_q[n];
|
||||
m_q[n] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const objects_vector &objects () const
|
||||
{
|
||||
return m_objects;
|
||||
}
|
||||
|
||||
box_type q_box (unsigned int n) const
|
||||
{
|
||||
if (! is_leaf () && m_q[n]) {
|
||||
return m_q[n]->box (m_center);
|
||||
} else {
|
||||
return box_type ();
|
||||
}
|
||||
}
|
||||
|
||||
quad_tree_node *node (unsigned int n) const
|
||||
{
|
||||
tl_assert (! is_leaf ());
|
||||
return m_q [n];
|
||||
}
|
||||
|
||||
bool empty () const
|
||||
{
|
||||
if (m_objects.empty ()) {
|
||||
if (! is_leaf ()) {
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n] && ! m_q[n]->empty ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size () const
|
||||
{
|
||||
size_t count = m_objects.size ();
|
||||
if (! is_leaf ()) {
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n]) {
|
||||
count += m_q[n]->size ();
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t levels () const
|
||||
{
|
||||
size_t l = 1;
|
||||
if (! is_leaf ()) {
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n]) {
|
||||
l = std::max (l, m_q[n]->levels () + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
bool check_top (const box_type &total_box) const
|
||||
{
|
||||
return check (propose_ucenter (total_box));
|
||||
}
|
||||
|
||||
private:
|
||||
point_type m_center;
|
||||
quad_tree_node *m_q [4];
|
||||
objects_vector m_objects;
|
||||
|
||||
void init (bool is_leaf)
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
m_q[i] = 0;
|
||||
}
|
||||
if (is_leaf) {
|
||||
m_q[0] = reinterpret_cast<quad_tree_node *> (1);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_leaf () const
|
||||
{
|
||||
return m_q[0] == reinterpret_cast<quad_tree_node *> (1);
|
||||
}
|
||||
|
||||
int quad_for (const box_type &box) const
|
||||
{
|
||||
int sx = coord_traits::less (box.right (), m_center.x ()) ? 0 : (coord_traits::less (m_center.x (), box.left ()) ? 1 : -1);
|
||||
int sy = coord_traits::less (box.top (), m_center.y ()) ? 0 : (coord_traits::less (m_center.y (), box.bottom ()) ? 2 : -1);
|
||||
if (sx < 0 || sy < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return sx + sy;
|
||||
}
|
||||
}
|
||||
|
||||
box_type box (const point_type &ucenter) const
|
||||
{
|
||||
return box_type (ucenter, ucenter - (ucenter - m_center) * 2.0);
|
||||
}
|
||||
|
||||
box_type q (unsigned int n, const point_type &ucenter) const
|
||||
{
|
||||
// NOTE: with this definition the opposite quad index is 3 - n
|
||||
|
||||
vector_type vx (std::abs (ucenter.x () - m_center.x ()), 0);
|
||||
vector_type vy (0, std::abs (ucenter.y () - m_center.y ()));
|
||||
switch (n) {
|
||||
case 0:
|
||||
return box_type (m_center - vx - vy, m_center);
|
||||
case 1:
|
||||
return box_type (m_center - vy, m_center + vx);
|
||||
case 2:
|
||||
return box_type (m_center - vx, m_center + vy);
|
||||
default:
|
||||
return box_type (m_center, m_center + vx + vy);
|
||||
}
|
||||
}
|
||||
|
||||
void split (const point_type &ucenter)
|
||||
{
|
||||
init (false);
|
||||
|
||||
objects_vector ov;
|
||||
ov.swap (m_objects);
|
||||
|
||||
for (auto o = ov.begin (); o != ov.end (); ++o) {
|
||||
insert (*o, ucenter, BC () (*o));
|
||||
}
|
||||
}
|
||||
|
||||
void insert (const T &value, const point_type &ucenter, const box_type &b)
|
||||
{
|
||||
if (is_leaf () && m_objects.size () + 1 < thr) {
|
||||
|
||||
m_objects.push_back (value);
|
||||
|
||||
} else {
|
||||
|
||||
if (is_leaf ()) {
|
||||
split (ucenter);
|
||||
}
|
||||
|
||||
if (inside (b, box (ucenter))) {
|
||||
|
||||
int n = quad_for (b);
|
||||
if (n < 0) {
|
||||
m_objects.push_back (value);
|
||||
} else {
|
||||
if (! m_q[n]) {
|
||||
box_type bq = q (n, ucenter);
|
||||
m_q[n] = new quad_tree_node (bq.center ());
|
||||
}
|
||||
m_q[n]->insert (value, m_center, b);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tl_assert (m_q[0] || m_q[1] || m_q[2] || m_q[3]);
|
||||
point_type new_ucenter = m_center - (m_center - ucenter) * 2.0;
|
||||
grow (new_ucenter);
|
||||
insert (value, new_ucenter, b);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void grow (const point_type &ucenter)
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (m_q[i]) {
|
||||
quad_tree_node *n = m_q[i];
|
||||
m_q[i] = new quad_tree_node (q (i, ucenter).center ());
|
||||
m_q[i]->init (false);
|
||||
m_q[i]->m_q[3 - i] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
point_type propose_ucenter (const box_type &total_box) const
|
||||
{
|
||||
if (! is_leaf ()) {
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (m_q[i]) {
|
||||
return m_center - (m_center - m_q[i]->center ()) * 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typename box_type::coord_type dx = std::max (std::abs (total_box.left () - m_center.x ()), std::abs (total_box.right () - m_center.y ()));
|
||||
typename box_type::coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ()));
|
||||
return m_center - vector_type (dx * 2, dy * 2);
|
||||
}
|
||||
|
||||
bool check (const point_type &ucenter) const
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
box_type bq = box (ucenter);
|
||||
|
||||
for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
|
||||
box_type b = BC () (*i);
|
||||
if (! inside (b, bq)) {
|
||||
tl::error << "Box " << b.to_string () << " not inside quad box " << bq.to_string ();
|
||||
result = false;
|
||||
}
|
||||
point_type ucenter_opp = m_center + (m_center - ucenter);
|
||||
if (coord_traits::equal (b.left (), ucenter.x ()) || coord_traits::equal (b.right (), ucenter.x ()) ||
|
||||
coord_traits::equal (b.bottom (), ucenter.y ()) || coord_traits::equal (b.top (), ucenter.y ()) ||
|
||||
coord_traits::equal (b.left (), ucenter_opp.x ()) || coord_traits::equal (b.right (), ucenter_opp.x ()) ||
|
||||
coord_traits::equal (b.bottom (), ucenter_opp.y ()) || coord_traits::equal (b.top (), ucenter_opp.y ())) {
|
||||
tl::error << "Box " << b.to_string () << " touches quad boundary " << ucenter.to_string () << " .. " << ucenter_opp.to_string ();
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_leaf ()) {
|
||||
|
||||
for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
|
||||
box_type b = BC () (*i);
|
||||
int n = quad_for (b);
|
||||
if (n >= 0) {
|
||||
tl::error << "Box " << b.to_string () << " on quad level not overlapping multiple quads";
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n]) {
|
||||
if (! m_q[n]->check (m_center)) {
|
||||
result = false;
|
||||
}
|
||||
box_type bbq = m_q[n]->box (m_center);
|
||||
if (! bbq.equal (q (n, ucenter))) {
|
||||
tl::error << "Quad not centered (quad box is " << bbq.to_string () << ", should be " << q (n, ucenter).to_string ();
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (m_objects.size () > thr) {
|
||||
tl::error << "Non-split object count exceeds threshold " << m_objects.size () << " > " << thr;
|
||||
result = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool inside (const box_type &box, const box_type &in)
|
||||
{
|
||||
if (box.empty () || in.empty ()) {
|
||||
return false;
|
||||
} else {
|
||||
return coord_traits::less (in.left (), box.left ()) && coord_traits::less (box.right (), in.right ()) &&
|
||||
coord_traits::less (in.bottom (), box.bottom ()) && coord_traits::less (box.top (), in.top ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The iterator implementation class
|
||||
*/
|
||||
template <class T, class BC, size_t thr, class CMP, class S>
|
||||
class quad_tree_iterator
|
||||
{
|
||||
public:
|
||||
typedef quad_tree_node<T, BC, thr, CMP> quad_tree_node_type;
|
||||
typedef typename BC::box_type box_type;
|
||||
|
||||
quad_tree_iterator ()
|
||||
: m_s (), m_i (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
quad_tree_iterator (const quad_tree_node_type *root, const S &s)
|
||||
: m_s (s), m_i (0)
|
||||
{
|
||||
m_stack.push_back (std::make_pair (root, -1));
|
||||
validate ();
|
||||
}
|
||||
|
||||
bool at_end () const
|
||||
{
|
||||
return m_stack.empty ();
|
||||
}
|
||||
|
||||
quad_tree_iterator &operator++ ()
|
||||
{
|
||||
++m_i;
|
||||
validate ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T &operator* () const
|
||||
{
|
||||
return m_stack.back ().first->objects () [m_i];
|
||||
}
|
||||
|
||||
const T *operator-> () const
|
||||
{
|
||||
return (m_stack.back ().first->objects ().begin () + m_i).operator-> ();
|
||||
}
|
||||
|
||||
public:
|
||||
S m_s;
|
||||
std::vector<std::pair<const quad_tree_node_type *, int> > m_stack;
|
||||
size_t m_i;
|
||||
|
||||
void validate ()
|
||||
{
|
||||
auto s = m_stack.end ();
|
||||
tl_assert (s != m_stack.begin ());
|
||||
--s;
|
||||
|
||||
const quad_tree_node_type *qn = s->first;
|
||||
while (m_i < s->first->objects ().size () && ! m_s.select (s->first->objects () [m_i])) {
|
||||
++m_i;
|
||||
}
|
||||
|
||||
if (m_i < qn->objects ().size ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_i = 0;
|
||||
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
box_type bq = qn->q_box (n);
|
||||
if (! bq.empty () && m_s.select_quad (bq)) {
|
||||
m_stack.back ().second = n;
|
||||
m_stack.push_back (std::make_pair (qn->node (n), -1));
|
||||
validate ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_stack.pop_back ();
|
||||
|
||||
while (! m_stack.empty ()) {
|
||||
|
||||
qn = m_stack.back ().first;
|
||||
|
||||
int &n = m_stack.back ().second;
|
||||
while (++n < 4) {
|
||||
box_type bq = qn->q_box (n);
|
||||
if (! bq.empty () && m_s.select_quad (bq)) {
|
||||
m_stack.push_back (std::make_pair (qn->node (n), -1));
|
||||
validate ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_stack.pop_back ();
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The selector for implementing the all-iterator
|
||||
*/
|
||||
template <class T, class BC>
|
||||
class quad_tree_always_sel
|
||||
{
|
||||
public:
|
||||
typedef typename BC::box_type box_type;
|
||||
|
||||
bool select (const T &) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool select_quad (const box_type &) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The selector for implementing the touching iterator
|
||||
*/
|
||||
template <class T, class BC>
|
||||
class quad_tree_touching_sel
|
||||
{
|
||||
public:
|
||||
typedef typename BC::box_type box_type;
|
||||
|
||||
quad_tree_touching_sel ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
quad_tree_touching_sel (const box_type &box)
|
||||
: m_box (box)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool select (const T &value) const
|
||||
{
|
||||
return select_quad (BC () (value));
|
||||
}
|
||||
|
||||
bool select_quad (const box_type &box) const
|
||||
{
|
||||
return m_box.touches (box);
|
||||
}
|
||||
|
||||
private:
|
||||
box_type m_box;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The selector for implementing the overlapping iterator
|
||||
*/
|
||||
template <class T, class BC>
|
||||
class quad_tree_overlapping_sel
|
||||
{
|
||||
public:
|
||||
typedef typename BC::box_type box_type;
|
||||
|
||||
quad_tree_overlapping_sel ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
quad_tree_overlapping_sel (const box_type &box)
|
||||
: m_box (box)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool select (const T &value) const
|
||||
{
|
||||
return select_quad (BC () (value));
|
||||
}
|
||||
|
||||
bool select_quad (const box_type &box) const
|
||||
{
|
||||
return m_box.overlaps (box);
|
||||
}
|
||||
|
||||
private:
|
||||
box_type m_box;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The default compare function
|
||||
*/
|
||||
template <class T>
|
||||
struct quad_tree_default_cmp
|
||||
{
|
||||
bool operator() (const T &a, const T &b) const
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A generic quad tree implementation
|
||||
*
|
||||
* In contrast to the box_tree implementation, this is a self-sorting implementation
|
||||
* which is more generic.
|
||||
*
|
||||
* @param T The value to be stored
|
||||
* @param BC The box converter
|
||||
* @param thr The number of items per leaf node before splitting
|
||||
* @param CMP The compare function (equality)
|
||||
*
|
||||
* T needs to have a type member named "coord_type".
|
||||
* BC is a function of T and delivers a db::box<coord_type> for T
|
||||
* CMP is a function that delivers a bool value for a pair of T (equality)
|
||||
*/
|
||||
template <class T, class BC, size_t thr = 10, class CMP = quad_tree_default_cmp<T>>
|
||||
class quad_tree
|
||||
{
|
||||
public:
|
||||
typedef quad_tree_node<T, BC, thr, CMP> quad_tree_node_type;
|
||||
typedef quad_tree_iterator<T, BC, thr, CMP, quad_tree_always_sel<T, BC> > quad_tree_flat_iterator;
|
||||
typedef quad_tree_iterator<T, BC, thr, CMP, quad_tree_touching_sel<T, BC> > quad_tree_touching_iterator;
|
||||
typedef quad_tree_iterator<T, BC, thr, CMP, quad_tree_overlapping_sel<T, BC> > quad_tree_overlapping_iterator;
|
||||
typedef typename BC::box_type box_type;
|
||||
typedef typename box_type::point_type point_type;
|
||||
typedef typename box_type::vector_type vector_type;
|
||||
typedef std::vector<T> objects_vector;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* This creates an empty tree.
|
||||
*/
|
||||
quad_tree ()
|
||||
: m_root (point_type ())
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
quad_tree (const quad_tree &other)
|
||||
: m_root (point_type ())
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
quad_tree &operator= (const quad_tree &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_root = other.m_root;
|
||||
m_total_box = other.m_total_box;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
quad_tree (quad_tree &&other)
|
||||
: m_root (point_type ())
|
||||
{
|
||||
swap (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment
|
||||
*/
|
||||
quad_tree &operator= (quad_tree &&other)
|
||||
{
|
||||
swap (other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Empties the tree
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_root.clear ();
|
||||
m_total_box = box_type ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps the tree with another
|
||||
*/
|
||||
void swap (quad_tree &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_root.swap (other.m_root);
|
||||
std::swap (m_total_box, other.m_total_box);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the tree is empty
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_root.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of items stored in the tree
|
||||
*/
|
||||
size_t size () const
|
||||
{
|
||||
return m_root.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of quad levels (for testing)
|
||||
*/
|
||||
size_t levels () const
|
||||
{
|
||||
return m_root.levels ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks the tree for consistency (for testing)
|
||||
*/
|
||||
bool check () const
|
||||
{
|
||||
return m_root.check_top (m_total_box);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an object into the tree
|
||||
*/
|
||||
void insert (const T &value)
|
||||
{
|
||||
box_type b = BC () (value);
|
||||
if (b.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_total_box += b;
|
||||
m_root.insert_top (value, m_total_box, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given element from the tree
|
||||
*
|
||||
* @return true, if the element was found and erased.
|
||||
*
|
||||
* If multiple elements of the same kind are stored, the
|
||||
* first one is erased.
|
||||
*/
|
||||
bool erase (const T &value)
|
||||
{
|
||||
return m_root.erase (value, BC () (value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief begin iterator for all elements
|
||||
*/
|
||||
quad_tree_flat_iterator begin () const
|
||||
{
|
||||
return quad_tree_flat_iterator (&m_root, quad_tree_always_sel<T, BC> ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief begin iterator for all elements overlapping the given box
|
||||
*/
|
||||
quad_tree_overlapping_iterator begin_overlapping (const box_type &box) const
|
||||
{
|
||||
if (m_total_box.overlaps (box)) {
|
||||
return quad_tree_overlapping_iterator (&m_root, quad_tree_overlapping_sel<T, BC> (box));
|
||||
} else {
|
||||
return quad_tree_overlapping_iterator ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief begin iterator for all elements touching the given box
|
||||
*/
|
||||
quad_tree_touching_iterator begin_touching (const box_type &box) const
|
||||
{
|
||||
if (m_total_box.touches (box)) {
|
||||
return quad_tree_touching_iterator (&m_root, quad_tree_touching_sel<T, BC> (box));
|
||||
} else {
|
||||
return quad_tree_touching_iterator ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
box_type m_total_box;
|
||||
quad_tree_node_type m_root;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
|
|||
m_layer = d.m_layer;
|
||||
mp_cell = d.mp_cell;
|
||||
m_current_layer = d.m_current_layer;
|
||||
m_skip_shapes = d.m_skip_shapes;
|
||||
m_skip_shapes_member = d.m_skip_shapes_member;
|
||||
m_shape = d.m_shape;
|
||||
m_trans = d.m_trans;
|
||||
m_global_trans = d.m_global_trans;
|
||||
|
|
@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
|
|||
m_local_complex_region_stack = d.m_local_complex_region_stack;
|
||||
m_local_region_stack = d.m_local_region_stack;
|
||||
m_skip_shapes_stack = d.m_skip_shapes_stack;
|
||||
m_skip_shapes_member_stack = d.m_skip_shapes_member_stack;
|
||||
m_needs_reinit = d.m_needs_reinit;
|
||||
m_inst_quad_id = d.m_inst_quad_id;
|
||||
m_inst_quad_id_stack = d.m_inst_quad_id_stack;
|
||||
|
|
@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
|
|||
m_local_region_stack.push_back (m_global_trans.inverted () * m_region);
|
||||
m_skip_shapes_stack.clear ();
|
||||
m_skip_shapes_stack.push_back (false);
|
||||
m_skip_shapes_member_stack.clear ();
|
||||
m_skip_shapes_member_stack.push_back (false);
|
||||
|
||||
m_local_complex_region_stack.clear ();
|
||||
if (mp_complex_region.get ()) {
|
||||
|
|
@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
|
|||
bool
|
||||
RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
|
||||
{
|
||||
bool skip_shapes = false;
|
||||
|
||||
if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) {
|
||||
|
||||
// Try some optimization: if the instance we're looking at is entirely covered
|
||||
// by a rectangle (other objects are too expensive to check), then we skip it
|
||||
//
|
||||
// We check 10 shapes max.
|
||||
|
||||
box_type inst_bx;
|
||||
if (m_inst->size () == 1) {
|
||||
inst_bx = m_inst->bbox (m_box_convert);
|
||||
} else {
|
||||
inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ());
|
||||
}
|
||||
|
||||
unsigned int l = m_has_layers ? m_layers.front () : m_layer;
|
||||
auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
|
||||
size_t nmax = 10;
|
||||
while (! si.at_end () && nmax-- > 0) {
|
||||
if (inst_bx.inside (si->rectangle ())) {
|
||||
skip_shapes = true;
|
||||
break;
|
||||
}
|
||||
++si;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tl_assert (mp_layout);
|
||||
|
||||
m_trans_stack.push_back (m_trans);
|
||||
|
|
@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
|
|||
}
|
||||
|
||||
m_local_region_stack.push_back (new_region);
|
||||
m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes);
|
||||
m_skip_shapes_stack.push_back (m_skip_shapes);
|
||||
m_skip_shapes_member_stack.push_back (m_skip_shapes_member);
|
||||
|
||||
if (! m_local_complex_region_stack.empty ()) {
|
||||
|
||||
|
|
@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const
|
|||
m_inst = m_inst_iterators.back ();
|
||||
m_inst_array = m_inst_array_iterators.back ();
|
||||
m_inst_quad_id = m_inst_quad_id_stack.back ();
|
||||
m_skip_shapes = m_skip_shapes_stack.back ();
|
||||
m_skip_shapes_member = m_skip_shapes_member_stack.back ();
|
||||
m_inst_iterators.pop_back ();
|
||||
m_inst_array_iterators.pop_back ();
|
||||
m_inst_quad_id_stack.pop_back ();
|
||||
|
|
@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const
|
|||
m_cells.pop_back ();
|
||||
m_local_region_stack.pop_back ();
|
||||
m_skip_shapes_stack.pop_back ();
|
||||
m_skip_shapes_member_stack.pop_back ();
|
||||
if (! m_local_complex_region_stack.empty ()) {
|
||||
m_local_complex_region_stack.pop_back ();
|
||||
}
|
||||
|
|
@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const
|
|||
void
|
||||
RecursiveShapeIterator::new_layer () const
|
||||
{
|
||||
if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
|
||||
if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
|
||||
m_shape = shape_iterator ();
|
||||
} else if (! m_overlapping) {
|
||||
m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
|
||||
|
|
@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
|
|||
new_inst (receiver);
|
||||
}
|
||||
|
||||
bool
|
||||
RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const
|
||||
{
|
||||
// Try some optimization: if the instance we're looking at is entirely covered
|
||||
// by a rectangle (other objects are too expensive to check), then we skip it
|
||||
//
|
||||
// We check 10 shapes max.
|
||||
|
||||
auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
|
||||
size_t nmax = 10;
|
||||
while (! si.at_end () && nmax-- > 0) {
|
||||
if (inst_bx.inside (si->rectangle ())) {
|
||||
return true;
|
||||
}
|
||||
++si;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RecursiveShapeIterator::skip_shapes () const
|
||||
{
|
||||
return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back ();
|
||||
}
|
||||
|
||||
void
|
||||
RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
|
||||
{
|
||||
|
|
@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
|
|||
all_of_instance = m_local_complex_region_stack.empty ();
|
||||
}
|
||||
|
||||
m_skip_shapes = skip_shapes ();
|
||||
m_skip_shapes_member = false;
|
||||
|
||||
if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
|
||||
box_type inst_bx = m_inst->bbox (m_box_convert);
|
||||
m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer);
|
||||
}
|
||||
|
||||
RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all;
|
||||
if (receiver) {
|
||||
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance);
|
||||
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes);
|
||||
} else if (m_skip_shapes) {
|
||||
ni = RecursiveShapeReceiver::NI_skip;
|
||||
}
|
||||
|
||||
if (ni == RecursiveShapeReceiver::NI_skip) {
|
||||
|
|
@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
// skip instance array members not part of the complex region
|
||||
while (! m_inst_array.at_end ()) {
|
||||
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
if (! is_outside_complex_region (ia_box)) {
|
||||
break;
|
||||
} else {
|
||||
|
|
@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
}
|
||||
|
||||
while (! m_inst_array.at_end () && receiver) {
|
||||
if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) {
|
||||
break;
|
||||
} else {
|
||||
++m_inst_array;
|
||||
m_skip_shapes_member = false;
|
||||
|
||||
while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) {
|
||||
|
||||
m_skip_shapes_member = m_skip_shapes;
|
||||
if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
|
||||
|
||||
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer);
|
||||
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
if (receiver) {
|
||||
skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance (), m_skip_shapes_member);
|
||||
} else {
|
||||
skip = m_skip_shapes_member;
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
++m_inst_array;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -868,6 +868,7 @@ private:
|
|||
mutable unsigned int m_layer;
|
||||
mutable const cell_type *mp_cell;
|
||||
mutable size_t m_current_layer;
|
||||
mutable bool m_skip_shapes, m_skip_shapes_member;
|
||||
mutable shape_iterator m_shape;
|
||||
mutable cplx_trans_type m_trans;
|
||||
mutable std::vector<cplx_trans_type> m_trans_stack;
|
||||
|
|
@ -876,7 +877,7 @@ private:
|
|||
mutable std::vector<const cell_type *> m_cells;
|
||||
mutable std::vector<box_tree_type> m_local_complex_region_stack;
|
||||
mutable std::vector<box_type> m_local_region_stack;
|
||||
mutable std::vector<bool> m_skip_shapes_stack;
|
||||
mutable std::vector<bool> m_skip_shapes_stack, m_skip_shapes_member_stack;
|
||||
mutable bool m_needs_reinit;
|
||||
mutable size_t m_inst_quad_id;
|
||||
mutable std::vector<size_t> m_inst_quad_id_stack;
|
||||
|
|
@ -899,6 +900,8 @@ private:
|
|||
bool down (RecursiveShapeReceiver *receiver) const;
|
||||
void pop () const;
|
||||
|
||||
bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const;
|
||||
bool skip_shapes () const;
|
||||
bool is_outside_complex_region (const db::Box &box) const;
|
||||
|
||||
void set_inactive (bool a) const
|
||||
|
|
@ -1013,8 +1016,11 @@ public:
|
|||
* - NI_all: iterate all members through "new_inst_member"
|
||||
* - NI_single: iterate a single member (the first one)
|
||||
* - NI_skip: skips the whole array (not a single instance is iterated)
|
||||
*
|
||||
* The "skip_shapes" parameter indicates that the instance is visited with the
|
||||
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
|
||||
*/
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; }
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; }
|
||||
|
||||
/**
|
||||
* @brief Enters a new array member of the instance
|
||||
|
|
@ -1026,8 +1032,11 @@ public:
|
|||
* "all" is true, if an instance array is iterated in "all" mode (see new_inst).
|
||||
*
|
||||
* If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered.
|
||||
*
|
||||
* The "skip_shapes" parameter indicates that the instance member is visited with the
|
||||
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
|
||||
*/
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; }
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; }
|
||||
|
||||
/**
|
||||
* @brief Delivers a shape
|
||||
|
|
|
|||
|
|
@ -1618,18 +1618,34 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
|
|||
}
|
||||
}
|
||||
|
||||
db::polygon_ref_generator<TR> pr (layout, result);
|
||||
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
|
||||
|
||||
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
const TR &subject = interactions.subject_shape (i->first);
|
||||
if (others.find (subject) != others.end ()) {
|
||||
|
||||
// shortcut (and: keep, not: drop)
|
||||
// Note that we still normalize and split the polygon, so we get a uniform
|
||||
// behavior.
|
||||
if (m_is_and) {
|
||||
result.insert (subject);
|
||||
db::Polygon poly;
|
||||
subject.instantiate (poly);
|
||||
splitter.put (poly);
|
||||
}
|
||||
|
||||
} else if (i->second.empty ()) {
|
||||
|
||||
// shortcut (not: keep, and: drop)
|
||||
// Note that we still normalize and split the polygon, so we get a uniform
|
||||
// behavior.
|
||||
if (! m_is_and) {
|
||||
result.insert (subject);
|
||||
db::Polygon poly;
|
||||
subject.instantiate (poly);
|
||||
splitter.put (poly);
|
||||
}
|
||||
|
||||
} else {
|
||||
for (auto e = subject.begin_edge (); ! e.at_end(); ++e) {
|
||||
ep.insert (*e, p1);
|
||||
|
|
@ -1649,8 +1665,6 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
|
|||
}
|
||||
|
||||
db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
|
||||
db::polygon_ref_generator<TR> pr (layout, result);
|
||||
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
|
||||
db::PolygonGenerator pg (splitter, true, true);
|
||||
ep.set_base_verbosity (50);
|
||||
ep.process (pg, op);
|
||||
|
|
|
|||
|
|
@ -867,15 +867,15 @@ size_t
|
|||
Shape::hash_value () const
|
||||
{
|
||||
size_t h = size_t (m_type);
|
||||
h = std::hcombine (h, std::hfunc (m_trans));
|
||||
h = tl::hcombine (h, tl::hfunc (m_trans));
|
||||
|
||||
if (m_stable) {
|
||||
// Use the bytes of the iterator binary pattern (see operator<)
|
||||
for (unsigned int i = 0; i < sizeof (tl::reuse_vector<box_type>::const_iterator); ++i) {
|
||||
h = std::hcombine (h, size_t (m_generic.iter[i]));
|
||||
h = tl::hcombine (h, size_t (m_generic.iter[i]));
|
||||
}
|
||||
} else {
|
||||
h = std::hcombine (h, size_t (m_generic.any));
|
||||
h = tl::hcombine (h, size_t (m_generic.any));
|
||||
}
|
||||
|
||||
return h;
|
||||
|
|
|
|||
|
|
@ -177,9 +177,9 @@ Texts::iter () const
|
|||
return *(i ? i : &def_iter);
|
||||
}
|
||||
|
||||
void Texts::polygons (Region &output, db::Coord e) const
|
||||
void Texts::polygons (Region &output, db::Coord e, const tl::Variant &text_prop) const
|
||||
{
|
||||
output.set_delegate (mp_delegate->polygons (e));
|
||||
output.set_delegate (mp_delegate->polygons (e, text_prop));
|
||||
}
|
||||
|
||||
void Texts::edges (Edges &output) const
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -590,7 +601,7 @@ public:
|
|||
* The given extension is applied in all directions rendering a square of 2*e
|
||||
* width and height. The center of the boxes will be the position of the texts.
|
||||
*/
|
||||
void polygons (Region &output, db::Coord e = 1) const;
|
||||
void polygons (Region &output, db::Coord e = 1, const tl::Variant &text_prop = tl::Variant ()) const;
|
||||
|
||||
/**
|
||||
* @brief Returns individual, dot-like edges
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ public:
|
|||
virtual TextsDelegate *processed (const TextProcessorBase &proc) const = 0;
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &proc) const = 0;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const = 0;
|
||||
virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const = 0;
|
||||
virtual EdgesDelegate *edges () const = 0;
|
||||
|
||||
virtual TextsDelegate *add_in_place (const Texts &other) = 0;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -32,19 +32,19 @@ namespace db
|
|||
// Vertex implementation
|
||||
|
||||
Vertex::Vertex ()
|
||||
: DPoint (), m_level (0)
|
||||
: DPoint (), m_is_precious (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Vertex::Vertex (const db::DPoint &p)
|
||||
: DPoint (p), m_level (0)
|
||||
: DPoint (p), m_is_precious (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Vertex::Vertex (const Vertex &v)
|
||||
: DPoint (), m_level (0)
|
||||
: DPoint (), m_is_precious (false)
|
||||
{
|
||||
operator= (v);
|
||||
}
|
||||
|
|
@ -54,13 +54,13 @@ Vertex &Vertex::operator= (const Vertex &v)
|
|||
if (this != &v) {
|
||||
// NOTE: edges are not copied!
|
||||
db::DPoint::operator= (v);
|
||||
m_level = v.m_level;
|
||||
m_is_precious = v.m_is_precious;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vertex::Vertex (db::DCoord x, db::DCoord y)
|
||||
: DPoint (x, y), m_level (0)
|
||||
: DPoint (x, y), m_is_precious (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -363,21 +363,33 @@ Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3)
|
|||
}
|
||||
mp_v[2] = mp_e[1]->other (mp_v[1]);
|
||||
|
||||
// establish link to edges
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
TriangleEdge *e = mp_e[i];
|
||||
int side_of = e->side_of (*mp_v[i == 0 ? 2 : i - 1]);
|
||||
// NOTE: in the degenerated case, the triangle is not attached to an edge!
|
||||
if (side_of < 0) {
|
||||
e->set_left (this);
|
||||
} else if (side_of > 0) {
|
||||
e->set_right (this);
|
||||
}
|
||||
// enforce clockwise orientation
|
||||
int s = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]);
|
||||
if (s < 0) {
|
||||
std::swap (mp_v[2], mp_v[1]);
|
||||
} else if (s == 0) {
|
||||
// Triangle is not orientable
|
||||
tl_assert (false);
|
||||
}
|
||||
|
||||
// enforce clockwise orientation
|
||||
if (db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]) < 0) {
|
||||
std::swap (mp_v[2], mp_v[1]);
|
||||
// establish link to edges
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
|
||||
TriangleEdge *e = mp_e[i];
|
||||
|
||||
unsigned int i1 = 0;
|
||||
for ( ; e->v1 () != mp_v[i1] && i1 < 3; ++i1)
|
||||
;
|
||||
unsigned int i2 = 0;
|
||||
for ( ; e->v2 () != mp_v[i2] && i2 < 3; ++i2)
|
||||
;
|
||||
|
||||
if ((i1 + 1) % 3 == i2) {
|
||||
e->set_right (this);
|
||||
} else {
|
||||
e->set_left (this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -436,22 +448,40 @@ Triangle::bbox () const
|
|||
|
||||
|
||||
std::pair<db::DPoint, double>
|
||||
Triangle::circumcircle () const
|
||||
Triangle::circumcircle (bool *ok) const
|
||||
{
|
||||
db::DVector v1 = *mp_v[0] - *mp_v[1];
|
||||
db::DVector v2 = *mp_v[0] - *mp_v[2];
|
||||
db::DVector n1 = db::DVector (v1.y (), -v1.x ());
|
||||
db::DVector n2 = db::DVector (v2.y (), -v2.x ());
|
||||
// see https://en.wikipedia.org/wiki/Circumcircle
|
||||
// we set A=(0,0), so the formulas simplify
|
||||
|
||||
double p1s = v1.sq_length ();
|
||||
double p2s = v2.sq_length ();
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
double s = db::vprod (v1, v2);
|
||||
tl_assert (fabs (s) > db::epsilon);
|
||||
db::DVector b = *mp_v[1] - *mp_v[0];
|
||||
db::DVector c = *mp_v[2] - *mp_v[0];
|
||||
|
||||
db::DVector r = (n1 * p2s - n2 * p1s) * (0.5 / s);
|
||||
db::DPoint center = *mp_v[0] + r;
|
||||
double radius = r.length ();
|
||||
double b2 = b.sq_length ();
|
||||
double c2 = c.sq_length ();
|
||||
|
||||
double sx = 0.5 * (b2 * c.y () - c2 * b.y ());
|
||||
double sy = 0.5 * (b.x () * c2 - c.x() * b2);
|
||||
|
||||
double a1 = b.x() * c.y();
|
||||
double a2 = c.x() * b.y();
|
||||
double a = a1 - a2;
|
||||
double a_abs = std::abs (a);
|
||||
|
||||
if (a_abs < (std::abs (a1) + std::abs (a2)) * db::epsilon) {
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
return std::make_pair (db::DPoint (), 0.0);
|
||||
} else {
|
||||
tl_assert (false);
|
||||
}
|
||||
}
|
||||
|
||||
double radius = sqrt (sx * sx + sy * sy) / a_abs;
|
||||
db::DPoint center = *mp_v[0] + db::DVector (sx / a, sy / a);
|
||||
|
||||
return std::make_pair (center, radius);
|
||||
}
|
||||
|
|
@ -507,18 +537,28 @@ Triangle::common_edge (const Triangle *other) const
|
|||
int
|
||||
Triangle::contains (const db::DPoint &point) const
|
||||
{
|
||||
auto c = *mp_v[2] - *mp_v[0];
|
||||
auto b = *mp_v[1] - *mp_v[0];
|
||||
|
||||
int vps = db::vprod_sign (c, b);
|
||||
if (vps == 0) {
|
||||
return db::vprod_sign (point - *mp_v[0], b) == 0 && db::vprod_sign (point - *mp_v[0], c) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int res = 1;
|
||||
const Vertex *vl = mp_v[2];;
|
||||
|
||||
const Vertex *vl = mp_v[2];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const Vertex *v = mp_v[i];;
|
||||
int s = db::DEdge (*vl, *v).side_of (point);
|
||||
if (s == 0) {
|
||||
res = 0;
|
||||
} else if (s > 0) {
|
||||
const Vertex *v = mp_v[i];
|
||||
int n = db::vprod_sign (point - *vl, *v - *vl) * vps;
|
||||
if (n < 0) {
|
||||
return -1;
|
||||
} else if (n == 0) {
|
||||
res = 0;
|
||||
}
|
||||
vl = v;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -536,8 +576,9 @@ double
|
|||
Triangle::b () const
|
||||
{
|
||||
double lmin = min_edge_length ();
|
||||
auto cr = circumcircle ();
|
||||
return lmin / cr.second;
|
||||
bool ok = false;
|
||||
auto cr = circumcircle (&ok);
|
||||
return ok ? lmin / cr.second : 0.0;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ public:
|
|||
|
||||
bool has_edge (const TriangleEdge *edge) const;
|
||||
|
||||
size_t level () const { return m_level; }
|
||||
void set_level (size_t l) { m_level = l; }
|
||||
void set_is_precious (bool f) { m_is_precious = f; }
|
||||
bool is_precious () const { return m_is_precious; }
|
||||
|
||||
std::string to_string (bool with_id = false) const;
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ private:
|
|||
}
|
||||
|
||||
edges_type mp_edges;
|
||||
size_t m_level;
|
||||
bool m_is_precious;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -483,8 +483,10 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Gets the center point and radius of the circumcircle
|
||||
* If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid.
|
||||
* An invalid circumcircle is an indicator for a degenerated triangle with area 0 (or close to).
|
||||
*/
|
||||
std::pair<db::DPoint, double> circumcircle () const;
|
||||
std::pair<db::DPoint, double> circumcircle (bool *ok = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the vertex opposite of the given edge
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
static inline bool is_equal (const db::DPoint &a, const db::DPoint &b)
|
||||
{
|
||||
return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon &&
|
||||
std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon;
|
||||
}
|
||||
|
||||
Triangles::Triangles ()
|
||||
: m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0)
|
||||
{
|
||||
|
|
@ -44,9 +50,7 @@ Triangles::Triangles ()
|
|||
|
||||
Triangles::~Triangles ()
|
||||
{
|
||||
while (! mp_triangles.empty ()) {
|
||||
remove_triangle (mp_triangles.begin ().operator-> ());
|
||||
}
|
||||
clear ();
|
||||
}
|
||||
|
||||
db::Vertex *
|
||||
|
|
@ -88,6 +92,7 @@ Triangles::create_triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3
|
|||
db::Triangle *res = new db::Triangle (e1, e2, e3);
|
||||
res->set_id (++m_id);
|
||||
mp_triangles.push_back (res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -366,26 +371,35 @@ Triangles::insert (db::Vertex *vertex, std::list<tl::weak_ptr<db::Triangle> > *n
|
|||
|
||||
// check, if the new vertex is on an edge (may be edge between triangles or edge on outside)
|
||||
std::vector<db::TriangleEdge *> on_edges;
|
||||
std::vector<db::TriangleEdge *> on_vertex;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
db::TriangleEdge *e = tris.front ()->edge (i);
|
||||
if (e->side_of (*vertex) == 0) {
|
||||
on_edges.push_back (e);
|
||||
if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) {
|
||||
on_vertex.push_back (e);
|
||||
} else {
|
||||
on_edges.push_back (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! on_edges.empty ()) {
|
||||
if (on_edges.size () == size_t (1)) {
|
||||
split_triangles_on_edge (tris, vertex, on_edges.front (), new_triangles);
|
||||
return vertex;
|
||||
} else {
|
||||
// the vertex is already present
|
||||
tl_assert (on_edges.size () == size_t (2));
|
||||
return on_edges.front ()->common_vertex (on_edges [1]);
|
||||
}
|
||||
if (! on_vertex.empty ()) {
|
||||
|
||||
tl_assert (on_vertex.size () == size_t (2));
|
||||
return on_vertex.front ()->common_vertex (on_vertex [1]);
|
||||
|
||||
} else if (! on_edges.empty ()) {
|
||||
|
||||
tl_assert (on_edges.size () == size_t (1));
|
||||
split_triangles_on_edge (vertex, on_edges.front (), new_triangles);
|
||||
return vertex;
|
||||
|
||||
} else if (tris.size () == size_t (1)) {
|
||||
|
||||
// the new vertex is inside one triangle
|
||||
split_triangle (tris.front (), vertex, new_triangles);
|
||||
return vertex;
|
||||
|
||||
}
|
||||
|
||||
tl_assert (false);
|
||||
|
|
@ -404,6 +418,7 @@ Triangles::find_triangle_for_point (const db::DPoint &point)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -642,7 +657,7 @@ Triangles::split_triangle (db::Triangle *t, db::Vertex *vertex, std::list<tl::we
|
|||
}
|
||||
|
||||
void
|
||||
Triangles::split_triangles_on_edge (const std::vector<db::Triangle *> &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::list<tl::weak_ptr<db::Triangle> > *new_triangles_out)
|
||||
Triangles::split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list<tl::weak_ptr<db::Triangle> > *new_triangles_out)
|
||||
{
|
||||
TriangleEdge *s1 = create_edge (split_edge->v1 (), vertex);
|
||||
TriangleEdge *s2 = create_edge (split_edge->v2 (), vertex);
|
||||
|
|
@ -651,6 +666,12 @@ Triangles::split_triangles_on_edge (const std::vector<db::Triangle *> &tris, db:
|
|||
|
||||
std::vector<db::Triangle *> new_triangles;
|
||||
|
||||
std::vector<db::Triangle *> tris;
|
||||
tris.reserve (2);
|
||||
for (auto t = split_edge->begin_triangles (); t != split_edge->end_triangles (); ++t) {
|
||||
tris.push_back (t.operator-> ());
|
||||
}
|
||||
|
||||
for (auto t = tris.begin (); t != tris.end (); ++t) {
|
||||
|
||||
(*t)->unlink ();
|
||||
|
|
@ -931,13 +952,15 @@ Triangles::is_illegal_edge (db::TriangleEdge *edge)
|
|||
return false;
|
||||
}
|
||||
|
||||
auto lr = left->circumcircle ();
|
||||
if (right->opposite (edge)->in_circle (lr.first, lr.second) > 0) {
|
||||
bool ok = false;
|
||||
|
||||
auto lr = left->circumcircle (&ok);
|
||||
if (! ok || right->opposite (edge)->in_circle (lr.first, lr.second) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto rr = right->circumcircle();
|
||||
if (left->opposite (edge)->in_circle (rr.first, rr.second) > 0) {
|
||||
auto rr = right->circumcircle(&ok);
|
||||
if (! ok || left->opposite (edge)->in_circle (rr.first, rr.second) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1163,16 +1186,16 @@ Triangles::search_edges_crossing (Vertex *from, Vertex *to)
|
|||
}
|
||||
|
||||
db::Vertex *
|
||||
Triangles::find_vertex_for_point (const db::DPoint &pt)
|
||||
Triangles::find_vertex_for_point (const db::DPoint &point)
|
||||
{
|
||||
db::TriangleEdge *edge = find_closest_edge (pt);
|
||||
db::TriangleEdge *edge = find_closest_edge (point);
|
||||
if (!edge) {
|
||||
return 0;
|
||||
}
|
||||
db::Vertex *v = 0;
|
||||
if (edge->v1 ()->equal (pt)) {
|
||||
if (is_equal (*edge->v1 (), point)) {
|
||||
v = edge->v1 ();
|
||||
} else if (edge->v2 ()->equal (pt)) {
|
||||
} else if (is_equal (*edge->v2 (), point)) {
|
||||
v = edge->v2 ();
|
||||
}
|
||||
return v;
|
||||
|
|
@ -1186,7 +1209,7 @@ Triangles::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2)
|
|||
return 0;
|
||||
}
|
||||
for (auto e = v->begin_edges (); e != v->end_edges (); ++e) {
|
||||
if ((*e)->other (v)->equal (p2)) {
|
||||
if (is_equal (*(*e)->other (v), p2)) {
|
||||
return *e;
|
||||
}
|
||||
}
|
||||
|
|
@ -1329,7 +1352,8 @@ Triangles::constrain (const std::vector<std::vector<db::Vertex *> > &contours)
|
|||
if (vv == c->end ()) {
|
||||
vv = c->begin ();
|
||||
}
|
||||
resolved_edges.push_back (std::make_pair (db::DEdge (**v, **vv), std::vector<db::TriangleEdge *> ()));
|
||||
db::DEdge e (**v, **vv);
|
||||
resolved_edges.push_back (std::make_pair (e, std::vector<db::TriangleEdge *> ()));
|
||||
resolved_edges.back ().second = ensure_edge (*v, *vv);
|
||||
}
|
||||
}
|
||||
|
|
@ -1455,10 +1479,14 @@ Triangles::create_constrained_delaunay (const db::Region ®ion, const CplxTran
|
|||
}
|
||||
|
||||
void
|
||||
Triangles::create_constrained_delaunay (const db::Polygon &p, const CplxTrans &trans)
|
||||
Triangles::create_constrained_delaunay (const db::Polygon &p, const std::vector<Point> &vertexes, const CplxTrans &trans)
|
||||
{
|
||||
clear ();
|
||||
|
||||
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
|
||||
insert_point (trans * *v)->set_is_precious (true);
|
||||
}
|
||||
|
||||
std::vector<std::vector<db::Vertex *> > edge_contours;
|
||||
make_contours (p, trans, edge_contours);
|
||||
|
||||
|
|
@ -1466,12 +1494,16 @@ Triangles::create_constrained_delaunay (const db::Polygon &p, const CplxTrans &t
|
|||
}
|
||||
|
||||
void
|
||||
Triangles::create_constrained_delaunay (const db::DPolygon &p)
|
||||
Triangles::create_constrained_delaunay (const db::DPolygon &p, const std::vector<DPoint> &vertexes, const DCplxTrans &trans)
|
||||
{
|
||||
clear ();
|
||||
|
||||
for (auto v = vertexes.begin (); v != vertexes.end (); ++v) {
|
||||
insert_point (trans * *v)->set_is_precious (true);
|
||||
}
|
||||
|
||||
std::vector<std::vector<db::Vertex *> > edge_contours;
|
||||
make_contours (p, db::DUnitTrans (), edge_contours);
|
||||
make_contours (p, trans, edge_contours);
|
||||
|
||||
constrain (edge_contours);
|
||||
}
|
||||
|
|
@ -1529,28 +1561,46 @@ Triangles::triangulate (const db::Region ®ion, const TriangulateParameters &p
|
|||
|
||||
void
|
||||
Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu)
|
||||
{
|
||||
triangulate (poly, std::vector<db::Point> (), parameters, dbu);
|
||||
}
|
||||
|
||||
void
|
||||
Triangles::triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulateParameters ¶meters, double dbu)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
|
||||
|
||||
create_constrained_delaunay (poly, db::CplxTrans (dbu));
|
||||
create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu));
|
||||
refine (parameters);
|
||||
}
|
||||
|
||||
void
|
||||
Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans)
|
||||
{
|
||||
triangulate (poly, std::vector<db::Point> (), parameters, trans);
|
||||
}
|
||||
|
||||
void
|
||||
Triangles::triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
|
||||
|
||||
create_constrained_delaunay (poly, trans);
|
||||
create_constrained_delaunay (poly, vertexes, trans);
|
||||
refine (parameters);
|
||||
}
|
||||
|
||||
void
|
||||
Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters)
|
||||
Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const DCplxTrans &trans)
|
||||
{
|
||||
triangulate (poly, std::vector<db::DPoint> (), parameters, trans);
|
||||
}
|
||||
|
||||
void
|
||||
Triangles::triangulate (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const TriangulateParameters ¶meters, const DCplxTrans &trans)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate");
|
||||
|
||||
create_constrained_delaunay (poly);
|
||||
create_constrained_delaunay (poly, vertexes, trans);
|
||||
refine (parameters);
|
||||
}
|
||||
|
||||
|
|
@ -1606,7 +1656,28 @@ Triangles::refine (const TriangulateParameters ¶meters)
|
|||
auto cr = (*t)->circumcircle();
|
||||
auto center = cr.first;
|
||||
|
||||
if ((*t)->contains (center) >= 0) {
|
||||
int s = (*t)->contains (center);
|
||||
if (s >= 0) {
|
||||
|
||||
if (s > 0) {
|
||||
|
||||
double snap = 1e-3;
|
||||
|
||||
// Snap the center to a segment center if "close" to it.
|
||||
// This avoids generating very skinny triangles that can't be fixed as the
|
||||
// segment cannot be flipped. This a part of the issue #1996 problem.
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
if ((*t)->edge (i)->is_segment ()) {
|
||||
auto e = (*t)->edge (i)->edge ();
|
||||
auto c = e.p1 () + e.d () * 0.5;
|
||||
if (c.distance (center) < e.length () * 0.5 * snap - db::epsilon) {
|
||||
center = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= parameters.base_verbosity + 20) {
|
||||
tl::info << "Inserting in-triangle center " << center.to_string () << " of " << (*t)->to_string (true);
|
||||
|
|
@ -1616,7 +1687,7 @@ Triangles::refine (const TriangulateParameters ¶meters)
|
|||
} else {
|
||||
|
||||
db::Vertex *vstart = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
db::TriangleEdge *edge = (*t)->edge (i);
|
||||
vstart = (*t)->opposite (edge);
|
||||
if (edge->side_of (*vstart) * edge->side_of (center) < 0) {
|
||||
|
|
@ -1653,7 +1724,7 @@ Triangles::refine (const TriangulateParameters ¶meters)
|
|||
for (auto e = (*v)->begin_edges (); e != (*v)->end_edges () && ! has_segment; ++e) {
|
||||
has_segment = (*e)->is_segment ();
|
||||
}
|
||||
if (! has_segment) {
|
||||
if (! has_segment && ! (*v)->is_precious ()) {
|
||||
to_delete.push_back (*v);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbTriangles
|
||||
#define HDR_dbTriangles
|
||||
|
||||
|
|
@ -160,14 +158,17 @@ public:
|
|||
void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, double dbu = 1.0);
|
||||
|
||||
// more versions
|
||||
void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu = 1.0);
|
||||
void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu = 1.0);
|
||||
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulateParameters ¶meters, double dbu = 1.0);
|
||||
void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Triangulates a floating-point polygon
|
||||
*/
|
||||
void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters);
|
||||
void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ());
|
||||
void triangulate (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Statistics: number of flips (fixing)
|
||||
|
|
@ -289,12 +290,12 @@ protected:
|
|||
/**
|
||||
* @brief Creates a constrained Delaunay triangulation from the given Polygon
|
||||
*/
|
||||
void create_constrained_delaunay (const db::Polygon &poly, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
void create_constrained_delaunay (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Creates a constrained Delaunay triangulation from the given DPolygon
|
||||
*/
|
||||
void create_constrained_delaunay (const db::DPolygon &poly);
|
||||
void create_constrained_delaunay (const db::DPolygon &poly, const std::vector<DPoint> &vertexes, const DCplxTrans &trans);
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion)
|
||||
|
|
@ -306,6 +307,15 @@ protected:
|
|||
std::vector<db::Vertex *> find_inside_circle (const db::DPoint ¢er, double radius) const;
|
||||
|
||||
private:
|
||||
struct TriangleBoxConvert
|
||||
{
|
||||
typedef db::DBox box_type;
|
||||
box_type operator() (db::Triangle *t) const
|
||||
{
|
||||
return t ? t->bbox () : box_type ();
|
||||
}
|
||||
};
|
||||
|
||||
tl::list<db::Triangle> mp_triangles;
|
||||
tl::stable_vector<db::TriangleEdge> m_edges_heap;
|
||||
std::vector<db::TriangleEdge *> m_returned_edges;
|
||||
|
|
@ -329,7 +339,7 @@ private:
|
|||
db::TriangleEdge *find_closest_edge (const db::DPoint &p, db::Vertex *vstart = 0, bool inside_only = false);
|
||||
db::Vertex *insert (db::Vertex *vertex, std::list<tl::weak_ptr<db::Triangle> > *new_triangles = 0);
|
||||
void split_triangle (db::Triangle *t, db::Vertex *vertex, std::list<tl::weak_ptr<db::Triangle> > *new_triangles_out);
|
||||
void split_triangles_on_edge (const std::vector<db::Triangle *> &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::list<tl::weak_ptr<db::Triangle> > *new_triangles_out);
|
||||
void split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list<tl::weak_ptr<db::Triangle> > *new_triangles_out);
|
||||
void add_more_triangles (std::vector<Triangle *> &new_triangles,
|
||||
db::TriangleEdge *incoming_edge,
|
||||
db::Vertex *from_vertex, db::Vertex *to_vertex,
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ struct box_defs
|
|||
|
||||
static size_t hash_value (const C *box)
|
||||
{
|
||||
return std::hfunc (*box);
|
||||
return tl::hfunc (*box);
|
||||
}
|
||||
|
||||
static const C &bbox (const C *box)
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ struct cell_inst_array_defs
|
|||
|
||||
static size_t hash_value (const C *i)
|
||||
{
|
||||
return std::hfunc (*i);
|
||||
return tl::hfunc (*i);
|
||||
}
|
||||
|
||||
static bool less (const C *i, const C &other)
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ struct edge_defs
|
|||
|
||||
static size_t hash_value (const C *e)
|
||||
{
|
||||
return std::hfunc (*e);
|
||||
return tl::hfunc (*e);
|
||||
}
|
||||
|
||||
static gsi::Methods methods ()
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ struct edge_pair_defs
|
|||
|
||||
static size_t hash_value (const C *ep)
|
||||
{
|
||||
return std::hfunc (*ep);
|
||||
return tl::hfunc (*ep);
|
||||
}
|
||||
|
||||
static gsi::Methods methods ()
|
||||
|
|
@ -370,7 +370,7 @@ static db::DEdgePairWithProperties *new_dedge_pair_with_properties2 (const db::D
|
|||
return new db::DEdgePairWithProperties (edge_pair, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ())));
|
||||
}
|
||||
|
||||
Class<db::DEdgePairWithProperties> decl_DEdgePairWithProperties (decl_EdgePair, "db", "DEdgePairWithProperties",
|
||||
Class<db::DEdgePairWithProperties> decl_DEdgePairWithProperties (decl_DEdgePair, "db", "DEdgePairWithProperties",
|
||||
gsi::properties_support_methods<db::DEdgePairWithProperties> () +
|
||||
constructor ("new", &new_dedge_pair_with_properties, gsi::arg ("edge_pair"), gsi::arg ("properties_id", db::properties_id_type (0)),
|
||||
"@brief Creates a new object from a property-less object and a properties ID."
|
||||
|
|
|
|||
|
|
@ -783,6 +783,16 @@ static std::vector<db::EdgePairs> split_with_area2 (const db::EdgePairs *r, db::
|
|||
return as_2edge_pairs_vector (r->split_filter (f));
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n)
|
||||
{
|
||||
const db::EdgePair *ep = edge_pairs->nth (n);
|
||||
if (! ep) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::EdgePairWithProperties> begin_edge_pairs (const db::EdgePairs *edge_pairs)
|
||||
{
|
||||
return db::generic_shape_iterator<db::EdgePairWithProperties> (db::make_wp_iter (edge_pairs->delegate ()->begin ()));
|
||||
|
|
@ -1855,13 +1865,15 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
|
|||
"\n"
|
||||
"Starting with version 0.30, the iterator delivers EdgePairWithProperties objects."
|
||||
) +
|
||||
method ("[]", &db::EdgePairs::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth edge pair\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. "
|
||||
"those for which \\has_valid_edge_pairs? is true. Use \\flatten to explicitly flatten an edge pair collection.\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the edge pairs."
|
||||
"The \\each iterator is the more general approach to access the edge pairs.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns a \\EdgePairWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::EdgePairs::flatten,
|
||||
"@brief Explicitly flattens an edge pair collection\n"
|
||||
|
|
|
|||
|
|
@ -848,6 +848,16 @@ static std::vector<db::Edges> split_interacting_with_region (const db::Edges *r,
|
|||
return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count));
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::Edges *edges, size_t n)
|
||||
{
|
||||
const db::Edge *e = edges->nth (n);
|
||||
if (! e) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::EdgeWithProperties> begin_edges (const db::Edges *edges)
|
||||
{
|
||||
return db::generic_shape_iterator<db::EdgeWithProperties> (db::make_wp_iter (edges->delegate ()->begin ()));
|
||||
|
|
@ -2439,14 +2449,16 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"This method has been introduced in version 0.25."
|
||||
"Starting with version 0.30, the iterator delivers an EdgeWithProperties object."
|
||||
) +
|
||||
method ("[]", &db::Edges::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth edge of the collection\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat edge collections only - i.e. "
|
||||
"those for which \\has_valid_edges? is true. Use \\flatten to explicitly flatten an edge collection.\n"
|
||||
"This method returns the raw edge (not merged edges, even if merged semantics is enabled).\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the edges."
|
||||
"The \\each iterator is the more general approach to access the edges.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns an \\EdgeWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::Edges::flatten,
|
||||
"@brief Explicitly flattens an edge collection\n"
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ db::LayerProperties li_from_string (const char *s, bool as_target)
|
|||
static
|
||||
size_t hash_value (const db::LayerProperties *l)
|
||||
{
|
||||
return std::hfunc (*l);
|
||||
return tl::hfunc (*l);
|
||||
}
|
||||
|
||||
static bool log_equal_ext (const db::LayerProperties *lp1, const db::LayerProperties &lp2)
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ struct LayoutQueryIteratorWrapper
|
|||
typedef void difference_type;
|
||||
typedef void pointer;
|
||||
|
||||
LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, tl::Eval *eval)
|
||||
: mp_iter (new db::LayoutQueryIterator (q, layout, eval))
|
||||
LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, const db::Cell *cell, tl::Eval *eval)
|
||||
: mp_iter (new db::LayoutQueryIterator (q, layout, cell, eval))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -77,9 +77,14 @@ private:
|
|||
tl::shared_ptr<db::LayoutQueryIterator> mp_iter;
|
||||
};
|
||||
|
||||
static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval)
|
||||
static LayoutQueryIteratorWrapper iterate1 (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval)
|
||||
{
|
||||
return LayoutQueryIteratorWrapper (*q, layout, eval);
|
||||
return LayoutQueryIteratorWrapper (*q, layout, 0, eval);
|
||||
}
|
||||
|
||||
static LayoutQueryIteratorWrapper iterate2 (const db::LayoutQuery *q, const db::Layout *layout, const db::Cell *cell, tl::Eval *eval)
|
||||
{
|
||||
return LayoutQueryIteratorWrapper (*q, layout, cell, eval);
|
||||
}
|
||||
|
||||
static tl::Variant iter_get (db::LayoutQueryIterator *iter, const std::string &name)
|
||||
|
|
@ -158,6 +163,16 @@ Class<db::LayoutQueryIterator> decl_LayoutQueryIterator ("db", "LayoutQueryItera
|
|||
"The LayoutQueryIterator class has been introduced in version 0.25."
|
||||
);
|
||||
|
||||
static void execute1 (db::LayoutQuery *q, db::Layout &layout, tl::Eval *context)
|
||||
{
|
||||
q->execute (layout, 0, context);
|
||||
}
|
||||
|
||||
static void execute2 (db::LayoutQuery *q, db::Layout &layout, db::Cell *cell, tl::Eval *context)
|
||||
{
|
||||
q->execute (layout, cell, context);
|
||||
}
|
||||
|
||||
Class<db::LayoutQuery> decl_LayoutQuery ("db", "LayoutQuery",
|
||||
gsi::constructor ("new", &new_query, gsi::arg ("query"),
|
||||
"@brief Creates a new query object from the given query string\n"
|
||||
|
|
@ -168,7 +183,7 @@ Class<db::LayoutQuery> decl_LayoutQuery ("db", "LayoutQuery",
|
|||
"This method allows detection of the properties available. Within the query, all of these "
|
||||
"properties can be obtained from the query iterator using \\LayoutQueryIterator#get.\n"
|
||||
) +
|
||||
gsi::method ("execute", &db::LayoutQuery::execute, gsi::arg("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
|
||||
gsi::method_ext ("execute", &execute1, gsi::arg("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
|
||||
"@brief Executes the query\n"
|
||||
"\n"
|
||||
"This method can be used to execute \"active\" queries such\n"
|
||||
|
|
@ -179,13 +194,27 @@ Class<db::LayoutQuery> decl_LayoutQuery ("db", "LayoutQuery",
|
|||
"The context argument allows supplying an expression execution context. This context can be used for "
|
||||
"example to supply variables for the execution. It has been added in version 0.26.\n"
|
||||
) +
|
||||
gsi::iterator_ext ("each", &iterate, gsi::arg ("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
|
||||
gsi::method_ext ("execute", &execute2, gsi::arg("layout"), gsi::arg("cell"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
|
||||
"@brief Executes the query\n"
|
||||
"\n"
|
||||
"This version allows specifying a context cell. This cell can be used as a default cell for cell expressions.\n"
|
||||
"\n"
|
||||
"This variant has been introduced in version 0.30."
|
||||
) +
|
||||
gsi::iterator_ext ("each", &iterate1, gsi::arg ("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
|
||||
"@brief Executes the query and delivered the results iteratively.\n"
|
||||
"The argument to the block is a \\LayoutQueryIterator object which can be "
|
||||
"asked for specific results.\n"
|
||||
"\n"
|
||||
"The context argument allows supplying an expression execution context. This context can be used for "
|
||||
"example to supply variables for the execution. It has been added in version 0.26.\n"
|
||||
) +
|
||||
gsi::iterator_ext ("each", &iterate2, gsi::arg ("layout"), gsi::arg("cell"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
|
||||
"@brief Executes the query and delivered the results iteratively.\n"
|
||||
"\n"
|
||||
"This version allows specifying a context cell. This cell can be used as a default cell for cell expressions.\n"
|
||||
"\n"
|
||||
"This variant has been introduced in version 0.30."
|
||||
),
|
||||
"@brief A layout query\n"
|
||||
"Layout queries are the backbone of the \"Search & replace\" feature. Layout queries allow retrieval of "
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ struct path_defs
|
|||
|
||||
static size_t hash_value (const C *e)
|
||||
{
|
||||
return std::hfunc (*e);
|
||||
return tl::hfunc (*e);
|
||||
}
|
||||
|
||||
static gsi::Methods methods ()
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ struct point_defs
|
|||
|
||||
static size_t hash_value (const C *pt)
|
||||
{
|
||||
return std::hfunc (*pt);
|
||||
return tl::hfunc (*pt);
|
||||
}
|
||||
|
||||
static C move_d (C *p, const vector_type &d)
|
||||
|
|
|
|||
|
|
@ -28,10 +28,124 @@
|
|||
#include "dbPolygonTools.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "dbHash.h"
|
||||
#include "dbTriangles.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
||||
template <class T>
|
||||
static db::Region region_from_triangles (const db::Triangles &tri, const T &trans)
|
||||
{
|
||||
db::Region result;
|
||||
|
||||
db::Point pts [3];
|
||||
|
||||
for (auto t = tri.begin (); t != tri.end (); ++t) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
pts [i] = trans * *t->vertex (i);
|
||||
}
|
||||
db::SimplePolygon poly;
|
||||
poly.assign_hull (pts + 0, pts + 3);
|
||||
result.insert (poly);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class P, class T>
|
||||
static std::vector<P> polygons_from_triangles (const db::Triangles &tri, const T &trans)
|
||||
{
|
||||
std::vector<P> result;
|
||||
result.reserve (tri.num_triangles ());
|
||||
|
||||
typename P::point_type pts [3];
|
||||
|
||||
for (auto t = tri.begin (); t != tri.end (); ++t) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
pts [i] = trans * *t->vertex (i);
|
||||
}
|
||||
P poly;
|
||||
poly.assign_hull (pts + 0, pts + 3);
|
||||
result.push_back (poly);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
static db::polygon<C> to_polygon (const db::simple_polygon<C> &sp)
|
||||
{
|
||||
db::polygon<C> p;
|
||||
p.assign_hull (sp.begin_hull (), sp.end_hull ());
|
||||
return p;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
static db::polygon<C> to_polygon (const db::polygon<C> &p)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001)
|
||||
{
|
||||
db::Triangles tris;
|
||||
db::Triangles::TriangulateParameters param;
|
||||
param.min_b = min_b;
|
||||
param.max_area = max_area * dbu * dbu;
|
||||
|
||||
db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ()));
|
||||
|
||||
tris.triangulate (to_polygon (*p), param, trans);
|
||||
|
||||
return region_from_triangles (tris, trans.inverted ());
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static db::Region triangulate_ipolygon_v (const P *p, const std::vector<db::Point> &vertexes, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001)
|
||||
{
|
||||
db::Triangles tris;
|
||||
db::Triangles::TriangulateParameters param;
|
||||
param.min_b = min_b;
|
||||
param.max_area = max_area * dbu * dbu;
|
||||
|
||||
db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ()));
|
||||
|
||||
tris.triangulate (to_polygon (*p), vertexes, param, trans);
|
||||
|
||||
return region_from_triangles (tris, trans.inverted ());
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static std::vector<P> triangulate_dpolygon (const P *p, double max_area = 0.0, double min_b = 0.0)
|
||||
{
|
||||
db::Triangles tris;
|
||||
db::Triangles::TriangulateParameters param;
|
||||
param.min_b = min_b;
|
||||
param.max_area = max_area;
|
||||
|
||||
db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ()));
|
||||
|
||||
tris.triangulate (to_polygon (*p), param, trans);
|
||||
|
||||
return polygons_from_triangles<P, db::DCplxTrans> (tris, trans.inverted ());
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static std::vector<P> triangulate_dpolygon_v (const P *p, const std::vector<db::DPoint> &vertexes, double max_area = 0.0, double min_b = 0.0)
|
||||
{
|
||||
db::Triangles tris;
|
||||
db::Triangles::TriangulateParameters param;
|
||||
param.min_b = min_b;
|
||||
param.max_area = max_area;
|
||||
|
||||
db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ()));
|
||||
|
||||
tris.triangulate (to_polygon (*p), vertexes, param, trans);
|
||||
|
||||
return polygons_from_triangles<P, db::DCplxTrans> (tris, trans.inverted ());
|
||||
}
|
||||
|
||||
template <class C>
|
||||
static std::vector<C> split_poly (const C *p)
|
||||
{
|
||||
|
|
@ -275,7 +389,7 @@ struct simple_polygon_defs
|
|||
|
||||
static size_t hash_value (const C *p)
|
||||
{
|
||||
return std::hfunc (*p);
|
||||
return tl::hfunc (*p);
|
||||
}
|
||||
|
||||
static bool touches_box (const C *p, const db::box<coord_type> &box)
|
||||
|
|
@ -766,6 +880,37 @@ Class<db::SimplePolygon> decl_SimplePolygon ("db", "SimplePolygon",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.18.\n"
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_ipolygon<db::SimplePolygon>, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"@return A \\Region holding the triangles of the refined, constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles "
|
||||
"larger than this area will be split. In addition 'skinny' triangles will be resolved where "
|
||||
"possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). "
|
||||
"A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree "
|
||||
"and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to "
|
||||
"a minimum angle of abouth 37 degree.\n"
|
||||
"\n"
|
||||
"The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n"
|
||||
"\n"
|
||||
"The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will "
|
||||
"make the implementation skip the refinement step. In that case, the results are identical to "
|
||||
"the standard constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions "
|
||||
"are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable "
|
||||
"for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_ipolygon_v<db::SimplePolygon>, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
simple_polygon_defs<db::SimplePolygon>::methods (),
|
||||
"@brief A simple polygon class\n"
|
||||
"\n"
|
||||
|
|
@ -861,6 +1006,33 @@ Class<db::DSimplePolygon> decl_DSimplePolygon ("db", "DSimplePolygon",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25.\n"
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_dpolygon<db::DSimplePolygon>, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"@return An array of triangular polygons of the refined, constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles "
|
||||
"larger than this area will be split. In addition 'skinny' triangles will be resolved where "
|
||||
"possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). "
|
||||
"A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree "
|
||||
"and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to "
|
||||
"a minimum angle of abouth 37 degree.\n"
|
||||
"\n"
|
||||
"The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n"
|
||||
"\n"
|
||||
"Picking a value of 0.0 for max area and min b will "
|
||||
"make the implementation skip the refinement step. In that case, the results are identical to "
|
||||
"the standard constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_dpolygon_v<db::DSimplePolygon>, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
simple_polygon_defs<db::DSimplePolygon>::methods (),
|
||||
"@brief A simple polygon class\n"
|
||||
"\n"
|
||||
|
|
@ -1215,7 +1387,7 @@ struct polygon_defs
|
|||
|
||||
static size_t hash_value (const C *p)
|
||||
{
|
||||
return std::hfunc (*p);
|
||||
return tl::hfunc (*p);
|
||||
}
|
||||
|
||||
static bool touches_box (const C *p, const db::box<coord_type> &box)
|
||||
|
|
@ -2035,6 +2207,37 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
|
|||
"\n"
|
||||
"This method was introduced in version 0.18.\n"
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_ipolygon<db::Polygon>, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"@return A \\Region holding the triangles of the refined, constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles "
|
||||
"larger than this area will be split. In addition 'skinny' triangles will be resolved where "
|
||||
"possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). "
|
||||
"A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree "
|
||||
"and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to "
|
||||
"a minimum angle of abouth 37 degree.\n"
|
||||
"\n"
|
||||
"The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n"
|
||||
"\n"
|
||||
"The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will "
|
||||
"make the implementation skip the refinement step. In that case, the results are identical to "
|
||||
"the standard constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions "
|
||||
"are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable "
|
||||
"for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_ipolygon_v<db::Polygon>, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
polygon_defs<db::Polygon>::methods (),
|
||||
"@brief A polygon class\n"
|
||||
"\n"
|
||||
|
|
@ -2157,6 +2360,33 @@ Class<db::DPolygon> decl_DPolygon ("db", "DPolygon",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25.\n"
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_dpolygon<db::DPolygon>, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"@return An array of triangular polygons of the refined, constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles "
|
||||
"larger than this area will be split. In addition 'skinny' triangles will be resolved where "
|
||||
"possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). "
|
||||
"A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree "
|
||||
"and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to "
|
||||
"a minimum angle of abouth 37 degree.\n"
|
||||
"\n"
|
||||
"The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n"
|
||||
"\n"
|
||||
"Picking a value of 0.0 for max area and min b will "
|
||||
"make the implementation skip the refinement step. In that case, the results are identical to "
|
||||
"the standard constrained Delaunay triangulation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
method_ext ("delaunay", &triangulate_dpolygon_v<db::DPolygon>, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0),
|
||||
"@brief Performs a Delaunay triangulation of the polygon.\n"
|
||||
"\n"
|
||||
"This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
polygon_defs<db::DPolygon>::methods (),
|
||||
"@brief A polygon class\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -217,6 +217,22 @@ static void map_properties (db::RecursiveShapeIterator *c, const std::map<tl::Va
|
|||
}
|
||||
}
|
||||
|
||||
static tl::Variant get_property (const db::RecursiveShapeIterator *s, const tl::Variant &key)
|
||||
{
|
||||
db::properties_id_type id = s->prop_id ();
|
||||
|
||||
const db::PropertiesSet &props = db::properties (id);
|
||||
return props.value (key);
|
||||
}
|
||||
|
||||
static tl::Variant get_properties (const db::RecursiveShapeIterator *s)
|
||||
{
|
||||
db::properties_id_type id = s->prop_id ();
|
||||
|
||||
const db::PropertiesSet &props = db::properties (id);
|
||||
return props.to_dict_var ();
|
||||
}
|
||||
|
||||
Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveShapeIterator",
|
||||
gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"),
|
||||
"@brief Creates a recursive, single-layer shape iterator.\n"
|
||||
|
|
@ -622,6 +638,21 @@ Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveS
|
|||
"\n"
|
||||
"This attribute has been introduced in version 0.28.4."
|
||||
) +
|
||||
gsi::method_ext ("property", &get_property, gsi::arg ("key"),
|
||||
"@brief Gets the effective user property with the given key\n"
|
||||
"See \\prop_id for the definition of 'effective user property'.\n\n"
|
||||
"This method is a convenience method that gets the effective property of the current shape with the given key. "
|
||||
"If no property with that key exists, it will return nil.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
gsi::method_ext ("properties", &get_properties,
|
||||
"@brief Gets the effective user properties\n"
|
||||
"See \\prop_id for the definition of 'effective user properties'.\n\n"
|
||||
"This method is a convenience method that gets the effective properties of the current shape as a single hash.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.30."
|
||||
) +
|
||||
gsi::method ("shape", &db::RecursiveShapeIterator::shape,
|
||||
"@brief Gets the current shape\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -1379,6 +1379,16 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector
|
|||
return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny);
|
||||
}
|
||||
|
||||
static tl::Variant nth (const db::Region *region, size_t n)
|
||||
{
|
||||
const db::Polygon *poly = region->nth (n);
|
||||
if (! poly) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (db::PolygonWithProperties (*poly, region->nth_prop_id (n)));
|
||||
}
|
||||
}
|
||||
|
||||
static db::generic_shape_iterator<db::PolygonWithProperties> begin_region (const db::Region *region)
|
||||
{
|
||||
return db::generic_shape_iterator<db::PolygonWithProperties> (db::make_wp_iter (region->delegate ()->begin ()));
|
||||
|
|
@ -4103,14 +4113,16 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n"
|
||||
"Starting with version 0.30, the iterator delivers a RegionWithProperties object."
|
||||
) +
|
||||
method ("[]", &db::Region::nth, gsi::arg ("n"),
|
||||
method_ext ("[]", &nth, gsi::arg ("n"),
|
||||
"@brief Returns the nth polygon of the region\n"
|
||||
"\n"
|
||||
"This method returns nil if the index is out of range. It is available for flat regions only - i.e. "
|
||||
"those for which \\has_valid_polygons? is true. Use \\flatten to explicitly flatten a region.\n"
|
||||
"This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).\n"
|
||||
"\n"
|
||||
"The \\each iterator is the more general approach to access the polygons."
|
||||
"The \\each iterator is the more general approach to access the polygons.\n"
|
||||
"\n"
|
||||
"Since version 0.30.1, this method returns a \\PolygonWithProperties object."
|
||||
) +
|
||||
method ("flatten", &db::Region::flatten,
|
||||
"@brief Explicitly flattens a region\n"
|
||||
|
|
|
|||
|
|
@ -1315,8 +1315,8 @@ Class<db::Shape> decl_Shape ("db", "Shape",
|
|||
gsi::method_ext ("property", &get_property, gsi::arg ("key"),
|
||||
"@brief Gets the user property with the given key\n"
|
||||
"This method is a convenience method that gets the property with the given key. "
|
||||
"If no property with that key does not exist, it will return nil. Using that method is more "
|
||||
"convenient than using the layout object and the properties ID to retrieve the property value. "
|
||||
"If no property with that key exists, it will return nil. Using that method is more "
|
||||
"convenient than using the layout object and the properties ID to retrieve the property value.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.22."
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ struct text_defs
|
|||
|
||||
static size_t hash_value (const C *box)
|
||||
{
|
||||
return std::hfunc (*box);
|
||||
return tl::hfunc (*box);
|
||||
}
|
||||
|
||||
static gsi::Methods methods ()
|
||||
|
|
|
|||
|
|
@ -349,10 +349,10 @@ static db::Texts moved_xy (const db::Texts *r, db::Coord x, db::Coord y)
|
|||
return r->transformed (db::Disp (db::Vector (x, y)));
|
||||
}
|
||||
|
||||
static db::Region polygons0 (const db::Texts *e, db::Coord d)
|
||||
static db::Region polygons0 (const db::Texts *e, db::Coord d, const tl::Variant &text_prop)
|
||||
{
|
||||
db::Region r;
|
||||
e->polygons (r, d);
|
||||
e->polygons (r, d, text_prop);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -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 ()));
|
||||
|
|
@ -710,9 +720,13 @@ Class<db::Texts> decl_Texts (decl_dbShapeCollection, "db", "Texts",
|
|||
"@brief Returns a region with the enlarged bounding boxes of the texts\n"
|
||||
"This method acts like the other version of \\extents, but allows giving different enlargements for x and y direction.\n"
|
||||
) +
|
||||
method_ext ("polygons", &polygons0, gsi::arg ("e", db::Coord (1)),
|
||||
method_ext ("polygons", &polygons0, gsi::arg ("e", db::Coord (1)), gsi::arg ("text_prop", tl::Variant (), "nil"),
|
||||
"@brief Converts the edge pairs to polygons\n"
|
||||
"This method creates polygons from the texts. This is equivalent to calling \\extents."
|
||||
"This method creates polygons from the texts. This is basically equivalent to calling \\extents. "
|
||||
"In addition, a user property with the key given by 'text_prop' can be attached. The value of that "
|
||||
"user property will be the text string. If 'text_prop' is nil, no user property is attached.\n"
|
||||
"\n"
|
||||
"The 'text_prop' argument has been added in version 0.30."
|
||||
) +
|
||||
method_ext ("filter", &filter, gsi::arg ("filter"),
|
||||
"@brief Applies a generic filter in place (replacing the texts from the Texts collection)\n"
|
||||
|
|
@ -842,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"
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ struct trans_defs
|
|||
|
||||
static size_t hash_value (const C *t)
|
||||
{
|
||||
return std::hfunc (*t);
|
||||
return tl::hfunc (*t);
|
||||
}
|
||||
|
||||
static gsi::Methods methods ()
|
||||
|
|
@ -853,7 +853,7 @@ struct cplx_trans_defs
|
|||
|
||||
static size_t hash_value (const C *t)
|
||||
{
|
||||
return std::hfunc (*t);
|
||||
return tl::hfunc (*t);
|
||||
}
|
||||
|
||||
static gsi::Methods methods ()
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ struct vector_defs
|
|||
|
||||
static size_t hash_value (const C *v)
|
||||
{
|
||||
return std::hfunc (*v);
|
||||
return tl::hfunc (*v);
|
||||
}
|
||||
|
||||
static db::point<coord_type> add_with_point (const C *v, const db::point<coord_type> &p)
|
||||
|
|
|
|||
|
|
@ -485,8 +485,41 @@ TEST(7)
|
|||
cib.push_back (b1.cell_index ());
|
||||
cib.push_back (b2.cell_index ());
|
||||
cm.create_multi_mapping_full (h, cib, *g, cia);
|
||||
EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5");
|
||||
EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5");
|
||||
|
||||
EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:");
|
||||
}
|
||||
|
||||
// Issue #2014
|
||||
TEST(8)
|
||||
{
|
||||
std::unique_ptr<db::Layout> g (new db::Layout ());
|
||||
db::Cell &a (g->cell (g->add_cell ("a")));
|
||||
db::Cell &b (g->cell (g->add_cell ("b")));
|
||||
db::Cell &b1 (g->cell (g->add_cell ("b1")));
|
||||
db::Cell &b2 (g->cell (g->add_cell ("b2")));
|
||||
db::Cell &c (g->cell (g->add_cell ("c")));
|
||||
|
||||
b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ()));
|
||||
b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ()));
|
||||
b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ()));
|
||||
b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ()));
|
||||
|
||||
db::Layout h;
|
||||
db::Cell &ha (h.cell (h.add_cell ("a")));
|
||||
db::Cell &hb (h.cell (h.add_cell ("b")));
|
||||
db::Cell &hc (h.cell (h.add_cell ("c")));
|
||||
|
||||
db::CellMapping cm;
|
||||
std::vector<db::cell_index_type> cib, cia;
|
||||
cia.push_back (a.cell_index ());
|
||||
cia.push_back (b.cell_index ());
|
||||
cia.push_back (c.cell_index ());
|
||||
cib.push_back (ha.cell_index ());
|
||||
cib.push_back (hb.cell_index ());
|
||||
cib.push_back (hc.cell_index ());
|
||||
cm.create_multi_mapping_full (h, cib, *g, cia);
|
||||
EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c");
|
||||
|
||||
EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,3 +279,24 @@ TEST(6_add_with_properties)
|
|||
EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}");
|
||||
}
|
||||
|
||||
TEST(7_properties)
|
||||
{
|
||||
db::PropertiesSet ps;
|
||||
|
||||
ps.insert (tl::Variant ("id"), 1);
|
||||
db::properties_id_type pid1 = db::properties_id (ps);
|
||||
|
||||
db::EdgePairs edge_pairs;
|
||||
db::Edge e1 (db::Point (0, 0), db::Point (10, 20));
|
||||
db::Edge e2 (db::Point (1, 2), db::Point (11, 22));
|
||||
edge_pairs.insert (db::EdgePairWithProperties (db::EdgePair (e1, e2), pid1));
|
||||
edge_pairs.insert (db::EdgePair (e1, e2));
|
||||
|
||||
EXPECT_EQ (edge_pairs.nth (0)->to_string (), "(0,0;10,20)/(1,2;11,22)");
|
||||
EXPECT_EQ (edge_pairs.nth (1)->to_string (), "(0,0;10,20)/(1,2;11,22)");
|
||||
EXPECT_EQ (edge_pairs.nth (2) == 0, true);
|
||||
|
||||
EXPECT_EQ (edge_pairs.nth_prop_id (0), db::properties_id_type (0));
|
||||
EXPECT_EQ (edge_pairs.nth_prop_id (1), pid1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1443,6 +1443,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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -515,6 +515,14 @@ TEST(1)
|
|||
EXPECT_EQ (s, "c1,c4,c5x");
|
||||
}
|
||||
|
||||
{
|
||||
// $_ is a placeholder for the current cell
|
||||
db::LayoutQuery q ("$_.*");
|
||||
db::LayoutQueryIterator iq (q, &g, &g.cell (g.cell_by_name ("c4").second));
|
||||
std::string s = q2s_var (iq, "cell_name");
|
||||
EXPECT_EQ (s, "c1,c3"); // child cells of "c4"
|
||||
}
|
||||
|
||||
{
|
||||
// Another way of saying "c2x.*"
|
||||
db::LayoutQuery q ("*.$(cell_name=='c2x'?'*':'')");
|
||||
|
|
|
|||
|
|
@ -362,3 +362,20 @@ TEST(11)
|
|||
EXPECT_EQ (to_string (pts), "(-100,10;1010,10;1010,-10;0,-10;0,10;1010,10;1010,-10;-100,-10)");
|
||||
}
|
||||
|
||||
// issue #2002
|
||||
TEST(12)
|
||||
{
|
||||
db::Path path;
|
||||
db::Path::pointlist_type pts;
|
||||
|
||||
tl::Extractor ("(143,381;262,260;381,141) w=400 bx=0 ex=0 r=false").read (path);
|
||||
|
||||
path.hull (pts, 4);
|
||||
EXPECT_EQ (to_string (pts), "(286,521;454,350;522,282;240,0;70,170;0,241)");
|
||||
|
||||
tl::Extractor ("(143,381;262,260;381,141) w=1000 bx=0 ex=0 r=false").read (path);
|
||||
|
||||
path.hull (pts, 4);
|
||||
EXPECT_EQ (to_string (pts), "(286,521;454,350;522,282;240,0;70,170;0,241;499,732;564,666;735,495;27,-213;-40,-146;-213,30)");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,595 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2025 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbQuadTree.h"
|
||||
#include "dbBoxConvert.h"
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlString.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct MyQuadTreeCMP
|
||||
{
|
||||
bool operator() (const db::DBox &a, const db::DBox &b) const
|
||||
{
|
||||
return a.equal (b);
|
||||
}
|
||||
};
|
||||
|
||||
typedef db::quad_tree<db::DBox, db::box_convert<db::DBox>, size_t (1), MyQuadTreeCMP> MyQuadTree;
|
||||
|
||||
std::string find_all (const MyQuadTree &qt)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin ();
|
||||
while (! i.at_end ()) {
|
||||
v.push_back (i->to_string ());
|
||||
++i;
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_touching (const MyQuadTree &qt, const db::DBox &box, bool report = false)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin_touching (box);
|
||||
while (! i.at_end ()) {
|
||||
v.push_back (i->to_string ());
|
||||
++i;
|
||||
}
|
||||
if (report) {
|
||||
tl::info << v.size () << " items found.";
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_touching_from_all (const MyQuadTree &qt, const db::DBox &box)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin ();
|
||||
while (! i.at_end ()) {
|
||||
if (i->touches (box)) {
|
||||
v.push_back (i->to_string ());
|
||||
}
|
||||
++i;
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_overlapping (const MyQuadTree &qt, const db::DBox &box, bool report = false)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin_overlapping (box);
|
||||
while (! i.at_end ()) {
|
||||
v.push_back (i->to_string ());
|
||||
++i;
|
||||
}
|
||||
if (report) {
|
||||
tl::info << v.size () << " items found.";
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_overlapping_from_all (const MyQuadTree &qt, const db::DBox &box)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin ();
|
||||
while (! i.at_end ()) {
|
||||
if (i->overlaps (box)) {
|
||||
v.push_back (i->to_string ());
|
||||
}
|
||||
++i;
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
TEST(basic)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
tree.insert (db::DBox ());
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (1));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)");
|
||||
|
||||
db::DBox bx;
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (2));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;3,0)");
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (3));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;3,0)");
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (4));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
}
|
||||
|
||||
TEST(remove)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
EXPECT_EQ (tree.erase (db::DBox (-1, -3, -0.5, -1)), false);
|
||||
EXPECT_EQ (tree.erase (db::DBox (-1, -3, -0.5, -2)), true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
while (! tree.empty ()) {
|
||||
EXPECT_EQ (tree.erase (*tree.begin ()), true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
}
|
||||
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
}
|
||||
|
||||
TEST(grow)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
tree.insert (db::DBox (-100, -3, -99, 2));
|
||||
EXPECT_EQ (tree.levels (), size_t (8));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)/(-100,-3;-99,2)");
|
||||
EXPECT_EQ (find_overlapping (tree, db::DBox (-100, -100, -90, 100)), "(-100,-3;-99,2)");
|
||||
|
||||
bool r = true;
|
||||
while (r && ! tree.empty ()) {
|
||||
r = tree.erase (*tree.begin ());
|
||||
EXPECT_EQ (r, true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
}
|
||||
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
}
|
||||
|
||||
TEST(grow2)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
tree.insert (db::DBox (-100, -3, -99, -1));
|
||||
EXPECT_EQ (tree.levels (), size_t (8));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)/(-100,-3;-99,-1)");
|
||||
EXPECT_EQ (find_overlapping (tree, db::DBox (-100, -100, -90, 100)), "(-100,-3;-99,-1)");
|
||||
|
||||
bool r = true;
|
||||
while (r && ! tree.empty ()) {
|
||||
r = tree.erase (*tree.begin ());
|
||||
EXPECT_EQ (r, true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
}
|
||||
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
}
|
||||
|
||||
TEST(clear)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
tree.clear ();
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
EXPECT_EQ (find_all (tree), "");
|
||||
}
|
||||
|
||||
TEST(copy)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
MyQuadTree tree2 (tree);
|
||||
tree.clear ();
|
||||
|
||||
EXPECT_EQ (tree2.check (), true);
|
||||
EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree2.levels (), size_t (3));
|
||||
}
|
||||
|
||||
TEST(assign)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
MyQuadTree tree2;
|
||||
tree2 = tree;
|
||||
tree.clear ();
|
||||
|
||||
EXPECT_EQ (tree2.check (), true);
|
||||
EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree2.levels (), size_t (3));
|
||||
}
|
||||
|
||||
TEST(swap)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
MyQuadTree tree2;
|
||||
tree2.swap (tree);
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (find_all (tree), "");
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (tree2.check (), true);
|
||||
EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree2.levels (), size_t (3));
|
||||
}
|
||||
|
||||
TEST(move)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
MyQuadTree tree2;
|
||||
tree2 = std::move (tree);
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (find_all (tree), "");
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (tree2.check (), true);
|
||||
EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree2.levels (), size_t (3));
|
||||
}
|
||||
|
||||
TEST(move_ctor)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree.levels (), size_t (3));
|
||||
|
||||
MyQuadTree tree2 (std::move (tree));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (find_all (tree), "");
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (tree2.check (), true);
|
||||
EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
EXPECT_EQ (tree2.levels (), size_t (3));
|
||||
}
|
||||
|
||||
static double rvalue ()
|
||||
{
|
||||
return ((rand () % 1000000) - 5000) * 0.001;
|
||||
}
|
||||
|
||||
static db::DBox rbox ()
|
||||
{
|
||||
db::DBox box;
|
||||
while ((box = db::DBox (db::DPoint (rvalue (), rvalue ()), db::DPoint (rvalue (), rvalue ()))).empty ()) {
|
||||
;
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
static db::DBox rbox (double dim)
|
||||
{
|
||||
db::DBox box;
|
||||
db::DPoint c (rvalue (), rvalue ());
|
||||
return box = db::DBox (c, c).enlarged (db::DVector (dim * 0.5, dim * 0.5));
|
||||
}
|
||||
|
||||
TEST(many)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
|
||||
unsigned int n = 1000;
|
||||
unsigned int ntests = 100;
|
||||
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
tree.insert (rbox ());
|
||||
}
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.size (), size_t (n));
|
||||
|
||||
bool report = false;
|
||||
for (unsigned int i = 0; i < ntests; ++i) {
|
||||
if (report) {
|
||||
tl::info << "Test iteration " << i << " ...";
|
||||
}
|
||||
auto bx = rbox ();
|
||||
EXPECT_EQ (find_overlapping (tree, bx, report), find_overlapping_from_all (tree, bx));
|
||||
EXPECT_EQ (find_touching (tree, bx, report), find_touching_from_all (tree, bx));
|
||||
bx = db::DBox (bx.center (), bx.center ());
|
||||
EXPECT_EQ (find_touching (tree, bx, report), find_touching_from_all (tree, bx));
|
||||
}
|
||||
|
||||
bool r = true;
|
||||
while (r && ! tree.empty ()) {
|
||||
r = tree.erase (*tree.begin ());
|
||||
EXPECT_EQ (r, true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
}
|
||||
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
}
|
||||
|
||||
TEST(timing_insert)
|
||||
{
|
||||
MyQuadTree tree;
|
||||
|
||||
{
|
||||
unsigned int n = 1000000;
|
||||
tl::SelfTimer timer (tl::sprintf ("%d inserts ..", int (n)));
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
tree.insert (rbox ());
|
||||
}
|
||||
tl::info << "Quad levels: " << tree.levels ();
|
||||
}
|
||||
|
||||
tree.clear ();
|
||||
|
||||
{
|
||||
unsigned int n = 2000000;
|
||||
tl::SelfTimer timer (tl::sprintf ("%d inserts ..", int (n)));
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
tree.insert (rbox ());
|
||||
}
|
||||
tl::info << "Quad levels: " << tree.levels ();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(timing_lookup)
|
||||
{
|
||||
test_is_long_runner ();
|
||||
|
||||
MyQuadTree tree;
|
||||
|
||||
unsigned int n = 1000000;
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
tree.insert (rbox (5.0));
|
||||
}
|
||||
|
||||
unsigned int ntests = 1000;
|
||||
|
||||
std::vector<std::pair<db::DBox, std::pair<size_t, size_t> > > tests;
|
||||
for (unsigned int i = 0; i < ntests; ++i) {
|
||||
db::DBox bx = rbox (5.0);
|
||||
tests.push_back (std::make_pair (bx, std::make_pair (size_t (0), size_t (0))));
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer (tl::sprintf ("%d tests (lookup) ..", int (ntests)));
|
||||
for (auto t = tests.begin (); t != tests.end (); ++t) {
|
||||
size_t n = 0;
|
||||
for (auto i = tree.begin_touching (t->first); ! i.at_end (); ++i) {
|
||||
++n;
|
||||
}
|
||||
t->second.first = n;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer (tl::sprintf ("%d tests (brute force) ..", int (ntests)));
|
||||
for (auto t = tests.begin (); t != tests.end (); ++t) {
|
||||
size_t n = 0;
|
||||
for (auto i = tree.begin (); ! i.at_end (); ++i) {
|
||||
if (i->touches (t->first)) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
t->second.second = n;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto t = tests.begin (); t != tests.end (); ++t) {
|
||||
EXPECT_EQ (t->second.first, t->second.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -756,15 +756,25 @@ namespace {
|
|||
: public db::RecursiveShapeReceiver
|
||||
{
|
||||
public:
|
||||
FlatPusher (std::set<db::Box> *boxes) : mp_boxes (boxes) { }
|
||||
FlatPusher (std::set<db::Box> *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { }
|
||||
|
||||
void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
|
||||
{
|
||||
mp_boxes->insert (trans * shape.bbox ());
|
||||
}
|
||||
|
||||
std::string to_string () const
|
||||
{
|
||||
std::vector<std::string> s;
|
||||
for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) {
|
||||
s.push_back (i->to_string ());
|
||||
}
|
||||
return tl::join (s.begin (), s.end (), ";");
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<db::Box> *mp_boxes;
|
||||
std::set<db::Box> m_boxes;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1038,7 +1048,7 @@ public:
|
|||
m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n";
|
||||
}
|
||||
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
|
||||
{
|
||||
m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ());
|
||||
if (all) {
|
||||
|
|
@ -1048,7 +1058,7 @@ public:
|
|||
return NI_all;
|
||||
}
|
||||
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
|
||||
{
|
||||
m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans);
|
||||
if (all) {
|
||||
|
|
@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray
|
|||
public:
|
||||
ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { }
|
||||
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all);
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
|
||||
return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip;
|
||||
}
|
||||
|
||||
|
|
@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne
|
|||
public:
|
||||
ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { }
|
||||
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all);
|
||||
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
|
||||
return inst.object ().cell_index () != m_rejected ? NI_all : NI_single;
|
||||
}
|
||||
|
||||
|
|
@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance
|
|||
public:
|
||||
ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { }
|
||||
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all);
|
||||
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes);
|
||||
return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected;
|
||||
}
|
||||
|
||||
|
|
@ -1586,49 +1596,174 @@ TEST(12_ForMerged)
|
|||
db::RecursiveShapeIterator i1 (*g, c0, 0);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
std::vector<unsigned int> lv;
|
||||
lv.push_back (0);
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, lv);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
lv.push_back (1); // empty, but kills "for merged" optimization
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, lv);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
// no longer optimized
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
|
||||
}
|
||||
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50));
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)");
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
|
||||
}
|
||||
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50));
|
||||
i1.set_overlapping (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(12b_ForMerged)
|
||||
{
|
||||
std::unique_ptr<db::Layout> g (new db::Layout ());
|
||||
g->insert_layer (0);
|
||||
g->insert_layer (1);
|
||||
db::Cell &c0 (g->cell (g->add_cell ()));
|
||||
db::Cell &c1 (g->cell (g->add_cell ()));
|
||||
|
||||
db::Box b (0, 100, 1000, 1200);
|
||||
c0.shapes (0).insert (db::Box (0, 0, 3000, 2200));
|
||||
c1.shapes (0).insert (b);
|
||||
|
||||
db::Trans tt;
|
||||
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt));
|
||||
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l));
|
||||
|
||||
std::string x;
|
||||
|
||||
db::RecursiveShapeIterator i1 (*g, c0, 0);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i1.reset ();
|
||||
i1.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
|
||||
i1.set_for_merged_input (false);
|
||||
x = collect(i1, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
|
||||
EXPECT_EQ (collect_with_copy(i1, *g), x);
|
||||
|
||||
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000))));
|
||||
|
||||
db::RecursiveShapeIterator i2 (*g, c0, 0);
|
||||
|
||||
x = collect(i2, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
|
||||
EXPECT_EQ (collect_with_copy(i2, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i2.reset ();
|
||||
i2.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
|
||||
i2.set_for_merged_input (true);
|
||||
x = collect(i2, *g);
|
||||
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
|
||||
EXPECT_EQ (collect_with_copy(i2, *g), x);
|
||||
|
||||
{
|
||||
FlatPusher f;
|
||||
i2.reset ();
|
||||
i2.push (&f);
|
||||
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(13_ForMergedPerformance)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -276,3 +276,53 @@ TEST(8_add_with_properties)
|
|||
EXPECT_EQ (r.to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}");
|
||||
EXPECT_EQ ((ro1 + rf2).to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}");
|
||||
}
|
||||
|
||||
TEST(9_polygons)
|
||||
{
|
||||
db::DeepShapeStore dss ("TOP", 0.001);
|
||||
db::Texts rf;
|
||||
db::Texts rd (dss);
|
||||
|
||||
rf.insert (db::Text ("ABC", db::Trans (db::Vector (10, 20))));
|
||||
rf.insert (db::Text ("XZY", db::Trans (db::Vector (-10, -20))));
|
||||
|
||||
rd.insert (db::Text ("ABC", db::Trans (db::Vector (10, 20))));
|
||||
rd.insert (db::Text ("XZY", db::Trans (db::Vector (-10, -20))));
|
||||
|
||||
db::Region r;
|
||||
|
||||
rf.polygons (r, 1);
|
||||
EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19);(-11,-21;-11,-19;-9,-19;-9,-21)");
|
||||
|
||||
rf.polygons (r, 2);
|
||||
EXPECT_EQ (r.to_string (), "(8,18;8,22;12,22;12,18);(-12,-22;-12,-18;-8,-18;-8,-22)");
|
||||
|
||||
rd.polygons (r, 1);
|
||||
EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19);(-11,-21;-11,-19;-9,-19;-9,-21)");
|
||||
|
||||
rf.polygons (r, 1, tl::Variant (17));
|
||||
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}");
|
||||
|
||||
rd.polygons (r, 1, tl::Variant (17));
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue