mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' of github.com:KLayout/klayout into qt6
This commit is contained in:
commit
fb4caf15ac
21
Changelog
21
Changelog
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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.
Binary file not shown.
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 206 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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("")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
8
setup.py
8
setup.py
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlUtils.h"
|
||||
|
||||
namespace db
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "dbLayoutUtils.h"
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
namespace db
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ..
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "dbEdgePairRelations.h"
|
||||
#include "dbLocalOperation.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
|
|
@ -10,7 +10,7 @@ SOURCES = \
|
|||
dbCompoundOperationTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
dbRecursiveInstanceIteratorTests.cc \
|
||||
dbRegionUtilsTests.cc \
|
||||
dbRegionCheckUtilsTests.cc \
|
||||
dbUtilsTests.cc \
|
||||
dbWriterTools.cc \
|
||||
dbLoadLayoutOptionsTests.cc \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1751,6 +1751,7 @@ CODE
|
|||
%w(
|
||||
enc
|
||||
enclosing
|
||||
enclosed
|
||||
overlap
|
||||
sep
|
||||
separation
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 < 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 < 0.2 um
|
||||
in = layer(1, 0)
|
||||
other = layer(2, 0)
|
||||
errors = in.drc(enclosed(other) < 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) < 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)
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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 ...")));
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue