diff --git a/Changelog b/Changelog
index 31fd56782..5a9b54af4 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,26 @@
+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
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/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
-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 c8f124c5c..ce8c3aa4a 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
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/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
index 40b93cbfe..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).
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/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/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
index b87a413c1..f9e4bfefc 100644
--- a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
+++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
@@ -602,3 +602,21 @@ TEST(Bug_1474)
EXPECT_EQ (ex.msg (), "Cell named ADDHX2 with ID 4 was already given name SEDFFTRX2 (position=763169, cell=)");
}
}
+
+TEST(DuplicateCellname)
+{
+ db::Manager m (false);
+ db::Layout layout (&m);
+
+ try {
+ tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas");
+ db::OASISReader reader (file);
+ reader.read (layout);
+ EXPECT_EQ (false, true);
+ } catch (tl::CancelException &ex) {
+ // Seen when private test data is not installed
+ throw;
+ } catch (tl::Exception &ex) {
+ EXPECT_EQ (ex.msg (), "Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)");
+ }
+}
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/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/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/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb
index e0abb515b..d08f161fa 100644
--- a/testdata/ruby/dbRegionTest.rb
+++ b/testdata/ruby/dbRegionTest.rb
@@ -1226,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/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")