Merge branch 'master' of github.com:KLayout/klayout into qt6

This commit is contained in:
Matthias Koefferlein 2021-11-24 23:21:27 +01:00
commit fb4caf15ac
143 changed files with 3746 additions and 1991 deletions

View File

@ -1,4 +1,25 @@
0.27.5 (2021-11-14):
* Enhancements: Better support for execution (also cross-interpreter) of macro code from scripts
- New methods for the Macro class like "run" and loading from file
- New class Interpreter for accessing Ruby interpreter state from Python and vice versa
* Enhancement: New DRC function "enclosed" as the reverse of "enclosing"
- Allows using the rectangle filters with "via enclosed by metal"
- Usually better performance than "metal enclosing via"
* Enhancements: Usability
- Navigation in the 2.5d view
- "tap" function shows layer names and details from layer view
- Documentation about layer views
* Enhancement: CentOS7 RPM's use Python 3 now
* Enhancement: Sending custom query shape objects to RDB produces shape markers now
* Enhancement: New method LayoutView#is_editable?
* Enhancement: %GITHUB%/issues/879 PCellDeclaration#layout and Layout#library ownership links
* Bugfix: %GITHUB%/issues/918 Editor options tab window too big
* Bugfix: %GITHUB%/issues/920 Fill tool border parameter sign issue
* Bugfix: %GITHUB%/issues/921 BJT3 extraction with collector shape fixed
* Bugfix: %GITHUB%/issues/933 QMessageLogger methods safe now with "%" characters
* Bugfix: %GITHUB%/issues/935 Cell#insert (of cell instances) now checks their cell indexes
0.27.4 (2021-09-25):
* Enhancements: Some enhancements on the 2.5d view (e.g. live layer color update, z only scaling, wire frames, etc.)
* Enhancement: simple versions of DRC with_holes/without_holes without parameters

View File

