diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d547472b0..7d9c02da0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,10 +34,10 @@ jobs: android: true dotnet: true haskell: true - large-packages: false # not working currently + large-packages: true - uses: hmarr/debug-action@v2 - name: Cancel Workflow Action - uses: styfle/cancel-workflow-action@0.12.0 + uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - name: ccache uses: hendrikmuhs/ccache-action@v1.2 @@ -52,7 +52,7 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.16.2 + uses: pypa/cibuildwheel@v2.16.5 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value diff --git a/Changelog b/Changelog index 349ba2941..5a9b54af4 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,27 @@ -0.28.15 (2023-01-04): +0.28.17 (2024-02-16): +* Enhancement: %GITHUB%/issues/1626 Technology specific grids + +0.28.16 (2024-02-12): +* Bugfix: %GITHUB%/issues/1623 Package installation with "-y" from command line does not work for URL or file +* Bugfix: %GITHUB%/issues/1619 Segfault on Fedora-39 with Python 3.12.1 +* Bugfix: %GITHUB%/issues/1618 Leaking Python reference causing trouble with iterators +* Bugfix: %GITHUB%/issues/1616 DRC doc typo +* Bugfix: %GITHUB%/issues/1614 LEF/DEF .map should not map boundaries and fill to ALL purpose +* Bugfix: %GITHUB%/issues/1609 Cell.read doesn't read LayoutMetaInfo +* Bugfix: %GITHUB%/issues/1608 CustomResistorExtraction: show faulty device in GUI +* Bugfix: %GITHUB%/issues/1603 Weird parameter dialog resizing when hiding / showing many parameters at once with callback_impl +* Bugfix: %GITHUB%/issues/1602 [Qt6] Cannot normally quit the main application window +* Bugfix: %GITHUB%/issues/1594 "connect" (in flat mode) + multiple top cells give an error message +* Bugfix: %GITHUB%/issues/1592 DXF file parsing error, about spline curve +* Enhancement: DRC enhancements related to touching edges + - New DRC function switches: "without_touching_corners", "without_touching_edges" + to skip width and space errors at kissing corners and touching-edge configurations + - Kissing corners are now detected also in non-collinear edge configurations +* Bugfix: fixed rendering of color selection buttons on High-DPI screens +* Bugfix: cross-hair cursor should not use selection default line width and styles +* Enhancement: OASIS reader errors out on broken OASIS with duplicate CELLNAMEs + +0.28.15 (2024-01-04): * Bugfix: %GITHUB%/issues/1578 Missing strm2*.exe in the Windows portable binary package (*.zip) * Enhancement: %GITHUB%/issues/1569 Make "toggle (selected) layer" key-bindable - Plus feature: "visibility follows selection" diff --git a/Changelog.Debian b/Changelog.Debian index d9933d6b3..8a6a53a23 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,17 @@ +klayout (0.28.17-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Fri, 16 Feb 2024 15:52:09 +0100 + +klayout (0.28.16-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Mon, 12 Feb 2024 00:00:00 +0100 + klayout (0.28.15-1) unstable; urgency=low * New features and bugfixes diff --git a/macbuild/ReadMe.md b/macbuild/ReadMe.md index 4d2fbea10..cfd8fe3c4 100644 --- a/macbuild/ReadMe.md +++ b/macbuild/ReadMe.md @@ -1,9 +1,9 @@ -Relevant KLayout version: 0.28.15
+Relevant KLayout version: 0.28.17
Author: Kazzz-S
-Last modified: 2024-01-05
+Last modified: 2024-02-16
# 1. Introduction -This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.28.13 or later for different 64-bit macOS, including: +This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.28.17 or later for different 64-bit macOS, including: * Monterey (12.x) : the primary development environment * Ventura (13.x) : experimental * Sonoma (14.x) : -- ditto -- @@ -51,6 +51,8 @@ Some typical use cases are described in Section 6. # 4. Prerequisites You need to have the followings: * The latest Xcode and command-line tool kit compliant with each OS + * https://developer.apple.com/xcode/resources/ + * https://mac.install.guide/commandlinetools/4 * Qt5 package from Homebrew, MacPorts, or Anaconda3 * Optionally, Ruby and Python packages from Homebrew, MacPorts, or Anaconda3 #### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`. @@ -63,12 +65,12 @@ The operating system type is detected automatically. ``` --------------------------------------------------------------------------------------------------------- << Usage of 'build4mac.py' >> - for building KLayout 0.28.13 or later on different Apple macOS platforms. + for building KLayout 0.28.17 or later on different Apple macOS platforms. $ [python] ./build4mac.py option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value --------------------------------------------------------------------------------------+--------------- - [-q|--qt ] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | qt5brew + [-q|--qt ] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | qt5macports : 'Qt6MacPorts', 'Qt6Brew'] | : Qt5MacPorts: use Qt5 from MacPorts | : Qt5Brew: use Qt5 from Homebrew | @@ -76,20 +78,21 @@ $ [python] ./build4mac.py : Qt6MacPorts: use Qt6 from MacPorts (*) | : Qt6Brew: use Qt6 from Homebrew (*) | : (*) migration to Qt6 is ongoing | - [-r|--ruby ] : case-insensitive type=['nil', 'Sys', 'MP32', 'HB32', 'Ana3'] | hb32 + [-r|--ruby ] : case-insensitive type=['nil', 'Sys', 'MP33', 'HB33', 'Ana3'] | sys : nil: don't bind Ruby | : Sys: use [Sonoma|Ventura|Monterey]-bundled Ruby 2.6 | - : MP32: use Ruby 3.2 from MacPorts | - : HB32: use Ruby 3.2 from Homebrew | + : MP33: use Ruby 3.3 from MacPorts | + : HB33: use Ruby 3.3 from Homebrew | : Ana3: use Ruby 3.2 from Anaconda3 | - [-p|--python ] : case-insensitive type=['nil', 'MP311', 'HB311', 'Ana3', | hb311 - : 'MP39', 'hb311', 'HBAuto'] | + [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP311', 'HB311', 'Ana3', | sys + : 'MP39', 'HB39', 'HBAuto'] | : nil: don't bind Python | + : Sys: use [Sonoma|Ventura|Monterey]-bundled Python 3.9 | : MP311: use Python 3.11 from MacPorts | : HB311: use Python 3.11 from Homebrew | : Ana3: use Python 3.11 from Anaconda3 | : MP39: use Python 3.9 from MacPorts (+) | - : hb311: use Python 3.9 from Homebrew (+) | + : HB39: use Python 3.9 from Homebrew (+) | : (+) for the backward compatibility tests | : HBAuto: use the latest Python 3.x auto-detected from Homebrew | [-P|--buildPymod] : build and deploy Pymod (*.whl) for LW-*.dmg | disabled @@ -118,24 +121,55 @@ $ [python] ./build4mac.py In this section, the actual file and directory names are those obtained on macOS Monterey.
On different OS, those names differ accordingly. -### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts -This build has been discontinued. +### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts Qt +0. Install MacPorts, then install Qt5 and libgit2 by +``` +$ sudo port install coreutils +$ sudo port install findutils +$ sudo port install qt5 +$ sudo port install libgit2 +``` -### 6B. Fully Homebrew-flavored build with Homebrew Ruby 3.2 and Homebrew Python 3.11 -0. Install Homebrew, then install Qt5, Ruby 3.2, Python 3.11, and libgit2 by +Confirm that you have: +``` +/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/* +``` + As of this writing, the provided Python version is `3.9.6`. + +1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Monterey, Ventura, and Sonoma. +``` +$ cd /where/'build.sh'/exists +$ ./build4mac.py -q qt5macports -r sys -p sys +``` +2. Confirm successful build (it will take about one hour, depending on your machine spec). +3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-y" to deploy executables and libraries under **`klayout.app`** bundle.
+ The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.
+ +``` +$ ./build4mac.py -q qt5macports -r sys -p sys -y +``` + The application bundle **`klayout.app`** is located under:
+ **`ST-qt5MP.pkg.macos-Monterey-release-RsysPsys`** directory, where +* "ST-" means this is a standard package. +* "qt5MP" means that Qt5 from MacPorts is used. +* "RsysPsys" means that Ruby is 2.6 provided by OS; Python is 3.9 provided by OS. +4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. + +### 6B. Fully Homebrew-flavored build with Homebrew Ruby 3.3 and Homebrew Python 3.11 +0. Install Homebrew, then install Qt5, Ruby 3.3, Python 3.11, and libgit2 by ``` $ brew install qt@5 -$ brew install ruby@3.2 +$ brew install ruby@3.3 $ brew install python@3.11 $ brew install libgit2 $ cd /where/'build.sh'/exists $ cd macbuild $ ./python3HB.py -v 3.11 ``` -1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Monterey, Ventura, and Sonoma. +1. Invoke **`build4mac.py`** with the following options: ``` $ cd /where/'build.sh'/exists -$ ./build4mac.py -q qt5brew -r hb32 -p hb311 +$ ./build4mac.py -q qt5brew -r hb33 -p hb311 ``` 2. Confirm successful build (it will take about one hour, depending on your machine spec). 3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.
@@ -143,13 +177,13 @@ $ ./build4mac.py -q qt5brew -r hb32 -p hb311 If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**. ``` -$ ./build4mac.py -q qt5brew -r hb32 -p hb311 -Y +$ ./build4mac.py -q qt5brew -r hb33 -p hb311 -Y ``` The application bundle **`klayout.app`** is located under:
- **`LW-qt5Brew.pkg.macos-Monterey-release-Rhb32Phb311`** directory, where + **`LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311`** directory, where * "LW-" means this is a lightweight package. * "qt5Brew" means that Qt5 from Homebrew is used. -* "Rhb32Phb311" means that Ruby is 3.2 from Homebrew; Python is 3.11 from Homebrew. +* "Rhb33Phb311" means that Ruby is 3.3 from Homebrew; Python is 3.11 from Homebrew. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. ### 6C. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.11 @@ -184,13 +218,13 @@ $ ./build4mac.py -q qt5brew -r sys -p hb311 -y So far, the deployment of Homebrew Ruby is not supported.
Therefore, if you intend to use the "-y" option for deployment, you need to use the "-r sys" option for building. -### 6D. Fully MacPorts-flavored build with MacPorts Ruby 3.2 and MacPorts Python 3.11 -0. Install MacPorts, then install Qt5, Ruby 3.2, Python 3.11, and libgit2 by +### 6D. Fully MacPorts-flavored build with MacPorts Ruby 3.3 and MacPorts Python 3.11 +0. Install MacPorts, then install Qt5, Ruby 3.3, Python 3.11, and libgit2 by ``` $ sudo port install coreutils $ sudo port install findutils $ sudo port install qt5 -$ sudo port install ruby32 +$ sudo port install ruby33 $ sudo port install python311 $ sudo port install py311-pip $ sudo port install libgit2 @@ -198,7 +232,7 @@ $ sudo port install libgit2 1. Invoke **`build4mac.py`** with the following options: ``` $ cd /where/'build.sh'/exists -$ ./build4mac.py -q qt5macports -r mp32 -p mp311 +$ ./build4mac.py -q qt5macports -r mp33 -p mp311 ``` 2. Confirm successful build (it will take about one hour, depending on your machine spec). 3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.
@@ -206,13 +240,13 @@ $ ./build4mac.py -q qt5macports -r mp32 -p mp311 If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**. ``` -$ ./build4mac.py -q qt5macports -r mp32 -p mp311 -Y +$ ./build4mac.py -q qt5macports -r mp33 -p mp311 -Y ``` The application bundle **`klayout.app`** is located under:
- **`LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311`** directory, where + **`LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311`** directory, where * "LW-" means this is a lightweight package. * "qt5MP" means that Qt5 from MacPorts is used. -* "Rmp32Pmp311" means that Ruby is 3.2 from MacPorts; Python is 3.11 from MacPorts. +* "Rmp33Pmp311" means that Ruby is 3.3 from MacPorts; Python is 3.11 from MacPorts. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. ### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.2 and Anaconda3 Python 3.11 @@ -273,11 +307,11 @@ makeDMG4mac.py -> macbuild/makeDMG4mac.py 2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example, ``` $ cd /where/'build.sh'/exists -$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311 -m +$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 -m ``` This command will generate the two files below:
-* **`LW-klayout-0.28.13-macOS-Monterey-1-qt5MP-Rmp32Pmp311.dmg`** ---(1) the main DMG file -* **`LW-klayout-0.28.13-macOS-Monterey-1-qt5MP-Rmp32Pmp311.dmg.md5`** ---(2) MD5-value text file +* **`LW-klayout-0.28.17-macOS-Monterey-1-qt5MP-Rmp33Pmp311.dmg`** ---(1) the main DMG file +* **`LW-klayout-0.28.17-macOS-Monterey-1-qt5MP-Rmp33Pmp311.dmg.md5`** ---(2) MD5-value text file # Known issues Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.
diff --git a/macbuild/Resources/icon-resources/klayout-Std.Rocket.icns b/macbuild/Resources/icon-resources/klayout-Std.Rocket.icns new file mode 100644 index 000000000..95385871f Binary files /dev/null and b/macbuild/Resources/icon-resources/klayout-Std.Rocket.icns differ diff --git a/macbuild/Resources/icon-resources/klayout-Std.Rocket.logoist b/macbuild/Resources/icon-resources/klayout-Std.Rocket.logoist new file mode 100644 index 000000000..d55a0fd35 Binary files /dev/null and b/macbuild/Resources/icon-resources/klayout-Std.Rocket.logoist differ diff --git a/macbuild/Resources/icon-resources/klayout-Std.Rocket.png b/macbuild/Resources/icon-resources/klayout-Std.Rocket.png new file mode 100644 index 000000000..e04caba1d Binary files /dev/null and b/macbuild/Resources/icon-resources/klayout-Std.Rocket.png differ diff --git a/macbuild/Resources/script-bundle-A.zip b/macbuild/Resources/script-bundle-A.zip index 5f21746d8..3f7e538fc 100644 Binary files a/macbuild/Resources/script-bundle-A.zip and b/macbuild/Resources/script-bundle-A.zip differ diff --git a/macbuild/Resources/script-bundle-B.zip b/macbuild/Resources/script-bundle-B.zip index 3f66e0a95..9855a5f57 100644 Binary files a/macbuild/Resources/script-bundle-B.zip and b/macbuild/Resources/script-bundle-B.zip differ diff --git a/macbuild/Resources/script-bundle-H.zip b/macbuild/Resources/script-bundle-H.zip index 821e0e51c..f3d73ffb4 100644 Binary files a/macbuild/Resources/script-bundle-H.zip and b/macbuild/Resources/script-bundle-H.zip differ diff --git a/macbuild/Resources/script-bundle-P.zip b/macbuild/Resources/script-bundle-P.zip index def41bdc4..d8df82a29 100644 Binary files a/macbuild/Resources/script-bundle-P.zip and b/macbuild/Resources/script-bundle-P.zip differ diff --git a/macbuild/Resources/script-bundle-S.zip b/macbuild/Resources/script-bundle-S.zip new file mode 100644 index 000000000..164a970ba Binary files /dev/null and b/macbuild/Resources/script-bundle-S.zip differ diff --git a/macbuild/build4mac.py b/macbuild/build4mac.py index 687763d1c..de2204b04 100755 --- a/macbuild/build4mac.py +++ b/macbuild/build4mac.py @@ -5,7 +5,7 @@ # File: "macbuild/build4mac.py" # # The top Python script for building KLayout (http://www.klayout.de/index.php) -# version 0.28.13 or later on different Apple Mac OSX platforms. +# version 0.28.17 or later on different Apple Mac OSX platforms. #=============================================================================== import sys import os @@ -35,17 +35,17 @@ from build4mac_util import * #------------------------------------------------------------------------------- def GenerateUsage(platform): if platform.upper() in [ "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ] - myQt56 = "qt5brew" - myRuby = "hb32" - myPython = "hb311" - moduleset = ('qt5Brew', 'HB32', 'HB311') + myQt56 = "qt5macports" + myRuby = "sys" + myPython = "sys" + moduleset = ('Qt5MacPorts', 'Sys', 'Sys') else: # too obsolete raise Exception( "! Too obsolete platform <%s>" % platform ) usage = "\n" usage += "---------------------------------------------------------------------------------------------------------\n" usage += "<< Usage of 'build4mac.py' >>\n" - usage += " for building KLayout 0.28.13 or later on different Apple macOS platforms.\n" + usage += " for building KLayout 0.28.17 or later on different Apple 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" @@ -58,15 +58,16 @@ def GenerateUsage(platform): usage += " : Qt6MacPorts: use Qt6 from MacPorts (*) |\n" usage += " : Qt6Brew: use Qt6 from Homebrew (*) |\n" usage += " : (*) migration to Qt6 is ongoing |\n" - usage += " [-r|--ruby ] : case-insensitive type=['nil', 'Sys', 'MP32', 'HB32', 'Ana3'] | %s\n" % myRuby + usage += " [-r|--ruby ] : case-insensitive type=['nil', 'Sys', 'MP33', 'HB33', 'Ana3'] | %s\n" % myRuby usage += " : nil: don't bind Ruby |\n" usage += " : Sys: use [Sonoma|Ventura|Monterey]-bundled Ruby 2.6 |\n" - usage += " : MP32: use Ruby 3.2 from MacPorts |\n" - usage += " : HB32: use Ruby 3.2 from Homebrew |\n" + usage += " : MP33: use Ruby 3.3 from MacPorts |\n" + usage += " : HB33: use Ruby 3.3 from Homebrew |\n" usage += " : Ana3: use Ruby 3.2 from Anaconda3 |\n" - usage += " [-p|--python ] : case-insensitive type=['nil', 'MP311', 'HB311', 'Ana3', | %s\n" % myPython + usage += " [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP311', 'HB311', 'Ana3', | %s\n" % myPython usage += " : 'MP39', 'HB39', 'HBAuto'] |\n" usage += " : nil: don't bind Python |\n" + usage += " : Sys: use [Sonoma|Ventura|Monterey]-bundled Python 3.9 |\n" usage += " : MP311: use Python 3.11 from MacPorts |\n" usage += " : HB311: use Python 3.11 from Homebrew |\n" usage += " : Ana3: use Python 3.11 from Anaconda3 |\n" @@ -149,19 +150,19 @@ def Get_Default_Config(): # Set the default modules if Platform == "Sonoma": - ModuleQt = "Qt5Brew" - ModuleRuby = "Ruby32Brew" - ModulePython = "Python311Brew" + ModuleQt = "Qt5MacPorts" + ModuleRuby = "Sys" + ModulePython = "Sys" elif Platform == "Ventura": - ModuleQt = "Qt5Brew" - ModuleRuby = "Ruby32Brew" - ModulePython = "Python311Brew" + ModuleQt = "Qt5MacPorts" + ModuleRuby = "Sys" + ModulePython = "Sys" elif Platform == "Monterey": - ModuleQt = "Qt5Brew" - ModuleRuby = "Ruby32Brew" - ModulePython = "Python311Brew" + ModuleQt = "Qt5MacPorts" + ModuleRuby = "Sys" + ModulePython = "Sys" else: - ModuleQt = "Qt5Brew" + ModuleQt = "Qt5MacPorts" ModuleRuby = "nil" ModulePython = "nil" @@ -179,6 +180,7 @@ def Get_Default_Config(): DeployVerbose = 1 Version = GetKLayoutVersionFrom( "./version.sh" ) HBPythonIs39 = False # because ModulePython == "Python311Brew" by default + OSPython3FW = None # system Python3 frameworks in [ None, MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ] config = dict() config['ProjectDir'] = ProjectDir # project directory where "build.sh" exists @@ -204,6 +206,7 @@ def Get_Default_Config(): config['ModuleSet'] = ModuleSet # (Qt, Ruby, Python)-tuple config['ToolDebug'] = ToolDebug # debug level list for this tool config['HBPythonIs39'] = HBPythonIs39 # True if the Homebrew Python version <= 3.9 + config['OSPython3FW'] = OSPython3FW # system Python3 frameworks in [ None, MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ] # auxiliary variables on platform config['System'] = System # 6-tuple from platform.uname() config['Node'] = Node # - do - @@ -246,6 +249,7 @@ def Parse_CLI_Args(config): ModuleSet = config['ModuleSet'] ToolDebug = config['ToolDebug'] HBPythonIs39 = config['HBPythonIs39'] + OSPython3FW = config['OSPython3FW'] #----------------------------------------------------- # [2] Parse the CLI arguments @@ -257,7 +261,7 @@ def Parse_CLI_Args(config): p.add_option( '-r', '--ruby', dest='type_ruby', - help="Ruby type=['nil', 'Sys', 'MP32', 'HB32', 'Ana3']" ) + help="Ruby type=['nil', 'Sys', 'MP33', 'HB33', 'Ana3']" ) p.add_option( '-p', '--python', dest='type_python', @@ -331,9 +335,9 @@ def Parse_CLI_Args(config): help='check usage' ) if Platform.upper() in [ "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ] - p.set_defaults( type_qt = "qt5brew", - type_ruby = "hb32", - type_python = "hb311", + p.set_defaults( type_qt = "qt5macports", + type_ruby = "sys", + type_python = "sys", build_pymod = False, no_qt_binding = False, no_qt_uitools = False, @@ -390,8 +394,8 @@ def Parse_CLI_Args(config): candidates = dict() candidates['NIL'] = 'nil' candidates['SYS'] = 'Sys' - candidates['MP32'] = 'MP32' - candidates['HB32'] = 'HB32' + candidates['MP33'] = 'MP33' + candidates['HB33'] = 'HB33' candidates['ANA3'] = 'Ana3' try: choiceRuby = candidates[ opt.type_ruby.upper() ] @@ -403,18 +407,17 @@ def Parse_CLI_Args(config): if choiceRuby == "nil": ModuleRuby = 'nil' elif choiceRuby == "Sys": - choiceRuby = "Sys" if Platform == "Sonoma": ModuleRuby = 'RubySonoma' elif Platform == "Ventura": ModuleRuby = 'RubyVentura' elif Platform == "Monterey": ModuleRuby = 'RubyMonterey' - elif choiceRuby == "MP32": - ModuleRuby = 'Ruby32MacPorts' + elif choiceRuby == "MP33": + ModuleRuby = 'Ruby33MacPorts' NonOSStdLang = True - elif choiceRuby == "HB32": - ModuleRuby = 'Ruby32Brew' + elif choiceRuby == "HB33": + ModuleRuby = 'Ruby33Brew' NonOSStdLang = True elif choiceRuby == "Ana3": ModuleRuby = 'RubyAnaconda3' @@ -429,6 +432,7 @@ def Parse_CLI_Args(config): # (C) Determine the Python type candidates = dict() candidates['NIL'] = 'nil' + candidates['SYS'] = 'Sys' candidates['MP311'] = 'MP311' candidates['HB311'] = 'HB311' candidates['ANA3'] = 'Ana3' @@ -445,29 +449,49 @@ def Parse_CLI_Args(config): if choicePython == "nil": ModulePython = 'nil' HBPythonIs39 = None + OSPython3FW = None + elif choicePython == "Sys": + if Platform == "Sonoma": + ModulePython = 'PythonSonoma' + HBPythonIs39 = None + OSPython3FW = SonomaPy3FW + elif Platform == "Ventura": + ModulePython = 'PythonVentura' + HBPythonIs39 = None + OSPython3FW = VenturaPy3FW + elif Platform == "Monterey": + ModulePython = 'PythonMonterey' + HBPythonIs39 = None + OSPython3FW = MontereyPy3FW elif choicePython == "MP311": ModulePython = 'Python311MacPorts' HBPythonIs39 = None + OSPython3FW = None NonOSStdLang = True elif choicePython == "HB311": ModulePython = 'Python311Brew' HBPythonIs39 = False + OSPython3FW = None NonOSStdLang = True elif choicePython == "Ana3": ModulePython = 'PythonAnaconda3' HBPythonIs39 = None + OSPython3FW = None NonOSStdLang = True elif choicePython == "MP39": ModulePython = 'Python39MacPorts' HBPythonIs39 = None + OSPython3FW = None NonOSStdLang = True elif choicePython == "HB39": ModulePython = 'Python39Brew' HBPythonIs39 = True + OSPython3FW = None NonOSStdLang = True elif choicePython == "HBAuto": ModulePython = 'PythonAutoBrew' HBPythonIs39 = (HBPythonAutoVersion == "3.9") + OSPython3FW = None NonOSStdLang = True if ModulePython == '': print("") @@ -524,7 +548,7 @@ def Parse_CLI_Args(config): else: message += "a lightweight (LW-) package with Pymod excluding Qt5, Ruby, and Python..." elif DeploymentF: - if (ModuleRuby in RubySys) and (ModulePython in PythonSys): # won't meet this condition any more! + if (ModuleRuby in RubySys) and (ModulePython in PythonSys): PackagePrefix = "ST-" message += "a standard (ST-) package including Qt[5|6] and using OS-bundled Ruby and Python..." elif ModulePython in ['Python311Brew', 'Python39Brew', 'PythonAutoBrew']: @@ -568,6 +592,7 @@ def Parse_CLI_Args(config): config['ModuleSet'] = ModuleSet config['ToolDebug'] = ToolDebug config['HBPythonIs39'] = HBPythonIs39 + config['OSPython3FW'] = OSPython3FW if CheckComOnly: pp = pprint.PrettyPrinter( indent=4, width=140 ) @@ -606,6 +631,7 @@ def Get_Build_Parameters(config): DeploymentF = config['DeploymentF'] DeploymentP = config['DeploymentP'] PackagePrefix = config['PackagePrefix'] + OSPython3FW = config['OSPython3FW'] #----------------------------------------------------- # [2] Set parameters passed to the main Bash script @@ -652,7 +678,10 @@ def Get_Build_Parameters(config): parameters['bin'] = MacBinDir parameters['build'] = MacBuildDir - parameters['rpath'] = "@executable_path/../Frameworks" + if OSPython3FW in [ MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ]: + parameters['rpath'] = OSPython3FW + else: + parameters['rpath'] = "@executable_path/../Frameworks" # (E) want Qt bindings with Ruby scripts? parameters['no_qt_bindings'] = NoQtBindings @@ -698,7 +727,7 @@ def Get_Build_Parameters(config): # will be built if: # BuildPymod = True # Platform = [ 'Sonoma', 'Ventura', 'Monterey'] - # ModuleRuby = [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ] + # ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] # ModulePython = [ 'Python311MacPorts', 'Python39MacPorts', # 'Python311Brew', Python39Brew', 'PythonAutoBrew', # 'PythonAnaconda3' ] @@ -709,13 +738,13 @@ def Get_Build_Parameters(config): PymodDistDir = dict() if Platform in [ 'Sonoma', 'Ventura', 'Monterey' ]: - if ModuleRuby in [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]: + if ModuleRuby in [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]: if ModulePython in [ 'Python311MacPorts', 'Python39MacPorts' ]: - PymodDistDir[ModulePython] = 'dist-MP3' + PymodDistDir[ModulePython] = 'dist-MP3-%s' % ModuleQt elif ModulePython in [ 'Python311Brew', 'Python39Brew', 'PythonAutoBrew' ]: - PymodDistDir[ModulePython] = 'dist-HB3' + PymodDistDir[ModulePython] = 'dist-HB3-%s' % ModuleQt elif ModulePython in [ 'PythonAnaconda3' ]: - PymodDistDir[ModulePython] = 'dist-ana3' + PymodDistDir[ModulePython] = 'dist-ana3-%s' % ModuleQt parameters['pymod_dist'] = PymodDistDir return parameters @@ -732,7 +761,7 @@ def Build_pymod(parameters): # [1] will be built if: # BuildPymod = True # Platform = [ 'Sonoma', 'Ventura', 'Monterey'] - # ModuleRuby = [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ] + # ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] # ModulePython = [ 'Python311MacPorts', 'Python39MacPorts', # 'Python311Brew', Python39Brew', 'PythonAutoBrew', # 'PythonAnaconda3' ] @@ -745,7 +774,7 @@ def Build_pymod(parameters): return 0 if not Platform in [ 'Sonoma', 'Ventura', 'Monterey' ]: return 0 - elif not ModuleRuby in [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]: + elif not ModuleRuby in [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]: return 0 elif not ModulePython in [ 'Python311MacPorts', 'Python39MacPorts', 'PythonAnaconda3', \ 'Python311Brew', 'Python39Brew', 'PythonAutoBrew' ]: @@ -758,17 +787,17 @@ def Build_pymod(parameters): #-------------------------------------------------------------------- PymodDistDir = parameters['pymod_dist'] # Using MacPorts - if PymodDistDir[ModulePython] == 'dist-MP3': + if PymodDistDir[ModulePython].find('dist-MP3') >= 0: addBinPath = "/opt/local/bin" addIncPath = "/opt/local/include" addLibPath = "/opt/local/lib" # Using Homebrew - elif PymodDistDir[ModulePython] == 'dist-HB3': + elif PymodDistDir[ModulePython].find('dist-HB3') >= 0: addBinPath = "%s/bin" % DefaultHomebrewRoot # defined in "build4mac_env.py" addIncPath = "%s/include" % DefaultHomebrewRoot # -- ditto -- addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto -- # Using Anaconda3 - elif PymodDistDir[ModulePython] == 'dist-ana3': + elif PymodDistDir[ModulePython].find('dist-ana3') >= 0: addBinPath = "/Applications/anaconda3/bin" addIncPath = "/Applications/anaconda3/include" addLibPath = "/Applications/anaconda3/lib" @@ -1149,7 +1178,7 @@ def Deploy_Binaries_For_Bundle(config, parameters): if BuildPymod: try: PymodDistDir = parameters['pymod_dist'] - pymodDistDir = PymodDistDir[ModulePython] # [ 'dist-MP3', 'dist-HB3', 'dist-ana3' ] + pymodDistDir = PymodDistDir[ModulePython] # [ 'dist-MP3-${ModuleQt}', 'dist-HB3-${ModuleQt}', 'dist-ana3-${ModuleQt}' ] except KeyError: pymodDistDir = "" else: @@ -1800,15 +1829,15 @@ def Deploy_Binaries_For_Bundle(config, parameters): return 1 #------------------------------------------------------------- - # [10] Special deployment of Ruby3.2 from Homebrew? + # [10] Special deployment of Ruby3.3 from Homebrew? #------------------------------------------------------------- - deploymentRuby32HB = (ModuleRuby == 'Ruby32Brew') + deploymentRuby32HB = (ModuleRuby == 'Ruby33Brew') if deploymentRuby32HB and NonOSStdLang: print( "" ) - print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby32Path ) + print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby33Path ) print( " [!!!] Sorry, the deployed package will not work properly since deployment of" ) - print( " Ruby3.2 from Homebrew is not yet supported." ) + print( " Ruby3.3 from Homebrew is not yet supported." ) print( " Since you have Homebrew development environment, there two options:" ) print( " (1) Retry to make a package with '-Y|--DEPLOY' option." ) print( " This will not deploy any of Qt[5|6], Python, and Ruby from Homebrew." ) diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index d87149133..feb04bad8 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- #=============================================================================== @@ -6,7 +6,7 @@ # # Here are dictionaries of ... # different modules for building KLayout (http://www.klayout.de/index.php) -# version 0.28.13 or later on different Apple Mac OSX platforms. +# version 0.28.17 or later on different Apple Mac OSX platforms. # # This file is imported by 'build4mac.py' script. #=============================================================================== @@ -117,7 +117,7 @@ Qt6Brew = { 'qmake' : '%s/opt/qt@6/bin/qmake' % DefaultHomebrewRoot, #----------------------------------------------------- RubyNil = [ 'nil' ] RubySys = [ 'RubyMonterey', 'RubyVentura', 'RubySonoma' ] -RubyExt = [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ] +RubyExt = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] Rubies = RubyNil + RubySys + RubyExt #----------------------------------------------------- @@ -150,21 +150,21 @@ RubySonoma = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/ 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % SonomaSDK } -# Ruby 3.2 from MacPorts (https://www.macports.org/) -# install with 'sudo port install ruby32' -# [Key Type Name] = 'MP32' -Ruby32MacPorts = { 'exe': '/opt/local/bin/ruby3.2', - 'inc': '/opt/local/include/ruby-3.2.2', - 'lib': '/opt/local/lib/libruby.3.2.dylib' +# Ruby 3.3 from MacPorts (https://www.macports.org/) +# install with 'sudo port install ruby33' +# [Key Type Name] = 'MP33' +Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3', + 'inc': '/opt/local/include/ruby-3.3.0', + 'lib': '/opt/local/lib/libruby.3.3.dylib' } -# Ruby 3.2 from Homebrew -# install with 'brew install ruby@3.2' -# [Key Type Name] = 'HB32' -HBRuby32Path = '%s/opt/ruby@3.2' % DefaultHomebrewRoot -Ruby32Brew = { 'exe': '%s/bin/ruby' % HBRuby32Path, - 'inc': '%s/include/ruby-3.2.0' % HBRuby32Path, - 'lib': '%s/lib/libruby.3.2.dylib' % HBRuby32Path +# Ruby 3.3 from Homebrew +# install with 'brew install ruby@3.3' +# [Key Type Name] = 'HB33' +HBRuby33Path = '%s/opt/ruby@3.3' % DefaultHomebrewRoot +Ruby33Brew = { 'exe': '%s/bin/ruby' % HBRuby33Path, + 'inc': '%s/include/ruby-3.3.0' % HBRuby33Path, + 'lib': '%s/lib/libruby.3.3.dylib' % HBRuby33Path } # Ruby 3.2 bundled with anaconda3 installed under /Applications/anaconda3/ @@ -181,8 +181,8 @@ RubyDictionary = { 'nil' : None, 'RubyMonterey' : RubyMonterey, 'RubyVentura' : RubyVentura, 'RubySonoma' : RubySonoma, - 'Ruby32MacPorts': Ruby32MacPorts, - 'Ruby32Brew' : Ruby32Brew, + 'Ruby33MacPorts': Ruby33MacPorts, + 'Ruby33Brew' : Ruby33Brew, 'RubyAnaconda3' : RubyAnaconda3 } @@ -195,7 +195,7 @@ RubyDictionary = { 'nil' : None, # for the previous states. #----------------------------------------------------- PythonNil = [ 'nil' ] -PythonSys = [ ] +PythonSys = [ 'PythonMonterey', 'PythonVentura', 'PythonSonoma' ] PythonExt = [ 'Python39MacPorts', 'Python39Brew' ] PythonExt += [ 'Python311MacPorts', 'Python311Brew' ] PythonExt += [ 'PythonAnaconda3', 'PythonAutoBrew' ] @@ -204,6 +204,33 @@ Pythons = PythonNil + PythonSys + PythonExt #----------------------------------------------------- # Whereabouts of different components of Python #----------------------------------------------------- +# Bundled with Monterey (12.x) +# [Key Type Name] = 'Sys' +MontereyPy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks" +MontereyPy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks" +PythonMonterey = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % MontereyPy3FW, + 'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % MontereyPy3FW, + 'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % MontereyPy3FW + } + +# Bundled with Ventura (13.x) +# [Key Type Name] = 'Sys' +VenturaPy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks" +VenturaPy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks" +PythonVentura = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % VenturaPy3FW, + 'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % VenturaPy3FW, + 'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % VenturaPy3FW + } + +# Bundled with Sonoma (14.x) +# [Key Type Name] = 'Sys' +SonomaPy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks" +SonomaPy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks" +PythonSonoma = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % SonomaPy3FW, + 'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % SonomaPy3FW, + 'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % SonomaPy3FW + } + # Python 3.9 from MacPorts (https://www.macports.org/) # install with 'sudo port install python39' # [Key Type Name] = 'MP39' @@ -299,6 +326,9 @@ else: # Consolidated dictionary kit for Python PythonDictionary = { 'nil' : None, + 'PythonMonterey' : PythonMonterey, + 'PythonVentura' : PythonVentura, + 'PythonSonoma' : PythonSonoma, 'Python39MacPorts' : Python39MacPorts, 'Python311MacPorts': Python311MacPorts, 'Python39Brew' : Python39Brew, @@ -312,7 +342,7 @@ if _have_Homebrew_Python: # [4] KLayout executables including buddy tools #----------------------------------------------------- KLayoutExecs = [ 'klayout' ] -KLayoutExecs += [ 'strm2cif', 'strm2dxf', 'strm2gds', 'strm2gdstxt', 'strm2oas' ] +KLayoutExecs += [ 'strm2cif', 'strm2dxf', 'strm2gds', 'strm2gdstxt', 'strm2mag', 'strm2oas' ] KLayoutExecs += [ 'strm2txt', 'strmclip', 'strmcmp', 'strmrun', 'strmxor' ] #---------------- diff --git a/macbuild/build4mac_util.py b/macbuild/build4mac_util.py index 5d12d242f..8241aebf7 100755 --- a/macbuild/build4mac_util.py +++ b/macbuild/build4mac_util.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- #======================================================================================== @@ -6,7 +6,7 @@ # # Here are utility functions and classes ... # for building KLayout (http://www.klayout.de/index.php) -# version 0.26.1 or later on different Apple Mac OSX platforms. +# version 0.28.17 or later on different Apple Mac OSX platforms. # # This file is imported by 'build4mac.py' script. #======================================================================================== diff --git a/macbuild/macQAT.py b/macbuild/macQAT.py index 36e389569..f62ffce16 100755 --- a/macbuild/macQAT.py +++ b/macbuild/macQAT.py @@ -5,7 +5,7 @@ # File: "macbuild/macQAT.py" # # The top Python script to run "ut_runner" after building KLayout -# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple +# (http://www.klayout.de/index.php) version 0.28.17 or later on different Apple # ßMac OSX platforms. # # This script must be copied to a "*.macQAT/" directory to run. diff --git a/macbuild/macQAT.sh b/macbuild/macQAT.sh index 888821b1b..e88c059da 100755 --- a/macbuild/macQAT.sh +++ b/macbuild/macQAT.sh @@ -4,7 +4,7 @@ # File: "macbuild/macQAT.sh" # # The top Bash script to run "ut_runner" after building KLayout -# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple +# (http://www.klayout.de/index.php) version 0.25.17 or later on different Apple # Mac OSX platforms. # # This script must be copied to a "*.macQAT/" directory to run. diff --git a/macbuild/macQAT2.sh b/macbuild/macQAT2.sh index c2fbd5445..59f9e4d83 100755 --- a/macbuild/macQAT2.sh +++ b/macbuild/macQAT2.sh @@ -4,7 +4,7 @@ # File: "macbuild/macQAT2.sh" # # The top Bash script to run "ut_runner" after building KLayout -# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple +# (http://www.klayout.de/index.php) version 0.28.17 or later on different Apple # Mac OSX platforms. # # This script must be copied to a directory that can be found in $PATH. diff --git a/macbuild/makeDMG4mac.py b/macbuild/makeDMG4mac.py index 566c9e203..1b2ae1683 100755 --- a/macbuild/makeDMG4mac.py +++ b/macbuild/makeDMG4mac.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- #============================================================================================= @@ -63,7 +63,7 @@ def SetGlobals(): global LatestOSHomebrew # True if 'LatestOS with Homebrew' and targeting LW-* global LatestOSAnaconda3 # True if 'LatestOS with Anaconda3' and targeting LW-* global LatestOSHomebrewH # True if 'LatestOS with Homebrew' and targeting HW-* - global DicLightHeavyW # dictionary for LW-* and HW-* packages + global DicStdLightHeavyW # dictionary for LW-* and HW-* packages global Item3AppleScript # ITEM_3 in the Apple script # auxiliary variables on platform global System # 6-tuple from platform.uname() @@ -77,13 +77,13 @@ def SetGlobals(): Usage = "\n" Usage += "---------------------------------------------------------------------------------------------------------\n" Usage += "<< Usage of 'makeDMG4mac.py' >>\n" - Usage += " for making a DMG file of KLayout 0.28.13 or later on different Apple macOS platforms.\n" + Usage += " for making a DMG file of KLayout 0.28.17 or later on different Apple macOS platforms.\n" Usage += "\n" Usage += "$ [python] ./makeDMG4mac.py\n" Usage += " option & argument : descriptions | default value\n" Usage += " ----------------------------------------------------------------------------------+-----------------\n" Usage += " <-p|--pkg > : package directory created by `build4mac.py` with [-y|-Y] | ``\n" - Usage += " : like 'LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311' | \n" + Usage += " : like 'LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311' | \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" @@ -158,51 +158,59 @@ def SetGlobals(): LatestOSHomebrew = False LatestOSAnaconda3 = False LatestOSHomebrewH = False - DicLightHeavyW = dict() + DicStdLightHeavyW = dict() Item3AppleScript = "" - # Populate DicLightHeavyW - DicLightHeavyW[ "ports" ] = dict() # LW-* - DicLightHeavyW[ "brew" ] = dict() # LW-* - DicLightHeavyW[ "ana3" ] = dict() # LW-* - DicLightHeavyW[ "brewH" ] = dict() # HW-* + # Populate DicStdLightHeavyW + DicStdLightHeavyW[ "std" ] = dict() # ST-* + DicStdLightHeavyW[ "ports" ] = dict() # LW-* + DicStdLightHeavyW[ "brew" ] = dict() # LW-* + DicStdLightHeavyW[ "ana3" ] = dict() # LW-* + DicStdLightHeavyW[ "brewH" ] = dict() # HW-* - DicLightHeavyW[ "ports" ]["zip"] = "macbuild/Resources/script-bundle-P.zip" - DicLightHeavyW[ "ports" ]["src"] = "script-bundle-P" - DicLightHeavyW[ "ports" ]["des"] = "MacPortsUser-ReadMeFirst" - DicLightHeavyW[ "ports" ]["item3"] = 'set position of item "MacPortsUser-ReadMeFirst" to {700, 400}' + DicStdLightHeavyW[ "std" ]["zip"] = "macbuild/Resources/script-bundle-S.zip" + DicStdLightHeavyW[ "std" ]["src"] = "script-bundle-S" + DicStdLightHeavyW[ "std" ]["des"] = "MacStdUser-ReadMeFirst" + DicStdLightHeavyW[ "std" ]["item3"] = 'set position of item "MacStdUser-ReadMeFirst" to {700, 400}' - DicLightHeavyW[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip" - DicLightHeavyW[ "brew" ]["src"] = "script-bundle-B" - DicLightHeavyW[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst" - DicLightHeavyW[ "brew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}' + DicStdLightHeavyW[ "ports" ]["zip"] = "macbuild/Resources/script-bundle-P.zip" + DicStdLightHeavyW[ "ports" ]["src"] = "script-bundle-P" + DicStdLightHeavyW[ "ports" ]["des"] = "MacPortsUser-ReadMeFirst" + DicStdLightHeavyW[ "ports" ]["item3"] = 'set position of item "MacPortsUser-ReadMeFirst" to {700, 400}' - DicLightHeavyW[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip" - DicLightHeavyW[ "ana3" ]["src"] = "script-bundle-A" - DicLightHeavyW[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst" - DicLightHeavyW[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-ReadMeFirst" to {700, 400}' + DicStdLightHeavyW[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip" + DicStdLightHeavyW[ "brew" ]["src"] = "script-bundle-B" + DicStdLightHeavyW[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst" + DicStdLightHeavyW[ "brew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}' - DicLightHeavyW[ "brewH" ]["zip"] = "macbuild/Resources/script-bundle-H.zip" - DicLightHeavyW[ "brewH" ]["src"] = "script-bundle-H" - DicLightHeavyW[ "brewH" ]["des"] = "Homebrew-HUser-ReadMeFirst" - DicLightHeavyW[ "brewH" ]["item3"] = 'set position of item "Homebrew-HUser-ReadMeFirst" to {700, 400}' + DicStdLightHeavyW[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip" + DicStdLightHeavyW[ "ana3" ]["src"] = "script-bundle-A" + DicStdLightHeavyW[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst" + DicStdLightHeavyW[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "brewH" ]["zip"] = "macbuild/Resources/script-bundle-H.zip" + DicStdLightHeavyW[ "brewH" ]["src"] = "script-bundle-H" + DicStdLightHeavyW[ "brewH" ]["des"] = "Homebrew-HUser-ReadMeFirst" + DicStdLightHeavyW[ "brewH" ]["item3"] = 'set position of item "Homebrew-HUser-ReadMeFirst" to {700, 400}' #------------------------------------------------------------------------------ ## To check the contents of the package directory # # The package directory name should look like: +# * ST-qt5MP.pkg.macos-Monterey-release-RsysPsys # * LW-qt5Ana3.pkg.macos-Monterey-release-Rana3Pana3 -# * LW-qt5Brew.pkg.macos-Monterey-release-Rhb32Phb311 --- (1) -# * LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311 +# * LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311 --- (1) +# * LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 # * HW-qt5Brew.pkg.macos-Monterey-release-RsysPhb311 # +# * ST-qt6MP.pkg.macos-Monterey-release-RsysPsys # * LW-qt6Ana3.pkg.macos-Monterey-release-Rana3Pana3 -# * LW-qt6Brew.pkg.macos-Monterey-release-Rhb32Phb311 -# * LW-qt6MP.pkg.macos-Monterey-release-Rmp32Pmp311 +# * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311 +# * LW-qt6MP.pkg.macos-Monterey-release-Rmp33Pmp311 # * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311 # # Generated DMG will be, for example, -# (1) ---> LW-klayout-0.28.13-macOS-Monterey-1-qt5Brew-Rhb32Phb311.dmg +# (1) ---> LW-klayout-0.28.17-macOS-Monterey-1-qt5Brew-Rhb33Phb311.dmg # # @return on success, positive integer in [MB] that tells approx. occupied disc space; # on failure, -1 @@ -223,7 +231,7 @@ def CheckPkgDirectory(): global LatestOSHomebrew global LatestOSAnaconda3 global LatestOSHomebrewH - global DicLightHeavyW + global DicStdLightHeavyW global Item3AppleScript #----------------------------------------------------------------------------- @@ -241,18 +249,20 @@ def CheckPkgDirectory(): #----------------------------------------------------------------------------------------------- # [2] Identify (Qt, Ruby, Python) from PkgDir + # * ST-qt5MP.pkg.macos-Monterey-release-RsysPsys # * LW-qt5Ana3.pkg.macos-Monterey-release-Rana3Pana3 - # * LW-qt5Brew.pkg.macos-Monterey-release-Rhb32Phb311 - # * LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311 + # * LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311 + # * LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 # * HW-qt5Brew.pkg.macos-Monterey-release-RsysPhb311 - # * EX-qt5MP.pkg.macos-Monterey-release-Rhb32Pmp311 + # * EX-qt5MP.pkg.macos-Monterey-release-Rhb33Pmp311 # + # * ST-qt6MP.pkg.macos-Monterey-release-RsysPsys # * LW-qt6Ana3.pkg.macos-Monterey-release-Rana3Pana3 - # * LW-qt6Brew.pkg.macos-Monterey-release-Rhb32Phb311 - # * LW-qt6MP.pkg.macos-Monterey-release-Rmp32Pmp311 + # * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311 + # * LW-qt6MP.pkg.macos-Monterey-release-Rmp33Pmp311 # * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311 #----------------------------------------------------------------------------------------------- - patQRP = u'(LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)' + patQRP = u'(ST|LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)' regQRP = re.compile(patQRP) if not regQRP.match(PkgDir): print( "! Cannot identify (Qt, Ruby, Python) from the package directory name" ) @@ -277,15 +287,20 @@ def CheckPkgDirectory(): #----------------------------------------------------------------------------- # [3] Check if the "LatestOS" with MacPorts / Homebrew / Anaconda3 #----------------------------------------------------------------------------- + LatestOSSys = Platform == LatestOS + LatestOSSys &= PackagePrefix == "ST" + LatestOSSys &= QtIdentification in [ "qt5MP", "qt6MP" ] + LatestOSSys &= RubyPythonID in [ "RsysPsys" ] + LatestOSMacPorts = Platform == LatestOS LatestOSMacPorts &= PackagePrefix == "LW" LatestOSMacPorts &= QtIdentification in [ "qt5MP", "qt6MP" ] - LatestOSMacPorts &= RubyPythonID in [ "Rmp32Pmp311", "Rmp32Pmp39" ] + LatestOSMacPorts &= RubyPythonID in [ "Rmp33Pmp311", "Rmp33Pmp39" ] LatestOSHomebrew = Platform == LatestOS LatestOSHomebrew &= PackagePrefix == "LW" LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew" ] - LatestOSHomebrew &= RubyPythonID in [ "Rhb32Phb311", "Rhb32Phb39", "Rhb32Phbauto" ] + LatestOSHomebrew &= RubyPythonID in [ "Rhb33Phb311", "Rhb33Phb39", "Rhb33Phbauto" ] LatestOSAnaconda3 = Platform == LatestOS LatestOSAnaconda3 &= PackagePrefix == "LW" @@ -297,8 +312,23 @@ def CheckPkgDirectory(): LatestOSHomebrewH &= QtIdentification in [ "qt5Brew", "qt6Brew" ] LatestOSHomebrewH &= RubyPythonID in [ "RsysPhb311", "RsysPhb39", "RsysPhbauto" ] # Sys-Homebre hybrid + if LatestOSSys: + mydic = DicStdLightHeavyW["std"] + srcDir = PkgDir + "/" + mydic["src"] + desDir = PkgDir + "/" + mydic["des"] + if OpMake: + with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref: + zip_ref.extractall(PkgDir) + os.rename( srcDir, desDir ) + if OpClean: + if os.path.isdir(srcDir): + shutil.rmtree(srcDir) + if os.path.isdir(desDir): + shutil.rmtree(desDir) + Item3AppleScript = mydic["item3"] + if LatestOSMacPorts: - mydic = DicLightHeavyW["ports"] + mydic = DicStdLightHeavyW["ports"] srcDir = PkgDir + "/" + mydic["src"] desDir = PkgDir + "/" + mydic["des"] if OpMake: @@ -313,7 +343,7 @@ def CheckPkgDirectory(): Item3AppleScript = mydic["item3"] if LatestOSHomebrew: - mydic = DicLightHeavyW["brew"] + mydic = DicStdLightHeavyW["brew"] srcDir = PkgDir + "/" + mydic["src"] desDir = PkgDir + "/" + mydic["des"] if OpMake: @@ -328,7 +358,7 @@ def CheckPkgDirectory(): Item3AppleScript = mydic["item3"] if LatestOSAnaconda3: - mydic = DicLightHeavyW["ana3"] + mydic = DicStdLightHeavyW["ana3"] srcDir = PkgDir + "/" + mydic["src"] desDir = PkgDir + "/" + mydic["des"] if OpMake: @@ -343,7 +373,7 @@ def CheckPkgDirectory(): Item3AppleScript = mydic["item3"] if LatestOSHomebrewH: - mydic = DicLightHeavyW["brewH"] + mydic = DicStdLightHeavyW["brewH"] srcDir = PkgDir + "/" + mydic["src"] desDir = PkgDir + "/" + mydic["des"] if OpMake: diff --git a/macbuild/nightlyBuild.py b/macbuild/nightlyBuild.py index 5ce1d2c9d..7ffb3d416 100755 --- a/macbuild/nightlyBuild.py +++ b/macbuild/nightlyBuild.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/Applications/anaconda3/bin/python3 # -*- coding: utf-8 -*- import sys @@ -8,6 +8,20 @@ import glob import platform import optparse import subprocess +#------------------------------------------------------------------------------ +# In general, avoid setting the first line to '#!/usr/bin/env python3'. +# If so, when this script is invoked in the 'KLayoutNightlyBuild.app' script +# bundle created by Automator, the python3 will be the macOS-bundled python3, +# where pandas is not included by default. +# Therefore, it is better to use one of: +# 1) #!/Applications/anaconda3/bin/python3 (Anaconda3) +# 2) #!/usr/local/bin/python3 (Homebrew needs 'pip3 install pandas') +# 3) #!/opt/local/bin/python3 (MacPorts needs 'sudo pip3 install pandas') +# +# However, if we install 'pandas' and its dependencies to the system Python +# environment, we can also set '#!/usr/bin/env python3'. +#------------------------------------------------------------------------------ +import pandas as pd #------------------------------------------------------------------------------ ## To test if the platform is a member of valid platforms @@ -44,7 +58,7 @@ def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma' ] ): #------------------------------------------------------------------------------ def Get_Build_Target_Dict(): buildTargetDic = dict() - # buildTargetDic[0] = 'std' + buildTargetDic[0] = 'std' buildTargetDic[1] = 'ports' buildTargetDic[2] = 'brew' buildTargetDic[3] = 'brewHW' @@ -61,45 +75,47 @@ def Get_Build_Target_Dict(): # @param[in] platform platform name # # @return (dictionary1, dictionary2)-tupple -# dictionary1: key=mnemonic, value=build option list -# dictionary2: key=mnemonic, value=log file name +# dictionary1: key=(qtVer, mnemonic), value=build option list +# dictionary2: key=(qtVer, mnemonic), value=log file name #------------------------------------------------------------------------------ def Get_Build_Options( targetDic, platform ): - if QtType == 5: - qtType = "Qt5" - else: - qtType = "Qt6" - buildOp = dict() logfile = dict() - for key in targetDic.keys(): - target = targetDic[key] - if target == "std": # use 'Qt5MacPorts' that provides Qt 5.15.2~ to run on "Big Sur", too - buildOp["std"] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys' ] - logfile["std"] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPsys") - elif target == "ports": - buildOp["ports"] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP32', '-p', 'MP311' ] - logfile["ports"] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp32Pmp311") - elif target == "brew": - buildOp["brew"] = [ '-q', '%sBrew' % qtType, '-r', 'HB32', '-p', 'HB311' ] - logfile["brew"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb32Phb311") - elif target == "brewHW": - buildOp["brewHW"] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ] - logfile["brewHW"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311") - elif target == "ana3": - buildOp["ana3"] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3' ] - logfile["ana3"] = "%sAna3.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rana3Pana3") - elif target == "brewA": - buildOp["brewA"] = [ '-q', '%sBrew' % qtType, '-r', 'HB32', '-p', 'HBAuto' ] - logfile["brewA"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb32Phbauto") - elif target == "brewAHW": - buildOp["brewAHW"] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto' ] - logfile["brewAHW"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhbauto") - if WithPymod: - buildOp["ports"] = buildOp["ports"] + ['--buildPymod'] - buildOp["brew"] = buildOp["brew"] + ['--buildPymod'] - buildOp["ana3"] = buildOp["ana3"] + ['--buildPymod'] + for qtVer in [5, 6]: + if qtVer == 5: + qtType = "Qt5" + elif qtVer == 6: + qtType = "Qt6" + + for key in targetDic.keys(): + target = targetDic[key] + if target == "std": + buildOp[(qtVer, "std")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys' ] + logfile[(qtVer, "std")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPsys") + elif target == "ports": + buildOp[(qtVer, "ports")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP33', '-p', 'MP311' ] + logfile[(qtVer, "ports")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp33Pmp311") + elif target == "brew": + buildOp[(qtVer, "brew")] = [ '-q', '%sBrew' % qtType, '-r', 'HB33', '-p', 'HB311' ] + logfile[(qtVer, "brew")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb33Phb311") + elif target == "brewHW": + buildOp[(qtVer, "brewHW")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ] + logfile[(qtVer, "brewHW")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311") + elif target == "ana3": + buildOp[(qtVer, "ana3")] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3' ] + logfile[(qtVer, "ana3")] = "%sAna3.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rana3Pana3") + elif target == "brewA": + buildOp[(qtVer, "brewA")] = [ '-q', '%sBrew' % qtType, '-r', 'HB33', '-p', 'HBAuto' ] + logfile[(qtVer, "brewA")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb33Phbauto") + elif target == "brewAHW": + buildOp[(qtVer, "brewAHW")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto' ] + logfile[(qtVer, "brewAHW")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhbauto") + + if WithPymod: + buildOp[(qtVer,"ports")] = buildOp[(qtVer,"ports")] + ['--buildPymod'] + buildOp[(qtVer,"brew")] = buildOp[(qtVer,"brew")] + ['--buildPymod'] + buildOp[(qtVer,"ana3")] = buildOp[(qtVer,"ana3")] + ['--buildPymod'] return (buildOp, logfile) @@ -109,29 +125,34 @@ def Get_Build_Options( targetDic, platform ): # @param[in] targetDic build target dictionary # @param[in] platform platform name # -# @return a dictionary; key=mnemonic, value=".macQAT" directory +# @return a dictionary; key=(qtVer, mnemonic), value=".macQAT" directory #------------------------------------------------------------------------------ def Get_QAT_Directory( targetDic, platform ): - if QtType == 5: - qtType = "qt5" - else: - qtType = "qt6" - dirQAT = dict() - for key in targetDic.keys(): - target = targetDic[key] - if target == "ports": - dirQAT["ports"] = '%sMP.build.macos-%s-release-Rmp32Pmp311.macQAT' % (qtType, platform) - elif target == "brew": - dirQAT["brew"] = '%sBrew.build.macos-%s-release-Rhb32Phb311.macQAT' % (qtType, platform) - elif target == "brewHW": - dirQAT["brewHW"] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType, platform) - elif target == "ana3": - dirQAT["ana3"] = '%sAna3.build.macos-%s-release-Rana3Pana3.macQAT' % (qtType, platform) - elif target == "brewA": - dirQAT["brewA"] = '%sBrew.build.macos-%s-release-Rhb32Phbauto.macQAT' % (qtType, platform) - elif target == "brewAHW": - dirQAT["brewAHW"] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType, platform) + + for qtVer in [5, 6]: + if qtVer == 5: + qtType = "Qt5" + elif qtVer == 6: + qtType = "Qt6" + + for key in targetDic.keys(): + target = targetDic[key] + if target == "std": + dirQAT[(qtVer, "std")] = '%sMP.build.macos-%s-release-RsysPsys.macQAT' % (qtType.lower(), platform) + elif target == "ports": + dirQAT[(qtVer, "ports")] = '%sMP.build.macos-%s-release-Rmp33Pmp311.macQAT' % (qtType.lower(), platform) + elif target == "brew": + dirQAT[(qtVer, "brew")] = '%sBrew.build.macos-%s-release-Rhb33Phb311.macQAT' % (qtType.lower(), platform) + elif target == "brewHW": + dirQAT[(qtVer, "brewHW")] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform) + elif target == "ana3": + dirQAT[(qtVer, "ana3")] = '%sAna3.build.macos-%s-release-Rana3Pana3.macQAT' % (qtType.lower(), platform) + elif target == "brewA": + dirQAT[(qtVer, "brewA")] = '%sBrew.build.macos-%s-release-Rhb33Phbauto.macQAT' % (qtType.lower(), platform) + elif target == "brewAHW": + dirQAT[(qtVer, "brewAHW")] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType.lower(), platform) + return dirQAT #------------------------------------------------------------------------------ @@ -142,40 +163,45 @@ def Get_QAT_Directory( targetDic, platform ): # @param[in] srlDMG serial number of DMG # @param[in] makeflag True to make; False to clean # -# @return a dictionary; key=mnemonic, value=build option list +# @return a dictionary; key=(qtVer, mnemonic), value=build option list #------------------------------------------------------------------------------ def Get_Package_Options( targetDic, platform, srlDMG, makeflag ): - if QtType == 5: - qtType = "qt5" - else: - qtType = "qt6" + packOp = dict() if makeflag: flag = '-m' else: flag = '-c' - packOp = dict() - for key in targetDic.keys(): - target = targetDic[key] - if target == "ports": - packOp["ports"] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp32Pmp311' % (qtType, platform), - '-s', '%d' % srlDMG, '%s' % flag ] - elif target == "brew": - packOp["brew"] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb32Phb311' % (qtType, platform), - '-s', '%d' % srlDMG, '%s' % flag ] - elif target == "brewHW": - packOp["brewHW"] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType, platform), - '-s', '%d' % srlDMG, '%s' % flag ] - elif target == "ana3": - packOp["ana3"] = [ '-p', 'LW-%sAna3.pkg.macos-%s-release-Rana3Pana3' % (qtType, platform), - '-s', '%d' % srlDMG, '%s' % flag ] - elif target == "brewA": - packOp["brewA"] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb32Phbauto' % (qtType, platform), - '-s', '%d' % srlDMG, '%s' % flag ] - elif target == "brewAHW": - packOp["brewAHW"] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhbauto' % (qtType, platform), - '-s', '%d' % srlDMG, '%s' % flag ] + for qtVer in [5, 6]: + if qtVer == 5: + qtType = "Qt5" + elif qtVer == 6: + qtType = "Qt6" + + for key in targetDic.keys(): + target = targetDic[key] + if target == "std": + packOp[(qtVer, "std")] = [ '-p', 'ST-%sMP.pkg.macos-%s-release-RsysPsys' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "ports": + packOp[(qtVer, "ports")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp33Pmp311' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "brew": + packOp[(qtVer, "brew")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb33Phb311' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "brewHW": + packOp[(qtVer, "brewHW")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "ana3": + packOp[(qtVer, "ana3")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-release-Rana3Pana3' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "brewA": + packOp[(qtVer, "brewA")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb33Phbauto' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "brewAHW": + packOp[(qtVer, "brewAHW")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhbauto' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] return packOp #------------------------------------------------------------------------------ @@ -185,6 +211,7 @@ def Parse_CommandLine_Arguments(): global Usage # usage global QtType # Qt type global Target # target list + global QtTarget # list of (Qt, target)-tuple global Build # operation flag global WithPymod # operation flag global QATest # operation flag @@ -198,9 +225,9 @@ def Parse_CommandLine_Arguments(): platform = Test_My_Platform() if platform in [ "Sonoma", "Ventura", "Monterey" ]: - targetopt = "1,2,3,4" + targetopt = "0,1,2,3,4" else: - targetopt = "0" + targetopt = None Usage = "\n" Usage += "----------------------------------------------------------------------------------------------------------\n" @@ -209,33 +236,39 @@ def Parse_CommandLine_Arguments(): Usage += " macOS Monterey, Ventura, or Sonoma >>\n" Usage += "\n" Usage += "$ [python] nightlyBuild.py\n" - Usage += " option & argument : comment on option if any | default value\n" - Usage += " ------------------------------------------------------------------------+--------------\n" - Usage += " [--qt ] : 5='qt5', 6='qt6' (migration to Qt6 is ongoing) | 5\n" - Usage += " [--target ] : 1='ports', 2='brew', 3='brewHW', 4='ana3', | '%s'\n" % targetopt - Usage += " 5='brewA', 6='brewAHW' |\n" - Usage += " * with --qt=6, use --target='2,3' (4 is ignored) |\n" - Usage += " [--build] : build and deploy | disabled\n" - Usage += " [--pymod] : build and deploy Pymod, too | disabled\n" - Usage += " [--test] : run the QA Test | disabled\n" - Usage += " [--check] : check the QA Test results | disabled\n" - Usage += " [--makedmg|--cleandmg ] : make or clean DMGs | disabled\n" - Usage += " [--upload ] : upload DMGs to $HOME/Dropbox/klayout/ | disabled\n" - Usage += " [--dryrun] : dry-run for --build option | disabled\n" - Usage += " [-?|--?] : print this usage and exit | disabled\n" - Usage += " |\n" - Usage += " To use this script, make a symbolic link in the project root by: |\n" - Usage += " $ ln -s ./macbuild/nightlyBuild.py . |\n" - Usage += " |\n" - Usage += " Regular sequence for using this script: |\n" - Usage += " (1) $ ./nightlyBuild.py --build --pymod |\n" - Usage += " (2) (confirm the build results) |\n" - 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.28.12' |\n" - Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n" - Usage += "---------------------------------------------------------------------------+------------------------------\n" + Usage += " option & argument : comment on option if any | default value\n" + Usage += " --------------------------------------------------------------------------+--------------\n" + Usage += " [--qt ] : 5='qt5', 6='qt6' (migration to Qt6 is ongoing) | 5\n" + Usage += " [--target ] : 0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3', | '%s'\n" % targetopt + Usage += " 5='brewA', 6='brewAHW' |\n" + Usage += " * with --qt=6, use --target='0,1,2,3' (4 is ignored) |\n" + Usage += " [--qttarget ] : ex. '5,1' for qt=5, target=1 | disabled\n" + Usage += " + This option supersedes, if used, the --qt and --target combination. |\n" + Usage += " + You can use this option multiple times. |\n" + Usage += " + Or you can pass those list by the 'nightlyBuild.csv' file. |\n" + Usage += " A sample file 'macbuild/nightlyBuild.sample.csv' is available. |\n" + Usage += " [--build] : build and deploy | disabled\n" + Usage += " [--pymod] : build and deploy Pymod, too | disabled\n" + Usage += " [--test] : run the QA Test | disabled\n" + Usage += " [--check] : check the QA Test results | disabled\n" + Usage += " [--makedmg|--cleandmg ] : make or clean DMGs | disabled\n" + Usage += " [--upload ] : upload DMGs to $HOME/Dropbox/klayout/ | disabled\n" + Usage += " [--dryrun] : dry-run for --build option | disabled\n" + Usage += " [-?|--?] : print this usage and exit | disabled\n" + Usage += " |\n" + Usage += " To use this script, make a symbolic link in the project root by: |\n" + Usage += " $ ln -s ./macbuild/nightlyBuild.py . |\n" + Usage += " + edit and save ./macbuild/nightlyBuild.csv (optional) |\n" + Usage += " |\n" + Usage += " Regular sequence for using this script: |\n" + Usage += " (1) $ ./nightlyBuild.py --build --pymod |\n" + Usage += " (2) (confirm the build results) |\n" + 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.28.17' |\n" + Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n" + Usage += "-----------------------------------------------------------------------------+----------------------------\n" p = optparse.OptionParser( usage=Usage ) p.add_option( '--qt', @@ -246,6 +279,11 @@ def Parse_CommandLine_Arguments(): dest='targets', help='build target list' ) + p.add_option( '--qttarget', + action='append', + dest='qt_target', + help='(Qt, target)-tuple' ) + p.add_option( '--build', action='store_true', dest='build', @@ -296,6 +334,7 @@ def Parse_CommandLine_Arguments(): p.set_defaults( qt_type = "5", targets = "%s" % targetopt, + qt_target = list(), build = False, with_pymod = False, qa_test = False, @@ -331,9 +370,51 @@ def Parse_CommandLine_Arguments(): targetDic = Get_Build_Target_Dict() Target = list() for idx in targetIdx: - if idx in range(1, 7): + if idx in range(0, 7): Target.append( targetDic[idx] ) + # Populate QtTarget + QtTarget = list() + for target in Target: + QtTarget.append( (QtType, target) ) + QtType = None + Target = None + print( "# The --qt and --target combination specifies..." ) + print(QtTarget) + + if len(opt.qt_target) == 1 and opt.qt_target[0] == "nightlyBuild.csv": # reserved file name + QtTarget = list() + withqttarget = True + df = pd.read_csv( opt.qt_target[0], comment="#" ) + if len(df) == 0: + print( "! --qttarget==nightlyBuild.csv is used but DataFrame is empty" ) + print(Usage) + quit() + for i in range(0, len(df)): + qt = df.iloc[i,0] + idx = df.iloc[i,1] + if (qt == 5 and idx in range(0, 7)) or (qt == 6 and idx in [0,1,2,3, 5,6]): + QtTarget.append( (qt, targetDic[idx]) ) + elif len(opt.qt_target) > 0: + QtTarget = list() + withqttarget = True + for item in opt.qt_target: + qt = int(item.split(",")[0]) + idx = int(item.split(",")[1]) + if (qt == 5 and idx in range(0, 7)) or (qt == 6 and idx in [0,1,2,3, 5,6]): + QtTarget.append( (qt, targetDic[idx]) ) + else: + withqttarget = False + + if withqttarget: + if len(QtTarget) > 0: + print( "# The --qttarget option superseded the --qt and --target combination" ) + print(QtTarget) + else: + print( "! --qttarget is used but there is no valid (Qt, target)-tuple" ) + print(Usage) + quit() + Build = opt.build WithPymod = opt.with_pymod QATest = opt.qa_test @@ -373,17 +454,18 @@ def Build_Deploy(): myPlatform = Test_My_Platform() buildOp, logfile = Get_Build_Options( Get_Build_Target_Dict(), myPlatform ) - for key in Target: - if key == "ana3" and QtType == 6: # anaconda3 does not provide Qt6 so far + for qttype, key in QtTarget: + if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far continue - deplog = logfile[key].replace( ".log", ".dep.log" ) - command1 = [ pyBuilder ] + buildOp[key] + deplog = logfile[(qttype, key)].replace( ".log", ".dep.log" ) - if key in [ "brewHW", "brewAHW" ] : + command1 = [ pyBuilder ] + buildOp[(qttype, key)] + + if key in [ "std", "brewHW", "brewAHW" ] : command2 = "time" command2 += " \\\n %s" % pyBuilder - for option in buildOp[key]: + for option in buildOp[(qttype, key)]: command2 += " \\\n %s" % option command2 += " \\\n %s" % '-y' command2 += " 2>&1 | tee %s; \\\n" % deplog @@ -391,7 +473,7 @@ def Build_Deploy(): else: command2 = "time" command2 += " \\\n %s" % pyBuilder - for option in buildOp[key]: + for option in buildOp[(qttype, key)]: command2 += " \\\n %s" % option command2 += " \\\n %s" % '-Y' command2 += " 2>&1 | tee %s; \\\n" % deplog @@ -442,14 +524,14 @@ def Run_QATest( exclude ): myPlatform = Test_My_Platform() dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform ) - for key in Target: - if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far + for qttype, key in QtTarget: + if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far continue command1 = [ pyRunnerQAT ] + [ '--run', '--exclude', '%s' % exclude ] - print( dirQAT[key], command1 ) + print( dirQAT[(qttype, key)], command1 ) #continue - os.chdir( dirQAT[key] ) + os.chdir( dirQAT[(qttype, key)] ) if subprocess.call( command1, shell=False ) != 0: print( "", file=sys.stderr ) @@ -477,14 +559,14 @@ def Check_QATest_Results( lines ): myPlatform = Test_My_Platform() dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform ) - for key in Target: - if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far + for qttype, key in QtTarget: + if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far continue - os.chdir( dirQAT[key] ) + os.chdir( dirQAT[(qttype, key)] ) logfile = glob.glob( "*.log" ) command1 = [ tailCommand ] + [ '-n', '%d' % lines ] + logfile - print( dirQAT[key], command1 ) + print( dirQAT[(qttype, key)], command1 ) #continue if subprocess.call( command1, shell=False ) != 0: @@ -518,11 +600,11 @@ def DMG_Make( srlDMG ): shutil.rmtree( stashDMG ) os.mkdir( stashDMG ) - for key in Target: - if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far + for qttype, key in QtTarget: + if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far continue - command1 = [ pyDMGmaker ] + packOp[key] + command1 = [ pyDMGmaker ] + packOp[(qttype, key)] print(command1) #continue @@ -558,11 +640,11 @@ def DMG_Clean( srlDMG ): if os.path.isdir( stashDMG ): shutil.rmtree( stashDMG ) - for key in Target: - if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far + for qttype, key in QtTarget: + if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far continue - command1 = [ pyDMGmaker ] + packOp[key] + command1 = [ pyDMGmaker ] + packOp[(qttype, key)] print(command1) #continue diff --git a/macbuild/nightlyBuild.sample.csv b/macbuild/nightlyBuild.sample.csv new file mode 100644 index 000000000..edc13bc0a --- /dev/null +++ b/macbuild/nightlyBuild.sample.csv @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------------- +# File: 'nightlyBuild.csv' +# +# Descriptions: +# This file (located where "build.sh" exists) is to be passed to +# 'nightlyBuild.py' via the --qttarget option. +# +# qtVer,target +# where +# qtVer = 5 or 6 +# target = [0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3'] +# note that +# (qtVer,target)=(6,4) will be omitted +#------------------------------------------------------------------------------- +qtVer,target +5,0 +5,1 +5,2 +5,3 +5,4 +#6,0 +#6,1 +#6,2 +#6,3 +#6,4 diff --git a/macbuild/python3HB.py b/macbuild/python3HB.py index 118fe13f0..a04f0e64a 100755 --- a/macbuild/python3HB.py +++ b/macbuild/python3HB.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- #============================================================================== diff --git a/scripts/drc_lvs_doc/create_drc_samples.rb b/scripts/drc_lvs_doc/create_drc_samples.rb index b09ed46fd..2799b88e1 100644 --- a/scripts/drc_lvs_doc/create_drc_samples.rb +++ b/scripts/drc_lvs_doc/create_drc_samples.rb @@ -177,6 +177,37 @@ run_demo gen, "input.width(1.2, projection)", "drc_width2.png" run_demo gen, "input.width(1.2, square)", "drc_width3.png" run_demo gen, "input.width(1.2, whole_edges)", "drc_width4.png" +class Gen + def produce(s1, s2) + pts = [ + RBA::Point::new(0, 0), + RBA::Point::new(2000, 0), + RBA::Point::new(2000, 2000), + RBA::Point::new(0, 2000) + ]; + s1.insert(RBA::Polygon::new(pts)) + pts = [ + RBA::Point::new(2000, 2000), + RBA::Point::new(4000, 2000), + RBA::Point::new(4000, 4000), + RBA::Point::new(2000, 4000) + ]; + s1.insert(RBA::Polygon::new(pts)) + pts = [ + RBA::Point::new( 500, 4000), + RBA::Point::new(2500, 4000), + RBA::Point::new(2500, 6000), + RBA::Point::new( 500, 6000) + ]; + s1.insert(RBA::Polygon::new(pts)) + end +end + +gen = Gen::new + +run_demo gen, "input.width(1.0)", "drc_width5.png" +run_demo gen, "input.width(1.0, without_touching_corners)", "drc_width6.png" + class Gen def produce(s1, s2) pts = [ @@ -381,6 +412,45 @@ run_demo gen, "input1.sep(input2, 1.0, projection,\n" + " one_side_allowed,\n" + " two_opposite_sides_allowed)", "drc_separation11.png" +class Gen + def produce(s1, s2) + pts = [ + RBA::Point::new(0, 0), + RBA::Point::new(2000, 0), + RBA::Point::new(2000, 1500), + RBA::Point::new(0, 1500) + ]; + s2.insert(RBA::Polygon::new(pts)) + pts = [ + RBA::Point::new(2000, 1500), + RBA::Point::new(3500, 1500), + RBA::Point::new(3500, 3500), + RBA::Point::new(2000, 3500) + ]; + s1.insert(RBA::Polygon::new(pts)) + pts = [ + RBA::Point::new(1000, 3500), + RBA::Point::new(3000, 3500), + RBA::Point::new(3000, 5000), + RBA::Point::new(1000, 5000) + ]; + s2.insert(RBA::Polygon::new(pts)) + pts = [ + RBA::Point::new(1000, 5500), + RBA::Point::new(3000, 5500), + RBA::Point::new(3000, 7000), + RBA::Point::new(1000, 7000) + ]; + s1.insert(RBA::Polygon::new(pts)) + end +end + +gen = Gen::new + +run_demo gen, "input1.sep(input2, 1.0)", "drc_separation12.png" +run_demo gen, "input1.sep(input2, 1.0, without_touching_corners)", "drc_separation13.png" +run_demo gen, "input1.sep(input2, 1.0, without_touching_edges)", "drc_separation14.png" + # ... class Gen diff --git a/scripts/makedeb.sh b/scripts/makedeb.sh index bcc8c8a48..b2befb82c 100755 --- a/scripts/makedeb.sh +++ b/scripts/makedeb.sh @@ -20,6 +20,10 @@ buildopts= # TODO: derive this list automatically? case $target in + +debian12) + depends="python3-dev, libc6-dev, libgcc-s1, libgit2-1.5, libqt6core5compat6, libqt6designer6, libqt6gui6, libqt6multimedia6, libqt6multimediaquick6, libqt6network6, libqt6printsupport6, libqt6sql6, libqt6svg6, libqt6widgets6, libqt6xml6, libruby3.1, libstdc++6, zlib1g" + ;; ubuntu16) depends="libqt4-designer (>= 4.8.6), libqt4-xml (>= 4.8.6), libqt4-sql (>= 4.8.6), libqt4-network (>= 4.8.6), libqtcore4 (>= 4.8.6), libqtgui4 (>= 4.8.6), zlib1g (>= 1.2.8), libgit2-24 (>= 0.24.0), libruby2.3 (>= 2.3.1), python3 (>= 3.5.1), libpython3.5 (>= 3.5.1), libstdc++6 (>= 4.6.3), libc6 (>= 2.15)" # No HTTPS support - that is somewhat useless @@ -63,7 +67,7 @@ libdir="usr/lib/klayout" rm -rf $bininstdir # do the actual build -./build.sh -j2 \ +./build.sh -j$(nproc) \ -bin $bininstdir \ -build $builddir \ -rpath /$libdir \ diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui index 7e2c2365c..87e5dc3cc 100644 --- a/src/ant/ant/RulerConfigPage4.ui +++ b/src/ant/ant/RulerConfigPage4.ui @@ -6,8 +6,8 @@ 0 0 - 668 - 410 + 745 + 438 @@ -845,7 +845,12 @@ - Angle measurement (three mouse clicks) + Auto measure along edge (points will be set automatically) + + + + + Angle or radius measurement (three mouse clicks) diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc index 36576b00a..5ed7c7d0b 100644 --- a/src/ant/ant/antConfig.cc +++ b/src/ant/ant/antConfig.cc @@ -247,6 +247,8 @@ RulerModeConverter::to_string (ant::Template::ruler_mode_type m) return "single_click"; } else if (m == ant::Template::RulerAutoMetric) { return "auto_metric"; + } else if (m == ant::Template::RulerAutoMetricEdge) { + return "auto_metric_edge"; } else if (m == ant::Template::RulerMultiSegment) { return "multi_segment"; } else if (m == ant::Template::RulerThreeClicks) { @@ -266,6 +268,8 @@ RulerModeConverter::from_string (const std::string &s, ant::Template::ruler_mode a = ant::Template::RulerSingleClick; } else if (t == "auto_metric") { a = ant::Template::RulerAutoMetric; + } else if (t == "auto_metric_edge") { + a = ant::Template::RulerAutoMetricEdge; } else if (t == "multi_segment") { a = ant::Template::RulerMultiSegment; } else if (t == "angle") { diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index 86a14d11a..06680c2a8 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -60,6 +60,9 @@ static std::vector make_standard_templates () templates.push_back (ant::Template (tl::to_string (tr ("Measure")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure")); templates.back ().set_mode (ant::Template::RulerAutoMetric); + templates.push_back (ant::Template (tl::to_string (tr ("Measure edge")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure_edge")); + templates.back ().set_mode (ant::Template::RulerAutoMetricEdge); + templates.push_back (ant::Template (tl::to_string (tr ("Angle")), "", "", "$(sprintf('%.5g',G))°", ant::Object::STY_line, ant::Object::OL_angle, true, lay::AC_Global, "_angle")); templates.back ().set_mode (ant::Template::RulerThreeClicks); diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index f7506bea6..11b0d2fb7 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1056,8 +1056,18 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) m_drawing (false), m_current (), m_move_mode (MoveNone), m_seg_index (0), - m_current_template (0) -{ + m_current_template (0), + m_hover (false), + m_hover_wait (false), + m_hover_buttons (0), + m_mouse_in_window (false) +{ +#if defined(HAVE_QT) + m_timer.setInterval (100 /*hover time*/); + m_timer.setSingleShot (true); + connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ())); +#endif + mp_view->annotations_changed_event.add (this, &Service::annotations_changed); } @@ -1809,9 +1819,36 @@ Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int button return false; } +lay::TwoPointSnapToObjectResult +Service::auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl) +{ + // for auto-metric we need some cutline constraint - any or global won't do. + if (ac == lay::AC_Global) { + ac = tpl.angle_constraint (); + } + if (ac == lay::AC_Global) { + ac = m_snap_mode; + } + if (ac == lay::AC_Global) { + ac = lay::AC_Diagonal; + } + + db::DVector g; + if (m_grid_snap) { + g = db::DVector (m_grid, m_grid); + } + + double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); + snap_range *= 0.5; + + return lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0); +} + bool Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { + hover_reset (); + if (prio && (buttons & lay::LeftButton) != 0) { const ant::Template &tpl = current_template (); @@ -1852,27 +1889,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio } else if (tpl.mode () == ant::Template::RulerAutoMetric) { - // for auto-metric we need some cutline constraint - any or global won't do. - lay::angle_constraint_type ac = ac_from_buttons (buttons); - if (ac == lay::AC_Global) { - ac = tpl.angle_constraint (); - } - if (ac == lay::AC_Global) { - ac = m_snap_mode; - } - if (ac == lay::AC_Global) { - ac = lay::AC_Diagonal; - } - - db::DVector g; - if (m_grid_snap) { - g = db::DVector (m_grid, m_grid); - } - - double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); - snap_range *= 0.5; - - lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0); + lay::TwoPointSnapToObjectResult ee = auto_measure (p, ac_from_buttons (buttons), tpl); if (ee.any) { // begin the transaction @@ -1893,6 +1910,29 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio } + } else if (tpl.mode () == ant::Template::RulerAutoMetricEdge) { + + lay::PointSnapToObjectResult snap_details = snap1_details (p, true); + if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge) { + + // begin the transaction + if (manager ()) { + tl_assert (! manager ()->transacting ()); + manager ()->transaction (tl::to_string (tr ("Create ruler"))); + } + + m_current = ant::Object (snap_details.object_ref.p1 (), snap_details.object_ref.p2 (), 0, tpl); + show_message (); + + insert_ruler (m_current, true); + + // end the transaction + if (manager ()) { + manager ()->commit (); + } + + } + } else { m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second; @@ -1968,21 +2008,33 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type bool Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { - if (prio) { + if (! prio) { + return false; + } - lay::PointSnapToObjectResult snap_details; - if (m_drawing) { - snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)); - } else { - const ant::Template &tpl = current_template (); - snap_details = snap1_details (p, m_obj_snap && tpl.snap ()); - } + if (! m_drawing && m_mouse_in_window && view ()->transient_selection_mode ()) { - mouse_cursor_from_snap_details (snap_details); + // Restart hover timer + m_hover_wait = true; +#if defined(HAVE_QT) + m_timer.start (); +#endif + m_hover_point = p; + m_hover_buttons = buttons; } - if (m_drawing && prio) { + lay::PointSnapToObjectResult snap_details; + if (m_drawing) { + snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)); + } else { + const ant::Template &tpl = current_template (); + snap_details = snap1_details (p, m_obj_snap && tpl.snap () && (tpl.mode () != ant::Template::RulerAutoMetricEdge || ! view ()->transient_selection_mode ())); + } + + mouse_cursor_from_snap_details (snap_details); + + if (m_drawing) { set_cursor (lay::Cursor::cross); @@ -2284,6 +2336,84 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo } } +bool +Service::enter_event (bool /*prio*/) +{ + m_mouse_in_window = true; + return false; +} + +bool +Service::leave_event (bool) +{ + m_mouse_in_window = false; + hover_reset (); + return false; +} + +void +Service::hover_reset () +{ + if (m_hover_wait) { +#if defined(HAVE_QT) + m_timer.stop (); +#endif + m_hover_wait = false; + } + if (m_hover) { + // as we use the transient selection for the hover ruler, we have to remove it here + clear_transient_selection (); + m_hover = false; + } +} + +#if defined(HAVE_QT) +void +Service::timeout () +{ + m_hover_wait = false; + m_hover = true; + + // as we use the transient selection for the hover ruler, we have to remove it here + clear_transient_selection (); + + // transiently create an auto-metric ruler if requested + + ant::Object *ruler = 0; + + const ant::Template &tpl = current_template (); + if (tpl.mode () == ant::Template::RulerAutoMetric) { + + lay::TwoPointSnapToObjectResult ee = auto_measure (m_hover_point, ac_from_buttons (m_hover_buttons), tpl); + if (ee.any) { + m_current = ant::Object (ee.first, ee.second, 0, tpl); + ruler = &m_current; + } + + } else if (tpl.mode () == ant::Template::RulerAutoMetricEdge) { + + lay::PointSnapToObjectResult snap_details = snap1_details (m_hover_point, true); + if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge) { + m_current = ant::Object (snap_details.object_ref.p1 (), snap_details.object_ref.p2 (), 0, tpl); + ruler = &m_current; + } + + } + + if (ruler) { + + // HINT: there is no special style for "transient selection on rulers" + mp_transient_ruler = new ant::View (this, ruler, true /*not selected*/); + + if (! editables ()->has_selection ()) { + display_status (true); + } + + } + +} +#endif + bool Service::transient_select (const db::DPoint &pos) { diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index eaf664726..fb3ed67fe 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -39,6 +39,11 @@ #include #include +#if defined (HAVE_QT) +# include +# include +#endif + namespace ant { class LayoutViewBase; @@ -177,12 +182,19 @@ private: // ------------------------------------------------------------- -class ANT_PUBLIC Service - : public lay::EditorServiceBase, +class ANT_PUBLIC Service : +#if defined (HAVE_QT) + public QObject, +#endif + public lay::EditorServiceBase, public lay::Drawing, public db::Object { -public: +#if defined (HAVE_QT) +Q_OBJECT +#endif + +public: typedef lay::AnnotationShapes::iterator obj_iterator; /** @@ -341,6 +353,21 @@ public: */ virtual db::DBox selection_bbox (); + /** + * @brief Implementation of the editables API + */ + virtual bool enter_event (bool); + + /** + * @brief Implementation of the editables API + */ + virtual bool leave_event (bool); + + /** + * @brief Implementation of the editables API + */ + virtual void hover_reset (); + /** * @brief Transform the selection (reimplementation of lay::Editable interface) */ @@ -506,6 +533,11 @@ public: */ tl::Event annotation_selection_changed_event; +#if defined (HAVE_QT) +public slots: + void timeout (); +#endif + private: // Ruler display and snapping configuration tl::Color m_color; @@ -551,10 +583,22 @@ private: std::vector m_ruler_templates; unsigned int m_current_template; + // Hover detector + bool m_hover; + bool m_hover_wait; + db::DPoint m_hover_point; + unsigned int m_hover_buttons; +#if defined (HAVE_QT) + QTimer m_timer; +#endif + + bool m_mouse_in_window; + std::pair snap1 (const db::DPoint &p, bool obj_snap); lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap); std::pair snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); + lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl); const ant::Template ¤t_template () const; diff --git a/src/ant/ant/antTemplate.h b/src/ant/ant/antTemplate.h index f589cc91e..6310ce2c6 100644 --- a/src/ant/ant/antTemplate.h +++ b/src/ant/ant/antTemplate.h @@ -64,15 +64,20 @@ public: */ RulerAutoMetric = 2, + /** + * @brief The ruler is auto-metric along an edge: a single click will place a ruler and the ruler will extend to the edge below + */ + RulerAutoMetricEdge = 3, + /** * @brief The ruler an angle type (two segments, three mouse clicks) for angle and circle radius measurements */ - RulerThreeClicks = 3, + RulerThreeClicks = 4, /** * @brief The ruler is a multi-segment type */ - RulerMultiSegment = 4 + RulerMultiSegment = 5 }; /** diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc index d7e709318..6961636e3 100644 --- a/src/ant/ant/gsiDeclAnt.cc +++ b/src/ant/ant/gsiDeclAnt.cc @@ -434,6 +434,11 @@ static int ruler_mode_auto_metric () return ant::Template::RulerAutoMetric; } +static int ruler_mode_auto_metric_edge () +{ + return ant::Template::RulerAutoMetricEdge; +} + static int ruler_mode_three_clicks () { return ant::Template::RulerThreeClicks; @@ -525,6 +530,12 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat "\n" "This constant has been introduced in version 0.25" ) + + gsi::method ("RulerModeAutoMetricEdge", &gsi::ruler_mode_auto_metric_edge, + "@brief Specifies edge-sensitive auto-metric ruler mode for the \\register_template method\n" + "In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the edge it is placed on.\n" + "\n" + "This constant has been introduced in version 0.29" + ) + gsi::method ("RulerThreeClicks", &gsi::ruler_mode_three_clicks, "@brief Specifies three-click ruler mode for the \\register_template method\n" "In this ruler mode, two segments are created for angle and circle radius measurements. Three mouse clicks are required.\n" diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 36c7c1e05..b428919c6 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -744,12 +744,7 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co } } - EdgeRelationFilter check (rel, d, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); + EdgeRelationFilter check (rel, d, options); edge2edge_check_for_edges edge_check (check, *result, other != 0); scanner.process (edge_check, d, db::box_convert ()); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index aa70225ad..9ee803c2a 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1163,12 +1163,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ()); bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); - EdgeRelationFilter check (rel, d, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); + EdgeRelationFilter check (rel, d, options); std::vector others; std::vector foreign; @@ -1241,12 +1236,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord std::unique_ptr result (new FlatEdgePairs ()); db::PropertyMapper pm (result->properties_repository (), properties_repository ()); - EdgeRelationFilter check (rel, d, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); + EdgeRelationFilter check (rel, d, options); for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 64eb1d85f..dc87204c9 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -154,13 +154,17 @@ CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first)); } + if (iname != m_name_map.end () && iname->second.first != null_id && iname->second.first != id) { + common_reader_error (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld")), cn, id, iname->second.first)); + } + if (iid != m_id_map.end () && iname != m_name_map.end ()) { if (iname->second.second != iid->second.second) { // Both cells already exist and are not identical: merge ID-declared cell into the name-declared one layout.force_update (); - merge_cell (layout, iname->second.second, iid->second.second); + merge_cell (layout, iname->second.second, iid->second.second, true); iid->second.second = iname->second.second; } @@ -235,7 +239,7 @@ CommonReaderBase::cell_for_instance (db::Layout &layout, const std::string &cn) } void -CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const +CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta) const { const db::Cell &src_cell = layout.cell (src_cell_index); db::Cell &target_cell = layout.cell (target_cell_index); @@ -249,11 +253,11 @@ CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cel } } - merge_cell_without_instances (layout, target_cell_index, src_cell_index); + merge_cell_without_instances (layout, target_cell_index, src_cell_index, with_meta); } void -CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const +CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta) const { const db::Cell &src_cell = layout.cell (src_cell_index); db::Cell &target_cell = layout.cell (target_cell_index); @@ -268,6 +272,16 @@ CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_ind // replace all instances of the new cell with the original one layout.replace_instances_of (src_cell.cell_index (), target_cell.cell_index ()); + // merge meta info + if (with_meta) { + auto ib = layout.begin_meta (src_cell.cell_index ()); + auto ie = layout.end_meta (src_cell.cell_index ()); + for (auto i = ib; i != ie; ++i) { + layout.add_meta_info (target_cell.cell_index (), i->first, i->second); + } + } + layout.clear_meta (src_cell.cell_index ()); + // finally delete the new cell layout.delete_cell (src_cell.cell_index ()); } @@ -371,7 +385,7 @@ CommonReaderBase::finish (db::Layout &layout) layout.cell (ci_org).clear_shapes (); - merge_cell (layout, ci_org, ci_new); + merge_cell (layout, ci_org, ci_new, true); } else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) { @@ -379,11 +393,11 @@ CommonReaderBase::finish (db::Layout &layout) layout.cell (ci_new).clear_shapes (); // NOTE: ignore instances -> this saves us a layout update - merge_cell_without_instances (layout, ci_org, ci_new); + merge_cell_without_instances (layout, ci_org, ci_new, false); } else { - merge_cell (layout, ci_org, ci_new); + merge_cell (layout, ci_org, ci_new, m_cc_resolution != SkipNewCell); } diff --git a/src/db/db/dbCommonReader.h b/src/db/db/dbCommonReader.h index 45afd6cc5..90e98d339 100644 --- a/src/db/db/dbCommonReader.h +++ b/src/db/db/dbCommonReader.h @@ -242,12 +242,12 @@ protected: /** * @brief Merge (and delete) the src_cell into target_cell */ - void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const; + void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta) const; /** * @brief Merge (and delete) the src_cell into target_cell without instances */ - void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const; + void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta) const; /** * @brief Gets the layer name map diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 21e23c1c4..be321c493 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -1625,19 +1625,13 @@ CompoundRegionEdgePairToEdgeProcessingOperationNode::do_compute_local (CompoundR // --------------------------------------------------------------------------------------------- CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (db::edge_relation_type rel, bool different_polygons, db::Coord d, const db::RegionCheckOptions &options) - : CompoundRegionMultiInputOperationNode (), m_check (rel, d, options.metrics), m_different_polygons (different_polygons), m_options (options), m_has_other (false), m_is_other_merged (false) + : CompoundRegionMultiInputOperationNode (), m_check (rel, d, options), m_different_polygons (different_polygons), m_options (options), m_has_other (false), m_is_other_merged (false) { set_description ("check"); - - m_check.set_include_zero (false); - m_check.set_whole_edges (options.whole_edges); - m_check.set_ignore_angle (options.ignore_angle); - m_check.set_min_projection (options.min_projection); - m_check.set_max_projection (options.max_projection); } CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegionOperationNode *input, db::edge_relation_type rel, bool different_polygons, db::Coord d, const db::RegionCheckOptions &options) - : CompoundRegionMultiInputOperationNode (input), m_check (rel, d, options.metrics), m_different_polygons (different_polygons), m_options (options), m_has_other (false), m_is_other_merged (false) + : CompoundRegionMultiInputOperationNode (input), m_check (rel, d, options), m_different_polygons (different_polygons), m_options (options), m_has_other (false), m_is_other_merged (false) { set_description ("check"); @@ -1645,16 +1639,10 @@ CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegi if (pc_always_different (m_options.prop_constraint)) { m_different_polygons = true; } - - m_check.set_include_zero (false); - m_check.set_whole_edges (options.whole_edges); - m_check.set_ignore_angle (options.ignore_angle); - m_check.set_min_projection (options.min_projection); - m_check.set_max_projection (options.max_projection); } CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegionOperationNode *input, CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, db::Coord d, const db::RegionCheckOptions &options) - : CompoundRegionMultiInputOperationNode (other), m_check (rel, d, options.metrics), m_different_polygons (different_polygons), m_options (options) + : CompoundRegionMultiInputOperationNode (other), m_check (rel, d, options), m_different_polygons (different_polygons), m_options (options) { tl_assert (input == 0); // input is a dummy parameter @@ -1663,12 +1651,6 @@ CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegi m_is_other_merged = other->is_merged (); set_description ("check"); - - m_check.set_include_zero (false); - m_check.set_whole_edges (options.whole_edges); - m_check.set_ignore_angle (options.ignore_angle); - m_check.set_min_projection (options.min_projection); - m_check.set_max_projection (options.max_projection); } db::OnEmptyIntruderHint diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 747b7fee7..c94839621 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -1777,12 +1777,7 @@ DeepEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord const db::DeepLayer &edges = merged_deep_layer (); - EdgeRelationFilter check (rel, d, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); + EdgeRelationFilter check (rel, d, options); std::unique_ptr res (new db::DeepEdgePairs (edges.derived ())); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index eb986ea15..50173c49e 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1957,12 +1957,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons const db::DeepLayer &polygons = needs_merged_primary ? merged_deep_layer () : deep_layer (); - EdgeRelationFilter check (rel, d, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); + EdgeRelationFilter check (rel, d, options); std::unique_ptr res (new db::DeepEdgePairs (polygons.derived ())); @@ -2025,12 +2020,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c double mag = tr.mag (); db::Coord d_with_mag = db::coord_traits::rounded (d / mag); - EdgeRelationFilter check (rel, d_with_mag, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); + EdgeRelationFilter check (rel, d_with_mag, options); const db::Shapes &shapes = c->shapes (polygons.layer ()); db::Shapes &result = c->shapes (res->deep_layer ().layer ()); diff --git a/src/db/db/dbEdgePairRelations.cc b/src/db/db/dbEdgePairRelations.cc index 4875b29b4..3e62bd3ca 100644 --- a/src/db/db/dbEdgePairRelations.cc +++ b/src/db/db/dbEdgePairRelations.cc @@ -48,17 +48,53 @@ db::Edge::distance_type edge_projection (const db::Edge &a, const db::Edge &b) return db::coord_traits::rounded (a.double_length () * fabs (l2 - l1)); } +/** + * @brief Gets a flag indicating whether zero distance is included in the checks + */ +static bool include_zero_flag (zero_distance_mode zd_mode, const db::Edge &a, const db::Edge &b) +{ + if (zd_mode == AlwaysIncludeZeroDistance) { + + return true; + + } else if (zd_mode == NeverIncludeZeroDistance) { + + return false; + + } else { + + int s1 = a.side_of (b.p1 ()); + int s2 = a.side_of (b.p2 ()); + + if (s1 == 0 && s2 == 0) { + if (zd_mode == IncludeZeroDistanceWhenTouching || zd_mode == IncludeZeroDistanceWhenCollinearAndTouching) { + return a.intersect (b); + } else if (zd_mode == IncludeZeroDistanceWhenOverlapping) { + return a.coincident (b); + } + } else if ((s1 == 0 || s2 == 0) && a.p1 () != b.p2 () && a.p2 () != b.p1 ()) { + if (zd_mode == IncludeZeroDistanceWhenTouching) { + return a.intersect (b); + } + } + + return false; + + } +} + /** * @brief Returns the part of the "other" edge which is on the inside side of e and within distance d * * This function applies Euclidian metrics. * If no such part is found, this function returns false. + * + * The input edges are normalized to "width" orientation. */ -bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output) +bool euclidian_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output) { // Handle the case of point-like basic edge: cannot determine // orientation - if (e.is_degenerate ()) { return false; } @@ -67,11 +103,7 @@ bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); - // "kissing corner" issue: force include zero if the edges are collinear and overlap. - if (! include_zero && s1 == 0 && s2 == 0 && e.intersect (g)) { - include_zero = true; - } - + bool include_zero = include_zero_flag (zd_mode, e, g); int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e @@ -89,18 +121,18 @@ bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits db::Point o = g.p1 (); - if (e.side_of (o) >= 0) { + if (e.side_of (o) > thr) { return false; } double a = e.double_sq_length (); - double b = db::sprod (db::Vector (e.p1 () - o), e.d ()); - double c = e.p1 ().sq_double_distance (o) - d * d; + double b = db::sprod (db::Vector (e.p1 () - o), e.d ()) / a; + double c = (e.p1 ().sq_double_distance (o) - double (d) * double (d)) / a; - double s = b * b - a * c; - if (s >= 0) { - double l1 = std::max (0.0, (-b - sqrt (s)) / a); - double l2 = std::min (1.0, (-b + sqrt (s)) / a); + double s = b * b - c; + if (s >= -db::epsilon) { + double l1 = std::max (0.0, (-b - sqrt (s))); + double l2 = std::min (1.0, (-b + sqrt (s))); if (l1 <= l2) { if (output) { *output = g; @@ -167,13 +199,13 @@ bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits db::Point o = i ? e.p2 () : e.p1 (); double a = g.double_sq_length (); - double b = db::sprod (db::Vector (g.p1 () - o), g.d ()); - double c = g.p1 ().sq_double_distance (o) - double (d) * double (d); + double b = db::sprod (db::Vector (g.p1 () - o), g.d ()) / a; + double c = (g.p1 ().sq_double_distance (o) - double (d) * double (d)) / a; - double s = b * b - a * c; - if (s >= 0) { - l1 = std::min (l1, (-b - sqrt (s)) / a); - l2 = std::max (l2, (-b + sqrt (s)) / a); + double s = b * b - c; + if (s >= -db::epsilon) { + l1 = std::min (l1, -b - sqrt (s)); + l2 = std::max (l2, -b + sqrt (s)); } } @@ -181,7 +213,7 @@ bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits l1 = std::max (0.0, l1); l2 = std::min (1.0, l2); - if (l1 >= l2) { + if (l1 > l2 - db::epsilon) { return false; } else { if (output) { @@ -194,10 +226,12 @@ bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits /** * @brief Returns the part of the "other" edge which is on the inside side of e and within distance d * - * This function applies Square metrics. + * This function applies Projection or Square metrics. * If no such part is found, this function returns false. + * + * The input edges are normalized to "width" orientation. */ -static bool var_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, db::coord_traits::distance_type dd, const db::Edge &e, const db::Edge &other, db::Edge *output) +static bool var_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits::distance_type d, db::coord_traits::distance_type dd, const db::Edge &e, const db::Edge &other, db::Edge *output) { // Handle the case of point-like basic edge: cannot determine // orientation @@ -210,11 +244,7 @@ static bool var_near_part_of_edge (bool include_zero, db::coord_traits= 0) { + if (e.side_of (g.p1 ()) > thr) { + return false; + } + if (double (e.distance (g.p1 ())) <= -double (d)) { return false; } if (db::sprod (db::Vector (g.p1 () - e.p1 ()), e.d ()) < -(dd * e.double_length ())) { @@ -314,9 +346,9 @@ static bool var_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output) +bool projected_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output) { - return var_near_part_of_edge (include_zero, d, 0, e, other, output); + return var_near_part_of_edge (zd_mode, d, 0, e, other, output); } /** @@ -325,20 +357,26 @@ bool projected_near_part_of_edge (bool include_zero, db::coord_traits * This function applies Square metrics. * If no such part is found, this function returns false. */ -bool square_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output) +bool square_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output) { - return var_near_part_of_edge (include_zero, d, d, e, other, output); + return var_near_part_of_edge (zd_mode, d, d, e, other, output); } // --------------------------------------------------------------------------------- // Implementation of EdgeRelationFilter -EdgeRelationFilter::EdgeRelationFilter (edge_relation_type r, distance_type d, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) - : m_whole_edges (false), m_include_zero (true), m_r (r), m_d (d), m_metrics (metrics), m_ignore_angle (0), m_min_projection (min_projection), m_max_projection (max_projection) +EdgeRelationFilter::EdgeRelationFilter (edge_relation_type r, distance_type d, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection, zero_distance_mode zd_mode) + : m_whole_edges (false), m_zero_distance_mode (zd_mode), m_r (r), m_d (d), m_metrics (metrics), m_ignore_angle (0), m_min_projection (min_projection), m_max_projection (max_projection) { set_ignore_angle (ignore_angle); } +EdgeRelationFilter::EdgeRelationFilter (edge_relation_type r, distance_type d, const EdgesCheckOptions &options) + : m_whole_edges (options.whole_edges), m_zero_distance_mode (options.zd_mode), m_r (r), m_d (d), m_metrics (options.metrics), m_ignore_angle (0), m_min_projection (options.min_projection), m_max_projection (options.max_projection) +{ + set_ignore_angle (options.ignore_angle); +} + void EdgeRelationFilter::set_ignore_angle (double a) { @@ -399,14 +437,14 @@ EdgeRelationFilter::check (const db::Edge &a, const db::Edge &b, db::EdgePair *o bool in1, in2; if (m_metrics == Euclidian) { - in2 = euclidian_near_part_of_edge (m_include_zero, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0); - in1 = euclidian_near_part_of_edge (m_include_zero, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0); + in2 = euclidian_near_part_of_edge (m_zero_distance_mode, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0); + in1 = euclidian_near_part_of_edge (m_zero_distance_mode, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0); } else if (m_metrics == Square) { - in2 = square_near_part_of_edge (m_include_zero, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0); - in1 = square_near_part_of_edge (m_include_zero, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0); + in2 = square_near_part_of_edge (m_zero_distance_mode, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0); + in1 = square_near_part_of_edge (m_zero_distance_mode, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0); } else { - in2 = projected_near_part_of_edge (m_include_zero, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0); - in1 = projected_near_part_of_edge (m_include_zero, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0); + in2 = projected_near_part_of_edge (m_zero_distance_mode, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0); + in1 = projected_near_part_of_edge (m_zero_distance_mode, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0); } if (in1 && in2) { diff --git a/src/db/db/dbEdgePairRelations.h b/src/db/db/dbEdgePairRelations.h index 99b5aec6c..8345b26b1 100644 --- a/src/db/db/dbEdgePairRelations.h +++ b/src/db/db/dbEdgePairRelations.h @@ -98,6 +98,111 @@ enum edge_relation_type InsideRelation = 4 }; +/** + * @brief An enum specifying whether how edges with zero distance are handled in checks + */ +enum zero_distance_mode { + + /** + * @brief Never include zero-distance edges + */ + NeverIncludeZeroDistance = 0, + + /** + * @brief Include zero-distance edges when they share at least one common point + */ + IncludeZeroDistanceWhenTouching = 1, + + /** + * @brief Include zero-distance edges when they share at least one common point and are collinear + */ + IncludeZeroDistanceWhenCollinearAndTouching = 2, + + /** + * @brief Include zero-distance edges when they share more than a single common point (this implies that they are collinear) + */ + IncludeZeroDistanceWhenOverlapping = 3, + + /** + * @brief Always include zero-distance edges (hardly useful) + */ + AlwaysIncludeZeroDistance = 4 +}; + +/** + * @brief A structure holding the options for the region checks (space, width, ...) + */ +struct DB_PUBLIC EdgesCheckOptions +{ + typedef db::coord_traits::distance_type distance_type; + + /** + * @brief Constructor + */ + EdgesCheckOptions (bool _whole_edges = false, + metrics_type _metrics = db::Euclidian, + double _ignore_angle = 90, + distance_type _min_projection = 0, + distance_type _max_projection = std::numeric_limits::max (), + zero_distance_mode _zd_mode = IncludeZeroDistanceWhenTouching) + : whole_edges (_whole_edges), + metrics (_metrics), + ignore_angle (_ignore_angle), + min_projection (_min_projection), + max_projection (_max_projection), + zd_mode (_zd_mode) + { } + + /** + * @brief Specifies is whole edges are to be delivered + * + * Without "whole_edges", the parts of + * the edges are returned which violate the condition. If "whole_edges" is true, the + * result will contain the complete edges participating in the result. + */ + bool whole_edges; + + /** + * @brief Measurement metrics + * + * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" + * metrics are available. + */ + metrics_type metrics; + + /** + * @brief Specifies the obtuse angle threshold + * + * "ignore_angle" allows specification of a maximum angle that connected edges can have to not participate + * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, + * but acute corners are for example. + */ + double ignore_angle; + + /** + * @brief Specifies the projection limit's minimum value + * + * With min_projection and max_projection it is possible to specify how edges must be related + * to each other. If the length of the projection of either edge on the other is >= min_projection + * or < max_projection, the edges are considered for the check. + */ + distance_type min_projection; + + /** + * @brief Specifies the projection limit's maximum value + */ + distance_type max_projection; + + /** + * @brief Specifies zero-distance edge handling + * + * This allows implementing the "kissing corners" case. When set to "IncludeZeroDistanceWhenTouching", kissing corners will + * be reported as errors, when set to "NeverIncludeZeroDistance", they won't. Note that with merged inputs, edges + * will not overlap except at the corners. + */ + zero_distance_mode zd_mode; +}; + /** * @brief A filter based on the edge pair relation * @@ -123,9 +228,14 @@ struct DB_PUBLIC EdgeRelationFilter * to each other. If the length of the projection of either edge on the other is >= min_projection * or < max_projection, the edges are considered for the check. */ - EdgeRelationFilter (edge_relation_type r, distance_type d, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()); + EdgeRelationFilter (edge_relation_type r, distance_type d, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max (), zero_distance_mode include_zero = AlwaysIncludeZeroDistance); - /** + /** + * Constructs an edge relation filter from a CheckOptions structure + */ + EdgeRelationFilter (edge_relation_type r, distance_type d, const EdgesCheckOptions &options); + + /** * @brief Tests whether two edges fulfil the check fail criterion * * If the output pointer is non-null, the object will receive the edge pair that @@ -150,19 +260,19 @@ struct DB_PUBLIC EdgeRelationFilter } /** - * @brief Sets a flag indicating whether zero distance shall be included in the check + * @brief Sets a value indicating whether zero-distance edges shall be included in the check */ - void set_include_zero (bool f) + void set_zero_distance_mode (zero_distance_mode f) { - m_include_zero = f; + m_zero_distance_mode = f; } /** - * @brief Gets a flag indicating whether zero distance shall be included in the check + * @brief Gets a value indicating whether zero-distance edges shall be included in the check */ - bool include_zero () const + zero_distance_mode get_zero_distance_mode () const { - return m_include_zero; + return m_zero_distance_mode; } /** @@ -262,7 +372,7 @@ struct DB_PUBLIC EdgeRelationFilter private: bool m_whole_edges; - bool m_include_zero; + zero_distance_mode m_zero_distance_mode; edge_relation_type m_r; distance_type m_d; metrics_type m_metrics; @@ -273,9 +383,9 @@ private: // Internal methods exposed for testing purposes -DB_PUBLIC bool projected_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output); -DB_PUBLIC bool square_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output); -DB_PUBLIC bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output); +DB_PUBLIC bool projected_near_part_of_edge (zero_distance_mode include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output); +DB_PUBLIC bool square_near_part_of_edge (zero_distance_mode include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output); +DB_PUBLIC bool euclidian_near_part_of_edge (zero_distance_mode include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output); DB_PUBLIC db::Edge::distance_type edge_projection (const db::Edge &a, const db::Edge &b); } diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index e5210c24f..5215da02f 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -39,69 +39,6 @@ namespace db { -/** - * @brief A structure holding the options for the region checks (space, width, ...) - */ -struct DB_PUBLIC EdgesCheckOptions -{ - typedef db::coord_traits::distance_type distance_type; - - /** - * @brief Constructor - */ - EdgesCheckOptions (bool _whole_edges = false, - metrics_type _metrics = db::Euclidian, - double _ignore_angle = 90, - distance_type _min_projection = 0, - distance_type _max_projection = std::numeric_limits::max ()) - : whole_edges (_whole_edges), - metrics (_metrics), - ignore_angle (_ignore_angle), - min_projection (_min_projection), - max_projection (_max_projection) - { } - - /** - * @brief Specifies is whole edges are to be delivered - * - * Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the - * result will contain the complete edges participating in the result. - */ - bool whole_edges; - - /** - * @brief Measurement metrics - * - * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" - * metrics are available. - */ - metrics_type metrics; - - /** - * @brief Specifies the obtuse angle threshold - * - * "ignore_angle" allows specification of a maximum angle that connected edges can have to not participate - * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, - * but acute corners are for example. - */ - double ignore_angle; - - /** - * @brief Specifies the projection limit's minimum value - * - * With min_projection and max_projection it is possible to specify how edges must be related - * to each other. If the length of the projection of either edge on the other is >= min_projection - * or < max_projection, the edges are considered for the check. - */ - distance_type min_projection; - - /** - * @brief Specifies the projection limit's maximum value - */ - distance_type max_projection; -}; - /** * @brief A base class for edge filters */ diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 8ab1e8717..4e29d27ef 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -257,6 +257,83 @@ private: bool m_insert; }; +struct SetLayoutMetaInfoOp + : public LayoutOp +{ + SetLayoutMetaInfoOp (db::Layout::meta_info_name_id_type name_id, const db::MetaInfo *f, const db::MetaInfo *t) + : m_name_id (name_id), m_has_from (f != 0), m_has_to (t != 0) + { + if (f) { + m_from = *f; + } + if (t) { + m_to = *t; + } + } + + virtual void redo (db::Layout *layout) const + { + if (! m_has_to) { + layout->remove_meta_info (m_name_id); + } else { + layout->add_meta_info (m_name_id, m_to); + } + } + + virtual void undo (db::Layout *layout) const + { + if (! m_has_from) { + layout->remove_meta_info (m_name_id); + } else { + layout->add_meta_info (m_name_id, m_from); + } + } + +private: + db::Layout::meta_info_name_id_type m_name_id; + bool m_has_from, m_has_to; + db::MetaInfo m_from, m_to; +}; + +struct SetCellMetaInfoOp + : public LayoutOp +{ + SetCellMetaInfoOp (db::cell_index_type ci, db::Layout::meta_info_name_id_type name_id, const db::MetaInfo *f, const db::MetaInfo *t) + : m_ci (ci), m_name_id (name_id), m_has_from (f != 0), m_has_to (t != 0) + { + if (f) { + m_from = *f; + } + if (t) { + m_to = *t; + } + } + + virtual void redo (db::Layout *layout) const + { + if (! m_has_to) { + layout->remove_meta_info (m_ci, m_name_id); + } else { + layout->add_meta_info (m_ci, m_name_id, m_to); + } + } + + virtual void undo (db::Layout *layout) const + { + if (! m_has_from) { + layout->remove_meta_info (m_ci, m_name_id); + } else { + layout->add_meta_info (m_ci, m_name_id, m_from); + } + } + +private: + db::cell_index_type m_ci; + db::Layout::meta_info_name_id_type m_name_id; + bool m_has_from, m_has_to; + db::MetaInfo m_from, m_to; +}; + // ----------------------------------------------------------------- // Implementation of the ProxyContextInfo class @@ -848,6 +925,9 @@ Layout::delete_cells (const std::set &cells_to_delete) // cell child objects that must remain. for (std::set::const_iterator c = cells_to_delete.begin (); c != cells_to_delete.end (); ++c) { + // supports undo + clear_meta (*c); + if (manager () && manager ()->transacting ()) { // note the "take" method - this takes out the cell @@ -917,9 +997,12 @@ Layout::delete_cell (cell_index_type id) // a backup container for the cell. This is necessary since the ID's within manager are given to // cell child objects that must remain. + // supports undo + clear_meta (id); + if (manager () && manager ()->transacting ()) { - // not the "take" method - this takes out the cell + // note the "take" method - this takes out the cell std::string cn (cell_name (id)); manager ()->queue (this, new NewRemoveCellOp (id, cn, true /*remove*/, take_cell (id))); @@ -1869,18 +1952,36 @@ Layout::meta_info_name_id (const std::string &name) const void Layout::clear_meta () { + if (manager () && manager ()->transacting ()) { + for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) { + manager ()->queue (this, new SetLayoutMetaInfoOp (i->first, &i->second, 0)); + } + } + m_meta_info.clear (); } void Layout::add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i) { + if (manager () && manager ()->transacting ()) { + auto e = m_meta_info.find (name_id); + manager ()->queue (this, new SetLayoutMetaInfoOp (name_id, e != m_meta_info.end () ? &e->second : 0, &i)); + } + m_meta_info[name_id] = i; } void Layout::remove_meta_info (meta_info_name_id_type name_id) { + if (manager () && manager ()->transacting ()) { + auto e = m_meta_info.find (name_id); + if (e != m_meta_info.end ()) { + manager ()->queue (this, new SetLayoutMetaInfoOp (name_id, &e->second, 0)); + } + } + m_meta_info.erase (name_id); } @@ -1901,12 +2002,41 @@ Layout::has_meta_info (meta_info_name_id_type name_id) const void Layout::clear_meta (db::cell_index_type ci) { + if (manager () && manager ()->transacting ()) { + auto ib = begin_meta (ci); + auto ie = end_meta (ci); + for (auto i = ib; i != ie; ++i) { + manager ()->queue (this, new SetCellMetaInfoOp (ci, i->first, &i->second, 0)); + } + } + m_meta_info_by_cell.erase (ci); } +void +Layout::clear_all_meta () +{ + clear_meta (); + while (! m_meta_info_by_cell.empty ()) { + clear_meta (m_meta_info_by_cell.begin ()->first); + } +} + void Layout::add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i) { + if (manager () && manager ()->transacting ()) { + const MetaInfo *from = 0; + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + auto e = c->second.find (name_id); + if (e != c->second.end ()) { + from = &e->second; + } + } + manager ()->queue (this, new SetCellMetaInfoOp (ci, name_id, from, &i)); + } + m_meta_info_by_cell[ci][name_id] = i; } @@ -1914,6 +2044,18 @@ void Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) { auto c = m_meta_info_by_cell.find (ci); + + if (manager () && manager ()->transacting ()) { + const MetaInfo *from = 0; + if (c != m_meta_info_by_cell.end ()) { + auto e = c->second.find (name_id); + if (e != c->second.end ()) { + from = &e->second; + } + } + manager ()->queue (this, new SetCellMetaInfoOp (ci, name_id, from, 0)); + } + if (c != m_meta_info_by_cell.end ()) { c->second.erase (name_id); } @@ -1945,6 +2087,40 @@ Layout::has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) c } } +void +Layout::merge_meta_info (const db::Layout &other) +{ + for (auto mi = other.begin_meta (); mi != other.end_meta (); ++mi) { + add_meta_info (other.meta_info_name (mi->first), mi->second); + } +} + +void +Layout::merge_meta_info (db::cell_index_type into_cell, const db::Layout &other, db::cell_index_type other_cell) +{ + auto mi_begin = other.begin_meta (other_cell); + auto mi_end = other.end_meta (other_cell); + for (auto mi = mi_begin; mi != mi_end; ++mi) { + add_meta_info (into_cell, other.meta_info_name (mi->first), mi->second); + } +} + +void +Layout::merge_meta_info (const db::Layout &other, const db::CellMapping &cm) +{ + for (auto i = cm.begin (); i != cm.end (); ++i) { + merge_meta_info (i->second, other, i->first); + } +} + +void +Layout::copy_meta_info (const db::Layout &other, const db::CellMapping &cm) +{ + for (auto i = cm.begin (); i != cm.end (); ++i) { + copy_meta_info (i->second, other, i->first); + } +} + void Layout::swap_layers (unsigned int a, unsigned int b) { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 7d2598da6..59a4ad19f 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1993,6 +1993,11 @@ public: */ void clear_meta (db::cell_index_type ci); + /** + * @brief Clears all meta information (cells and layout) + */ + void clear_all_meta (); + /** * @brief Adds meta information for a given cell * The given meta information object is to the meta information list for the given cell. @@ -2021,6 +2026,50 @@ public: } } + /** + * @brief Merges meta information from the other layout into self + * This applies to the layout-only meta information. Same keys get overwritten, new ones are added. + */ + void merge_meta_info (const db::Layout &other); + + /** + * @brief Copies meta information from the other layout into self + * This applies to the layout-only meta information. All keys are replaced. + */ + void copy_meta_info (const db::Layout &other) + { + clear_meta (); + merge_meta_info (other); + } + + /** + * @brief Merges meta information from the other cell into the target cell from sel. + * This applies to the cell-specific meta information. Same keys get overwritten, new ones are added. + */ + void merge_meta_info (db::cell_index_type into_cell, const db::Layout &other, db::cell_index_type other_cell); + + /** + * @brief Copies meta information from the other cell into the target cell from sel. + * This applies to the cell-specific meta information. All keys are replaced. + */ + void copy_meta_info (db::cell_index_type into_cell, const db::Layout &other, db::cell_index_type other_cell) + { + clear_meta (into_cell); + merge_meta_info (into_cell, other, other_cell); + } + + /** + * @brief Merges meta information from the other cell into the target cell from sel using the given cell mapping. + * The cell mapping specifies which meta information to merge from which cell into which cell. + */ + void merge_meta_info (const db::Layout &other, const db::CellMapping &cm); + + /** + * @brief Copies meta information from the other cell into the target cell from sel using the given cell mapping. + * The cell mapping specifies which meta information to copy from which cell into which cell. + */ + void copy_meta_info (const db::Layout &other, const db::CellMapping &cm); + /** * @brief Gets a value indicating whether a meta info with the given name is present for the given cell */ diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index bc35d4c62..b52e71b02 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -702,6 +702,29 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout r.dbu_differs (a.dbu (), b.dbu ()); } + if ((flags & layout_diff::f_with_meta) != 0) { + std::map > mi; + for (auto i = a.begin_meta (); i != a.end_meta (); ++i) { + if (i->second.persisted) { + mi [a.meta_info_name (i->first)].first = i->second.value; + } + } + for (auto i = b.begin_meta (); i != b.end_meta (); ++i) { + if (i->second.persisted) { + mi [b.meta_info_name (i->first)].second = i->second.value; + } + } + for (auto i = mi.begin (); i != mi.end (); ++i) { + if (i->second.first != i->second.second) { + differs = true; + if (flags & layout_diff::f_silent) { + return false; + } + r.layout_meta_info_differs (i->first, i->second.first, i->second.second); + } + } + } + bool verbose = (flags & layout_diff::f_verbose); bool no_duplicates = (flags & layout_diff::f_ignore_duplicates); @@ -928,6 +951,33 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout r.begin_cell (common_cells [cci], common_cells_a [cci], common_cells_b [cci]); + if ((flags & layout_diff::f_with_meta) != 0) { + std::map > mi; + auto ib = a.begin_meta (common_cells_a [cci]); + auto ie = a.end_meta (common_cells_a [cci]); + for (auto i = ib; i != ie; ++i) { + if (i->second.persisted) { + mi [a.meta_info_name (i->first)].first = i->second.value; + } + } + ib = b.begin_meta (common_cells_b [cci]); + ie = b.end_meta (common_cells_b [cci]); + for (auto i = ib; i != ie; ++i) { + if (i->second.persisted) { + mi [b.meta_info_name (i->first)].second = i->second.value; + } + } + for (auto i = mi.begin (); i != mi.end (); ++i) { + if (i->second.first != i->second.second) { + differs = true; + if (flags & layout_diff::f_silent) { + return false; + } + r.cell_meta_info_differs (i->first, i->second.first, i->second.second); + } + } + } + if (!verbose && cell_a->bbox () != cell_b->bbox ()) { differs = true; if (flags & layout_diff::f_silent) { @@ -1215,6 +1265,7 @@ public: } void dbu_differs (double dbu_a, double dbu_b); + void layout_meta_info_differs (const std::string &name, const tl::Variant &a, const tl::Variant &b); void layer_in_a_only (const db::LayerProperties &la); void layer_in_b_only (const db::LayerProperties &lb); void layer_name_differs (const db::LayerProperties &la, const db::LayerProperties &lb); @@ -1222,6 +1273,7 @@ public: void cell_in_b_only (const std::string &cellname, db::cell_index_type ci); void cell_name_differs (const std::string &cellname_a, db::cell_index_type cia, const std::string &cellname_b, db::cell_index_type cib); void begin_cell (const std::string &cellname, db::cell_index_type cia, db::cell_index_type cib); + void cell_meta_info_differs (const std::string &name, const tl::Variant &a, const tl::Variant &b); void bbox_differs (const db::Box &ba, const db::Box &bb); void begin_inst_differences (); void instances_in_a (const std::vector &insts_a, const std::vector &cell_names, const db::PropertiesRepository &props); @@ -1384,6 +1436,16 @@ PrintingDifferenceReceiver::dbu_differs (double dbu_a, double dbu_b) } } +void +PrintingDifferenceReceiver::layout_meta_info_differs (const std::string &name, const tl::Variant &a, const tl::Variant &b) +{ + try { + enough (tl::error) << "Global meta info differs - [" << name << "]: " << a << " vs. " << b; + } catch (tl::CancelException &) { + // ignore cancel exceptions + } +} + void PrintingDifferenceReceiver::layer_in_a_only (const db::LayerProperties &la) { @@ -1461,6 +1523,16 @@ PrintingDifferenceReceiver::begin_cell (const std::string &cellname, db::cell_in m_cellname = cellname; } +void +PrintingDifferenceReceiver::cell_meta_info_differs (const std::string &name, const tl::Variant &a, const tl::Variant &b) +{ + try { + enough (tl::error) << "Meta info differs in cell " << m_cellname << " - [" << name << "]: " << a << " vs. " << b; + } catch (tl::CancelException &) { + // ignore cancel exceptions + } +} + void PrintingDifferenceReceiver::begin_inst_differences () { diff --git a/src/db/db/dbLayoutDiff.h b/src/db/db/dbLayoutDiff.h index ce39f5988..223ddd4f4 100644 --- a/src/db/db/dbLayoutDiff.h +++ b/src/db/db/dbLayoutDiff.h @@ -56,6 +56,9 @@ const unsigned int f_no_text_orientation = 0x02; // Ignore properties const unsigned int f_no_properties = 0x04; +// With meta info +const unsigned int f_with_meta = 0x08; + // Do not compare layer names const unsigned int f_no_layer_names = 0x10; @@ -94,6 +97,7 @@ public: virtual ~DifferenceReceiver () { } virtual void dbu_differs (double /*dbu_a*/, double /*dbu_b*/) { } + virtual void layout_meta_info_differs (const std::string & /*name*/, const tl::Variant & /*value_a*/, const tl::Variant & /*value_b*/) { } virtual void layer_in_a_only (const db::LayerProperties & /*la*/) { } virtual void layer_in_b_only (const db::LayerProperties & /*lb*/) { } virtual void layer_name_differs (const db::LayerProperties & /*la*/, const db::LayerProperties & /*lb*/) { } @@ -102,6 +106,7 @@ public: virtual void cell_in_b_only (const std::string & /*cellname*/, db::cell_index_type /*ci*/) { } virtual void bbox_differs (const db::Box & /*ba*/, const db::Box & /*bb*/) { } virtual void begin_cell (const std::string & /*cellname*/, db::cell_index_type /*cia*/, db::cell_index_type /*cib*/) { } + virtual void cell_meta_info_differs (const std::string & /*name*/, const tl::Variant & /*value_a*/, const tl::Variant & /*value_b*/) { } virtual void begin_inst_differences () { } virtual void instances_in_a (const std::vector & /*insts_a*/, const std::vector & /*cell_names*/, const db::PropertiesRepository & /*props*/) { } virtual void instances_in_b (const std::vector & /*insts_b*/, const std::vector & /*cell_names*/, const db::PropertiesRepository & /*props*/) { } diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 1c4a7e0d1..2fe5174fb 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -252,6 +252,9 @@ merge_layouts (db::Layout &target, const db::Cell &source_cell = source.cell (*c); db::Cell &target_cell = target.cell (target_cell_index); + // merge meta info + target.merge_meta_info (target_cell_index, source, *c); + // NOTE: this implementation employs the safe but cumbersome "local transformation" feature. // This means, all cells are transformed according to the given transformation and their // references are transformed to account for that effect. This will lead to somewhat strange diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 6e4bc6ae2..00cbe62a5 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -287,19 +287,37 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: extractor_cache_type::const_iterator ec = extractor_cache.find (layer_geometry); if (ec == extractor_cache.end ()) { + log_entry_list log_entries; + m_log_entries.swap (log_entries); + // do the actual device extraction extract_devices (layer_geometry); // push the new devices to the layout push_new_devices (disp); - ExtractorCacheValueType &ecv = extractor_cache [layer_geometry]; - ecv.disp = disp; + if (m_log_entries.empty ()) { + + // cache unless log entries are produced + ExtractorCacheValueType &ecv = extractor_cache [layer_geometry]; + ecv.disp = disp; + + for (std::map >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + ecv.devices.push_back (d->second.first); + } + + } else { + + // transform the marker geometries from the log entries to match the device + db::DVector disp_dbu = db::CplxTrans (dbu ()) * disp; + for (auto l = m_log_entries.begin (); l != m_log_entries.end (); ++l) { + l->set_geometry (l->geometry ().moved (disp_dbu)); + } - for (std::map >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { - ecv.devices.push_back (d->second.first); } + m_log_entries.splice (m_log_entries.begin (), log_entries); + m_new_devices.clear (); } else { diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index a59b03903..4ccb7dd6b 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -1566,23 +1566,26 @@ compute_rounded (const db::DPolygon &polygon, double rinner, double router, unsi } // ------------------------------------------------------------------------- -// Implementation of AreaMap +// Implementation of area_map -AreaMap::AreaMap () +template +area_map::area_map () : m_nx (0), m_ny (0) { mp_av = 0; } -AreaMap::AreaMap (const AreaMap &other) +template +area_map::area_map (const area_map &other) : m_nx (0), m_ny (0) { mp_av = 0; operator= (other); } -AreaMap & -AreaMap::operator= (const AreaMap &other) +template +area_map & +area_map::operator= (const area_map &other) { if (this != &other) { // TODO: this could be copy on write @@ -1594,21 +1597,24 @@ AreaMap::operator= (const AreaMap &other) return *this; } -AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny) +template +area_map::area_map (const area_map::point_type &p0, const area_map::vector_type &d, size_t nx, size_t ny) : m_p0 (p0), m_d (d), m_p (d), m_nx (nx), m_ny (ny) { mp_av = new area_type [nx * ny]; clear (); } -AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny) +template +area_map::area_map (const area_map::point_type &p0, const area_map::vector_type &d, const area_map::vector_type &p, size_t nx, size_t ny) : m_p0 (p0), m_d (d), m_p (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())), m_nx (nx), m_ny (ny) { mp_av = new area_type [nx * ny]; clear (); } -AreaMap::~AreaMap () +template +area_map::~area_map () { if (mp_av) { delete[] mp_av; @@ -1616,18 +1622,20 @@ AreaMap::~AreaMap () mp_av = 0; } +template void -AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny) +area_map::reinitialize (const area_map::point_type &p0, const area_map::vector_type &d, size_t nx, size_t ny) { reinitialize (p0, d, d, nx, ny); } +template void -AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny) +area_map::reinitialize (const area_map::point_type &p0, const area_map::vector_type &d, const area_map::vector_type &p, size_t nx, size_t ny) { m_p0 = p0; m_d = d; - m_p = db::Vector (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())); + m_p = vector_type (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())); if (nx != m_nx || ny != m_ny) { @@ -1645,8 +1653,9 @@ AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vecto clear (); } +template void -AreaMap::clear () +area_map::clear () { if (mp_av) { area_type *a = mp_av; @@ -1656,8 +1665,9 @@ AreaMap::clear () } } +template void -AreaMap::swap (AreaMap &other) +area_map::swap (area_map &other) { std::swap (m_p0, other.m_p0); std::swap (m_d, other.m_d); @@ -1667,8 +1677,9 @@ AreaMap::swap (AreaMap &other) std::swap (mp_av, other.mp_av); } -AreaMap::area_type -AreaMap::total_area () const +template +typename area_map::area_type +area_map::total_area () const { area_type asum = 0; if (mp_av) { @@ -1680,16 +1691,21 @@ AreaMap::total_area () const return asum; } -db::Box -AreaMap::bbox () const +template +typename area_map::box_type +area_map::bbox () const { if (m_nx == 0 || m_ny == 0) { - return db::Box (); + return box_type (); } else { - return db::Box (m_p0, m_p0 + db::Vector (db::Coord (m_nx - 1) * m_d.x () + m_p.x (), db::Coord (m_ny - 1) * m_d.y () + m_p.y ())); + return box_type (m_p0, m_p0 + vector_type (C (m_nx - 1) * m_d.x () + m_p.x (), C (m_ny - 1) * m_d.y () + m_p.y ())); } } +// explicit instantiations +template class area_map; +template class area_map; + // ------------------------------------------------------------------------- // Implementation of rasterize @@ -1707,29 +1723,69 @@ static bool edge_is_partially_left_of (const db::Edge &e, const db::Edge &e_orig } } -bool -rasterize (const db::Polygon &polygon, db::AreaMap &am) +static bool edge_is_partially_left_of (const db::DEdge &e, const db::DEdge &e_original, db::DCoord x) { - typedef db::AreaMap::area_type area_type; - db::Box box = am.bbox (); - db::Box pbox = polygon.box (); + DCoord xmin = db::edge_xmin (e); + if (db::coord_traits::less (xmin, x)) { + return true; + } else if (db::coord_traits::equal (xmin, x) && ! db::coord_traits::equal (e_original.dx (), 0)) { + // the skew edge is cut partially rendering a straight vertical line (due to rounding) + // which we will count as "left of" + return true; + } else { + return false; + } +} + +static size_t npixels_floor (db::Coord d, db::Coord p) +{ + return size_t (std::max (db::Coord (0), d / p)); +} + +static size_t npixels_ceil (db::Coord d, db::Coord p) +{ + return size_t (std::max (db::Coord (0), (d + p - 1) / p)); +} + +static size_t npixels_floor (db::DCoord d, db::DCoord p) +{ + return size_t (std::max (db::DCoord (0), floor (d / p + db::epsilon))); +} + +static size_t npixels_ceil (db::DCoord d, db::DCoord p) +{ + return size_t (std::max (db::DCoord (0), ceil (d / p - db::epsilon))); +} + + +template +static +bool +rasterize_impl (const db::polygon &polygon, db::area_map &am) +{ + typedef typename db::area_map::area_type area_type; + typedef db::box box_type; + typedef db::edge edge_type; + + box_type box = am.bbox (); + box_type pbox = polygon.box (); // check if the polygon overlaps the rasterization area. Otherwise, we simply do nothing. if (! pbox.overlaps (box)) { return false; } - db::Coord ymin = box.bottom (), ymax = box.top (); - db::Coord dy = am.d ().y (), dx = am.d ().x (); - db::Coord py = am.p ().y (), px = am.p ().x (); - db::Coord y0 = am.p0 ().y (), x0 = am.p0 ().x (); + C ymin = box.bottom (), ymax = box.top (); + C dy = am.d ().y (), dx = am.d ().x (); + C py = am.p ().y (), px = am.p ().x (); + C y0 = am.p0 ().y (), x0 = am.p0 ().x (); size_t ny = am.ny (), nx = am.nx (); - size_t iy0 = std::min (ny, size_t (std::max (db::Coord (0), (pbox.bottom () - am.p0 ().y ()) / am.d ().y ()))); - size_t iy1 = std::min (ny, size_t (std::max (db::Coord (0), (pbox.top () - am.p0 ().y () + am.d ().y () - 1) / am.d ().y ()))); + size_t iy0 = std::min (ny, npixels_floor (pbox.bottom () - am.p0 ().y (), am.d ().y ())); + size_t iy1 = std::min (ny, npixels_ceil (pbox.top () - am.p0 ().y (), am.d ().y ())); - size_t ix0 = std::min (nx, size_t (std::max (db::Coord (0), (pbox.left () - am.p0 ().x ()) / am.d ().x ()))); - size_t ix1 = std::min (nx, size_t (std::max (db::Coord (0), (pbox.right () - am.p0 ().x () + am.d ().x () - 1) / am.d ().x ()))); + size_t ix0 = std::min (nx, npixels_floor (pbox.left () - am.p0 ().x (), am.d ().x ())); + size_t ix1 = std::min (nx, npixels_ceil (pbox.right () - am.p0 ().x (), am.d ().x ())); // no scanning required (i.e. degenerated polygon) -> do nothing if (iy0 == iy1 || ix0 == ix1) { @@ -1738,26 +1794,26 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) // collect edges size_t n = 0; - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + for (typename db::polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { if ((*e).dy () != 0 && db::edge_ymax (*e) > ymin && db::edge_ymin (*e) < ymax) { ++n; } } - std::vector edges; + std::vector edges; edges.reserve (n); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + for (typename db::polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { if ((*e).dy () != 0 && db::edge_ymax (*e) > ymin && db::edge_ymin (*e) < ymax) { edges.push_back (*e); } } // sort edges - std::sort (edges.begin (), edges.end (), db::edge_ymin_compare ()); + std::sort (edges.begin (), edges.end (), db::edge_ymin_compare ()); - std::vector ::iterator c = edges.begin (); + typename std::vector ::iterator c = edges.begin (); - db::Coord y = y0 + dy * db::Coord (iy0); + C y = y0 + dy * C (iy0); while (c != edges.end () && db::edge_ymax (*c) <= y) { ++c; @@ -1767,36 +1823,36 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) return false; } - std::vector ::iterator f = c; + typename std::vector ::iterator f = c; for (size_t iy = iy0; iy < iy1; ++iy) { - db::Coord yy = y + py; + C yy = y + py; while (f != edges.end () && db::edge_ymin (*f) < yy) { ++f; } - std::sort (c, f, db::edge_xmin_compare ()); + std::sort (c, f, db::edge_xmin_compare ()); - db::Coord x = x0 + dx * db::Coord (ix0); - db::Coord xl = pbox.left (); + C x = x0 + dx * C (ix0); + C xl = pbox.left (); area_type a = 0; - std::vector ::iterator cc = c; + typename std::vector ::iterator cc = c; while (cc != edges.end () && cc != f && db::edge_xmax (*cc) <= x) { - db::Coord y1 = std::max (y, std::min (yy, cc->p1 ().y ())); - db::Coord y2 = std::max (y, std::min (yy, cc->p2 ().y ())); + C y1 = std::max (y, std::min (yy, cc->p1 ().y ())); + C y2 = std::max (y, std::min (yy, cc->p2 ().y ())); a += area_type (px) * area_type (y2 - y1); ++cc; } - std::vector ::iterator ff = cc; + typename std::vector ::iterator ff = cc; for (size_t ix = ix0; ix < ix1; ++ix) { - db::Coord xx = x + px; - db::Coord xxx = x + dx; + C xx = x + px; + C xxx = x + dx; // TODO: edge_xmin_at_interval(y, yy) and edge_xmax.. would be more efficient in the // all-angle case. However, it is crucial that the edge clipping produces @@ -1807,7 +1863,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) ++ff; } - std::vector ::iterator fff = ff; + typename std::vector ::iterator fff = ff; if (xx < xxx) { while (fff != f && db::edge_xmin (*fff) < xxx) { @@ -1818,11 +1874,11 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) if (xl < x) { // consider all edges or parts of those left of the first cell - db::Box left (xl, y, x, yy); + box_type left (xl, y, x, yy); - for (std::vector ::iterator e = cc; e != ff; ++e) { + for (typename std::vector ::iterator e = cc; e != ff; ++e) { - std::pair ec = e->clipped (left); + std::pair ec = e->clipped (left); if (ec.first && edge_is_partially_left_of (ec.second, *e, x)) { a += area_type (ec.second.dy ()) * area_type (px); } @@ -1835,11 +1891,11 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) if (dx == py) { - db::Box cell (x, y, xx, yy); + box_type cell (x, y, xx, yy); - for (std::vector ::iterator e = cc; e != ff; ++e) { + for (typename std::vector ::iterator e = cc; e != ff; ++e) { - std::pair ec = e->clipped (cell); + std::pair ec = e->clipped (cell); if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) { aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2; a += area_type (ec.second.dy ()) * area_type (px); @@ -1849,22 +1905,22 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) } else { - db::Box cell (x, y, xx, yy); + box_type cell (x, y, xx, yy); - for (std::vector ::iterator e = cc; e != ff; ++e) { + for (typename std::vector ::iterator e = cc; e != ff; ++e) { - std::pair ec = e->clipped (cell); + std::pair ec = e->clipped (cell); if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) { aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2; } } - db::Box wide_cell (x, y, x + dx, yy); + box_type wide_cell (x, y, x + dx, yy); - for (std::vector ::iterator e = cc; e != fff; ++e) { + for (typename std::vector ::iterator e = cc; e != fff; ++e) { - std::pair wide_ec = e->clipped (wide_cell); + std::pair wide_ec = e->clipped (wide_cell); if (wide_ec.first && edge_is_partially_left_of (wide_ec.second, *e, x + dx)) { a += area_type (wide_ec.second.dy ()) * area_type (px); } @@ -1880,7 +1936,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) ff = fff; - for (std::vector ::iterator ccx = cc; ccx != ff; ++ccx) { + for (typename std::vector ::iterator ccx = cc; ccx != ff; ++ccx) { if (db::edge_xmax (*ccx) <= x) { std::swap (*ccx, *cc); ++cc; @@ -1898,7 +1954,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) y = yy; - for (std::vector ::iterator cx = c; cx != f; ++cx) { + for (typename std::vector ::iterator cx = c; cx != f; ++cx) { if (db::edge_ymax (*cx) <= y) { std::swap (*cx, *c); ++c; @@ -1910,6 +1966,18 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) return true; } +bool +rasterize (const db::Polygon &polygon, db::AreaMap &am) +{ + return rasterize_impl (polygon, am); +} + +bool +rasterize (const db::DPolygon &polygon, db::DAreaMap &am) +{ + return rasterize_impl (polygon, am); +} + // ------------------------------------------------------------------------- // Implementation of Minkowski sum diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index e931aa54e..87497812f 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -493,55 +493,59 @@ bool DB_PUBLIC is_non_orientable_polygon (const db::Polygon &poly, std::vector +class DB_PUBLIC area_map { public: - typedef db::coord_traits::area_type area_type; + typedef typename db::coord_traits::area_type area_type; + typedef db::point point_type; + typedef db::vector vector_type; + typedef db::box box_type; /** * @brief Constructor */ - AreaMap (); + area_map (); /** * @brief Copy constructor */ - AreaMap (const AreaMap &); + area_map (const area_map &); /** * @brief Constructor */ - AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny); + area_map (const point_type &p0, const vector_type &d, size_t nx, size_t ny); /** * @brief Constructor with pixel size */ - AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny); + area_map (const point_type &p0, const vector_type &d, const vector_type &p, size_t nx, size_t ny); /** * @brief Destructor */ - ~AreaMap (); + ~area_map (); /** * @brief Assignment */ - AreaMap &operator= (const AreaMap &); + area_map &operator= (const area_map &); /** * @brief Reinitialize */ - void reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny); + void reinitialize (const point_type &p0, const vector_type &d, size_t nx, size_t ny); /** * @brief Reinitialize with pixel size */ - void reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny); + void reinitialize (const point_type &p0, const vector_type &d, const vector_type &p, size_t nx, size_t ny); /** * @brief Swap of two maps */ - void swap (AreaMap &other); + void swap (area_map &other); /** * @brief Get the area of one pixel @@ -578,7 +582,7 @@ public: /** * @brief The origin */ - const db::Point &p0 () const + const point_type &p0 () const { return m_p0; } @@ -586,7 +590,7 @@ public: /** * @brief Move the origin */ - void move (const db::Vector &d) + void move (const vector_type &d) { m_p0 += d; } @@ -594,7 +598,7 @@ public: /** * @brief The per-pixel displacement vector (pixel size) */ - const db::Vector &d () const + const vector_type &d () const { return m_d; } @@ -602,7 +606,7 @@ public: /** * @brief The pixel size (must be less than d) */ - const db::Vector &p () const + const vector_type &p () const { return m_p; } @@ -610,7 +614,7 @@ public: /** * @brief Compute the bounding box of the area map */ - db::Box bbox () const; + box_type bbox () const; /** * @brief Compute the total area @@ -632,12 +636,15 @@ public: private: area_type *mp_av; - db::Point m_p0; - db::Vector m_d; - db::Vector m_p; + point_type m_p0; + vector_type m_d; + vector_type m_p; size_t m_nx, m_ny; }; +typedef area_map AreaMap; +typedef area_map DAreaMap; + /** * @brief Rasterize the polygon into the given area map * @@ -648,6 +655,16 @@ private: */ bool DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am); +/** + * @brief Rasterize the polygon into the given area map (double version) + * + * This will decompose the polygon and produce per-pixel area values for the given + * polygon. The area contributions will be added to the given area map. + * + * Returns a value indicating whether the map will be non-empty. + */ +bool DB_PUBLIC rasterize (const db::DPolygon &polygon, db::DAreaMap &am); + /** * @brief Minkowski sum of an edge and a polygon */ diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index 90e91053e..27b09b187 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -113,6 +113,7 @@ enum OppositeFilter * @brief A structure holding the options for the region checks (space, width, ...) */ struct DB_PUBLIC RegionCheckOptions + : public EdgesCheckOptions { typedef db::coord_traits::distance_type distance_type; @@ -128,12 +129,9 @@ struct DB_PUBLIC RegionCheckOptions OppositeFilter _opposite_filter = NoOppositeFilter, RectFilter _rect_filter = NoRectFilter, bool _negative = false, - PropertyConstraint _prop_constraint = IgnoreProperties) - : whole_edges (_whole_edges), - metrics (_metrics), - ignore_angle (_ignore_angle), - min_projection (_min_projection), - max_projection (_max_projection), + PropertyConstraint _prop_constraint = IgnoreProperties, + zero_distance_mode _zd_mode = IncludeZeroDistanceWhenTouching) + : EdgesCheckOptions (_whole_edges, _metrics, _ignore_angle, _min_projection, _max_projection, _zd_mode), shielded (_shielded), opposite_filter (_opposite_filter), rect_filter (_rect_filter), @@ -141,46 +139,6 @@ struct DB_PUBLIC RegionCheckOptions prop_constraint (_prop_constraint) { } - /** - * @brief Specifies is whole edges are to be delivered - * - * Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the - * result will contain the complete edges participating in the result. - */ - bool whole_edges; - - /** - * @brief Measurement metrics - * - * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" - * metrics are available. - */ - metrics_type metrics; - - /** - * @brief Specifies the obtuse angle threshold - * - * "ignore_angle" allows specification of a maximum angle that connected edges can have to not participate - * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, - * but acute corners are for example. - */ - double ignore_angle; - - /** - * @brief Specifies the projection limit's minimum value - * - * With min_projection and max_projection it is possible to specify how edges must be related - * to each other. If the length of the projection of either edge on the other is >= min_projection - * or < max_projection, the edges are considered for the check. - */ - distance_type min_projection; - - /** - * @brief Specifies the projection limit's maximum value - */ - distance_type max_projection; - /** * @brief Specifies shielding * diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index 5084c0332..15b7b8bc7 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -359,12 +359,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector result; - EdgeRelationFilter check (m_relation, m_d, m_options.metrics); - check.set_include_zero (false); - check.set_whole_edges (m_options.whole_edges); - check.set_ignore_angle (m_options.ignore_angle); - check.set_min_projection (m_options.min_projection); - check.set_max_projection (m_options.max_projection); + EdgeRelationFilter check (m_relation, m_d, m_options); edge2edge_check_negative_or_positive > edge_check (check, result, m_options.negative, false /*=same polygons*/, false /*=same layers*/, m_options.shielded, true /*=symmetric*/); poly2poly_check poly_check (edge_check); diff --git a/src/db/db/dbTechnology.cc b/src/db/db/dbTechnology.cc index 2801b29e1..2910fbbab 100644 --- a/src/db/db/dbTechnology.cc +++ b/src/db/db/dbTechnology.cc @@ -283,6 +283,7 @@ Technology::~Technology () Technology::Technology (const Technology &d) : tl::Object (), m_name (d.m_name), m_description (d.m_description), m_group (d.m_group), m_grain_name (d.m_grain_name), m_dbu (d.m_dbu), + m_default_grids (d.m_default_grids), m_explicit_base_path (d.m_explicit_base_path), m_default_base_path (d.m_default_base_path), m_load_layout_options (d.m_load_layout_options), m_save_layout_options (d.m_save_layout_options), @@ -303,6 +304,7 @@ Technology &Technology::operator= (const Technology &d) m_group = d.m_group; m_grain_name = d.m_grain_name; m_dbu = d.m_dbu; + m_default_grids = d.m_default_grids; m_default_base_path = d.m_default_base_path; m_explicit_base_path = d.m_explicit_base_path; m_load_layout_options = d.m_load_layout_options; @@ -345,6 +347,26 @@ Technology::get_display_string () const return d; } +std::vector +Technology::default_grid_list () const +{ + tl::Extractor ex (m_default_grids.c_str ()); + + std::vector grids; + + // convert the list of grids to a list of doubles + while (! ex.at_end ()) { + double g = 0.0; + if (! ex.try_read (g)) { + break; + } + grids.push_back (g); + ex.test (","); + } + + return grids; +} + tl::XMLElementList Technology::xml_elements () { @@ -353,6 +375,7 @@ Technology::xml_elements () tl::make_member (&Technology::description, &Technology::set_description, "description") + tl::make_member (&Technology::group, &Technology::set_group, "group") + tl::make_member (&Technology::dbu, &Technology::set_dbu, "dbu") + + tl::make_member (&Technology::default_grids, &Technology::set_default_grids, "default-grids") + tl::make_member (&Technology::explicit_base_path, &Technology::set_explicit_base_path, "base-path") + tl::make_member (&Technology::default_base_path, &Technology::set_default_base_path, "original-base-path") + tl::make_member (&Technology::layer_properties_file, &Technology::set_layer_properties_file, "layer-properties_file") + diff --git a/src/db/db/dbTechnology.h b/src/db/db/dbTechnology.h index 2f8c88d76..e06ddca14 100644 --- a/src/db/db/dbTechnology.h +++ b/src/db/db/dbTechnology.h @@ -467,6 +467,30 @@ public: } } + /** + * @brief Gets the default grids + */ + const std::string &default_grids () const + { + return m_default_grids; + } + + /** + * @brief Gets the default grids, parsed as a list of double values + */ + std::vector default_grid_list () const; + + /** + * @brief Sets the default default grids + */ + void set_default_grids (const std::string &default_grids) + { + if (default_grids != m_default_grids) { + m_default_grids = default_grids; + technology_changed (); + } + } + /** * @brief Gets the layer properties file path (empty if none is specified) */ @@ -650,6 +674,7 @@ private: std::string m_name, m_description, m_group; std::string m_grain_name; double m_dbu; + std::string m_default_grids; std::string m_explicit_base_path, m_default_base_path; db::LoadLayoutOptions m_load_layout_options; db::SaveLayoutOptions m_save_layout_options; diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index c93757909..871e66396 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -139,6 +139,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) + | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ , tolerance, 100 /*max diff lines*/); if (equal && n > 0) { diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 4963b2ec4..5369a6e90 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -58,7 +58,8 @@ enum NormalizationMode NormFileMask = 7, // bits the extract for file mode NoContext = 8, // write tmp file without context AsPolygons = 16, // paths and boxes are treated as polygons - WithArrays = 32 // do not flatten arrays + WithArrays = 32, // do not flatten arrays + WithMeta = 64 // with meta info }; /** diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 8968e9ad4..69d7c324e 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -491,7 +491,7 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi db::DVector r = p - *cv; double edge_sp = db::sprod (r, edge_d) / edge_d.length (); double s_sp = db::sprod (r, e_d) / e_d.length (); - if (s_sp > edge_sp) { + if (s_sp > edge_sp + db::epsilon) { edge = *e; vnext = edge->other (v); } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 4a65a4a91..fc32fa381 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1004,6 +1004,22 @@ static void cell_add_meta_info (db::Cell *cell, const MetaInfo &mi) } } +static void cell_merge_meta_info (db::Cell *cell, const db::Cell *source) +{ + if (! source || ! cell->layout () || ! source->layout ()) { + return; + } + cell->layout ()->merge_meta_info (cell->cell_index (), *source->layout (), source->cell_index ()); +} + +static void cell_copy_meta_info (db::Cell *cell, const db::Cell *source) +{ + if (! source || ! cell->layout () || ! source->layout ()) { + return; + } + cell->layout ()->copy_meta_info (cell->cell_index (), *source->layout (), source->cell_index ()); +} + static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::string &name) { if (! cell->layout ()) { @@ -1755,6 +1771,7 @@ read_options (db::Cell *cell, const std::string &path, const db::LoadLayoutOptio db::CellMapping cm; std::vector new_cells = cm.create_single_mapping_full (*cell->layout (), cell->cell_index (), tmp, *tmp.begin_top_down ()); cell->move_tree_shapes (tmp.cell (*tmp.begin_top_down ()), cm); + cell->layout ()->merge_meta_info (tmp, cm); return new_cells; } @@ -1834,6 +1851,21 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.28.8." ) + + gsi::method_ext ("merge_meta_info", &cell_merge_meta_info, gsi::arg ("other"), + "@brief Merges the meta information from the other cell into this cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "Existing keys in this cell will be overwritten by the respective values from the other cell.\n" + "New keys will be added.\n" + "\n" + "This method has been introduced in version 0.28.16." + ) + + gsi::method_ext ("copy_meta_info", &cell_copy_meta_info, gsi::arg ("other"), + "@brief Copies the meta information from the other cell into this cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "The meta information from this cell will be replaced by the meta information from the other cell.\n" + "\n" + "This method has been introduced in version 0.28.16." + ) + gsi::method_ext ("clear_meta_info", &cell_clear_meta_info, "@brief Clears the meta information of the cell\n" "See \\LayoutMetaInfo for details about cells and meta information.\n" diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 06e30f87d..0ee676e20 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -390,23 +390,27 @@ static db::CompoundRegionOperationNode *new_edge_pair_to_second_edges (db::Compo return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToSecondEdgesProcessor (), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_check_node (db::CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, 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) +static db::CompoundRegionOperationNode *new_check_node (db::CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, 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, db::zero_distance_mode zd_mode, bool negative) { check_non_null (other, "other"); - return new db::CompoundRegionCheckOperationNode (0, other, rel, different_polygons, d, - db::RegionCheckOptions (whole_edges, - metrics, - ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), - min_projection.is_nil () ? db::Region::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), - shielded, - opposite_filter, - rect_filter, - negative) - ); + + db::RegionCheckOptions options; + + options.whole_edges = whole_edges; + options.metrics = metrics; + options.ignore_angle = ignore_angle.is_nil () ? 90 : ignore_angle.to_double (); + options.min_projection = min_projection.is_nil () ? db::Region::distance_type (0) : min_projection.to (); + options.max_projection = max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (); + options.shielded = shielded; + options.opposite_filter = opposite_filter; + options.rect_filter = rect_filter; + options.negative = negative; + options.zd_mode = zd_mode; + + return new db::CompoundRegionCheckOperationNode (0, other, rel, different_polygons, d, options); } -static db::CompoundRegionOperationNode *new_width_check (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, bool negative) +static db::CompoundRegionOperationNode *new_width_check (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::zero_distance_mode zd_mode, bool negative) { db::RegionCheckOptions options (whole_edges, metrics, @@ -415,28 +419,29 @@ static db::CompoundRegionOperationNode *new_width_check (db::Coord d, bool whole max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), shielded); options.negative = negative; + options.zd_mode = zd_mode; return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::SinglePolygonCheck (db::WidthRelation, d, options), new_primary (), true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_space_or_isolated_check (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, bool isolated) +static db::CompoundRegionOperationNode *new_space_or_isolated_check (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, db::zero_distance_mode zd_mode, bool negative, bool isolated) { // NOTE: we have to use the "foreign" scheme with a filter because only this scheme // guarantees that all subject shapes are visited and receive all intruders. Having all intruders is crucial for the // semantics of the "drc" feature - return new_check_node (new_foreign (), db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (new_foreign (), db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, negative); } -static db::CompoundRegionOperationNode *new_space_check (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) +static db::CompoundRegionOperationNode *new_space_check (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, db::zero_distance_mode zd_mode, bool negative) { - return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative, false); + return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, negative, false); } -static db::CompoundRegionOperationNode *new_isolated_check (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) +static db::CompoundRegionOperationNode *new_isolated_check (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, db::zero_distance_mode zd_mode, bool negative) { - return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative, true); + return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, negative, true); } -static db::CompoundRegionOperationNode *new_notch_check (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, bool negative) +static db::CompoundRegionOperationNode *new_notch_check (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::zero_distance_mode zd_mode, bool negative) { db::RegionCheckOptions options (whole_edges, metrics, @@ -445,27 +450,28 @@ static db::CompoundRegionOperationNode *new_notch_check (db::Coord d, bool whole max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), shielded); options.negative = negative; + options.zd_mode = zd_mode; return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::SinglePolygonCheck (db::SpaceRelation, d, options), new_primary (), true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_separation_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) +static db::CompoundRegionOperationNode *new_separation_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, db::zero_distance_mode zd_mode, bool negative) { - return new_check_node (other, db::SpaceRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::SpaceRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, negative); } -static db::CompoundRegionOperationNode *new_overlap_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) +static db::CompoundRegionOperationNode *new_overlap_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, db::zero_distance_mode zd_mode, bool negative) { - return new_check_node (other, db::WidthRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::WidthRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, negative); } -static db::CompoundRegionOperationNode *new_enclosing_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) +static db::CompoundRegionOperationNode *new_enclosing_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, db::zero_distance_mode zd_mode, bool negative) { - return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, 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) +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, db::zero_distance_mode zd_mode, 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); + return new_check_node (other, db::InsideRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, zd_mode, negative); } static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::perimeter_type pmin, db::coord_traits::perimeter_type pmax) @@ -660,31 +666,45 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_minkowski_sum|#new_minkowsky_sum", &new_minkowski_sum_node4, gsi::arg ("input"), gsi::arg ("p"), "@brief Creates a node providing a Minkowski sum with a point sequence forming a contour.\n" ) + - gsi::constructor ("new_width_check", &new_width_check, 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 ("negative", false), + gsi::constructor ("new_width_check", &new_width_check, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing a width check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_space_check", &new_space_check, 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), + gsi::constructor ("new_space_check", &new_space_check, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing a space check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_isolated_check", &new_isolated_check, 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), + gsi::constructor ("new_isolated_check", &new_isolated_check, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing a isolated polygons (space between different polygons) check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_notch_check", &new_notch_check, 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 ("negative", false), + gsi::constructor ("new_notch_check", &new_notch_check, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing a intra-polygon space check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_separation_check", &new_separation_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), + gsi::constructor ("new_separation_check", &new_separation_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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing a separation check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_overlap_check", &new_overlap_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), + gsi::constructor ("new_overlap_check", &new_overlap_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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing an overlap check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\n" ) + - 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), + 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), gsi::arg ("negative", false), "@brief Creates a node providing an inside (enclosure) check.\n" + "\n" + "The zero_distance_mode argument has been inserted in version 0.29.\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), + 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), 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" + "This method has been added in version 0.27.5. The zero_distance_mode argument has been inserted in version 0.29.\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::perimeter_type>::max (), "max"), "@brief Creates a node filtering the input by perimeter.\n" @@ -807,9 +827,7 @@ Class decl_CompoundRegionOperationNode ("db", " "\n" "The search distance for intruder shapes is determined by the operation and computed from the operation's requirements.\n" "\n" - "NOTE: this feature is experimental and not deployed into the the DRC framework yet.\n" - "\n" - "This class has been introduced in version 0.27." + "This class has been introduced in version 0.27. The API is considered internal and will change without notice." ); gsi::EnumIn decl_dbCompoundRegionLogicalBoolOperationNode_LogicalOp ("db", "LogicalOp", diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 6c648ee86..927777e1b 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -234,63 +234,69 @@ static db::Edges with_angle3 (const db::Edges *r, db::SpecialEdgeOrientationFilt return r->filtered (f); } -static db::EdgePairs width2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) +static db::EdgePairs width2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->width_check (d, db::EdgesCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), min_projection.is_nil () ? db::Edges::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to ()) + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + zd_mode) ); } -static db::EdgePairs space2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) +static db::EdgePairs space2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->space_check (d, db::EdgesCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), min_projection.is_nil () ? db::Edges::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to ()) + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + zd_mode) ); } -static db::EdgePairs inside2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) +static db::EdgePairs inside2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->inside_check (other, d, db::EdgesCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), min_projection.is_nil () ? db::Edges::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to ()) + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + zd_mode) ); } -static db::EdgePairs overlap2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) +static db::EdgePairs overlap2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->overlap_check (other, d, db::EdgesCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), min_projection.is_nil () ? db::Edges::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to ()) + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + zd_mode) ); } -static db::EdgePairs enclosing2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) +static db::EdgePairs enclosing2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->enclosing_check (other, d, db::EdgesCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), min_projection.is_nil () ? db::Edges::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to ()) + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + zd_mode) ); } -static db::EdgePairs separation2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) +static db::EdgePairs separation2 (const db::Edges *r, const db::Edges &other, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->separation_check (other, d, db::EdgesCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), min_projection.is_nil () ? db::Edges::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to ()) + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + zd_mode) ); } @@ -1445,7 +1451,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This variant has been introduced in version 0.27." ) + - method_ext ("width_check", &width2, 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 ("width_check", &width2, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a width check with options\n" "@param d The minimum width for which the edges are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -1453,6 +1459,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@param ignore_angle The threshold angle above which no check is performed\n" "@param min_projection The lower threshold of the projected length of one edge onto another\n" "@param max_projection The upper threshold of the projected length of one edge onto another\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -1470,8 +1477,10 @@ Class 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" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("space_check", &space2, 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 ("space_check", &space2, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a space check with options\n" "@param d The minimum distance for which the edges are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -1479,6 +1488,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@param ignore_angle The threshold angle above which no check is performed\n" "@param min_projection The lower threshold of the projected length of one edge onto another\n" "@param max_projection The upper threshold of the projected length of one edge onto another\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the space check.\n" @@ -1496,8 +1506,10 @@ Class 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" + "'zero_distance_mode' has been added in version 0.29." ) + - 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"), + 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"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@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" @@ -1506,6 +1518,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@param ignore_angle The threshold angle above which no check is performed\n" "@param min_projection The lower threshold of the projected length of one edge onto another\n" "@param max_projection The upper threshold of the projected length of one edge onto another\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -1525,8 +1538,9 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "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" + "'zero_distance_mode' has been added in version 0.29." ) + - 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"), + 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"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs an enclosing 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" @@ -1535,6 +1549,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@param ignore_angle The threshold angle above which no check is performed\n" "@param min_projection The lower threshold of the projected length of one edge onto another\n" "@param max_projection The upper threshold of the projected length of one edge onto another\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -1552,8 +1567,10 @@ Class 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" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("overlap_check", &overlap2, 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 ("overlap_check", &overlap2, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs an overlap 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" @@ -1562,6 +1579,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@param ignore_angle The threshold angle above which no check is performed\n" "@param min_projection The lower threshold of the projected length of one edge onto another\n" "@param max_projection The upper threshold of the projected length of one edge onto another\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -1579,8 +1597,10 @@ Class 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" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("separation_check", &separation2, 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 ("separation_check", &separation2, 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 ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs an overlap 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" @@ -1589,6 +1609,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@param ignore_angle The threshold angle above which no check is performed\n" "@param min_projection The lower threshold of the projected length of one edge onto another\n" "@param max_projection The upper threshold of the projected length of one edge onto another\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -1606,6 +1627,8 @@ Class 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" + "'zero_distance_mode' has been added in version 0.29." ) + method_ext ("extents", &extents0, "@brief Returns a region with the bounding boxes of the edges\n" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index d85fe3687..db8c78233 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -1105,12 +1105,54 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25." ) + + gsi::method ("merge_meta_info", static_cast (&db::Layout::merge_meta_info), gsi::arg ("other"), + "@brief Merges the meta information from the other layout into this layout\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "Existing keys in this layout will be overwritten by the respective values from the other layout.\n" + "New keys will be added.\n" + "\n" + "This method has been introduced in version 0.28.16." + ) + + gsi::method ("merge_meta_info", static_cast (&db::Layout::merge_meta_info), gsi::arg ("other"), gsi::arg ("cm"), + "@brief Merges the meta information from the other layout into this layout for the cells given by the cell mapping\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "This method will use the source/target cell pairs from the cell mapping object and merge the meta information " + "from each source cell from the 'other' layout into the mapped cell inside self.\n" + "This method can be used with '\\copy_tree_shapes' and similar to copy meta information in addition to the shapes.\n" + "Existing cell-specific keys in this layout will be overwritten by the respective values from the other layout.\n" + "New keys will be added.\n" + "\n" + "This method has been introduced in version 0.28.16." + ) + + gsi::method ("copy_meta_info", static_cast (&db::Layout::copy_meta_info), gsi::arg ("other"), + "@brief Copies the meta information from the other layout into this layout\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "The meta information from this layout will be replaced by the meta information from the other layout.\n" + "\n" + "This method has been introduced in version 0.28.16." + ) + + gsi::method ("copy_meta_info", static_cast (&db::Layout::copy_meta_info), gsi::arg ("other"), gsi::arg ("cm"), + "@brief Copies the meta information from the other layout into this layout for the cells given by the cell mapping\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "This method will use the source/target cell pairs from the cell mapping object and merge the meta information " + "from each source cell from the 'other' layout into the mapped cell inside self.\n" + "This method can be used with '\\copy_tree_shapes' and similar to copy meta information in addition to the shapes.\n" + "All cell-specific keys in this layout will be replaced by the respective values from the other layout.\n" + "\n" + "This method has been introduced in version 0.28.16." + ) + gsi::method ("clear_meta_info", static_cast (&db::Layout::clear_meta), "@brief Clears the meta information of the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.28.8." ) + + gsi::method ("clear_all_meta_info", static_cast (&db::Layout::clear_all_meta), + "@brief Clears all meta information of the layout (cell specific and global)\n" + "See \\LayoutMetaInfo for details about layouts and meta information." + "\n" + "This method has been introduced in version 0.28.16." + ) + gsi::method ("remove_meta_info", static_cast (&db::Layout::remove_meta_info), gsi::arg ("name"), "@brief Removes meta information from the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." diff --git a/src/db/db/gsiDeclDbLayoutDiff.cc b/src/db/db/gsiDeclDbLayoutDiff.cc index 84ac3217e..3e8098750 100644 --- a/src/db/db/gsiDeclDbLayoutDiff.cc +++ b/src/db/db/gsiDeclDbLayoutDiff.cc @@ -90,6 +90,11 @@ public: dbu_differs_event (dbu_a, dbu_b); } + virtual void layout_meta_info_differs (const std::string &name, const tl::Variant &value_a, const tl::Variant &value_b) + { + layout_meta_info_differs_event (name, value_a, value_b); + } + virtual void layer_in_a_only (const db::LayerProperties &la) { layer_in_a_only_event (la); @@ -132,6 +137,11 @@ public: begin_cell_event (mp_cell_a, mp_cell_b); } + virtual void cell_meta_info_differs (const std::string &name, const tl::Variant &value_a, const tl::Variant &value_b) + { + cell_meta_info_differs_event (name, value_a, value_b); + } + virtual void begin_inst_differences () { begin_inst_differences_event (); @@ -343,6 +353,7 @@ public: } tl::event dbu_differs_event; + tl::event layout_meta_info_differs_event; tl::event layer_in_a_only_event; tl::event layer_in_b_only_event; tl::event layer_name_differs_event; @@ -351,6 +362,7 @@ public: tl::event cell_in_b_only_event; tl::event bbox_differs_event; tl::event begin_cell_event; + tl::event cell_meta_info_differs_event; tl::Event begin_inst_differences_event; tl::event instance_in_a_only_event; tl::event instance_in_b_only_event; @@ -409,6 +421,10 @@ static unsigned int f_no_properties () { return db::layout_diff::f_no_properties; } +static unsigned int f_with_meta () { + return db::layout_diff::f_with_meta; +} + static unsigned int f_no_layer_names () { return db::layout_diff::f_no_layer_names; } @@ -450,7 +466,7 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", "full compare.\n" "\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("IgnoreDuplicates", &f_ignore_duplicates, "@brief Ignore duplicate instances or shapes\n" @@ -462,17 +478,25 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", gsi::constant ("NoTextOrientation", &f_no_text_orientation, "@brief Ignore text orientation\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("NoProperties", &f_no_properties, "@brief Ignore properties\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." + ) + + gsi::constant ("WithMetaInfo", &f_with_meta, + "@brief Ignore meta info\n" + "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " + "combined with other constants to form a flag set. If present, this option tells the compare algorithm " + "to include persisted meta info in the compare.\n" + "\n" + "This flag has been introduced in version 0.28.16." ) + gsi::constant ("NoLayerNames", &f_no_layer_names, "@brief Do not compare layer names\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("Verbose", &f_verbose, "@brief Enables verbose mode (gives details about the differences)\n" @@ -480,22 +504,22 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", "See the event descriptions for details about the differences in verbose and non-verbose mode.\n" "\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("BoxesAsPolygons", &f_boxes_as_polygons, "@brief Compare boxes to polygons\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("FlattenArrayInsts", &f_flatten_array_insts, "@brief Compare array instances instance by instance\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("PathsAsPolygons", &f_paths_as_polygons, "@brief Compare paths to polygons\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("SmartCellMapping", &f_smart_cell_mapping, "@brief Derive smart cell mapping instead of name mapping (available only if top cells are specified)\n" @@ -503,7 +527,7 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", "cells are compared (with \\LayoutDiff#compare with cells instead of layout objects).\n" "\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set.\n" + "combined with other constants to form a flag set.\n" ) + gsi::constant ("DontSummarizeMissingLayers", &f_dont_summarize_missing_layers, "@brief Don't summarize missing layers\n" @@ -511,12 +535,12 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", "layer will be reported as difference.\n" "\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::constant ("NoTextDetails", &f_no_text_details, "@brief Ignore text details (font, size, presentation)\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " - "compared with other constants to form a flag set." + "combined with other constants to form a flag set." ) + gsi::method ("compare", &LayoutDiff::compare_layouts, gsi::arg("a"), @@ -593,6 +617,14 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", gsi::event ("on_dbu_differs", &LayoutDiff::dbu_differs_event, gsi::arg ("dbu_a"), gsi::arg ("dbu_b"), "@brief This signal indicates a difference in the database units of the layouts\n" ) + + gsi::event ("on_layout_meta_info_differs", &LayoutDiff::layout_meta_info_differs_event, gsi::arg ("name"), gsi::arg ("a"), gsi::arg ("b"), + "@brief This signal indicates that global meta info differs\n" + "Meta information is only compared when \\WithMetaInfo is added to the compare flags.\n" + "'a' and 'b' are the values for the first and second layout. 'nil' is passed to these values to indicate " + "missing meta information on one side.\n" + "\n" + "This event has been added in version 0.28.16." + ) + gsi::event ("on_layer_in_a_only", &LayoutDiff::layer_in_a_only_event, gsi::arg ("a"), "@brief This signal indicates a layer that is present only in the first layout\n" ) + @@ -619,9 +651,17 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", "In verbose mode detailed events will be issued indicating the differences.\n" ) + gsi::event ("on_begin_cell", &LayoutDiff::begin_cell_event, gsi::arg ("ca"), gsi::arg ("cb"), - "@brief This signal initiates the sequence of events for a cell pair\n" + "@brief This signal indicates the sequence of events for a cell pair\n" "All cell specific events happen between \\begin_cell_event and \\end_cell_event signals." ) + + gsi::event ("on_cell_meta_info_differs", &LayoutDiff::cell_meta_info_differs_event, gsi::arg ("name"), gsi::arg ("a"), gsi::arg ("b"), + "@brief This signal indicates that meta info between the current cells differs\n" + "Meta information is only compared when \\WithMetaInfo is added to the compare flags.\n" + "'a' and 'b' are the values for the first and second layout. 'nil' is passed to these values to indicate " + "missing meta information on one side.\n" + "\n" + "This event has been added in version 0.28.16." + ) + gsi::event ("on_begin_inst_differences", &LayoutDiff::begin_inst_differences_event, "@brief This signal indicates differences in the cell instances\n" "In verbose mode (see \\Verbose) more events will follow that indicate the instances that are present only " diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 700cd2162..70abf754b 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -526,7 +526,7 @@ static db::Region merged_ext2 (db::Region *r, bool min_coherence, int min_wc) return r->merged (min_coherence, std::max (0, min_wc - 1)); } -static db::EdgePairs width2 (const db::Region *r, db::Region::distance_type 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, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs width2 (const db::Region *r, db::Region::distance_type 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, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->width_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -537,11 +537,12 @@ static db::EdgePairs width2 (const db::Region *r, db::Region::distance_type d, b db::NoOppositeFilter, db::NoRectFilter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs notch2 (const db::Region *r, db::Region::distance_type 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, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs notch2 (const db::Region *r, db::Region::distance_type 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, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->notch_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -552,11 +553,12 @@ static db::EdgePairs notch2 (const db::Region *r, db::Region::distance_type d, b db::NoOppositeFilter, db::NoRectFilter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs isolated2 (const db::Region *r, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs isolated2 (const db::Region *r, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->isolated_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -567,11 +569,12 @@ static db::EdgePairs isolated2 (const db::Region *r, db::Region::distance_type d opposite, rect_filter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs space2 (const db::Region *r, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs space2 (const db::Region *r, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->space_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -582,11 +585,12 @@ static db::EdgePairs space2 (const db::Region *r, db::Region::distance_type d, b opposite, rect_filter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs inside2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs inside2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->inside_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -597,11 +601,12 @@ static db::EdgePairs inside2 (const db::Region *r, const db::Region &other, db:: opposite, rect_filter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs overlap2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs overlap2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->overlap_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -612,11 +617,12 @@ static db::EdgePairs overlap2 (const db::Region *r, const db::Region &other, db: opposite, rect_filter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs enclosing2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs enclosing2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->enclosing_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -627,11 +633,12 @@ static db::EdgePairs enclosing2 (const db::Region *r, const db::Region &other, d opposite, rect_filter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } -static db::EdgePairs separation2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +static db::EdgePairs separation2 (const db::Region *r, const db::Region &other, db::Region::distance_type 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, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint, db::zero_distance_mode zero_distance_mode) { return r->separation_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -642,7 +649,8 @@ static db::EdgePairs separation2 (const db::Region *r, const db::Region &other, opposite, rect_filter, negative, - prop_constraint) + prop_constraint, + zero_distance_mode) ); } @@ -792,6 +800,40 @@ edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode) } } +static std::vector > +rasterize2 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_distance, const db::Vector &pixel_size, unsigned int nx, unsigned int ny) +{ + db::DAreaMap am (db::DPoint (origin), db::DVector (pixel_distance), db::DVector (pixel_size), nx, ny); + + auto pi = region->begin (); + pi = pi.confined (db::Box (am.bbox ()), false /*not overlapping*/); + + while (! pi.at_end ()) { + db::DPolygon dp (*pi); + db::rasterize (dp, am); + ++pi; + } + + std::vector > result; + result.reserve (ny); + for (unsigned int y = 0; y < ny; ++y) { + result.push_back (std::vector ()); + std::vector &row = result.back (); + row.reserve (nx); + for (unsigned int x = 0; x < nx; ++x) { + row.push_back (am.get (x, y)); + } + } + + return result; +} + +static std::vector > +rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_size, unsigned int nx, unsigned int ny) +{ + return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny); +} + static db::Point default_origin; // provided by gsiDeclDbPolygon.cc: @@ -2606,7 +2648,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This variant was introduced in version 0.27.\n" ) + - method_ext ("width_check", &width2, 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 ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + method_ext ("width_check", &width2, 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 ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a width check with options\n" "@param d The minimum width for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2617,6 +2659,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param shielded Enables shielding\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param property_constraint Only \\IgnoreProperties and \\NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output. " + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "Other than 'width' allow more options here.\n" "\n" "This version is similar to the simple version with one parameter. In addition, it allows " @@ -2647,9 +2690,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" "The 'shielded' and 'negative' options have been introduced in version 0.27. " - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.28.16." ) + - method_ext ("space_check", &space2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + method_ext ("space_check", &space2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a space check with options\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2661,6 +2705,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2690,9 +2735,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27.\n" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("notch_check", ¬ch2, 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 ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + method_ext ("notch_check", ¬ch2, 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 ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a space check between edges of the same polygon with options\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2704,6 +2750,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "@param property_constraint Only \\IgnoreProperties and \\NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "This version is similar to the simple version with one parameter. In addition, it allows " "to specify many more options.\n" @@ -2733,9 +2780,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" "The 'shielded' and 'negative' options have been introduced in version 0.27.\n" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("isolated_check", &isolated2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + method_ext ("isolated_check", &isolated2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a space check between edges of different polygons with options\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2747,6 +2795,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2776,9 +2825,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27.\n" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + - 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@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" @@ -2791,6 +2841,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2827,9 +2878,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "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" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + - 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs an overlap check with options\n" "@param d The minimum overlap for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2842,6 +2894,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2877,9 +2930,10 @@ Class 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" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("enclosing_check", &enclosing2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + method_ext ("enclosing_check", &enclosing2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs an enclosing check with options\n" "@param d The minimum enclosing distance for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2892,6 +2946,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2927,9 +2982,10 @@ Class 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" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + - method_ext ("separation_check", &separation2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + method_ext ("separation_check", &separation2, 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), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a separation check with options\n" "@param d The minimum separation for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2942,6 +2998,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param zero_distance_mode Specifies how to handle edges with zero distance\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2977,7 +3034,8 @@ Class 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" - "'property_constraint' has been added in version 0.28.4." + "'property_constraint' has been added in version 0.28.4.\n" + "'zero_distance_mode' has been added in version 0.29." ) + method_ext ("area", &area1, "@brief The area of the region\n" @@ -3088,6 +3146,36 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@brief Converts the region to a string\n" "This version allows specification of the maximum number of polygons contained in the string." ) + + method_ext ("rasterize", &rasterize1, gsi::arg ("origin"), gsi::arg ("pixel_size"), gsi::arg ("nx"), gsi::arg ("ny"), + "@brief A grayscale rasterizer delivering the area covered per pixel\n" + "@param origin The lower-left corner of the lowest-left pixel\n" + "@param pixel_size The dimension of each pixel (the x component gives the width, the y component the height)\n" + "@param nx The number of pixels in horizontal direction\n" + "@param ny The number of pixels in vertical direction\n" + "The method will create a grayscale, high-resolution density map of a rectangular region.\n" + "The scan region is defined by the origin, the pixel size and the number of pixels in horizontal (nx) and\n" + "vertical (ny) direction. The resulting array will contain the area covered by polygons from the region\n" + "in square database units.\n" + "\n" + "For non-overlapping polygons, the maximum density value is px*py. Overlapping polygons are counted multiple\n" + "times, so the actual values may be larger. If you want overlaps removed, you have to\n" + "merge the region before. Merge semantics does not apply for the 'rasterize' method.\n" + "\n" + "The resulting area values are precise within the limits of double-precision floating point arithmetics.\n" + "\n" + "A second version exists that allows specifying an active pixel size which is smaller than the\n" + "pixel distance hence allowing pixels samples that do not cover the full area, but leave gaps between the pixels.\n" + "\n" + "This method has been added in version 0.29.\n" + ) + + method_ext ("rasterize", &rasterize2, gsi::arg ("origin"), gsi::arg ("pixel_distance"), gsi::arg ("pixel_size"), gsi::arg ("nx"), gsi::arg ("ny"), + "@brief A version of 'rasterize' that allows a pixel step distance which is larger than the pixel size\n" + "This version behaves like the first variant of 'rasterize', but the pixel distance (pixel-to-pixel step raster)\n" + "can be specified separately from the pixel size. Currently, the pixel size must be equal or smaller than the\n" + "pixel distance - i.e. the pixels must not overlap.\n" + "\n" + "This method has been added in version 0.29.\n" + ) + method ("enable_progress", &db::Region::enable_progress, gsi::arg ("label"), "@brief Enable progress reporting\n" "After calling this method, the region will report the progress through a progress bar while " @@ -3298,6 +3386,46 @@ gsi::Enum decl_EdgeMode ("db", "EdgeMode", // Inject the Region::EdgeMode declarations into Region: gsi::ClassExt inject_EdgeMode_in_Region (decl_EdgeMode.defs ()); +gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode", + gsi::enum_const ("NeverIncludeZeroDistance", db::NeverIncludeZeroDistance, + "@brief Specifies that check functions should never include edges with zero distance.\n" + "With this specification, the check functions will ignore edges which are collinear or touch." + ) + + gsi::enum_const ("AlwaysIncludeZeroDistance", db::AlwaysIncludeZeroDistance, + "@hide\n" + "@brief Specifies that check functions should always include edges with zero distance\n" + "This mode has little practical value.\n" + ) + + gsi::enum_const ("IncludeZeroDistanceWhenTouching", db::IncludeZeroDistanceWhenTouching, + "@brief Specifies that check functions should include edges when they touch\n" + "With this specification, the check functions will also check edges if they share at least one common point. " + "This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.28.16 and later. " + ) + + gsi::enum_const ("IncludeZeroDistanceWhenCollinearAndTouching", db::IncludeZeroDistanceWhenCollinearAndTouching, + "@brief Specifies that check functions should include edges when they are collinear and touch\n" + "With this specification, the check functions will also check edges if they share at least one common point and are collinear. " + "This is the mode that includes checking the 'kissing corner' cases when the kissing edges are collinear. This mode was default up to version 0.28. " + ) + + gsi::enum_const ("IncludeZeroDistanceWhenOverlapping", db::IncludeZeroDistanceWhenOverlapping, + "@brief Specifies that check functions should include edges when they overlap\n" + "With this specification, the check functions will also check edges which are collinear and share more than a single point. " + "This is the mode that excludes the 'kissing corner' cases." + ), + "@brief This class represents the zero_distance_mode type for \\Region#width and related checks.\n" + "This mode determines how edges with zero distance are treated in the DRC checks. Formally these edges do neither represent " + "a space other other relation as they do not face each other. There are three modes available to treat this boundary case: " + "Ignore such edges (\\NeverIncludeZeroDistance) or only include them " + "if they share at least one common point (\\IncludeZeroDistanceWhenTouching). The latter mode allows activating checks for " + "the 'kissing corner' case and is the default mode in most checks." + "\n" + "This enum has been introduced in version 0.28.16." +); + +// Inject the Region::ZeroDistanceMode declarations into Region and Edges: +// (Edges injection has to be done here because only here defs() is available) +gsi::ClassExt inject_ZeroDistanceMode_in_Region (decl_ZeroDistanceMode.defs ()); +gsi::ClassExt inject_ZeroDistanceMode_in_Edges (decl_ZeroDistanceMode.defs ()); + gsi::Enum decl_PropertyConstraint ("db", "PropertyConstraint", gsi::enum_const ("IgnoreProperties", db::IgnoreProperties, "@brief Specifies to ignore properties\n" diff --git a/src/db/db/gsiDeclDbTechnologies.cc b/src/db/db/gsiDeclDbTechnologies.cc index 2a7fa7167..de670fa4f 100644 --- a/src/db/db/gsiDeclDbTechnologies.cc +++ b/src/db/db/gsiDeclDbTechnologies.cc @@ -135,6 +135,19 @@ gsi::Class technology_component_decl ("db", "Technology DB_PUBLIC gsi::Class &decl_dbTechnologyComponent () { return technology_component_decl; } +static void +set_default_grid_list (db::Technology *tech, const std::vector &grids) +{ + std::string r; + for (auto g = grids.begin (); g != grids.end (); ++g) { + if (! r.empty ()) { + r += ","; + } + r += tl::micron_to_string (*g); + } + tech->set_default_grids (r); +} + gsi::Class technology_decl ("db", "Technology", gsi::method ("name", &db::Technology::name, "@brief Gets the name of the technology" @@ -218,6 +231,19 @@ gsi::Class technology_decl ("db", "Technology", gsi::method ("dbu=", &db::Technology::set_dbu, gsi::arg ("dbu"), "@brief Sets the default database unit\n" ) + + gsi::method ("default_grids", &db::Technology::default_grid_list, + "@brief Gets the default grids\n" + "\n" + "See \\default_grids for details.\n" + "\n" + "This property has been introduced in version 0.28.17." + ) + + gsi::method_ext ("default_grids=", &set_default_grid_list, gsi::arg ("grids"), + "@brief Sets the default grids\n" + "If not empty, this list replaces the global grid list for this technology.\n" + "\n" + "This property has been introduced in version 0.28.17." + ) + gsi::method ("layer_properties_file", &db::Technology::layer_properties_file, "@brief Gets the path of the layer properties file\n" "\n" diff --git a/src/db/unit_tests/dbEdgePairRelationsTests.cc b/src/db/unit_tests/dbEdgePairRelationsTests.cc index c0b3f918f..4b3d7f8c0 100644 --- a/src/db/unit_tests/dbEdgePairRelationsTests.cc +++ b/src/db/unit_tests/dbEdgePairRelationsTests.cc @@ -51,139 +51,139 @@ TEST(1) TEST(2) { db::Edge output; - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 10), db::Point (100, 200)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (100, 200)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 50), db::Point (100, 50)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (100, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 10), db::Point (100, 200)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (100, 200)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 50), db::Point (100, 50)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (100, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;100,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;187,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(100,-50;187,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(-87,-50;187,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (0, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (0, -50)), &output), true); EXPECT_EQ (output.to_string (), "(-87,-50;0,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -100), db::Point (300, -100)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, 0), db::Point (300, -100)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -100), db::Point (300, -100)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, 0), db::Point (300, -100)), &output), true); EXPECT_EQ (output.to_string (), "(-94,-34;164,-77)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(0,0;50,-100)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (40, 0), db::Point (140, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (40, 0), db::Point (140, -200)), &output), true); EXPECT_EQ (output.to_string (), "(40,0;90,-100)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (200, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;145,-89)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -200), db::Point (200, -200)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -200), db::Point (200, -200)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;145,-89)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), true); EXPECT_EQ (output.to_string (), "(120,0;120,-98)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;100,-100)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true); EXPECT_EQ (output.to_string (), "(80,0;80,-100)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), true); EXPECT_EQ (output.to_string (), "(-80,0;-80,-60)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(80,0;-45,-89)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true); EXPECT_EQ (output.to_string (), "(100,-50;100,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (50, -50), db::Point (50, -50)), &output), true); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (50, -50), db::Point (50, -50)), &output), true); EXPECT_EQ (output.to_string (), "(50,-50;50,-50)"); - EXPECT_EQ (euclidian_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (190, -50), db::Point (190, -50)), &output), false); + EXPECT_EQ (euclidian_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (190, -50), db::Point (190, -50)), &output), false); } TEST(3) { db::Edge output; - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (100, 200)), &output), false); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 50), db::Point (100, 50)), &output), false); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (100, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (100, 200)), &output), false); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 50), db::Point (100, 50)), &output), false); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (100, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;100,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;200,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(100,-50;200,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(-100,-50;200,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (0, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (0, -50)), &output), true); EXPECT_EQ (output.to_string (), "(-100,-50;0,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -100), db::Point (300, -100)), &output), false); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, 0), db::Point (300, -100)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -100), db::Point (300, -100)), &output), false); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, 0), db::Point (300, -100)), &output), true); EXPECT_EQ (output.to_string (), "(-100,-33;200,-83)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(0,0;50,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (40, 0), db::Point (140, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (40, 0), db::Point (140, -200)), &output), true); EXPECT_EQ (output.to_string (), "(40,0;90,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (200, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;150,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -200), db::Point (200, -200)), &output), false); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -200), db::Point (200, -200)), &output), false); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;150,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), true); EXPECT_EQ (output.to_string (), "(120,0;120,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;100,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true); EXPECT_EQ (output.to_string (), "(80,0;80,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), true); EXPECT_EQ (output.to_string (), "(-80,0;-80,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(80,0;-60,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(-100,0;-100,-100)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true); EXPECT_EQ (output.to_string (), "(100,-50;100,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (50, -50), db::Point (50, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (50, -50), db::Point (50, -50)), &output), true); EXPECT_EQ (output.to_string (), "(50,-50;50,-50)"); - EXPECT_EQ (square_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (190, -50), db::Point (190, -50)), &output), true); + EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (190, -50), db::Point (190, -50)), &output), true); EXPECT_EQ (output.to_string (), "(190,-50;190,-50)"); } TEST(4) { db::Edge output; - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (100, 200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 50), db::Point (100, 50)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (100, -50)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (100, 200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 50), db::Point (100, 50)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (100, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;100,-50)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;100,-50)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (300, -50)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (300, -50)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (300, -50)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (300, -50)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;100,-50)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (0, -50)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -100), db::Point (300, -100)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, 0), db::Point (300, -100)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -50), db::Point (0, -50)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, -100), db::Point (300, -100)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-300, 0), db::Point (300, -100)), &output), true); EXPECT_EQ (output.to_string (), "(0,-50;100,-67)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, -200)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(0,0;50,-100)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (40, 0), db::Point (140, -200)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (40, 0), db::Point (140, -200)), &output), true); EXPECT_EQ (output.to_string (), "(40,0;90,-100)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (200, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -200), db::Point (200, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (200, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, -200), db::Point (200, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true); EXPECT_EQ (output.to_string (), "(100,0;100,-100)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true); EXPECT_EQ (output.to_string (), "(80,0;80,-100)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true); EXPECT_EQ (output.to_string (), "(80,0;0,-57)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true); EXPECT_EQ (output.to_string (), "(100,-50;100,-50)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (50, -50), db::Point (50, -50)), &output), true); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (50, -50), db::Point (50, -50)), &output), true); EXPECT_EQ (output.to_string (), "(50,-50;50,-50)"); - EXPECT_EQ (projected_near_part_of_edge (true, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (190, -50), db::Point (190, -50)), &output), false); + EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (190, -50), db::Point (190, -50)), &output), false); } TEST(5) @@ -291,7 +291,7 @@ TEST(7) EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(1,30;1,20)"); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, 20)), &output); EXPECT_EQ (res, false); - f.set_include_zero (false); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, 20)), &output); EXPECT_EQ (res, false); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (1, 30), db::Point (1, 20)), &output); @@ -300,7 +300,7 @@ TEST(7) res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, 20)), &output); EXPECT_EQ (res, false); - f.set_include_zero (true); + f.set_zero_distance_mode (db::AlwaysIncludeZeroDistance); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 300), db::Point (0, -200)), &output); EXPECT_EQ (res, true); EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,110;0,-100)"); @@ -309,7 +309,7 @@ TEST(7) EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(1,110;1,-100)"); f.set_metrics (db::Square); - f.set_include_zero (true); + f.set_zero_distance_mode (db::AlwaysIncludeZeroDistance); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, 20)), &output); EXPECT_EQ (res, true); EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,30;0,20)"); @@ -318,7 +318,7 @@ TEST(7) EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(1,30;1,20)"); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, 20)), &output); EXPECT_EQ (res, false); - f.set_include_zero (false); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, 20)), &output); EXPECT_EQ (res, false); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (1, 30), db::Point (1, 20)), &output); @@ -327,7 +327,7 @@ TEST(7) res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, 20)), &output); EXPECT_EQ (res, false); - f.set_include_zero (true); + f.set_zero_distance_mode (db::AlwaysIncludeZeroDistance); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 300), db::Point (0, -200)), &output); EXPECT_EQ (res, true); EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,110;0,-100)"); @@ -336,7 +336,7 @@ TEST(7) EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(1,110;1,-100)"); f.set_metrics (db::Projection); - f.set_include_zero (true); + f.set_zero_distance_mode (db::AlwaysIncludeZeroDistance); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, -20)), &output); EXPECT_EQ (res, true); EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,10;0,0)"); @@ -345,7 +345,7 @@ TEST(7) EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(1,10;1,0)"); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, -20)), &output); EXPECT_EQ (res, false); - f.set_include_zero (false); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, 11)), &output); EXPECT_EQ (res, false); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (1, 30), db::Point (1, -20)), &output); @@ -361,7 +361,7 @@ TEST(8_KissingCornerProblem) // if the projection is >0. db::EdgeRelationFilter f (db::WidthRelation, 10); - f.set_include_zero (false); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); db::EdgePair output; bool res; @@ -382,8 +382,39 @@ TEST(8_KissingCornerProblem) res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); EXPECT_EQ (res, false); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenOverlapping); + + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,40;0,100):(0,110;0,50)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::NeverIncludeZeroDistance); + + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + f = db::EdgeRelationFilter (db::SpaceRelation, 10); - f.set_include_zero (false); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); f.set_metrics (db::Euclidian); res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); @@ -401,4 +432,188 @@ TEST(8_KissingCornerProblem) EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,10;0,0):(0,-10;0,0)"); res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenOverlapping); + + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,40):(0,50;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::NeverIncludeZeroDistance); + + f.set_metrics (db::Euclidian); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); +} + +TEST(9_KissingCornerProblemSquareMetrics) +{ + // The kissing corner problem is solved by allowing distance-0 width and space relations and checking them + // if the projection is >0. + + db::EdgeRelationFilter f (db::WidthRelation, 10); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); + db::EdgePair output; + bool res; + + f.set_metrics (db::Square); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,90;0,100):(0,110;0,100)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,40;0,100):(0,110;0,50)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,0;0,-10)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::NeverIncludeZeroDistance); + + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f = db::EdgeRelationFilter (db::SpaceRelation, 10); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); + + f.set_metrics (db::Square); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,90):(0,100;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,40):(0,50;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,10;0,0):(0,-10;0,0)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::NeverIncludeZeroDistance); + + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); +} + +TEST(10_KissingCornerProblemProjectionMetrics) +{ + // The kissing corner problem is solved by allowing distance-0 width and space relations and checking them + // if the projection is >0. It is not effective in projection metrics as there is no overlap. + + db::EdgeRelationFilter f (db::WidthRelation, 10); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); + db::EdgePair output; + bool res; + + f.set_metrics (db::Projection); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,50;0,100):(0,100;0,50)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::NeverIncludeZeroDistance); + + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f = db::EdgeRelationFilter (db::SpaceRelation, 10); + f.set_zero_distance_mode (db::IncludeZeroDistanceWhenTouching); + + f.set_metrics (db::Projection); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,50):(0,50;0,100)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); + + f.set_zero_distance_mode (db::NeverIncludeZeroDistance); + + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); } diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc index 87ef5f1ba..6fb0d413b 100644 --- a/src/db/unit_tests/dbLayoutDiffTests.cc +++ b/src/db/unit_tests/dbLayoutDiffTests.cc @@ -38,6 +38,7 @@ public: std::string text () const { return m_os.str (); } void clear () { m_os.str (std::string ()); } void dbu_differs (double dbu_a, double dbu_b); + void layout_meta_info_differs (const std::string &, const tl::Variant &, const tl::Variant &); void layer_in_a_only (const db::LayerProperties &la); void layer_in_b_only (const db::LayerProperties &lb); void layer_name_differs (const db::LayerProperties &la, const db::LayerProperties &lb); @@ -45,6 +46,7 @@ public: void cell_in_b_only (const std::string &cellname, db::cell_index_type ci); void cell_name_differs (const std::string &cellname_a, db::cell_index_type cia, const std::string &cellname_b, db::cell_index_type cib); void begin_cell (const std::string &cellname, db::cell_index_type cia, db::cell_index_type cib); + void cell_meta_info_differs (const std::string &, const tl::Variant &, const tl::Variant &); void bbox_differs (const db::Box &ba, const db::Box &bb); void begin_inst_differences (); void instances_in_a (const std::vector &insts_a, const std::vector &cell_names, const db::PropertiesRepository &props); @@ -155,6 +157,12 @@ TestDifferenceReceiver::dbu_differs (double dbu_a, double dbu_b) m_os << "layout_diff: database units differ " << dbu_a << " vs. " << dbu_b << std::endl; } +void +TestDifferenceReceiver::layout_meta_info_differs (const std::string &name, const tl::Variant &va, const tl::Variant &vb) +{ + m_os << "layout_diff: global meta info differs " << name << ": " << va.to_string () << " vs. " << vb.to_string () << std::endl; +} + void TestDifferenceReceiver::layer_in_a_only (const db::LayerProperties &la) { @@ -204,6 +212,12 @@ TestDifferenceReceiver::begin_cell (const std::string &cellname, db::cell_index_ m_cellname = cellname; } +void +TestDifferenceReceiver::cell_meta_info_differs (const std::string &name, const tl::Variant &va, const tl::Variant &vb) +{ + m_os << "layout_diff: cell meta info differs for cell " << m_cellname << " - " << name << ": " << va.to_string () << " vs. " << vb.to_string () << std::endl; +} + void TestDifferenceReceiver::begin_inst_differences () { @@ -1704,4 +1718,46 @@ TEST(8) ); } +// meta info +TEST(9) +{ + db::Layout a; + db::cell_index_type caa = a.add_cell ("A"); + db::cell_index_type cab = a.add_cell ("B"); + db::Layout b; + db::cell_index_type cba = b.add_cell ("A"); + db::cell_index_type cbb = b.add_cell ("B"); + + a.add_meta_info ("x", db::MetaInfo ("", 17.0, true)); + a.add_meta_info ("y", db::MetaInfo ("", -1.0, false)); // not persisted + a.add_meta_info ("z", db::MetaInfo ("", -1.0, true)); + a.add_meta_info (caa, "a1", db::MetaInfo ("", "a", true)); + a.add_meta_info (caa, "a2", db::MetaInfo ("", 42, false)); // not persisted + a.add_meta_info (caa, "a3", db::MetaInfo ("", 41, true)); + a.add_meta_info (cab, "b1", db::MetaInfo ("", "b", true)); + a.add_meta_info (cab, "b2", db::MetaInfo ("", 3, false)); // not persisted + a.add_meta_info (cab, "b3", db::MetaInfo ("", "q", true)); + + b.add_meta_info ("x", db::MetaInfo ("", 21.0, true)); + b.add_meta_info ("y", db::MetaInfo ("", -1.0, true)); + b.add_meta_info (cba, "a1", db::MetaInfo ("", "aa", true)); + b.add_meta_info (cba, "a2", db::MetaInfo ("", 42, true)); + b.add_meta_info (cbb, "b1", db::MetaInfo ("", "bb", true)); + b.add_meta_info (cbb, "b2", db::MetaInfo ("", 3, true)); + + TestDifferenceReceiver r; + bool eq = db::compare_layouts (a, b, db::layout_diff::f_verbose | db::layout_diff::f_with_meta, 0, r); + EXPECT_EQ (eq, false); + EXPECT_EQ (r.text (), + "layout_diff: global meta info differs x: 17 vs. 21\n" + "layout_diff: global meta info differs y: nil vs. -1\n" + "layout_diff: global meta info differs z: -1 vs. nil\n" + "layout_diff: cell meta info differs for cell A - a1: a vs. aa\n" + "layout_diff: cell meta info differs for cell A - a2: nil vs. 42\n" + "layout_diff: cell meta info differs for cell A - a3: 41 vs. nil\n" + "layout_diff: cell meta info differs for cell B - b1: b vs. bb\n" + "layout_diff: cell meta info differs for cell B - b2: nil vs. 3\n" + "layout_diff: cell meta info differs for cell B - b3: q vs. nil\n" + ); +} diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index 27d7eb4ec..cf238d44f 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -126,7 +126,7 @@ TEST(insert_vertex_convex) tris.insert_point (0.6, 0.5); tris.insert_point (0.7, 0.5); tris.insert_point (0.6, 0.4); - EXPECT_EQ (tris.to_string (), "((0.2, 0.2), (0.2, 0.8), (0.6, 0.5)), ((0.7, 0.5), (0.6, 0.5), (0.2, 0.8)), ((0.6, 0.5), (0.6, 0.4), (0.2, 0.2)), ((0.6, 0.5), (0.7, 0.5), (0.6, 0.4))"); + EXPECT_EQ (tris.to_string (), "((0.2, 0.2), (0.2, 0.8), (0.6, 0.5)), ((0.2, 0.8), (0.7, 0.5), (0.6, 0.5)), ((0.6, 0.4), (0.6, 0.5), (0.7, 0.5)), ((0.6, 0.4), (0.2, 0.2), (0.6, 0.5))"); EXPECT_EQ (tris.check(), true); } diff --git a/src/doc/doc/about/drc_ref.xml b/src/doc/doc/about/drc_ref.xml index 203941a1d..889c3fedc 100644 --- a/src/doc/doc/about/drc_ref.xml +++ b/src/doc/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index 2a81774f7..a7c226581 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index f18b0f0e7..7e8b6ba82 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1,12 +1,17 @@ - + DRC Reference: Global Functions +

+Some functions are available on global level and can be used without any object. +Most of them are convenience functions that basically act on some default object +or provide function-like alternatives for the methods. +

"angle" - In universal DRC context: selects edges based on their orientation

@@ -828,7 +833,7 @@ The following example selects all shapes which are rectangles and whose area is larger than 0.5 square micrometers:

-out = in.drc(if_all(area > 0.5, rectangle))
+out = in.drc(if_all(area > 0.5, rectangles))
 

The condition expressions may be of any type (edges, edge pairs and polygons). diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 1a691b473..547d1b7b1 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -1,12 +1,15 @@ - + DRC Reference: Layer Object +

+The layer object represents a collection of polygons, edges or edge pairs. +

"&" - Boolean AND operation

@@ -2929,7 +2932,45 @@ The following image shows the effect of the separation check (input1: red, input

-

opposite and rectangle error filtering

+

Touching shapes

+

+Like width and space, the separation check also supports the "without_touching_corners" option. +

+This option will turn off errors that arise due to +edges touching in one corner (the "kissing corners" configuration). +By default, such edges will yield an error, as they +form a zero-distance situation. With this option in place, no errors will be reported. +

+The following images illustrate the effect of the "without_touching_corners" option. +The white line at the top of the bottom red shape is actually an edge pair indicating +the zero-distance violation of the separation check: +

+ + + + + +
+

+Another option is "without_touching_edges" which turns off errors that arise +at coincident edges. Formally such edges represent a zero-distance situation, hence +are flagged by default. Turning off the check in this case can be helpful when +separating a layer into two parts (e.g. thin/wide metal separation) and an error +between touching regions is not desired. +

+The "without_touching_edges" option is a stronger version of "without_touching_corners" and +makes sense only for two-layer checks. +

+The following images illustrate the effect of the "without_touching_edges" option: +

+ + + + + +
+

+

Opposite and rectangle error filtering

The options for the separation check are those available for the width or space method plus opposite and rectangle error filtering. @@ -3440,10 +3481,15 @@ each other is more or equal than min and less than max but is more intuitive, as "projecting" is written with a condition, like "projecting < 2.um". Available operators are: "==", "<", "<=", ">" and ">=". Double-bounded ranges are also available, like: "0.5 <= projecting < 2.0". -

  • transparent : performs the check without shielding (polygon layers only)
  • -
  • shielded : performs the check with shielding (polygon layers only)
  • +
  • without_touching_corners : With this option present, touching corners (aka "kissing +corners") will not yield errors. The default is to produce errors in these cases.
  • +
  • without_touching_edges : With this option present, coincident edges will not yield errors. +This is a stronger version of "without_touching_corners" and makes sense only for two-layer checks +or raw-mode input layers. It is listed here for completeness.
  • +
  • transparent : Performs the check without shielding (polygon layers only)
  • +
  • shielded : Performs the check with shielding (polygon layers only)
  • props_eq , props_ne , props_copy : (only props_copy applies to width check) - -see "Properties constraints" below.
  • +See "Properties constraints" below.

    Note that without the angle_limit, acute corners will always be reported, since two @@ -3544,6 +3590,22 @@ shape's properties to the output too: space_not_connected = metal1_nets.space(0.4.um, props_ne + props_copy) space_connected = metal1_nets.space(0.4.um, props_eq + props_copy) +

    +

    Touching shapes

    +

    +The "without_touching_corners" option will turn off errors that arise due to +the "kissing corner" configuration (or "checkerboard pattern"). Formally +this is a width violation across the diagonal, but when considering this +configuration as disconnected boxes, no error should be reported. +

    +The following images illustrate the effect of the "without_touching_corners" option: +

    + + + + + +

    "with_angle" - Selects edges by their angle

    diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index 8fd41f6f5..f03149944 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml index 74d119bcb..a9ab3eaab 100644 --- a/src/doc/doc/about/drc_ref_source.xml +++ b/src/doc/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml index 58168f012..4a38dd6db 100644 --- a/src/doc/doc/about/lvs_ref.xml +++ b/src/doc/doc/about/lvs_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index c1d2ad914..c262d6815 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index 9feef786f..f5f851203 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/images/drc_separation12.png b/src/doc/doc/images/drc_separation12.png new file mode 100644 index 000000000..70847f791 Binary files /dev/null and b/src/doc/doc/images/drc_separation12.png differ diff --git a/src/doc/doc/images/drc_separation13.png b/src/doc/doc/images/drc_separation13.png new file mode 100644 index 000000000..00de3a91c Binary files /dev/null and b/src/doc/doc/images/drc_separation13.png differ diff --git a/src/doc/doc/images/drc_separation14.png b/src/doc/doc/images/drc_separation14.png new file mode 100644 index 000000000..e9a4cca31 Binary files /dev/null and b/src/doc/doc/images/drc_separation14.png differ diff --git a/src/doc/doc/images/drc_width5.png b/src/doc/doc/images/drc_width5.png new file mode 100644 index 000000000..9bb781814 Binary files /dev/null and b/src/doc/doc/images/drc_width5.png differ diff --git a/src/doc/doc/images/drc_width6.png b/src/doc/doc/images/drc_width6.png new file mode 100644 index 000000000..50c558bfe Binary files /dev/null and b/src/doc/doc/images/drc_width6.png differ diff --git a/src/doc/docDRCLVSResources.qrc b/src/doc/docDRCLVSResources.qrc index acf5f25a2..905f12d19 100644 --- a/src/doc/docDRCLVSResources.qrc +++ b/src/doc/docDRCLVSResources.qrc @@ -4,6 +4,8 @@ doc/images/drc_width2.png doc/images/drc_width3.png doc/images/drc_width4.png + doc/images/drc_width5.png + doc/images/drc_width6.png doc/images/drc_width1u.png doc/images/drc_width2u.png doc/images/drc_width3u.png @@ -29,6 +31,9 @@ doc/images/drc_separation9.png doc/images/drc_separation10.png doc/images/drc_separation11.png + doc/images/drc_separation12.png + doc/images/drc_separation13.png + doc/images/drc_separation14.png doc/images/drc_raw1.png doc/images/drc_raw2.png doc/images/drc_raw3.png diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index 8eebae6fb..8bf358bf0 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -555,7 +555,7 @@ module DRC # whose area is larger than 0.5 square micrometers: # # @code - # out = in.drc(if_all(area > 0.5, rectangle)) + # out = in.drc(if_all(area > 0.5, rectangles)) # @/code # # The condition expressions may be of any type (edges, edge pairs and polygons). @@ -1485,6 +1485,7 @@ CODE shielded = nil opposite_filter = RBA::Region::NoOppositeFilter rect_filter = RBA::Region::NoRectFilter + zd_mode = RBA::Region::IncludeZeroDistanceWhenTouching n = 1 args.each do |a| @@ -1492,6 +1493,10 @@ CODE metrics = a.value elsif a.is_a?(DRCWholeEdges) whole_edges = a.value + elsif a.is_a?(DRCPropertiesConstraint) + prop_constraint = a.value + elsif a.is_a?(DRCZeroDistanceMode) + zd_mode = a.value elsif a.is_a?(DRCOppositeErrorFilter) opposite_filter = a.value elsif a.is_a?(DRCRectangleErrorFilter) @@ -1523,6 +1528,8 @@ CODE elsif rect_filter != RBA::Region::NoRectFilter raise("A rectangle error filter cannot be used with this check") end + + args << zd_mode if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated other && raise("No other layer must be specified for a single-layer check") diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index e7289f3f6..e5f2ffa26 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -362,6 +362,18 @@ module DRC end end + def without_touching_corners(f = true) + self._context("without_touching_corners") do + DRCZeroDistanceMode::new(f ? RBA::Region::IncludeZeroDistanceWhenOverlapping : RBA::Region::IncludeZeroDistanceWhenTouching) + end + end + + def without_touching_edges(f = true) + self._context("without_touching_edges") do + DRCZeroDistanceMode::new(f ? RBA::Region::NeverIncludeZeroDistance : RBA::Region::IncludeZeroDistanceWhenTouching) + end + end + def euclidian DRCMetrics::new(RBA::Region::Euclidian) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index fc722810a..5a1e06452 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -3737,10 +3737,15 @@ CODE # but is more intuitive, as "projecting" is written with a condition, like # "projecting < 2.um". Available operators are: "==", "<", "<=", ">" and ">=". # Double-bounded ranges are also available, like: "0.5 <= projecting < 2.0". @/li - # @li @b transparent @/b: performs the check without shielding (polygon layers only) @/li - # @li @b shielded @/b: performs the check with shielding (polygon layers only) @/li + # @li @b without_touching_corners @/b: With this option present, touching corners (aka "kissing + # corners") will not yield errors. The default is to produce errors in these cases. @/li + # @li @b without_touching_edges @/b: With this option present, coincident edges will not yield errors. + # This is a stronger version of "without_touching_corners" and makes sense only for two-layer checks + # or raw-mode input layers. It is listed here for completeness. @/li + # @li @b transparent @/b: Performs the check without shielding (polygon layers only) @/li + # @li @b shielded @/b: Performs the check with shielding (polygon layers only) @/li # @li @b props_eq @/b, @b props_ne @/b, @b props_copy @/b: (only props_copy applies to width check) - - # see "Properties constraints" below. @/li + # See "Properties constraints" below. @/li # @/ul # # Note that without the angle_limit, acute corners will always be reported, since two @@ -3842,6 +3847,21 @@ CODE # space_connected = metal1_nets.space(0.4.um, props_eq + props_copy) # @/code # + # @h3 Touching shapes @/h3 + # + # The "without_touching_corners" option will turn off errors that arise due to + # the "kissing corner" configuration (or "checkerboard pattern"). Formally + # this is a width violation across the diagonal, but when considering this + # configuration as disconnected boxes, no error should be reported. + # + # The following images illustrate the effect of the "without_touching_corners" option: + # + # @table + # @tr + # @td @img(/images/drc_width5.png) @/td + # @td @img(/images/drc_width6.png) @/td + # @/tr + # @/table # %DRC% # @name space @@ -3962,7 +3982,45 @@ CODE # @/tr # @/table # - # @h3 opposite and rectangle error filtering @/h3 + # @h3 Touching shapes @/h3 + # + # Like \width and \space, the separation check also supports the "without_touching_corners" option. + # + # This option will turn off errors that arise due to + # edges touching in one corner (the "kissing corners" configuration). + # By default, such edges will yield an error, as they + # form a zero-distance situation. With this option in place, no errors will be reported. + # + # The following images illustrate the effect of the "without_touching_corners" option. + # The white line at the top of the bottom red shape is actually an edge pair indicating + # the zero-distance violation of the separation check: + # + # @table + # @tr + # @td @img(/images/drc_separation12.png) @/td + # @td @img(/images/drc_separation13.png) @/td + # @/tr + # @/table + # + # Another option is "without_touching_edges" which turns off errors that arise + # at coincident edges. Formally such edges represent a zero-distance situation, hence + # are flagged by default. Turning off the check in this case can be helpful when + # separating a layer into two parts (e.g. thin/wide metal separation) and an error + # between touching regions is not desired. + # + # The "without_touching_edges" option is a stronger version of "without_touching_corners" and + # makes sense only for two-layer checks. + # + # The following images illustrate the effect of the "without_touching_edges" option: + # + # @table + # @tr + # @td @img(/images/drc_separation12.png) @/td + # @td @img(/images/drc_separation14.png) @/td + # @/tr + # @/table + # + # @h3 Opposite and rectangle error filtering @/h3 # # The options for the separation check are those available for the \width or \space # method plus opposite and rectangle error filtering. @@ -4171,6 +4229,7 @@ CODE opposite_filter = RBA::Region::NoOppositeFilter rect_filter = RBA::Region::NoRectFilter prop_constraint = RBA::Region::IgnoreProperties + zd_mode = RBA::Region::IncludeZeroDistanceWhenTouching n = 1 args.each do |a| @@ -4182,6 +4241,8 @@ CODE negative = true elsif a.is_a?(DRCPropertiesConstraint) prop_constraint = a.value + elsif a.is_a?(DRCZeroDistanceMode) + zd_mode = a.value elsif a.is_a?(DRCOppositeErrorFilter) opposite_filter = a.value elsif a.is_a?(DRCRectangleErrorFilter) @@ -4235,6 +4296,8 @@ CODE raise("A rectangle error filter can only be used for polygon layers") end + args << zd_mode + border = (metrics == RBA::Region::Square ? value * 1.5 : value) if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 99823336b..1985cc67c 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -702,7 +702,8 @@ module DRC @l2n = RBA::LayoutToNetlist::new(@engine._dss) else layout = @engine.source.layout - @l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu) + cell_name = @engine.source.cell_name + @l2n = RBA::LayoutToNetlist::new(cell_name, layout.dbu) end @l2n.name = "DRC" diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index e0ad17d31..482f18db8 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -103,6 +103,16 @@ module DRC end end + # A wrapper for the "zero distance mode" for + # the DRC functions. The purpose of this class + # is to identify the value by the class. + class DRCZeroDistanceMode + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the "as_dots" or "as_boxes" flag for # some DRC functions. The purpose of this class # is to identify the value by the class. diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 690f2b644..1c8180a8d 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1617,6 +1617,62 @@ TEST(89_deep_with_mag_cop_size_aniso) run_test (_this, "89", true); } +TEST(90_zero_distance_mode) +{ + run_test (_this, "90", false); +} + +TEST(90d_zero_distance_mode) +{ + run_test (_this, "90", true); +} + +TEST(91_zero_distance_mode) +{ + run_test (_this, "91", false); +} + +TEST(91d_zero_distance_mode) +{ + run_test (_this, "91", true); +} + +TEST(92_issue1594_dual_top) +{ + std::string rs = tl::testdata (); + rs += "/drc/issue_1594.drc"; + + std::string input = tl::testdata (); + input += "/drc/issue_1594.gds"; + + std::string au = tl::testdata (); + au += "/drc/issue_1594_au.cir"; + + std::string output = this->tmp_file ("tmp.cir"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_force_gc = true\n" + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify + + CHECKPOINT (); + compare_netlists (_this, output, au); +} + TEST(100_edge_interaction_with_count) { run_test (_this, "100", false); diff --git a/src/edt/edt/EditorOptionsInst.ui b/src/edt/edt/EditorOptionsInst.ui index 86b3e2c56..5d5c8a5aa 100644 --- a/src/edt/edt/EditorOptionsInst.ui +++ b/src/edt/edt/EditorOptionsInst.ui @@ -548,6 +548,23 @@
    layWidgets.h
    + + scrollArea + lib_cbx + browse_pb + cell_le + place_origin_cb + scale_le + angle_le + mirror_cbx + array_grp + rows_le + columns_le + row_x_le + row_y_le + column_x_le + column_y_le + diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index a79b6a1d1..2e5488d2e 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -41,6 +41,8 @@ #include #include #include +#include +#include namespace edt { @@ -294,32 +296,32 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P mp_parameters_area = new QScrollArea (this); mp_parameters_area->setFrameShape (QFrame::NoFrame); + mp_parameters_area->setWidgetResizable (true); QGridLayout *frame_layout = dynamic_cast (QFrame::layout ()); frame_layout->addWidget (mp_parameters_area, 2, 0, 1, 1); frame_layout->setRowStretch (2, 1); - QFrame *fi = new QFrame (mp_parameters_area); - QWidget *inner_frame = fi; - fi->setFrameShape (QFrame::NoFrame); + mp_main_frame = new QFrame (mp_parameters_area); + mp_main_frame->setFrameShape (QFrame::NoFrame); setFrameShape (QFrame::NoFrame); - QGridLayout *inner_grid = new QGridLayout (inner_frame); - inner_frame->setLayout (inner_grid); + QGridLayout *main_grid = new QGridLayout (mp_main_frame); + mp_main_frame->setLayout (main_grid); if (m_dense) { - inner_grid->setContentsMargins (4, 4, 4, 4); - inner_grid->setHorizontalSpacing (6); - inner_grid->setVerticalSpacing (2); + main_grid->setContentsMargins (4, 4, 4, 4); + main_grid->setHorizontalSpacing (6); + main_grid->setVerticalSpacing (2); } - QWidget *main_frame = inner_frame; - QGridLayout *main_grid = inner_grid; - if (! mp_pcell_decl) { - mp_parameters_area->setWidget (main_frame); + mp_parameters_area->setWidget (mp_main_frame); update_current_parameters (); return; } + QWidget *inner_frame = mp_main_frame; + QGridLayout *inner_grid = main_grid; + int main_row = 0; int row = 0; std::string group_title; @@ -362,7 +364,8 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P if (! gt.empty ()) { // create a new group - QGroupBox *gb = new QGroupBox (main_frame); + QGroupBox *gb = new QGroupBox (mp_main_frame); + mp_groups.push_back (gb); gb->setTitle (tl::to_qstring (gt)); main_grid->addWidget (gb, main_row, 0, 1, 3); @@ -382,7 +385,7 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P // back to the main group inner_grid = main_grid; - inner_frame = main_frame; + inner_frame = mp_main_frame; row = main_row; } @@ -427,6 +430,9 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P hb->setContentsMargins (0, 0, 0, 0); f->setLayout (hb); f->setFrameShape (QFrame::NoFrame); + QSizePolicy sp = f->sizePolicy (); + sp.setHorizontalStretch (1); + f->setSizePolicy (sp); QLineEdit *le = new QLineEdit (f); hb->addWidget (le); @@ -434,9 +440,13 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P le->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (le); - QLabel *ul = new QLabel (f); - hb->addWidget (ul, 1); - ul->setText (tl::to_qstring (p->get_unit ())); + if (! p->get_unit ().empty ()) { + QLabel *ul = new QLabel (f); + hb->addWidget (ul, 1); + ul->setText (tl::to_qstring (p->get_unit ())); + } + + hb->addStretch (1); inner_grid->addWidget (f, row, 2); m_all_widgets.back ().push_back (f); @@ -450,7 +460,10 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P QPushButton *pb = new QPushButton (inner_frame); pb->setObjectName (tl::to_qstring (p->get_name ())); pb->setText (tl::to_qstring (description)); - pb->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred); + QSizePolicy sp = pb->sizePolicy (); + sp.setHorizontalPolicy (QSizePolicy::Fixed); + sp.setHorizontalStretch (1); + pb->setSizePolicy (sp); m_widgets.push_back (pb); inner_grid->addWidget (pb, row, 2); @@ -466,6 +479,9 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P { QLineEdit *le = new QLineEdit (inner_frame); le->setObjectName (tl::to_qstring (p->get_name ())); + QSizePolicy sp = le->sizePolicy (); + sp.setHorizontalStretch (1); + le->setSizePolicy (sp); m_widgets.push_back (le); inner_grid->addWidget (le, row, 2); m_all_widgets.back ().push_back (le); @@ -476,13 +492,29 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P case db::PCellParameterDeclaration::t_layer: { - lay::LayerSelectionComboBox *ly = new lay::LayerSelectionComboBox (inner_frame); + QFrame *f = new QFrame (inner_frame); + QHBoxLayout *hb = new QHBoxLayout (f); + hb->setContentsMargins (0, 0, 0, 0); + f->setLayout (hb); + f->setFrameShape (QFrame::NoFrame); + QSizePolicy sp = f->sizePolicy (); + sp.setHorizontalStretch (1); + f->setSizePolicy (sp); + + lay::LayerSelectionComboBox *ly = new lay::LayerSelectionComboBox (f); + hb->addWidget (ly); ly->set_no_layer_available (true); ly->set_view (mp_view, m_cv_index, true /*all layers*/); ly->setObjectName (tl::to_qstring (p->get_name ())); + sp = ly->sizePolicy (); + sp.setHorizontalPolicy (QSizePolicy::Fixed); + ly->setSizePolicy (sp); m_widgets.push_back (ly); - inner_grid->addWidget (ly, row, 2); - m_all_widgets.back ().push_back (ly); + + hb->addStretch (1); + + inner_grid->addWidget (f, row, 2); + m_all_widgets.back ().push_back (f); connect (ly, SIGNAL (activated (int)), this, SLOT (parameter_changed ())); } @@ -492,7 +524,9 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P { QCheckBox *cbx = new QCheckBox (inner_frame); // this makes the checkbox not stretch over the full width - better when navigating with tab - cbx->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred)); + QSizePolicy sp = cbx->sizePolicy (); + sp.setHorizontalStretch (1); + cbx->setSizePolicy (sp); cbx->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (cbx); inner_grid->addWidget (cbx, row, 2); @@ -509,8 +543,20 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P } else { - QComboBox *cb = new QComboBox (inner_frame); + QFrame *f = new QFrame (inner_frame); + QHBoxLayout *hb = new QHBoxLayout (f); + hb->setContentsMargins (0, 0, 0, 0); + f->setLayout (hb); + f->setFrameShape (QFrame::NoFrame); + QSizePolicy sp = f->sizePolicy (); + sp.setHorizontalStretch (1); + f->setSizePolicy (sp); + + QComboBox *cb = new QComboBox (f); + hb->addWidget (cb); cb->setObjectName (tl::to_qstring (p->get_name ())); + cb->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred); + cb->setSizeAdjustPolicy (QComboBox::AdjustToContents); int i = 0; for (std::vector::const_iterator c = p->get_choices ().begin (); c != p->get_choices ().end (); ++c, ++i) { @@ -523,21 +569,25 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P connect (cb, SIGNAL (activated (int)), this, SLOT (parameter_changed ())); - cb->setMinimumContentsLength (30); - cb->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon); m_widgets.push_back (cb); - inner_grid->addWidget (cb, row, 2); - m_all_widgets.back ().push_back (cb); + + hb->addStretch (1); + + inner_grid->addWidget (f, row, 2); + m_all_widgets.back ().push_back (f); } ++row; - if (inner_frame == main_frame) { + if (inner_frame == mp_main_frame) { ++main_row; } } + // adds some default buffer space + main_grid->setRowStretch (main_row, 1); + // initial callback try { @@ -556,8 +606,8 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P update_widgets_from_states (m_states); - mp_parameters_area->setWidget (main_frame); - main_frame->show (); + mp_parameters_area->setWidget (mp_main_frame); + mp_main_frame->show (); update_current_parameters (); } @@ -944,15 +994,15 @@ PCellParametersPage::update_widgets_from_states (const db::ParameterStates &stat break; case db::ParameterState::InfoIcon: m_icon_widgets [i]->setPixmap (info); - m_icon_widgets [i]->show (); + m_icon_widgets [i]->setVisible (ps.is_visible ()); break; case db::ParameterState::WarningIcon: m_icon_widgets [i]->setPixmap (warning); - m_icon_widgets [i]->show (); + m_icon_widgets [i]->setVisible (ps.is_visible ()); break; case db::ParameterState::ErrorIcon: m_icon_widgets [i]->setPixmap (error); - m_icon_widgets [i]->show (); + m_icon_widgets [i]->setVisible (ps.is_visible ()); break; } @@ -961,6 +1011,12 @@ PCellParametersPage::update_widgets_from_states (const db::ParameterStates &stat } set_parameters_internal (states, lazy_evaluation ()); + + // QGridLayouts are bad in handling nested QFrame (or QGroupBox) with their own layouts, + // so we help a little here: + for (auto g = mp_groups.begin (); g != mp_groups.end (); ++g) { + (*g)->resize (QSize ((*g)->width (), (*g)->sizeHint ().height ())); + } } void diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index e16179dff..e6e501399 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -29,10 +29,12 @@ #include "tlDeferredExecution.h" #include -#include -#include -#include -#include + +class QGroupBox; +class QCheckBox; +class QLabel; +class QToolButton; +class QScrollArea; namespace lay { @@ -150,6 +152,8 @@ private slots: private: lay::Dispatcher *mp_dispatcher; QScrollArea *mp_parameters_area; + QFrame *mp_main_frame; + std::vector mp_groups; QLabel *mp_error_label; QLabel *mp_error_icon; QLabel *mp_changed_label; diff --git a/src/lay/lay/TechBaseEditorPage.ui b/src/lay/lay/TechBaseEditorPage.ui index b2ab51375..668e04ffe 100644 --- a/src/lay/lay/TechBaseEditorPage.ui +++ b/src/lay/lay/TechBaseEditorPage.ui @@ -6,25 +6,163 @@ 0 0 - 604 - 498 + 625 + 587
    Form - + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + + + + Database +unit + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + Qt::Horizontal - - + + + + ... + + + + + + + QAbstractItemView::NoSelection + + + + + + + (Use the rename button to change this) + + + + + + + + + + The base path is used to locate auxiliary files if those are specified with a relative path. If none is specified, the default path is used. The default path is the one from which a technology was imported. + + + true + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + + + + + + + (Used for creating tech groups) + + + + + - Qt::Horizontal + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + Technology +specific +libraries + + + + + + + Grids @@ -42,24 +180,6 @@ - - - - Name - - - - - - - Database -unit - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - @@ -74,6 +194,53 @@ unit + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + µm (g1,g2,...) + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + Load layer properties file @@ -127,10 +294,7 @@ unit - - - - + Layer @@ -141,6 +305,13 @@ properties + + + + Name + + + @@ -148,75 +319,13 @@ properties - - + + - (Use the rename button to change this) + Group - - - - ... - - - - - - - Qt::Horizontal - - - - - - - QAbstractItemView::NoSelection - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - µm - - - - - - @@ -224,7 +333,7 @@ properties - + Qt::Vertical @@ -240,55 +349,10 @@ properties - - - - Qt::Vertical - - - - 20 - 40 - - - - - - + + - The base path is used to locate auxiliary files if those are specified with a relative path. If none is specified, the default path is used. The default path is the one from which a technology was imported. - - - true - - - - - - - - - - Technology -specific -libraries - - - - - - - - - - Group - - - - - - - (Used for creating tech groups) + These grids are available for selection from the "View" menu diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 1adfb3bb7..0eb2eb397 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -458,6 +458,12 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (&lay::LayoutHandle::file_watcher (), SIGNAL (fileChanged (const QString &)), this, SLOT (file_changed (const QString &))); connect (&lay::LayoutHandle::file_watcher (), SIGNAL (fileRemoved (const QString &)), this, SLOT (file_removed (const QString &))); + lay::TechnologyController *tc = lay::TechnologyController::instance (); + if (tc) { + connect (tc, SIGNAL (active_technology_changed ()), this, SLOT (technology_changed ())); + connect (tc, SIGNAL (technologies_edited ()), this, SLOT (technology_changed ())); + } + // make the main window accept drops setAcceptDrops (true); } @@ -555,6 +561,31 @@ MainWindow::init_menu () } } +static std::string tech_string_from_name (const std::string &tn) +{ + if (tn.empty ()) { + return tl::to_string (QObject::tr ("(Default)")); + } else { + return tn; + } +} + +void +MainWindow::technology_changed () +{ + lay::TechnologyController *tc = lay::TechnologyController::instance (); + if (tc) { + if (tc->active_technology ()) { + tech_message (tech_string_from_name (tc->active_technology ()->name ())); + } else { + tech_message (std::string ()); + } + } + + m_default_grids_updated = true; // potentially ... + dm_do_update_menu (); +} + void MainWindow::dock_widget_visibility_changed (bool visible) { @@ -908,36 +939,7 @@ MainWindow::config_finalize () // Update the default grids menu if necessary if (m_default_grids_updated) { - - m_default_grids_updated = false; - - std::vector group = menu ()->group ("default_grids_group"); - - for (std::vector::const_iterator t = group.begin (); t != group.end (); ++t) { - std::vector items = menu ()->items (*t); - for (std::vector::const_iterator i = items.begin (); i != items.end (); ++i) { - menu ()->delete_item (*i); - } - } - - int i = 1; - for (std::vector::const_iterator g = m_default_grids.begin (); g != m_default_grids.end (); ++g, ++i) { - - std::string name = "default_grid_" + tl::to_string (i); - - lay::Action *ga = new lay::ConfigureAction (tl::to_string (*g) + tl::to_string (QObject::tr (" um")), cfg_grid, tl::to_string (*g)); - ga->set_checkable (true); - ga->set_checked (fabs (*g - m_grid_micron) < 1e-10); - - for (std::vector::const_iterator t = group.begin (); t != group.end (); ++t) { - menu ()->insert_item (*t + ".end", name, ga); - } - - } - - // re-apply key bindings for the default grids - apply_key_bindings (); - + dm_do_update_menu (); } // make the changes visible in the setup form if the form is visible @@ -1489,7 +1491,11 @@ MainWindow::closeEvent (QCloseEvent *event) END_PROTECTED } - event->ignore (); + if (! m_exited) { + event->ignore (); + } else { + event->accept (); + } } void @@ -4038,6 +4044,57 @@ MainWindow::menu_changed () void MainWindow::do_update_menu () { + if (m_default_grids_updated) { + + m_default_grids_updated = false; + + const std::vector *grids = &m_default_grids; + std::vector tech_grids; + lay::TechnologyController *tc = lay::TechnologyController::instance (); + if (tc && tc->active_technology ()) { + tech_grids = tc->active_technology ()->default_grid_list (); + if (! tech_grids.empty ()) { + grids = &tech_grids; + } + } + + std::vector group = menu ()->group ("default_grids_group"); + + for (std::vector::const_iterator t = group.begin (); t != group.end (); ++t) { + std::vector items = menu ()->items (*t); + for (std::vector::const_iterator i = items.begin (); i != items.end (); ++i) { + menu ()->delete_item (*i); + } + } + + int i = 1; + for (std::vector::const_iterator g = grids->begin (); g != grids->end (); ++g, ++i) { + + std::string name = "default_grid_" + tl::to_string (i); + + std::string gs; + if (*g < 0.4) { + // pick nm units below 400nm + gs = tl::to_string (*g * 1000.0) + tl::to_string (QObject::tr (" nm")); + } else { + gs = tl::to_string (*g) + tl::to_string (QObject::tr (" um")); + } + + lay::Action *ga = new lay::ConfigureAction (gs, cfg_grid, tl::to_string (*g)); + ga->set_checkable (true); + ga->set_checked (fabs (*g - m_grid_micron) < 1e-10); + + for (std::vector::const_iterator t = group.begin (); t != group.end (); ++t) { + menu ()->insert_item (*t + ".end", name, ga); + } + + } + + // re-apply key bindings for the default grids + apply_key_bindings (); + + } + menu ()->build (menuBar (), mp_tool_bar); lay::GuiApplication *app = dynamic_cast (qApp); if (app) { diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index a0e9e6d62..326bc3c07 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -697,6 +697,7 @@ protected slots: void message_timer (); void edits_enabled_changed (); void menu_needs_update (); + void technology_changed (); void file_changed_timer (); void file_changed (const QString &path); diff --git a/src/lay/lay/laySaltController.cc b/src/lay/lay/laySaltController.cc index 7862bf4cc..4e5e062a2 100644 --- a/src/lay/lay/laySaltController.cc +++ b/src/lay/lay/laySaltController.cc @@ -179,6 +179,10 @@ SaltController::install_packages (const std::vector &packages, bool { lay::SaltDownloadManager manager; + // This method is used for command-line installation ignoring the package index. + // Hence we have to download package information here: + manager.set_always_download_package_information (true); + lay::Salt salt_mine; if (! m_salt_mine_url.empty ()) { tl::log << tl::to_string (tr ("Downloading package repository from %1").arg (tl::to_qstring (m_salt_mine_url))); diff --git a/src/lay/lay/laySaltDownloadManager.cc b/src/lay/lay/laySaltDownloadManager.cc index 753781d57..ef300f8a4 100644 --- a/src/lay/lay/laySaltDownloadManager.cc +++ b/src/lay/lay/laySaltDownloadManager.cc @@ -166,7 +166,7 @@ ConfirmationDialog::finish () SaltDownloadManager::SaltDownloadManager () { - // .. nothing yet .. + m_always_download_package_information = false; } void @@ -344,7 +344,7 @@ SaltDownloadManager::fetch_missing (const lay::Salt &salt, const lay::Salt &salt } - if (! p->downloaded && salt_mine.download_package_information ()) { + if (! p->downloaded && (m_always_download_package_information || salt_mine.download_package_information ())) { // If requested, download package information to complete information from index or dependencies if (tl::verbosity() >= 10) { @@ -362,7 +362,7 @@ SaltDownloadManager::fetch_missing (const lay::Salt &salt, const lay::Salt &salt if (! p->downloaded) { if (p->name.empty ()) { - throw tl::Exception (tl::to_string (tr ("No name given package from '%s' (from dependencies or command line installation request)")), p->url); + throw tl::Exception (tl::to_string (tr ("No name given for package from '%s' (from dependencies or command line installation request)")), p->url); } if (tl::verbosity() >= 10) { diff --git a/src/lay/lay/laySaltDownloadManager.h b/src/lay/lay/laySaltDownloadManager.h index 477555ada..ffe408a04 100644 --- a/src/lay/lay/laySaltDownloadManager.h +++ b/src/lay/lay/laySaltDownloadManager.h @@ -103,6 +103,22 @@ public: */ SaltDownloadManager (); + /** + * @brief Gets a flag indicating whether to always download package information + */ + bool always_download_package_information () const + { + return m_always_download_package_information; + } + + /** + * @brief Sets a flag indicating whether to always download package information + */ + void set_always_download_package_information (bool f) + { + m_always_download_package_information = f; + } + /** * @brief Registers an URL (with version) for download in the given target directory * @@ -176,6 +192,7 @@ private: }; std::vector m_registry; + bool m_always_download_package_information; bool needs_iteration (); void fetch_missing (const lay::Salt &salt, const lay::Salt &salt_mine, tl::AbsoluteProgress &progress); diff --git a/src/lay/lay/laySaltGrain.cc b/src/lay/lay/laySaltGrain.cc index 6139574b3..f9c3cb181 100644 --- a/src/lay/lay/laySaltGrain.cc +++ b/src/lay/lay/laySaltGrain.cc @@ -487,7 +487,7 @@ SaltGrain::load (tl::InputStream &p) void SaltGrain::save () const { - save (tl::to_string (QDir (tl::to_qstring (path ())).filePath (tl::to_qstring (grain_filename)))); + save (tl::to_string (QDir (tl::to_qstring (path ())).filePath (tl::to_qstring (SaltGrain::spec_file ())))); } void @@ -503,7 +503,7 @@ SaltGrain::from_path (const std::string &path) QDir dir (tl::to_qstring (path)); SaltGrain g; - g.load (tl::to_string (dir.filePath (tl::to_qstring (grain_filename)))); + g.load (tl::to_string (dir.filePath (tl::to_qstring (SaltGrain::spec_file ())))); g.set_path (tl::to_string (dir.absolutePath ())); return g; } @@ -552,7 +552,7 @@ SaltGrain::stream_from_url (std::string &generic_url, double timeout, tl::InputH } else { - return new tl::InputStream (url); + return new tl::InputStream (url + "/" + SaltGrain::spec_file ()); } } @@ -576,10 +576,10 @@ SaltGrain::is_grain (const std::string &path) if (path[0] != ':') { QDir dir (tl::to_qstring (path)); - QString gf = dir.filePath (tl::to_qstring (grain_filename)); + QString gf = dir.filePath (tl::to_qstring (SaltGrain::spec_file ())); return QFileInfo (gf).exists (); } else { - return QResource (tl::to_qstring (path + "/" + grain_filename)).isValid (); + return QResource (tl::to_qstring (path + "/" + SaltGrain::spec_file ())).isValid (); } } diff --git a/src/lay/lay/layTechSetupDialog.cc b/src/lay/lay/layTechSetupDialog.cc index 4bcaa2ec3..b690ce8c1 100644 --- a/src/lay/lay/layTechSetupDialog.cc +++ b/src/lay/lay/layTechSetupDialog.cc @@ -114,6 +114,7 @@ TechBaseEditorPage::setup () mp_ui->desc_le->setText (tl::to_qstring (tech ()->description ())); mp_ui->group_le->setText (tl::to_qstring (tech ()->group ())); mp_ui->dbu_le->setText (tl::to_qstring (tl::to_string (tech ()->dbu ()))); + mp_ui->grids_le->setText (tl::to_qstring (tl::to_string (tech ()->default_grids ()))); mp_ui->desc_le->setEnabled (! tech ()->name ().empty ()); mp_ui->base_path_le->setText (tl::to_qstring (tech ()->explicit_base_path ())); #if QT_VERSION >= 0x040700 @@ -164,6 +165,7 @@ TechBaseEditorPage::commit () tech ()->set_description (tl::to_string (mp_ui->desc_le->text ())); tech ()->set_group (tl::to_string (mp_ui->group_le->text ())); tech ()->set_explicit_base_path (tl::to_string (mp_ui->base_path_le->text ())); + tech ()->set_default_grids (tl::to_string (mp_ui->grids_le->text ())); double d = 0.001; tl::from_string_ext (tl::to_string (mp_ui->dbu_le->text ()), d); diff --git a/src/lay/lay/layTechnologyController.cc b/src/lay/lay/layTechnologyController.cc index 169afa383..8e493de2d 100644 --- a/src/lay/lay/layTechnologyController.cc +++ b/src/lay/lay/layTechnologyController.cc @@ -40,7 +40,7 @@ namespace lay static const std::string cfg_tech_editor_window_state ("tech-editor-window-state"); -std::string tech_string_from_name (const std::string &tn) +static std::string tech_string_from_name (const std::string &tn) { if (tn.empty ()) { return tl::to_string (QObject::tr ("(Default)")); @@ -179,21 +179,12 @@ TechnologyController::update_active_technology () } - mp_active_technology = active_tech; - - if (mp_mw) { - if (active_tech) { - mp_mw->tech_message (tech_string_from_name (active_tech->name ())); - } else { - mp_mw->tech_message (std::string ()); - } - } - if (mp_active_technology != active_tech) { + mp_active_technology = active_tech; emit active_technology_changed (); } -#if 0 +#if 0 // Hint with this implementation, the current technology follows the current layout. // Although that's a nice way to display the current technology, it's pretty confusing lay::Dispatcher *pr = mp_plugin_root; diff --git a/src/laybasic/laybasic/layMouseTracker.cc b/src/laybasic/laybasic/layMouseTracker.cc index 4de45d0f3..3c531e29b 100644 --- a/src/laybasic/laybasic/layMouseTracker.cc +++ b/src/laybasic/laybasic/layMouseTracker.cc @@ -105,15 +105,22 @@ MouseTracker::mouse_move_event (const db::DPoint &p, unsigned int /*buttons*/, b double max_coord = 1e30; // big enough I guess - mp_markers.push_back (new lay::DMarker (mp_view)); - mp_markers.back ()->set_line_style (m_cursor_line_style); - mp_markers.back ()->set_color (m_cursor_color); - mp_markers.back ()->set (db::DEdge (db::DPoint (tp.x (), -max_coord), db::DPoint (tp.x (), max_coord))); + for (int i = 0; i < 2; ++i) { - mp_markers.push_back (new lay::DMarker (mp_view)); - mp_markers.back ()->set_line_style (m_cursor_line_style); - mp_markers.back ()->set_color (m_cursor_color); - mp_markers.back ()->set (db::DEdge (db::DPoint (-max_coord, tp.y ()), db::DPoint (max_coord, tp.y ()))); + mp_markers.push_back (new lay::DMarker (mp_view)); + mp_markers.back ()->set_line_style (m_cursor_line_style); + mp_markers.back ()->set_line_width (1); + mp_markers.back ()->set_halo (false); + mp_markers.back ()->set_dither_pattern (1); + mp_markers.back ()->set_color (m_cursor_color.is_valid () ? m_cursor_color : mp_view->canvas ()->foreground_color ()); + + if (i == 0) { + mp_markers.back ()->set (db::DEdge (db::DPoint (tp.x (), -max_coord), db::DPoint (tp.x (), max_coord))); + } else { + mp_markers.back ()->set (db::DEdge (db::DPoint (-max_coord, tp.y ()), db::DPoint (max_coord, tp.y ()))); + } + + } } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index ab6c4d6d6..4bc22e7d0 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -305,10 +305,7 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_ if (mp_editables->begin_move (p, ac_from_buttons (buttons))) { - lay::SelectionService *selector = mp_view->selection_service (); - if (selector) { - selector->hover_reset (); - } + ui ()->hover_reset (); mp_view->clear_transient_selection (); diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h index a47789158..6ad7600a9 100644 --- a/src/laybasic/laybasic/laySelector.h +++ b/src/laybasic/laybasic/laySelector.h @@ -69,14 +69,7 @@ public: virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual bool wheel_event (int delta, bool horizontal, const db::DPoint &p, unsigned int buttons, bool prio); - - /** - * @brief Reset the hover timer for the transient selection - * - * This method may be used by other services (in particular Move) to avoid the transient to - * be triggered from a move operation. - */ - void hover_reset (); + virtual void hover_reset (); #if defined (HAVE_QT) public slots: diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index 0927fd261..d794fe87f 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -1051,7 +1051,15 @@ ViewObjectUI::drag_cancel () } } -namespace +void +ViewObjectUI::hover_reset () +{ + for (service_iterator svc = begin_services (); svc != end_services (); ++svc) { + (*svc)->hover_reset (); + } +} + +namespace { struct z_order_compare_f { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 76f9b5175..8ba1bebb9 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -147,6 +147,17 @@ public: virtual bool drop_event (const db::DPoint & /*p*/, const DragDropDataBase * /*data*/) { return false; } #endif + /** + * @brief Hover reset request + * + * This event is issued for services providing some "hover" mode - i.e. capture + * mouse move events and start a timer on them. + * + * The implementation of this event should cancel this timer and + * not raise a hover condition. + */ + virtual void hover_reset () { } + /** * @brief Mouse press event handler * @@ -605,6 +616,11 @@ public: */ void drag_cancel (); + /** + * @brief Calls hover_reset on all services + */ + void hover_reset (); + /** * @brief CanvasPlane rendering * diff --git a/src/layui/layui/LayoutViewConfigPage2d.ui b/src/layui/layui/LayoutViewConfigPage2d.ui index ad779f091..f70f43881 100644 --- a/src/layui/layui/LayoutViewConfigPage2d.ui +++ b/src/layui/layui/LayoutViewConfigPage2d.ui @@ -55,9 +55,6 @@ - - The color in which the rulers are drawn - @@ -151,9 +148,6 @@ - - The color in which the rulers are drawn - diff --git a/src/layui/layui/layBrowser.cc b/src/layui/layui/layBrowser.cc index e47e56525..edfbf4a26 100644 --- a/src/layui/layui/layBrowser.cc +++ b/src/layui/layui/layBrowser.cc @@ -95,6 +95,16 @@ Browser::accept () } } +void +Browser::reject () +{ + if (active ()) { + m_active = false; + deactivated (); + QDialog::reject (); + } +} + } #endif diff --git a/src/layui/layui/layBrowser.h b/src/layui/layui/layBrowser.h index 03416ce20..30edb4199 100644 --- a/src/layui/layui/layBrowser.h +++ b/src/layui/layui/layBrowser.h @@ -138,6 +138,7 @@ private: void closeEvent (QCloseEvent *); void accept (); + void reject (); }; } diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc index cdc7a4e1b..792818c65 100644 --- a/src/layui/layui/layWidgets.cc +++ b/src/layui/layui/layWidgets.cc @@ -972,17 +972,31 @@ SimpleColorButton::set_color_internal (QColor c) m_color = c; QFontMetrics fm (font (), this); - QRect rt (fm.boundingRect (QObject::tr ("Auto"))); // dummy text to be compliant with the other color button - QPixmap pxmp (rt.width () + 24, rt.height ()); + QRect rt (fm.boundingRect (QObject::tr ("XXXXXXX"))); + +#if QT_VERSION >= 0x050000 + double dpr = devicePixelRatio (); +#else + double dpr = 1.0; +#endif + + QPixmap pxmp (rt.width () * dpr, rt.height () * dpr); +#if QT_VERSION >= 0x050000 + pxmp.setDevicePixelRatio (dpr); +#endif QPainter pxpainter (&pxmp); QColor text_color = palette ().color (QPalette::Active, QPalette::Text); - pxpainter.setPen (QPen (text_color)); pxpainter.setBrush (QBrush (c.isValid () ? c : QColor (128, 128, 128))); - QRect r (0, 0, pxmp.width () - 1, pxmp.height () - 1); + QPen frame_pen (text_color); + frame_pen.setWidthF (1.0); + frame_pen.setJoinStyle (Qt::MiterJoin); + pxpainter.setPen (frame_pen); + int dpri = int (dpr); + QRectF r ((dpri / 2) / dpr, (dpri / 2) / dpr, rt.width () - 1.0, rt.height () - 1.0); pxpainter.drawRect (r); - setIconSize (pxmp.size ()); + setIconSize (QSize (rt.width (), rt.height ())); setIcon (QIcon (pxmp)); } @@ -1216,22 +1230,25 @@ ColorButton::set_color_internal (QColor c) #if QT_VERSION >= 0x50000 pixmap.setDevicePixelRatio (dpr); #endif - pixmap.fill (QColor (0, 0, 0, 0)); QColor text_color = palette ().color (QPalette::Active, QPalette::Text); QPainter pxpainter (&pixmap); - pxpainter.setPen (QPen (text_color)); + QPen frame_pen (text_color); + frame_pen.setWidthF (1.0); + frame_pen.setJoinStyle (Qt::MiterJoin); + pxpainter.setPen (frame_pen); + + int dpri = int (dpr); + QRectF r ((dpri / 2) / dpr, (dpri / 2) / dpr, rt.width () - 1.0, rt.height () - 1.0); if (! m_color.isValid ()) { pxpainter.setFont (font ()); - QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ()); pxpainter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, QObject::tr ("Auto")); } else { pxpainter.setBrush (QBrush (c)); - QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ()); pxpainter.drawRect (r); } diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index cde9e8f7f..d82d5d56d 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -28,7 +28,7 @@ #include "lymMacro.h" #include "tlFileUtils.h" -void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, const std::string &top = std::string (), bool change_case = false) +void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, bool with_lvs = true, const std::string &top = std::string (), bool change_case = false) { std::string rs = tl::testdata (); rs += "/lvs/" + suffix + ".lvs"; @@ -70,7 +70,9 @@ void run_test (tl::TestBase *_this, const std::string &suffix, const std::string lvs.load_from (rs); EXPECT_EQ (lvs.run (), 0); - _this->compare_text_files (output_lvsdb, au_lvsdb); + if (with_lvs) { + _this->compare_text_files (output_lvsdb, au_lvsdb); + } _this->compare_text_files (output_cir, au_cir); if (with_l2n) { _this->compare_text_files (output_l2n, au_l2n); @@ -121,14 +123,14 @@ TEST(6_simple_pin_swapping) { run_test (_this, "ringo_simple_pin_swapping", "ringo.gds"); // change case - run_test (_this, "ringo_simple_pin_swapping", "ringo.gds", false, std::string (), true); + run_test (_this, "ringo_simple_pin_swapping", "ringo.gds", false, true, std::string (), true); } TEST(7_net_and_circuit_equivalence) { run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds"); // change case - run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds", false, std::string (), true); + run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds", false, true, std::string (), true); } TEST(8_simplification) @@ -166,7 +168,7 @@ TEST(13_simple_ringo_device_subcircuits) { run_test (_this, "ringo_device_subcircuits", "ringo.gds"); // change case - run_test (_this, "ringo_device_subcircuits", "ringo.gds", false, std::string (), true); + run_test (_this, "ringo_device_subcircuits", "ringo.gds", false, true, std::string (), true); } TEST(14_simple_ringo_mixed_hierarchy) @@ -181,7 +183,7 @@ TEST(15_simple_dummy_device) TEST(16_floating) { - run_test (_this, "floating", "floating.gds", false, "TOP"); + run_test (_this, "floating", "floating.gds", false, true, "TOP"); } TEST(17_layout_variants) @@ -287,3 +289,9 @@ TEST(31_MustConnect2) run_test (_this, "must_connect2", "must_connect2.gds"); } +// issue 1609 +TEST(40_DeviceExtractorErrors) +{ + run_test (_this, "custom_resistors", "custom_resistors.gds", true, false /*no LVS*/); +} + diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index 40de5d859..616b561e2 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -165,7 +165,7 @@ TEST(20_private) TEST(21_private) { - run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21_3.lvsdb"); + run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21_4.lvsdb"); } // issue #1021 diff --git a/src/lym/unit_tests/lymBasicTests.cc b/src/lym/unit_tests/lymBasicTests.cc index 458421789..92d1bf49e 100644 --- a/src/lym/unit_tests/lymBasicTests.cc +++ b/src/lym/unit_tests/lymBasicTests.cc @@ -247,7 +247,7 @@ TEST(102_PythonInclude) throw; } - EXPECT_EQ (console.text (), "Stop 1: m2.py:8\nf: a_inc.py:5\nStop 2: m2.py:14\n"); + EXPECT_EQ (console.text (), "Stop 1: m2.py:9\nf: a_inc.py:5\nStop 2: m2.py:15\n"); } #endif diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc index ec409cbf2..a5c4b81ed 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc @@ -816,7 +816,9 @@ De Boor algorithm for NURBS static db::DPoint b_spline_point (double x, const std::vector > &control_points, int p, const std::vector &t, int &k) { - k = (int) (std::lower_bound (t.begin (), t.end (), x - 1e-6) - t.begin ()); + double eps = 1e-12 * (fabs (t.back ()) + fabs (t.front ())); + + k = (int) (std::lower_bound (t.begin (), t.end (), x - eps) - t.begin ()); --k; if (k < p) { k = p; @@ -1740,6 +1742,7 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector std::string layer; unsigned int xy_flag = 0; int degree = 1; + int flags = 0; while ((g = read_group_code ()) != 0) { if (g == 8) { @@ -1763,9 +1766,9 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector } else if (g == 70) { - int flags = read_int32 (); - if (flags != 8 && flags != 12) { - warn ("Invalid SPLINE flag (code 70): " + tl::to_string (flags) + ". Only types 8 (non-rational) and 12 (rational) are supported currently."); + flags = read_int32 (); + if ((flags & 2) != 0) { + warn ("Invalid SPLINE flag (code 70): " + tl::to_string (flags) + ". Periodic splines not supported currently."); } } else if (g == 71) { @@ -1814,6 +1817,13 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector } } + } else if ((flags & 1) && m_polyline_mode == 2) { + + // create a polygon for the spline + db::DSimplePolygon p; + p.assign_hull (new_points.begin (), new_points.end (), tt); + cell.shapes (ll.second).insert (safe_from_double (p)); + } else { // create a path with width 0 for the spline diff --git a/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc b/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc index d38d5ed20..87ba522e7 100644 --- a/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc +++ b/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc @@ -536,3 +536,20 @@ TEST(35c) db::DXFReaderOptions opt; run_test_public (_this, "issue_1422c.dxf", "issue_1422c_au.gds.gz", opt); } + +// issue #1592, polyline mode 2 +TEST(36a) +{ + db::DXFReaderOptions opt; + opt.dbu = 1e-5; + opt.polyline_mode = 2; + run_test_public (_this, "issue_1592.dxf.gz", "issue_1592a_au.oas.gz", opt, true); +} + +// issue #1592 +TEST(36b) +{ + db::DXFReaderOptions opt; + opt.dbu = 1e-5; + run_test_public (_this, "issue_1592.dxf.gz", "issue_1592b_au.oas.gz", opt, true); +} diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc similarity index 98% rename from src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc rename to src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc index 4e3774736..c503bab0a 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc @@ -538,7 +538,7 @@ TEST(4_CollectModeRename) } std::string fn_au (tl::testdata () + "/gds/collect_rename_au.gds"); - db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); + db::compare_layouts (_this, layout, fn_au, db::NormalizationMode (db::WriteGDS2 | db::WithMeta), 1); } TEST(4_CollectModeRenameWithGhost) @@ -634,7 +634,7 @@ TEST(4_CollectModeOverwrite) } std::string fn_au (tl::testdata () + "/gds/collect_overwrite_au.gds"); - db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); + db::compare_layouts (_this, layout, fn_au, db::NormalizationMode (db::WriteGDS2 | db::WithMeta), 1); } TEST(4_CollectModeSkip) @@ -658,7 +658,7 @@ TEST(4_CollectModeSkip) } std::string fn_au (tl::testdata () + "/gds/collect_skip_au.gds"); - db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); + db::compare_layouts (_this, layout, fn_au, db::NormalizationMode (db::WriteGDS2 | db::WithMeta), 1); } TEST(4_CollectModeAdd) @@ -682,7 +682,7 @@ TEST(4_CollectModeAdd) } std::string fn_au (tl::testdata () + "/gds/collect_add_au.gds"); - db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); + db::compare_layouts (_this, layout, fn_au, db::NormalizationMode (db::WriteGDS2 | db::WithMeta), 1); } // border case with multiple padding 0 for SNAME and STRING records diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc similarity index 100% rename from src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc rename to src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc diff --git a/src/plugins/streamers/gds2/unit_tests/unit_tests.pro b/src/plugins/streamers/gds2/unit_tests/unit_tests.pro index cc5d39a61..396a6273e 100644 --- a/src/plugins/streamers/gds2/unit_tests/unit_tests.pro +++ b/src/plugins/streamers/gds2/unit_tests/unit_tests.pro @@ -6,8 +6,8 @@ TARGET = gds2_tests include($$PWD/../../../../lib_ut.pri) SOURCES = \ - dbGDS2Reader.cc \ - dbGDS2Writer.cc \ + dbGDS2ReaderTests.cc \ + dbGDS2WriterTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index fc9df3080..5f0394057 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1098,7 +1098,7 @@ LEFDEFReaderState::read_single_map_file (const std::string &path, std::map= 0x030A0000 @@ -123,6 +124,8 @@ public: #if PY_VERSION_HEX >= 0x030A0000 frame = PyFrame_GetBack(frame); + // PyFrame_GetBack returns a strong reference, hence we need to make sure it is released + frame_object_ref = (PyObject *) frame; #else frame = frame->f_back; #endif @@ -182,7 +185,7 @@ PythonInterpreter::PythonInterpreter (bool embedded) : 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) + mp_current_frame (NULL), m_embedded (embedded) { // Don't attempt any additional initialization in the standalone module case if (! embedded) { @@ -222,6 +225,8 @@ PythonInterpreter::PythonInterpreter (bool embedded) // If set, use $KLAYOUT_PYTHONHOME to initialize the path. // Otherwise there may be some conflict between external installations and KLayout. + bool has_klayout_pythonhome = false; + // Python is not easily convinced to use an external path properly. // So we simply redirect PYTHONHOME std::string pythonhome_name ("PYTHONHOME"); @@ -230,6 +235,7 @@ PythonInterpreter::PythonInterpreter (bool embedded) tl::unset_env (pythonhome_name); } if (tl::has_env (klayout_pythonhome_name)) { + has_klayout_pythonhome = true; tl::set_env (pythonhome_name, tl::get_env (klayout_pythonhome_name)); } @@ -237,6 +243,23 @@ PythonInterpreter::PythonInterpreter (bool embedded) tl_assert (sizeof (wchar_t) == 2); + std::string inst_dir; + + wchar_t buffer[MAX_PATH]; + int len; + + if ((len = GetModuleFileNameW (NULL, buffer, MAX_PATH)) > 0) { + inst_dir = tl::absolute_path (tl::to_string (std::wstring (buffer, len))); + } + + if (! has_klayout_pythonhome) { + + // Use our own installation path for PYTHONHOME unless given + // (Our Windows installation comes with its own copy of the libraries) + tl::set_env (pythonhome_name, inst_dir); + + } + if (! has_klayout_pythonpath) { // If present, read the paths from a file in INST_PATH/.python-paths.txt. @@ -247,39 +270,32 @@ PythonInterpreter::PythonInterpreter (bool embedded) std::string path; - wchar_t buffer[MAX_PATH]; - int len; - if ((len = GetModuleFileNameW (NULL, buffer, MAX_PATH)) > 0) { + std::string path_file = tl::combine_path (inst_dir, ".python-paths.txt"); + if (tl::file_exists (path_file)) { - std::string inst_dir = tl::absolute_path (tl::to_string (std::wstring (buffer, len))); - std::string path_file = tl::combine_path (inst_dir, ".python-paths.txt"); - if (tl::file_exists (path_file)) { + tl::log << tl::to_string (tr ("Reading Python path from ")) << path_file; - tl::log << tl::to_string (tr ("Reading Python path from ")) << path_file; + tl::InputStream path_file_stream (path_file); + std::string path_file_text = path_file_stream.read_all (); - tl::InputStream path_file_stream (path_file); - std::string path_file_text = path_file_stream.read_all (); + tl::Eval eval; + eval.set_global_var ("inst_path", tl::Variant (inst_dir)); + tl::Expression ex; + eval.parse (ex, path_file_text.c_str ()); + tl::Variant v = ex.execute (); - tl::Eval eval; - eval.set_global_var ("inst_path", tl::Variant (inst_dir)); - tl::Expression ex; - eval.parse (ex, path_file_text.c_str ()); - tl::Variant v = ex.execute (); - - if (v.is_list ()) { - for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) { - if (! path.empty ()) { - path += ";"; - } - path += i->to_string (); + if (v.is_list ()) { + for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) { + if (! path.empty ()) { + path += ";"; } + path += i->to_string (); } - } - } + Py_SetPath (tl::to_wstring (path).c_str ()); - Py_SetPath (tl::to_wstring (path).c_str ()); + } } catch (tl::Exception &ex) { tl::error << tl::to_string (tr ("Evaluation of Python path expression failed")) << ": " << ex.msg (); @@ -309,18 +325,15 @@ PythonInterpreter::PythonInterpreter (bool embedded) #else // Python 3 requires a unicode string for the application name - PyObject *an = c2python (app_path); - tl_assert (an != NULL); - mp_py3_app_name = PyUnicode_AsWideCharString (an, NULL); - tl_assert (mp_py3_app_name != NULL); - Py_DECREF (an); - Py_SetProgramName (mp_py3_app_name); + + mp_py3_app_name = tl::to_wstring (app_path); + Py_SetProgramName (const_cast (mp_py3_app_name.c_str ())); Py_InitializeEx (0 /*don't set signals*/); // Set dummy argv[] // TODO: more? - wchar_t *argv[1] = { mp_py3_app_name }; + wchar_t *argv[1] = { const_cast (mp_py3_app_name.c_str()) }; PySys_SetArgvEx (1, argv, 0); #endif @@ -355,14 +368,7 @@ PythonInterpreter::~PythonInterpreter () sp_interpreter = 0; if (m_embedded) { - Py_Finalize (); - - if (mp_py3_app_name) { - PyMem_Free (mp_py3_app_name); - mp_py3_app_name = 0; - } - } } diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index 217c87230..ee8d35381 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -277,7 +277,7 @@ private: std::string m_debugger_scope; PyFrameObject *mp_current_frame; std::map m_file_id_map; - wchar_t *mp_py3_app_name; + std::wstring mp_py3_app_name; bool m_embedded; std::unique_ptr m_pya_module; }; diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index e4c271d37..8cd688a4f 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -1126,6 +1126,14 @@ class Cell: This method has been added in version 0.23. """ + def copy_meta_info(self, other: Cell) -> None: + r""" + @brief Copies the meta information from the other cell into this cell + See \LayoutMetaInfo for details about cells and meta information. + The meta information from this cell will be replaced by the meta information from the other cell. + + This method has been introduced in version 0.28.16. + """ @overload def copy_shapes(self, source_cell: Cell) -> None: r""" @@ -1771,6 +1779,15 @@ class Cell: This method has been introduced in version 0.22. """ + def merge_meta_info(self, other: Cell) -> None: + r""" + @brief Merges the meta information from the other cell into this cell + See \LayoutMetaInfo for details about cells and meta information. + Existing keys in this cell will be overwritten by the respective values from the other cell. + New keys will be added. + + This method has been introduced in version 0.28.16. + """ def meta_info(self, name: str) -> LayoutMetaInfo: r""" @brief Gets the meta information for a given name @@ -3885,9 +3902,7 @@ class CompoundRegionOperationNode: The search distance for intruder shapes is determined by the operation and computed from the operation's requirements. - NOTE: this feature is experimental and not deployed into the the DRC framework yet. - - This class has been introduced in version 0.27. + This class has been introduced in version 0.27. The API is considered internal and will change without notice. """ class GeometricalOp: r""" @@ -3926,12 +3941,12 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -4140,12 +4155,12 @@ class CompoundRegionOperationNode: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -4320,12 +4335,12 @@ class CompoundRegionOperationNode: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -4460,11 +4475,11 @@ class CompoundRegionOperationNode: @brief Creates a node delivering an empty result of the given type """ @classmethod - def new_enclosed_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_enclosed_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing an enclosed (secondary enclosing primary) check. - This method has been added in version 0.27.5. + This method has been added in version 0.27.5. The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_enclosing(cls, a: CompoundRegionOperationNode, b: CompoundRegionOperationNode, inverse: Optional[bool] = ..., min_count: Optional[int] = ..., max_count: Optional[int] = ...) -> CompoundRegionOperationNode: @@ -4472,9 +4487,11 @@ class CompoundRegionOperationNode: @brief Creates a node representing an inside selection operation between the inputs. """ @classmethod - def new_enclosing_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_enclosing_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing an inside (enclosure) check. + + The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_end_segments(cls, input: CompoundRegionOperationNode, length: int, fraction: float) -> CompoundRegionOperationNode: @@ -4539,9 +4556,11 @@ class CompoundRegionOperationNode: @brief Creates a node representing an interacting selection operation between the inputs. """ @classmethod - def new_isolated_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_isolated_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing a isolated polygons (space between different polygons) check. + + The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_join(cls, inputs: Sequence[CompoundRegionOperationNode]) -> CompoundRegionOperationNode: @@ -4609,9 +4628,11 @@ class CompoundRegionOperationNode: @brief Creates a node providing a Minkowski sum with a point sequence forming a contour. """ @classmethod - def new_notch_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_notch_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing a intra-polygon space check. + + The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_outside(cls, a: CompoundRegionOperationNode, b: CompoundRegionOperationNode, inverse: Optional[bool] = ...) -> CompoundRegionOperationNode: @@ -4619,9 +4640,11 @@ class CompoundRegionOperationNode: @brief Creates a node representing an outside selection operation between the inputs. """ @classmethod - def new_overlap_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_overlap_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing an overlap check. + + The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_overlapping(cls, a: CompoundRegionOperationNode, b: CompoundRegionOperationNode, inverse: Optional[bool] = ..., min_count: Optional[int] = ..., max_count: Optional[int] = ...) -> CompoundRegionOperationNode: @@ -4695,9 +4718,11 @@ class CompoundRegionOperationNode: @brief Creates a node object representing the secondary input from the given region """ @classmethod - def new_separation_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_separation_check(cls, other: CompoundRegionOperationNode, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing a separation check. + + The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_sized(cls, input: CompoundRegionOperationNode, dx: int, dy: int, mode: int) -> CompoundRegionOperationNode: @@ -4712,9 +4737,11 @@ class CompoundRegionOperationNode: @param keep_hv If true, horizontal and vertical edges are maintained. """ @classmethod - def new_space_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_space_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing a space check. + + The zero_distance_mode argument has been inserted in version 0.29. """ @classmethod def new_start_segments(cls, input: CompoundRegionOperationNode, length: int, fraction: float) -> CompoundRegionOperationNode: @@ -4733,9 +4760,11 @@ class CompoundRegionOperationNode: @brief Creates a node providing a composition into trapezoids. """ @classmethod - def new_width_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: + def new_width_check(cls, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ..., negative: Optional[bool] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node providing a width check. + + The zero_distance_mode argument has been inserted in version 0.29. """ def __init__(self) -> None: r""" @@ -11347,7 +11376,8 @@ class DText: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: float r""" @@ -19058,6 +19088,12 @@ class Edges(ShapeCollection): r""" @brief Gets the symbolic string from an enum """ + AlwaysIncludeZeroDistance: ClassVar[ZeroDistanceMode] + r""" + @hide + @brief Specifies that check functions should always include edges with zero distance + This mode has little practical value. + """ DiagonalEdges: ClassVar[Edges.EdgeType] r""" @brief Diagonal edges are selected (-45 and 45 degree) @@ -19088,6 +19124,26 @@ class Edges(ShapeCollection): @brief Specifies to ignore properties When using this constraint - for example on a boolean operation - properties are ignored and are not generated in the output. """ + IncludeZeroDistanceWhenCollinearAndTouching: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they are collinear and touch + With this specification, the check functions will also check edges if they share at least one common point and are collinear. This is the mode that includes checking the 'kissing corner' cases when the kissing edges are collinear. This mode was default up to version 0.28. + """ + IncludeZeroDistanceWhenOverlapping: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they overlap + With this specification, the check functions will also check edges which are collinear and share more than a single point. This is the mode that excludes the 'kissing corner' cases. + """ + IncludeZeroDistanceWhenTouching: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they touch + With this specification, the check functions will also check edges if they share at least one common point. This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.29 and later. + """ + NeverIncludeZeroDistance: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should never include edges with zero distance. + With this specification, the check functions will ignore edges which are collinear or touch. + """ NoPropertyConstraint: ClassVar[PropertyConstraint] r""" @brief Specifies not to apply any property constraint @@ -19960,7 +20016,7 @@ class Edges(ShapeCollection): This method has been introduced in version 0.28.4. """ - def enclosed_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def enclosed_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an inside check with options @param d The minimum distance for which the edges are checked @@ -19970,6 +20026,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -19982,8 +20039,9 @@ class Edges(ShapeCollection): "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. The 'enclosed_check' alias was introduced in version 0.27.5. + 'zero_distance_mode' has been added in version 0.29. """ - def enclosing_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def enclosing_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an enclosing check with options @param d The minimum distance for which the edges are checked @@ -19993,6 +20051,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -20003,6 +20062,8 @@ class Edges(ShapeCollection): Use nil for this value to select the default. "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. + + 'zero_distance_mode' has been added in version 0.29. """ def end_segments(self, length: int, fraction: float) -> Edges: r""" @@ -20247,7 +20308,7 @@ class Edges(ShapeCollection): This method has been introduced in version 0.28. """ - def inside_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def inside_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an inside check with options @param d The minimum distance for which the edges are checked @@ -20257,6 +20318,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -20269,6 +20331,7 @@ class Edges(ShapeCollection): "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. The 'enclosed_check' alias was introduced in version 0.27.5. + 'zero_distance_mode' has been added in version 0.29. """ def inside_outside_part(self, other: Region) -> List[Edges]: r""" @@ -20625,7 +20688,7 @@ class Edges(ShapeCollection): This method has been introduced in version 0.24. """ - def overlap_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def overlap_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an overlap check with options @param d The minimum distance for which the edges are checked @@ -20635,6 +20698,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -20645,6 +20709,8 @@ class Edges(ShapeCollection): Use nil for this value to select the default. "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. + + 'zero_distance_mode' has been added in version 0.29. """ @overload def pull_interacting(self, other: Edges) -> Edges: @@ -20803,7 +20869,7 @@ class Edges(ShapeCollection): This method has been introduced in version 0.24. """ - def separation_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def separation_check(self, other: Edges, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an overlap check with options @param d The minimum distance for which the edges are checked @@ -20813,6 +20879,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -20823,6 +20890,8 @@ class Edges(ShapeCollection): Use nil for this value to select the default. "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. + + 'zero_distance_mode' has been added in version 0.29. """ def size(self) -> int: r""" @@ -20833,7 +20902,7 @@ class Edges(ShapeCollection): Starting with version 0.27, the method is called 'count' for consistency with \Region. 'size' is still provided as an alias. """ - def space_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def space_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a space check with options @param d The minimum distance for which the edges are checked @@ -20842,6 +20911,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the space check. @@ -20852,6 +20922,8 @@ class Edges(ShapeCollection): Use nil for this value to select the default. "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. + + 'zero_distance_mode' has been added in version 0.29. """ @overload def split_inside(self, other: Edges) -> List[Edges]: @@ -21066,7 +21138,7 @@ class Edges(ShapeCollection): @return The transformed edge collection. """ - def width_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ...) -> EdgePairs: + def width_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a width check with options @param d The minimum width for which the edges are checked @@ -21075,6 +21147,7 @@ class Edges(ShapeCollection): @param ignore_angle The threshold angle above which no check is performed @param min_projection The lower threshold of the projected length of one edge onto another @param max_projection The upper threshold of the projected length of one edge onto another + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -21085,6 +21158,8 @@ class Edges(ShapeCollection): Use nil for this value to select the default. "min_projection" and "max_projection" allow selecting edges by their projected value upon each other. 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. + + 'zero_distance_mode' has been added in version 0.29. """ @overload def with_angle(self, angle: float, inverse: bool) -> Edges: @@ -21777,12 +21852,12 @@ class HAlign: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -21807,12 +21882,12 @@ class HAlign: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -24021,9 +24096,10 @@ class Instance: @brief Gets the transformation of the instance or the first instance in the array The transformation returned is only valid if the array does not represent a complex transformation array Setter: - @brief Sets the transformation of the instance or the first instance in the array + @brief Sets the transformation of the instance or the first instance in the array (in micrometer units) + This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. - This method has been introduced in version 0.23. + This method has been introduced in version 0.25. """ @classmethod def new(cls) -> Instance: @@ -26798,6 +26874,12 @@ class Layout: Clears the layout completely. """ + def clear_all_meta_info(self) -> None: + r""" + @brief Clears all meta information of the layout (cell specific and global) + See \LayoutMetaInfo for details about layouts and meta information. + This method has been introduced in version 0.28.16. + """ @overload def clear_layer(self, layer_index: int) -> None: r""" @@ -26951,6 +27033,26 @@ class Layout: This method variant has been introduced in version 0.28.9. """ @overload + def copy_meta_info(self, other: Layout) -> None: + r""" + @brief Copies the meta information from the other layout into this layout + See \LayoutMetaInfo for details about cells and meta information. + The meta information from this layout will be replaced by the meta information from the other layout. + + This method has been introduced in version 0.28.16. + """ + @overload + def copy_meta_info(self, other: Layout, cm: CellMapping) -> None: + r""" + @brief Copies the meta information from the other layout into this layout for the cells given by the cell mapping + See \LayoutMetaInfo for details about cells and meta information. + This method will use the source/target cell pairs from the cell mapping object and merge the meta information from each source cell from the 'other' layout into the mapped cell inside self. + This method can be used with '\copy_tree_shapes' and similar to copy meta information in addition to the shapes. + All cell-specific keys in this layout will be replaced by the respective values from the other layout. + + This method has been introduced in version 0.28.16. + """ + @overload def copy_tree_shapes(self, source_layout: Layout, cell_mapping: CellMapping) -> None: r""" @brief Copies the shapes for all given mappings in the \CellMapping object @@ -27451,6 +27553,28 @@ class Layout: @brief Gets the library this layout lives in or nil if the layout is not part of a library This attribute has been introduced in version 0.27.5. """ + @overload + def merge_meta_info(self, other: Layout) -> None: + r""" + @brief Merges the meta information from the other layout into this layout + See \LayoutMetaInfo for details about cells and meta information. + Existing keys in this layout will be overwritten by the respective values from the other layout. + New keys will be added. + + This method has been introduced in version 0.28.16. + """ + @overload + def merge_meta_info(self, other: Layout, cm: CellMapping) -> None: + r""" + @brief Merges the meta information from the other layout into this layout for the cells given by the cell mapping + See \LayoutMetaInfo for details about cells and meta information. + This method will use the source/target cell pairs from the cell mapping object and merge the meta information from each source cell from the 'other' layout into the mapped cell inside self. + This method can be used with '\copy_tree_shapes' and similar to copy meta information in addition to the shapes. + Existing cell-specific keys in this layout will be overwritten by the respective values from the other layout. + New keys will be added. + + This method has been introduced in version 0.28.16. + """ def meta_info(self, name: str) -> LayoutMetaInfo: r""" @brief Gets the meta information for a given name @@ -27963,19 +28087,19 @@ class LayoutDiff: BoxesAsPolygons: ClassVar[int] r""" @brief Compare boxes to polygons - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ DontSummarizeMissingLayers: ClassVar[int] r""" @brief Don't summarize missing layers If this mode is present, missing layers are treated as empty ones and every shape on the other layer will be reported as difference. - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ FlattenArrayInsts: ClassVar[int] r""" @brief Compare array instances instance by instance - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ IgnoreDuplicates: ClassVar[int] r""" @@ -27987,41 +28111,41 @@ class LayoutDiff: NoLayerNames: ClassVar[int] r""" @brief Do not compare layer names - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ NoProperties: ClassVar[int] r""" @brief Ignore properties - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ NoTextDetails: ClassVar[int] r""" @brief Ignore text details (font, size, presentation) - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ NoTextOrientation: ClassVar[int] r""" @brief Ignore text orientation - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ PathsAsPolygons: ClassVar[int] r""" @brief Compare paths to polygons - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ Silent: ClassVar[int] r""" @brief Silent compare - just report whether the layouts are identical Silent mode will not issue any signals, but instead the return value of the \LayoutDiff#compare method will indicate whether the layouts are identical. In silent mode, the compare method will return immediately once a difference has been encountered so that mode may be much faster than the full compare. - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ SmartCellMapping: ClassVar[int] r""" @brief Derive smart cell mapping instead of name mapping (available only if top cells are specified) Smart cell mapping is only effective currently when cells are compared (with \LayoutDiff#compare with cells instead of layout objects). - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. """ Verbose: ClassVar[int] r""" @@ -28029,7 +28153,14 @@ class LayoutDiff: See the event descriptions for details about the differences in verbose and non-verbose mode. - This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. + """ + WithMetaInfo: ClassVar[int] + r""" + @brief Ignore meta info + This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be combined with other constants to form a flag set. If present, this option tells the compare algorithm to include persisted meta info in the compare. + + This flag has been introduced in version 0.28.16. """ on_bbox_differs: None r""" @@ -28053,10 +28184,10 @@ class LayoutDiff: on_begin_cell: None r""" Getter: - @brief This signal initiates the sequence of events for a cell pair + @brief This signal indicates the sequence of events for a cell pair All cell specific events happen between \begin_cell_event and \end_cell_event signals. Setter: - @brief This signal initiates the sequence of events for a cell pair + @brief This signal indicates the sequence of events for a cell pair All cell specific events happen between \begin_cell_event and \end_cell_event signals. """ on_begin_edge_differences: None @@ -28154,6 +28285,21 @@ class LayoutDiff: Setter: @brief This signal indicates that the given cell is only present in the second layout """ + on_cell_meta_info_differs: None + r""" + Getter: + @brief This signal indicates that meta info between the current cells differs + Meta information is only compared when \WithMetaInfo is added to the compare flags. + 'a' and 'b' are the values for the first and second layout. 'nil' is passed to these values to indicate missing meta information on one side. + + This event has been added in version 0.28.16. + Setter: + @brief This signal indicates that meta info between the current cells differs + Meta information is only compared when \WithMetaInfo is added to the compare flags. + 'a' and 'b' are the values for the first and second layout. 'nil' is passed to these values to indicate missing meta information on one side. + + This event has been added in version 0.28.16. + """ on_cell_name_differs: None r""" Getter: @@ -28320,6 +28466,21 @@ class LayoutDiff: Setter: @brief This signal indicates a difference in the layer names """ + on_layout_meta_info_differs: None + r""" + Getter: + @brief This signal indicates that global meta info differs + Meta information is only compared when \WithMetaInfo is added to the compare flags. + 'a' and 'b' are the values for the first and second layout. 'nil' is passed to these values to indicate missing meta information on one side. + + This event has been added in version 0.28.16. + Setter: + @brief This signal indicates that global meta info differs + Meta information is only compared when \WithMetaInfo is added to the compare flags. + 'a' and 'b' are the values for the first and second layout. 'nil' is passed to these values to indicate missing meta information on one side. + + This event has been added in version 0.28.16. + """ on_path_in_a_only: None r""" Getter: @@ -29074,12 +29235,12 @@ class LayoutToNetlist: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -30363,12 +30524,12 @@ class LoadLayoutOptions: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -30393,12 +30554,12 @@ class LoadLayoutOptions: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -33099,14 +33260,14 @@ class NetPinRef: @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to. + @brief Gets the net this pin reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this pin reference is attached to. """ def pin(self) -> Pin: r""" @@ -34153,14 +34314,6 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload - def circuits_by_name(self, name_pattern: str) -> List[Circuit]: - r""" - @brief Gets the circuit objects for a given name filter. - The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This method has been introduced in version 0.26.4. - """ - @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" @brief Gets the circuit objects for a given name filter (const version). @@ -34169,6 +34322,14 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ + @overload + def circuits_by_name(self, name_pattern: str) -> List[Circuit]: + r""" + @brief Gets the circuit objects for a given name filter. + The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. + + This method has been introduced in version 0.26.4. + """ def combine_devices(self) -> None: r""" @brief Combines devices where possible @@ -34195,16 +34356,16 @@ class Netlist: @overload def device_class_by_name(self, name: str) -> DeviceClass: r""" - @brief Gets the device class for a given name (const version). + @brief Gets the device class for a given name. If the name is not a valid device class name, nil is returned. - - This constness variant has been introduced in version 0.26.8. """ @overload def device_class_by_name(self, name: str) -> DeviceClass: r""" - @brief Gets the device class for a given name. + @brief Gets the device class for a given name (const version). If the name is not a valid device class name, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ def dup(self) -> Netlist: r""" @@ -34223,12 +34384,6 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload - def each_circuit_bottom_up(self) -> Iterator[Circuit]: - r""" - @brief Iterates over the circuits bottom-up - Iterating bottom-up means the parent circuits come after the child circuits. This is the basically the reverse order as delivered by \each_circuit_top_down. - """ - @overload def each_circuit_bottom_up(self) -> Iterator[Circuit]: r""" @brief Iterates over the circuits bottom-up (const version) @@ -34237,10 +34392,10 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload - def each_circuit_top_down(self) -> Iterator[Circuit]: + def each_circuit_bottom_up(self) -> Iterator[Circuit]: r""" - @brief Iterates over the circuits top-down - Iterating top-down means the parent circuits come before the child circuits. The first \top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits. + @brief Iterates over the circuits bottom-up + Iterating bottom-up means the parent circuits come after the child circuits. This is the basically the reverse order as delivered by \each_circuit_top_down. """ @overload def each_circuit_top_down(self) -> Iterator[Circuit]: @@ -34251,6 +34406,12 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload + def each_circuit_top_down(self) -> Iterator[Circuit]: + r""" + @brief Iterates over the circuits top-down + Iterating top-down means the parent circuits come before the child circuits. The first \top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits. + """ + @overload def each_device_class(self) -> Iterator[DeviceClass]: r""" @brief Iterates over the device classes of the netlist @@ -36669,12 +36830,12 @@ class PCellParameterState: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -39291,12 +39452,12 @@ class PreferredOrientation: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -39476,12 +39637,12 @@ class PropertyConstraint: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -39506,12 +39667,12 @@ class PropertyConstraint: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -40974,12 +41135,12 @@ class Region(ShapeCollection): @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -41107,6 +41268,12 @@ class Region(ShapeCollection): r""" @brief Gets the symbolic string from an enum """ + AlwaysIncludeZeroDistance: ClassVar[ZeroDistanceMode] + r""" + @hide + @brief Specifies that check functions should always include edges with zero distance + This mode has little practical value. + """ DifferentPropertiesConstraint: ClassVar[PropertyConstraint] r""" @brief Specifies to consider shapes only if their user properties are different @@ -41137,6 +41304,26 @@ class Region(ShapeCollection): @brief Specifies to ignore properties When using this constraint - for example on a boolean operation - properties are ignored and are not generated in the output. """ + IncludeZeroDistanceWhenCollinearAndTouching: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they are collinear and touch + With this specification, the check functions will also check edges if they share at least one common point and are collinear. This is the mode that includes checking the 'kissing corner' cases when the kissing edges are collinear. This mode was default up to version 0.28. + """ + IncludeZeroDistanceWhenOverlapping: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they overlap + With this specification, the check functions will also check edges which are collinear and share more than a single point. This is the mode that excludes the 'kissing corner' cases. + """ + IncludeZeroDistanceWhenTouching: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they touch + With this specification, the check functions will also check edges if they share at least one common point. This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.29 and later. + """ + NeverIncludeZeroDistance: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should never include edges with zero distance. + With this specification, the check functions will ignore edges which are collinear or touch. + """ NoOppositeFilter: ClassVar[Region.OppositeFilter] r""" @brief No opposite filtering @@ -42011,7 +42198,7 @@ class Region(ShapeCollection): This method has been introduced in version 0.28.4. """ - def enclosed_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def enclosed_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an inside check with options @param d The minimum distance for which the polygons are checked @@ -42025,6 +42212,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative Negative output from the first input @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -42047,8 +42235,9 @@ class Region(ShapeCollection): 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. The 'enclosed_check' alias was introduced in version 0.27.5. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ - def enclosing_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def enclosing_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an enclosing check with options @param d The minimum enclosing distance for which the polygons are checked @@ -42062,6 +42251,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative Negative output from the first input @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -42083,6 +42273,7 @@ class Region(ShapeCollection): 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. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ def extent_refs(self, arg0: float, arg1: float, arg2: float, arg3: float, arg4: int, arg5: int) -> Region: r""" @@ -42313,7 +42504,7 @@ class Region(ShapeCollection): Merged semantics applies for this method (see \merged_semantics= for a description of this concept) """ - def inside_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def inside_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an inside check with options @param d The minimum distance for which the polygons are checked @@ -42327,6 +42518,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative Negative output from the first input @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -42349,6 +42541,7 @@ class Region(ShapeCollection): 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. The 'enclosed_check' alias was introduced in version 0.27.5. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ @overload def interacting(self, other: Edges, min_count: Optional[int] = ..., max_count: Optional[int] = ...) -> Region: @@ -42421,7 +42614,7 @@ class Region(ShapeCollection): @brief Returns true if the region is merged If the region is merged, polygons will not touch or overlap. You can ensure merged state by calling \merge. """ - def isolated_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def isolated_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a space check between edges of different polygons with options @param d The minimum space for which the polygons are checked @@ -42434,6 +42627,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative If true, edges not violation the condition will be output as pseudo-edge pairs @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -42453,6 +42647,7 @@ class Region(ShapeCollection): The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ def join(self, other: Region) -> Region: r""" @@ -42865,7 +43060,7 @@ class Region(ShapeCollection): This variant has been introduced in version 0.28.4. """ - def notch_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def notch_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a space check between edges of the same polygon with options @param d The minimum space for which the polygons are checked @@ -42877,7 +43072,8 @@ class Region(ShapeCollection): @param shielded Enables shielding @param negative If true, edges not violation the condition will be output as pseudo-edge pairs @param property_constraint Specifies whether to consider only shapes with a certain property relation - @param property_constraint Only \IgnoreProperties and \NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output + @param property_constraint Only \IgnoreProperties and \NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output@param zero_distance_mode Specifies how to handle edges with zero distance + This version is similar to the simple version with one parameter. In addition, it allows to specify many more options. If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the space check. @@ -42896,6 +43092,7 @@ class Region(ShapeCollection): The 'shielded' and 'negative' options have been introduced in version 0.27. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ def or_(self, other: Region) -> Region: r""" @@ -42925,7 +43122,7 @@ class Region(ShapeCollection): Merged semantics applies for this method (see \merged_semantics= for a description of this concept) """ - def overlap_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def overlap_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs an overlap check with options @param d The minimum overlap for which the polygons are checked @@ -42939,6 +43136,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative Negative output from the first input @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -42960,6 +43158,7 @@ class Region(ShapeCollection): 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. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ def overlapping(self, other: Region, min_count: Optional[int] = ..., max_count: Optional[int] = ...) -> Region: r""" @@ -43258,7 +43457,7 @@ class Region(ShapeCollection): The count options have been introduced in version 0.27. """ - def separation_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def separation_check(self, other: Region, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a separation check with options @param d The minimum separation for which the polygons are checked @@ -43272,6 +43471,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative Negative output from the first input @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -43293,6 +43493,7 @@ class Region(ShapeCollection): 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. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ @overload def size(self) -> int: @@ -43427,7 +43628,7 @@ class Region(ShapeCollection): @brief Returns the snapped region This method will snap the region to the given grid and return the snapped region (see \snap). The original region is not modified. """ - def space_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def space_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., opposite_filter: Optional[Region.OppositeFilter] = ..., rect_filter: Optional[Region.RectFilter] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a space check with options @param d The minimum space for which the polygons are checked @@ -43440,6 +43641,7 @@ class Region(ShapeCollection): @param rect_filter Specifies an error filter for rectangular input shapes @param negative If true, edges not violation the condition will be output as pseudo-edge pairs @param property_constraint Specifies whether to consider only shapes with a certain property relation + @param zero_distance_mode Specifies how to handle edges with zero distance If "whole_edges" is true, the resulting \EdgePairs collection will receive the whole edges which contribute in the width check. @@ -43459,6 +43661,7 @@ class Region(ShapeCollection): The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ def split_covering(self, other: Region, min_count: Optional[int] = ..., max_count: Optional[int] = ...) -> List[Region]: r""" @@ -43721,7 +43924,7 @@ class Region(ShapeCollection): @return The transformed region. """ - def width_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ...) -> EdgePairs: + def width_check(self, d: int, whole_edges: Optional[bool] = ..., metrics: Optional[Metrics] = ..., ignore_angle: Optional[Any] = ..., min_projection: Optional[Any] = ..., max_projection: Optional[Any] = ..., shielded: Optional[bool] = ..., negative: Optional[bool] = ..., property_constraint: Optional[PropertyConstraint] = ..., zero_distance_mode: Optional[ZeroDistanceMode] = ...) -> EdgePairs: r""" @brief Performs a width check with options @param d The minimum width for which the polygons are checked @@ -43732,7 +43935,8 @@ class Region(ShapeCollection): @param max_projection The upper limit of the projected length of one edge onto another @param shielded Enables shielding @param negative If true, edges not violation the condition will be output as pseudo-edge pairs - @param property_constraint Only \IgnoreProperties and \NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output. Other than 'width' allow more options here. + @param property_constraint Only \IgnoreProperties and \NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output. @param zero_distance_mode Specifies how to handle edges with zero distance + Other than 'width' allow more options here. This version is similar to the simple version with one parameter. In addition, it allows to specify many more options. @@ -43751,6 +43955,7 @@ class Region(ShapeCollection): Merged semantics applies for the input of this method (see \merged_semantics= for a description of this concept) The 'shielded' and 'negative' options have been introduced in version 0.27. 'property_constraint' has been added in version 0.28.4. + 'zero_distance_mode' has been added in version 0.29. """ @overload def with_angle(self, amin: float, amax: float, inverse: bool) -> EdgePairs: @@ -45271,10 +45476,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a path. Setter: - @brief Replaces the shape by the given path (in micrometer units) - This method replaces the shape by the given path, like \path= with a \Path argument does. This version translates the path from micrometer units to database units internally. + @brief Replaces the shape by the given path object + This method replaces the shape by the given path object. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ path_bgnext: int r""" @@ -49204,7 +49410,8 @@ class Text: Setter: @brief Sets the horizontal alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ size: int r""" @@ -51995,12 +52202,12 @@ class TrapezoidDecompositionMode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -52334,12 +52541,12 @@ class VAlign: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -53692,3 +53899,184 @@ class Vector: @return 1 if the vector product is positive, 0 if it is zero and -1 if it is negative. """ +class ZeroDistanceMode: + r""" + @brief This class represents the zero_distance_mode type for \Region#width and related checks. + This mode determines how edges with zero distance are treated in the DRC checks. Formally these edges do neither represent a space other other relation as they do not face each other. There are three modes available to treat this boundary case: Ignore such edges (\NeverIncludeZeroDistance) or only include them if they share at least one common point (\IncludeZeroDistanceWhenTouching). The latter mode allows activating checks for the 'kissing corner' case and is the default mode in most checks. + This enum has been introduced in version 0.29. + """ + AlwaysIncludeZeroDistance: ClassVar[ZeroDistanceMode] + r""" + @hide + @brief Specifies that check functions should always include edges with zero distance + This mode has little practical value. + """ + IncludeZeroDistanceWhenCollinearAndTouching: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they are collinear and touch + With this specification, the check functions will also check edges if they share at least one common point and are collinear. This is the mode that includes checking the 'kissing corner' cases when the kissing edges are collinear. This mode was default up to version 0.28. + """ + IncludeZeroDistanceWhenOverlapping: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they overlap + With this specification, the check functions will also check edges which are collinear and share more than a single point. This is the mode that excludes the 'kissing corner' cases. + """ + IncludeZeroDistanceWhenTouching: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should include edges when they touch + With this specification, the check functions will also check edges if they share at least one common point. This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.29 and later. + """ + NeverIncludeZeroDistance: ClassVar[ZeroDistanceMode] + r""" + @brief Specifies that check functions should never include edges with zero distance. + With this specification, the check functions will ignore edges which are collinear or touch. + """ + @overload + @classmethod + def new(cls, i: int) -> ZeroDistanceMode: + r""" + @brief Creates an enum from an integer value + """ + @overload + @classmethod + def new(cls, s: str) -> ZeroDistanceMode: + r""" + @brief Creates an enum from a string value + """ + def __copy__(self) -> ZeroDistanceMode: + r""" + @brief Creates a copy of self + """ + def __deepcopy__(self) -> ZeroDistanceMode: + r""" + @brief Creates a copy of self + """ + @overload + def __eq__(self, other: object) -> bool: + r""" + @brief Compares two enums + """ + @overload + def __eq__(self, other: object) -> bool: + r""" + @brief Compares an enum with an integer value + """ + @overload + def __init__(self, i: int) -> None: + r""" + @brief Creates an enum from an integer value + """ + @overload + def __init__(self, s: str) -> None: + r""" + @brief Creates an enum from a string value + """ + @overload + def __lt__(self, other: ZeroDistanceMode) -> bool: + r""" + @brief Returns true if the first enum is less (in the enum symbol order) than the second + """ + @overload + def __lt__(self, other: int) -> bool: + r""" + @brief Returns true if the enum is less (in the enum symbol order) than the integer value + """ + @overload + def __ne__(self, other: object) -> bool: + r""" + @brief Compares an enum with an integer for inequality + """ + @overload + def __ne__(self, other: object) -> bool: + r""" + @brief Compares two enums for inequality + """ + def __repr__(self) -> str: + r""" + @brief Converts an enum to a visual string + """ + def __str__(self) -> str: + r""" + @brief Gets the symbolic string from an enum + """ + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + def assign(self, other: ZeroDistanceMode) -> None: + r""" + @brief Assigns another object to self + """ + def create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + def destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + def destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + def dup(self) -> ZeroDistanceMode: + r""" + @brief Creates a copy of self + """ + def inspect(self) -> str: + r""" + @brief Converts an enum to a visual string + """ + def is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + def to_i(self) -> int: + r""" + @brief Gets the integer value from the enum + """ + def to_s(self) -> str: + r""" + @brief Gets the symbolic string from an enum + """ + diff --git a/src/pymod/distutils_src/klayout/laycore.pyi b/src/pymod/distutils_src/klayout/laycore.pyi index 90ce81c53..f5e2077b4 100644 --- a/src/pymod/distutils_src/klayout/laycore.pyi +++ b/src/pymod/distutils_src/klayout/laycore.pyi @@ -7204,12 +7204,12 @@ class Macro: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -7234,12 +7234,12 @@ class Macro: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -7331,12 +7331,12 @@ class Macro: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" diff --git a/testdata/drc/drcSimpleTests_90.drc b/testdata/drc/drcSimpleTests_90.drc new file mode 100644 index 000000000..eb85f24ee --- /dev/null +++ b/testdata/drc/drcSimpleTests_90.drc @@ -0,0 +1,34 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l1_raw = input(1, 0).raw +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l1.width(600.nm).output(100, 0) +l1.width(600.nm, without_touching_corners).output(101, 0) +l1.width(600.nm, without_touching_edges).output(102, 0) + +l1.space(600.nm).output(200, 0) +l1.space(600.nm, without_touching_corners).output(201, 0) +l1.space(600.nm, without_touching_edges).output(202, 0) +l1_raw.space(600.nm).output(210, 0) +l1_raw.space(600.nm, without_touching_corners).output(211, 0) +l1_raw.space(600.nm, without_touching_edges).output(212, 0) + +l1.sep(l2, 600.nm).output(300, 0) +l1.sep(l2, 600.nm, without_touching_corners).output(301, 0) +l1.sep(l2, 600.nm, without_touching_edges).output(302, 0) + +l1.drc(space < 600.nm).output(400, 0) +l1.drc(space(without_touching_corners) < 600.nm).output(401, 0) +l1.drc(space(without_touching_edges) < 600.nm).output(402, 0) + diff --git a/testdata/drc/drcSimpleTests_90.gds b/testdata/drc/drcSimpleTests_90.gds new file mode 100644 index 000000000..1fedad706 Binary files /dev/null and b/testdata/drc/drcSimpleTests_90.gds differ diff --git a/testdata/drc/drcSimpleTests_91.drc b/testdata/drc/drcSimpleTests_91.drc new file mode 100644 index 000000000..0b79b7773 --- /dev/null +++ b/testdata/drc/drcSimpleTests_91.drc @@ -0,0 +1,34 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) + +l1.output(1, 0) + +l1.space(600.nm).output(100, 0) +l1.space(600.nm, without_touching_corners).output(101, 0) +l1.space(600.nm, without_touching_edges).output(102, 0) +l1.space(600.nm, DRCZeroDistanceMode::new(RBA::ZeroDistanceMode::IncludeZeroDistanceWhenCollinearAndTouching)).output(103, 0) + +l1.space(600.nm, projection).output(110, 0) +l1.space(600.nm, projection, without_touching_corners).output(111, 0) +l1.space(600.nm, projection, without_touching_edges).output(112, 0) +l1.space(600.nm, projection, DRCZeroDistanceMode::new(RBA::ZeroDistanceMode::IncludeZeroDistanceWhenCollinearAndTouching)).output(113, 0) + +l1.space(600.nm, square).output(120, 0) +l1.space(600.nm, square, without_touching_corners).output(121, 0) +l1.space(600.nm, square, without_touching_edges).output(122, 0) +l1.space(600.nm, square, DRCZeroDistanceMode::new(RBA::ZeroDistanceMode::IncludeZeroDistanceWhenCollinearAndTouching)).output(123, 0) + +l1.data.min_coherence = true + +l1.space(600.nm).output(200, 0) +l1.space(600.nm, without_touching_corners).output(201, 0) +l1.space(600.nm, without_touching_edges).output(202, 0) +l1.space(600.nm, DRCZeroDistanceMode::new(RBA::ZeroDistanceMode::IncludeZeroDistanceWhenCollinearAndTouching)).output(203, 0) + diff --git a/testdata/drc/drcSimpleTests_91.gds b/testdata/drc/drcSimpleTests_91.gds new file mode 100644 index 000000000..ab89a181d Binary files /dev/null and b/testdata/drc/drcSimpleTests_91.gds differ diff --git a/testdata/drc/drcSimpleTests_au90.gds b/testdata/drc/drcSimpleTests_au90.gds new file mode 100644 index 000000000..42058a4a8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au90.gds differ diff --git a/testdata/drc/drcSimpleTests_au90d.gds b/testdata/drc/drcSimpleTests_au90d.gds new file mode 100644 index 000000000..42058a4a8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au90d.gds differ diff --git a/testdata/drc/drcSimpleTests_au91.gds b/testdata/drc/drcSimpleTests_au91.gds new file mode 100644 index 000000000..ff5c26d23 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au91.gds differ diff --git a/testdata/drc/drcSimpleTests_au91d.gds b/testdata/drc/drcSimpleTests_au91d.gds new file mode 100644 index 000000000..ff5c26d23 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au91d.gds differ diff --git a/testdata/drc/issue_1594.drc b/testdata/drc/issue_1594.drc new file mode 100644 index 000000000..63d7ea7dc --- /dev/null +++ b/testdata/drc/issue_1594.drc @@ -0,0 +1,15 @@ +source($drc_test_source, "TOP1") + +# This is just a smoke test without actual devices + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +connect(l1, l2) +connect(l2, l3) + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "netlist") + diff --git a/testdata/drc/issue_1594.gds b/testdata/drc/issue_1594.gds new file mode 100644 index 000000000..fa9036ee3 Binary files /dev/null and b/testdata/drc/issue_1594.gds differ diff --git a/testdata/drc/issue_1594_au.cir b/testdata/drc/issue_1594_au.cir new file mode 100644 index 000000000..ea8030c0a --- /dev/null +++ b/testdata/drc/issue_1594_au.cir @@ -0,0 +1,5 @@ +* netlist + +* cell TOP1 +.SUBCKT TOP1 +.ENDS TOP1 diff --git a/testdata/dxf/issue_1592.dxf.gz b/testdata/dxf/issue_1592.dxf.gz new file mode 100644 index 000000000..76ddf557d Binary files /dev/null and b/testdata/dxf/issue_1592.dxf.gz differ diff --git a/testdata/dxf/issue_1592a_au.oas.gz b/testdata/dxf/issue_1592a_au.oas.gz new file mode 100644 index 000000000..61bce16f7 Binary files /dev/null and b/testdata/dxf/issue_1592a_au.oas.gz differ diff --git a/testdata/dxf/issue_1592b_au.oas.gz b/testdata/dxf/issue_1592b_au.oas.gz new file mode 100644 index 000000000..a55226a25 Binary files /dev/null and b/testdata/dxf/issue_1592b_au.oas.gz differ diff --git a/testdata/gds/collect_add_au.gds b/testdata/gds/collect_add_au.gds index ca0206610..ec52fb206 100644 Binary files a/testdata/gds/collect_add_au.gds and b/testdata/gds/collect_add_au.gds differ diff --git a/testdata/gds/collect_added.gds b/testdata/gds/collect_added.gds index e4882b6d8..2e366a6a0 100644 Binary files a/testdata/gds/collect_added.gds and b/testdata/gds/collect_added.gds differ diff --git a/testdata/gds/collect_basic.gds b/testdata/gds/collect_basic.gds index 9fad7517e..b9c8c7e63 100644 Binary files a/testdata/gds/collect_basic.gds and b/testdata/gds/collect_basic.gds differ diff --git a/testdata/gds/collect_overwrite_au.gds b/testdata/gds/collect_overwrite_au.gds index e4790a23d..b77fbcde9 100644 Binary files a/testdata/gds/collect_overwrite_au.gds and b/testdata/gds/collect_overwrite_au.gds differ diff --git a/testdata/gds/collect_rename_au.gds b/testdata/gds/collect_rename_au.gds index af98c817f..f66ca2fde 100644 Binary files a/testdata/gds/collect_rename_au.gds and b/testdata/gds/collect_rename_au.gds differ diff --git a/testdata/gds/collect_skip_au.gds b/testdata/gds/collect_skip_au.gds index 5a0b96fa9..7b77e88e0 100644 Binary files a/testdata/gds/collect_skip_au.gds and b/testdata/gds/collect_skip_au.gds differ diff --git a/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz b/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz index c9dd21723..907e64016 100644 Binary files a/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz and b/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz differ diff --git a/testdata/lefdef/patternname/au.oas.gz b/testdata/lefdef/patternname/au.oas.gz index f63a96863..b6ae4e87d 100644 Binary files a/testdata/lefdef/patternname/au.oas.gz and b/testdata/lefdef/patternname/au.oas.gz differ diff --git a/testdata/lvs/custom_resistors.cir b/testdata/lvs/custom_resistors.cir new file mode 100644 index 000000000..f49736e76 --- /dev/null +++ b/testdata/lvs/custom_resistors.cir @@ -0,0 +1,18 @@ + +* cell TOP +.SUBCKT TOP +* cell instance $1 r180 *1 2,2.6 +X$1 6 1 A +* cell instance $2 r0 *1 2.2,1 +X$2 6 5 A +* device instance $1 r0 *1 0.8,0.75 RPP1 +R$1 2 1 0.555555555556 RPP1 +.ENDS TOP + +* cell A +* pin +* pin +.SUBCKT A 1 2 +* device instance $1 r0 *1 -0.2,0.4 RPP1 +R$1 1 2 1.25 RPP1 +.ENDS A diff --git a/testdata/lvs/custom_resistors.gds b/testdata/lvs/custom_resistors.gds new file mode 100644 index 000000000..df4b2d400 Binary files /dev/null and b/testdata/lvs/custom_resistors.gds differ diff --git a/testdata/lvs/custom_resistors.l2n.1 b/testdata/lvs/custom_resistors.l2n.1 new file mode 100644 index 000000000..1047b4c0a --- /dev/null +++ b/testdata/lvs/custom_resistors.l2n.1 @@ -0,0 +1,103 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l4 '15/0') +L(l3 '16/0') +L(l1) +C(l4 l4 l3 l1) +C(l3 l4 l3) +C(l1 l4 l1) +H(E B('Resistor shape does not touch marker border in exactly two places') C(TOP) X('device-extract') Q('(0.8,0.75;0.8,1.15;1,1.15;1,0.75)')) +H(E B('Resistor shape does not touch marker border in exactly two places') C(TOP) X('device-extract') Q('(0,0.75;0,1.15;0.2,1.15;0.2,0.75)')) +H(E B('Resistor shape does not touch marker border in exactly two places') C(A) X('device-extract') Q('(0.85,-0.4;0.85,-0.2;1.25,-0.2;1.25,-0.4)')) +K(RPP1 RES) +D(D$RPP1 RPP1 + T(A + R(l1 (0 400) (200 300)) + ) + T(B + R(l1 (0 1200) (200 250)) + ) +) +D(D$RPP1$1 RPP1 + T(A + R(l1 (0 0) (250 200)) + ) + T(B + R(l1 (750 0) (250 200)) + ) +) +X(A + R((-200 -450) (1750 1350)) + N(1 + R(l4 (-150 450) (100 100)) + R(l3 (-150 -150) (200 500)) + R(l1 (-200 -500) (250 200)) + ) + N(2 + R(l4 (650 450) (100 100)) + R(l4 (-100 -900) (100 100)) + R(l3 (-150 200) (200 650)) + R(l3 (-200 -1000) (200 500)) + R(l1 (-250 300) (250 200)) + R(l1 (-200 -1000) (250 200)) + ) + N(3 + R(l4 (1450 -350) (100 100)) + ) + P(1) + P(2) + D(1 D$RPP1$1 + Y(-200 400) + E(R 1.25) + E(L 0.5) + E(W 0.2) + E(A 0.1) + E(P 1.4) + T(A 1) + T(B 2) + ) +) +X(TOP + R((-50 450) (3800 2600)) + N(1 + R(l4 (850 2050) (100 100)) + R(l3 (-150 -150) (500 200)) + R(l1 (-500 -250) (200 250)) + ) + N(2 + R(l4 (850 1250) (100 100)) + R(l4 (-100 -100) (100 100)) + R(l4 (-900 -100) (100 100)) + R(l3 (200 -150) (650 200)) + R(l3 (-1000 -200) (500 200)) + R(l1 (300 -250) (200 300)) + R(l1 (-1000 -300) (200 250)) + ) + N(3 + R(l4 (50 450) (100 100)) + ) + N(4 + R(l4 (850 450) (100 100)) + ) + N(5) + N(6) + D(1 D$RPP1 + Y(800 750) + E(R 0.555555555556) + E(L 0.333333333333) + E(W 0.3) + E(A 0.1) + E(P 1.26666666667) + T(A 2) + T(B 1) + ) + X(1 A O(180) Y(2000 2600) + P(0 6) + P(1 1) + ) + X(2 A Y(2200 1000) + P(0 6) + P(1 5) + ) +) diff --git a/testdata/lvs/custom_resistors.l2n.2 b/testdata/lvs/custom_resistors.l2n.2 new file mode 100644 index 000000000..17deb7617 --- /dev/null +++ b/testdata/lvs/custom_resistors.l2n.2 @@ -0,0 +1,103 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l4 '15/0') +L(l3 '16/0') +L(l1) +C(l4 l4 l3 l1) +C(l3 l4 l3) +C(l1 l4 l1) +H(E B('Resistor shape does not touch marker border in exactly two places') C(TOP) X('device-extract') Q('(0,0.75;0,1.15;0.2,1.15;0.2,0.75)')) +H(E B('Resistor shape does not touch marker border in exactly two places') C(TOP) X('device-extract') Q('(0.8,0.75;0.8,1.15;1,1.15;1,0.75)')) +H(E B('Resistor shape does not touch marker border in exactly two places') C(A) X('device-extract') Q('(0.85,-0.4;0.85,-0.2;1.25,-0.2;1.25,-0.4)')) +K(RPP1 RES) +D(D$RPP1 RPP1 + T(A + R(l1 (0 400) (200 300)) + ) + T(B + R(l1 (0 1200) (200 250)) + ) +) +D(D$RPP1$1 RPP1 + T(A + R(l1 (0 0) (250 200)) + ) + T(B + R(l1 (750 0) (250 200)) + ) +) +X(A + R((-200 -450) (1750 1350)) + N(1 + R(l4 (-150 450) (100 100)) + R(l3 (-150 -150) (200 500)) + R(l1 (-200 -500) (250 200)) + ) + N(2 + R(l4 (650 450) (100 100)) + R(l4 (-100 -900) (100 100)) + R(l3 (-150 200) (200 650)) + R(l3 (-200 -1000) (200 500)) + R(l1 (-250 300) (250 200)) + R(l1 (-200 -1000) (250 200)) + ) + N(3 + R(l4 (1450 -350) (100 100)) + ) + P(1) + P(2) + D(1 D$RPP1$1 + Y(-200 400) + E(R 1.25) + E(L 0.5) + E(W 0.2) + E(A 0.1) + E(P 1.4) + T(A 1) + T(B 2) + ) +) +X(TOP + R((-50 450) (3800 2600)) + N(1 + R(l4 (850 2050) (100 100)) + R(l3 (-150 -150) (500 200)) + R(l1 (-500 -250) (200 250)) + ) + N(2 + R(l4 (850 1250) (100 100)) + R(l4 (-100 -100) (100 100)) + R(l4 (-900 -100) (100 100)) + R(l3 (200 -150) (650 200)) + R(l3 (-1000 -200) (500 200)) + R(l1 (300 -250) (200 300)) + R(l1 (-1000 -300) (200 250)) + ) + N(3 + R(l4 (50 450) (100 100)) + ) + N(4 + R(l4 (850 450) (100 100)) + ) + N(5) + N(6) + D(1 D$RPP1 + Y(800 750) + E(R 0.555555555556) + E(L 0.333333333333) + E(W 0.3) + E(A 0.1) + E(P 1.26666666667) + T(A 2) + T(B 1) + ) + X(1 A O(180) Y(2000 2600) + P(0 6) + P(1 1) + ) + X(2 A Y(2200 1000) + P(0 6) + P(1 5) + ) +) diff --git a/testdata/lvs/custom_resistors.lvs b/testdata/lvs/custom_resistors.lvs new file mode 100644 index 000000000..78072f645 --- /dev/null +++ b/testdata/lvs/custom_resistors.lvs @@ -0,0 +1,86 @@ + +source($lvs_test_source, "TOP") + +report_netlist($lvs_test_target_l2n) +target_netlist($lvs_test_target_cir) + +deep + +# ------------------------------------------------------------------- +# Layers + +poly_dg = input(13, 0) +cont = input(15, 0) +met1_dg = input(16, 0) +sblk = input(34, 0) +rp_1 = sblk & poly_dg +p1trm = poly_dg - rp_1 + +class ResistorExtractor < RBA::GenericDeviceExtractor + + def initialize(name, sheet_rho) + self.name = name + @sheet_rho = sheet_rho + end + + def setup + define_layer("C", "Conductor") + define_layer("R", "Resistor") + register_device_class(RBA::DeviceClassResistor::new) + end + + def get_connectivity(layout, layers) + # this "connectivity" forms the shape clusters that make up the device + conn = RBA::Connectivity::new + conn.connect(layers[0], layers[1]) # collect touching contacts + conn.connect(layers[1], layers[1]) # combine resistor shapes into one area + conn + end + + def extract_devices(layer_geometry) + # layer_geometry provides the input layers in the order they are defined with "define_layer" + conductor = layer_geometry[0] + resistor = layer_geometry[1] + + resistor_regions = resistor.merged + + resistor_regions.each do |r| + terminals = conductor.interacting(RBA::Region::new(r)) + if terminals.size != 2 + error("Resistor shape does not touch marker border in exactly two places", r) + else + double_width = 0 + (terminals.edges & resistor.edges).merged.each do |e| + double_width += e.length + end + # A = L*W + # -> L = A/W + a = r.area*dbu*dbu + w = (double_width / 2.0)*dbu + l = a / w + + device = create_device + device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w); + + device.set_parameter(RBA::DeviceClassResistor::PARAM_A, a) + device.set_parameter(RBA::DeviceClassResistor::PARAM_L, l) + device.set_parameter(RBA::DeviceClassResistor::PARAM_P, 2*l+2*w) + device.set_parameter(RBA::DeviceClassResistor::PARAM_W, w) + define_terminal(device, "A", "C", terminals[0]); + define_terminal(device, "B", "C", terminals[1]); + end + end + end +end + +extract_devices(ResistorExtractor::new("RPP1", 0.5), # intentionally wrong: 1565.15/5 + { "C" => p1trm, "R" => rp_1 }) + +connect(met1_dg, cont) +connect(p1trm, cont) + +begin + netlist +rescue => ex +end + diff --git a/testdata/lym/m2.py b/testdata/lym/m2.py index 401d57365..ca794598c 100644 --- a/testdata/lym/m2.py +++ b/testdata/lym/m2.py @@ -1,6 +1,7 @@ import os import inspect +import pya def lineno(): return inspect.currentframe().f_back.f_lineno diff --git a/testdata/oasis/duplicate_cellname.oas b/testdata/oasis/duplicate_cellname.oas new file mode 100644 index 000000000..0ce5df921 Binary files /dev/null and b/testdata/oasis/duplicate_cellname.oas differ diff --git a/testdata/python/layObjects.py b/testdata/python/layObjects.py index 695621ec6..1b58f4430 100644 --- a/testdata/python/layObjects.py +++ b/testdata/python/layObjects.py @@ -26,7 +26,7 @@ class LAYObjectsTests(unittest.TestCase): class MyBrowserSource(pya.BrowserSource): def get(self, url): next_url = "int:" + str(int(url.split(":")[1]) + 1) - return f"This is {url}. Goto next ({next_url})" + return "This is " + url + ". Goto next (" + next_url + ")" dialog = pya.BrowserDialog() dialog.home = "int:0" diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 1a0910a9d..f64dc910e 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -476,6 +476,48 @@ class DBEdges_TestClass < TestBase assert_equal((r2 | r1).width_check(60, true, RBA::Edges::Projection, nil, 50, nil).to_s, "(120,20;120,380)/(130,380;130,20)") assert_equal((r2 | r1).width_check(60, true, RBA::Edges::Projection, nil, nil, 50).to_s, "(50,200;50,220)/(100,400;100,0)") + # kissing corner/separation case + + r1 = RBA::Region::new + r1.insert(RBA::Box::new(0, 0, 100, 200)) + r1 = r1.edges + + r2 = RBA::Region::new + r2.insert(RBA::Box::new(100, 200, 200, 400)) + r2 = r2.edges + + r3a = RBA::Region::new + r3a.insert(RBA::Box::new(-10, 0, 110, 100)) + r3a = r3a.edges + + r3b = RBA::Region::new + r3b.insert(RBA::Box::new(10, 0, 90, 100)) + r3b = r3b.edges + + assert_equal(csort((r2 | r1).width_check(60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)/(100,200;100,260);(160,200;100,200)/(40,200;100,200)")) + assert_equal(csort((r2 | r1).width_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort((r2 | r1).width_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)/(100,200;100,260);(160,200;100,200)/(40,200;100,200)")) + + assert_equal(csort((r2 | r1).space_check(60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)/(100,200;100,260);(160,200;100,200)/(40,200;100,200)")) + assert_equal(csort((r2 | r1).space_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort((r2 | r1).space_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)/(100,200;100,260);(160,200;100,200)/(40,200;100,200)")) + + assert_equal(csort(r1.separation_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + assert_equal(csort(r1.separation_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.separation_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + + assert_equal(csort(r1.inside_check(r3b, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,0;0,0)/(90,0;10,0)")) + assert_equal(csort(r1.inside_check(r3b, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.inside_check(r3b, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::IncludeZeroDistanceWhenTouching).to_s), csort("(100,0;0,0)/(90,0;10,0)")) + + assert_equal(csort(r1.enclosing_check(r3a, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,0;0,0)/(110,0;-10,0)")) + assert_equal(csort(r1.enclosing_check(r3a, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.enclosing_check(r3a, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::IncludeZeroDistanceWhenTouching).to_s), csort("(100,0;0,0)/(110,0;-10,0)")) + + assert_equal(csort(r1.overlap_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + assert_equal(csort(r1.overlap_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.overlap_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, RBA::Edges::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + end # with.. diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index b5dbb9a50..bf5349645 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -23,6 +23,10 @@ end load("test_prologue.rb") +def mi2s(obj) + obj.each_meta_info.collect { |mi| mi.name + ":" + mi.value.to_s }.sort.join(";") +end + class DBLayoutTests2_TestClass < TestBase # LayerInfo @@ -1253,6 +1257,175 @@ class DBLayoutTests2_TestClass < TestBase end + # Cell#read and meta info (issue #1609) + def test_16 + + tmp = File::join($ut_testtmp, "test16.gds") + + ly1 = RBA::Layout::new + a = ly1.create_cell("A") + b = ly1.create_cell("B") + a.insert(RBA::DCellInstArray::new(b, RBA::Trans::new)) + + a.add_meta_info(RBA::LayoutMetaInfo::new("am1", 42.0, "", true)) + a.add_meta_info(RBA::LayoutMetaInfo::new("am2", "u", "", true)) + assert_equal(mi2s(a), "am1:42.0;am2:u") + + b.add_meta_info(RBA::LayoutMetaInfo::new("bm1", 17, "", true)) + assert_equal(mi2s(b), "bm1:17") + + ly1.add_meta_info(RBA::LayoutMetaInfo::new("lm1", -2.0, "", true)) + ly1.add_meta_info(RBA::LayoutMetaInfo::new("lm2", "v", "", true)) + assert_equal(mi2s(ly1), "lm1:-2.0;lm2:v") + + ly1.write(tmp) + + ly2 = RBA::Layout::new + top = ly2.create_cell("TOP") + a = ly2.create_cell("A") + c = ly2.create_cell("C") + top.insert(RBA::DCellInstArray::new(a, RBA::Trans::new)) + a.insert(RBA::DCellInstArray::new(c, RBA::Trans::new)) + + top.add_meta_info(RBA::LayoutMetaInfo::new("topm1", "abc")) + assert_equal(mi2s(top), "topm1:abc") + a.add_meta_info(RBA::LayoutMetaInfo::new("am1", "a number")) + a.add_meta_info(RBA::LayoutMetaInfo::new("am3", 0)) + assert_equal(mi2s(a), "am1:a number;am3:0") + c.add_meta_info(RBA::LayoutMetaInfo::new("cm1", 3)) + assert_equal(mi2s(c), "cm1:3") + + ly2.add_meta_info(RBA::LayoutMetaInfo::new("lm1", 5)) + assert_equal(mi2s(ly2), "lm1:5") + + a.read(tmp) + # not modified + assert_equal(mi2s(ly2), "lm1:5") + # merged + assert_equal(mi2s(a), "am1:42.0;am2:u;am3:0") + # not modified + assert_equal(mi2s(c), "cm1:3") + + b2 = ly2.cell("B") + # imported + assert_equal(mi2s(b2), "bm1:17") + + puts "done." + + end + + # Layout, meta info diverse + def test_17 + + manager = RBA::Manager::new + + ly = RBA::Layout::new(manager) + a = ly.create_cell("A") + + manager.transaction("trans") + ly.add_meta_info(RBA::LayoutMetaInfo::new("lm1", 17)) + a.add_meta_info(RBA::LayoutMetaInfo::new("am1", "u")) + manager.commit + + assert_equal(mi2s(ly), "lm1:17") + assert_equal(mi2s(a), "am1:u") + + manager.undo + assert_equal(mi2s(ly), "") + assert_equal(mi2s(a), "") + + manager.redo + assert_equal(mi2s(ly), "lm1:17") + assert_equal(mi2s(a), "am1:u") + + manager.transaction("trans") + ly.add_meta_info(RBA::LayoutMetaInfo::new("lm1", 117)) + a.add_meta_info(RBA::LayoutMetaInfo::new("am1", "v")) + manager.commit + + assert_equal(mi2s(ly), "lm1:117") + assert_equal(mi2s(a), "am1:v") + + manager.undo + assert_equal(mi2s(ly), "lm1:17") + assert_equal(mi2s(a), "am1:u") + + manager.redo + assert_equal(mi2s(ly), "lm1:117") + assert_equal(mi2s(a), "am1:v") + + manager.undo + assert_equal(mi2s(ly), "lm1:17") + assert_equal(mi2s(a), "am1:u") + + manager.transaction("trans") + ly.remove_meta_info("lm1") + a.remove_meta_info("am1") + a.remove_meta_info("doesnotexist") + manager.commit + + assert_equal(mi2s(ly), "") + assert_equal(mi2s(a), "") + + manager.undo + assert_equal(mi2s(ly), "lm1:17") + assert_equal(mi2s(a), "am1:u") + + manager.transaction("trans") + ly.clear_all_meta_info + manager.commit + + assert_equal(mi2s(ly), "") + assert_equal(mi2s(a), "") + + manager.undo + assert_equal(mi2s(ly), "lm1:17") + assert_equal(mi2s(a), "am1:u") + + manager.redo + assert_equal(mi2s(ly), "") + assert_equal(mi2s(a), "") + + ly2 = RBA::Layout::new + ly.add_meta_info(RBA::LayoutMetaInfo::new("lm1", 17)) + ly2.add_meta_info(RBA::LayoutMetaInfo::new("lm2", 42)) + assert_equal(mi2s(ly), "lm1:17") + ly.merge_meta_info(ly2) + assert_equal(mi2s(ly), "lm1:17;lm2:42") + ly.copy_meta_info(ly2) + assert_equal(mi2s(ly), "lm2:42") + + a = ly.create_cell("A") + a.add_meta_info(RBA::LayoutMetaInfo::new("am1", "u")) + b = ly2.create_cell("B") + b.add_meta_info(RBA::LayoutMetaInfo::new("bm1", "v")) + + assert_equal(mi2s(a), "am1:u") + a.merge_meta_info(b) + assert_equal(mi2s(a), "am1:u;bm1:v") + a.copy_meta_info(b) + assert_equal(mi2s(a), "bm1:v") + + ly = RBA::Layout::new + ly2 = RBA::Layout::new + + a = ly.create_cell("A") + a.add_meta_info(RBA::LayoutMetaInfo::new("am1", "u")) + ly2.create_cell("X") + b = ly2.create_cell("B") + b.add_meta_info(RBA::LayoutMetaInfo::new("bm1", "v")) + + cm = RBA::CellMapping::new + cm.map(b.cell_index, a.cell_index) + + assert_equal(mi2s(a), "am1:u") + ly.merge_meta_info(ly2, cm) + assert_equal(mi2s(a), "am1:u;bm1:v") + ly.copy_meta_info(ly2, cm) + assert_equal(mi2s(a), "bm1:v") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index de204bc3d..d08f161fa 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -550,6 +550,44 @@ class DBRegion_TestClass < TestBase assert_equal((r1 | r2).merged.width_check(60, true, RBA::Region::Projection, nil, 50, nil).to_s, "(120,20;120,380)|(130,380;130,20)") assert_equal((r1 | r2).merged.width_check(60, true, RBA::Region::Projection, nil, nil, 50).to_s, "(50,200;50,220)|(100,400;100,0)") + # kissing corner/separation case + + r1 = RBA::Region::new + r1.insert(RBA::Box::new(0, 0, 100, 200)) + + r2 = RBA::Region::new + r2.insert(RBA::Box::new(100, 200, 200, 400)) + + r3a = RBA::Region::new + r3a.insert(RBA::Box::new(-10, 0, 110, 100)) + + r3b = RBA::Region::new + r3b.insert(RBA::Box::new(10, 0, 90, 100)) + + assert_equal(csort((r2 | r1).width_check(60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)|(100,200;100,260);(40,200;100,200)|(160,200;100,200)")) + assert_equal(csort((r2 | r1).width_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, true, false, RBA::Region::IgnoreProperties, RBA::Region::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort((r2 | r1).width_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, true, false, RBA::Region::IgnoreProperties, RBA::Region::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)|(100,200;100,260);(40,200;100,200)|(160,200;100,200)")) + + assert_equal(csort((r2 | r1).space_check(60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)|(100,200;100,260);(40,200;100,200)|(160,200;100,200)")) + assert_equal(csort((r2 | r1).space_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort((r2 | r1).space_check(60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)|(100,200;100,260);(40,200;100,200)|(160,200;100,200)")) + + assert_equal(csort(r1.separation_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + assert_equal(csort(r1.separation_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.separation_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + + assert_equal(csort(r1.inside_check(r3b, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,0;0,0)/(90,0;10,0)")) + assert_equal(csort(r1.inside_check(r3b, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.inside_check(r3b, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::IncludeZeroDistanceWhenTouching).to_s), csort("(100,0;0,0)/(90,0;10,0)")) + + assert_equal(csort(r1.enclosing_check(r3a, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,0;0,0)/(110,0;-10,0)")) + assert_equal(csort(r1.enclosing_check(r3a, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.enclosing_check(r3a, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::IncludeZeroDistanceWhenTouching).to_s), csort("(100,0;0,0)/(110,0;-10,0)")) + + assert_equal(csort(r1.overlap_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + assert_equal(csort(r1.overlap_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::NeverIncludeZeroDistance).to_s), csort("")) + assert_equal(csort(r1.overlap_check(r2, 60, false, RBA::Edges::Euclidian, nil, nil, nil, true, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::IgnoreProperties, RBA::Region::IncludeZeroDistanceWhenTouching).to_s), csort("(100,200;100,140)/(100,200;100,260);(40,200;100,200)/(160,200;100,200)")) + end # Others @@ -1188,6 +1226,36 @@ class DBRegion_TestClass < TestBase end + # rasterize + def test_rasterize + + r = RBA::Region::new() + r.insert(RBA::Polygon::new([[0, 0], [100, 100], [150, 0]])) + r.insert(RBA::Polygon::new(RBA::Box::new([0, 200], [100, 300]))) + + pd = RBA::Vector::new(50, 50) + ps = RBA::Vector::new(25, 25) + + sum = 0 + 2.times do |ix| + 2.times do |iy| + am = r.rasterize(RBA::Point::new(-50 + ix * ps.x, -20 + iy * ps.y), pd, ps, 7, 7) + sum += am.collect { |r| r.inject(:+) }.inject(:+) + end + end + + assert_equal("%.12g" % sum, "%.12g" % (7.0 * pd.x * pd.y)) + + tot = 0.0 + pd = RBA::Vector::new(50, 50) + + am = r.rasterize(RBA::Point::new(-50, -20), pd, 7, 7) + sum = am.collect { |r| r.inject(:+) }.inject(:+) + + assert_equal("%.12g" % sum, "%.12g" % (7.0 * pd.x * pd.y)) + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbTechnologies.rb b/testdata/ruby/dbTechnologies.rb index 23b932291..d1dc9cf93 100644 --- a/testdata/ruby/dbTechnologies.rb +++ b/testdata/ruby/dbTechnologies.rb @@ -103,6 +103,13 @@ END tech.dbu = 5.0 assert_equal(tech.dbu, 5.0) + tech.default_grids = [] + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "") + tech.default_grids = [0.001, 0.01, 0.2] + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.001,0.01,0.2") + tech.default_grids = [1] + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "1") + tech.default_base_path = "/default/path" assert_equal(tech.default_base_path, "/default/path") assert_equal(tech.base_path, "/default/path") diff --git a/testdata/ruby/layMainWindow.rb b/testdata/ruby/layMainWindow.rb index ba4d0f35a..81ba66439 100644 --- a/testdata/ruby/layMainWindow.rb +++ b/testdata/ruby/layMainWindow.rb @@ -49,6 +49,10 @@ class LAYMainWindow_TestClass < TestBase # smoke test + if !RBA.constants.member?(:Application) + return + end + app = RBA::Application.instance mw = app.main_window s = mw.synchronous diff --git a/version.sh b/version.sh index 8d5021453..9163a6f1d 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.28.15" +KLAYOUT_VERSION="0.28.17" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.28.15" +KLAYOUT_PYPI_VERSION="0.28.17" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")