Compare commits

...

108 Commits

Author SHA1 Message Date
Matthias Koefferlein 6cd4b97cd3 Including internal net names in LVS DB
Problem: so far, these names have not been conveyed.
But after the net IDs changed in the LVS DB (renumbering),
the names should be retained. Otherwise it is difficult to
relate log level entries or Spice netlists to them.
2025-05-15 21:17:15 +02:00
Matthias Koefferlein a208c6d25d Merge branch 'master' of www.klayout.org:/home/matthias/git/klayout 2025-05-11 21:42:09 +02:00
Matthias Koefferlein e232bf8127 Specify build system in pyproject.toml, using SPDX compliant license spec. 2025-05-10 19:59:19 +00:00
Matthias Köfferlein 56d91acc15
Merge pull request #2035 from KLayout/dependabot/github_actions/pypa/cibuildwheel-2.23.3
Bump pypa/cibuildwheel from 2.23.2 to 2.23.3
2025-05-04 23:13:33 +02:00
Matthias Köfferlein 0f39cef21f
Merge pull request #2037 from Kazzz-S/0.30.1-mac1
To use Ruby 3.3.8 from MacPorts
2025-05-04 23:13:22 +02:00
Matthias Koefferlein 6d871166f7 Updating changelog for 0.30.2 2025-05-04 23:12:47 +02:00
Matthias Koefferlein 38df5aa392 Updating changelog for 0.30.1 2025-05-04 23:06:22 +02:00
dependabot[bot] 92d79d46c1
Bump pypa/cibuildwheel from 2.23.2 to 2.23.3
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.2 to 2.23.3.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.2...v2.23.3)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 2.23.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 21:31:41 +00:00
Kazunari Sekigawa 65a9465bc1 To use Ruby 3.3.8 from MacPorts and fix some Python syntax warnings 2025-04-30 06:40:20 +09:00
Matthias Koefferlein cb649e6ee6 Merge branch 'master' of github.com:KLayout/klayout 2025-04-27 20:56:38 +02:00
Matthias Köfferlein e76e9b7d0b
Merge pull request #2017 from KLayout/bugfix/issue-2014
Fixed issue #2014 (strm2oas lef/def/gds collect drops cells reference…
2025-04-27 20:53:47 +02:00
klayoutmatthias 6e7eff95e7 Fixed glob feature for Windows. 2025-04-27 19:16:37 +02:00
Matthias Koefferlein 43310e7f49 Updating Python stubs 2025-04-27 16:07:01 +02:00
Matthias Koefferlein 9c7ccefc9f Merge branch 'master' of github.com:KLayout/klayout 2025-04-27 14:44:32 +02:00
Matthias Koefferlein 9aa8d79bfc Enhanced unit test for better debugging 2025-04-27 14:43:53 +02:00
Matthias Köfferlein c7514ba24d
Merge pull request #2031 from KLayout/bugfix/issue-2011
Bugfix/issue 2011
2025-04-27 14:35:37 +02:00
Matthias Köfferlein efb3b3eb3d
Merge pull request #2030 from KLayout/feature/issue-2024
Provide a solution of feature request #2024
2025-04-27 14:35:24 +02:00
Matthias Köfferlein 345f316af5
Merge pull request #2029 from KLayout/bugfix/issue-2026
Fixing issue #2026 (after 2.5D display main 2D layout does not displa…
2025-04-27 14:35:14 +02:00
Matthias Köfferlein 89d5bf0df1
Merge pull request #2028 from KLayout/bugfix/issue-2025
Fixed issue #2025 (brackets get added on PCell parameters)
2025-04-27 14:34:52 +02:00
Matthias Köfferlein d8a13037a9
Merge pull request #2023 from KLayout/bugfix/issue-2020
Fixing the strict weak ordering issue inside the edge processor
2025-04-27 14:34:37 +02:00
Matthias Köfferlein 09ad36e298
Merge pull request #2022 from KLayout/bugfix/issue-2019
Fixing issue #2019 (build issue against Qt 6.9)
2025-04-27 14:34:24 +02:00
Matthias Koefferlein c55a0757c1 Preparations for 0.30.1 2025-04-27 14:27:04 +02:00
Matthias Koefferlein 5077b22963 Bugfix: deep, empty layers still need a layout attached 2025-04-27 00:30:50 +02:00
Matthias Koefferlein 5efcf83640 Bugfix: Deep mode XOR needs to maintain the layout origin of the first argument even if it is empty 2025-04-26 23:24:26 +02:00
Matthias Koefferlein ffa42653fe Addressing issue #2011
- "report" can now be late in DRC without internal error
  Yet, the report will only capture the output layers
  after the report statement has been called.
- Text objects don't create zero-area polygons in deep mode
  XOR now.
2025-04-26 22:04:50 +02:00
Matthias Koefferlein 2435e774f4 Preventing an internal error when using report after 'input' 2025-04-26 19:05:40 +02:00
Matthias Koefferlein 2bd82af6fe Provide a solution of feature request #2024
- There is a new configuration page entry called
  "Min spacing" for the grid. The default value is 4.
  The value specifies the grid min spacing in
  units of UI font height.
- A bugfix is included: the ruler now is drawn after
  the grid, hence is not hidden by it (specifically
  in checkerboard pattern mode)
- To allow bigger grid spacing, the ruler now
  is allowed to grow bigger than before.
2025-04-26 16:52:39 +02:00
Matthias Koefferlein 148498f840 Fixing issue #2026 (after 2.5D display main 2D layout does not display anymore with visible shapes) 2025-04-26 16:15:25 +02:00
Matthias Koefferlein d317dc2fe3 Fixed issue #2025 (brackets get added on PCell parameters) 2025-04-24 23:30:50 +02:00
Matthias Koefferlein fa618a5b76 Fixing the strict weak ordering issue inside the edge processor 2025-04-09 23:35:09 +02:00
Matthias Koefferlein 09329442f0 Fixing issue #2019 (build issue against Qt 6.9) 2025-04-09 23:30:13 +02:00
Matthias Koefferlein 0ec8e18173 refining the DEF warning on missing foreign cell 2025-04-08 23:52:50 +02:00
Matthias Koefferlein 3bd9a96f67 Merge branch 'bugfix/strmxor' into bugfix/issue-2014 2025-04-08 22:55:45 +02:00
Matthias Koefferlein a27fd3e0be Drop OASIS warning about ghost cells and print a DEF reader warning if a foreign cell cannot be substituted 2025-04-08 22:40:04 +02:00
Matthias Koefferlein 0542ef835a strm2xor: parallelize by layer and not internally with -u 2025-04-08 21:14:19 +02:00
Matthias Koefferlein 3a752fd2c7 Adding 'total' time for -d11 in all scripts 2025-04-08 19:55:50 +02:00
Matthias Koefferlein e8d796aded Fixed unit tests 2025-04-08 19:38:56 +02:00
Matthias Koefferlein 8150e732af Hopefully fixing strm2xor finally, added a test 2025-04-08 19:14:01 +02:00
Matthias Koefferlein 789e183be9 Shortcutting hierarchy in case of skipped shapes, this restores the original performance 2025-04-08 00:04:21 +02:00
Matthias Koefferlein 41e9cb5893 Maybe fixing basic issues with strmxor
1. Output of shape countsi in deep mode was hierarchical with
   output file, flat without
2. Refactoring of XOR (for_merged optimization) needed to
   create cover cell variants