@ -1,3 +1,10 @@
klayout (0.27.5-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Sun, 14 Nov 2021 17:04:40 +0100
klayout (0.27.4-1) unstable; urgency=low
* New features and bugfixes

43
Jenkinsfile-pypi Normal file
View File

@ -0,0 +1,43 @@
@Library("platform") _
properties([disableConcurrentBuilds()])
// from shared library - uses tags to set the platform name
// (this is a pretty stupid way to set a parameter, but I don't
// know of a better way to parameterize a multibranch pipeline)
platform = pypi_platform()
py_version = pypi_py_version()
currentBuild.description = "PyPI deployment " + platform
docker_image = "jenkins-manylinux2014_x86_64-pypi"
target = platform + ".whl"
node("master") {
stage("Checkout sources") {
checkout scm
}
stage("Building target ${target}") {
sh("rm -rf wheelhouse ; mkdir -p wheelhouse")
withDockerContainer(image: docker_image, args: "-v " + pwd() + ":/io") {
sh("PY_VERSION=" + py_version + " /io/ci-scripts/docker/docker_build_jenkins.sh")
}
}
stage("Publish and test") {
// publish for release tags
if (BRANCH_NAME.startsWith('v')) {
sh("twine upload --skip-existing wheelhouse/klayout-*manylinux2014*.whl wheelhouse/*.zip")
}
}
}

View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
if [[ -z $PY_VERSION ]]; then
echo '$PY_VERSION is not set'
exit 1
fi
echo PY_VERSION=$PY_VERSION
# Use single cores only so we do not overload the Jenkins host
export KLAYOUT_SETUP_MULTICORE=1
# Compile wheel and build source distribution
cd /io
"/opt/python/$PY_VERSION/bin/python" setup.py bdist_wheel -d /io/wheelhouse/ || exit 1
"/opt/python/$PY_VERSION/bin/python" setup.py sdist --formats=zip -d /io/wheelhouse || exit 1
# Bundle external shared libraries into the wheels via auditwheel
for whl in /io/wheelhouse/*linux_*.whl; do
auditwheel repair "$whl" -w /io/wheelhouse/ || exit 1
done
# Install packages and test
TEST_HOME=/io/testdata
"/opt/python/$PY_VERSION/bin/pip" install klayout --no-index -f /io/wheelhouse || exit 1
"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/import_db.py || exit 1
"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/import_rdb.py || exit 1
"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/import_tl.py || exit 1
"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/pya_tests.py || exit 1

View File

@ -1,13 +1,22 @@
Relevant KLayout version: 0.26.9
Relevant KLayout version: 0.27.4<br>
Author: Kazzz-S<br>
Last modified: 2021-11-07<br>
# 1. Introduction
This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.26.1 or later for different 64-bit Mac OSXs, including:
* El Capitan (10.11)
* Sierra (10.12)
* High Sierra (10.13)
This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.27.4 or later for different 64-bit macOS, including:
* Catalina (10.15.7) : the primary development environment
* Big Sur (11.x) : experimental; Apple M1 chip is not tested since the author does not own an M1 Mac
* Monterey (12.x) : -- ditto --
Building KLayout for the previous operating systems listed below should still be possible. <br>
However, they are not actively supported, and DMG packages for them are not provided.
* Mojave (10.14)
* Catalina (10.15) : the primary development environment
* Big Sur (11.0) : under development for the future support
* High Sierra (10.13)
* Sierra (10.12)
* El Capitan (10.11)
Throughout this document, the primary target machine is **Intel x86_64** with **macOS Catalina**.<br>
A **((Notes))** marker indicates special notes for specific operating systems.
# 2. Qt5 Frameworks
By default, the Qt framework is "Qt5" from MacPorts (https://www.macports.org/), which is usually located under:
@ -17,7 +26,7 @@ By default, the Qt framework is "Qt5" from MacPorts (https://www.macports.org/),
Alternatively, you can use "Qt5" from Homebrew (https://brew.sh/), which is usually located under:
```
/usr/local/opt/qt/
/usr/local/opt/qt@5/
```
OR
@ -31,10 +40,11 @@ If you have installed Anaconda3 under $HOME/opt/anaconda3/, make a symbolic link
```
/Applications/anaconda3/ ---> $HOME/opt/anaconda3/
```
**((Notes))** "Qt5" from Homebrew is chosen as the default for **Big Sur** and **Monterey**.
# 3. Script language support: Ruby and Python
By default, supported script languages, i.e., Ruby and Python, are those standard ones bundled with the OS.<br>
As for Catalina (10.15),
Earlier, by default, supported script languages, i.e., Ruby and Python, were those standard ones bundled with the OS.<br>
This configuration is possible only for macOS Catalina (10.15.7).<br>
```
$ /usr/bin/ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]
@ -42,9 +52,12 @@ $ /usr/bin/ruby -v
$ /usr/bin/python --version
Python 2.7.16
```
Even in the latest OS (11.0 Big Sur) as of today (November 2020), Python 3.x is not bundled with the OS, which is why users want non-OS-standard script language support.
To meet such a requirement, the build script **`build4mac.py`** provides several possible combinations of Qt5, Ruy, and Python module.<br>
Big Sur (11.x) and Monterey (12.x) still provide the Python 2.7 binaries to run various legacy applications.<br>
However, the latest Xcode 13.1 does not allow us to link the legacy Python 2.7 library with the newly compiled KLayout binaries.<br>
Therefore, Homebrew is adopted as the default environment for Big Sur and Monterey.
The build script **`build4mac.py`** provides several possible combinations of Qt5, Ruy, and Python modules to accommodate such a slightly complex environment.<br>
Some typical use cases are described in Section 6.
# 4. Prerequisites
@ -52,51 +65,100 @@ You need to have:
* the latest Xcode and command-line tool kit compliant with each OS
* Qt5 package from MacPorts, Homebrew, or Anaconda3
* optionally Ruby and Python packages from MacPorts, Homebrew, or Anaconda3
#### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`. ####
#### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`.
# 5. Command-line options of **`build4mac.py`** are as shown below.
**((Notes))** For **Catalina**
```
---------------------------------------------------------------------------------------------------------
<< Usage of **`build4mac.py`** >>
for building KLayout 0.26.1 or later on different Apple Mac OSX platforms.
<< Usage of 'build4mac.py' >>
for building KLayout 0.27.4 or later on different Apple macOS / Mac OSX platforms.
$ [python] ./build4mac.py
$ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value
--------------------------------------------------------------------------------------+---------------
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3'] | qt5macports
: Qt5MacPorts: use Qt5 from MacPorts |
: Qt5Brew: use Qt5 from Homebrew |
: Qt5Ana3: use Qt5 from Anaconda3 |
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP27', 'HB27', 'Ana3'] | sys
: nil: don't bind Ruby |
: Sys: use OS-bundled Ruby [2.0 - 2.6] depending on OS |
: MP27: use Ruby 2.7 from MacPorts |
: HB27: use Ruby 2.7 from Homebrew |
: Ana3: use Ruby 2.5 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3', | sys
: 'HBAuto'] |
: nil: don't bind Python |
: Sys: use OS-bundled Python 2.7 [ElCapitan -- BigSur] |
: MP38: use Python 3.8 from MacPorts |
: HB38: use Python 3.8 from Homebrew |
: Ana3: use Python 3.7 from Anaconda3 |
: HBAuto: use the latest Python 3.x auto-detected from Homebrew |
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-m|--make <option>] : option passed to 'make' | '-j4'
[-d|--debug] : enable debug mode build | disabled
[-c|--checkcom] : check command-line and exit without building | disabled
[-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled
[-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled
: from the source code and use the tools in the same machine |
: ! After confirmation of successful build of 'klayout.app', |
: rerun this script with BOTH: |
: 1) the same options used for building AND |
: 2) <-y|--deploy> OR <-Y|--DEPLOY> |
: optionally with [-v|--verbose <0-3>] |
[-v|--verbose <0-3>] : verbose level of `macdeployqt' (effective with -y only) | 1
: 0 = no output, 1 = error/warning (default), |
: 2 = normal, 3 = debug |
[-?|--?] : print this usage and exit | disabled
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3'] | qt5macports
: Qt5MacPorts: use Qt5 from MacPorts |
: Qt5Brew: use Qt5 from Homebrew |
: Qt5Ana3: use Qt5 from Anaconda3 |
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP27', 'HB27', 'Ana3'] | sys
: nil: don't bind Ruby |
: Sys: use OS-bundled Ruby [2.0 - 2.6] depending on OS |
: MP27: use Ruby 2.7 from MacPorts |
: HB27: use Ruby 2.7 from Homebrew |
: Ana3: use Ruby 2.5 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3', | sys
: 'HBAuto'] |
: nil: don't bind Python |
: Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] |
: MP38: use Python 3.8 from MacPorts |
: HB38: use Python 3.8 from Homebrew |
: Ana3: use Python 3.8 from Anaconda3 |
: HBAuto: use the latest Python 3.x auto-detected from Homebrew |
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-u|--noqtuitools] : don't include uitools in Qt binding | disabled
[-m|--make <option>] : option passed to 'make' | '--jobs=4'
[-d|--debug] : enable debug mode build | disabled
[-c|--checkcom] : check command-line and exit without building | disabled
[-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled
[-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled
: from the source code and use the tools in the same machine |
: ! After confirmation of successful build of 'klayout.app', |
: rerun this script with BOTH: |
: 1) the same options used for building AND |
: 2) <-y|--deploy> OR <-Y|--DEPLOY> |
: optionally with [-v|--verbose <0-3>] |
[-v|--verbose <0-3>] : verbose level of `macdeployqt' (effective with -y only) | 1
: 0 = no output, 1 = error/warning (default), |
: 2 = normal, 3 = debug |
[-?|--?] : print this usage and exit | disabled
-----------------------------------------------------------------------------------------+---------------
```
**((Notes))** For **Big Sur** and **Monterey**
```
---------------------------------------------------------------------------------------------------------
<< Usage of 'build4mac.py' >>
for building KLayout 0.27.4 or later on different Apple macOS / Mac OSX platforms.
$ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value
--------------------------------------------------------------------------------------+---------------
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3'] | qt5brew
: Qt5MacPorts: use Qt5 from MacPorts |
: Qt5Brew: use Qt5 from Homebrew |
: Qt5Ana3: use Qt5 from Anaconda3 |
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP27', 'HB27', 'Ana3'] | hb27
: nil: don't bind Ruby |
: Sys: use OS-bundled Ruby [2.0 - 2.6] depending on OS |
: MP27: use Ruby 2.7 from MacPorts |
: HB27: use Ruby 2.7 from Homebrew |
: Ana3: use Ruby 2.5 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3', | hb38
: 'HBAuto'] |
: nil: don't bind Python |
: Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] |
: MP38: use Python 3.8 from MacPorts |
: HB38: use Python 3.8 from Homebrew |
: Ana3: use Python 3.8 from Anaconda3 |
: HBAuto: use the latest Python 3.x auto-detected from Homebrew |
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-u|--noqtuitools] : don't include uitools in Qt binding | disabled
[-m|--make <option>] : option passed to 'make' | '--jobs=4'
[-d|--debug] : enable debug mode build | disabled
[-c|--checkcom] : check command-line and exit without building | disabled
[-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled
[-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled
: from the source code and use the tools in the same machine |
: ! After confirmation of successful build of 'klayout.app', |
: rerun this script with BOTH: |
: 1) the same options used for building AND |
: 2) <-y|--deploy> OR <-Y|--DEPLOY> |
: optionally with [-v|--verbose <0-3>] |
[-v|--verbose <0-3>] : verbose level of `macdeployqt' (effective with -y only) | 1
: 0 = no output, 1 = error/warning (default), |
: 2 = normal, 3 = debug |
[-?|--?] : print this usage and exit | disabled
-----------------------------------------------------------------------------------------+---------------
```
@ -105,7 +167,13 @@ In this section, the actual file names and directory names are those obtained on
On different OS, those names differ accordingly.
### 6A. Standard build using the OS-bundled Ruby and Python
1. Invoke **`build4mac.py`** with the default options:
0. Install MacPorts, then install Qt5 by
```
$ sudo port install coreutils
$ sudo port install findutils
$ sudo port install qt5
```
1. Invoke **`build4mac.py`** with the default options: **((Notes))** only for Catalina
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py
@ -127,9 +195,19 @@ If you use the "-Y" option instead of the "-y" in Step-3, the Qt5 framework is N
Then the directory name will be **`LW-qt5MP.pkg.macos-Catalina-release-RsysPsys`**, where
* "LW-" means that this is a lightweight package.
#### If you build KLayout from the source code AND run it on the same machine, the "-Y" option is highly recommended. ####
#### If you build KLayout from the source code AND run it on the same machine, the "-Y" option is highly recommended.
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.7 and MacPorts Python 3.8
0. Install MacPorts, then install Qt5, Ruby 2.7, and Python 3.8 by
```
$ sudo port install coreutils
$ sudo port install findutils
$ sudo port install qt5
$ sudo port install ruby27
$ sudo port install python38
$ sudo port install py38-pip
```
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r mp27 -p mp38
@ -148,6 +226,13 @@ $ ./build4mac.py -q qt5macports -r mp27 -p mp38 -Y
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.8
0. Install Homebrew, then install Qt5, Ruby 2.7, and Python 3.8 by
```
$ brew install qt@5
$ brew install ruby@2.7
$ brew install python@3.8
```
1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default for Big Sur and Monterey.
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r hb27 -p hb38
@ -166,6 +251,12 @@ $ ./build4mac.py -q qt5brew -r hb27 -p hb38 -Y
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.8
0. Install Homebrew, then install Qt5 and Python 3.8 by
```
$ brew install qt@5
$ brew install python@3.8
```
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r sys -p hb38
@ -178,15 +269,20 @@ $ ./build4mac.py -q qt5brew -r sys -p hb38 -y
```
The application bundle **`klayout.app`** is located under:<br>
**`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38`** directory, where
* "HW-" means that this is a heavyweight package because both Qt5 and Python are deployed.
* "HW-" means that this is a heavyweight package because both Qt5 and Python Frameworks are deployed.
* "qt5Brew" means that Qt5 from Homebrew is used.
* "RsysPhb38" means that Ruby is OS-bundled; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### Important ###
### Important
So far, the deployment of Homebrew Ruby is not supported. <br>
Therefore, if you intend to use the "-y" option for deployment, you need to use the "-r sys" option for building.
### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.8
0. Install Anaconda3, then install Ruby 2.5 by
```
$ conda install ruby
```
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
@ -216,7 +312,7 @@ If you choose such a combination, the resultant package directory name will begi
# 7. Making a DMG installer
You can make a DMG installer using another Python script **`makeDMG4mac.py`**.
This script requires a directory generated by **`build4mac.py`** with [-y|-Y] option (refer to 6A through 6E).
This script requires a directory generated by **`build4mac.py`** with the [-y|-Y] option (refer to 6A through 6E).
1. Make a symbolic link (if it does not exist) from the parent directory (where **`build.sh`** exists) to **`makeDMG4mac.py`**, that is,
```
@ -228,8 +324,8 @@ $ cd /where/'build.sh'/exists
$ ./makeDMG4mac.py -p ST-qt5MP.pkg.macos-Catalina-release-RsysPsys -m
```
This command will generate the two files below:<br>
* **`ST-klayout-0.26.9-macOS-Catalina-1-qt5MP-RsysPsys.dmg`** ---(1) the main DMG file
* **`ST-klayout-0.26.9-macOS-Catalina-1-qt5MP-RsysPsys.dmg.md5`** ---(2) MD5-value text file
* **`ST-klayout-0.27.4-macOS-Catalina-1-qt5MP-RsysPsys.dmg`** ---(1) the main DMG file
* **`ST-klayout-0.27.4-macOS-Catalina-1-qt5MP-RsysPsys.dmg.md5`** ---(2) MD5-value text file
# Known issues
Because we assume some specific versions of non-OS-standard Ruby and Python, updating MacPorts, Homebrew, or Anaconda3 may cause build- and link errors.<br>
@ -237,7 +333,7 @@ In such cases, you need to update the dictionary contents of **`build4mac_env.py
# Final comments
No need to say, KLayout is a great tool! <br>
With the object-oriented script language (both Ruby and Python) support, our error-prone jobs can be greatly simplified and speed-up.<br>
With the object-oriented script language (both Ruby and Python) support, our error-prone layout jobs can be greatly simplified and speed-up.<br>
Building KLayout from its source code is not difficult. Try it with your favorite environment!
[End of File]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

@ -25,57 +25,77 @@ sys.path.append( mydir + "/macbuild" )
from build4mac_env import *
from build4mac_util import *
#-------------------------------------------------------------------------------
## To generate the OS-wise usage strings and the default module set
#
# @param[in] platform platform name
#
# @return (usage, moduleset)-tuple
#-------------------------------------------------------------------------------
def GenerateUsage(platform):
if platform.upper() in [ "MONTEREY", "BIGSUR" ]: # with Xcode [13.1 .. ]
myQt5 = "qt5brew"
myRuby = "hb27"
myPython = "hb38"
moduleset = ('qt5Brew', 'HB27', 'HB38')
else: # with Xcode [ .. 12.4]
myQt5 = "qt5macports"
myRuby = "sys"
myPython = "sys"
moduleset = ('qt5MP', 'Sys', 'Sys')
usage = "\n"
usage += "---------------------------------------------------------------------------------------------------------\n"
usage += "<< Usage of 'build4mac.py' >>\n"
usage += " for building KLayout 0.27.4 or later on different Apple macOS / Mac OSX platforms.\n"
usage += "\n"
usage += "$ [python] ./build4mac.py\n"
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value\n"
usage += " --------------------------------------------------------------------------------------+---------------\n"
usage += " [-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3'] | %s \n" % myQt5
usage += " : Qt5MacPorts: use Qt5 from MacPorts | \n"
usage += " : Qt5Brew: use Qt5 from Homebrew | \n"
usage += " : Qt5Ana3: use Qt5 from Anaconda3 | \n"
usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP27', 'HB27', 'Ana3'] | %s \n" % myRuby
usage += " : nil: don't bind Ruby | \n"
usage += " : Sys: use OS-bundled Ruby [2.0 - 2.6] depending on OS | \n"
usage += " : MP27: use Ruby 2.7 from MacPorts | \n"
usage += " : HB27: use Ruby 2.7 from Homebrew | \n"
usage += " : Ana3: use Ruby 2.5 from Anaconda3 | \n"
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3', | %s \n" % myPython
usage += " : 'HBAuto'] | \n"
usage += " : nil: don't bind Python | \n"
usage += " : Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] | \n"
usage += " : MP38: use Python 3.8 from MacPorts | \n"
usage += " : HB38: use Python 3.8 from Homebrew | \n"
usage += " : Ana3: use Python 3.8 from Anaconda3 | \n"
usage += " : HBAuto: use the latest Python 3.x auto-detected from Homebrew | \n"
usage += " [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled\n"
usage += " [-u|--noqtuitools] : don't include uitools in Qt binding | disabled\n"
usage += " [-m|--make <option>] : option passed to 'make' | '--jobs=4'\n"
usage += " [-d|--debug] : enable debug mode build | disabled\n"
usage += " [-c|--checkcom] : check command-line and exit without building | disabled\n"
usage += " [-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled\n"
usage += " [-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled\n"
usage += " : from the source code and use the tools in the same machine | \n"
usage += " : ! After confirmation of successful build of 'klayout.app', | \n"
usage += " : rerun this script with BOTH: | \n"
usage += " : 1) the same options used for building AND | \n"
usage += " : 2) <-y|--deploy> OR <-Y|--DEPLOY> | \n"
usage += " : optionally with [-v|--verbose <0-3>] | \n"
usage += " [-v|--verbose <0-3>] : verbose level of `macdeployqt' (effective with -y only) | 1\n"
usage += " : 0 = no output, 1 = error/warning (default), | \n"
usage += " : 2 = normal, 3 = debug | \n"
usage += " [-?|--?] : print this usage and exit | disabled\n"
usage += "-----------------------------------------------------------------------------------------+---------------\n"
return (usage, moduleset)
#-------------------------------------------------------------------------------
## To get the default configurations
#
# @return a dictionary containing the default configuration for the macOS build
#-------------------------------------------------------------------------------
def Get_Default_Config():
Usage = "\n"
Usage += "---------------------------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'build4mac.py' >>\n"
Usage += " for building KLayout 0.26.12 or later on different Apple Mac OSX / macOS platforms.\n"
Usage += "\n"
Usage += "$ [python] ./build4mac.py \n"
Usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value\n"
Usage += " --------------------------------------------------------------------------------------+---------------\n"
Usage += " [-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3'] | qt5macports \n"
Usage += " : Qt5MacPorts: use Qt5 from MacPorts | \n"
Usage += " : Qt5Brew: use Qt5 from Homebrew | \n"
Usage += " : Qt5Ana3: use Qt5 from Anaconda3 | \n"
Usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP27', 'HB27', 'Ana3'] | sys \n"
Usage += " : nil: don't bind Ruby | \n"
Usage += " : Sys: use OS-bundled Ruby [2.0 - 2.7] depending on OS | \n"
Usage += " : MP27: use Ruby 2.7 from MacPorts | \n"
Usage += " : HB27: use Ruby 2.7 from Homebrew | \n"
Usage += " : Ana3: use Ruby 2.5 from Anaconda3 | \n"
Usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3', | sys \n"
Usage += " : 'HBAuto'] | \n"
Usage += " : nil: don't bind Python | \n"
Usage += " : Sys: use OS-bundled Python 2.7 [ElCapitan -- BigSur] | \n"
Usage += " : MP38: use Python 3.8 from MacPorts | \n"
Usage += " : HB38: use Python 3.8 from Homebrew | \n"
Usage += " : Ana3: use Python 3.8 from Anaconda3 | \n"
Usage += " : HBAuto: use the latest Python 3.x auto-detected from Homebrew | \n"
Usage += " [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled \n"
Usage += " [-u|--noqtuitools] : don't include uitools in Qt binding | disabled \n"
Usage += " [-m|--make <option>] : option passed to 'make' | '--jobs=4' \n"
Usage += " [-d|--debug] : enable debug mode build | disabled \n"
Usage += " [-c|--checkcom] : check command-line and exit without building | disabled \n"
Usage += " [-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled \n"
Usage += " [-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled \n"
Usage += " : from the source code and use the tools in the same machine | \n"
Usage += " : ! After confirmation of successful build of 'klayout.app', | \n"
Usage += " : rerun this script with BOTH: | \n"
Usage += " : 1) the same options used for building AND | \n"
Usage += " : 2) <-y|--deploy> OR <-Y|--DEPLOY> | \n"
Usage += " : optionally with [-v|--verbose <0-3>] | \n"
Usage += " [-v|--verbose <0-3>] : verbose level of `macdeployqt' (effective with -y only) | 1 \n"
Usage += " : 0 = no output, 1 = error/warning (default), | \n"
Usage += " : 2 = normal, 3 = debug | \n"
Usage += " [-?|--?] : print this usage and exit | disabled \n"
Usage += "-----------------------------------------------------------------------------------------+---------------\n"
ProjectDir = os.getcwd()
BuildBash = "./build.sh"
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
@ -83,11 +103,13 @@ def Get_Default_Config():
if not System == "Darwin":
print("")
print( "!!! Sorry. Your system <%s> looks like non-Mac" % System, file=sys.stderr )
print(Usage)
print( GenerateUsage("")[0] )
sys.exit(1)
release = int( Release.split(".")[0] ) # take the first of ['19', '0', '0']
if release == 20:
if release == 21:
Platform = "Monterey"
elif release == 20:
Platform = "BigSur"
elif release == 19:
Platform = "Catalina"
@ -103,23 +125,29 @@ def Get_Default_Config():
Platform = ""
print("")
print( "!!! Sorry. Unsupported major OS release <%d>" % release, file=sys.stderr )
print(Usage)
print( GenerateUsage("")[0] )
sys.exit(1)
if not Machine == "x86_64":
if Machine == "arm64" and Platform == "BigSur": # with an Apple Silicon Chip
if Machine == "arm64" and (Platform == "Monterey" or Platform == "BigSur"): # with an Apple Silicon Chip
print("")
print( "### Your Mac equips an Apple Silicon Chip ###" )
print("")
else:
print("")
print( "!!! Sorry. Only x86_64/arm64 architecture machine is supported but found <%s>" % Machine, file=sys.stderr )
print(Usage)
print( GenerateUsage("")[0] )
sys.exit(1)
# Set the OS-wise usage and module set
Usage, ModuleSet = GenerateUsage(Platform)
# Set the default modules
ModuleQt = "Qt5MacPorts"
if Platform == "BigSur":
if Platform == "Monterey":
ModuleRuby = "RubyMonterey"
ModulePython = "PythonMonterey"
elif Platform == "BigSur":
ModuleRuby = "RubyBigSur"
ModulePython = "PythonBigSur"
elif Platform == "Catalina":
@ -152,7 +180,6 @@ def Get_Default_Config():
PackagePrefix = ""
DeployVerbose = 1
Version = GetKLayoutVersionFrom( "./version.sh" )
ModuleSet = ( 'qt5MP', 'Sys', 'Sys' )
config = dict()
config['ProjectDir'] = ProjectDir # project directory where "build.sh" exists
@ -341,7 +368,9 @@ def Parse_CLI_Args(config):
ModuleRuby = 'nil'
elif choiceRuby == "Sys":
choiceRuby = "Sys"
if Platform == "BigSur":
if Platform == "Monterey":
ModuleRuby = 'RubyMonterey'
elif Platform == "BigSur":
ModuleRuby = 'RubyBigSur'
elif Platform == "Catalina":
ModuleRuby = 'RubyCatalina'
@ -387,7 +416,9 @@ def Parse_CLI_Args(config):
if choicePython == "nil":
ModulePython = 'nil'
elif choicePython == "Sys":
if Platform == "BigSur":
if Platform == "Monterey":
ModulePython = 'PythonMonterey'
elif Platform == "BigSur":
ModulePython = 'PythonBigSur'
elif Platform == "Catalina":
ModulePython = 'PythonCatalina'
@ -1098,7 +1129,7 @@ def Deploy_Binaries_For_Bundle(config, parameters):
cmd06 = "rm -rf %s" % binTarget
cmd07 = "mkdir %s" % sitepackagesTarget
cmd08 = "cp -RL %s/{pip*,pkg_resources,setuptools*,wheel*} %s" % (sitepackagesSource, sitepackagesTarget)
cmd08 = "cp -RL %s/{*distutils*,pip*,pkg_resources,setuptools*,wheel*} %s" % (sitepackagesSource, sitepackagesTarget)
shell_commands = list()
shell_commands.append(cmd01)

View File

@ -64,10 +64,11 @@ Qt5Ana3 = { 'qmake' : '/Applications/anaconda3/bin/qmake',
#-----------------------------------------------------
# [2] Ruby
#-----------------------------------------------------
RubyNil = [ 'nil' ]
RubySys = [ 'RubyElCapitan', 'RubySierra', 'RubyHighSierra', 'RubyMojave', 'RubyCatalina', 'RubyBigSur' ]
RubyExt = [ 'Ruby27MacPorts', 'Ruby27Brew', 'RubyAnaconda3' ]
Rubies = RubyNil + RubySys + RubyExt
RubyNil = [ 'nil' ]
RubySys = [ 'RubyElCapitan', 'RubySierra', 'RubyHighSierra', 'RubyMojave' ]
RubySys += [ 'RubyCatalina', 'RubyBigSur', 'RubyMonterey' ]
RubyExt = [ 'Ruby27MacPorts', 'Ruby27Brew', 'RubyAnaconda3' ]
Rubies = RubyNil + RubySys + RubyExt
#-----------------------------------------------------
# Whereabout of different components of Ruby
@ -135,7 +136,7 @@ RubyCatalina = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % CatalinaSDK
}
# Bundled with Big Sur (11.0)
# Bundled with Big Sur (11.x)
# Refer to the "Catalina" section above
# [Key Type Name] = 'Sys'
BigSurSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
@ -145,6 +146,16 @@ RubyBigSur = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % BigSurSDK
}
# Bundled with Monterey (12.x)
# Refer to the "Catalina" section above
# [Key Type Name] = 'Sys'
MontereySDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
RubyMonterey = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby',
'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % MontereySDK,
'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % MontereySDK,
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % MontereySDK
}
# Ruby 2.7 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
# install with 'sudo port install ruby27'
# [Key Type Name] = 'MP27'
@ -180,6 +191,7 @@ RubyDictionary = { 'nil' : None,
'RubyMojave' : RubyMojave,
'RubyCatalina' : RubyCatalina,
'RubyBigSur' : RubyBigSur,
'RubyMonterey' : RubyMonterey,
'Ruby27MacPorts': Ruby27MacPorts,
'Ruby27Brew' : Ruby27Brew,
'RubyAnaconda3' : RubyAnaconda3
@ -188,10 +200,11 @@ RubyDictionary = { 'nil' : None,
#-----------------------------------------------------
# [3] Python
#-----------------------------------------------------
PythonNil = [ 'nil' ]
PythonSys = [ 'PythonElCapitan', 'PythonSierra', 'PythonHighSierra', 'PythonMojave', 'PythonCatalina', 'PythonBigSur' ]
PythonExt = [ 'Python38MacPorts', 'Python38Brew', 'PythonAnaconda3', 'PythonAutoBrew' ]
Pythons = PythonNil + PythonSys + PythonExt
PythonNil = [ 'nil' ]
PythonSys = [ 'PythonElCapitan', 'PythonSierra', 'PythonHighSierra', 'PythonMojave' ]
PythonSys += [ 'PythonCatalina', 'PythonBigSur', 'PythonMonterey' ]
PythonExt = [ 'Python38MacPorts', 'Python38Brew', 'PythonAnaconda3', 'PythonAutoBrew' ]
Pythons = PythonNil + PythonSys + PythonExt
#-----------------------------------------------------
# Whereabout of different components of Python
@ -240,13 +253,26 @@ PythonCatalina = { 'exe': '/System/Library/Frameworks/Python.framework/Versions
'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib'
}
# Bundled with Big Sur (11.0)
# Bundled with Big Sur (11.x)
# ** IMPORTANT NOTES **
# Xcode [13.1 .. ] does not allow to link the legacy Python 2.7 library not to produce unsupported applications.
# This code block remains here to keep the parallelism.
# [Key Type Name] = 'Sys'
PythonBigSur = { 'exe': '/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python',
'inc': '/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7',
'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib'
}
# Bundled with Monterey (12.x)
# ** IMPORTANT NOTES **
# Xcode [13.1 .. ] does not allow to link the legacy Python 2.7 library not to produce unsupported applications.
# This code block remains here to keep the parallelism.
# [Key Type Name] = 'Sys'
PythonMonterey = { 'exe': '/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python',
'inc': '/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7',
'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib'
}
# Python 3.8 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
# install with 'sudo port install python38'
# [Key Type Name] = 'MP38'
@ -303,6 +329,7 @@ PythonDictionary = { 'nil' : None,
'PythonMojave' : PythonMojave,
'PythonCatalina' : PythonCatalina,
'PythonBigSur' : PythonBigSur,
'PythonMonterey' : PythonMonterey,
'Python38MacPorts': Python38MacPorts,
'Python38Brew' : Python38Brew,
'PythonAnaconda3' : PythonAnaconda3

View File

@ -75,24 +75,24 @@ def SetGlobals():
global Bit # machine bit-size
Usage = "\n"
Usage += "--------------------------------------------------------------------------------------------------------\n"
Usage += "---------------------------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'makeDMG4mac.py' >>\n"
Usage += " for making a DMG file of KLayout 0.26.12 or later on different Apple Mac OSX / macOS platforms.\n"
Usage += " for making a DMG file of KLayout 0.27.4 or later on different Apple macOS / Mac OSX platforms.\n"
Usage += "\n"
Usage += "$ [python] ./makeDMG4mac.py \n"
Usage += "$ [python] ./makeDMG4mac.py\n"
Usage += " option & argument : descriptions | default value\n"
Usage += " ----------------------------------------------------------------------------------+---------------\n"
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | `` \n"
Usage += " ----------------------------------------------------------------------------------+-----------------\n"
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n"
Usage += " : like 'ST-qt5MP.pkg.macos-Catalina-release-RsysPsys' | \n"
Usage += " <-c|--clean> : clean the work directory | disabled \n"
Usage += " <-m|--make> : make a compressed DMG file | disabled \n"
Usage += " <-c|--clean> : clean the work directory | disabled\n"
Usage += " <-m|--make> : make a compressed DMG file | disabled\n"
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
Usage += " [-b|--bundle <name>] : forcibly use this bundle name in the DMG | '' \n"
Usage += " [-s|--serial <num>] : DMG serial number | 1 \n"
Usage += " [-u|--unsafe] : Ignores a few checks (use with caution) | disabled \n"
Usage += " [-t|--targetdmg] : Specify output .dmg filename | chosen by script \n"
Usage += " [-?|--?] : print this usage and exit | disabled \n"
Usage += "-------------------------------------------------------------------------------------+------------------\n"
Usage += " [-b|--bundle <name>] : forcibly use this bundle name in the DMG | ''\n"
Usage += " [-s|--serial <num>] : DMG serial number | 1\n"
Usage += " [-u|--unsafe] : Ignores a few checks (use with caution) | disabled\n"
Usage += " [-t|--targetdmg] : Specify output .dmg filename | chosen by script\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += "-------------------------------------------------------------------------------------+-------------------\n"
ProjectDir = os.getcwd()
(System, Node, Release, Version, Machine, Processor) = platform.uname()
@ -105,7 +105,11 @@ def SetGlobals():
release = int( Release.split(".")[0] ) # take the first of ['19', '0', '0']
LatestOS = ""
if release == 20:
if release == 21:
GenOSName = "macOS"
Platform = "Monterey"
LatestOS = Platform
elif release == 20:
GenOSName = "macOS"
Platform = "BigSur"
LatestOS = Platform
@ -133,7 +137,7 @@ def SetGlobals():
sys.exit(1)
if not Machine == "x86_64":
if Machine == "arm64" and Platform == "BigSur": # with an Apple Silicon Chip
if Machine == "arm64" and (Platform == "Monterey" or Platform == "BigSur"): # with an Apple Silicon Chip
print("")
print( "### Your Mac equips an Apple Silicon Chip ###" )
print("")

View File

@ -17,14 +17,16 @@ import subprocess
#
# @return matching platform name on success; "" on failure
#------------------------------------------------------------------------------
def Test_My_Platform( platforms=['Catalina', 'BigSur'] ):
def Test_My_Platform( platforms=['Catalina', 'BigSur', 'Monterey' ] ):
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
if not System == "Darwin":
return ""
release = int( Release.split(".")[0] ) # take the first of ['19', '0', '0']
if release == 20:
if release == 21:
Platform = "Monterey"
elif release == 20:
Platform = "BigSur"
elif release == 19:
Platform = "Catalina"
@ -173,15 +175,23 @@ def Parse_CommandLine_Arguments():
global SrlDMG # DMG serial number
global Dropbox # Dropbox directory
platform = Test_My_Platform()
if platform in [ "Monterey", "BigSur" ]:
targetopt = "1,2,3,4"
elif platform in ["Catalina"]:
targetopt = "0,1,2,3,4"
else:
targetopt = ""
Usage = "\n"
Usage += "--------------------------------------------------------------------------------------------\n"
Usage += "--------------------------------------------------------------------------------------------------\n"
Usage += " nightlyBuild.py [EXPERIMENTAL] \n"
Usage += " << To execute the jobs for making KLayout's DMGs for macOS Catalina or Big Sur >> \n"
Usage += " << To execute the jobs for making KLayout's DMGs for macOS Catalina, Big Sur, or Monterey >>\n"
Usage += "\n"
Usage += "$ [python] nightlyBuild.py \n"
Usage += "$ [python] nightlyBuild.py\n"
Usage += " option & argument : comment on option if any | default value\n"
Usage += " ------------------------------------------------------------------------+--------------\n"
Usage += " [--target <list>] : 0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3', | '0,1,2,3,4'\n"
Usage += " [--target <list>] : 0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3', | '%s'\n" % targetopt
Usage += " 5='brewA', 6='brewAHW' | \n"
Usage += " [--build] : build and deploy | disabled\n"
Usage += " [--test] : run the QA Test | disabled\n"
@ -199,9 +209,9 @@ def Parse_CommandLine_Arguments():
Usage += " (3) $ ./nightlyBuild.py --test | \n"
Usage += " (4) $ ./nightlyBuild.py --check (confirm the QA Test results) | \n"
Usage += " (5) $ ./nightlyBuild.py --makedmg 1 | \n"
Usage += " (6) $ ./nightlyBuild.py --upload '0.26.12' | \n"
Usage += " (6) $ ./nightlyBuild.py --upload '0.27.4' | \n"
Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 | \n"
Usage += "---------------------------------------------------------------------------+----------------\n"
Usage += "---------------------------------------------------------------------------+----------------------\n"
p = optparse.OptionParser( usage=Usage )
p.add_option( '--target',
@ -244,7 +254,7 @@ def Parse_CommandLine_Arguments():
default=False,
help='check usage' )
p.set_defaults( targets = "0,1,2,3,4",
p.set_defaults( targets = "%s" % targetopt,
build = False,
qa_test = False,
qa_check = False,
@ -258,9 +268,9 @@ def Parse_CommandLine_Arguments():
print(Usage)
quit()
myPlatform = Test_My_Platform( ['Catalina', 'BigSur'] )
myPlatform = Test_My_Platform( ['Catalina', 'BigSur', 'Monterey' ] )
if myPlatform == "":
print( "! Current platform is not ['Catalina', 'BigSur']" )
print( "! Current platform is not ['Catalina', 'BigSur', 'Monterey' ]" )
print(Usage)
quit()
@ -295,7 +305,7 @@ def Parse_CommandLine_Arguments():
Dropbox = opt.upload
if not (Build or QATest or QACheck or MakeDMG or CleanDMG or Upload):
print( "! No option selected" )
print( "! No action selected" )
print(Usage)
quit()

View File

@ -452,6 +452,22 @@ run_demo gen, "input1.drc(enclosing(input2) < 2.0.um)", "drc_enc1u.png"
run_demo gen, "input1.drc(enclosing(input2,\n"+
" projection) < 2.0.um)", "drc_enc2u.png"
class Gen
def produce(s1, s2)
s1.insert(RBA::Box::new(3000, 0, 6000, 6000))
s2.insert(RBA::Box::new(0, 1000, 6000, 7000))
end
end
gen = Gen::new
run_demo gen, "input1.enclosed(input2, 2.0.um)", "drc_encd1.png"
run_demo gen, "input1.enclosed(input2, 2.0.um, projection)", "drc_encd2.png"
run_demo gen, "input1.drc(enclosed(input2) < 2.0.um)", "drc_encd1u.png"
run_demo gen, "input1.drc(enclosed(input2,\n"+
" projection) < 2.0.um)", "drc_encd2u.png"
class Gen
def produce(s1, s2)

View File

@ -425,6 +425,57 @@ add_native_impl_polygons
no_copy_ctor "QBasicMutex"
no_copy_ctor "QMapDataBase"
drop_method "QMessageLogger", /QMessageLogger::critical.*\.\.\./ # does not support ...
drop_method "QMessageLogger", /QMessageLogger::debug.*\.\.\./ # does not support ...
drop_method "QMessageLogger", /QMessageLogger::fatal.*\.\.\./ # does not support ...
drop_method "QMessageLogger", /QMessageLogger::info.*\.\.\./ # does not support ...
drop_method "QMessageLogger", /QMessageLogger::noDebug.*\.\.\./ # does not support ...
drop_method "QMessageLogger", /QMessageLogger::warning.*\.\.\./ # does not support ...
add_native_impl("QMessageLogger", <<'CODE', <<'DECL')
static void critical1(QMessageLogger *logger, const char *msg) {
logger->critical("%s", msg);
}
static void critical2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->critical(cat, "%s", msg);
}
static void debug1(QMessageLogger *logger, const char *msg) {
logger->debug("%s", msg);
}
static void debug2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->debug(cat, "%s", msg);
}
static void fatal1(QMessageLogger *logger, const char *msg) {
logger->fatal("%s", msg);
}
static void info1(QMessageLogger *logger, const char *msg) {
logger->info("%s", msg);
}
static void info2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->info(cat, "%s", msg);
}
static void noDebug1(QMessageLogger *logger, const char *msg) {
logger->noDebug("%s", msg);
}
static void warning1(QMessageLogger *logger, const char *msg) {
logger->warning("%s", msg);
}
static void warning2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->warning(cat, "%s", msg);
}
CODE
gsi::method_ext("critical", &critical1, "@brief Method void QMessageLogger::critical(const char *msg)") +
gsi::method_ext("critical", &critical2, "@brief Method void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg)") +
gsi::method_ext("debug", &debug1, "@brief Method void QMessageLogger::debug(const char *msg)") +
gsi::method_ext("debug", &debug2, "@brief Method void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg)") +
gsi::method_ext("fatal", &fatal1, "@brief Method void QMessageLogger::fatal(const char *msg)") +
gsi::method_ext("info", &info1, "@brief Method void QMessageLogger::info(const char *msg)") +
gsi::method_ext("info", &info2, "@brief Method void QMessageLogger::info(const QLoggingCategory &cat, const char *msg)") +
gsi::method_ext("noDebug", &noDebug1, "@brief Method void QMessageLogger::noDebug(const char *msg)") +
gsi::method_ext("warning", &warning1, "@brief Method void QMessageLogger::warning(const char *msg)") +
gsi::method_ext("warning", &warning2, "@brief Method void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg)")
DECL
# --------------------------------------------------------------
# QtGui

View File

@ -52,10 +52,10 @@ Requires: qt5-qttools-devel >= 5.11.1
# CentOS7 requirements
%if "%{target_system}" == "centos7"
Requires: ruby >= 2.0.0
Requires: python >= 2.7.5
Requires: python3 >= 3.6.0
Requires: qt-x11 >= 4.8.5
%define buildopt -j2
%define pylib %{python_sitearch}
%define pylib %{python3_sitearch}
%endif
%if "%{target_system}" == "centos6"

View File

@ -65,7 +65,13 @@ from distutils.errors import CompileError
import distutils.command.build_ext
import setuptools.command.build_ext
import multiprocessing
N_cores = multiprocessing.cpu_count()
# for Jenkins we do not want to be greedy
multicore = os.getenv("KLAYOUT_SETUP_MULTICORE")
if multicore:
N_cores = int(multicore)
else:
N_cores = multiprocessing.cpu_count()
# monkey-patch for parallel compilation

View File

@ -180,7 +180,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
<< tl::arg ("input_b", &infile_b, "The second input file (any format, may be gzip compressed)")
<< tl::arg ("?output", &output, "The output file to which the XOR differences are written",
"This argument is optional. If not given, the exit status alone will indicate whether the layouts "
"are identical or not."
"are identical or not. The output is a layout file. The format of the file is derived "
"from the file name's suffix (.oas[.gz] for (gzipped) OASIS, .gds[.gz] for (gzipped) GDS2 etc.)."
)
<< tl::arg ("-ta|--top-a=name", &top_a, "Specifies the top cell for the first layout",
"Use this option to take a specific cell as the top cell from the first layout. All "
@ -248,6 +249,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
db::Layout layout_b;
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (A): ")) + infile_a);
db::LoadLayoutOptions load_options;
generic_reader_options_a.configure (load_options);
@ -257,6 +260,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
}
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (B): ")) + infile_b);
db::LoadLayoutOptions load_options;
generic_reader_options_b.configure (load_options);
@ -549,6 +554,7 @@ bool run_deep_xor (const XORData &xor_data)
{
db::DeepShapeStore dss;
dss.set_threads (xor_data.threads);
dss.set_keep_layouts (true); // avoids excessive cell mapping
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
@ -593,6 +599,8 @@ bool run_deep_xor (const XORData &xor_data)
} 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) {
@ -607,15 +615,14 @@ bool run_deep_xor (const XORData &xor_data)
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
db::Region xor_res;
xor_res = in_a ^ in_b;
{
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) {
if (tl::verbosity () >= 20) {
tl::log << "Running XOR on layer " << ll->first.to_string () << " with tolerance " << *t;
}
db::LayerProperties lp = ll->first;
if (lp.layer >= 0) {
lp.layer += tol_index * xor_data.tolerance_bump;
@ -628,6 +635,7 @@ bool run_deep_xor (const XORData &xor_data)
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));
}

View File

@ -72,6 +72,7 @@ SOURCES = \
dbRecursiveInstanceIterator.cc \
dbRecursiveShapeIterator.cc \
dbRegion.cc \
dbRegionCheckUtils.cc \
dbRegionLocalOperations.cc \
dbSaveLayoutOptions.cc \
dbShape.cc \
@ -290,6 +291,7 @@ HEADERS = \
dbRecursiveInstanceIterator.h \
dbRecursiveShapeIterator.h \
dbRegion.h \
dbRegionCheckUtils.h \
dbRegionLocalOperations.h \
dbSaveLayoutOptions.h \
dbShape.h \

View File

@ -964,9 +964,7 @@ struct iterated_array
virtual std::pair <basic_array_iterator <Coord> *, bool>
begin_touching (const box_type &b) const
{
if (b.empty ()) {
return std::make_pair (new iterated_array_iterator <Coord> (m_v.begin (), m_v.end ()), false);
} else if (! b.touches (m_box)) {
if (b.empty () || ! b.touches (m_box)) {
return std::make_pair (new iterated_array_iterator <Coord> (m_v.end (), m_v.end ()), false);
} else {
box_convert_type bc;
@ -1699,7 +1697,7 @@ struct array
* an appropriate basic_array object using a complex transformation.
*/
array (const Obj &obj, const complex_trans_type &ct, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
: m_obj (obj), m_trans (ct), mp_base (new regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax) : new regular_array <coord_type> (a, b, amax, bmax))
{
// .. nothing yet ..
}
@ -1727,7 +1725,7 @@ struct array
* it's own storage.
*/
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
: m_obj (obj), m_trans (ct), mp_base (rep.insert (regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax)))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax)) : rep.insert (regular_array <coord_type> (a, b, amax, bmax)))
{
// .. nothing yet ..
}
@ -1766,7 +1764,7 @@ struct array
*/
template <class Iter>
array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to)
: m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to) : new iterated_array <coord_type> (from, to))
{
// .. nothing yet ..
}
@ -1779,7 +1777,7 @@ struct array
*/
explicit
array (const Obj &obj, const complex_trans_type &ct)
: m_obj (obj), m_trans (ct), mp_base (new single_complex_inst <coord_type> (ct.rcos (), ct.mag ()))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new single_complex_inst <coord_type> (ct.rcos (), ct.mag ()) : 0)
{
// .. nothing yet ..
}
@ -1809,7 +1807,7 @@ struct array
*/
explicit
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep)
: m_obj (obj), m_trans (ct), mp_base (rep.insert (single_complex_inst <coord_type> (ct.rcos (), ct.mag ())))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (single_complex_inst <coord_type> (ct.rcos (), ct.mag ())) : 0)
{
// .. nothing yet ..
}
@ -1890,17 +1888,13 @@ struct array
array_iterator <coord_type, Trans> begin_touching (const box_type &b, const BoxConv &bc) const
{
if (b.empty ()) {
if (mp_base) {
return array_iterator <coord_type, Trans> (m_trans, mp_base->begin_touching (box_type ()));
} else {
return array_iterator <coord_type, Trans> (m_trans, true);
}
return array_iterator <coord_type, Trans> (m_trans, true);
} else if (b == box_type::world ()) {
return begin ();
} else if (mp_base) {
box_type ob (bc (m_obj));
if (ob.empty ()) {
return array_iterator <coord_type, Trans> (m_trans, mp_base->begin_touching (box_type ()));
return array_iterator <coord_type, Trans> (m_trans, true);
} else {
if (mp_base->is_complex ()) {
complex_trans_type ct = mp_base->complex_trans (simple_trans_type (m_trans));

View File

@ -1150,7 +1150,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
size_t n = 0;
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
poly_check.enter (*p, n);
poly_check.single (*p, n);
n += 2;
}

View File

@ -487,6 +487,41 @@ Cell::hierarchy_levels () const
return m_hier_levels;
}
static bool
has_shapes_touching_impl (const db::Cell &cell, unsigned int layer, const db::Box &box)
{
if (! cell.shapes (layer).begin_touching (box, db::ShapeIterator::All).at_end ()) {
return true;
}
for (db::Cell::touching_iterator i = cell.begin_touching (box); ! i.at_end (); ++i) {
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (box, db::box_convert<db::CellInst> (*cell.layout (), layer)); ! ia.at_end (); ++ia) {
db::Box cbox;
if (i->is_complex ()) {
cbox = i->complex_trans (*ia).inverted () * box;
} else {
cbox = (*ia).inverted () * box;
}
if (has_shapes_touching_impl (cell.layout ()->cell (i->cell_index ()), layer, cbox)) {
return true;
}
}
}
return false;
}
bool
Cell::has_shapes_touching (unsigned int layer, const db::Box &box) const
{
return has_shapes_touching_impl (*this, layer, box);
}
void
Cell::collect_caller_cells (std::set<cell_index_type> &callers) const
{

View File

@ -721,6 +721,11 @@ public:
return shapes (layer).begin_touching (box, flags, prop_sel, inv_prop_sel);
}
/**
* @brief A quick, recursive test whether the cell has shapes touching the given box on the given layer
*/
bool has_shapes_touching (unsigned int layer, const db::Box &box) const;
/**
* @brief Collect all calling cells (either calling this cell directly or indirectly)
*

View File

@ -269,6 +269,11 @@ void CellMapping::clear ()
m_b2a_mapping.clear ();
}
void CellMapping::swap (CellMapping &other)
{
m_b2a_mapping.swap (other.m_b2a_mapping);
}
std::vector<db::cell_index_type> CellMapping::source_cells () const
{
std::vector<db::cell_index_type> s;
@ -409,9 +414,9 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
void
CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Cell mapping")));
tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Cell mapping")));
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - first step: mapping instance count and instance identity";
}
@ -449,7 +454,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
++a;
} else {
if (tl::verbosity () >= 30) {
if (tl::verbosity () >= 50) {
size_t na = 0, nb = 0;
for (std::multimap<size_t, db::cell_index_type>::const_iterator aa = a; aa != cm_a.end () && aa->first == w; ++aa) {
++na;
@ -497,7 +502,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Checked cell " << layout_a.cell_name (a->second) << ": " << candidates [a->second].size () << " candidates remaining.";
}
@ -519,7 +524,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
++a;
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Mapping candidates:";
dump_mapping (candidates, layout_a, layout_b);
}
@ -536,7 +541,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
reduction = false;
++iteration;
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - iteration " << iteration << ": cross-instance cone reduction";
}
@ -553,7 +558,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
refined_cand.clear ();
refined_cand.insert (refined_cand.end (), cand->second.begin (), cand->second.end ());
if (tl::verbosity () >= 50) {
if (tl::verbosity () >= 70) {
tl::info << "--- Cell: " << layout_a.cell_name (cand->first);
tl::info << "Before reduction: " << tl::noendl;
for (size_t i = 0; i < refined_cand.size (); ++i) {
@ -582,7 +587,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
tl::info << "Reduction because of caller mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
tl::info << " -> " << tl::noendl;
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
@ -619,7 +624,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
tl::info << "Reduction because of callee mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
tl::info << " -> " << tl::noendl;
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
@ -646,7 +651,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
if (ed < uc->second.second) {
uc->second = std::make_pair (ca, ed);
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as new unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
}
}
@ -654,7 +659,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
} else {
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
unique_candidates.insert (std::make_pair (cb, std::make_pair (ca, ed)));
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
}
}
@ -679,12 +684,12 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Further refined candidates:";
dump_mapping (candidates, layout_a, layout_b);
}
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - iteration " << iteration << ": removal of uniquely mapped cells on B side";
}
@ -709,15 +714,15 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "After reduction of mapped cells on b side:";
dump_mapping (candidates, layout_a, layout_b);
}
}
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
int total = 0;
int not_mapped = 0;
@ -747,7 +752,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
// Resolve mapping according to string match
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - string mapping as last resort";
}
@ -784,7 +789,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
int total = 0;
int not_mapped = 0;
@ -795,7 +800,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
++total;
if (cand->second.size () == 0) {
if (tl::verbosity () >= 30) {
if (tl::verbosity () >= 50) {
tl::info << "Unmapped cell: " << layout_a.cell_name (cand->first);
}
++not_mapped;
@ -823,12 +828,12 @@ CellMapping::extract_unique (std::map <db::cell_index_type, std::vector<db::cell
{
if (cand->second.size () == 1) {
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << " (U) " << layout_a.cell_name (cand->first) << " -> " << layout_b.cell_name (cand->second.front ()) << " (" << cand->first << " -> " << cand->second.front () << ")";
}
unique_mapping.insert (std::make_pair (cand->second.front (), cand->first));
} else if (tl::verbosity () >= 30) {
} else if (tl::verbosity () >= 50) {
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
int n = 5;

View File

@ -64,6 +64,11 @@ public:
*/
void clear ();
/**
* @brief Swaps the cell mapping with another one
*/
void swap (CellMapping &other);
/**
* @brief Create a single cell mapping
*

View File

@ -22,7 +22,7 @@
#include "dbCellVariants.h"
#include "dbRegionUtils.h"
#include "dbPolygonTools.h"
#include "tlUtils.h"
namespace db

View File

@ -1725,7 +1725,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c
s->polygon (poly);
do {
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (edge_check.prepare_next_pass ());
}

View File

@ -154,17 +154,25 @@ DeepLayer::add_from (const DeepLayer &dl)
db::cell_index_type source_cell = dl.initial_cell ().cell_index ();
const db::Layout *source_layout = &dl.layout ();
db::CellMapping cm;
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
// create or reuse a layout mapping
// Actually copy the shapes
const db::CellMapping *cell_mapping = 0;
db::CellMapping cm;
if (store () == dl.store ()) {
cell_mapping = &const_cast<db::DeepShapeStore *> (mp_store.get ())->internal_cell_mapping (layout_index (), dl.layout_index ());
} else {
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
cell_mapping = &cm;
}
// actually copy the shapes
std::map<unsigned int, unsigned int> lm;
lm.insert (std::make_pair (dl.layer (), layer ()));
std::vector <db::cell_index_type> source_cells;
source_cells.push_back (source_cell);
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm);
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cell_mapping->table (), lm);
}
}
@ -418,11 +426,13 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
}
DeepShapeStore::DeepShapeStore ()
: m_keep_layouts (false)
{
++s_instance_count;
}
DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu)
: m_keep_layouts (false)
{
++s_instance_count;
@ -711,6 +721,16 @@ db::Layout &DeepShapeStore::layout (unsigned int n)
return m_layouts [n]->layout;
}
unsigned int DeepShapeStore::layout_index (const db::Layout *layout) const
{
for (std::vector<LayoutHolder *>::const_iterator i = m_layouts.begin (); i != m_layouts.end (); ++i) {
if (&(*i)->layout == layout) {
return (unsigned int) (i - m_layouts.begin ());
}
}
tl_assert (false);
}
size_t DeepShapeStore::instance_count ()
{
return s_instance_count;
@ -743,7 +763,7 @@ void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer)
}
if ((m_layouts[layout]->refs -= 1) <= 0) {
if ((m_layouts[layout]->refs -= 1) <= 0 && ! m_keep_layouts) {
delete m_layouts[layout];
m_layouts[layout] = 0;
clear_breakout_cells (layout);
@ -768,7 +788,6 @@ DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db:
}
db::Layout &layout = m_layouts[layout_index]->layout;
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
@ -792,7 +811,6 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive
m_layouts[layout_index] = new LayoutHolder (trans);
db::Layout &layout = m_layouts[layout_index]->layout;
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
@ -915,17 +933,9 @@ DeepLayer DeepShapeStore::create_text_layer (const db::RecursiveShapeIterator &s
return create_custom_layer (si, &refs, trans);
}
void
DeepShapeStore::invalidate_hier ()
{
m_delivery_mapping_cache.clear ();
}
void
DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_map)
{
invalidate_hier ();
db::HierarchyBuilder &builder = m_layouts [layout_index]->builder;
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) {
for (std::map<db::ICplxTrans, db::cell_index_type>::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
@ -934,6 +944,28 @@ DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::ce
}
}
const db::CellMapping &
DeepShapeStore::internal_cell_mapping (unsigned int into_layout_index, unsigned int from_layout_index)
{
db::Layout &into_layout = layout (into_layout_index);
db::cell_index_type into_cell = initial_cell (into_layout_index).cell_index ();
const db::Layout &source_layout = layout (from_layout_index);
db::cell_index_type source_cell = initial_cell (from_layout_index).cell_index ();
std::map<std::pair<unsigned int, unsigned int>, CellMappingWithGenerationIds>::iterator cm = m_internal_mapping_cache.find (std::make_pair (from_layout_index, into_layout_index));
if (cm == m_internal_mapping_cache.end () || ! cm->second.is_valid (into_layout, source_layout)) {
cm = m_internal_mapping_cache.insert (std::make_pair (std::make_pair (from_layout_index, into_layout_index), CellMappingWithGenerationIds ())).first;
cm->second.clear ();
cm->second.create_from_geometry_full (into_layout, into_cell, source_layout, source_cell);
cm->second.set_generation_ids (into_layout, source_layout);
}
return cm->second;
}
const db::CellMapping &
DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells, const std::set<db::cell_index_type> *included_cells)
{
@ -953,10 +985,11 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell);
std::map<DeliveryMappingCacheKey, db::CellMapping>::iterator cm = m_delivery_mapping_cache.find (key);
if (cm == m_delivery_mapping_cache.end ()) {
std::map<DeliveryMappingCacheKey, CellMappingWithGenerationIds>::iterator cm = m_delivery_mapping_cache.find (key);
if (cm == m_delivery_mapping_cache.end () || ! cm->second.is_valid (*into_layout, *source_layout)) {
cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first;
cm->second.clear ();
// collects the cell mappings we skip because they are variants (variant building or box variants)
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey> cm_skipped_variants;
@ -1045,6 +1078,8 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
into_layout->delete_cells (cells_to_delete);
}
cm->second.set_generation_ids (*into_layout, *source_layout);
}
return cm->second;

View File

@ -32,6 +32,7 @@
#include "dbLayout.h"
#include "dbRecursiveShapeIterator.h"
#include "dbHierarchyBuilder.h"
#include "dbCellMapping.h"
#include "gsiObject.h"
#include <set>
@ -291,6 +292,41 @@ struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy
}
};
/**
* @brief An object holding a cell mapping with the hierarchy generation Ids of the involved layouts
*/
class DB_PUBLIC CellMappingWithGenerationIds
: public db::CellMapping
{
public:
CellMappingWithGenerationIds ()
: db::CellMapping (), m_into_generation_id (0), m_from_generation_id (0)
{
// .. nothing yet ..
}
void swap (CellMappingWithGenerationIds &other)
{
db::CellMapping::swap (other);
std::swap (m_into_generation_id, other.m_into_generation_id);
std::swap (m_from_generation_id, other.m_from_generation_id);
}
bool is_valid (const db::Layout &into_layout, const db::Layout &from_layout) const
{
return into_layout.hier_generation_id () == m_into_generation_id && from_layout.hier_generation_id () == m_from_generation_id;
}
void set_generation_ids (const db::Layout &into_layout, const db::Layout &from_layout)
{
m_into_generation_id = into_layout.hier_generation_id ();
m_from_generation_id = from_layout.hier_generation_id ();
}
private:
size_t m_into_generation_id, m_from_generation_id;
};
/**
* @brief The "deep shape store" is a working model for the hierarchical ("deep") processor
*
@ -339,6 +375,25 @@ public:
*/
bool is_singular () const;
/**
* @brief Sets a value indicating whether to keep layouts
*
* If this value is set to true, layouts are not released when their reference count
* goes down to zero.
*/
void set_keep_layouts (bool f)
{
m_keep_layouts = f;
}
/**
* @brief Gets a value indicating whether to keep layouts
*/
bool keep_layouts () const
{
return m_keep_layouts;
}
/**
* @brief Creates a new layer from a flat region (or the region is made flat)
*
@ -479,6 +534,11 @@ public:
*/
const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells = 0, const std::set<db::cell_index_type> *included_cells = 0);
/**
* @brief Gets the cell mapping from one internal layout to another
*/
const db::CellMapping &internal_cell_mapping (unsigned int from_layout_index, unsigned int into_layout_index);
/**
* @brief Create cell variants from the given variant collector
*
@ -544,6 +604,11 @@ public:
*/
db::Cell &initial_cell (unsigned int n);
/**
* @brief Gets the layout index for a given internal layout
*/
unsigned int layout_index (const db::Layout *layout) const;
/**
* @brief Gets the singular layout (const version)
*
@ -730,7 +795,6 @@ private:
struct LayoutHolder;
void invalidate_hier ();
void add_ref (unsigned int layout, unsigned int layer);
void remove_ref (unsigned int layout, unsigned int layer);
@ -752,6 +816,7 @@ private:
layout_map_type m_layout_map;
DeepShapeStoreState m_state;
std::list<DeepShapeStoreState> m_state_stack;
bool m_keep_layouts;
tl::Mutex m_lock;
struct DeliveryMappingCacheKey
@ -780,7 +845,8 @@ private:
db::cell_index_type into_cell;
};
std::map<DeliveryMappingCacheKey, db::CellMapping> m_delivery_mapping_cache;
std::map<DeliveryMappingCacheKey, CellMappingWithGenerationIds> m_delivery_mapping_cache;
std::map<std::pair<unsigned int, unsigned int>, CellMappingWithGenerationIds> m_internal_mapping_cache;
};
template <class VarCollector>

View File

@ -1015,79 +1015,15 @@ private:
}
};
static bool
instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2, db::Coord dist)
{
// TODO: this algorithm is not in particular effective for identical arrays
const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ());
db::box_convert <db::CellInst, true> inst2_bc (*layout2, layer2);
std::unordered_set<db::ICplxTrans> relative_trans_seen;
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn1 = inst1->complex_trans (*n);
db::ICplxTrans tni1 = tn1.inverted ();
db::Box ibox1 = tn1 * cell1.bbox (layer1).enlarged (db::Vector (dist, dist));
if (! ibox1.empty ()) {
// TODO: in some cases, it may be possible to optimize this for arrays
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
if (inst1 == inst2 && *n == *k) {
// skip self-interactions - this is handled inside the cell
continue;
}
db::ICplxTrans tn2 = inst2->complex_trans (*k);
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
db::Box ibox2 = tn2 * cell2.bbox (layer2).enlarged (db::Vector (dist, dist));
db::ICplxTrans tn21 = tni1 * tn2;
if (! relative_trans_seen.insert (tn21).second) {
// this relative transformation was already seen
continue;
}
db::Box cbox = ibox1 & ibox2;
if (! cbox.empty ()) {
db::ICplxTrans tni2 = tn2.inverted ();
// not very strong, but already useful: the cells interact if there is a layer1 in cell1
// in the common box and a layer2 in the cell2 in the common box
// NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
// objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
// argument)
if (! db::RecursiveShapeIterator (*layout1, cell1, layer1, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
! db::RecursiveShapeIterator (*layout2, cell2, layer2, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ()) {
return true;
}
}
}
}
}
return false;
}
template <class T>
template <class TS, class TI, class TR>
struct interaction_registration_inst2inst
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, db::CellInstArray, unsigned int>
{
public:
typedef std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > interaction_value_type;
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, std::unordered_map<const db::CellInstArray *, interaction_value_type> *result)
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result), m_foreign (foreign)
{
// nothing yet ..
@ -1109,8 +1045,8 @@ public:
}
}
if (! ignore && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) {
(*mp_result) [inst1].first.insert (inst2);
if (! ignore) {
collect_instance_interactions (inst1, inst2);
}
}
@ -1120,66 +1056,176 @@ private:
const db::Layout *mp_subject_layout, *mp_intruder_layout;
unsigned int m_subject_layer, m_intruder_layer;
db::Coord m_dist;
std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *mp_result;
interactions_type *mp_result;
std::unordered_set<std::pair<unsigned int, unsigned int> > m_interactions;
bool m_foreign;
};
template <class T>
static bool
instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const T &ref, db::Coord dist)
{
const db::Cell &cell = layout->cell (inst->object ().cell_index ());
db::box_convert <db::CellInst, true> inst_bc (*layout, layer);
db::Box rbox = db::box_convert<T> () (ref);
void
collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
{
#if 0
printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
mp_subject_layout->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert<db::CellInst> (*mp_subject_layout)).to_string()).c_str(),
mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert<db::CellInst> (*mp_intruder_layout)).to_string()).c_str()); // @@@
#endif
// TODO: this algorithm is not in particular effective for identical arrays
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
db::box_convert <db::CellInst, true> inst2_bc (*mp_intruder_layout, m_intruder_layer);
db::ICplxTrans tn = inst->complex_trans (*n);
db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > > interactions_cache;
if (! cbox.empty ()) {
#if 0
printf("@@@ -> #%d\n", int(inst1->size()));
#endif
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tni = tn.inverted ();
db::ICplxTrans tn1 = inst1->complex_trans (*n);
db::ICplxTrans tni1 = tn1.inverted ();
db::Box ibox1 = tn1 * cell1.bbox (m_subject_layer).enlarged (db::Vector (m_dist, m_dist));
std::set<db::CellInstArray> *insts = 0;
if (! ibox1.empty ()) {
// TODO: in some cases, it may be possible to optimize this for arrays
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
if (inst1 == inst2 && *n == *k) {
// skip self-interactions - this is handled inside the cell
continue;
}
db::ICplxTrans tn2 = inst2->complex_trans (*k);
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box cbox = ibox1 & ibox2;
if (! cbox.empty () && cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1))) {
db::ICplxTrans tn21 = tni1 * tn2;
std::list<std::pair<db::cell_index_type, db::ICplxTrans> > *interactions = 0;
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > >::iterator ic = interactions_cache.find (tn21);
if (ic != interactions_cache.end ()) {
interactions = &ic->second;
} else {
interactions = &interactions_cache [tn21];
collect_intruder_tree_interactions (cell1, cell2, tni1, tn21, cbox, *interactions);
}
for (std::list<std::pair<db::cell_index_type, db::ICplxTrans> >::const_iterator i = interactions->begin (); i != interactions->end (); ++i) {
if (! insts) {
insts = & (*mp_result) [std::make_pair (cell1.cell_index (), tn1)].first;
}
insts->insert (db::CellInstArray (db::CellInst (i->first), i->second));
}
}
}
// not very strong, but already useful: the cells interact if there is a layer in cell
// in the common box
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
// dot edges. Instead safe-shrink the search box and use touching mode.
if (! db::RecursiveShapeIterator (*layout, cell, layer, safe_box_enlarged (tni * cbox, -1, -1), false).at_end ()) {
return true;
}
}
}
return false;
}
void collect_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > &interactions)
{
db::ICplxTrans tni2 = tn21.inverted () * tni1;
db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
template <class T>
if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
// we're overlapping with shapes from the intruder - do not recursive further
interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
return;
}
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (tbox2, db::box_convert<db::CellInst> (*mp_intruder_layout, m_intruder_layer)); ! ia.at_end (); ++ia) {
db::ICplxTrans it = i->complex_trans (*ia);
db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box ccbox = cbox & ibox2;
if (! ccbox.empty ()) {
collect_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox, interactions);
}
}
}
}
};
template <class TS, class TI, class TR>
struct interaction_registration_inst2shape
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, T, unsigned int>
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, TI, unsigned int>
{
public:
interaction_registration_inst2shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *result)
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst, unsigned int, const T *ref, unsigned int layer)
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
{
if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) {
(*mp_result) [inst].second [layer].insert (*ref);
}
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
}
private:
const db::Layout *mp_subject_layout;
db::Layout *mp_subject_layout;
unsigned int m_subject_layer;
db::Coord m_dist;
std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *mp_result;
interactions_type *mp_result;
void
collect_instance_shape_interactions (const db::CellInstArray *inst, unsigned int layer, const TI &ref, db::Coord dist)
{
const db::Cell &cell = mp_subject_layout->cell (inst->object ().cell_index ());
db::box_convert <db::CellInst, true> inst_bc (*mp_subject_layout, m_subject_layer);
db::Box rbox = db::box_convert<TI> () (ref);
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
db::ICplxTrans tn = inst->complex_trans (*n);
db::Box cbox = (tn * cell.bbox (m_subject_layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
if (! cbox.empty ()) {
db::ICplxTrans tni = tn.inverted ();
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> rt (mp_subject_layout, tni);
std::set<TI> *shapes = 0;
// not very strong, but already useful: the cells interact if there is a layer in cell
// in the common box
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
// dot edges. Instead safe-shrink the search box and use touching mode.
for (db::RecursiveShapeIterator s (*mp_subject_layout, cell, m_subject_layer, safe_box_enlarged (tni * cbox, -1, -1), false); !s.at_end (); ++s) {
if (! shapes) {
shapes = & (*mp_result) [std::make_pair (cell.cell_index (), tn)].second [layer];
}
shapes->insert (rt (ref));
}
}
}
}
};
}
@ -1436,6 +1482,9 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist) const
{
#if 0 // @@@
printf("@@@ --- compute_contexts (%s @ %s)\n", mp_subject_layout->cell_name (subject_cell->cell_index ()), subject_cell_inst.to_string().c_str()); fflush(stdout);
#endif
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
if (tl::verbosity () >= m_base_verbosity + 20) {
@ -1504,26 +1553,32 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
if (! subject_cell->begin ().at_end ()) {
typedef std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<TI> > > interaction_value_type;
std::unordered_map<const db::CellInstArray *, interaction_value_type> interactions;
// Key: single instance given by cell index and transformation
// Value the contexts for the child cell for this instance
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interactions_type interactions;
// insert dummy interactions to handle at least the child cell vs. itself
// - this is important so we will always handle the instances unless they are
// entirely empty in the subject layer
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty ()) {
interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ()));
db::cell_index_type ci = i->cell_inst ().object ().cell_index ();
for (db::CellInstArray::iterator n = i->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn = i->complex_trans (*n);
interactions.insert (std::make_pair (std::make_pair (ci, tn), interactions_value_type ()));
}
}
}
// TODO: can we shortcut this if interactions is empty?
// TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TI> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
unsigned int id = 0;
@ -1570,10 +1625,10 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
}
// TODO: can we shortcut this if interactions is empty?
// TODO: can we shortcut this if interactions is empty?
{
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
interaction_registration_inst2shape<TI> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
@ -1596,76 +1651,13 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
}
// this cache should reduce the effort of checking array vs. array
typedef std::pair<unsigned int, std::pair<db::cell_index_type, db::ICplxTrans> > effective_instance_cache_key_type;
typedef std::map<effective_instance_cache_key_type, std::pair<bool, db::CellInstArray> > effective_instance_cache_type;
effective_instance_cache_type effective_instance_cache;
// produce the tasks for computing the next-level interactions
for (typename interactions_type::iterator i = interactions.begin (); i != interactions.end (); ++i) {
for (typename std::unordered_map<const db::CellInstArray *, interaction_value_type>::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ());
for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn = i->first->complex_trans (*n);
db::ICplxTrans tni = tn.inverted ();
db::Box nbox = tn * subject_child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist));
if (! nbox.empty ()) {
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type intruders_below;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> rt (mp_subject_layout, tni);
for (typename std::map<unsigned int, std::unordered_set<TI> >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) {
std::set<TI> &out = intruders_below.second [pl->first];
for (typename std::unordered_set<TI>::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) {
if (nbox.overlaps (db::box_convert<TI> () (*p))) {
out.insert (rt (*p));
}
}
}
// TODO: in some cases, it may be possible to optimize this for arrays
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
unsigned int ail = contexts.actual_intruder_layer (*il);
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, ail);
for (std::unordered_set<const db::CellInstArray *>::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) {
for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
db::ICplxTrans tk = (*j)->complex_trans (*k);
// NOTE: no self-interactions
if (i->first != *j || tn != tk) {
// optimize the intruder instance so it will be as low as possible
effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
if (cached == effective_instance_cache.end ()) {
std::pair<bool, db::CellInstArray> ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
}
if (cached->second.first) {
intruders_below.first.insert (cached->second.second);
}
}
}
}
}
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? &subject_child_cell : 0);
issue_compute_contexts (contexts, cell_context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist);
}
}
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
}
@ -1673,63 +1665,6 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
}
/**
* @brief Returns a cell instance array suitable for adding as intruder
*
* The given intruder cell with the transformation ti2s - which transforms the intruder instance into
* the coordinate system of the subject cell - is analysed and either this instance or a sub-instance
* is chosen.
* Sub-instances are chosen if the intruder cell does not have shapes which interact with the subject
* cell and there is exactly one sub-instance interacting with the subject cell.
*/
template <class TS, class TI, class TR>
std::pair<bool, db::CellInstArray>
local_processor<TS, TI, TR>::effective_instance (unsigned int subject_layer, db::cell_index_type subject_cell_index, unsigned int intruder_layer, db::cell_index_type intruder_cell_index, const db::ICplxTrans &ti2s, db::Coord dist) const
{
db::Box bbox = safe_box_enlarged (mp_subject_layout->cell (subject_cell_index).bbox (subject_layer), dist - 1, dist - 1);
if (bbox.empty ()) {
// should not happen, but skip if it does
return std::make_pair (false, db::CellInstArray ());
}
db::Box ibbox = bbox.transformed (ti2s.inverted ());
const db::Cell &intruder_cell = mp_intruder_layout->cell (intruder_cell_index);
const db::Shapes &intruder_shapes = intruder_cell.shapes (intruder_layer);
if (! intruder_shapes.empty () && ! intruder_shapes.begin_touching (ibbox, db::ShapeIterator::All).at_end ()) {
return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
}
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, intruder_layer);
size_t ni = 0;
db::cell_index_type eff_cell_index = 0;
db::ICplxTrans eff_trans;
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (ibbox); ! i.at_end() && ni < 2; ++i) {
const db::CellInstArray &ci = i->cell_inst ();
db::Box cbox = mp_intruder_layout->cell (ci.object ().cell_index ()).bbox (intruder_layer);
for (db::CellInstArray::iterator k = ci.begin_touching (ibbox, inst_bcii); ! k.at_end () && ni < 2; ++k) {
db::ICplxTrans tk = ci.complex_trans (*k);
if (ibbox.overlaps (cbox.transformed (tk))) {
eff_trans = tk;
eff_cell_index = ci.object ().cell_index ();
++ni;
}
}
}
if (ni == 0) {
// should not happen, but skip if it does
return std::make_pair (false, db::CellInstArray ());
} else if (ni == 1) {
// one instance - dive down
return effective_instance (subject_layer, subject_cell_index, intruder_layer, eff_cell_index, ti2s * eff_trans, dist);
} else {
return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
}
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &output_layers) const

View File

@ -381,14 +381,16 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
} else {
db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox ();
db::cell_index_type inst_cell = inst.object ().cell_index ();
db::Box cell_bbox = iter->cell_bbox (inst_cell);
std::pair<bool, std::set<db::Box> > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region);
if (! clip_variant.first) {
return false;
}
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), clip_variant.second);
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second);
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell));
// for a new cell, create this instance
if (m_cell_stack.back ().first) {

View File

@ -346,6 +346,7 @@ ProxyContextInfo::serialize (std::vector<std::string> &strings)
Layout::Layout (db::Manager *manager)
: db::Object (manager),
mp_library (0),
m_cells_size (0),
m_invalid (0),
m_top_cells (0),
@ -362,6 +363,7 @@ Layout::Layout (db::Manager *manager)
Layout::Layout (bool editable, db::Manager *manager)
: db::Object (manager),
mp_library (0),
m_cells_size (0),
m_invalid (0),
m_top_cells (0),
@ -382,6 +384,7 @@ Layout::Layout (const db::Layout &layout)
gsi::ObjectBase (),
tl::Object (),
tl::UniqueId (),
mp_library (0),
m_cells_size (0),
m_invalid (0),
m_top_cells (0),
@ -2270,6 +2273,7 @@ Layout::register_pcell (const std::string &name, pcell_declaration_type *declara
declaration->m_id = id;
declaration->m_name = name;
declaration->mp_layout = this;
// marks this object being held by the layout
declaration->keep ();

View File

@ -528,12 +528,12 @@ public:
*
* The editable mode will be taken from db::default_editable_mode.
*/
Layout (db::Manager *manager = 0);
explicit Layout (db::Manager *manager = 0);
/**
* @brief Standard constructor which allows one to specify editable mode
*/
Layout (bool editable, db::Manager *manager = 0);
explicit Layout (bool editable, db::Manager *manager = 0);
/**
* @brief The copy ctor
@ -582,6 +582,22 @@ public:
return m_tech_name;
}
/**
* @brief Gets the library the layout lives in or NULL if the layout is not part of a library
*/
Library *library () const
{
return mp_library;
}
/**
* @brief Sets the library pointer
*/
void set_library (db::Library *library)
{
mp_library = library;
}
/**
* @brief Gets the technology object the layout is associated with or null if no valid technology is associated
*/
@ -1843,6 +1859,7 @@ protected:
private:
enum LayerState { Normal, Free, Special };
db::Library *mp_library;
cell_list m_cells;
size_t m_cells_size;
cell_ptr_vector m_cell_ptrs;

View File

@ -29,13 +29,13 @@ namespace db
{
LayoutStateModel::LayoutStateModel (bool busy)
: m_hier_dirty (false), m_all_bboxes_dirty (false), m_busy (busy)
: m_hier_dirty (false), m_hier_generation_id (0), m_all_bboxes_dirty (false), m_busy (busy)
{
// .. nothing yet ..
}
LayoutStateModel::LayoutStateModel (const LayoutStateModel &d)
: m_hier_dirty (d.m_hier_dirty), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
: m_hier_dirty (d.m_hier_dirty), m_hier_generation_id (d.m_hier_generation_id), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
{
// .. nothing yet ..
}
@ -44,6 +44,7 @@ LayoutStateModel &
LayoutStateModel::operator= (const LayoutStateModel &d)
{
m_hier_dirty = d.m_hier_dirty;
m_hier_generation_id = d.m_hier_generation_id;
m_bboxes_dirty = d.m_bboxes_dirty;
m_all_bboxes_dirty = d.m_all_bboxes_dirty;
m_busy = d.m_busy;

View File

@ -85,6 +85,7 @@ public:
*/
void invalidate_hier ()
{
++m_hier_generation_id;
if (! m_hier_dirty || m_busy) {
do_invalidate_hier (); // must be called before the hierarchy is invalidated (stopping of redraw thread requires this)
m_hier_dirty = true;
@ -127,6 +128,17 @@ public:
return m_hier_dirty;
}
/**
* @brief Gets the hierarchy generation ID
*
* The hierarchy generation ID is a number which is incremented on every hierarchy
* change.
*/
size_t hier_generation_id () const
{
return m_hier_generation_id;
}
/**
* @brief The "dirty bounding box" attribute
*
@ -195,6 +207,7 @@ public:
private:
bool m_hier_dirty;
size_t m_hier_generation_id;
std::vector<bool> m_bboxes_dirty;
bool m_all_bboxes_dirty;
bool m_busy;

View File

@ -23,7 +23,7 @@
#include "dbLayoutUtils.h"
#include "dbCellVariants.h"
#include "dbRegionUtils.h"
#include "dbPolygonTools.h"
#include "tlProgress.h"
namespace db

View File

@ -32,13 +32,13 @@ namespace db
Library::Library()
: m_id (0), m_layout (true)
{
// .. nothing yet ..
m_layout.set_library (this);
}
Library::Library(const Library &d)
: gsi::ObjectBase (), tl::Object (), m_name (d.m_name), m_description (d.m_description), m_id (0), m_layout (d.m_layout)
{
// .. nothing yet ..
m_layout.set_library (this);
}
Library::~Library ()

View File

@ -42,14 +42,18 @@ LibraryProxy::LibraryProxy (db::cell_index_type ci, db::Layout &layout, lib_id_t
LibraryProxy::~LibraryProxy ()
{
if (layout ()) {
layout ()->unregister_lib_proxy (this);
}
if (db::LibraryManager::initialized ()) {
db::Library *lib = db::LibraryManager::instance ().lib (m_lib_id);
if (lib) {
lib->unregister_proxy (this, layout ());
try {
if (layout ()) {
layout ()->unregister_lib_proxy (this);
}
if (db::LibraryManager::initialized ()) {
db::Library *lib = db::LibraryManager::instance ().lib (m_lib_id);
if (lib) {
lib->unregister_proxy (this, layout ());
}
}
} catch (...) {
// ignore exceptions (may happen due to broken PCell instantiations)
}
}

View File

@ -694,13 +694,16 @@ void NetlistDeviceExtractorBJT3Transistor::extract_devices (const std::vector<db
rcollector = rbase;
} else if ((rbase - rcollector2base).empty ()) {
// vertical transistor: collector entirely covers base -> collector terminal is collector outside base
rcollector = rcollector2base - rbase;
rcollector = rcollectors.selected_interacting (rbase) - rbase;
} else {
// lateral transistor: base is reduced by collector area
rcollector = rcollector2base;
rbase -= rcollector2base;
}
// TODO: rbase - rcollector2base above could basically split a base region into different
// subregions potentially forming one transistor each.
// this is what is the true base contact
rbase -= remitter2base;

View File

@ -28,7 +28,7 @@ namespace db
{
PCellDeclaration::PCellDeclaration ()
: m_ref_count (0), m_id (0), m_has_parameter_declarations (false)
: m_ref_count (0), m_id (0), mp_layout (0), m_has_parameter_declarations (false)
{
// .. nothing yet ..
}

View File

@ -429,6 +429,14 @@ public:
return db::Trans ();
}
/**
* @brief Gets the Layout object the PCell is registered inside or NULL if it is not registered
*/
db::Layout *layout () const
{
return mp_layout;
}
/**
* @brief Add a reference to this object
*
@ -518,6 +526,7 @@ private:
int m_ref_count;
pcell_id_type m_id;
std::string m_name;
db::Layout *mp_layout;
mutable bool m_has_parameter_declarations;
mutable std::vector<PCellParameterDeclaration> m_parameter_declarations;

View File

@ -2827,5 +2827,93 @@ decompose_trapezoids (const db::SimplePolygon &sp, TrapezoidDecompositionMode mo
}
}
// -------------------------------------------------------------------------------------
// Polygon snapping
db::Polygon
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Polygon
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Vector
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
{
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
return db::Vector (db::Coord (x), db::Coord (y));
}
}

View File

@ -760,6 +760,37 @@ void DB_PUBLIC decompose_trapezoids (const db::Polygon &p, TrapezoidDecompositio
*/
void DB_PUBLIC decompose_trapezoids (const db::SimplePolygon &p, TrapezoidDecompositionMode mode, SimplePolygonSink &sink);
template <class C>
static inline C snap_to_grid (C c, C g)
{
// This form of snapping always snaps g/2 to right/top.
if (c < 0) {
c = -g * ((-c + (g - 1) / 2) / g);
} else {
c = g * ((c + g / 2) / g);
}
return c;
}
/**
* @brief Snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
*/
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a vector to the given grid
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
}
#endif

View File

@ -646,7 +646,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const
// skip insts outside the complex region
if (! m_inst.at_end ()) {
if (! is_outside_complex_region (m_inst->bbox ())) {
if (! is_outside_complex_region (m_inst->bbox (m_box_convert))) {
break;
} else {
++m_inst;
@ -775,7 +775,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
// compute the region inside the new cell
if (new_region != m_region) {
new_region = m_trans.inverted () * m_region;
new_region &= cell ()->bbox ();
new_region &= cell_bbox (cell_index ());
}
m_local_region_stack.push_back (new_region);
@ -969,7 +969,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) * m_box_convert (m_inst->cell_inst ().object ());
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
if (! is_outside_complex_region (ia_box)) {
break;
} else {

View File

@ -665,6 +665,16 @@ public:
return reinterpret_cast<const cell_type *> (c - (c & size_t (3)));
}
/**
* @brief Gets the current cell's bounding box
*
* The returned box is limited to the selected layer if applicable.
*/
box_type cell_bbox (db::cell_index_type cell_index) const
{
return m_box_convert (db::CellInst (cell_index));
}
/**
* @brief Increments the iterator (operator version)
*/

View File

@ -0,0 +1,638 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 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 "dbRegionCheckUtils.h"
#include "dbPolygonTools.h"
#include "dbEdgeBoolean.h"
#include "tlSelect.h"
namespace db
{
// -------------------------------------------------------------------------------------
// Edge2EdgeCheckBase implementation
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
m_first_pseudo (std::numeric_limits<size_t>::max ()),
m_with_shielding (with_shielding),
m_symmetric_edges (symmetric_edges),
m_has_edge_pair_output (true),
m_has_negative_edge_output (false),
m_pass (0)
{
m_distance = check.distance ();
}
bool
Edge2EdgeCheckBase::prepare_next_pass ()
{
++m_pass;
if (m_pass == 1) {
m_first_pseudo = m_ep.size ();
if (m_with_shielding && ! m_ep.empty ()) {
m_ep_discarded.resize (m_ep.size (), false);
// second pass:
return true;
} else if (m_has_negative_edge_output) {
// second pass:
return true;
}
}
if (! m_ep.empty () && m_has_edge_pair_output) {
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
bool use_result = true;
if (d != m_ep_discarded.end ()) {
use_result = ! *d;
++d;
}
if (use_result) {
put (*ep, *i);
}
++ep;
++i;
}
}
return false;
}
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
{
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
if (ip1.first && ip2.first) {
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
} else {
return false;
}
}
void
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
{
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
std::pair<db::Edge, size_t> k (*o, p);
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
bool fully_removed = false;
bool any = false;
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
any = true;
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
}
}
if (! any) {
put_negative (*o, (int) p);
} else if (! fully_removed) {
std::set<db::Edge> partial_edges;
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
ec.add (o, 0);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
}
}
ec.finish ();
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
put_negative (*e, (int) p);
}
}
}
}
bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
{
if (m_pass == 1) {
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
scanner.insert (&e->first, e->second);
}
return ! m_pseudo_edges.empty ();
} else {
return false;
}
}
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
{
if (p1 == p2) {
if (requires_different_polygons) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges from the same polygon are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate intra-polygon markers.
return false;
}
}
if (((p1 ^ p2) & size_t (1)) == 0) {
if (requires_different_layers) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges on the same layer are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate inter-polygon markers between them.
return false;
}
}
return true;
}
void
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
if (m_pass == 0) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
ep.set_symmetric (m_symmetric_edges);
// found a violation: store inside the local buffer for now. In the second
// pass we will eliminate those which are shielded completely (with shielding)
// and/or compute the negative edges.
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2);
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
if (m_has_negative_edge_output) {
bool antiparallel = (mp_check->relation () == WidthRelation || mp_check->relation () == SpaceRelation);
// pseudo1 and pseudo2 are the connecting edges of the edge pairs. Together with the
// original edges they form a quadrangle.
db::Edge pseudo1 (ep.first ().p1 (), antiparallel ? ep.second ().p2 () : ep.second ().p1 ());
db::Edge pseudo2 (antiparallel ? ep.second ().p1 () : ep.second ().p2 (), ep.first ().p2 ());
m_pseudo_edges.insert (std::make_pair (pseudo1, p1));
m_pseudo_edges.insert (std::make_pair (pseudo2, p1));
if (p1 != p2) {
m_pseudo_edges.insert (std::make_pair (pseudo1, p2));
m_pseudo_edges.insert (std::make_pair (pseudo2, p2));
}
}
}
}
} else {
// set the discarded flags for shielded output
if (m_with_shielding) {
// a simple (complete) shielding implementation which is based on the
// assumption that shielding is relevant as soon as a foreign edge cuts through
// both of the edge pair's connecting edges.
// TODO: this implementation does not take into account the nature of the
// EdgePair - because of "whole_edge" it may not reflect the part actually
// violating the distance.
std::vector<size_t> n1, n2;
for (unsigned int p = 0; p < 2; ++p) {
std::pair<db::Edge, size_t> k (*o1, p1);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
n1.push_back (n);
}
}
std::sort (n1.begin (), n1.end ());
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
for (unsigned int p = 0; p < 2; ++p) {
std::vector<size_t> nn;
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::EdgePair ep = m_ep [*i].normalized ();
if (shields (ep, *o2)) {
m_ep_discarded [*i] = true;
}
}
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
}
// for negative output edges are cancelled by short interactions perpendicular to them
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
// negative case this means we cancel a real edge.
if (m_has_negative_edge_output &&
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
}
}
}
}
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::requires_different_layers () const
{
return m_requires_different_layers;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
{
m_requires_different_layers = f;
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::different_polygons () const
{
return m_different_polygons;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_different_polygons (bool f)
{
m_different_polygons = f;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type
Edge2EdgeCheckBase::distance () const
{
return m_distance;
}
// -------------------------------------------------------------------------------------
// Poly2PolyCheckBase implementation
template <class PolygonType>
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
: mp_output (& output)
{
// .. nothing yet ..
}
template <class PolygonType>
poly2poly_check<PolygonType>::poly2poly_check ()
: mp_output (0)
{
// .. nothing yet ..
}
static size_t vertices (const db::Polygon &p)
{
return p.vertices ();
}
static size_t vertices (const db::PolygonRef &p)
{
return p.obj ().vertices ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::single (const PolygonType &o, size_t p)
{
tl_assert (! mp_output->requires_different_layers () && ! mp_output->different_polygons ());
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
m_scanner.clear ();
m_scanner.reserve (vertices (o));
m_edge_heap.clear ();
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
mp_output->feed_pseudo_edges (m_scanner);
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::connect (Edge2EdgeCheckBase &output)
{
mp_output = &output;
clear ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::clear ()
{
m_scanner.clear ();
m_edge_heap.clear ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
{
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
}
// TODO: move to generic header
static bool interact (const db::Box &box, const db::Edge &e)
{
if (! e.bbox ().touches (box)) {
return false;
} else if (e.is_ortho ()) {
return true;
} else {
return e.clipped (box).first;
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p, const poly2poly_check<PolygonType>::box_type &box)
{
if (box.empty ()) {
return;
}
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
if (interact (box, *e)) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::process ()
{
mp_output->feed_pseudo_edges (m_scanner);
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
}
// explicit instantiations
template class poly2poly_check<db::Polygon>;
template class poly2poly_check<db::PolygonRef>;
// -------------------------------------------------------------------------------------
// RegionToEdgeInteractionFilterBase implementation
template <class PolygonType, class EdgeType, class OutputType>
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
{
const OutputType *o = 0;
tl::select (o, p, e);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an edge interact if the edge is either inside completely
// of at least one edge of the polygon intersects with the edge
bool interacts = false;
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
interacts = true;
} else {
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
if ((*pe).intersect (*e)) {
interacts = true;
}
}
}
if (interacts) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
// -------------------------------------------------------------------------------------
// RegionToTextInteractionFilterBase implementation
template <class PolygonType, class TextType, class OutputType>
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
{
const OutputType *o = 0;
tl::select (o, p, t);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an text interact if the text is either inside completely
// of at least one text of the polygon intersects with the text
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
}

View File

@ -0,0 +1,427 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 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_dbRegionCheckUtils
#define HDR_dbRegionCheckUtils
#include "dbCommon.h"
#include "dbCellVariants.h"
#include "dbBoxScanner.h"
#include "dbEdgePairRelations.h"
namespace db {
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
class DB_PUBLIC Edge2EdgeCheckBase
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
/**
* @brief Call this to initiate a new pass until the return value is false
*/
bool prepare_next_pass ();
/**
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (required for negative edge output - cancellation of perpendicular edges)
*/
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void finish (const Edge *o, size_t);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool requires_different_layers () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_requires_different_layers (bool f);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool different_polygons () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_different_polygons (bool f);
/**
* @brief Sets a flag indicating that this class wants negative edge output
*/
void set_has_negative_edge_output (bool f)
{
m_has_negative_edge_output = f;
}
/**
* @brief Gets a flag indicating that this class wants negative edge output
*/
bool has_negative_edge_output () const
{
return m_has_negative_edge_output;
}
/**
* @brief Sets a flag indicating that this class wants normal edge pair output
*/
void set_has_edge_pair_output (bool f)
{
m_has_edge_pair_output = f;
}
/**
* @brief Gets a flag indicating that this class wants normal edge pair output
*/
bool has_edge_pair_output () const
{
return m_has_edge_pair_output;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type distance () const;
protected:
/**
* @brief Normal edge pair output (violations)
*/
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
/**
* @brief Negative edge output
*/
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
private:
const EdgeRelationFilter *mp_check;
bool m_requires_different_layers;
bool m_different_polygons;
EdgeRelationFilter::distance_type m_distance;
std::vector<db::EdgePair> m_ep;
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
size_t m_first_pseudo;
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
bool m_with_shielding;
bool m_symmetric_edges;
bool m_has_edge_pair_output;
bool m_has_negative_edge_output;
unsigned int m_pass;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check
: public Edge2EdgeCheckBase
{
public:
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
{
// .. nothing yet ..
}
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
{
// .. nothing yet ..
}
protected:
void put (const db::EdgePair &edge, bool inter_polygon) const
{
if (! inter_polygon || ! mp_output_intra) {
mp_output_inter->insert (edge);
} else {
mp_output_intra->insert (edge);
}
}
private:
Output *mp_output_inter;
Output *mp_output_intra;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version allows delivery of the negative edges.
*/
template <class Output, class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
: public edge2edge_check<Output>
{
public:
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has only negative edge output.
*/
template <class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
: public Edge2EdgeCheckBase
{
public:
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
set_has_negative_edge_output (true);
set_has_edge_pair_output (false);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has positive or negative output. Negative output is mapped to edge pairs
* as well.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
: public edge2edge_check<Output>
{
public:
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
}
#if 0
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
// the outer edges to not represent the actual contour.
if (layer == 1) {
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
}
#endif
}
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
template <class PolygonType>
class DB_PUBLIC poly2poly_check
{
public:
typedef typename PolygonType::box_type box_type;
poly2poly_check (Edge2EdgeCheckBase &output);
poly2poly_check ();
void clear ();
void single (const PolygonType&o, size_t p);
void connect (Edge2EdgeCheckBase &output);
void enter (const PolygonType &o, size_t p);
void enter (const PolygonType &o, size_t p, const box_type &search_box);
void process ();
private:
db::Edge2EdgeCheckBase *mp_output;
db::box_scanner<db::Edge, size_t> m_scanner;
std::list<db::Edge> m_edge_heap;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputType>
class DB_PUBLIC region_to_edge_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
{
public:
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
{
public:
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &res) const
{
mp_output->insert (res);
}
private:
OutputContainer *mp_output;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputType>
class DB_PUBLIC region_to_text_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
{
public:
region_to_text_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const TextType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
{
public:
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &poly) const
{
mp_output->insert (poly);
}
private:
OutputContainer *mp_output;
};
} // namespace db
#endif

View File

@ -21,8 +21,8 @@
*/
#include "dbRegionUtils.h"
#include "dbRegionLocalOperations.h"
#include "dbRegionUtils.h"
#include "dbLocalOperationUtils.h"
#include "dbHierProcessor.h"
@ -209,7 +209,6 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
poly2poly_check<TS> poly_check (edge_check);
std::list<TS> heap;
db::box_scanner<TS, size_t> scanner;
std::unordered_set<TI> polygons;
std::set<unsigned int> ids;
@ -219,12 +218,40 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
}
}
bool take_all = edge_check.has_negative_edge_output () || interactions.num_intruders () == 0;
db::Box common_box;
if (! take_all) {
db::Vector e (edge_check.distance (), edge_check.distance ());
db::Box subject_box;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
subject_box += db::box_convert<TS> () (interactions.subject_shape (i->first));
}
if (edge_check.requires_different_layers ()) {
db::Box intruder_box;
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
intruder_box += db::box_convert<TI> () (interactions.intruder_shape (*id).second);
}
common_box = subject_box.enlarged (e) & intruder_box.enlarged (e);
} else {
common_box = subject_box.enlarged (e);
}
}
if (m_has_other) {
size_t n = 0;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
const TS &subject = interactions.subject_shape (i->first);
scanner.insert (push_polygon_to_heap (layout, subject, heap), n);
if (! take_all) {
poly_check.enter (subject, n, common_box);
} else {
poly_check.enter (subject, n);
}
n += 2;
}
@ -262,7 +289,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
n = 1;
for (typename std::unordered_set<TI>::const_iterator o = polygons.begin (); o != polygons.end (); ++o) {
scanner.insert (push_polygon_to_heap (layout, *o, heap), n);
if (! take_all) {
poly_check.enter (*o, n, common_box);
} else {
poly_check.enter (*o, n);
}
n += 2;
}
@ -270,7 +301,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
n = 1;
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
scanner.insert (push_polygon_to_heap (layout, interactions.intruder_shape (*id).second, heap), n);
if (! take_all) {
poly_check.enter (interactions.intruder_shape (*id).second, n, common_box);
} else {
poly_check.enter (interactions.intruder_shape (*id).second, n);
}
n += 2;
}
@ -286,7 +321,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
// we can't directly insert because TS may be != TI
const TS &ts = interactions.subject_shape (i->first);
insert_into_hash (polygons, ts);
scanner.insert (push_polygon_to_heap (layout, ts, heap), n);
if (! take_all) {
poly_check.enter (ts, n, common_box);
} else {
poly_check.enter (ts, n);
}
n += 2;
}
@ -295,7 +334,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
const TI &ti = interactions.intruder_shape (*id).second;
if (polygons.find (ti) == polygons.end ()) {
scanner.insert (push_polygon_to_heap (layout, ti, heap), n);
if (! take_all) {
poly_check.enter (ti, n, common_box);
} else {
poly_check.enter (ti, n);
}
n += 2;
}
}
@ -303,7 +346,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
}
do {
scanner.process (poly_check, m_check.distance (), db::box_convert<TS> ());
poly_check.process ();
} while (edge_check.prepare_next_pass ());
// detect and remove parts of the result which have or do not have results "opposite"

View File

@ -28,6 +28,7 @@
#include "dbEdgePairRelations.h"
#include "dbLocalOperation.h"
#include "dbEdgeProcessor.h"
#include "dbRegionCheckUtils.h"
#include <vector>
#include <unordered_set>

View File

@ -22,6 +22,7 @@
#include "dbRegionUtils.h"
#include "dbRegionCheckUtils.h"
#include "dbPolygonTools.h"
#include "dbEdgeBoolean.h"
#include "tlSelect.h"
@ -29,496 +30,6 @@
namespace db
{
// -------------------------------------------------------------------------------------
// Edge2EdgeCheckBase implementation
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
m_first_pseudo (std::numeric_limits<size_t>::max ()),
m_with_shielding (with_shielding),
m_symmetric_edges (symmetric_edges),
m_has_edge_pair_output (true),
m_has_negative_edge_output (false),
m_pass (0)
{
m_distance = check.distance ();
}
bool
Edge2EdgeCheckBase::prepare_next_pass ()
{
++m_pass;
if (m_pass == 1) {
m_first_pseudo = m_ep.size ();
if (m_with_shielding && ! m_ep.empty ()) {
m_ep_discarded.resize (m_ep.size (), false);
// second pass:
return true;
} else if (m_has_negative_edge_output) {
// second pass:
return true;
}
}
if (! m_ep.empty () && m_has_edge_pair_output) {
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
bool use_result = true;
if (d != m_ep_discarded.end ()) {
use_result = ! *d;
++d;
}
if (use_result) {
put (*ep, *i);
}
++ep;
++i;
}
}
return false;
}
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
{
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
if (ip1.first && ip2.first) {
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
} else {
return false;
}
}
void
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
{
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
std::pair<db::Edge, size_t> k (*o, p);
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
bool fully_removed = false;
bool any = false;
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
any = true;
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
}
}
if (! any) {
put_negative (*o, (int) p);
} else if (! fully_removed) {
std::set<db::Edge> partial_edges;
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
ec.add (o, 0);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
}
}
ec.finish ();
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
put_negative (*e, (int) p);
}
}
}
}
bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
{
if (m_pass == 1) {
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
scanner.insert (&e->first, e->second);
}
return ! m_pseudo_edges.empty ();
} else {
return false;
}
}
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
{
if (p1 == p2) {
if (requires_different_polygons) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges from the same polygon are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate intra-polygon markers.
return false;
}
}
if (((p1 ^ p2) & size_t (1)) == 0) {
if (requires_different_layers) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges on the same layer are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate inter-polygon markers between them.
return false;
}
}
return true;
}
void
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
if (m_pass == 0) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
ep.set_symmetric (m_symmetric_edges);
// found a violation: store inside the local buffer for now. In the second
// pass we will eliminate those which are shielded completely (with shielding)
// and/or compute the negative edges.
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2);
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
if (m_has_negative_edge_output) {
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1));
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1));
if (p1 != p2) {
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2));
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2));
}
}
}
}
} else {
// set the discarded flags for shielded output
if (m_with_shielding) {
// a simple (complete) shielding implementation which is based on the
// assumption that shielding is relevant as soon as a foreign edge cuts through
// both of the edge pair's connecting edges.
// TODO: this implementation does not take into account the nature of the
// EdgePair - because of "whole_edge" it may not reflect the part actually
// violating the distance.
std::vector<size_t> n1, n2;
for (unsigned int p = 0; p < 2; ++p) {
std::pair<db::Edge, size_t> k (*o1, p1);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
n1.push_back (n);
}
}
std::sort (n1.begin (), n1.end ());
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
for (unsigned int p = 0; p < 2; ++p) {
std::vector<size_t> nn;
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::EdgePair ep = m_ep [*i].normalized ();
if (shields (ep, *o2)) {
m_ep_discarded [*i] = true;
}
}
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
}
// for negative output edges are cancelled by short interactions perpendicular to them
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
// negative case this means we cancel a real edge.
if (m_has_negative_edge_output &&
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
}
}
}
}
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::requires_different_layers () const
{
return m_requires_different_layers;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
{
m_requires_different_layers = f;
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::different_polygons () const
{
return m_different_polygons;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_different_polygons (bool f)
{
m_different_polygons = f;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type
Edge2EdgeCheckBase::distance () const
{
return m_distance;
}
// -------------------------------------------------------------------------------------
// Poly2PolyCheckBase implementation
template <class PolygonType>
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
: mp_output (& output)
{
// .. nothing yet ..
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::finish (const PolygonType *o, size_t p)
{
enter (*o, p);
}
static size_t vertices (const db::Polygon &p)
{
return p.vertices ();
}
static size_t vertices (const db::PolygonRef &p)
{
return p.obj ().vertices ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
{
if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) {
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
m_scanner.clear ();
m_scanner.reserve (vertices (o));
m_edges.clear ();
m_edges.reserve (vertices (o));
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p);
}
mp_output->feed_pseudo_edges (m_scanner);
tl_assert (m_edges.size () == vertices (o));
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2)
{
enter (*o1, p1, *o2, p2);
}
static bool interact (const db::Box &box, const db::Edge &e)
{
if (! e.bbox ().touches (box)) {
return false;
} else if (e.is_ortho ()) {
return true;
} else {
return e.clipped (box).first;
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2)
{
if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
bool take_all = mp_output->has_negative_edge_output ();
db::Box common_box;
if (! take_all) {
db::Vector e (mp_output->distance (), mp_output->distance ());
common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e);
if (common_box.empty ()) {
return;
}
}
m_scanner.clear ();
m_scanner.reserve (vertices (o1) + vertices (o2));
m_edges.clear ();
m_edges.reserve (vertices (o1) + vertices (o2));
bool any_o1 = false, any_o2 = false;
for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) {
if (take_all || interact (common_box, *e)) {
m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p1);
any_o1 = true;
}
}
for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) {
if (take_all || interact (common_box, *e)) {
m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p2);
any_o2 = true;
}
}
if (! take_all && (! any_o1 || ! any_o2)) {
return;
}
mp_output->feed_pseudo_edges (m_scanner);
// temporarily disable intra-polygon check in that step .. we do that later in finish()
// if required (#650).
bool no_intra = mp_output->different_polygons ();
mp_output->set_different_polygons (true);
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
mp_output->set_different_polygons (no_intra);
}
}
// explicit instantiations
template class poly2poly_check<db::Polygon>;
template class poly2poly_check<db::PolygonRef>;
// -------------------------------------------------------------------------------------
// RegionPerimeterFilter implementation
@ -859,7 +370,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector<db::EdgePai
poly2poly_check<db::Polygon> poly_check (edge_check);
do {
poly_check.enter (polygon, 0);
poly_check.single (polygon, 0);
} while (edge_check.prepare_next_pass ());
res.insert (res.end (), result.begin (), result.end ());
@ -967,219 +478,4 @@ HullExtractionProcessor::process (const db::Polygon &poly, std::vector<db::Polyg
res.back ().assign_hull (poly.begin_hull (), poly.end_hull ());
}
// -------------------------------------------------------------------------------------
// RegionToEdgeInteractionFilterBase implementation
template <class PolygonType, class EdgeType, class OutputType>
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
{
const OutputType *o = 0;
tl::select (o, p, e);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an edge interact if the edge is either inside completely
// of at least one edge of the polygon intersects with the edge
bool interacts = false;
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
interacts = true;
} else {
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
if ((*pe).intersect (*e)) {
interacts = true;
}
}
}
if (interacts) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
// -------------------------------------------------------------------------------------
// RegionToTextInteractionFilterBase implementation
template <class PolygonType, class TextType, class OutputType>
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
{
const OutputType *o = 0;
tl::select (o, p, t);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an text interact if the text is either inside completely
// of at least one text of the polygon intersects with the text
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
// -------------------------------------------------------------------------------------
// Polygon snapping
db::Polygon
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Polygon
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Vector
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
{
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
return db::Vector (db::Coord (x), db::Coord (y));
}
}

View File

@ -583,298 +583,6 @@ public:
virtual bool result_must_not_be_merged () const { return false; }
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
class DB_PUBLIC Edge2EdgeCheckBase
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
/**
* @brief Call this to initiate a new pass until the return value is false
*/
bool prepare_next_pass ();
/**
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (required for negative edge output - cancellation of perpendicular edges)
*/
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void finish (const Edge *o, size_t);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool requires_different_layers () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_requires_different_layers (bool f);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool different_polygons () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_different_polygons (bool f);
/**
* @brief Sets a flag indicating that this class wants negative edge output
*/
void set_has_negative_edge_output (bool f)
{
m_has_negative_edge_output = f;
}
/**
* @brief Gets a flag indicating that this class wants negative edge output
*/
bool has_negative_edge_output () const
{
return m_has_negative_edge_output;
}
/**
* @brief Sets a flag indicating that this class wants normal edge pair output
*/
void set_has_edge_pair_output (bool f)
{
m_has_edge_pair_output = f;
}
/**
* @brief Gets a flag indicating that this class wants normal edge pair output
*/
bool has_edge_pair_output () const
{
return m_has_edge_pair_output;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type distance () const;
protected:
/**
* @brief Normal edge pair output (violations)
*/
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
/**
* @brief Negative edge output
*/
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
private:
const EdgeRelationFilter *mp_check;
bool m_requires_different_layers;
bool m_different_polygons;
EdgeRelationFilter::distance_type m_distance;
std::vector<db::EdgePair> m_ep;
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
size_t m_first_pseudo;
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
bool m_with_shielding;
bool m_symmetric_edges;
bool m_has_edge_pair_output;
bool m_has_negative_edge_output;
unsigned int m_pass;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check
: public Edge2EdgeCheckBase
{
public:
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
{
// .. nothing yet ..
}
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
{
// .. nothing yet ..
}
protected:
void put (const db::EdgePair &edge, bool inter_polygon) const
{
if (! inter_polygon || ! mp_output_intra) {
mp_output_inter->insert (edge);
} else {
mp_output_intra->insert (edge);
}
}
private:
Output *mp_output_inter;
Output *mp_output_intra;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version allows delivery of the negative edges.
*/
template <class Output, class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
: public edge2edge_check<Output>
{
public:
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has only negative edge output.
*/
template <class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
: public Edge2EdgeCheckBase
{
public:
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
set_has_negative_edge_output (true);
set_has_edge_pair_output (false);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has positive or negative output. Negative output is mapped to edge pairs
* as well.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
: public edge2edge_check<Output>
{
public:
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
}
#if 0
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
// the outer edges to not represent the actual contour.
if (layer == 1) {
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
}
#endif
}
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
template <class PolygonType>
class DB_PUBLIC poly2poly_check
: public db::box_scanner_receiver<PolygonType, size_t>
{
public:
poly2poly_check (Edge2EdgeCheckBase &output);
void finish (const PolygonType *o, size_t p);
void enter (const PolygonType&o, size_t p);
void add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2);
void enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2);
private:
db::Edge2EdgeCheckBase *mp_output;
db::box_scanner<db::Edge, size_t> m_scanner;
std::vector<db::Edge> m_edges;
};
/**
* @brief A class wrapping the single-polygon checks into a polygon-to-edge pair processor
*/
@ -892,129 +600,6 @@ private:
db::RegionCheckOptions m_options;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputType>
class DB_PUBLIC region_to_edge_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
{
public:
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
{
public:
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &res) const
{
mp_output->insert (res);
}
private:
OutputContainer *mp_output;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputType>
class DB_PUBLIC region_to_text_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
{
public:
region_to_text_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const TextType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
{
public:
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &poly) const
{
mp_output->insert (poly);
}
private:
OutputContainer *mp_output;
};
template <class C>
static inline C snap_to_grid (C c, C g)
{
// This form of snapping always snaps g/2 to right/top.
if (c < 0) {
c = -g * ((-c + (g - 1) / 2) / g);
} else {
c = g * ((c + g / 2) / g);
}
return c;
}
/**
* @brief Snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
*/
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a vector to the given grid
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
} // namespace db
#endif

View File

@ -2011,6 +2011,15 @@ public:
m_mag = m_mag < 0.0 ? -m : m;
}
/**
* @brief Returns a value indicating whether the transformation is a complex one
* The transformation can safely be converted to a simple transformation if this value is false.
*/
bool is_complex () const
{
return is_mag () || ! is_ortho ();
}
/**
* @brief Test, if the transformation is mirroring
*/

View File

@ -708,8 +708,19 @@ static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlappi
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All));
}
static db::Instance insert_inst (db::Cell *c, const db::Cell::cell_inst_array_type &inst)
{
if (c->layout () && ! c->layout ()->is_valid_cell_index (inst.object ().cell_index ())) {
throw tl::Exception (tl::to_string (tr ("Cell index is not valid")));
}
return c->insert (inst);
}
static db::Instance insert_inst_with_props (db::Cell *c, const db::Cell::cell_inst_array_type &inst, db::properties_id_type id)
{
if (c->layout () && ! c->layout ()->is_valid_cell_index (inst.object ().cell_index ())) {
throw tl::Exception (tl::to_string (tr ("Cell index is not valid")));
}
if (id) {
return c->insert (db::CellInstArrayWithProperties (inst, id));
} else {
@ -2393,7 +2404,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"It has been added in version 0.16."
) +
gsi::method ("insert", (db::Instance (db::Cell::*)(const db::Cell::cell_inst_array_type &)) &db::Cell::insert, gsi::arg ("cell_inst_array"),
gsi::method_ext ("insert", &insert_inst, gsi::arg ("cell_inst_array"),
"@brief Inserts a cell instance (array)\n"
"@return An Instance object representing the new instance\n"
"With version 0.16, this method returns an Instance object that represents the new instance.\n"

View File

@ -460,6 +460,11 @@ static db::CompoundRegionOperationNode *new_enclosing_check (db::CompoundRegionO
return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative);
}
static db::CompoundRegionOperationNode *new_enclosed_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative)
{
return new_check_node (other, db::InsideRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative);
}
static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits<db::Coord>::perimeter_type pmin, db::coord_traits<db::Coord>::perimeter_type pmax)
{
check_non_null (input, "input");
@ -673,6 +678,11 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_enclosing_check", &new_enclosing_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Creates a node providing an inside (enclosure) check.\n"
) +
gsi::constructor ("new_enclosed_check", &new_enclosed_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Creates a node providing an enclosed (secondary enclosing primary) check.\n"
"\n"
"This method has been added in version 0.27.5.\n"
) +
gsi::constructor ("new_perimeter_filter", &new_perimeter_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits<db::coord_traits<db::Coord>::perimeter_type>::max (), "max"),
"@brief Creates a node filtering the input by perimeter.\n"
"This node renders the input if the perimeter is between pmin and pmax (exclusively). If 'inverse' is set to true, the "

View File

@ -1160,7 +1160,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". "
"If you don't want to specify one threshold, pass nil to the respective value.\n"
) +
method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
"@brief Performs an inside check with options\n"
"@param d The minimum distance for which the edges are checked\n"
"@param other The other edge collection against which to check\n"
@ -1186,6 +1186,8 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"It is sufficient if the projection of one edge on the other matches the specified condition. "
"The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". "
"If you don't want to specify one threshold, pass nil to the respective value.\n"
"\n"
"The 'enclosed_check' alias was introduced in version 0.27.5.\n"
) +
method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
"@brief Performs an enclosing check with options\n"

View File

@ -980,6 +980,10 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method was introduced in version 0.22.\n"
) +
gsi::method ("library", &db::Layout::library,
"@brief Gets the library this layout lives in or nil if the layout is not part of a library\n"
"This attribute has been introduced in version 0.27.5."
) +
gsi::method ("add_meta_info", &db::Layout::add_meta_info, gsi::arg ("info"),
"@brief Adds meta information to the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information."

View File

@ -277,6 +277,10 @@ Class<db::PCellDeclaration> decl_PCellDeclaration_Native ("db", "PCellDeclaratio
gsi::method ("parameters_from_shape", &db::PCellDeclaration::parameters_from_shape) +
gsi::method ("transformation_from_shape", &db::PCellDeclaration::transformation_from_shape) +
gsi::method ("display_text", &db::PCellDeclaration::get_display_name) +
gsi::method ("layout", &db::PCellDeclaration::layout,
"@brief Gets the Layout object the PCell is registered in or nil if it is not registered yet.\n"
"This attribute has been added in version 0.27.5."
) +
gsi::method ("id", &db::PCellDeclaration::id,
"@brief Gets the integer ID of the PCell declaration\n"
"This ID is used to identify the PCell in the context of a Layout object for example"

View File

@ -2591,7 +2591,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27."
) +
method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Performs an inside check with options\n"
"@param d The minimum distance for which the polygons are checked\n"
"@param other The other region against which to check\n"
@ -2639,6 +2639,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
"The 'enclosed_check' alias was introduced in version 0.27.5.\n"
) +
method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Performs an overlap check with options\n"

View File

@ -916,7 +916,7 @@ struct cplx_trans_defs
"@brief Gets the magnification\n"
) +
method ("is_mag?", &C::is_mag,
"@brief Test, if the transformation is a magnifying one\n"
"@brief Tests, if the transformation is a magnifying one\n"
"\n"
"This is the recommended test for checking if the transformation represents\n"
"a magnification.\n"
@ -925,6 +925,15 @@ struct cplx_trans_defs
"@brief Sets the magnification\n"
"@param m The new magnification"
) +
method ("is_complex?", &C::is_complex,
"@brief Returns true if the transformation is a complex one\n"
"\n"
"If this predicate is false, the transformation can safely be converted to a simple transformation.\n"
"Otherwise, this conversion will be lossy.\n"
"The predicate value is equivalent to 'is_mag || !is_ortho'.\n"
"\n"
"This method has been introduced in version 0.27.5."
) +
method ("R0", &trans_r0,
"@brief A constant giving \"unrotated\" (unit) transformation\n"
"The previous integer constant has been turned into a transformation in version 0.25."

View File

@ -1014,3 +1014,49 @@ TEST(6)
}
TEST(10_HasShapesTouching)
{
db::Layout ly;
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
db::Cell &a = ly.cell (ly.add_cell ("A"));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
a.shapes (l1).insert (db::Box (-100, -100, 0, 0));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 1, 100, 100)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, -1, 100, 100)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (-1, -1, -1, -1)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (1, 1, 1, 1)), false);
}
TEST(11_HasShapesTouchingWithHier)
{
db::Layout ly;
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
unsigned int l2 = ly.insert_layer (db::LayerProperties (2, 0));
db::Cell &a = ly.cell (ly.add_cell ("A"));
db::Cell &b = ly.cell (ly.add_cell ("B"));
a.insert (db::CellInstArray (b.cell_index (), db::Trans (db::Vector (100, 100)), db::Vector (0, 200), db::Vector (200, 0), 2, 2));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
EXPECT_EQ (a.has_shapes_touching (l2, db::Box ()), false);
b.shapes (l1).insert (db::Box (0, 0, 10, 10));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
EXPECT_EQ (a.has_shapes_touching (l2, db::Box (0, 0, 100, 100)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 99, 100)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 99)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (100, 100, 110, 110)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (150, 150, 160, 160)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 300, 310, 310)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 100, 310, 110)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 400, 310, 410)), false);
}

View File

@ -261,12 +261,15 @@ TEST(2)
db::cell_index_type ci;
db::Cell *top;
EXPECT_EQ (g.hier_generation_id (), size_t (0));
ci = g.add_cell ("TOP");
EXPECT_EQ (el.flags, (unsigned int) 0);
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
EXPECT_EQ (g.hier_generation_id (), size_t (1));
el.reset ();
top = &g.cell (ci);
@ -276,6 +279,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
EXPECT_EQ (g.hier_generation_id (), size_t (2));
el.reset ();
top->insert (db::CellInstArray (ci, db::Trans ()));
@ -284,10 +288,12 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
EXPECT_EQ (g.hier_generation_id (), size_t (3));
g.clear ();
g.update ();
el.reset ();
EXPECT_EQ (g.hier_generation_id (), size_t (4));
ci = g.add_cell ("TOP");
@ -295,6 +301,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
EXPECT_EQ (g.hier_generation_id (), size_t (5));
el.reset ();
g.update ();
@ -305,6 +312,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (6));
el.reset ();
g.update ();
@ -314,6 +322,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (7));
// busy mode will make events issued always
g.clear ();
@ -326,6 +335,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
EXPECT_EQ (g.hier_generation_id (), size_t (9));
el.reset ();
top = &g.cell (ci);
@ -335,6 +345,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (10));
el.reset ();
top->insert (db::CellInstArray (ci, db::Trans ()));
@ -343,6 +354,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (11));
}

View File

@ -853,3 +853,295 @@ TEST(42_DMOS4DeviceExtractorTestCircular)
EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)");
EXPECT_EQ (o4.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)");
}
TEST(50_BJT3DeviceExtractorTest)
{
db::Layout ly;
{
db::LoadLayoutOptions options;
std::string fn (tl::testdata ());
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "bjt3_1.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
db::Region o1 (dss);
db::Region o2 (dss);
db::Region o3 (dss);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::NetShape> cl;
db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
db::NetlistDeviceExtractor::input_layers dl;
dl["E"] = &l0;
dl["B"] = &l1;
dl["C"] = &l2;
dl["tE"] = &o1;
dl["tB"] = &o2;
dl["tC"] = &o3;
ex.extract (dss, 0, dl, nl, cl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n"
"end;\n"
);
EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
EXPECT_EQ (o3.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
}
TEST(51_BJT3DeviceExtractorTest)
{
db::Layout ly;
{
db::LoadLayoutOptions options;
std::string fn (tl::testdata ());
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "bjt3_2.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
db::Region o1 (dss);
db::Region o2 (dss);
db::Region o3 (dss);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::NetShape> cl;
db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
db::NetlistDeviceExtractor::input_layers dl;
dl["E"] = &l0;
dl["B"] = &l1;
dl["C"] = &l2;
dl["tE"] = &o1;
dl["tB"] = &o2;
dl["tC"] = &o3;
ex.extract (dss, 0, dl, nl, cl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n"
"end;\n"
);
EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000)");
}
TEST(52_BJT3DeviceExtractorTestLateral)
{
db::Layout ly;
{
db::LoadLayoutOptions options;
std::string fn (tl::testdata ());
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "bjt3_3.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
db::Region o1 (dss);
db::Region o2 (dss);
db::Region o3 (dss);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::NetShape> cl;
db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
db::NetlistDeviceExtractor::input_layers dl;
dl["E"] = &l0;
dl["B"] = &l1;
dl["C"] = &l2;
dl["tE"] = &o1;
dl["tB"] = &o2;
dl["tC"] = &o3;
ex.extract (dss, 0, dl, nl, cl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=0.8,PC=4.8,NE=1);\n"
"end;\n"
);
EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2100,2000;2100,0/700,400;1600,400;1600,1300;700,1300)");
EXPECT_EQ (o3.to_string (), "(2100,0;2100,2000;2500,2000;2500,0)");
}
TEST(53_BJT3DeviceExtractorTestMultEmitter)
{
db::Layout ly;
{
db::LoadLayoutOptions options;
std::string fn (tl::testdata ());
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "bjt3_4.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
db::Region o1 (dss);
db::Region o2 (dss);
db::Region o3 (dss);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::NetShape> cl;
db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
db::NetlistDeviceExtractor::input_layers dl;
dl["E"] = &l0;
dl["B"] = &l1;
dl["C"] = &l2;
dl["tE"] = &o1;
dl["tB"] = &o2;
dl["tC"] = &o3;
ex.extract (dss, 0, dl, nl, cl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.5,PE=3,AB=10,PB=14,AC=10,PC=14,NE=1);\n"
" device BJT3 $2 (C=(null),B=(null),E=(null)) (AE=0.5,PE=3,AB=10,PB=14,AC=10,PC=14,NE=1);\n"
"end;\n"
);
EXPECT_EQ (o1.to_string (), "(1000,500;1000,1500;1500,1500;1500,500);(3500,500;3500,1500;4000,1500;4000,500)");
EXPECT_EQ (o2.to_string (), "(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500);(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500)");
EXPECT_EQ (o3.to_string (), "(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000);(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000)");
}
TEST(54_BJT4DeviceExtractorTest)
{
db::Layout ly;
{
db::LoadLayoutOptions options;
std::string fn (tl::testdata ());
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "bjt4_1.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
db::Region l3 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(3, 0))), dss);
db::Region o1 (dss);
db::Region o2 (dss);
db::Region o3 (dss);
db::Region o4 (dss);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::NetShape> cl;
db::NetlistDeviceExtractorBJT4Transistor ex ("BJT4");
db::NetlistDeviceExtractor::input_layers dl;
dl["E"] = &l0;
dl["B"] = &l1;
dl["C"] = &l2;
dl["S"] = &l3;
dl["tE"] = &o1;
dl["tB"] = &o2;
dl["tC"] = &o3;
dl["tS"] = &o4;
ex.extract (dss, 0, dl, nl, cl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device BJT4 $1 (C=(null),B=(null),E=(null),S=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n"
"end;\n"
);
EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000)");
EXPECT_EQ (o4.to_string (), "(0,0;0,2000;2500,2000;2500,0)");
}

View File

@ -25,7 +25,7 @@
#include "tlUnitTest.h"
#include "tlStringEx.h"
#include "dbRegionUtils.h"
#include "dbRegionCheckUtils.h"
TEST(1_SimpleLShape)
{
@ -52,7 +52,7 @@ TEST(1_SimpleLShape)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(2000,1000;1000,1000)|(1000,2000;2000,2000)");
@ -85,7 +85,7 @@ TEST(1s_SimpleLShape)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(1000,2000;2000,2000)/(2000,1000;1000,1000)");
@ -118,7 +118,7 @@ TEST(2_SimpleLWithBigPart)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0)");
@ -153,7 +153,7 @@ TEST(3_SimpleTWithBigPart)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(0,2500;0,3500)|(1000,3500;1000,2500)");
@ -188,7 +188,7 @@ TEST(4_SimpleNotch)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(1000,1000;2000,1000)|(2000,2000;1000,2000)");
@ -225,7 +225,7 @@ TEST(5_LShapeNotch)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(1500,500;2000,500)|(2000,1500;1500,1500),(1500,1500;1500,2500)|(500,2500;500,1500)");
@ -264,14 +264,12 @@ TEST(6_SeparationLvsBox)
db::Polygon poly2;
poly2.assign_hull (pts2, pts2 + sizeof (pts2) / sizeof (pts2[0]));
db::box_scanner<db::Polygon, size_t> scanner;
scanner.insert (&poly1, 0); // layer 0
scanner.insert (&poly2, 1); // layer 1
db::poly2poly_check<db::Polygon> poly_check (e2e);
poly_check.enter (poly1, 0); // layer 0
poly_check.enter (poly2, 1); // layer 1
do {
scanner.process (poly_check, er.distance (), db::box_convert<db::Polygon> ());
poly_check.process ();
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(1000,1000;1000,0)/(2000,0;2000,1000),(3000,2000;2000,2000)/(2000,1000;3000,1000)");

View File

@ -10,7 +10,7 @@ SOURCES = \
dbCompoundOperationTests.cc \
dbFillToolTests.cc \
dbRecursiveInstanceIteratorTests.cc \
dbRegionUtilsTests.cc \
dbRegionCheckUtilsTests.cc \
dbUtilsTests.cc \
dbWriterTools.cc \
dbLoadLayoutOptionsTests.cc \

View File

@ -1888,7 +1888,7 @@ class DRCOpNodeCheck < DRCOpNodeWithCompare
factory = { :width => :new_width_check, :space => :new_space_check,
:notch => :new_notch_check, :separation => :new_separation_check,
:isolated => :new_isolated_check, :overlap => :new_overlap_check,
:enclosing => :new_enclosing_check }[self.check]
:enclosing => :new_enclosing_check, :enclosed => :new_enclosed_check }[self.check]
oargs = []
if self.other

View File

@ -1042,6 +1042,84 @@ CODE
# actual edges from the first input (see \separation for an example).
#
# %DRC%
# @name enclosed
# @brief Performs an enclosing check (other enclosing layer)
# @synopsis enclosed(other [, options ]) (in conditions)
# @synopsis enclosed(layer, other [, options ])
#
# This check verifies if the polygons of the input layer are enclosed by shapes
# of the other input layer by a certain distance.
# It has manifold options. See \Layer#width for the basic options such
# as metrics, projection and angle constraints etc. This check also features
# opposite and rectangle filtering. See \Layer#separation for details about opposite and
# rectangle error filtering.
#
# This function is essentially the reverse of \enclosing. In case of
# "enclosed", the other layer must be bigger than the primary layer.
#
# @h3 Classic mode @/h3
#
# This function can be used in classic mode with a layer argument. In this case it
# is equivalent to "layer.enclosed" (see \Layer#enclosed).
#
# @code
# # classic "enclosed" check for < 0.2 um
# in = layer(1, 0)
# other = layer(2, 0)
# errors = enclosed(in, other, 0.2.um)
# @/code
#
# @h3 Universal DRC @/h3
#
# The version without a first layer is intended for use within \DRC# expressions
# together with the "universal DRC" method \Layer#drc. In this case, this function needs to be
# put into a condition to specify the check constraints. The other options
# of \Layer#enclosed (e.g. metrics, projection constraints, angle limits etc.)
# apply to this version too:
#
# @code
# # universal DRC "enclosed" check for < 0.2 um
# in = layer(1, 0)
# other = layer(2, 0)
# errors = in.drc(enclosed(other) < 0.2.um)
# @/code
#
# The conditions may involve an upper and lower limit. The following examples
# illustrate the use of this function with conditions:
#
# @code
# out = in.drc(enclosed(other) < 0.2.um)
# out = in.drc(enclosed(other) <= 0.2.um)
# out = in.drc(enclosed(other) > 0.2.um)
# out = in.drc(enclosed(other) >= 0.2.um)
# out = in.drc(enclosed(other) == 0.2.um)
# out = in.drc(enclosed(other) != 0.2.um)
# out = in.drc(0.1.um <= enclosed(other) < 0.2.um)
# @/code
#
# The result of the enclosed check are edges or edge pairs forming the markers.
# These markers indicate the presence of the specified condition.
#
# With a lower and upper limit, the results are edges marking the positions on the
# primary shape where the condition is met.
# With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
# the primary shape. Without an upper limit only, the first edge of the marker is attached to the
# primary shape while the second edge is attached to the shape of the "other" layer.
#
# @table
# @tr
# @td @img(/images/drc_encd1u.png) @/td
# @td @img(/images/drc_encd2u.png) @/td
# @/tr
# @/table
#
# When "larger than" constraints are used, this function will produce the edges from the
# first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
# the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
# actual edges from the first input (see \separation for an example).
#
# %DRC%
# @name separation
# @brief Performs a separation check
@ -1367,6 +1445,7 @@ CODE
%w(
enclosing
enclosed
isolated
notch
overlap

View File

@ -1751,6 +1751,7 @@ CODE
%w(
enc
enclosing
enclosed
overlap
sep
separation

View File

@ -3547,7 +3547,7 @@ CODE
# method will only report space violations to other polygons. \separation is a two-layer
# space check where space is checked against polygons of another layer.
#
# Like for the \width method, merged semantics applies.
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
@ -3632,6 +3632,7 @@ CODE
# will also trigger an error while for \space it will not.
#
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@ -3739,8 +3740,8 @@ CODE
# the orientation of the edges matters: only edges which run back to back with their
# inside side pointing towards each other are checked for distance.
#
# As for the other DRC methods, merged semantics applies. The options available
# are the same than for \width.
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@ -3755,7 +3756,7 @@ CODE
# %DRC%
# @name enclosing
# @brief An enclosing check
# @brief An enclosing check (layer enclosing other_layer)
# @synopsis layer.enclosing(other_layer, value [, options])
# @synopsis layer.enc(other_layer, value [, options])
#
@ -3763,8 +3764,8 @@ CODE
# the \DRC framework. These variants have more options and are more intuitive to use.
# See \global#enclosing for more details.
#
# This method checks whether layer encloses (is bigger than) other_layer by the
# given dimension. Locations, where this is not the case will be reported in form
# This method checks whether layer encloses (is bigger than) other_layer by not less than the
# given distance value. Locations, where the distance is less will be reported in form
# of edge pair error markers.
# Locations, where both edges coincide will be reported as errors as well. Formally
# such locations form an enclosure with a distance of 0. Locations, where other_layer
@ -3779,8 +3780,8 @@ CODE
# the orientation of the edges matters and only edges looking into the same direction
# are checked.
#
# As for the other DRC methods, merged semantics applies. The options available
# are the same than for \width.
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@ -3793,13 +3794,48 @@ CODE
# @/tr
# @/table
%w(width space overlap enclosing separation isolated notch).each do |f|
# %DRC%
# @name enclosed
# @brief An enclosing check (other_layer enclosing layer)
# @synopsis layer.enclosed(other_layer, value [, options])
#
# @b Note: @/b "enclosed" is available as operators for the "universal DRC" function \drc within
# the \DRC framework. These variants have more options and are more intuitive to use.
# See \global#enclosed for more details.
#
# This method checks whether layer is enclosed by (is inside of) other_layer by not less than the
# given distance value. Locations, where the distance is less will be reported in form
# of edge pair error markers.
# Locations, where both edges coincide will be reported as errors as well. Formally
# such locations form an enclosure with a distance of 0. Locations, where other_layer
# is inside layer will not be reported as errors. Such regions can be detected
# by \inside or a boolean "not" operation.
#
# The options are the same as for \separation.
#
# This method is available for edge and polygon layers.
#
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
# The following images show the effect of two enclosed checks (red: input1, blue: input2):
#
# @table
# @tr
# @td @img(/images/drc_encd1.png) @/td
# @td @img(/images/drc_encd2.png) @/td
# @/tr
# @/table
%w(width space overlap enclosing enclosed separation isolated notch).each do |f|
eval <<"CODE"
def #{f}(*args)
@engine._context("#{f}") do
if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosing || :#{f} == :separation
if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosed || :#{f} == :enclosing || :#{f} == :separation
requires_edges_or_region
else
requires_region

View File

@ -2115,7 +2115,7 @@ MainService::cm_tap ()
QPoint mp = view ()->view_object_widget ()->mapToGlobal (view ()->view_object_widget ()->mouse_position ());
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->source (true).to_string ()));
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->display_string (view (), true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}

View File

@ -22,12 +22,24 @@
#include "gsiDeclBasic.h"
#include "gsiInterpreter.h"
#include "gsiDecl.h"
#include "tlTypeTraits.h"
namespace tl
{
template <> struct type_traits<gsi::Interpreter>
: tl::type_traits<void>
{
typedef false_tag has_copy_constructor;
typedef false_tag has_default_constructor;
typedef false_tag has_public_destructor;
};
}
namespace gsi
{
// ---------------------------------------------------------------------------------
// A generic value wrapper that allows wrapping a plain data type into an object
@ -66,4 +78,83 @@ Class<Value> decl_Value ("tl", "Value",
"This class has been introduced in version 0.22."
);
static void eval_string_impl (Interpreter *ip, const char *string, const char *filename, int line)
{
ip->eval_string (string, filename, line);
}
static tl::Variant eval_expr_impl (Interpreter *ip, const char *string, const char *filename, int line)
{
return ip->eval_expr (string, filename, line);
}
static void define_variable_impl (Interpreter *ip, const std::string &name, const tl::Variant &value)
{
ip->define_variable (name, value);
}
static gsi::Interpreter *interpreter_by_name (const std::string &name)
{
for (tl::Registrar<gsi::Interpreter>::iterator i = gsi::interpreters.begin (); i != gsi::interpreters.end (); ++i) {
if (i.current_name () == name) {
return i->available () ? i.operator-> () : 0;
}
}
return 0;
}
static gsi::Interpreter *python_interpreter ()
{
return interpreter_by_name ("pya");
}
static gsi::Interpreter *ruby_interpreter ()
{
return interpreter_by_name ("rba");
}
Class<Interpreter> decl_Macro ("tl", "Interpreter",
gsi::method ("load_file", &Interpreter::load_file, gsi::arg ("path"),
"@brief Loads the given file into the interpreter\n"
"This will execute the code inside the file.\n"
) +
gsi::method_ext ("eval_string", &eval_string_impl, gsi::arg ("string"), gsi::arg ("filename", (const char *) 0, "nil"), gsi::arg ("line", 1),
"@brief Executes the code inside the given string\n"
"Use 'filename' and 'line' to indicate the original source for the error messages.\n"
) +
gsi::method_ext ("eval_expr", &eval_expr_impl, gsi::arg ("string"), gsi::arg ("filename", (const char *) 0, "nil"), gsi::arg ("line", 1),
"@brief Executes the expression inside the given string and returns the result value\n"
"Use 'filename' and 'line' to indicate the original source for the error messages.\n"
) +
gsi::method_ext ("define_variable", &define_variable_impl, gsi::arg ("name"), gsi::arg ("value"),
"@brief Defines a (global) variable with the given name and value\n"
"You can use the \\Value class to provide 'out' or 'inout' parameters which can be modified by code executed inside the interpreter and read back by the caller."
) +
gsi::method ("python_interpreter", &python_interpreter,
"@brief Gets the instance of the Python interpreter\n"
) +
gsi::method ("ruby_interpreter", &ruby_interpreter,
"@brief Gets the instance of the Ruby interpreter\n"
),
"@brief A generalization of script interpreters\n"
"The main purpose of this class is to provide cross-language call options. "
"Using the Python interpreter, it is possible to execute Python code from Ruby for example.\n"
"\n"
"The following example shows how to use the interpreter class to execute Python code from Ruby "
"and how to pass values from Ruby to Python and back using the \\Value wrapper object:\n"
"\n"
"@code\n"
"pya = RBA::Interpreter::python_interpreter\n"
"out_param = RBA::Value::new(17)\n"
"pya.define_variable(\"out_param\", out_param)\n"
"pya.eval_string(<<END)\n"
"print(\"This is Python now!\")\n"
"out_param.value = out_param.value + 25\n"
"END\n"
"puts out_param.value # gives '42'"
"@/code\n"
"\n"
"This class was introduced in version 0.27.5.\n"
);
}

View File