2025-04-07 00:53:43 +02:00
Matthias Koefferlein 6b5268e5f7 Feature glob expansion on LEF and GDS lists for LEF/DEF reader options. 2025-04-06 19:21:02 +02:00
Matthias Koefferlein 83e0c17291 Print total runtime for converter buddy tools with -d 11 2025-04-06 19:20:38 +02:00
Matthias Koefferlein 163c3b8edc Making "assume FOREIGN always default for strm* tools", OASIS warns on ghost cells 2025-04-06 12:50:34 +02:00
Matthias Koefferlein bcf14ede3e Fixed issue #2014 (strm2oas lef/def/gds collect drops cells referenced by sky130 spare)
Problem was that there was the implicit assumption that
substitution cells would be top cells (or at least: not
child cells of other substitution cells).
2025-04-05 22:06:29 +02:00
Matthias Köfferlein 449a9a968e
Merge pull request #2015 from KLayout/bugfix/issue-2012
Bugfix/issue 2012
2025-04-05 19:21:35 +02:00
Matthias Köfferlein efe90cf29b
Merge pull request #2013 from KLayout/dependabot/github_actions/pypa/cibuildwheel-2.23.2
Bump pypa/cibuildwheel from 2.23.0 to 2.23.2
2025-04-05 19:21:20 +02:00
Matthias Köfferlein edcb283a87
Merge pull request #2006 from KLayout/bugfix/issue-2004
Bugfix/issue 2004
2025-04-05 19:21:05 +02:00
Matthias Koefferlein 78e2074b4c Added a unit test 2025-04-03 21:15:49 +02:00
Matthias Koefferlein c656700b44 Maybe fixed issue-2012 (leaking reference in Python) 2025-04-03 20:44:34 +02:00
dependabot[bot] 0a587fa079
Bump pypa/cibuildwheel from 2.23.0 to 2.23.2
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.0 to 2.23.2.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.0...v2.23.2)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 2.23.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-01 21:10:10 +00:00
Matthias Koefferlein a38bea3086 Fixed a unit test 2025-03-29 19:35:26 +01:00
Matthias Koefferlein 40a0113ce5 Fixing Texts[], EdgePairs[] and Edges[] operators - both in terms of returning xWithProperties and objects with properties at all 2025-03-27 00:27:53 +01:00
Matthias Koefferlein 3aebf90ecd Texts[n] now also provides a TextWithProperties object. Bugfix: this method was not delivering any objects with properties at all. 2025-03-26 01:22:54 +01:00
Matthias Koefferlein a24d5388d7 Region[] now returns a PolygonWithProperties object or nil 2025-03-26 00:56:54 +01:00
Matthias Koefferlein 227203cdd1 Providing a less strict overload resolution
Downcast now has precedence over conversion constructors, hence less ambiguities
Solution is implemented for
- Ruby
- Python
- Expressions

For Expressions:
- The overload resolution is less evolved anyway
- There was an additional bug preventing to pass arrays (hashes) in expressions
2025-03-26 00:45:16 +01:00
Matthias Koefferlein db66a6ee74 Base class of DEdgePairWithProperties was EdgePair, not DEdgePair 2025-03-25 23:43:14 +01:00
Matthias Koefferlein e6ff30adee Modifying definition of Timer#memory_size on Linux to become resident size. 2025-03-23 18:46:15 +01:00
Matthias Koefferlein db728dc68a Merge commit 'refs/pull/2003/head' of github.com:KLayout/klayout 2025-03-23 18:38:42 +01:00
Matthias Koefferlein b748579bd7 Updating Python stubs 2025-03-23 18:35:50 +01:00
Matthias Koefferlein a5625c6d86 New golden testdata for MSYS2 2025-03-23 18:14:44 +01:00
Matthias Koefferlein b493be2dfa New golden testdata for MSVC2017 2025-03-23 18:12:13 +01:00
Matthias Koefferlein 8760ac4bcb Updating Changelog. 2025-03-23 18:09:40 +01:00
Matthias Koefferlein 740f36a2c2 Macro editor won't jump back after leaving find mode 2025-03-23 18:03:09 +01:00
Matthias Koefferlein a3593efa18 Layer settings are now better preserved between calls of boolean, merge and size dialogs 2025-03-23 17:36:07 +01:00
Matthias Koefferlein efeb2c061b Layout queries support diff as placeholder for the current cell 2025-03-23 16:51:47 +01:00
Matthias Koefferlein a22f48d87a Texts#polygons now has an argument to specify a property key that receives the text string 2025-03-23 15:54:18 +01:00
Matthias Koefferlein f977973b85 RecursiveShapeIterator#property and RecursiveShapeIterator#properties 2025-03-23 15:23:19 +01:00
Matthias Koefferlein cf5e62a4ed Added RBA/pya tests for triangulation 2025-03-23 11:41:26 +01:00
Matthias Koefferlein 71644fa56c Implementing additional vertexes for triangulation 2025-03-23 11:34:13 +01:00
Matthias Koefferlein 8727c31d36 Merge branch 'bugfix/issue-1996' into wip 2025-03-23 11:11:20 +01:00
Matthias Koefferlein 16be7bf8c1 Merge branch 'bugfix/issue-2002' into wip 2025-03-23 11:10:54 +01:00
Matthias Koefferlein 593678e228 WIP: Polygon#delaunay and variants 2025-03-23 09:55:08 +01:00
Matthias Koefferlein 986474d465 Added one more testcase 2025-03-23 08:57:06 +01:00
Matthias Koefferlein 73364ee406 Solving issue #2002 by allowing variable widths on the path segments due to 45 degree segment snapping. 2025-03-23 00:57:25 +01:00
Martin Köhler b8a6d0a1cd Timer::memory_size() implementation for macOS 2025-03-23 00:03:29 +01:00
Matthias Koefferlein 21bfe7a632 New golden testdata variants 2025-03-22 22:36:15 +01:00
Matthias Koefferlein 85bb9be5c0 Fixed Python tests for Python 2 backward compatibility 2025-03-22 21:55:44 +01:00
Matthias Koefferlein 6228668fa1 Fixing issue #1996: Providing a more robust triangulation 2025-03-22 21:47:22 +01:00
Matthias Koefferlein a727ed0b1d Fixed a compiler warning 2025-03-22 20:47:28 +01:00
Matthias Koefferlein 2191febc38 Updating test data for better robustness 2025-03-22 20:41:24 +01:00
Matthias Koefferlein 2668b42d29 Separating tl::hfunc from std namespace. Needed to support int128 in tl::Variant for 64bit coordinate builds. 2025-03-22 20:35:15 +01:00
Matthias Koefferlein 561a760881 Updating doc, python stubs, fixed non-Qt builds 2025-03-22 17:51:07 +01:00
Matthias Koefferlein e6914c78b9 Fixing builds for Qt4 2025-03-22 17:29:16 +01:00
Matthias Koefferlein ee60ae8146 Changelog for 0.30 2025-03-22 14:49:16 +01:00
Matthias Koefferlein 2aa88fe29c Fixed a linker issue on Qt4 2025-03-22 14:09:00 +01:00
Matthias Koefferlein 283a9d0b22 Build fixes for Qt4 2025-03-22 00:02:13 +01:00
Matthias Koefferlein 69a8f3ac11 Preparations for 0.30. 2025-03-21 23:52:08 +01:00
Matthias Koefferlein 12b3cd011b Merge branch 'master' into devel 2025-03-21 19:24:33 +01:00
Matthias Koefferlein 6f69efd427 WIP 2025-03-20 23:30:30 +01:00
Matthias Koefferlein d9343ee530 WIP 2025-03-18 17:28:12 +01:00
Matthias Koefferlein 1f5c2b5132 Marked one test as long runner 2025-03-18 14:44:27 +01:00
Matthias Koefferlein df631aa970 Some minor refactoring 2025-03-18 00:19:15 +01:00
Matthias Koefferlein 6596008826 instrumenting triangles implementation with Quad Tree, but without effect 2025-03-18 00:10:00 +01:00
Matthias Koefferlein cd62f62140 WIP (quad tree) 2025-03-16 23:24:06 +01:00
Matthias Koefferlein 4e65b96cb7 WIP (quad tree) 2025-03-16 21:10:15 +01:00
Matthias Koefferlein f3037d11f3 WIP (quad tree) 2025-03-16 18:16:15 +01:00
Matthias Koefferlein 4aeb94d42e WIP (quad tree) 2025-03-16 17:13:06 +01:00
Matthias Koefferlein 477e2b5a31 WIP (quad tree) 2025-03-16 16:43:56 +01:00
Matthias Koefferlein 4369835e8b WIP (quad tree) 2025-03-16 16:36:49 +01:00
Matthias Koefferlein f1f35ae2a4 WIP (quad tree) 2025-03-16 14:46:03 +01:00
Matthias Koefferlein 5c8e0539ee WIP (quad tree) 2025-03-16 13:56:16 +01:00
Matthias Koefferlein 54b5d9f5d6 WIP (quad tree) 2025-03-16 13:35:51 +01:00
Matthias Koefferlein f136fdcde6 WIP 2025-03-16 00:52:57 +01:00
Matthias Köfferlein 1feffe7beb
Merge pull request #2001 from Kazzz-S/0.29.12-mac1
Use Ruby 3.3.7 from MacPorts
2025-03-14 14:06:59 +01:00
Matthias Koefferlein 82d2adeb05 Maybe fixing Github action 2025-03-14 01:29:49 +01:00
Matthias Koefferlein ed809952c3 Pinning setuptools to <76.0.0 for now as this version is broken for MSVC 2025-03-14 01:12:13 +01:00
Matthias Koefferlein 60f7c70f11 Trying to fix github workflow 2025-03-13 23:35:58 +01:00
Kazunari Sekigawa c0f2bdd3d6 Use Ruby 3.3.7 from MacPorts 2025-03-07 06:51:21 +09:00
265 changed files with 12621 additions and 1569 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -192,7 +192,7 @@ RubySequoia = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions
# install with 'sudo port install ruby33'
# [Key Type Name] = 'MP33'
Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3',
'inc': '/opt/local/include/ruby-3.3.6',
'inc': '/opt/local/include/ruby-3.3.8',
'lib': '/opt/local/lib/libruby.3.3.dylib'
}

View File

@ -424,7 +424,7 @@ def CheckPkgDirectory():
#------------------------------------------------------
# [5] Check the occupied disk space
#------------------------------------------------------
command = "\du -sm %s" % DefaultBundleName
command = r"\du -sm %s" % DefaultBundleName
sizeApp = int( os.popen(command).read().strip("\n").split("\t")[0] )
#------------------------------------------------------
@ -671,14 +671,14 @@ def MakeTargetDMGFile(msg=""):
imageDest = "%s/.background" % MountDir
if not os.path.isdir(imageDest):
os.mkdir(imageDest)
command = "\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG)
command = r"\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG)
os.system(command)
#--------------------------------------------------------
# (6) Create a symbolic link to /Applications
#--------------------------------------------------------
print( ">>> (6) Creating a symbolic link to /Applications..." )
command = "\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications)
command = r"\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications)
os.system(command)
#--------------------------------------------------------
@ -702,7 +702,7 @@ def MakeTargetDMGFile(msg=""):
print( ">>> (8) Copying the volume icon..." )
iconsSrc = "macbuild/Resources/%s" % VolumeIcons
iconsDest = "%s/.VolumeIcon.icns" % MountDir
command1 = "\cp -p %s %s" % (iconsSrc, iconsDest)
command1 = r"\cp -p %s %s" % (iconsSrc, iconsDest)
command2 = "SetFile -c icnC %s" % iconsDest
os.system(command1)
sleep(2)
@ -713,7 +713,7 @@ def MakeTargetDMGFile(msg=""):
# (9) Change the permission
#--------------------------------------------------------
print( ">>> (9) Changing permission to 755..." )
command = "\chmod -Rf 755 %s &> /dev/null" % MountDir
command = r"\chmod -Rf 755 %s &> /dev/null" % MountDir
os.system(command)
#--------------------------------------------------------

View File

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

View File

@ -547,7 +547,7 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m
drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?)
drop_method "QNoDebug", /QNoDebug::operator<</ # nothing usable (TODO: how to map?)
# No longer supported operator== and operator!= in Qt 6.7/6.8
# No longer supported operator== and operator!= in Qt 6.7/6.8/6.9
add_native_operator_neq(self, "QEasingCurve")
add_native_operator_neq(self, "QTimeZone")
add_native_operator_neq(self, "QDir")
@ -569,6 +569,7 @@ add_native_operator_neq(self, "QProcessEnvironment")
add_native_operator_neq(self, "QRegularExpression")
add_native_operator_neqlt(self, "QUrl")
add_native_operator_neq(self, "QUrlQuery")
add_native_operator_neq(self, "QDomNodeList")
add_native_operator_neq(self, "QXmlStreamAttribute")
add_native_operator_neq(self, "QXmlStreamEntityDeclaration")
add_native_operator_neq(self, "QXmlStreamNamespaceDeclaration")

View File

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

View File

@ -27,6 +27,7 @@
#include "dbReader.h"
#include "dbWriter.h"
#include "tlCommandLineParser.h"
#include "tlTimer.h"
namespace bd
{
@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format)
db::Layout layout;
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
{
db::LoadLayoutOptions load_options;
generic_reader_options.configure (load_options);

View File

@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions ()
m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool ();
m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool ();
m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string ();
m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
// Don't take the default, as in practice, it's more common to substitute LEF macros by layouts
// m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always"
}
void

View File

@ -29,6 +29,7 @@
#include "dbSaveLayoutOptions.h"
#include "tlLog.h"
#include "tlCommandLineParser.h"
#include "tlTimer.h"
struct ClipData
@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
cmd.parse (argc, argv);
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
clip (data);
return 0;

View File

@ -25,6 +25,7 @@
#include "dbLayoutDiff.h"
#include "dbReader.h"
#include "tlCommandLineParser.h"
#include "tlTimer.h"
BD_PUBLIC int strmcmp (int argc, char *argv[])
{
@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given");
}
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
db::Layout layout_a;
db::Layout layout_b;

View File

@ -28,6 +28,7 @@
#include "tlLog.h"
#include "tlCommandLineParser.h"
#include "tlFileUtils.h"
#include "tlTimer.h"
#include "rba.h"
#include "pya.h"
#include "gsi.h"
@ -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 ();
}

View File

@ -32,6 +32,8 @@
#include "gsiExpression.h"
#include "tlCommandLineParser.h"
#include "tlThreads.h"
#include "tlThreadedWorkers.h"
#include "tlTimer.h"
namespace {
@ -319,7 +321,8 @@ struct XORData
dont_summarize_missing_layers (false), silent (false), no_summary (false),
threads (0),
tile_size (0.0), heal_results (false),
output_layout (0), output_cell (0)
output_layout (0), output_cell (0),
layers_missing (0)
{ }
db::Layout *layout_a, *layout_b;
@ -336,6 +339,8 @@ struct XORData
db::cell_index_type output_cell;
std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> l2l_map;
std::map<std::pair<int, db::LayerProperties>, ResultDescriptor> *results;
mutable int layers_missing;
mutable tl::Mutex lock;
};
}
@ -455,6 +460,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
}
}
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
db::Layout layout_a;
db::Layout layout_b;
@ -572,14 +579,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
if (! silent && ! no_summary) {
if (result) {
tl::info << "No differences found";
tl::info << tl::to_string (tr ("No differences found"));
} else {
const char *line_format = " %-10s %-12s %s";
const char *sep = " -------------------------------------------------------";
tl::info << "Result summary (layers without differences are not shown):" << tl::endl;
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
std::string headline;
if (deep) {
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)")));
} else {
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)")));
}
const char *sep = " ----------------------------------------------------------------";
tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl;
tl::info << headline << tl::endl << sep;
int ti = -1;
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) {
@ -587,17 +602,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
if (r->first.first != ti) {
ti = r->first.first;
if (tolerances[ti] > db::epsilon) {
tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
tl::info << headline << tl::endl << sep;
}
}
std::string out ("-");
std::string value;
if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) {
value = "(no such layer in first layout)";
value = tl::to_string (tr ("(no such layer in first layout)"));
} else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) {
value = "(no such layer in second layout)";
value = tl::to_string (tr ("(no such layer in second layout)"));
} else if (! r->second.is_empty ()) {
if (r->second.layer_output >= 0 && r->second.layout) {
out = r->second.layout->get_properties (r->second.layer_output).to_string ();
@ -758,15 +773,174 @@ bool run_tiled_xor (const XORData &xor_data)
return result;
}
bool run_deep_xor (const XORData &xor_data)
{
db::DeepShapeStore dss;
dss.set_threads (xor_data.threads);
class XORJob
: public tl::JobBase
{
public:
XORJob (int nworkers)
: tl::JobBase (nworkers)
{
}
virtual tl::Worker *create_worker ();
};
class XORWorker
: public tl::Worker
{
public:
XORWorker (XORJob *job);
void perform_task (tl::Task *task);
db::DeepShapeStore &dss ()
{
return m_dss;
}
private:
XORJob *mp_job;
db::DeepShapeStore m_dss;
};
class XORTask
: public tl::Task
{
public:
XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu)
: mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu)
{
// .. nothing yet ..
}
void run (XORWorker *worker) const
{
if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) {
if (m_la < 0) {
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second";
} else {
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first";
}
tl::MutexLocker locker (&mp_xor_data->lock);
mp_xor_data->layers_missing += 1;
int tol_index = 0;
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
result.layer_a = m_la;
result.layer_b = m_lb;
result.layout = mp_xor_data->output_layout;
result.top_cell = mp_xor_data->output_cell;
++tol_index;
}
} else {
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ());
db::RecursiveShapeIterator ri_a, ri_b;
if (m_la >= 0) {
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
} else {
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector<unsigned int> ());
}
ri_a.set_for_merged_input (true);
if (m_lb >= 0) {
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
} else {
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector<unsigned int> ());
}
ri_b.set_for_merged_input (true);
db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
db::Region xor_res;
{
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ());
xor_res = in_a ^ in_b;
}
int tol_index = 0;
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
db::LayerProperties lp = m_layer_props;
if (lp.layer >= 0) {
lp.layer += tol_index * mp_xor_data->tolerance_bump;
}
if (*t > db::epsilon) {
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ());
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
}
{
tl::MutexLocker locker (&mp_xor_data->lock);
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
result.layer_a = m_la;
result.layer_b = m_lb;
result.layout = mp_xor_data->output_layout;
result.top_cell = mp_xor_data->output_cell;
if (mp_xor_data->output_layout) {
result.layer_output = result.layout->insert_layer (lp);
xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output);
} else {
result.shape_count = xor_res.hier_count ();
}
}
++tol_index;
}
}
}
private:
const XORData *mp_xor_data;
const db::LayerProperties &m_layer_props;
int m_la;
int m_lb;
double m_dbu;
};
XORWorker::XORWorker (XORJob *job)
: tl::Worker (), mp_job (job)
{
// TODO: this conflicts with the "set_for_merged_input" optimization below.
// It seems not to be very effective then. Why?
dss.set_wants_all_cells (true); // saves time for less cell mapping operations
m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations
}
void
XORWorker::perform_task (tl::Task *task)
{
XORTask *xor_task = dynamic_cast <XORTask *> (task);
if (xor_task) {
xor_task->run (this);
}
}
tl::Worker *
XORJob::create_worker ()
{
return new XORWorker (this);
}
bool run_deep_xor (const XORData &xor_data)
{
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
if (tl::verbosity () >= 20) {
@ -779,98 +953,18 @@ bool run_deep_xor (const XORData &xor_data)
xor_data.output_layout->dbu (dbu);
}
bool result = true;
int index = 1;
XORJob job (xor_data.threads);
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) {
if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) {
if (ll->second.first < 0) {
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second";
} else {
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first";
}
result = false;
int tol_index = 0;
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
result.layer_a = ll->second.first;
result.layer_b = ll->second.second;
result.layout = xor_data.output_layout;
result.top_cell = xor_data.output_cell;
++tol_index;
}
} else {
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
db::RecursiveShapeIterator ri_a, ri_b;
if (ll->second.first >= 0) {
ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first);
ri_a.set_for_merged_input (true);
}
if (ll->second.second >= 0) {
ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second);
ri_b.set_for_merged_input (true);
}
db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu));
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
db::Region xor_res;
{
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
xor_res = in_a ^ in_b;
}
int tol_index = 0;
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
db::LayerProperties lp = ll->first;
if (lp.layer >= 0) {
lp.layer += tol_index * xor_data.tolerance_bump;
}
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
result.layer_a = ll->second.first;
result.layer_b = ll->second.second;
result.layout = xor_data.output_layout;
result.top_cell = xor_data.output_cell;
if (*t > db::epsilon) {
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
}
if (xor_data.output_layout) {
result.layer_output = result.layout->insert_layer (lp);
xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output);
} else {
result.shape_count = xor_res.count ();
}
++tol_index;
}
}
++index;
job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu));
}
// Determines the output status
job.start ();
job.wait ();
// Determine the output status
bool result = (xor_data.layers_missing == 0);
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) {
result = r->second.is_empty ();
}

View File

@ -105,7 +105,7 @@ TEST(1A_Flat)
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n"
" ----------------------------------------------------------------\n"
" 3/0 3/0 30\n"
" 6/0 6/0 41\n"
" 8/1 8/1 1\n"
@ -146,8 +146,8 @@ TEST(1A_Deep)
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n"
" Layer Output Differences (hierarchical shape count)\n"
" ----------------------------------------------------------------\n"
" 3/0 3/0 3\n"
" 6/0 6/0 314\n"
" 8/1 8/1 1\n"
@ -177,7 +177,7 @@ TEST(1B_Flat)
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n"
" ----------------------------------------------------------------\n"
" 3/0 - 30\n"
" 6/0 - 41\n"
" 8/1 - 1\n"
@ -206,9 +206,9 @@ TEST(1B_Deep)
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n"
" 3/0 - 30\n"
" Layer Output Differences (hierarchical shape count)\n"
" ----------------------------------------------------------------\n"
" 3/0 - 3\n"
" 6/0 - 314\n"
" 8/1 - 1\n"
" 10/0 - (no such layer in first layout)\n"
@ -417,7 +417,7 @@ TEST(3_FlatCount)
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n"
" ----------------------------------------------------------------\n"
" 3/0 - 31\n"
" 6/0 - 217\n"
" 8/1 - 168\n"
@ -483,7 +483,7 @@ TEST(3_FlatCountHeal)
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n"
" ----------------------------------------------------------------\n"
" 3/0 - 30\n"
" 6/0 - 41\n"
" 8/1 - 1\n"
@ -756,3 +756,42 @@ TEST(6_Deep)
"Layer 10/0 is not present in first layout, but in second\n"
);
}
TEST(7_OptimizeDeep)
{
tl::CaptureChannel cap;
std::string input_a = tl::testdata ();
input_a += "/bd/strmxor_covered1.gds";
std::string input_b = tl::testdata ();
input_b += "/bd/strmxor_covered2.gds";
std::string au = tl::testdata ();
au += "/bd/strmxor_au7d.oas";
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "-u", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (hierarchical shape count)\n"
" ----------------------------------------------------------------\n"
" 2/0 2/0 1\n"
" 3/0 3/0 8\n"
"\n"
);
}

View File

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

View File

@ -1571,6 +1571,17 @@ struct array_iterator
}
}
/**
* @brief Gets a value indicating whether the iterator is a synthetic one
*
* "is_singular" is true, if the iterator was default-created or with a single
* transformation.
*/
bool is_singular () const
{
return mp_base == 0;
}
private:
trans_type m_trans;
basic_array_iterator <Coord> *mp_base;

View File

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

View File

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

View File

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

View File

@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
std::vector<db::cell_index_type> &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int);
std::vector<db::cell_index_type> new_cells_b;
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > all_a2b;
for (std::vector<db::cell_index_type>::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) {
auto m = m_b2a_mapping.find (*b);
tl_assert (m != m_b2a_mapping.end ());
all_a2b.push_back (std::make_pair (m->second, *b));
}
std::set<db::cell_index_type> called_b;
for (std::vector<db::cell_index_type>::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) {
layout_b.cell (*i).collect_called_cells (called_b);
@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b);
new_cells.push_back (new_cell);
new_cells_b.push_back (*b);
all_a2b.push_back (std::make_pair (new_cell, *b));
if (mapped_pairs) {
mapped_pairs->push_back (std::make_pair (*b, new_cell));
@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
}
}
if (! new_cells.empty ()) {
if (all_a2b.empty ()) {
return;
}
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
db::LayoutLocker locker (&layout_a);
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
db::LayoutLocker locker (&layout_a);
// Create instances for the new cells in layout A according to their instantiation in layout B
double mag = layout_b.dbu () / layout_a.dbu ();
for (size_t i = 0; i < new_cells.size (); ++i) {
// Create instances for the new cells in layout A according to their instantiation in layout B
double mag = layout_b.dbu () / layout_a.dbu ();
for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) {
const db::Cell &b = layout_b.cell (new_cells_b [i]);
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
const db::Cell &b = layout_b.cell (i->second);
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
db::Instance bi = pb->child_inst ();
db::Instance bi = pb->child_inst ();
db::CellInstArray bci = bi.cell_inst ();
bci.object ().cell_index (new_cells [i]);
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
if (bi.has_prop_id ()) {
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
} else {
pa.insert (bci);
}
db::CellInstArray bci = bi.cell_inst ();
bci.object ().cell_index (i->first);
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
if (bi.has_prop_id ()) {
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
} else {
pa.insert (bci);
}
}

View File

@ -311,6 +311,11 @@ const db::EdgePair *DeepEdgePairs::nth (size_t) const
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
}
db::properties_id_type DeepEdgePairs::nth_prop_id (size_t) const
{
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
}
bool DeepEdgePairs::has_valid_edge_pairs () const
{
return false;

View File

@ -70,6 +70,7 @@ public:
virtual Box bbox () const;
virtual bool empty () const;
virtual const db::EdgePair *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_edge_pairs () const;
virtual const db::RecursiveShapeIterator *iter () const;
virtual void apply_property_translator (const db::PropertiesTranslator &pt);

View File

@ -438,6 +438,12 @@ DeepEdges::nth (size_t /*n*/) const
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat edge collections")));
}
db::properties_id_type
DeepEdges::nth_prop_id (size_t) const
{
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
}
bool
DeepEdges::has_valid_edges () const
{

View File

@ -76,6 +76,7 @@ public:
virtual bool is_merged () const;
virtual const db::Edge *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_edges () const;
virtual bool has_valid_merged_edges () const;

View File

@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
// Nothing to do
return other.delegate ()->clone ();
} else if (other.empty ()) {
if (other.empty ()) {
// Nothing to do
return clone ();
@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
return AsIfFlatRegion::xor_with (other, property_constraint);
} else if (empty ()) {
// Nothing to do, but to maintain the normal behavior, we have to map the other
// input to our layout if neccessary
if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) {
return other.delegate ()->clone ();
} else {
std::unique_ptr<DeepRegion> other_deep_mapped (dynamic_cast<DeepRegion *> (clone ()));
other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ());
return other_deep_mapped.release ();
}
} else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) {
return new DeepRegion (deep_layer ().derived ());

View File

@ -332,6 +332,12 @@ const db::Text *DeepTexts::nth (size_t) const
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat text collections")));
}
db::properties_id_type
DeepTexts::nth_prop_id (size_t) const
{
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
}
bool DeepTexts::has_valid_texts () const
{
return false;
@ -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));
}
}
}

View File

@ -71,6 +71,7 @@ public:
virtual Box bbox () const;
virtual bool empty () const;
virtual const db::Text *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_texts () const;
virtual const db::RecursiveShapeIterator *iter () const;
virtual void apply_property_translator (const db::PropertiesTranslator &pt);
@ -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;

View File

@ -678,7 +678,7 @@ public:
/**
* @brief Returns the nth edge pair
*
* This operation is available only for flat regions - i.e. such for which
* This operation is available only for flat edge pair collections - i.e. such for which
* "has_valid_edge_pairs" is true.
*/
const db::EdgePair *nth (size_t n) const
@ -686,6 +686,17 @@ public:
return mp_delegate->nth (n);
}
/**
* @brief Returns the nth edge pair's property ID
*
* This operation is available only for flat edge pair collections - i.e. such for which
* "has_valid_edge_pairs" is true.
*/
db::properties_id_type nth_prop_id (size_t n) const
{
return mp_delegate->nth_prop_id (n);
}
/**
* @brief Forces flattening of the edge pair collection
*

View File

@ -273,6 +273,7 @@ public:
virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0;
virtual const db::EdgePair *nth (size_t n) const = 0;
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
virtual bool has_valid_edge_pairs () const = 0;
virtual const db::RecursiveShapeIterator *iter () const = 0;

View File

@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare
{
if (edge_xmax (a) < edge_xmin (b)) {
return true;
} else if (edge_xmin (a) >= edge_xmax (b)) {
} else if (edge_xmin (a) > edge_xmax (b)) {
return false;
} else {
C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2);

View File

@ -1440,13 +1440,24 @@ public:
/**
* @brief Returns the nth edge
*
* This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true.
* This operation is available only for flat edge collections - i.e. such for which "has_valid_edges" is true.
*/
const db::Edge *nth (size_t n) const
{
return mp_delegate->nth (n);
}
/**
* @brief Returns the nth edge's property ID
*
* This operation is available only for flat edge collections - i.e. such for which
* "has_valid_edges" is true.
*/
db::properties_id_type nth_prop_id (size_t n) const
{
return mp_delegate->nth_prop_id (n);
}
/**
* @brief Forces flattening of the edge collection
*

View File

@ -280,6 +280,7 @@ public:
virtual std::pair<EdgesDelegate *, EdgesDelegate *> in_and_out (const Edges &) const = 0;
virtual const db::Edge *nth (size_t n) const = 0;
virtual db::properties_id_type nth_prop_id (size_t n) const = 0;
virtual bool has_valid_edges () const = 0;
virtual bool has_valid_merged_edges () const = 0;

View File

@ -89,6 +89,7 @@ public:
virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); }
virtual const db::EdgePair *nth (size_t) const { tl_assert (false); }
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
virtual bool has_valid_edge_pairs () const { return true; }
virtual const db::RecursiveShapeIterator *iter () const { return 0; }

View File

@ -119,6 +119,7 @@ public:
virtual std::pair<EdgesDelegate *, EdgesDelegate *> in_and_out (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual const db::Edge *nth (size_t) const { tl_assert (false); }
virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); }
virtual bool has_valid_edges () const { return true; }
virtual bool has_valid_merged_edges () const { return true; }

View File

@ -49,7 +49,7 @@ EmptyTexts::clone () const
}
RegionDelegate *
EmptyTexts::polygons (db::Coord) const
EmptyTexts::polygons (db::Coord, const tl::Variant &) const
{
return new EmptyRegion ();
}

View File

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

View File

@ -173,7 +173,46 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
const db::EdgePair *FlatEdgePairs::nth (size_t n) const
{
return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
// NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties
if (n >= mp_edge_pairs->size ()) {
return 0;
}
const db::layer<db::EdgePair, db::unstable_layer_tag> &l = mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ();
if (n < l.size ()) {
return &l.begin () [n];
}
n -= l.size ();
const db::layer<db::EdgePairWithProperties, db::unstable_layer_tag> &lp = mp_edge_pairs->get_layer<db::EdgePairWithProperties, db::unstable_layer_tag> ();
if (n < lp.size ()) {
return &lp.begin () [n];
}
return 0;
}
db::properties_id_type FlatEdgePairs::nth_prop_id (size_t n) const
{
// NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties
if (n >= mp_edge_pairs->size ()) {
return 0;
}
const db::layer<db::EdgePair, db::unstable_layer_tag> &l = mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ();
if (n < l.size ()) {
return 0;
}
n -= l.size ();
const db::layer<db::EdgePairWithProperties, db::unstable_layer_tag> &lp = mp_edge_pairs->get_layer<db::EdgePairWithProperties, db::unstable_layer_tag> ();
if (n < lp.size ()) {
return lp.begin () [n].properties_id ();
}
return 0;
}
bool FlatEdgePairs::has_valid_edge_pairs () const

View File

@ -75,6 +75,7 @@ public:
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
virtual const db::EdgePair *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_edge_pairs () const;
virtual const db::RecursiveShapeIterator *iter () const;

View File

@ -361,7 +361,46 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other)
const db::Edge *FlatEdges::nth (size_t n) const
{
return n < mp_edges->size () ? &mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ().begin () [n] : 0;
// NOTE: this assumes that we iterate over non-property edges first and then over edges with properties
if (n >= mp_edges->size ()) {
return 0;
}
const db::layer<db::Edge, db::unstable_layer_tag> &l = mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ();
if (n < l.size ()) {
return &l.begin () [n];
}
n -= l.size ();
const db::layer<db::EdgeWithProperties, db::unstable_layer_tag> &lp = mp_edges->get_layer<db::EdgeWithProperties, db::unstable_layer_tag> ();
if (n < lp.size ()) {
return &lp.begin () [n];
}
return 0;
}
db::properties_id_type FlatEdges::nth_prop_id (size_t n) const
{
// NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties
if (n >= mp_edges->size ()) {
return 0;
}
const db::layer<db::Edge, db::unstable_layer_tag> &l = mp_edges->get_layer<db::Edge, db::unstable_layer_tag> ();
if (n < l.size ()) {
return 0;
}
n -= l.size ();
const db::layer<db::EdgeWithProperties, db::unstable_layer_tag> &lp = mp_edges->get_layer<db::EdgeWithProperties, db::unstable_layer_tag> ();
if (n < lp.size ()) {
return lp.begin () [n].properties_id ();
}
return 0;
}
bool FlatEdges::has_valid_edges () const

View File

@ -89,6 +89,7 @@ public:
virtual EdgesDelegate *add (const Edges &other) const;
virtual const db::Edge *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_edges () const;
virtual bool has_valid_merged_edges () const;

View File

@ -171,7 +171,46 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other)
const db::Text *FlatTexts::nth (size_t n) const
{
return n < mp_texts->size () ? &mp_texts->get_layer<db::Text, db::unstable_layer_tag> ().begin () [n] : 0;
// NOTE: this assumes that we iterate over non-property texts first and then over texts with properties
if (n >= mp_texts->size ()) {
return 0;
}
const db::layer<db::Text, db::unstable_layer_tag> &l = mp_texts->get_layer<db::Text, db::unstable_layer_tag> ();
if (n < l.size ()) {
return &l.begin () [n];
}
n -= l.size ();
const db::layer<db::TextWithProperties, db::unstable_layer_tag> &lp = mp_texts->get_layer<db::TextWithProperties, db::unstable_layer_tag> ();
if (n < lp.size ()) {
return &lp.begin () [n];
}
return 0;
}
db::properties_id_type FlatTexts::nth_prop_id (size_t n) const
{
// NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties
if (n >= mp_texts->size ()) {
return 0;
}
const db::layer<db::Text, db::unstable_layer_tag> &l = mp_texts->get_layer<db::Text, db::unstable_layer_tag> ();
if (n < l.size ()) {
return 0;
}
n -= l.size ();
const db::layer<db::TextWithProperties, db::unstable_layer_tag> &lp = mp_texts->get_layer<db::TextWithProperties, db::unstable_layer_tag> ();
if (n < lp.size ()) {
return lp.begin () [n].properties_id ();
}
return 0;
}
bool FlatTexts::has_valid_texts () const

View File

@ -76,6 +76,7 @@ public:
virtual TextsDelegate *add (const Texts &other) const;
virtual const db::Text *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_texts () const;
virtual const db::RecursiveShapeIterator *iter () const;

View File

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

View File

@ -152,15 +152,17 @@ static std::pair<bool, std::set<db::Box> > compute_clip_variant (const db::Box &
}
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
: mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
{
set_shape_receiver (pipe);
reset ();
}
HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
: mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
{
set_shape_receiver (pipe);
reset ();
}
HierarchyBuilder::~HierarchyBuilder ()
@ -178,6 +180,8 @@ void
HierarchyBuilder::reset ()
{
m_initial_pass = true;
m_cm_new_entry = false;
mp_initial_cell = 0;
m_cells_to_be_filled.clear ();
@ -186,7 +190,6 @@ HierarchyBuilder::reset ()
m_cells_seen.clear ();
m_cell_stack.clear ();
m_cm_entry = null_iterator;
m_cm_new_entry = false;
}
const std::pair<db::cell_index_type, std::string> &
@ -351,7 +354,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
if (! key.clip_region.empty ()) {
cn += "$CLIP_VAR";
description += "CLIP";
}
if (key.inactive) {
cn += "$DIS";
@ -383,7 +385,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
}
HierarchyBuilder::new_inst_mode
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes)
{
if (all) {
@ -402,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
}
// To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array.
return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
} else {
@ -413,7 +415,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
}
bool
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all)
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{
if (all) {
@ -441,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
}
}
return (m_cells_seen.find (key) == m_cells_seen.end ());
return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ();
}
}

View File

@ -294,8 +294,8 @@ public:
virtual void end (const RecursiveShapeIterator *iter);
virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box &region, const box_tree_type *complex_region);
virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell);
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all);
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all);
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes);
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes);
virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region);
/**

View File

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

View File

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

View File

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

View File

@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db
void PolygonRefToShapesGenerator::put (const db::Polygon &polygon)
{
tl::MutexLocker locker (&mp_layout->lock ());
if (m_prop_id != 0) {
if (polygon.is_empty ()) {
// ignore empty polygons
} else if (m_prop_id != 0) {
mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id));
} else {
mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size
void
PolygonSplitter::put (const db::Polygon &poly)
{
if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
if (poly.is_empty ()) {
// ignore empty polygons
} else if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
std::vector <db::Polygon> split_polygons;
db::split_polygon (poly, split_polygons);

View File

@ -190,6 +190,12 @@ OriginalLayerEdgePairs::nth (size_t) const
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections")));
}
db::properties_id_type
OriginalLayerEdgePairs::nth_prop_id (size_t) const
{
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections")));
}
bool
OriginalLayerEdgePairs::has_valid_edge_pairs () const
{

View File

@ -53,6 +53,7 @@ public:
virtual bool empty () const;
virtual const db::EdgePair *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_edge_pairs () const;
virtual const db::RecursiveShapeIterator *iter () const;

View File

@ -233,6 +233,12 @@ OriginalLayerEdges::nth (size_t) const
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
}
db::properties_id_type
OriginalLayerEdges::nth_prop_id (size_t) const
{
throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections")));
}
bool
OriginalLayerEdges::has_valid_edges () const
{

View File

@ -58,6 +58,7 @@ public:
virtual bool is_merged () const;
virtual const db::Edge *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_edges () const;
virtual bool has_valid_merged_edges () const;

View File

@ -190,6 +190,12 @@ OriginalLayerTexts::nth (size_t) const
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
}
db::properties_id_type
OriginalLayerTexts::nth_prop_id (size_t) const
{
throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections")));
}
bool
OriginalLayerTexts::has_valid_texts () const
{

View File

@ -53,6 +53,7 @@ public:
virtual bool empty () const;
virtual const db::Text *nth (size_t n) const;
virtual db::properties_id_type nth_prop_id (size_t n) const;
virtual bool has_valid_texts () const;
virtual const db::RecursiveShapeIterator *iter () const;

View File

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

View File

@ -1771,6 +1771,14 @@ public:
return true;
}
/**
* @brief Returns a value indicating that the polygon is an empty one
*/
bool is_empty () const
{
return m_ctrs.size () == size_t (1) && m_ctrs[0].size () == 0;
}
/**
* @brief Returns the number of points in the polygon
*/
@ -1879,6 +1887,7 @@ public:
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
h->transform (db::unit_trans<C> (), true /*compress*/, remove_reflected);
}
m_bbox = m_ctrs [0].bbox ();
return *this;
}
@ -2804,6 +2813,7 @@ public:
{
// compress the polygon by employing the transform method
m_hull.transform (db::unit_trans<C> (), true, remove_reflected);
m_bbox = m_hull.bbox ();
return *this;
}
@ -3022,6 +3032,14 @@ public:
return m_hull.is_halfmanhattan ();
}
/**
* @brief Returns a value indicating that the polygon is an empty one
*/
bool is_empty () const
{
return m_hull.size () == 0;
}
/**
* @brief The number of holes
*

View File

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

24
src/db/db/dbQuadTree.cc Normal file
View File

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

825
src/db/db/dbQuadTree.h Normal file
View File

@ -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 &center)
: 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 &center () 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

View File

@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
m_layer = d.m_layer;
mp_cell = d.mp_cell;
m_current_layer = d.m_current_layer;
m_skip_shapes = d.m_skip_shapes;
m_skip_shapes_member = d.m_skip_shapes_member;
m_shape = d.m_shape;
m_trans = d.m_trans;
m_global_trans = d.m_global_trans;
@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
m_local_complex_region_stack = d.m_local_complex_region_stack;
m_local_region_stack = d.m_local_region_stack;
m_skip_shapes_stack = d.m_skip_shapes_stack;
m_skip_shapes_member_stack = d.m_skip_shapes_member_stack;
m_needs_reinit = d.m_needs_reinit;
m_inst_quad_id = d.m_inst_quad_id;
m_inst_quad_id_stack = d.m_inst_quad_id_stack;
@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
m_local_region_stack.push_back (m_global_trans.inverted () * m_region);
m_skip_shapes_stack.clear ();
m_skip_shapes_stack.push_back (false);
m_skip_shapes_member_stack.clear ();
m_skip_shapes_member_stack.push_back (false);
m_local_complex_region_stack.clear ();
if (mp_complex_region.get ()) {
@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
bool
RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
{
bool skip_shapes = false;
if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) {
// Try some optimization: if the instance we're looking at is entirely covered
// by a rectangle (other objects are too expensive to check), then we skip it
//
// We check 10 shapes max.
box_type inst_bx;
if (m_inst->size () == 1) {
inst_bx = m_inst->bbox (m_box_convert);
} else {
inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ());
}
unsigned int l = m_has_layers ? m_layers.front () : m_layer;
auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
size_t nmax = 10;
while (! si.at_end () && nmax-- > 0) {
if (inst_bx.inside (si->rectangle ())) {
skip_shapes = true;
break;
}
++si;
}
}
if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) {
return false;
}
tl_assert (mp_layout);
m_trans_stack.push_back (m_trans);
@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
}
m_local_region_stack.push_back (new_region);
m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes);
m_skip_shapes_stack.push_back (m_skip_shapes);
m_skip_shapes_member_stack.push_back (m_skip_shapes_member);
if (! m_local_complex_region_stack.empty ()) {
@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const
m_inst = m_inst_iterators.back ();
m_inst_array = m_inst_array_iterators.back ();
m_inst_quad_id = m_inst_quad_id_stack.back ();
m_skip_shapes = m_skip_shapes_stack.back ();
m_skip_shapes_member = m_skip_shapes_member_stack.back ();
m_inst_iterators.pop_back ();
m_inst_array_iterators.pop_back ();
m_inst_quad_id_stack.pop_back ();
@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const
m_cells.pop_back ();
m_local_region_stack.pop_back ();
m_skip_shapes_stack.pop_back ();
m_skip_shapes_member_stack.pop_back ();
if (! m_local_complex_region_stack.empty ()) {
m_local_complex_region_stack.pop_back ();
}
@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const
void
RecursiveShapeIterator::new_layer () const
{
if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
m_shape = shape_iterator ();
} else if (! m_overlapping) {
m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
new_inst (receiver);
}
bool
RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const
{
// Try some optimization: if the instance we're looking at is entirely covered
// by a rectangle (other objects are too expensive to check), then we skip it
//
// We check 10 shapes max.
auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
size_t nmax = 10;
while (! si.at_end () && nmax-- > 0) {
if (inst_bx.inside (si->rectangle ())) {
return true;
}
++si;
}
return false;
}
bool
RecursiveShapeIterator::skip_shapes () const
{
return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back ();
}
void
RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
{
@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
all_of_instance = m_local_complex_region_stack.empty ();
}
m_skip_shapes = skip_shapes ();
m_skip_shapes_member = false;
if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
box_type inst_bx = m_inst->bbox (m_box_convert);
m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer);
}
RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all;
if (receiver) {
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance);
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes);
} else if (m_skip_shapes) {
ni = RecursiveShapeReceiver::NI_skip;
}
if (ni == RecursiveShapeReceiver::NI_skip) {
@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
// skip instance array members not part of the complex region
while (! m_inst_array.at_end ()) {
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
if (! is_outside_complex_region (ia_box)) {
break;
} else {
@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
}
while (! m_inst_array.at_end () && receiver) {
if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) {
break;
} else {
++m_inst_array;
m_skip_shapes_member = false;
while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) {
m_skip_shapes_member = m_skip_shapes;
if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer);
}
bool skip = false;
if (receiver) {
skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance (), m_skip_shapes_member);
} else {
skip = m_skip_shapes_member;
}
if (skip) {
++m_inst_array;
} else {
break;
}
}
}

View File

@ -868,6 +868,7 @@ private:
mutable unsigned int m_layer;
mutable const cell_type *mp_cell;
mutable size_t m_current_layer;
mutable bool m_skip_shapes, m_skip_shapes_member;
mutable shape_iterator m_shape;
mutable cplx_trans_type m_trans;
mutable std::vector<cplx_trans_type> m_trans_stack;
@ -876,7 +877,7 @@ private:
mutable std::vector<const cell_type *> m_cells;
mutable std::vector<box_tree_type> m_local_complex_region_stack;
mutable std::vector<box_type> m_local_region_stack;
mutable std::vector<bool> m_skip_shapes_stack;
mutable std::vector<bool> m_skip_shapes_stack, m_skip_shapes_member_stack;
mutable bool m_needs_reinit;
mutable size_t m_inst_quad_id;
mutable std::vector<size_t> m_inst_quad_id_stack;
@ -899,6 +900,8 @@ private:
bool down (RecursiveShapeReceiver *receiver) const;
void pop () const;
bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const;
bool skip_shapes () const;
bool is_outside_complex_region (const db::Box &box) const;
void set_inactive (bool a) const
@ -1013,8 +1016,11 @@ public:
* - NI_all: iterate all members through "new_inst_member"
* - NI_single: iterate a single member (the first one)
* - NI_skip: skips the whole array (not a single instance is iterated)
*
* The "skip_shapes" parameter indicates that the instance is visited with the
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
*/
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; }
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; }
/**
* @brief Enters a new array member of the instance
@ -1026,8 +1032,11 @@ public:
* "all" is true, if an instance array is iterated in "all" mode (see new_inst).
*
* If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered.
*
* The "skip_shapes" parameter indicates that the instance member is visited with the
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
*/
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; }
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; }
/**
* @brief Delivers a shape

View File

@ -1618,18 +1618,34 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
}
}
db::polygon_ref_generator<TR> pr (layout, result);
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
const TR &subject = interactions.subject_shape (i->first);
if (others.find (subject) != others.end ()) {
// shortcut (and: keep, not: drop)
// Note that we still normalize and split the polygon, so we get a uniform
// behavior.
if (m_is_and) {
result.insert (subject);
db::Polygon poly;
subject.instantiate (poly);
splitter.put (poly);
}
} else if (i->second.empty ()) {
// shortcut (not: keep, and: drop)
// Note that we still normalize and split the polygon, so we get a uniform
// behavior.
if (! m_is_and) {
result.insert (subject);
db::Polygon poly;
subject.instantiate (poly);
splitter.put (poly);
}
} else {
for (auto e = subject.begin_edge (); ! e.at_end(); ++e) {
ep.insert (*e, p1);
@ -1649,8 +1665,6 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
}
db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
db::polygon_ref_generator<TR> pr (layout, result);
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
db::PolygonGenerator pg (splitter, true, true);
ep.set_base_verbosity (50);
ep.process (pg, op);

View File

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

View File

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

View File

@ -495,7 +495,7 @@ public:
/**
* @brief Returns the nth text
*
* This operation is available only for flat regions - i.e. such for which
* This operation is available only for flat text collections - i.e. such for which
* "has_valid_texts" is true.
*/
const db::Text *nth (size_t n) const
@ -503,6 +503,17 @@ public:
return mp_delegate->nth (n);
}
/**
* @brief Returns the nth text's property ID
*
* This operation is available only for flat text collections - i.e. such for which
* "has_valid_texts" is true.
*/
db::properties_id_type nth_prop_id (size_t n) const
{
return mp_delegate->nth_prop_id (n);
}
/**
* @brief Forces flattening of the text collection
*
@ -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

View File

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

View File

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

View File

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

View File

@ -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 &region, 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 &region, const TriangulateParameters &p
void
Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters &parameters, 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 &parameters, 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 &parameters, 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 &parameters, 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 &parameters)
Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters &parameters, 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 &parameters, 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 &parameters)
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 &parameters)
} 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 &parameters)
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);
}
}

View File

@ -20,8 +20,6 @@
*/
#ifndef HDR_dbTriangles
#define HDR_dbTriangles
@ -160,14 +158,17 @@ public:
void triangulate (const db::Region &region, const TriangulateParameters &parameters, double dbu = 1.0);
// more versions
void triangulate (const db::Polygon &poly, const TriangulateParameters &parameters, double dbu = 1.0);
void triangulate (const db::Region &region, const TriangulateParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void triangulate (const db::Polygon &poly, const TriangulateParameters &parameters, double dbu = 1.0);
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulateParameters &parameters, double dbu = 1.0);
void triangulate (const db::Polygon &poly, const TriangulateParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulateParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Triangulates a floating-point polygon
*/
void triangulate (const db::DPolygon &poly, const TriangulateParameters &parameters);
void triangulate (const db::DPolygon &poly, const TriangulateParameters &parameters, const db::DCplxTrans &trans = db::DCplxTrans ());
void triangulate (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const TriangulateParameters &parameters, 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 &center, 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,

View File

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

View File

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

View File

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

View File

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

View File

@ -783,6 +783,16 @@ static std::vector<db::EdgePairs> split_with_area2 (const db::EdgePairs *r, db::
return as_2edge_pairs_vector (r->split_filter (f));
}
static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n)
{
const db::EdgePair *ep = edge_pairs->nth (n);
if (! ep) {
return tl::Variant ();
} else {
return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n)));
}
}
static db::generic_shape_iterator<db::EdgePairWithProperties> begin_edge_pairs (const db::EdgePairs *edge_pairs)
{
return db::generic_shape_iterator<db::EdgePairWithProperties> (db::make_wp_iter (edge_pairs->delegate ()->begin ()));
@ -1855,13 +1865,15 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\n"
"Starting with version 0.30, the iterator delivers EdgePairWithProperties objects."
) +
method ("[]", &db::EdgePairs::nth, gsi::arg ("n"),
method_ext ("[]", &nth, gsi::arg ("n"),
"@brief Returns the nth edge pair\n"
"\n"
"This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. "
"those for which \\has_valid_edge_pairs? is true. Use \\flatten to explicitly flatten an edge pair collection.\n"
"\n"
"The \\each iterator is the more general approach to access the edge pairs."
"The \\each iterator is the more general approach to access the edge pairs.\n"
"\n"
"Since version 0.30.1, this method returns a \\EdgePairWithProperties object."
) +
method ("flatten", &db::EdgePairs::flatten,
"@brief Explicitly flattens an edge pair collection\n"

View File

@ -848,6 +848,16 @@ static std::vector<db::Edges> split_interacting_with_region (const db::Edges *r,
return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count));
}
static tl::Variant nth (const db::Edges *edges, size_t n)
{
const db::Edge *e = edges->nth (n);
if (! e) {
return tl::Variant ();
} else {
return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n)));
}
}
static db::generic_shape_iterator<db::EdgeWithProperties> begin_edges (const db::Edges *edges)
{
return db::generic_shape_iterator<db::EdgeWithProperties> (db::make_wp_iter (edges->delegate ()->begin ()));
@ -2439,14 +2449,16 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"This method has been introduced in version 0.25."
"Starting with version 0.30, the iterator delivers an EdgeWithProperties object."
) +
method ("[]", &db::Edges::nth, gsi::arg ("n"),
method_ext ("[]", &nth, gsi::arg ("n"),
"@brief Returns the nth edge of the collection\n"
"\n"
"This method returns nil if the index is out of range. It is available for flat edge collections only - i.e. "
"those for which \\has_valid_edges? is true. Use \\flatten to explicitly flatten an edge collection.\n"
"This method returns the raw edge (not merged edges, even if merged semantics is enabled).\n"
"\n"
"The \\each iterator is the more general approach to access the edges."
"The \\each iterator is the more general approach to access the edges.\n"
"\n"
"Since version 0.30.1, this method returns an \\EdgeWithProperties object."
) +
method ("flatten", &db::Edges::flatten,
"@brief Explicitly flattens an edge collection\n"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1379,6 +1379,16 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector
return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny);
}
static tl::Variant nth (const db::Region *region, size_t n)
{
const db::Polygon *poly = region->nth (n);
if (! poly) {
return tl::Variant ();
} else {
return tl::Variant (db::PolygonWithProperties (*poly, region->nth_prop_id (n)));
}
}
static db::generic_shape_iterator<db::PolygonWithProperties> begin_region (const db::Region *region)
{
return db::generic_shape_iterator<db::PolygonWithProperties> (db::make_wp_iter (region->delegate ()->begin ()));
@ -4103,14 +4113,16 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n"
"Starting with version 0.30, the iterator delivers a RegionWithProperties object."
) +
method ("[]", &db::Region::nth, gsi::arg ("n"),
method_ext ("[]", &nth, gsi::arg ("n"),
"@brief Returns the nth polygon of the region\n"
"\n"
"This method returns nil if the index is out of range. It is available for flat regions only - i.e. "
"those for which \\has_valid_polygons? is true. Use \\flatten to explicitly flatten a region.\n"
"This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).\n"
"\n"
"The \\each iterator is the more general approach to access the polygons."
"The \\each iterator is the more general approach to access the polygons.\n"
"\n"
"Since version 0.30.1, this method returns a \\PolygonWithProperties object."
) +
method ("flatten", &db::Region::flatten,
"@brief Explicitly flattens a region\n"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -485,8 +485,41 @@ TEST(7)
cib.push_back (b1.cell_index ());
cib.push_back (b2.cell_index ());
cm.create_multi_mapping_full (h, cib, *g, cia);
EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5");
EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5");
EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:");
}
// Issue #2014
TEST(8)
{
std::unique_ptr<db::Layout> g (new db::Layout ());
db::Cell &a (g->cell (g->add_cell ("a")));
db::Cell &b (g->cell (g->add_cell ("b")));
db::Cell &b1 (g->cell (g->add_cell ("b1")));
db::Cell &b2 (g->cell (g->add_cell ("b2")));
db::Cell &c (g->cell (g->add_cell ("c")));
b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ()));
b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ()));
b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ()));
b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ()));
db::Layout h;
db::Cell &ha (h.cell (h.add_cell ("a")));
db::Cell &hb (h.cell (h.add_cell ("b")));
db::Cell &hc (h.cell (h.add_cell ("c")));
db::CellMapping cm;
std::vector<db::cell_index_type> cib, cia;
cia.push_back (a.cell_index ());
cia.push_back (b.cell_index ());
cia.push_back (c.cell_index ());
cib.push_back (ha.cell_index ());
cib.push_back (hb.cell_index ());
cib.push_back (hc.cell_index ());
cm.create_multi_mapping_full (h, cib, *g, cia);
EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c");
EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:");
}

View File

@ -279,3 +279,24 @@ TEST(6_add_with_properties)
EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}");
}
TEST(7_properties)
{
db::PropertiesSet ps;
ps.insert (tl::Variant ("id"), 1);
db::properties_id_type pid1 = db::properties_id (ps);
db::EdgePairs edge_pairs;
db::Edge e1 (db::Point (0, 0), db::Point (10, 20));
db::Edge e2 (db::Point (1, 2), db::Point (11, 22));
edge_pairs.insert (db::EdgePairWithProperties (db::EdgePair (e1, e2), pid1));
edge_pairs.insert (db::EdgePair (e1, e2));
EXPECT_EQ (edge_pairs.nth (0)->to_string (), "(0,0;10,20)/(1,2;11,22)");
EXPECT_EQ (edge_pairs.nth (1)->to_string (), "(0,0;10,20)/(1,2;11,22)");
EXPECT_EQ (edge_pairs.nth (2) == 0, true);
EXPECT_EQ (edge_pairs.nth_prop_id (0), db::properties_id_type (0));
EXPECT_EQ (edge_pairs.nth_prop_id (1), pid1);
}

View File

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

View File

@ -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'?'*':'')");

View File

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

View File

@ -69,6 +69,7 @@ TEST(1)
EXPECT_EQ (empty == p, true);
EXPECT_EQ (p.is_box (), false);
EXPECT_EQ (p.is_empty (), true);
std::vector <db::Point> c1, c2, c3;
c1.push_back (db::Point (0, 0));
@ -76,6 +77,7 @@ TEST(1)
c1.push_back (db::Point (100, 1000));
c1.push_back (db::Point (100, 0));
p.assign_hull (c1.begin (), c1.end ());
EXPECT_EQ (p.is_empty (), false);
b = p.box ();
EXPECT_EQ (p.holes (), size_t (0));
EXPECT_EQ (p.area (), 1000*100);
@ -1404,3 +1406,30 @@ TEST(28)
db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000));
EXPECT_EQ (b.perimeter (), 8000000000.0);
}
TEST(29)
{
// Degenerated boxes and compress
db::Polygon b (db::Box (10, 20, 10, 20));
EXPECT_EQ (b.is_empty (), false);
EXPECT_EQ (b == db::Polygon (), false);
EXPECT_EQ (b.to_string (), "(10,20;10,20;10,20;10,20)");
EXPECT_EQ (double (b.area ()), 0.0);
b.compress (true);
EXPECT_EQ (b.is_empty (), true);
EXPECT_EQ (b == db::Polygon (), true);
db::SimplePolygon sb (db::Box (10, 20, 10, 20));
EXPECT_EQ (sb.is_empty (), false);
EXPECT_EQ (sb == db::SimplePolygon (), false);
EXPECT_EQ (sb.to_string (), "(10,20;10,20;10,20;10,20)");
EXPECT_EQ (double (sb.area ()), 0.0);
sb.compress (true);
EXPECT_EQ (sb.is_empty (), true);
EXPECT_EQ (sb == db::SimplePolygon (), true);
}