@ -30,6 +30,7 @@
#include <iostream>
#include "tlVariant.h"
#include "gsiObject.h"
namespace gsi
{
@ -38,6 +39,7 @@ namespace gsi
* @brief Provides a basic implementation for a "boxed" plain value using a Variant as the basic type
*/
class GSI_PUBLIC Value
: public gsi::ObjectBase
{
public:
/**

View File

@ -25,6 +25,7 @@
#include "tlScriptError.h"
#include "tlClassRegistry.h"
#include "tlVariant.h"
#include "gsiCommon.h"
namespace gsi
@ -267,7 +268,7 @@ public:
/**
* @brief Defines a global variable with the given name and value
*/
virtual void define_variable (const std::string &name, const std::string &value) = 0;
virtual void define_variable (const std::string &name, const tl::Variant &value) = 0;
/**
* @brief Installs the given console for output

View File

@ -9,27 +9,27 @@ SUBDIRS = \
QtGui.depends += QtCore
equals(HAVE_QT_NETWORK, "1") {
!equals(HAVE_QT_NETWORK, "0") {
SUBDIRS += QtNetwork
QtNetwork.depends += QtCore
}
equals(HAVE_QT_SQL, "1") {
!equals(HAVE_QT_SQL, "0") {
SUBDIRS += QtSql
QtSql.depends += QtCore
}
equals(HAVE_QT_DESIGNER, "1") {
!equals(HAVE_QT_DESIGNER, "0") {
SUBDIRS += QtDesigner
QtDesigner.depends += QtCore
}
equals(HAVE_QT_XML, "1") {
!equals(HAVE_QT_XML, "0") {
SUBDIRS += QtXml
QtXml.depends += QtCore
}
equals(HAVE_QT_UITOOLS, "1") {
!equals(HAVE_QT_UITOOLS, "0") {
SUBDIRS += QtUiTools
QtUiTools.depends += QtCore
}

View File

@ -38,6 +38,36 @@
// -----------------------------------------------------------------------
// class QMessageLogger
static void critical1(QMessageLogger *logger, const char *msg) {
logger->critical("%s", msg);
}
static void critical2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->critical(cat, "%s", msg);
}
static void debug1(QMessageLogger *logger, const char *msg) {
logger->debug("%s", msg);
}
static void debug2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->debug(cat, "%s", msg);
}
static void fatal1(QMessageLogger *logger, const char *msg) {
logger->fatal("%s", msg);
}
static void info1(QMessageLogger *logger, const char *msg) {
logger->info("%s", msg);
}
static void info2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->info(cat, "%s", msg);
}
static void noDebug1(QMessageLogger *logger, const char *msg) {
logger->noDebug("%s", msg);
}
static void warning1(QMessageLogger *logger, const char *msg) {
logger->warning("%s", msg);
}
static void warning2(QMessageLogger *logger, const QLoggingCategory &cat, const char *msg) {
logger->warning(cat, "%s", msg);
}
// Constructor QMessageLogger::QMessageLogger()
@ -107,49 +137,6 @@ static void _call_ctor_QMessageLogger_5636 (const qt_gsi::GenericStaticMethod *
}
// void QMessageLogger::critical(const char *msg, ...)
static void _init_f_critical_c1731 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("msg");
decl->add_arg<const char * > (argspec_0);
decl->set_return<void > ();
}
static void _call_f_critical_c1731 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const char *arg1 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->critical (arg1);
}
// void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...)
static void _init_f_critical_c4558 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("cat");
decl->add_arg<const QLoggingCategory & > (argspec_0);
static gsi::ArgSpecBase argspec_1 ("msg");
decl->add_arg<const char * > (argspec_1);
decl->set_return<void > ();
}
static void _call_f_critical_c4558 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const QLoggingCategory &arg1 = gsi::arg_reader<const QLoggingCategory & >() (args, heap);
const char *arg2 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->critical (arg1, arg2);
}
// QDebug QMessageLogger::critical()
@ -184,49 +171,6 @@ static void _call_f_critical_c2935 (const qt_gsi::GenericMethod * /*decl*/, void
}
// void QMessageLogger::debug(const char *msg, ...)
static void _init_f_debug_c1731 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("msg");
decl->add_arg<const char * > (argspec_0);
decl->set_return<void > ();
}
static void _call_f_debug_c1731 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const char *arg1 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->debug (arg1);
}
// void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...)
static void _init_f_debug_c4558 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("cat");
decl->add_arg<const QLoggingCategory & > (argspec_0);
static gsi::ArgSpecBase argspec_1 ("msg");
decl->add_arg<const char * > (argspec_1);
decl->set_return<void > ();
}
static void _call_f_debug_c4558 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const QLoggingCategory &arg1 = gsi::arg_reader<const QLoggingCategory & >() (args, heap);
const char *arg2 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->debug (arg1, arg2);
}
// QDebug QMessageLogger::debug()
@ -261,69 +205,6 @@ static void _call_f_debug_c2935 (const qt_gsi::GenericMethod * /*decl*/, void *c
}
// void QMessageLogger::fatal(const char *msg, ...)
static void _init_f_fatal_c1731 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("msg");
decl->add_arg<const char * > (argspec_0);
decl->set_return<void > ();
}
static void _call_f_fatal_c1731 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const char *arg1 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->fatal (arg1);
}
// void QMessageLogger::info(const char *msg, ...)
static void _init_f_info_c1731 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("msg");
decl->add_arg<const char * > (argspec_0);
decl->set_return<void > ();
}
static void _call_f_info_c1731 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const char *arg1 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->info (arg1);
}
// void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...)
static void _init_f_info_c4558 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("cat");
decl->add_arg<const QLoggingCategory & > (argspec_0);
static gsi::ArgSpecBase argspec_1 ("msg");
decl->add_arg<const char * > (argspec_1);
decl->set_return<void > ();
}
static void _call_f_info_c4558 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const QLoggingCategory &arg1 = gsi::arg_reader<const QLoggingCategory & >() (args, heap);
const char *arg2 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->info (arg1, arg2);
}
// QDebug QMessageLogger::info()
@ -358,26 +239,6 @@ static void _call_f_info_c2935 (const qt_gsi::GenericMethod * /*decl*/, void *cl
}
// void QMessageLogger::noDebug(const char *, ...)
static void _init_f_noDebug_c1731 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("arg1");
decl->add_arg<const char * > (argspec_0);
decl->set_return<void > ();
}
static void _call_f_noDebug_c1731 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const char *arg1 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->noDebug (arg1);
}
// QNoDebug QMessageLogger::noDebug()
@ -393,49 +254,6 @@ static void _call_f_noDebug_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cl
}
// void QMessageLogger::warning(const char *msg, ...)
static void _init_f_warning_c1731 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("msg");
decl->add_arg<const char * > (argspec_0);
decl->set_return<void > ();
}
static void _call_f_warning_c1731 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const char *arg1 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->warning (arg1);
}
// void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...)
static void _init_f_warning_c4558 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("cat");
decl->add_arg<const QLoggingCategory & > (argspec_0);
static gsi::ArgSpecBase argspec_1 ("msg");
decl->add_arg<const char * > (argspec_1);
decl->set_return<void > ();
}
static void _call_f_warning_c4558 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const QLoggingCategory &arg1 = gsi::arg_reader<const QLoggingCategory & >() (args, heap);
const char *arg2 = gsi::arg_reader<const char * >() (args, heap);
__SUPPRESS_UNUSED_WARNING(ret);
((QMessageLogger *)cls)->warning (arg1, arg2);
}
// QDebug QMessageLogger::warning()
@ -479,29 +297,30 @@ static gsi::Methods methods_QMessageLogger () {
methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QMessageLogger::QMessageLogger()\nThis method creates an object of class QMessageLogger.", &_init_ctor_QMessageLogger_0, &_call_ctor_QMessageLogger_0);
methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QMessageLogger::QMessageLogger(const char *file, int line, const char *function)\nThis method creates an object of class QMessageLogger.", &_init_ctor_QMessageLogger_4013, &_call_ctor_QMessageLogger_4013);
methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QMessageLogger::QMessageLogger(const char *file, int line, const char *function, const char *category)\nThis method creates an object of class QMessageLogger.", &_init_ctor_QMessageLogger_5636, &_call_ctor_QMessageLogger_5636);
methods += new qt_gsi::GenericMethod ("critical", "@brief Method void QMessageLogger::critical(const char *msg, ...)\n", true, &_init_f_critical_c1731, &_call_f_critical_c1731);
methods += new qt_gsi::GenericMethod ("critical", "@brief Method void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...)\n", true, &_init_f_critical_c4558, &_call_f_critical_c4558);
methods += new qt_gsi::GenericMethod ("critical", "@brief Method QDebug QMessageLogger::critical()\n", true, &_init_f_critical_c0, &_call_f_critical_c0);
methods += new qt_gsi::GenericMethod ("critical", "@brief Method QDebug QMessageLogger::critical(const QLoggingCategory &cat)\n", true, &_init_f_critical_c2935, &_call_f_critical_c2935);
methods += new qt_gsi::GenericMethod ("debug", "@brief Method void QMessageLogger::debug(const char *msg, ...)\n", true, &_init_f_debug_c1731, &_call_f_debug_c1731);
methods += new qt_gsi::GenericMethod ("debug", "@brief Method void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...)\n", true, &_init_f_debug_c4558, &_call_f_debug_c4558);
methods += new qt_gsi::GenericMethod ("debug", "@brief Method QDebug QMessageLogger::debug()\n", true, &_init_f_debug_c0, &_call_f_debug_c0);
methods += new qt_gsi::GenericMethod ("debug", "@brief Method QDebug QMessageLogger::debug(const QLoggingCategory &cat)\n", true, &_init_f_debug_c2935, &_call_f_debug_c2935);
methods += new qt_gsi::GenericMethod ("fatal", "@brief Method void QMessageLogger::fatal(const char *msg, ...)\n", true, &_init_f_fatal_c1731, &_call_f_fatal_c1731);
methods += new qt_gsi::GenericMethod ("info", "@brief Method void QMessageLogger::info(const char *msg, ...)\n", true, &_init_f_info_c1731, &_call_f_info_c1731);
methods += new qt_gsi::GenericMethod ("info", "@brief Method void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...)\n", true, &_init_f_info_c4558, &_call_f_info_c4558);
methods += new qt_gsi::GenericMethod ("info", "@brief Method QDebug QMessageLogger::info()\n", true, &_init_f_info_c0, &_call_f_info_c0);
methods += new qt_gsi::GenericMethod ("info", "@brief Method QDebug QMessageLogger::info(const QLoggingCategory &cat)\n", true, &_init_f_info_c2935, &_call_f_info_c2935);
methods += new qt_gsi::GenericMethod ("noDebug", "@brief Method void QMessageLogger::noDebug(const char *, ...)\n", true, &_init_f_noDebug_c1731, &_call_f_noDebug_c1731);
methods += new qt_gsi::GenericMethod ("noDebug", "@brief Method QNoDebug QMessageLogger::noDebug()\n", true, &_init_f_noDebug_c0, &_call_f_noDebug_c0);
methods += new qt_gsi::GenericMethod ("warning", "@brief Method void QMessageLogger::warning(const char *msg, ...)\n", true, &_init_f_warning_c1731, &_call_f_warning_c1731);
methods += new qt_gsi::GenericMethod ("warning", "@brief Method void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...)\n", true, &_init_f_warning_c4558, &_call_f_warning_c4558);
methods += new qt_gsi::GenericMethod ("warning", "@brief Method QDebug QMessageLogger::warning()\n", true, &_init_f_warning_c0, &_call_f_warning_c0);
methods += new qt_gsi::GenericMethod ("warning", "@brief Method QDebug QMessageLogger::warning(const QLoggingCategory &cat)\n", true, &_init_f_warning_c2935, &_call_f_warning_c2935);
return methods;
}
gsi::Class<QMessageLogger> decl_QMessageLogger ("QtCore", "QMessageLogger",
gsi::method_ext("critical", &critical1, "@brief Method void QMessageLogger::critical(const char *msg)") +
gsi::method_ext("critical", &critical2, "@brief Method void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg)") +
gsi::method_ext("debug", &debug1, "@brief Method void QMessageLogger::debug(const char *msg)") +
gsi::method_ext("debug", &debug2, "@brief Method void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg)") +
gsi::method_ext("fatal", &fatal1, "@brief Method void QMessageLogger::fatal(const char *msg)") +
gsi::method_ext("info", &info1, "@brief Method void QMessageLogger::info(const char *msg)") +
gsi::method_ext("info", &info2, "@brief Method void QMessageLogger::info(const QLoggingCategory &cat, const char *msg)") +
gsi::method_ext("noDebug", &noDebug1, "@brief Method void QMessageLogger::noDebug(const char *msg)") +
gsi::method_ext("warning", &warning1, "@brief Method void QMessageLogger::warning(const char *msg)") +
gsi::method_ext("warning", &warning2, "@brief Method void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg)")
+
methods_QMessageLogger (),
"@qt\n@brief Binding of QMessageLogger");

View File

@ -11,43 +11,43 @@ SUBDIRS = \
QtGui.depends += QtCore
QtWidgets.depends += QtGui
equals(HAVE_QT_NETWORK, "1") {
!equals(HAVE_QT_NETWORK, "0") {
SUBDIRS += QtNetwork
QtNetwork.depends += QtCore
}
equals(HAVE_QT_SQL, "1") {
!equals(HAVE_QT_SQL, "0") {
SUBDIRS += QtSql
QtSql.depends += QtCore
}
equals(HAVE_QT_SVG, "1") {
!equals(HAVE_QT_SVG, "0") {
SUBDIRS += QtSvg
QtSvg.depends += QtCore QtWidgets
}
equals(HAVE_QT_PRINTSUPPORT, "1") {
!equals(HAVE_QT_PRINTSUPPORT, "0") {
SUBDIRS += QtPrintSupport
QtPrintSupport.depends += QtCore QtWidgets
}
equals(HAVE_QT_MULTIMEDIA, "1") {
!equals(HAVE_QT_MULTIMEDIA, "0") {
SUBDIRS += QtMultimedia
QtMultimedia.depends += QtCore QtWidgets QtNetwork
}
equals(HAVE_QT_DESIGNER, "1") {
!equals(HAVE_QT_DESIGNER, "0") {
SUBDIRS += QtDesigner
QtDesigner.depends += QtCore
}
equals(HAVE_QT_XML, "1") {
!equals(HAVE_QT_XML, "0") {
SUBDIRS += QtXml QtXmlPatterns
QtXmlPatterns.depends += QtCore
QtXml.depends += QtCore
}
equals(HAVE_QT_UITOOLS, "1") {
!equals(HAVE_QT_UITOOLS, "0") {
SUBDIRS += QtUiTools
QtUiTools.depends += QtCore
}

View File

@ -94,7 +94,9 @@ plugins.depends += lib rdb db
ant.depends += laybasic
img.depends += laybasic
edt.depends += laybasic
lay.depends += laybasic ant img edt
klayout_main.depends += plugins $$MAIN_DEPENDS
}

View File

@ -536,6 +536,85 @@ See <a href="/about/drc_ref_source.xml#edges">Source#edges</a> for a description
<p>
"enc" is the short form for <a href="#enclosing">enclosing</a>.
</p>
<a name="enclosed"/><h2>"enclosed" - Performs an enclosing check (other enclosing layer)</h2>
<keyword name="enclosed"/>
<p>Usage:</p>
<ul>
<li><tt>enclosed(other [, options ]) (in conditions)</tt></li>
<li><tt>enclosed(layer, other [, options ])</tt></li>
</ul>
<p>
This check verifies if the polygons of the input layer are enclosed by shapes
of the other input layer by a certain distance.
It has manifold options. See <a href="/about/drc_ref_layer.xml#width">Layer#width</a> for the basic options such
as metrics, projection and angle constraints etc. This check also features
opposite and rectangle filtering. See <a href="/about/drc_ref_layer.xml#separation">Layer#separation</a> for details about opposite and
rectangle error filtering.
</p><p>
This function is essentially the reverse of <a href="#enclosing">enclosing</a>. In case of
"enclosed", the other layer must be bigger than the primary layer.
</p><p>
<h3>Classic mode </h3>
</p><p>
This function can be used in classic mode with a layer argument. In this case it
is equivalent to "layer.enclosed" (see <a href="/about/drc_ref_layer.xml#enclosed">Layer#enclosed</a>).
</p><p>
<pre>
# classic "enclosed" check for &lt; 0.2 um
in = layer(1, 0)
other = layer(2, 0)
errors = enclosed(in, other, 0.2.um)
</pre>
</p><p>
<h3>Universal DRC </h3>
</p><p>
The version without a first layer is intended for use within <a href="/about/drc_ref_drc.xml">DRC</a> expressions
together with the "universal DRC" method <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a>. In this case, this function needs to be
put into a condition to specify the check constraints. The other options
of <a href="/about/drc_ref_layer.xml#enclosed">Layer#enclosed</a> (e.g. metrics, projection constraints, angle limits etc.)
apply to this version too:
</p><p>
<pre>
# universal DRC "enclosed" check for &lt; 0.2 um
in = layer(1, 0)
other = layer(2, 0)
errors = in.drc(enclosed(other) &lt; 0.2.um)
</pre>
</p><p>
The conditions may involve an upper and lower limit. The following examples
illustrate the use of this function with conditions:
</p><p>
<pre>
out = in.drc(enclosed(other) &lt; 0.2.um)
out = in.drc(enclosed(other) &lt;= 0.2.um)
out = in.drc(enclosed(other) &gt; 0.2.um)
out = in.drc(enclosed(other) &gt;= 0.2.um)
out = in.drc(enclosed(other) == 0.2.um)
out = in.drc(enclosed(other) != 0.2.um)
out = in.drc(0.1.um &lt;= enclosed(other) &lt; 0.2.um)
</pre>
</p><p>
The result of the enclosed check are edges or edge pairs forming the markers.
These markers indicate the presence of the specified condition.
</p><p>
With a lower and upper limit, the results are edges marking the positions on the
primary shape where the condition is met.
With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
the primary shape. Without an upper limit only, the first edge of the marker is attached to the
primary shape while the second edge is attached to the shape of the "other" layer.
</p><p>
<table>
<tr>
<td><img src="/images/drc_encd1u.png"/></td>
<td><img src="/images/drc_encd2u.png"/></td>
</tr>
</table>
</p><p>
When "larger than" constraints are used, this function will produce the edges from the
first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
actual edges from the first input (see <a href="#separation">separation</a> for an example).
</p>
<a name="enclosing"/><h2>"enclosing" - Performs an enclosing check</h2>
<keyword name="enclosing"/>
<p>Usage:</p>

View File

@ -778,7 +778,44 @@ individual ones unless raw mode is chosen.
<p>
See <a href="#enclosing">enclosing</a> for a description of that method
</p>
<a name="enclosing"/><h2>"enclosing" - An enclosing check</h2>
<a name="enclosed"/><h2>"enclosed" - An enclosing check (other_layer enclosing layer)</h2>
<keyword name="enclosed"/>
<p>Usage:</p>
<ul>
<li><tt>layer.enclosed(other_layer, value [, options])</tt></li>
</ul>
<p>
<b>Note: </b>"enclosed" is available as operators for the "universal DRC" function <a href="#drc">drc</a> within
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use.
See <a href="/about/drc_ref_global.xml#enclosed">enclosed</a> for more details.
</p><p>
This method checks whether layer is enclosed by (is inside of) other_layer by not less than the
given distance value. Locations, where the distance is less will be reported in form
of edge pair error markers.
Locations, where both edges coincide will be reported as errors as well. Formally
such locations form an enclosure with a distance of 0. Locations, where other_layer
is inside layer will not be reported as errors. Such regions can be detected
by <a href="#inside">inside</a> or a boolean "not" operation.
</p><p>
The options are the same as for <a href="#separation">separation</a>.
</p><p>
This method is available for edge and polygon layers.
</p><p>
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
The following images show the effect of two enclosed checks (red: input1, blue: input2):
</p><p>
<table>
<tr>
<td><img src="/images/drc_encd1.png"/></td>
<td><img src="/images/drc_encd2.png"/></td>
</tr>
</table>
</p>
<a name="enclosing"/><h2>"enclosing" - An enclosing check (layer enclosing other_layer)</h2>
<keyword name="enclosing"/>
<p>Usage:</p>
<ul>
@ -790,8 +827,8 @@ See <a href="#enclosing">enclosing</a> for a description of that method
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use.
See <a href="/about/drc_ref_global.xml#enclosing">enclosing</a> for more details.
</p><p>
This method checks whether layer encloses (is bigger than) other_layer by the
given dimension. Locations, where this is not the case will be reported in form
This method checks whether layer encloses (is bigger than) other_layer by not less than the
given distance value. Locations, where the distance is less will be reported in form
of edge pair error markers.
Locations, where both edges coincide will be reported as errors as well. Formally
such locations form an enclosure with a distance of 0. Locations, where other_layer
@ -806,8 +843,8 @@ The enclosing method can be applied to both edge or polygon layers. On edge laye
the orientation of the edges matters and only edges looking into the same direction
are checked.
</p><p>
As for the other DRC methods, merged semantics applies. The options available
are the same than for <a href="#width">width</a>.
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
@ -2046,8 +2083,8 @@ The overlap method can be applied to both edge or polygon layers. On edge layers
the orientation of the edges matters: only edges which run back to back with their
inside side pointing towards each other are checked for distance.
</p><p>
As for the other DRC methods, merged semantics applies. The options available
are the same than for <a href="#width">width</a>.
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
@ -2569,6 +2606,7 @@ layers touch are also reported. More specifically, the case of zero spacing
will also trigger an error while for <a href="#space">space</a> it will not.
</p><p>
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
@ -2789,7 +2827,7 @@ The <a href="#notch">notch</a> method is similar, but will only report self-spac
method will only report space violations to other polygons. <a href="#separation">separation</a> is a two-layer
space check where space is checked against polygons of another layer.
</p><p>
Like for the <a href="#width">width</a> method, merged semantics applies.
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -24,9 +24,9 @@
<topic href="/manual/view_state.xml"/>
<topic href="/manual/bookmarks.xml"/>
<topic href="/manual/descend.xml"/>
<topic href="/manual/layer_views.xml"/>
<topic href="/manual/layer_color.xml"/>
<topic href="/manual/layer_content.xml"/>
<topic href="/manual/layer_content.xml"/>
<topic href="/manual/line_style.xml"/>
<topic href="/manual/layer_animation.xml"/>
<topic href="/manual/layer_style.xml"/>

View File

@ -100,5 +100,9 @@
a selector as well, only those shapes will be shown that meet both criteria.
</p>
<p>
A general description for the source notation is found here: <link href="/about/layer_sources.xml"/>.
</p>
</doc>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<doc>
<title>The Layer List (Layer Views)</title>
<keyword name="Layer list"/>
<keyword name="Layer views"/>
<p>
An important concept in KLayout are the layer views. KLayout displays the layers
of a layout by default in a list on the right side of the main window.
This list however, does not directly reflect the layers in the layout database.
Instead this list is a collection of "views". A view is a description of what is
to be displayed and how.
</p>
<p>
Essentially, the entries in the layer list are pointers to layers in the
database, together with a description how to paint the shapes on these layers (the
"layer properties").
</p>
<p>
The pointer is the "source" of a layer view. This is typically a GDS layer and datatype,
but can be a layer name (for DXF for example). There are also abstract sources (such as
cell boundaries) and the sources can include selectors or modifiers. Selectors are
used to only display shapes with certain user properties or from certain hierarchy
levels. Modifiers transform the shapes before they are drawn for example.
The source is defined by a "source specification" - this is a string describing the
database layer and selectors and modifiers. A simple source string is "1/0" which is
for GDS layer 1, datatype 0 without and selectors or modifiers.
To change the source, use "Change Source" from the layer list's context menu.
</p>
<p>
See <link href="/manual/layer_source_expert.xml"/> for some source specification string applications
and more details.
</p>
<p>
Beside the source, a layer entry has a display name. This is an arbitrary text
providing a description for the user. By default - when no such name is present -
the source of the layer will be displayed.
To change the display name, use "Rename" from the layer list's context menu.
</p>
<p>
Plus of course, the layer views have many options to specify the drawing style,
animations and decorations.
</p>
<p>
The concept of separating views from the database layers opens some interesting options:
</p>
<ul>
<li>Layer views can refer to individual layouts from multi-layout views (through the "@1", "@2", ... notation in the source).
Hence, multiple layouts can be mixed in a single layer list.</li>
<li>Layers can be present in the list which do not need to be present in the database. Such a layer is shown as empty.
This is important as in GDS an empty layer is equivalent to non-existing. Still you may want to have it shown in the
layer list - the views offer this option.</li>
<li>Vice versa, database layer may not be listed in the layer list if no corresponding layer view is present. This
way, auxiliary or debug layers can be omitted from the layer list. A "wildcard specification" is available to make
sure, all layers are shown if you need to see all.</li>
<li>Multiple tabs can be present to provide multiple views on the same layouts. This is just an alternative set of
layer views.</li>
<li>Layer grouping, sorting etc. are just operations on the views, no database change is involved.</li>
</ul>
<p>
The concept on the other hand is slightly counter-intuitive at first.
Here are some hints:
</p>
<ul>
<li>Renaming a layer does not change the source - if you rename a layer to something like "1/0", you are likely to fool yourself thinking this is layer 1, datatype 0.</li>
<li>Changing a layer view's source does not change the database too - it will just change the pointer. To change a layer's information in the database, use Edit/Layer/Edit Layer Specification.</li>
<li>Deleting a layer from the layer list does <b>not delete</b> the layer from the database. Use Edit/Layer/Delete Layer instead.</li>
<li>Additing a new layer does not immediately create the layer in the database. Only once you draw something on that layer, it is generated in the database.</li>
</ul>
</doc>

View File

@ -243,7 +243,7 @@ end</pre>
include RBA
ui_file = QFile::new(QFileInfo::new($0).dir.filePath("MyDialog.ui"))
ui_file.open(QIODevice::ReadOnly.to_i)
ui_file.open(QIODevice::ReadOnly)
dialog = QFormBuilder::new.load(ui_file, Application::instance.main_window)
ui_file.close

View File

@ -36,6 +36,10 @@
<file alias="drc_enc2.png">doc/images/drc_enc2.png</file>
<file alias="drc_enc1u.png">doc/images/drc_enc1u.png</file>
<file alias="drc_enc2u.png">doc/images/drc_enc2u.png</file>
<file alias="drc_encd1.png">doc/images/drc_encd1.png</file>
<file alias="drc_encd2.png">doc/images/drc_encd2.png</file>
<file alias="drc_encd1u.png">doc/images/drc_encd1u.png</file>
<file alias="drc_encd2u.png">doc/images/drc_encd2u.png</file>
<file alias="drc_overlap1.png">doc/images/drc_overlap1.png</file>
<file alias="drc_overlap2.png">doc/images/drc_overlap2.png</file>
<file alias="drc_overlap1u.png">doc/images/drc_overlap1u.png</file>

View File

@ -220,7 +220,7 @@ FillDialog::generate_fill (const FillParameters &fp)
// preprocess fill regions
if (distance_x != 0 || distance_y != 0) {
fill_region.size (distance_x, distance_y);
fill_region.size (-distance_x, -distance_y);
} else {
fill_region.merge ();
}

View File

@ -158,6 +158,7 @@
<file alias="landmarks.xml">doc/manual/landmarks.xml</file>
<file alias="layer_animation.xml">doc/manual/layer_animation.xml</file>
<file alias="layer_boolean.xml">doc/manual/layer_boolean.xml</file>
<file alias="layer_views.xml">doc/manual/layer_views.xml</file>
<file alias="layer_color.xml">doc/manual/layer_color.xml</file>
<file alias="layer_content.xml">doc/manual/layer_content.xml</file>
<file alias="layer_fill.xml">doc/manual/layer_fill.xml</file>

View File

@ -2025,44 +2025,47 @@ MainWindow::cm_load_layer_props ()
if (current_view ()) {
std::string fn;
if (mp_lprops_fdia->get_open (fn, tl::to_string (QObject::tr ("Load Layer Properties File")))) {
int target_cv_index = -2;
if (current_view ()->cellviews () > 1 && is_single_cv_layer_properties_file (fn)) {
QStringList items;
items << QString (QObject::tr ("Take it as it is"));
items << QString (QObject::tr ("Apply to all layouts"));
for (unsigned int i = 0; i < current_view ()->cellviews (); ++i) {
items << QString (tl::to_qstring (tl::to_string (QObject::tr ("Apply to ")) + current_view ()->cellview (i)->name () + " (@" + tl::to_string (i + 1) + ")"));
}
bool ok;
QString item = QInputDialog::getItem(this, QObject::tr ("Apply Layer Properties File"),
QObject::tr ("There are multiple layouts in that panel but the layer properties file contains properties for a single one.\nWhat should be done?"),
items, 1, false, &ok);
if (!ok || item.isEmpty()) {
return;
}
target_cv_index = items.indexOf (item) - 2;
}
if (target_cv_index > -2) {
load_layer_properties (fn, target_cv_index, false /*current view only*/, false /*don't add default*/);
} else {
load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
}
load_layer_props_from_file (fn);
add_to_other_mru (fn, cfg_mru_layer_properties);
}
} else {
throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the layer properties for")));
}
}
void
MainWindow::load_layer_props_from_file (const std::string &fn)
{
int target_cv_index = -2;
if (current_view ()->cellviews () > 1 && is_single_cv_layer_properties_file (fn)) {
QStringList items;
items << QString (QObject::tr ("Take it as it is"));
items << QString (QObject::tr ("Apply to all layouts"));
for (unsigned int i = 0; i < current_view ()->cellviews (); ++i) {
items << QString (tl::to_qstring (tl::to_string (QObject::tr ("Apply to ")) + current_view ()->cellview (i)->name () + " (@" + tl::to_string (i + 1) + ")"));
}
bool ok;
QString item = QInputDialog::getItem(this, QObject::tr ("Apply Layer Properties File"),
QObject::tr ("There are multiple layouts in that panel but the layer properties file contains properties for a single one.\nWhat should be done?"),
items, 1, false, &ok);
if (!ok || item.isEmpty()) {
return;
}
target_cv_index = items.indexOf (item) - 2;
}
if (target_cv_index > -2) {
load_layer_properties (fn, target_cv_index, false /*current view only*/, false /*don't add default*/);
} else {
load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
}
}
void
MainWindow::save_session (const std::string &fn)
{
@ -3238,7 +3241,7 @@ MainWindow::open_recent_layer_properties (size_t n)
if (n < m_mru_layer_properties.size ()) {
std::string fn = m_mru_layer_properties [n];
load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
load_layer_props_from_file (fn);
add_to_other_mru (fn, cfg_mru_layer_properties); // make it the latest
}

View File

@ -805,6 +805,7 @@ private:
int dirty_files (std::string &dirty_files);
void load_layer_props_from_file (const std::string &fn);
void interactive_close_view (int index, bool all_cellviews);
void call_on_current_view (void (lay::LayoutView::*func) (), const std::string &op_desc);
void current_view_changed ();

View File

@ -38,6 +38,7 @@
#include "tlLog.h"
#include "tlProgress.h"
#include "tlTimer.h"
#include "rdbUtils.h"
#include <QInputDialog>
#include <QHeaderView>
@ -537,10 +538,10 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu)
rdb::Item *item = rdb.create_item (cell->id (), cat->id ());
if (! v->is_list ()) {
item->add_value (std::string (v->to_string ()));
rdb::add_item_value (item, *v, dbu);
} else {
for (std::vector<tl::Variant>::const_iterator i = v->get_list ().begin (); i != v->get_list ().end (); ++i) {
item->add_value (std::string (i->to_string ()));
rdb::add_item_value (item, *i, dbu);
}
}

View File

@ -690,6 +690,11 @@ Class<lay::LayoutView> decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
"\n"
"In 0.25, this method has been moved from MainWindow to LayoutView.\n"
) +
gsi::method ("is_editable?", &lay::LayoutView::is_editable,
"@brief Returns true if the view is in editable mode\n"
"\n"
"This read-only attribute has been added in version 0.27.5.\n"
) +
gsi::method ("reload_layout", &lay::LayoutView::reload_layout, gsi::arg ("cv"),
"@brief Reloads the given cellview\n"
"\n"

View File

@ -56,6 +56,7 @@ EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector<lay::
ly1->setMargin (0);
mp_pages = new QTabWidget (this);
mp_pages->setSizePolicy (QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored));
ly1->addWidget (mp_pages);
m_pages = pages;

View File

@ -24,6 +24,7 @@
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "gsiInterpreter.h"
#include "gsiEnums.h"
#include "lymMacroInterpreter.h"
#include "lymMacro.h"
#include "rba.h"
@ -96,11 +97,11 @@ Class<gsi::MacroExecutionContext> decl_MacroExecutionContext ("lay", "MacroExecu
"suppress exceptions when re-raising them."
);
class MacroInterpreter
class MacroInterpreterImpl
: public lym::MacroInterpreter
{
public:
MacroInterpreter ()
MacroInterpreterImpl ()
: lym::MacroInterpreter (),
mp_registration (0), m_supports_include_expansion (true)
{
@ -111,7 +112,7 @@ public:
m_debugger_scheme = lym::MacroInterpreter::debugger_scheme ();
}
~MacroInterpreter ()
~MacroInterpreterImpl ()
{
delete mp_registration;
mp_registration = 0;
@ -161,9 +162,9 @@ public:
return m_supports_include_expansion;
}
void set_storage_scheme (int scheme)
void set_storage_scheme (lym::Macro::Format scheme)
{
m_storage_scheme = lym::Macro::Format (scheme);
m_storage_scheme = scheme;
}
virtual lym::Macro::Format storage_scheme () const
@ -171,9 +172,9 @@ public:
return m_storage_scheme;
}
void set_debugger_scheme (int scheme)
void set_debugger_scheme (lym::Macro::Interpreter scheme)
{
m_debugger_scheme = lym::Macro::Interpreter (scheme);
m_debugger_scheme = scheme;
}
virtual lym::Macro::Interpreter debugger_scheme () const
@ -256,50 +257,58 @@ private:
bool m_supports_include_expansion;
};
int const_PlainTextFormat ()
{
return int (lym::Macro::PlainTextFormat);
}
int const_PlainTextWithHashAnnotationsFormat ()
{
return int (lym::Macro::PlainTextWithHashAnnotationsFormat);
}
int const_MacroFormat ()
{
return int (lym::Macro::MacroFormat);
}
int const_RubyDebugger ()
{
return int (lym::Macro::Ruby);
}
int const_NoDebugger ()
{
return int (lym::Macro::None);
}
Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
gsi::method ("PlainTextFormat", &const_PlainTextFormat,
"@brief Indicates plain text format for \\storage_scheme\n"
gsi::EnumIn<lym::Macro, lym::Macro::Format> decl_FormatEnum ("lay", "Format",
gsi::enum_const ("PlainTextFormat", lym::Macro::PlainTextFormat,
"@brief The macro has plain text format"
) +
gsi::method ("PlainTextWithHashAnnotationsFormat", &const_PlainTextWithHashAnnotationsFormat,
"@brief Indicates plain text format for \\storage_scheme\n"
"This format is identical to \\PlainTextFormat but indicates that it is possible "
"to insert annotations (properties) into the text in a hash-commented header."
gsi::enum_const ("PlainTextWithHashAnnotationsFormat", lym::Macro::PlainTextWithHashAnnotationsFormat,
"@brief The macro has plain text format with special pseudo-comment annotations"
) +
gsi::method ("MacroFormat", &const_MacroFormat,
"@brief Indicates macro (XML) format for \\storage_scheme\n"
gsi::enum_const ("MacroFormat", lym::Macro::MacroFormat,
"@brief The macro has macro (XML) format"
),
"@brief Specifies the format of a macro\n"
"This enum has been introduced in version 0.27.5."
);
gsi::EnumIn<lym::Macro, lym::Macro::Interpreter> decl_InterpreterEnum ("lay", "Interpreter",
gsi::enum_const ("Ruby", lym::Macro::Ruby,
"@brief The interpreter is Ruby"
) +
gsi::enum_const ("Python", lym::Macro::Python,
"@brief The interpreter is Python"
) +
gsi::enum_const ("Text", lym::Macro::Text,
"@brief Plain text"
) +
gsi::enum_const ("DSLInterpreter", lym::Macro::DSLInterpreter,
"@brief A domain-specific interpreter (DSL)"
) +
gsi::enum_const ("None", lym::Macro::None,
"@brief No specific interpreter"
),
"@brief Specifies the interpreter used for executing a macro\n"
"This enum has been introduced in version 0.27.5."
);
lym::Macro::Interpreter const_RubyDebugger ()
{
return lym::Macro::Ruby;
}
lym::Macro::Interpreter const_NoDebugger ()
{
return lym::Macro::None;
}
Class<MacroInterpreterImpl> decl_MacroInterpreter ("lay", "MacroInterpreter",
gsi::method ("RubyDebugger", &const_RubyDebugger,
"@brief Indicates Ruby debugger for \\debugger_scheme\n"
) +
gsi::method ("NoDebugger", &const_NoDebugger,
"@brief Indicates no debugging for \\debugger_scheme\n"
) +
gsi::method ("register", &MacroInterpreter::register_gsi, gsi::arg ("name"),
gsi::method ("register", &MacroInterpreterImpl::register_gsi, gsi::arg ("name"),
"@brief Registers the macro interpreter\n"
"@param name The interpreter name. This is an arbitrary string which should be unique.\n"
"\n"
@ -307,7 +316,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"is set to 'dsl' can use this object to run the script. For executing a script, the system will "
"call the interpreter's \\execute method.\n"
) +
gsi::method ("create_template", &MacroInterpreter::create_template, gsi::arg ("url"),
gsi::method ("create_template", &MacroInterpreterImpl::create_template, gsi::arg ("url"),
"@brief Creates a new macro template\n"
"@param url The template will be initialized from that URL.\n"
"\n"
@ -317,7 +326,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"\n"
"This method must be called after \\register has called.\n"
) +
gsi::method ("supports_include_expansion=", &MacroInterpreter::set_supports_include_expansion, gsi::arg ("flag"),
gsi::method ("supports_include_expansion=", &MacroInterpreterImpl::set_supports_include_expansion, gsi::arg ("flag"),
"@brief Sets a value indicating whether this interpreter supports the default include file expansion scheme.\n"
"If this value is set to true (the default), lines like '# %include ...' will be substituted by the "
"content of the file following the '%include' keyword.\n"
@ -325,7 +334,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"\n"
"This attribute has been introduced in version 0.27.\n"
) +
gsi::method ("syntax_scheme=", &gsi::MacroInterpreter::set_syntax_scheme, gsi::arg ("scheme"),
gsi::method ("syntax_scheme=", &MacroInterpreterImpl::set_syntax_scheme, gsi::arg ("scheme"),
"@brief Sets a string indicating the syntax highlighter scheme\n"
"\n"
"The scheme string can be empty (indicating no syntax highlighting), \"ruby\" for the Ruby syntax "
@ -337,7 +346,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for "
"performance reasons in version 0.25.\n"
) +
gsi::method ("debugger_scheme=", &gsi::MacroInterpreter::set_debugger_scheme, gsi::arg ("scheme"),
gsi::method ("debugger_scheme=", &MacroInterpreterImpl::set_debugger_scheme, gsi::arg ("scheme"),
"@brief Sets the debugger scheme (which debugger to use for the DSL macro)\n"
"\n"
"The value can be one of the constants \\RubyDebugger or \\NoDebugger.\n"
@ -347,7 +356,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for "
"performance reasons in version 0.25.\n"
) +
gsi::method ("storage_scheme=", &gsi::MacroInterpreter::set_storage_scheme, gsi::arg ("scheme"),
gsi::method ("storage_scheme=", &MacroInterpreterImpl::set_storage_scheme, gsi::arg ("scheme"),
"@brief Sets the storage scheme (the format as which the macro is stored)\n"
"\n"
"This value indicates how files for this DSL macro type shall be stored. "
@ -358,7 +367,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for "
"performance reasons in version 0.25.\n"
) +
gsi::method ("description=", &gsi::MacroInterpreter::set_description, gsi::arg ("description"),
gsi::method ("description=", &MacroInterpreterImpl::set_description, gsi::arg ("description"),
"@brief Sets a description string\n"
"\n"
"This string is used for showing the type of DSL macro in the file selection box together with the "
@ -369,7 +378,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for "
"performance reasons in version 0.25.\n"
) +
gsi::method ("suffix=", &gsi::MacroInterpreter::set_suffix, gsi::arg ("suffix"),
gsi::method ("suffix=", &MacroInterpreterImpl::set_suffix, gsi::arg ("suffix"),
"@brief Sets the file suffix\n"
"\n"
"This string defines which file suffix to associate with the DSL macro. If an empty string is given (the default) "
@ -380,7 +389,7 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for "
"performance reasons in version 0.25.\n"
) +
gsi::callback ("executable", &gsi::MacroInterpreter::executable, &gsi::MacroInterpreter::f_executable, gsi::arg ("macro"),
gsi::callback ("executable", &MacroInterpreterImpl::executable, &MacroInterpreterImpl::f_executable, gsi::arg ("macro"),
"@brief Returns the executable object which implements the macro execution\n"
"This method must be reimplemented to return an \\Executable object for the actual implementation. "
"The system will use this function to execute the script when a macro with interpreter type 'dsl' and the "
@ -469,6 +478,9 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
"This class has been introduced in version 0.23 and modified in 0.27.\n"
);
// Inject the Macro::Format declarations into MacroInterpreter:
gsi::ClassExt<MacroInterpreterImpl> inject_Format_in_parent (decl_FormatEnum.defs ());
static lym::Macro *macro_by_path (const std::string &path)
{
return lym::MacroCollection::root ().find_macro (path);
@ -492,7 +504,133 @@ static int real_line (const std::string &path, int line)
}
}
lym::Macro *new_from_path (const std::string &path)
{
std::unique_ptr<lym::Macro> m (new lym::Macro ());
m->set_is_file ();
m->set_file_path (path);
m->load_from (path);
return m.release ();
}
Class<lym::Macro> decl_Macro ("lay", "Macro",
gsi::constructor ("new", &new_from_path, gsi::arg ("path"),
"@brief Loads the macro from the given file path\n"
"\n"
"This constructor has been introduced in version 0.27.5.\n"
) +
gsi::method ("run", &lym::Macro::run,
"@brief Executes the macro\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("save_to", &lym::Macro::save_to, gsi::arg ("path"),
"@brief Saves the macro to the given file\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("version", &lym::Macro::version,
"@brief Gets the macro's version\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("version=", &lym::Macro::set_version, gsi::arg ("version"),
"@brief Sets the macro's version\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("doc", &lym::Macro::doc,
"@brief Gets the macro's documentation string\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("doc=", &lym::Macro::set_doc, gsi::arg ("doc"),
"@brief Sets the macro's documentation string\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("shortcut", &lym::Macro::shortcut,
"@brief Gets the macro's keyboard shortcut\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("shortcut=", &lym::Macro::set_shortcut, gsi::arg ("shortcut"),
"@brief Sets the macro's keyboard shortcut\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("is_autorun?", &lym::Macro::is_autorun,
"@brief Gets a flag indicating whether the macro is automatically executed on startup\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("is_autorun=", &lym::Macro::set_autorun, gsi::arg ("flag"),
"@brief Sets a flag indicating whether the macro is automatically executed on startup\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("is_autorun_early?", &lym::Macro::is_autorun_early,
"@brief Gets a flag indicating whether the macro is automatically executed early on startup\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("is_autorun_early=", &lym::Macro::set_autorun_early, gsi::arg ("flag"),
"@brief Sets a flag indicating whether the macro is automatically executed early on startup\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("format", &lym::Macro::format,
"@brief Gets the macro's storage format\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("format=", &lym::Macro::set_format, gsi::arg ("format"),
"@brief Sets the macro's storage format\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("interpreter", &lym::Macro::interpreter,
"@brief Gets the macro's interpreter\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("interpreter=", &lym::Macro::set_interpreter, gsi::arg ("interpreter"),
"@brief Sets the macro's interpreter\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("interpreter_name", &lym::Macro::interpreter_name,
"@brief Gets the macro interpreter name\n"
"This is the string version of \\interpreter.\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("dsl_interpreter", &lym::Macro::dsl_interpreter,
"@brief Gets the macro's DSL interpreter name (if interpreter is DSLInterpreter)\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("dsl_interpreter=", &lym::Macro::set_dsl_interpreter, gsi::arg ("dsl_interpreter"),
"@brief Sets the macro's DSL interpreter name (if interpreter is DSLInterpreter)\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("sync_text_with_properties", &lym::Macro::sync_text_with_properties,
"@brief Synchronizes the macro text with the properties\n"
"\n"
"This method applies to PlainTextWithHashAnnotationsFormat format. The macro text will "
"be enhanced with pseudo-comments reflecting the macro properties. This way, the macro "
"properties can be stored in plain files.\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("sync_properties_with_text", &lym::Macro::sync_properties_with_text,
"@brief Synchronizes the macro properties with the text\n"
"\n"
"This method performs the reverse process of \\sync_text_with_properties.\n"
"\n"
"This method has been introduced in version 0.27.5.\n"
) +
gsi::method ("path", &lym::Macro::path,
"@brief Gets the path of the macro\n"
"\n"
@ -589,7 +727,7 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
"@brief Sets the menu path\n"
"See \\menu_path for details.\n"
) +
gsi::method ("real_path", &real_path,
gsi::method ("real_path", &real_path, gsi::arg ("path"), gsi::arg ("line"),
"@brief Gets the real path for an include-encoded path and line number\n"
"\n"
"When using KLayout's include scheme based on '# %include ...', __FILE__ and __LINE__ (Ruby) will "
@ -614,7 +752,7 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
"\n"
"This feature has been introduced in version 0.27."
) +
gsi::method ("real_line", &real_line,
gsi::method ("real_line", &real_line, gsi::arg ("path"), gsi::arg ("line"),
"@brief Gets the real line number for an include-encoded path and line number\n"
"\n"
"When using KLayout's include scheme based on '# %include ...', __FILE__ and __LINE__ (Ruby) will "
@ -647,7 +785,27 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
"This class is provided mainly to support generation of template macros in the "
"DSL interpreter framework provided by \\MacroInterpreter. The implementation may be "
"enhanced in future versions and provide access to macros stored inside KLayout's macro repository."
"\n"
"But it can be used to execute macro code in a consistent way:\n"
"\n"
"@code\n"
"path = \"path-to-macro.lym\"\n"
"RBA::Macro::new(path).run()\n"
"@/code\n"
"\n"
"Using the Macro class with \\run for executing code will chose the right interpreter and is "
"able to execute DRC and LVS scripts in the proper environment. This also provides an option to "
"execute Ruby code from Python and vice versa.\n"
"\n"
"In this scenario you can pass values to the script using \\Interpreter#define_variable. "
"The interpreter to choose for DRC and LVS scripts is \\Interpreter#ruby_interpreter. "
"For passing values back from the script, wrap the variable value into a \\Value object "
"which can be modified by the called script and read back by the caller."
);
// Inject the Macro::Format declarations into MacroInterpreter:
gsi::ClassExt<lym::Macro> inject_Format_in_macro (decl_FormatEnum.defs ());
gsi::ClassExt<lym::Macro> inject_Interpreter_in_macro (decl_InterpreterEnum.defs ());
}

View File

@ -62,7 +62,7 @@
<item>
<widget class="QSlider" name="zoom_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -153,6 +153,12 @@
</item>
<item>
<widget class="QSlider" name="vzoom_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>

View File

@ -203,7 +203,7 @@ D25View::activated ()
bool any = mp_ui->d25_view->attach_view (view ());
if (! any) {
mp_ui->d25_view->attach_view (0);
throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in the view.\nUse \"Tools/Manage Technologies\" to set up a z stack.")));
throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in this view.\nUse \"Tools/Manage Technologies\" to set up a z stack or check if it applies to the layers here.")));
}
mp_ui->d25_view->reset ();

View File

@ -311,13 +311,13 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
// Move "into" or "out"
double d = (event->key () == Qt::Key_Up ? 0.1 : -0.1);
double d = (event->key () == Qt::Key_Up ? 0.05 : -0.05);
QMatrix4x4 t;
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
QVector3D cd = t.inverted ().map (QVector3D (0, 0, cam_dist ()));
set_displacement (displacement () + d * cd);
set_displacement (displacement () + d * cd / m_scale_factor);
}
@ -327,7 +327,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
// Ctrl + left/right changes azimuths
double d = (event->key () == Qt::Key_Right ? 2 : -2);
double d = (event->key () == Qt::Key_Right ? 1 : -1);
double a = cam_azimuth () + d;
if (a < -180.0) {
@ -348,7 +348,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
QVector3D cd = t.inverted ().map (QVector3D (cam_dist (), 0, 0));
set_displacement (displacement () + d * cd);
set_displacement (displacement () + d * cd / m_scale_factor);
}
@ -634,11 +634,6 @@ D25ViewWidget::prepare_view ()
}
m_bbox &= cell_bbox;
if (m_bbox.empty ()) {
return false;
}
bool any = false;
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));

View File

@ -175,7 +175,8 @@ static void reset_interpreter ()
}
PythonInterpreter::PythonInterpreter (bool embedded)
: mp_current_console (0), mp_current_exec_handler (0), m_current_exec_level (0),
: gsi::Interpreter (0, "pya"),
mp_current_console (0), mp_current_exec_handler (0), m_current_exec_level (0),
m_in_trace (false), m_block_exceptions (false), m_ignore_next_exception (false),
mp_current_frame (NULL), mp_py3_app_name (0), m_embedded (embedded)
{
@ -572,7 +573,7 @@ PythonInterpreter::inspector (int context)
}
void
PythonInterpreter::define_variable (const std::string &name, const std::string &value)
PythonInterpreter::define_variable (const std::string &name, const tl::Variant &value)
{
PythonPtr main_module (PyImport_AddModule ("__main__"));
PythonPtr dict (PyModule_GetDict (main_module.get ()));

Some files were not shown because too many files have changed in this diff Show More