View File

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

View File

@ -756,15 +756,25 @@ namespace {
: public db::RecursiveShapeReceiver
{
public:
FlatPusher (std::set<db::Box> *boxes) : mp_boxes (boxes) { }
FlatPusher (std::set<db::Box> *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { }
void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
{
mp_boxes->insert (trans * shape.bbox ());
}
std::string to_string () const
{
std::vector<std::string> s;
for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) {
s.push_back (i->to_string ());
}
return tl::join (s.begin (), s.end (), ";");
}
private:
std::set<db::Box> *mp_boxes;
std::set<db::Box> m_boxes;
};
}
@ -1038,7 +1048,7 @@ public:
m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n";
}
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
{
m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ());
if (all) {
@ -1048,7 +1058,7 @@ public:
return NI_all;
}
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
{
m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans);
if (all) {
@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray
public:
ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { }
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all)
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all);
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip;
}
@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne
public:
ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { }
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all)
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all);
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
return inst.object ().cell_index () != m_rejected ? NI_all : NI_single;
}
@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance
public:
ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { }
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all)
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all);
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes);
return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected;
}
@ -1586,49 +1596,174 @@ TEST(12_ForMerged)
db::RecursiveShapeIterator i1 (*g, c0, 0);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
i1.set_for_merged_input (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
std::vector<unsigned int> lv;
lv.push_back (0);
i1 = db::RecursiveShapeIterator (*g, c0, lv);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
i1.set_for_merged_input (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
lv.push_back (1); // empty, but kills "for merged" optimization
i1 = db::RecursiveShapeIterator (*g, c0, lv);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
}
i1.set_for_merged_input (true);
x = collect(i1, *g);
// no longer optimized
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
}
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50));
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)");
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)");
}
i1.set_for_merged_input (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
}
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50));
i1.set_overlapping (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
}
i1.set_for_merged_input (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
}
}
TEST(12b_ForMerged)
{
std::unique_ptr<db::Layout> g (new db::Layout ());
g->insert_layer (0);
g->insert_layer (1);
db::Cell &c0 (g->cell (g->add_cell ()));
db::Cell &c1 (g->cell (g->add_cell ()));
db::Box b (0, 100, 1000, 1200);
c0.shapes (0).insert (db::Box (0, 0, 3000, 2200));
c1.shapes (0).insert (b);
db::Trans tt;
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt));
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l));
std::string x;
db::RecursiveShapeIterator i1 (*g, c0, 0);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
i1.set_for_merged_input (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
i1.set_for_merged_input (false);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000))));
db::RecursiveShapeIterator i2 (*g, c0, 0);
x = collect(i2, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
EXPECT_EQ (collect_with_copy(i2, *g), x);
{
FlatPusher f;
i2.reset ();
i2.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
i2.set_for_merged_input (true);
x = collect(i2, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
EXPECT_EQ (collect_with_copy(i2, *g), x);
{
FlatPusher f;
i2.reset ();
i2.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
}
TEST(13_ForMergedPerformance)
{

View File

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