diff --git a/Changelog b/Changelog
index bba18b673..2ff284e28 100644
--- a/Changelog
+++ b/Changelog
@@ -1,8 +1,66 @@
-0.26 (2018-06-23):
-* Enhancement: Standalone Python modules provide
+0.26 (xxxx-xx-xx):
+* Enhancement: Standalone Python modules provided
For details see:
https://github.com/klayoutmatthias/klayout/wiki/klayout---Standalone-KLayout-Python-Module
+0.25.5 (xxxx-xx-xx):
+
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/162
+ GDS2 LIBNAME was not maintained on "File/Save".
+
+0.25.4 (2018-08-25):
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/121
+ Issue with multiple reads of GDS2 layouts including PCells
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/134
+ Error in cell.fill_region caused by big polygon with
+ spikes
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/139
+ Libraries have not been reassigned when loading a GDS file
+ from command line (does not happen on File/Open)
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/141
+ Issue with RBA::QHostAddress (ambiguous overload) on Qt5
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/142
+ Issue with RBA::RecursiveShapeIterator#region=
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/144
+ The Salt package descriptions are not shown with Motif
+ style
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/148
+ Wrong font is used
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/152
+ Shapes#size reported a wrong shape count in viewer mode
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/153
+ Application crash when editing guiding shape properties
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/155
+ Program freezes after replacing nothing by something in
+ Macro editor
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/157
+ "Replace cell with ..." rejected cell names with a library
+ prefix
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/158
+ Repaint issue on cell context
+* Bugfix: https://github.com/klayoutmatthias/klayout/issues/159
+ Tech specific macros and DRC scripts were not shown in tech
+ manager
+* Bugfix: 8 bit indexed GIF images can be used for package icons now
+* Enhancement: Provide a way to specify the type of a macro
+ This feature is mainly useful for command line arguments.
+ If you run KLayout with "klayout -b -r myscript"
+ it will not be able to determine the type of macro without
+ a suffix.
+
+ You can now explicitly specify a certain type by
+ giving the suffix implicitly like: "klayout -b -r myscript[rb]"
+ This will read "myscript" but pretend it was "myscript.rb"
+ and execute it as Ruby script.
+
+ This feature is handy if you need to run a file with a
+ specific interpreter but cannot modify the file name.
+* Enhancement: Selection now shows PCell display names
+ Before, the internal name was shown for instances
+* Enhancement: There is an option in the View menu to show or hide markers
+ Markers may hide layout under them. With this feature you can
+ quickly disable all markers and the layout becomes visible.
+
0.25.3 (2018-05-03):
* Enhancement: Compile option to use libcurl instead of QtNetwork
diff --git a/Changelog.Debian b/Changelog.Debian
index 47de11253..979733773 100644
--- a/Changelog.Debian
+++ b/Changelog.Debian
@@ -1,9 +1,9 @@
-klayout (0.26-1) unstable; urgency=low
+klayout (0.25.4-1) unstable; urgency=low
* New features and bugfixes
- See changelog
- -- Matthias Köfferlein Sun, 24 Jun 2018 15:43:26 +0200
+ -- Matthias Köfferlein Sat, 25 Aug 2018 23:15:23 +0200
klayout (0.25.3-1) unstable; urgency=low
diff --git a/macbuild/ReadMe.md b/macbuild/ReadMe.md
index 65be120bf..f03e3ca97 100644
--- a/macbuild/ReadMe.md
+++ b/macbuild/ReadMe.md
@@ -97,6 +97,9 @@ Homebrew's installation of python3 (`brew install python3`) places a `Python.fra
```
# Build step
./build4mac.py -p B36 -q Qt5Brew
+# build with log
+./build4mac.py -p B36 -q Qt5Brew 2>&1 | tee qt5.build.macos-HighSierra-release-version.log
+
# Deploy step
./build4mac.py -p B36 -q Qt5Brew -y # normal deploy
diff --git a/macbuild/Resources/klayout_console b/macbuild/Resources/klayout_console
new file mode 100644
index 000000000..2a4319e34
--- /dev/null
+++ b/macbuild/Resources/klayout_console
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+if [ "$1" != "" ]; then
+/Applications/klayout.app/Contents/MacOS/klayout $1 -r /Applications/klayout.app/Contents/MacOS/start-console.py
+else
+/Applications/klayout.app/Contents/MacOS/klayout -z -r /Applications/klayout.app/Contents/MacOS/start-console.py
+fi
diff --git a/macbuild/Resources/start-console.py b/macbuild/Resources/start-console.py
new file mode 100644
index 000000000..5880e2eed
--- /dev/null
+++ b/macbuild/Resources/start-console.py
@@ -0,0 +1,16 @@
+#!/Applications/klayout.app/Contents/MacOS/klayout -b -r
+import readline
+import code
+import sys
+import os
+pwd = os.getcwd()
+sys.path.append(pwd)
+
+variables = globals().copy()
+variables.update(locals())
+shell = code.InteractiveConsole(variables)
+cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
+banner = "Python %s on %s\n%s\n(%s)" % (sys.version, sys.platform,
+ cprt, "KLayout Python Console")
+exit_msg = 'now exiting %s...' % "KLayout Python Console"
+shell.interact(banner, exit_msg)
diff --git a/macbuild/build4mac.py b/macbuild/build4mac.py
index e9200c88a..907226d2e 100755
--- a/macbuild/build4mac.py
+++ b/macbuild/build4mac.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===============================================================================
@@ -125,7 +125,7 @@ def SetGlobals():
ModulePython = "PythonYosemite"
elif Platform == "ElCapitan":
ModuleRuby = "RubyElCapitan"
- ModulePytyon = "PythonElCapitan"
+ ModulePython = "PythonElCapitan"
elif Platform == "Sierra":
ModuleRuby = "RubySierra"
ModulePython = "PythonSierra"
@@ -538,8 +538,8 @@ def DeployBinariesForBundle():
if not DeploymentF and not DeploymentP:
return 1
if DeploymentF and NonOSStdLang:
- print( "WARNING!!! You chose <-y|--deploy> while using non-OS-standard script language.", file=sys.stderr )
- print( " Use <-Y|--DEPLOY> instead", file=sys.stderr )
+ print( " WARNING!!! You chose <-y|--deploy> while using non-OS-standard script language.", file=sys.stderr )
+ print( " Consider using <-Y|--DEPLOY> instead", file=sys.stderr )
#return 1
if not os.path.isfile(MacBuildLog):
print( "!!! Build log file <%s> does not present !!!" % MacBuildLog, file=sys.stderr )
@@ -756,7 +756,6 @@ def DeployBinariesForBundle():
os.chdir(ProjectDir)
os.chdir(MacPkgDir)
command = "%s %s %s" % ( deploytool, app_bundle, options )
- print(command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to deploy applications on OSX !!!"
print( msg, file=sys.stderr )
@@ -764,43 +763,111 @@ def DeployBinariesForBundle():
os.chdir(ProjectDir)
return 1
- deploymentPython = False
- if deploymentPython:
- # TODO Code incomplete! To be finished.
- from macbuild.build4mac_util import WalkFrameworkPaths, PerformChanges, DetectChanges
-
- bundlePath = 'qt5.pkg.macos-HighSierra-release/klayout.app'
- bundleExecPathAbs = os.getcwd() + '/%s/Contents/MacOS/' % bundlePath
-
- # rsync -a --safe-links /usr/local/opt/python/Frameworks/Python.framework/ qt5.pkg.macos-HighSierra-release/klayout.app/Contents/Frameworks/Python.framework
- # cp -RL /usr/local/opt/python/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages qt5.pkg.macos-HighSierra-release/klayout.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/
- # rm -rf qt5.pkg.macos-HighSierra-release/klayout.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/test
+ deploymentPython = True
+ if deploymentPython and NonOSStdLang:
+ from build4mac_util import WalkFrameworkPaths, PerformChanges, DetectChanges
+ bundlePath = AbsMacPkgDir + '/klayout.app'
+ # bundlePath = os.getcwd() + '/qt5.pkg.macos-HighSierra-release/klayout.app'
+ bundleExecPathAbs = '%s/Contents/MacOS/' % bundlePath
+ pythonOriginalFrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework'
pythonFrameworkPath = '%s/Contents/Frameworks/Python.framework' % bundlePath
+
+ print(" [8.1] Deploying Python from %s ..." % pythonOriginalFrameworkPath)
+ print(" [1] Copying Python Framework")
+ shell_commands = list()
+ shell_commands.append("rm -rf %s" % pythonFrameworkPath)
+ shell_commands.append("rsync -a --safe-links %s/ %s" % (pythonOriginalFrameworkPath, pythonFrameworkPath))
+ shell_commands.append("mkdir %s/Versions/3.7/lib/python3.7/site-packages/" % pythonFrameworkPath)
+ shell_commands.append("cp -RL %s/Versions/3.7/lib/python3.7/site-packages/{pip*,pkg_resources,setuptools*,wheel*} " % pythonOriginalFrameworkPath +
+ "%s/Versions/3.7/lib/python3.7/site-packages/" % pythonFrameworkPath)
+ shell_commands.append("rm -rf %s/Versions/3.7/lib/python3.7/test" % pythonFrameworkPath)
+ shell_commands.append("rm -rf %s/Versions/3.7/Resources" % pythonFrameworkPath)
+ shell_commands.append("rm -rf %s/Versions/3.7/bin" % pythonFrameworkPath)
+
+ for command in shell_commands:
+ if subprocess.call( command, shell=True ) != 0:
+ msg = "command failed: %s"
+ print( msg % command, file=sys.stderr )
+ exit(1)
+
+ shutil.copy2( sourceDir2 + "/start-console.py", targetDirM )
+ shutil.copy2( sourceDir2 + "/klayout_console", targetDirM )
+ os.chmod( targetDirM + "/klayout_console", 0o0755 )
+
+ print(" [2] Relinking dylib dependencies inside Python.framework")
+ print(" [2.1] Patching Python Framework")
depdict = WalkFrameworkPaths(pythonFrameworkPath)
-
- pythonOriginalFrameworkPath = '/usr/local/opt/python/Frameworks/Python.framework'
appPythonFrameworkPath = '@executable_path/../Frameworks/Python.framework/'
- PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath)], bundleExecPathAbs)
-
- klayoutPath = bundleExecPathAbs
- depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$')
- PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath)], bundleExecPathAbs)
-
- klayoutPath = bundleExecPathAbs + '../Frameworks'
- depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'libklayout')
- PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath)], bundleExecPathAbs)
-
+ PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
+ print(" [2.2] Patching /usr/local/opt/ libs")
usrLocalPath = '/usr/local/opt/'
appUsrLocalPath = '@executable_path/../Frameworks/'
- depdict = WalkFrameworkPaths(pythonFrameworkPath)
- PerformChanges(depdict, [(usrLocalPath, appUsrLocalPath)], bundleExecPathAbs)
+ replacePairs = [(usrLocalPath, appUsrLocalPath, True)]
+ depdict = WalkFrameworkPaths(pythonFrameworkPath, search_path_filter=r'\t+/usr/local/(opt|Cellar)')
+ PerformChanges(depdict, replacePairs, bundleExecPathAbs)
- # usrLocalPath = '/usr/local/lib/'
- # appUsrLocalPath = '@executable_path/../Frameworks/'
- # depdict = WalkFrameworkPaths(pythonFrameworkPath)
- # PerformChanges(depdict, [(usrLocalPath, appUsrLocalPath)], bundleExecPathAbs)
+ print(" [2.3] Patching openssl, gdbm, readline, sqlite, tcl-tk, xz")
+ usrLocalPath = '/usr/local/opt'
+ appUsrLocalPath = '@executable_path/../Frameworks/'
+ replacePairs = [(usrLocalPath, appUsrLocalPath, True)]
+ replacePairs.extend([(openssl_version, '@executable_path/../Frameworks/openssl', True)
+ for openssl_version in glob.glob('/usr/local/Cellar/openssl/*')])
+ depdict = WalkFrameworkPaths([pythonFrameworkPath + '/../openssl',
+ pythonFrameworkPath + '/../gdbm',
+ pythonFrameworkPath + '/../readline',
+ pythonFrameworkPath + '/../sqlite',
+ pythonFrameworkPath + '/../tcl-tk',
+ pythonFrameworkPath + '/../xz'], search_path_filter=r'\t+/usr/local/(opt|Cellar)')
+
+ PerformChanges(depdict, replacePairs, bundleExecPathAbs)
+
+ print(" [3] Relinking dylib dependencies for klayout")
+ klayoutPath = bundleExecPathAbs
+ depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$')
+ PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
+
+ libKlayoutPath = bundleExecPathAbs + '../Frameworks'
+ depdict = WalkFrameworkPaths(libKlayoutPath, filter_regex=r'libklayout')
+ PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
+
+ print(" [4] Patching site.py, pip/, and distutils/")
+ site_module = "%s/Versions/3.7/lib/python3.7/site.py" % pythonFrameworkPath
+ with open(site_module, 'r') as site:
+ buf = site.readlines()
+ with open(site_module, 'w') as site:
+ import re
+ for line in buf:
+ # This will fool pip into thinking it's inside a virtual environment
+ # and install new packates to the correct site-packages
+ if re.match("^PREFIXES", line) is not None:
+ line = line + "sys.real_prefix = sys.prefix\n"
+ # do not allow installation in the user folder.
+ if re.match("^ENABLE_USER_SITE", line) is not None:
+ line = "ENABLE_USER_SITE = False\n"
+ site.write(line)
+
+ pip_module = "%s/Versions/3.7/lib/python3.7/site-packages/pip/__init__.py" % pythonFrameworkPath
+ with open(pip_module, 'r') as pip:
+ buf = pip.readlines()
+ with open(pip_module, 'w') as pip:
+ import re
+ for line in buf:
+ # this will reject user's configuration of pip, forcing the isolated mode
+ line = re.sub("return isolated$", "return isolated or True", line)
+ pip.write(line)
+
+ distutilsconfig = "%s/Versions/3.7/lib/python3.7/distutils/distutils.cfg" % pythonFrameworkPath
+ with open(distutilsconfig, 'r') as file:
+ buf = file.readlines()
+ with open(distutilsconfig, 'w') as file:
+ import re
+ for line in buf:
+ # This will cause all packages to be installed to sys.prefix
+ if re.match('prefix=', line) is not None:
+ continue
+ file.write(line)
else:
print( " [8] Skipped deploying Qt's Frameworks ..." )
@@ -925,9 +992,7 @@ def main():
#----------------------------------------------------------
ret = RunMainBuildBash()
if not DeploymentF and not DeploymentP:
- if ret == 0:
- sys.exit(0)
- else:
+ if not ret == 0:
sys.exit(1)
else:
#----------------------------------------------------------
@@ -944,6 +1009,7 @@ def main():
# to make "KLayoutEditor.app" and "KLayoutViewer.app"
#----------------------------------------------------------
ret2 = DeployScriptBundles()
+
if not ret2 == 0:
sys.exit(1)
else:
diff --git a/macbuild/build4mac_util.py b/macbuild/build4mac_util.py
index c9372dd19..17aa26a2c 100755
--- a/macbuild/build4mac_util.py
+++ b/macbuild/build4mac_util.py
@@ -18,14 +18,6 @@ import string
import subprocess
import shutil
-CAN_DEPLOY_PYTHON = False
-try:
- from pathlib import Path
- Path('~').expanduser()
- CAN_DEPLOY_PYTHON = True
-except (ImportError,AttributeError): # python2
- print("Warning: Cannot import pathlib, use python3 if you need python deployment.")
-
#-------------------------------------------------------------------------------
## To import global dictionaries of different modules
#-------------------------------------------------------------------------------
@@ -181,176 +173,167 @@ def SetChangeLibIdentificationName( executable, relativedir ):
# for-lib
return 0
-# TODO: undocumented
-if CAN_DEPLOY_PYTHON:
- def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'):
- NOTHINGTODO = [] # return empty list if nothing to do.
- dylibPath = str(Path(dylibPath))
- cmdNameId = XcodeToolChain['nameID']
- cmdNameChg = XcodeToolChain['nameCH']
- otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
- otoolOut = os.popen( otoolCm ).read()
- exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
- keys = exedepdic.keys()
- deplibs = exedepdic[ list(keys)[0] ]
+def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'):
+ NOTHINGTODO = [] # return empty list if nothing to do.
+ cmdNameId = XcodeToolChain['nameID']
+ cmdNameChg = XcodeToolChain['nameCH']
+ otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
+ otoolOut = os.popen( otoolCm ).read()
+ exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
+ keys = exedepdic.keys()
+ deplibs = exedepdic[ list(keys)[0] ]
- if depth < 5:
- if len(deplibs) > 0:
- for idx, lib in enumerate(deplibs):
- lib = str(Path(lib))
- if lib != list(keys)[0]:
- deplibs[idx] = WalkLibDependencyTree(lib, depth+1, filter_regex)
- else:
- return NOTHINGTODO
- if depth == 0:
- return deplibs
- return exedepdic
+ if depth < 5:
+ if len(deplibs) > 0:
+ for idx, lib in enumerate(deplibs):
+ lib = str(lib)
+ if lib != list(keys)[0]:
+ deplibs[idx] = WalkLibDependencyTree(lib, depth+1, filter_regex)
+ if depth == 0:
+ return deplibs
+ return exedepdic
+ else:
+ raise RuntimeError("Exceeded maximum recursion depth.")
+
+def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$', search_path_filter=r'\t+/usr/local/opt'):
+
+ if isinstance(frameworkPaths, str):
+ frameworkPathsIter = [frameworkPaths]
+ else:
+ frameworkPathsIter = frameworkPaths
+
+ dependency_dict = dict()
+
+ for frameworkPath in frameworkPathsIter:
+ # print("Calling:", 'find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex))
+ find_grep_results = os.popen('find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex)).read().split('\n')
+ framework_files = filter(lambda x: x != '',
+ map(lambda x: x.strip(),
+ find_grep_results))
+
+ dependency_dict[frameworkPath] = list()
+ for idx, file in enumerate(framework_files):
+ dict_file = {file: WalkLibDependencyTree(file, filter_regex=search_path_filter)}
+ dependency_dict[frameworkPath].append(dict_file)
+ return dependency_dict
+
+def WalkDictTree(dependencyDict, visited_files):
+ libNameChanges = list()
+ for lib, dependencies in dependencyDict.items():
+ if lib in visited_files:
+ continue
+
+ dependency_list = list()
+ if isinstance(dependencies, list):
+ for deplib in dependencies:
+ if isinstance(deplib, str):
+ dependency_list.append(deplib)
+ if deplib not in visited_files:
+ visited_files.append(deplib)
+ elif isinstance(deplib, dict):
+ dependency_list.append(next(iter(deplib)))
+ libNameChanges.extend(WalkDictTree(deplib, visited_files))
+ else:
+ #raise RuntimeError("Unexpected value: %s" % deplib)
+ pass
else:
- raise RuntimeError("Exceeded maximum recursion depth.")
+ raise RuntimeError("Unexpected value: %s" % dependencies)
+ if len(dependency_list) > 0:
+ libNameChanges.append((lib, dependency_list))
+ else:
+ libNameChanges.append((lib, ))
+ visited_files.append(lib)
+ return libNameChanges
- def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$'):
- try:
- frameworkPathsIter = iter(frameworkPaths)
- except TypeError:
- frameworkPathsIter = [frameworkPaths]
- dependency_dict = dict()
+def FindFramework(path, root_path):
+ relPath = os.path.relpath(path, root_path)
+ return os.path.join(root_path, relPath.split(os.sep)[0])
+def ResolveExecutablePath(path, executable_path):
+ """ Transforms @executable_path into executable_path"""
+ p = path.replace("@executable_path", "/%s/" % executable_path)
+ return p
- for frameworkPath in frameworkPaths:
- frameworkPath = str(Path(frameworkPath))
- find_grep_results = os.popen('find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex)).read().split('\n')
- framework_files = filter(lambda x: x != '',
- map(lambda x: x.strip(),
- find_grep_results))
+def DetectChanges(frameworkDependencyDict):
+ visited_files = list()
+ libNameChanges = list()
+ for framework, libraries in frameworkDependencyDict.items():
+ for libraryDict in libraries:
+ libNameChanges.extend(WalkDictTree(libraryDict, visited_files))
+ # Changes are stored in libNameChanges in the form of ('lib.dylib', ['dep1.dylib', ...])
- dependency_dict[frameworkPath] = list()
- for idx, file in enumerate(framework_files):
- dict_file = {file: WalkLibDependencyTree(file)}
- dependency_dict[frameworkPath].append(dict_file)
- return dependency_dict
+ return libNameChanges
- def WalkDictTree(dependencyDict, visited_files):
- libNameChanges = list()
- for lib, dependencies in dependencyDict.items():
- if lib in visited_files:
- continue
+def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout"):
+ libNameChanges = DetectChanges(frameworkDependencyDict)
+ cmdNameId = XcodeToolChain['nameID']
+ cmdNameChg = XcodeToolChain['nameCH']
- dependency_list = list()
- if isinstance(dependencies, list):
- for deplib in dependencies:
- if isinstance(deplib, str):
- dependency_list.append(deplib)
- if deplib not in visited_files:
- visited_files.append(deplib)
- elif isinstance(deplib, dict):
- dependency_list.append(str(next(iter(deplib))))
- libNameChanges.extend(WalkDictTree(deplib, visited_files))
+ if replaceFromToPairs is not None:
+ for libNameChange in libNameChanges:
+ libNameChangeIterator = iter(libNameChange)
+ lib = next(libNameChangeIterator)
+ try:
+ dependencies = next(libNameChangeIterator)
+ except StopIteration:
+ dependencies = list()
+ for replaceFrom, replaceTo, libdir in replaceFromToPairs:
+ fileName = ResolveExecutablePath(lib.replace(replaceFrom, replaceTo), executable_path)
+ if fileName.startswith('/usr'):
+ # print(f'skipping fileName: {fileName}')
+ continue
+
+ if lib.find(replaceFrom) >= 0:
+ if libdir:
+ frameworkPath = FindFramework(lib, replaceFrom)
else:
- #raise RuntimeError("Unexpected value: %s" % deplib)
- pass
- else:
- raise RuntimeError("Unexpected value: %s" % dependencies)
- if len(dependency_list) > 0:
- libNameChanges.append((lib, dependency_list))
- else:
- libNameChanges.append((lib, ))
- visited_files.append(lib)
- return libNameChanges
+ frameworkPath = lib
+ destFrameworkPath = frameworkPath.replace(replaceFrom, replaceTo)
+ destFrameworkPath = ResolveExecutablePath(destFrameworkPath, executable_path)
- def FindFramework(path, root_path):
- path = Path(path)
- root_path = Path(root_path)
- relPath = path.relative_to(root_path)
- return str(root_path / relPath.parts[0])
+ if not os.path.exists(fileName):
+ print (lib.replace(replaceFrom, replaceTo), "DOES NOT EXIST")
+ print ("COPY", frameworkPath, " -> ", destFrameworkPath)
+ shutil.copytree(frameworkPath, destFrameworkPath)
- def ReplaceExecutablePath(path, executable_path):
- executable_path = str(executable_path)
- p = Path(str(path).replace("@executable_path", "/%s/" % executable_path))
- return p
+ nameId = lib.replace(replaceFrom, replaceTo)
+ command = "%s %s %s" % ( cmdNameId, nameId, fileName )
+ if not os.access(fileName, os.W_OK):
+ command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
+ # print("\t%s" % command)
+ if subprocess.call( command, shell=True ) != 0:
+ msg = "!!! Failed to set the new identification name to <%s> !!!"
+ print( msg % fileName, file=sys.stderr )
+ return 1
- def FileExists(file_path, executable_path):
- p = ReplaceExecutablePath(file_path, executable_path)
- return p.exists()
+ for dependency in dependencies:
+ if dependency.find(replaceFrom) >= 0:
+ print("\tIn:", fileName)
+ print("\tRENAME", dependency, " -> ", dependency.replace(replaceFrom, replaceTo))
- def DetectChanges(frameworkDependencyDict):
- visited_files = list()
- libNameChanges = list()
- for framework, libraries in frameworkDependencyDict.items():
- for libraryDict in libraries:
- libNameChanges.extend(WalkDictTree(libraryDict, visited_files))
- # Changes are stored in libNameChanges in the form of ('lib.dylib', ['dep1.dylib', ...])
-
- return libNameChanges
-
- def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout", libdir=False):
- libNameChanges = DetectChanges(frameworkDependencyDict)
- cmdNameId = XcodeToolChain['nameID']
- cmdNameChg = XcodeToolChain['nameCH']
-
- if replaceFromToPairs is not None:
- for libNameChange in libNameChanges:
- libNameChangeIterator = iter(libNameChange)
- lib = next(libNameChangeIterator)
- try:
- dependencies = next(libNameChangeIterator)
- except StopIteration:
- dependencies = list()
- for replaceFrom, replaceTo in replaceFromToPairs:
- replaceFrom = str(Path(replaceFrom))
- replaceTo = str(Path(replaceTo))
-
- if lib.find(replaceFrom) >= 0:
- if libdir:
- frameworkPath = FindFramework(lib, replaceFrom)
- else:
- frameworkPath = lib
- destFrameworkPath = frameworkPath.replace(replaceFrom, replaceTo)
- destFrameworkPath = ReplaceExecutablePath(destFrameworkPath, executable_path)
- if not FileExists(lib.replace(replaceFrom, replaceTo), executable_path):
- print (lib.replace(replaceFrom, replaceTo), "DOES NOT EXIST")
- print ("COPY", frameworkPath, " -> ", destFrameworkPath)
- shutil.copytree(frameworkPath, destFrameworkPath)
-
- fileName = ReplaceExecutablePath(lib.replace(replaceFrom, replaceTo), executable_path)
- nameId = lib.replace(replaceFrom, replaceTo)
- command = "%s %s %s" % ( cmdNameId, nameId, fileName )
- if not os.access(fileName, os.W_OK):
+ # Try changing id first
+ nameId = dependency.replace(replaceFrom, replaceTo)
+ command = "%s %s %s" % ( cmdNameId, nameId, fileName)
+ if not os.access(str(fileName), os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
- print("\t%s" % command)
+ # print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
- fileName = ReplaceExecutablePath(lib.replace(replaceFrom, replaceTo), executable_path)
- for dependency in dependencies:
- if dependency.find(replaceFrom) >= 0:
- print("In:", fileName)
- print("\tRENAME", dependency, " -> ", dependency.replace(replaceFrom, replaceTo))
+ # Rename dependencies
+ nameOld = dependency
+ nameNew = dependency.replace(replaceFrom, replaceTo)
+ command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, str(fileName) )
+ if not os.access(str(fileName), os.W_OK):
+ command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
- # Try changing id first
- nameId = dependency.replace(replaceFrom, replaceTo)
- command = "%s %s %s" % ( cmdNameId, nameId, fileName )
- if not os.access(fileName, os.W_OK):
- command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
- print("\t%s" % command)
- if subprocess.call( command, shell=True ) != 0:
- msg = "!!! Failed to set the new identification name to <%s> !!!"
- print( msg % fileName, file=sys.stderr )
- return 1
-
- # Rename dependencies
- nameOld = dependency
- nameNew = dependency.replace(replaceFrom, replaceTo)
- command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, fileName )
- if not os.access(fileName, os.W_OK):
- command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
-
- print("\t%s" % command)
- if subprocess.call( command, shell=True ) != 0:
- msg = "!!! Failed to set the new identification name to <%s> !!!"
- print( msg % fileName, file=sys.stderr )
- return 1
+ # print("\t%s" % command)
+ if subprocess.call( command, shell=True ) != 0:
+ msg = "!!! Failed to set the new identification name to <%s> !!!"
+ print( msg % fileName, file=sys.stderr )
+ return 1
#------------------------------------------------------------------------------
diff --git a/scripts/makedeb.sh b/scripts/makedeb.sh
index 2ced7ad03..10cda0a29 100755
--- a/scripts/makedeb.sh
+++ b/scripts/makedeb.sh
@@ -89,10 +89,15 @@ cd ..
mkdir -p makedeb-tmp/${sharedir}/doc/klayout
mkdir -p makedeb-tmp/${sharedir}/applications
mkdir -p makedeb-tmp/${sharedir}/pixmaps
+<<<<<<< HEAD
mkdir -p makedeb-tmp/${libdir}/db_plugins
mkdir -p makedeb-tmp/${libdir}/lay_plugins
mkdir -p makedeb-tmp/${pylibdir}
mkdir -p makedeb-tmp/${bindir}
+=======
+mkdir -p makedeb-tmp/${bindir}
+mkdir -p makedeb-tmp/${libdir}
+>>>>>>> master
cp etc/klayout.desktop makedeb-tmp/${sharedir}/applications
cp etc/logo.png makedeb-tmp/${sharedir}/pixmaps
@@ -103,10 +108,13 @@ cp COPYRIGHT makedeb-tmp/${sharedir}/doc/klayout/copyright
cp -pd $bininstdir/strm* makedeb-tmp/${bindir}
cp -pd $bininstdir/klayout makedeb-tmp/${bindir}
cp -pd $bininstdir/lib*so* makedeb-tmp/${libdir}
+<<<<<<< HEAD
cp -pd $bininstdir/db_plugins/lib*so* makedeb-tmp/${libdir}/db_plugins
cp -pd $bininstdir/lay_plugins/lib*so* makedeb-tmp/${libdir}/lay_plugins
cp -pd $bininstdir/pymod/klayout/*so makedeb-tmp/${pylibdir}
cp -pd $bininstdir/pymod/klayout/*py makedeb-tmp/${pylibdir}
+=======
+>>>>>>> master
cd makedeb-tmp
@@ -131,9 +139,12 @@ echo "Modifying control file .."
strip ${bindir}/*
strip ${libdir}/*.so*
+<<<<<<< HEAD
strip ${pylibdir}/*.so
strip ${libdir}/db_plugins/*.so*
strip ${libdir}/lay_plugins/*.so*
+=======
+>>>>>>> master
size=`du -ck usr | grep total | sed "s/ *total//"`
diff --git a/scripts/mkqtdecl5/mkqtdecl.conf b/scripts/mkqtdecl5/mkqtdecl.conf
index 224b69d5f..097ebb151 100644
--- a/scripts/mkqtdecl5/mkqtdecl.conf
+++ b/scripts/mkqtdecl5/mkqtdecl.conf
@@ -864,10 +864,10 @@ no_imports "QAbstractXmlNodeModel" # base class is QSharedData which is not ava
# QtNetwork
drop_method "QUrlInfo", /QUrlInfo::QUrlInfo\(.*permissions/ # too many arguments (13)
-drop_method "QHostAddress", /QHostAddress::QHostAddress\(\s*quint8\s*\*/ # requires char *, a string version is available for IPv6
+drop_method "QHostAddress", /QHostAddress::QHostAddress\(\s*(const\s*)?quint8\s*\*/ # requires char *, a string version is available for IPv6
drop_method "QHostAddress", /QHostAddress::QHostAddress\(\s*const\s+QIPv6Address/ # requires QIPv6Address struct, a string version is available for IPv6
drop_method "QHostAddress", /QHostAddress::QHostAddress\(\s*const\s+sockaddr/ # requires sockaddr struct, a string version is available for IPv6
-drop_method "QHostAddress", /QHostAddress::setAddress\(\s*quint8\s*\*/ # requires char *, a string version is available for IPv6
+drop_method "QHostAddress", /QHostAddress::setAddress\(\s*(const\s*)?quint8\s*\*/ # requires char *, a string version is available for IPv6
drop_method "QHostAddress", /QHostAddress::setAddress\(\s*const\s+QIPv6Address/ # requires QIPv6Address struct, a string version is available for IPv6
drop_method "QHostAddress", /QHostAddress::setAddress\(\s*const\s+sockaddr/ # requires sockaddr struct, a string version is available for IPv6
drop_method "QHostAddress", /QHostAddress::toIPv6Address\(/ # requires QIPv6Address struct
diff --git a/scripts/rpm-data/klayout.spec b/scripts/rpm-data/klayout.spec
index e6c74036a..bf9826b85 100644
--- a/scripts/rpm-data/klayout.spec
+++ b/scripts/rpm-data/klayout.spec
@@ -77,6 +77,14 @@ Requires: libqt4-x11 >= 4.8.7
%define pylib %{python3_sitearch}
%endif
+%if "%{target_system}" == "opensuse15"
+# OpenSuSE Leap 15 requirements
+Requires: ruby >= 2.5
+Requires: python3 >= 3.6
+Requires: libqt4-x11 >= 4.8.7
+%define buildopt -j2
+%endif
+
%description
Mask layout viewer and editor for the chip design engineer.
diff --git a/src/buddies/unit_tests/bdStrmrunTests.cc b/src/buddies/unit_tests/bdStrmrunTests.cc
index 60187733b..4bf980501 100644
--- a/src/buddies/unit_tests/bdStrmrunTests.cc
+++ b/src/buddies/unit_tests/bdStrmrunTests.cc
@@ -30,7 +30,7 @@ TEST(1)
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.py";
- std::string path = tl::combine_path (".", "strmrun ") + fp;
+ std::string path = tl::combine_path (tl::get_inst_path (), "strmrun ") + fp;
tl::InputPipe pipe (path);
tl::InputStream is (pipe);
std::string data = is.read_all ();
diff --git a/src/db/db/dbEdge.cc b/src/db/db/dbEdge.cc
index 3237e6582..3af27263f 100644
--- a/src/db/db/dbEdge.cc
+++ b/src/db/db/dbEdge.cc
@@ -22,6 +22,41 @@
#include "dbEdge.h"
+#include "tlLongInt.h"
+
+namespace db
+{
+
+/**
+ * @brief Computes the gcd of two numbers
+ */
+template
+inline C gcd (C a, C b)
+{
+ while (b != 0) {
+ a %= b;
+ std::swap (a, b);
+ }
+ return a;
+}
+
+#if defined(__SIZEOF_INT128__)
+typedef __int128 a2_type;
+#else
+// fallback to long_int in case the __int128 isn't defined
+typedef tl::long_int<4, uint32_t, uint64_t> a2_type;
+#endif
+
+db::Coord div_exact (db::Coord a, db::coord_traits::area_type b, db::coord_traits::area_type d)
+{
+ if (a < 0) {
+ return -db::Coord ((a2_type (-a) * a2_type (b) + a2_type (d / 2)) / a2_type (d));
+ } else {
+ return db::Coord ((a2_type (a) * a2_type (b) + a2_type ((d - 1) / 2)) / a2_type (d));
+ }
+}
+
+}
namespace tl
{
diff --git a/src/db/db/dbEdge.h b/src/db/db/dbEdge.h
index 29b6bd477..15d0cbcdf 100644
--- a/src/db/db/dbEdge.h
+++ b/src/db/db/dbEdge.h
@@ -38,6 +38,25 @@
namespace db {
+/**
+ * @brief A helper function for dividing integers with exact rounding
+ * This function computes (a*b/d) where rounding is exact in the sense of:
+ * a*b/d == N+0.5 => div_exact(a*b/d) = N
+ * b and d needs to be positive.
+ * a can be positive or negative.
+ * The implementation uses the gcd to reduce the ratios. This way we can
+ * represent the numbers with the area type.
+ */
+db::Coord DB_PUBLIC div_exact (db::Coord a, db::coord_traits::area_type b, db::coord_traits::area_type d);
+
+/**
+ * @brief An overload of div_exact for double types
+ */
+inline db::DCoord div_exact (db::DCoord a, db::coord_traits::area_type b, db::coord_traits::area_type d)
+{
+ return db::coord_traits::rounded (double (a) * double (b) / double (d));
+}
+
template class generic_repository;
class ArrayRepository;
@@ -794,10 +813,15 @@ public:
} else if (res) {
- double f = fabs (double (vxa)) / (fabs (double (vxa)) + fabs (double (vxb)));
+ if (vxa < 0) {
+ vxa = -vxa;
+ }
+ if (vxb < 0) {
+ vxb = -vxb;
+ }
- coord_type x = m_p1.x () + coord_traits::rounded (dx () * f);
- coord_type y = m_p1.y () + coord_traits::rounded (dy () * f);
+ coord_type x = m_p1.x () + div_exact (dx (), vxa, vxa + vxb);
+ coord_type y = m_p1.y () + div_exact (dy (), vxa, vxa + vxb);
return std::make_pair (true, db::point (x, y));
@@ -1073,10 +1097,15 @@ public:
if (res) {
- double f = fabs (double (vxa)) / (fabs (double (vxa)) + fabs (double (vxb)));
+ if (vxa < 0) {
+ vxa = -vxa;
+ }
+ if (vxb < 0) {
+ vxb = -vxb;
+ }
- coord_type x = e.p1 ().x () + coord_traits::rounded (e.dx () * f);
- coord_type y = e.p1 ().y () + coord_traits::rounded (e.dy () * f);
+ coord_type x = e.p1 ().x () + div_exact (e.dx (), vxa, vxa + vxb);
+ coord_type y = e.p1 ().y () + div_exact (e.dy (), vxa, vxa + vxb);
return std::make_pair (true, db::point (x, y));
diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc
index 79640fb53..00025fd8a 100644
--- a/src/db/db/dbEdgeProcessor.cc
+++ b/src/db/db/dbEdgeProcessor.cc
@@ -121,6 +121,26 @@ private:
db::Coord m_y;
};
+inline bool
+is_point_on_exact (const db::Edge &e, const db::Point &pt)
+{
+ if (pt.x () < db::edge_xmin (e) || pt.x () > db::edge_xmax (e) ||
+ pt.y () < db::edge_ymin (e) || pt.y () > db::edge_ymax (e)) {
+
+ return false;
+
+ } else if (e.dy () == 0 || e.dx () == 0) {
+
+ // shortcut for orthogonal edges
+ return true;
+
+ } else {
+
+ return db::vprod_sign (pt - e.p1 (), e.p2 () - e.p1 ()) == 0;
+
+ }
+}
+
inline bool
is_point_on_fuzzy (const db::Edge &e, const db::Point &pt)
{
@@ -141,9 +161,12 @@ is_point_on_fuzzy (const db::Edge &e, const db::Point &pt)
} else {
+ bool with_equal = false;
+
db::Vector offset;
if ((e.dx () < 0 && e.dy () > 0) || (e.dx () > 0 && e.dy () < 0)) {
offset = db::Vector (1, 1);
+ with_equal = true;
} else {
offset = db::Vector (-1, 1);
}
@@ -152,30 +175,16 @@ is_point_on_fuzzy (const db::Edge &e, const db::Point &pt)
typedef db::coord_traits::area_type area_type;
area_type a1 = 2 * db::vprod (pp1, e.d ());
- if (a1 < 0) { a1 = -a1; }
area_type a2 = db::vprod (offset, e.d ());
+
+ if ((a1 < 0) == (a2 < 0)) {
+ with_equal = false;
+ }
+
+ if (a1 < 0) { a1 = -a1; }
if (a2 < 0) { a2 = -a2; }
- return a1 <= a2;
- }
-}
-
-inline bool
-is_point_on_exact (const db::Edge &e, const db::Point &pt)
-{
- if (pt.x () < db::edge_xmin (e) || pt.x () > db::edge_xmax (e) ||
- pt.y () < db::edge_ymin (e) || pt.y () > db::edge_ymax (e)) {
-
- return false;
-
- } else if (e.dy () == 0 || e.dx () == 0) {
-
- // shortcut for orthogonal edges
- return true;
-
- } else {
-
- return db::vprod_sign (pt - e.p1 (), e.p2 () - e.p1 ()) == 0;
+ return a1 < a2 || (a1 == a2 && with_equal);
}
}
@@ -804,7 +813,7 @@ MergeOp::edge (bool north, bool enter, property_type p)
bool inside_after = (*wcv != 0);
m_zeroes += (!inside_after) - (!inside_before);
#ifdef DEBUG_MERGEOP
- printf ("north=%d, enter=%d, prop=%d -> %d\n", north, enter, p, m_zeroes);
+ printf ("north=%d, enter=%d, prop=%d -> %d\n", north, enter, int (p), int (m_zeroes));
#endif
tl_assert (long (m_zeroes) >= 0);
@@ -887,7 +896,7 @@ BooleanOp::edge_impl (bool north, bool enter, property_type p, const InsideFunc
bool inside_after = ((p % 2) == 0 ? inside_a (*wcv) : inside_b (*wcv));
m_zeroes += (!inside_after) - (!inside_before);
#ifdef DEBUG_BOOLEAN
- printf ("north=%d, enter=%d, prop=%d -> %d\n", north, enter, p, m_zeroes);
+ printf ("north=%d, enter=%d, prop=%d -> %d\n", north, enter, int (p), int (m_zeroes));
#endif
tl_assert (long (m_zeroes) >= 0);
@@ -1345,32 +1354,17 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector
#endif
// The new cutpoint must be inserted into other edges as well.
- // If the cutpoint is exactly on the edge and there is just one other edge
- // the cutpoint will be a weak attractor - that is an optional cutpoint.
- // In that case we can skip the cutpoint because no related edge will move.
ip_weak.clear ();
- size_t n_off_edge = on_edge1 ? 0 : 1;
for (std::vector ::iterator cc = c; cc != f; ++cc) {
if ((with_h || cc->dy () != 0) && cc != c1 && cc != c2 && is_point_on_fuzzy (*cc, cp.second)) {
ip_weak.push_back (&*cc);
- if (!is_point_on_exact (*cc, cp.second)) {
- ++n_off_edge;
- }
}
}
for (std::vector ::iterator icc = ip_weak.begin (); icc != ip_weak.end (); ++icc) {
- if (n_off_edge > 1) {
- (*icc)->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
+ (*icc)->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
#ifdef DEBUG_EDGE_PROCESSOR
- printf ("intersection point %s gives cutpoint in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
+ printf ("intersection point %s gives cutpoint in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
#endif
- } else {
- CutPoints *cpp = (*icc)->make_cutpoints (cutpoints);
- cpp->add_attractor (cp.second, (*icc)->data - 1);
-#ifdef DEBUG_EDGE_PROCESSOR
- printf ("intersection point %s gives weak attractor in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
-#endif
- }
}
}
@@ -1435,38 +1429,17 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector
#endif
// The new cutpoint must be inserted into other edges as well.
- // If the cutpoint is exactly on the edge and there is just one other edge
- // the cutpoint will be a weak attractor - that is an optional cutpoint.
- // In that case we can skip the cutpoint because no related edge will move.
ip_weak.clear ();
- size_t n_off_edge = 0;
- if (!on_edge1) {
- n_off_edge += 1;
- }
- if (!on_edge2) {
- n_off_edge += 1;
- }
for (std::vector ::iterator cc = c; cc != f; ++cc) {
if ((with_h || cc->dy () != 0) && cc != c1 && cc != c2 && is_point_on_fuzzy (*cc, cp.second)) {
ip_weak.push_back (&*cc);
- if (!is_point_on_exact (*cc, cp.second)) {
- ++n_off_edge;
- }
}
}
for (std::vector ::iterator icc = ip_weak.begin (); icc != ip_weak.end (); ++icc) {
- if (n_off_edge > 1) {
- (*icc)->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
+ (*icc)->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
#ifdef DEBUG_EDGE_PROCESSOR
- printf ("intersection point %s gives cutpoint in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
+ printf ("intersection point %s gives cutpoint in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
#endif
- } else {
- CutPoints *cpp = (*icc)->make_cutpoints (cutpoints);
- cpp->add_attractor (cp.second, (*icc)->data - 1);
-#ifdef DEBUG_EDGE_PROCESSOR
- printf ("intersection point %s gives weak attractor in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
-#endif
- }
}
}
@@ -1829,7 +1802,7 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
size_t skip = c->data % skip_unit;
size_t skip_res = c->data / skip_unit;
#ifdef DEBUG_EDGE_PROCESSOR
- printf ("X %ld->%d,%d\n", long(c->data), skip, skip_res);
+ printf ("X %ld->%d,%d\n", long (c->data), int (skip), int (skip_res));
#endif
if (skip != 0 && (c + skip >= future || (c + skip)->data != 0)) {
diff --git a/src/db/db/dbPolygonGenerators.cc b/src/db/db/dbPolygonGenerators.cc
index c75b8f24d..dcb9cf274 100644
--- a/src/db/db/dbPolygonGenerators.cc
+++ b/src/db/db/dbPolygonGenerators.cc
@@ -296,7 +296,7 @@ PolygonGenerator::flush ()
{
#ifdef DEBUG_POLYGON_GENERATOR
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
- printf ("%d:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
+ printf ("%ld:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
}
printf ("\n");
#endif
@@ -325,12 +325,12 @@ PolygonGenerator::begin_scanline (db::Coord y)
#ifdef DEBUG_POLYGON_GENERATOR
printf ("m_open=");
for (open_map_type::const_iterator o = m_open.begin (); o != m_open.end (); ++o) {
- printf ("%d:%s ", o->contour, o->point.to_string().c_str());
+ printf ("%ld:%s ", o->contour, o->point.to_string().c_str());
}
printf ("\n");
printf ("contours:\n");
for (size_t j = 0; j < mp_contours->size (); ++j) {
- printf ("c%d%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
+ printf ("c%ld%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
for (size_t i = 0; i < (*mp_contours)[j].size (); ++i) {
printf ("%s ", ((*mp_contours)[j].begin () + i)->to_string().c_str ());
}
@@ -364,7 +364,7 @@ PolygonGenerator::skip_n (size_t n)
{
join_contours (std::numeric_limits::max ());
#ifdef DEBUG_POLYGON_GENERATOR
- printf ("skip(%d)\n", n);
+ printf ("skip(%ld)\n", n);
#endif
while (n-- > 0) {
++m_open_pos;
@@ -375,9 +375,9 @@ void
PolygonGenerator::put (const db::Edge &e)
{
#ifdef DEBUG_POLYGON_GENERATOR
- printf ("put(%s) y=%d m_open(%d)=", e.to_string().c_str(),m_y,std::distance (m_open.begin (), m_open_pos));
+ printf ("put(%s) y=%d m_open(%ld)=", e.to_string().c_str(),m_y,std::distance (m_open.begin (), m_open_pos));
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
- printf ("%d:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
+ printf ("%ld:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
}
printf ("\n");
#endif
@@ -438,7 +438,7 @@ PolygonGenerator::put (const db::Edge &e)
cnew.push_back (e.p2 ());
#ifdef DEBUG_POLYGON_GENERATOR
- printf ("create %s %d\n", hole ? "hole" : "hull", poly_index);
+ printf ("create %s %ld\n", hole ? "hole" : "hull", inew);
#endif
m_open.insert (m_open_pos, PGPoint (hole ? e.p1 () : e.p2 (), inew, true));
m_open.insert (m_open_pos, PGPoint (hole ? e.p2 () : e.p1 (), inew, false));
@@ -449,7 +449,7 @@ PolygonGenerator::put (const db::Edge &e)
#ifdef DEBUG_POLYGON_GENERATOR
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
- printf ("%d:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
+ printf ("%ld:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
}
printf ("\n");
#endif
@@ -552,14 +552,14 @@ PolygonGenerator::join_contours (db::Coord x)
size_t i1 = m_open_pos->contour;
size_t i2 = n->contour;
#ifdef DEBUG_POLYGON_GENERATOR
- printf ("join %d and %d\n", i1, i2);
+ printf ("join %ld and %ld\n", i1, i2);
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
- printf ("%d:%s%c%c%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ', i == n ? '+' : ' ', i == nn ? '#' : ' ');
+ printf ("%ld:%s%c%c%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ', i == n ? '+' : ' ', i == nn ? '#' : ' ');
}
printf ("\n");
printf ("--> input contours:\n");
for (size_t j = 0; j < mp_contours->size (); ++j) {
- printf ("--> c%d%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
+ printf ("--> c%ld%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
for (size_t i = 0; i < (*mp_contours)[j].size (); ++i) {
printf ("%s ", ((*mp_contours)[j].begin () + i)->to_string().c_str ());
}
@@ -705,7 +705,7 @@ PolygonGenerator::join_contours (db::Coord x)
if (! c1.is_hole ()) {
#ifdef DEBUG_POLYGON_GENERATOR
- printf ("finish %d (hull)\n", i1);
+ printf ("finish %ld (hull)\n", i1);
#endif
produce_poly (c1);
@@ -775,7 +775,7 @@ PolygonGenerator::join_contours (db::Coord x)
#ifdef DEBUG_POLYGON_GENERATOR
printf ("--> output contours:\n");
for (size_t j = 0; j < mp_contours->size (); ++j) {
- printf ("--> c%d%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
+ printf ("--> c%ld%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
for (size_t i = 0; i < (*mp_contours)[j].size (); ++i) {
printf ("%s ", ((*mp_contours)[j].begin () + i)->to_string().c_str ());
}
diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc
index a46081de5..27b6964c1 100644
--- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc
+++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc
@@ -280,7 +280,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS
) +
gsi::method ("region=", (void (db::RecursiveShapeIterator::*)(const db::RecursiveShapeIterator::box_type &)) &db::RecursiveShapeIterator::set_region,
"@brief Sets the rectangular region that is iterator is iterating over\n"
- "@args region\n"
+ "@args box_region\n"
"See \\region for a description of this attribute.\n"
"Setting a simple region will reset the complex region to a rectangle and reset the iterator to "
"the beginning of the sequence."
@@ -298,15 +298,19 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS
) +
gsi::method ("confine_region", (void (db::RecursiveShapeIterator::*)(const db::RecursiveShapeIterator::box_type &)) &db::RecursiveShapeIterator::confine_region,
"@brief Confines the region that is iterator is iterating over\n"
- "@args region\n"
- "This method is similar to setting the region (see \\region=), but will add to any (complex or simple) region already set. "
+ "@args box_region\n"
+ "This method is similar to setting the region (see \\region=), but will confine any region (complex or simple) already set. "
+ "Essentially it does a logical AND operation between the existing and given region. "
+ "Hence this method can only reduce a region, not extend it.\n"
"\n"
"This method has been introduced in version 0.25.\n"
) +
- gsi::method ("region=", (void (db::RecursiveShapeIterator::*)(const db::RecursiveShapeIterator::region_type &)) &db::RecursiveShapeIterator::confine_region,
+ gsi::method ("confine_region", (void (db::RecursiveShapeIterator::*)(const db::RecursiveShapeIterator::region_type &)) &db::RecursiveShapeIterator::confine_region,
"@brief Confines the region that is iterator is iterating over\n"
- "@args region\n"
- "This method is similar to setting the complex region (see \\region=), but will add to any (complex or simple) region already set."
+ "@args complex_region\n"
+ "This method is similar to setting the region (see \\region=), but will confine any region (complex or simple) already set. "
+ "Essentially it does a logical AND operation between the existing and given region. "
+ "Hence this method can only reduce a region, not extend it.\n"
"\n"
"This method has been introduced in version 0.25.\n"
) +
diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc
index 6ceacab9e..ec8dd1408 100644
--- a/src/db/db/gsiDeclDbShapes.cc
+++ b/src/db/db/gsiDeclDbShapes.cc
@@ -50,6 +50,21 @@ static void dump_mem_statistics (const db::Shapes *shapes, bool detailed)
ms.print ();
}
+static size_t shapes_size (const db::Shapes *shapes)
+{
+ // we may have shape arrays - expand their count to match the shape count with the shapes delivered
+ size_t n = 0;
+ for (db::Shapes::shape_iterator i = shapes->begin (db::ShapeIterator::All); ! i.at_end (); ++i) {
+ if (i.in_array ()) {
+ n += i.array ().array_size ();
+ i.finish_array ();
+ } else {
+ ++n;
+ }
+ }
+ return n;
+}
+
template
static db::Shape insert (db::Shapes *s, const Sh &p)
{
@@ -1088,7 +1103,7 @@ Class decl_Shapes ("db", "Shapes",
"@brief Clears the shape container\n"
"This method has been introduced in version 0.16. It can only be used in editable mode."
) +
- gsi::method ("size", (size_t (db::Shapes::*)() const) &db::Shapes::size,
+ gsi::method_ext ("size", &shapes_size,
"@brief Gets the number of shapes in this container\n"
"This method was introduced in version 0.16\n"
"@return The number of shapes in this container\n"
diff --git a/src/db/unit_tests/dbEdge.cc b/src/db/unit_tests/dbEdge.cc
index 91d5a77b3..32d91641b 100644
--- a/src/db/unit_tests/dbEdge.cc
+++ b/src/db/unit_tests/dbEdge.cc
@@ -514,3 +514,74 @@ TEST(14)
EXPECT_EQ (e.coincident (db::Edge (db::Point (49, 0), db::Point (200, 0))), false);
}
+// exact rounding behaviour
+TEST(15)
+{
+ typedef db::coord_traits::area_type area_type;
+ // div_exact(a, b, d) computes a*b/d with exact rounding behaviour
+ EXPECT_EQ (db::div_exact (area_type (0), area_type (22), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (5), area_type (0), area_type (176)), 0);
+
+ EXPECT_EQ (db::div_exact (area_type (3), area_type (22), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (4), area_type (22), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (5), area_type (22), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (7), area_type (22), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (8), area_type (22), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (12), area_type (22), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (13), area_type (22), area_type (176)), 2);
+
+ EXPECT_EQ (db::div_exact (area_type (3 * 11), area_type (2), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (4 * 11), area_type (2), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (5 * 11), area_type (2), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (7 * 11), area_type (2), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (8 * 11), area_type (2), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (12 * 11), area_type (2), area_type (176)), 1);
+ EXPECT_EQ (db::div_exact (area_type (13 * 11), area_type (2), area_type (176)), 2);
+
+ EXPECT_EQ (db::div_exact (area_type (-3), area_type (22), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (-4), area_type (22), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-5), area_type (22), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-7), area_type (22), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-8), area_type (22), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-12), area_type (22), area_type (176)), -2);
+ EXPECT_EQ (db::div_exact (area_type (-13), area_type (22), area_type (176)), -2);
+
+ EXPECT_EQ (db::div_exact (area_type (-3 * 11), area_type (2), area_type (176)), 0);
+ EXPECT_EQ (db::div_exact (area_type (-4 * 11), area_type (2), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-5 * 11), area_type (2), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-7 * 11), area_type (2), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-8 * 11), area_type (2), area_type (176)), -1);
+ EXPECT_EQ (db::div_exact (area_type (-12 * 11), area_type (2), area_type (176)), -2);
+ EXPECT_EQ (db::div_exact (area_type (-13 * 11), area_type (2), area_type (176)), -2);
+
+ area_type f = 790014345;
+
+ EXPECT_EQ (db::div_exact (area_type (4), area_type (22) * f, area_type (176) * f), 0);
+ EXPECT_EQ (db::div_exact (area_type (5), area_type (22) * f, area_type (176) * f), 1);
+ EXPECT_EQ (db::div_exact (area_type (8), area_type (22) * f, area_type (176) * f), 1);
+
+ EXPECT_EQ (db::div_exact (area_type (-3), area_type (22) * f, area_type (176) * f), 0);
+ EXPECT_EQ (db::div_exact (area_type (-4), area_type (22) * f, area_type (176) * f), -1);
+ EXPECT_EQ (db::div_exact (area_type (-5), area_type (22) * f, area_type (176) * f), -1);
+ EXPECT_EQ (db::div_exact (area_type (-8), area_type (22) * f, area_type (176) * f), -1);
+
+ EXPECT_EQ (db::div_exact (area_type (4) * 100000000, area_type (22) * f, area_type (176) * f), 50000000);
+ EXPECT_EQ (db::div_exact (area_type (5) * 100000000, area_type (22) * f, area_type (176) * f), 62500000);
+ EXPECT_EQ (db::div_exact (area_type (-4) * 100000000, area_type (22) * f, area_type (176) * f), -50000000);
+ EXPECT_EQ (db::div_exact (area_type (-5) * 100000000, area_type (22) * f, area_type (176) * f), -62500000);
+
+ EXPECT_EQ (db::div_exact (1000000004, area_type (22) * f, area_type (176) * f), 125000000);
+ EXPECT_EQ (db::div_exact (1000000005, area_type (22) * f, area_type (176) * f), 125000001);
+ EXPECT_EQ (db::div_exact (-1000000003, area_type (22) * f, area_type (176) * f), -125000000);
+ EXPECT_EQ (db::div_exact (-1000000004, area_type (22) * f, area_type (176) * f), -125000001);
+ EXPECT_EQ (db::div_exact (-1000000005, area_type (22) * f, area_type (176) * f), -125000001);
+
+ db::Edge e1 (db::Point (3, -3), db::Point (-8, -1));
+ db::Edge e2 (db::Point (-4, -2), db::Point (13, -4));
+
+ std::pair ip;
+ ip = e1.intersect_point (e2);
+ EXPECT_EQ (ip.second.to_string ().c_str (), "0,-3");
+ ip = e2.intersect_point (e1);
+ EXPECT_EQ (ip.second.to_string ().c_str (), "0,-3");
+}
diff --git a/src/db/unit_tests/dbEdgeProcessor.cc b/src/db/unit_tests/dbEdgeProcessor.cc
index a7abc8de4..5c019c9be 100644
--- a/src/db/unit_tests/dbEdgeProcessor.cc
+++ b/src/db/unit_tests/dbEdgeProcessor.cc
@@ -25,6 +25,7 @@
#include "dbShapeProcessor.h"
#include "dbPolygon.h"
+#include "dbPolygonGenerators.h"
#include "dbLayout.h"
#include "dbReader.h"
#include "dbCommonReader.h"
@@ -1332,7 +1333,7 @@ TEST(23)
EXPECT_EQ (out.size (), size_t (1));
std::sort (out.begin (), out.end ());
- EXPECT_EQ (out[0].to_string (), "(0,0;0,1;1,0)");
+ EXPECT_EQ (out[0].to_string (), "(0,0;0,1;1,1)");
}
TEST(24)
@@ -1390,15 +1391,29 @@ TEST(24)
in1.back ().assign_hull (p1 + 0, p1 + sizeof (p1) / sizeof (p1[0]));
}
- std::vector out;
+ {
+ std::vector out;
- db::EdgeProcessor ep;
- ep.simple_merge (in1, out, false, false);
+ db::EdgeProcessor ep;
+ ep.simple_merge (in1, out, false, false);
- EXPECT_EQ (out.size (), size_t (2));
- std::sort (out.begin (), out.end ());
- EXPECT_EQ (out[0].to_string (), "(0,-9;0,0;3,0;3,-2;1,0;1,-9)");
- EXPECT_EQ (out[1].to_string (), "(-2,1;-2,3;0,1;0,10;1,10;1,1)");
+ EXPECT_EQ (out.size (), size_t (1));
+ std::sort (out.begin (), out.end ());
+ EXPECT_EQ (out[0].to_string (), "(0,-9;0,0;-2,1;-2,3;0,1;0,10;1,10;1,1;0,0;3,0;3,-2;1,0;1,-9)");
+ }
+
+ {
+ std::vector out;
+
+ db::EdgeProcessor ep;
+ ep.simple_merge (in1, out, false, true);
+
+ EXPECT_EQ (out.size (), size_t (3));
+ std::sort (out.begin (), out.end ());
+ EXPECT_EQ (out[0].to_string (), "(0,-9;0,0;1,0;1,-9)");
+ EXPECT_EQ (out[1].to_string (), "(3,-2;1,0;3,0)");
+ EXPECT_EQ (out[2].to_string (), "(0,0;-2,1;-2,3;0,1;0,10;1,10;1,1)");
+ }
}
TEST(25)
@@ -2247,7 +2262,7 @@ TEST(100)
}
// #74 (GitHub)
-TEST(101)
+std::string run_test101 (tl::TestBase *_this, const db::Trans &t)
{
db::EdgeProcessor ep;
@@ -2260,6 +2275,7 @@ TEST(101)
};
db::Polygon p;
p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
+ p.transform (t);
ep.insert (p, 0);
}
@@ -2272,6 +2288,7 @@ TEST(101)
};
db::Polygon p;
p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
+ p.transform (t);
ep.insert (p, 1);
}
@@ -2284,6 +2301,7 @@ TEST(101)
};
db::Polygon p;
p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
+ p.transform (t);
ep.insert (p, 1);
}
@@ -2295,7 +2313,16 @@ TEST(101)
ep.process (pg, op);
EXPECT_EQ (out.size (), size_t (1));
- EXPECT_EQ (out[0].to_string (), "(0,0;0,9;1,10;10,10;10,0)");
+
+ return out.empty () ? std::string () : out.front ().to_string ();
+}
+
+TEST(101)
+{
+ EXPECT_EQ (run_test101 (_this, db::Trans (db::Trans::r0)), "(0,0;0,9;1,10;10,10;10,0)");
+ EXPECT_EQ (run_test101 (_this, db::Trans (db::Trans::r90)), "(-9,0;-10,1;-10,10;0,10;0,0)");
+ EXPECT_EQ (run_test101 (_this, db::Trans (db::Trans::r180)), "(-10,-10;-10,0;0,0;0,-9;-1,-10)");
+ EXPECT_EQ (run_test101 (_this, db::Trans (db::Trans::r270)), "(0,-10;0,0;9,0;10,-1;10,-10)");
}
TEST(102)
@@ -2348,3 +2375,111 @@ TEST(102)
EXPECT_EQ (out.size (), size_t (1));
EXPECT_EQ (out[0].to_string (), "(0,0;0,200;100,200;100,100;200,100;200,200;500,200;500,100;600,100;600,200;0,200;0,1000;1000,1000;1000,0)");
}
+
+// Bug 134
+TEST(134)
+{
+ const char *pd = "(30,-7957;0,0;56,-4102;30,-7921)";
+
+ db::Coord dx = 0;
+ db::Coord dy = -3999;
+ unsigned int mode = 3;
+
+ db::Polygon p;
+ tl::from_string (pd, p);
+
+ db::EdgeProcessor ep;
+ db::Polygon ps (p.sized (dx, dy, mode));
+ ep.insert (ps);
+
+ db::SimpleMerge op (1 /*wc>0*/);
+ std::vector out;
+ db::PolygonContainer pc (out);
+ db::PolygonGenerator pg (pc);
+ ep.process (pg, op);
+
+ EXPECT_EQ (out.size (), size_t (0));
+}
+
+void run_test135a (tl::TestBase *_this, const db::Trans &t)
+{
+ db::EdgeProcessor ep;
+
+ db::Point pts[] = {
+ db::Point (0, 0),
+ db::Point (19, 19),
+ db::Point (19, 18),
+ db::Point (43, 32),
+ db::Point (37, 27)
+ };
+
+ db::Polygon p;
+ p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
+ p.transform (t);
+ p.size (-2, -2, 2);
+
+ ep.insert (p);
+
+ // this is just supposed to work and not fail with internal error "m_open.empty()"
+ std::vector out;
+ db::PolygonContainer pc (out);
+ db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/);
+ db::SimpleMerge op (1 /*wc>0*/);
+ ep.process (pg2, op);
+
+ EXPECT_EQ (out.size (), size_t (0));
+}
+
+TEST(135a)
+{
+ run_test135a (_this, db::Trans (db::Trans::r0));
+ run_test135a (_this, db::Trans (db::Trans::r90));
+ run_test135a (_this, db::Trans (db::Trans::r180));
+ run_test135a (_this, db::Trans (db::Trans::r270));
+ run_test135a (_this, db::Trans (db::Trans::m0));
+ run_test135a (_this, db::Trans (db::Trans::m45));
+ run_test135a (_this, db::Trans (db::Trans::m90));
+ run_test135a (_this, db::Trans (db::Trans::m135));
+}
+
+std::string run_test135b (tl::TestBase *_this, const db::Trans &t)
+{
+ db::EdgeProcessor ep;
+
+ db::Point pts[] = {
+ db::Point (215, 0),
+ db::Point (145, 11),
+ db::Point (37, 31),
+ db::Point (36, 31),
+ db::Point (0, 43)
+ };
+
+ db::Polygon p;
+ p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
+ p.transform (t);
+ p.size (-2, -2, 2);
+
+ ep.insert (p);
+
+ // this is just supposed to work and not fail with internal error "m_open.empty()"
+ std::vector out;
+ db::PolygonContainer pc (out);
+ db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/);
+ db::SimpleMerge op (1 /*wc>0*/);
+ ep.process (pg2, op);
+
+ EXPECT_EQ (out.size (), size_t (1));
+ return out.empty () ? std::string () : out.front ().to_string ();
+}
+
+TEST(135b)
+{
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::r0)), "(36,33;32,34;37,33)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::r90)), "(-35,32;-26,77;-33,37;-33,36)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::r180)), "(-33,-35;-78,-26;-37,-33;-36,-33)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::r270)), "(25,-78;33,-37;33,-36;34,-33)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::m0)), "(32,-35;36,-33;37,-33;77,-26)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::m45)), "(34,32;33,36;33,37)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::m90)), "(-78,25;-33,34;-36,33;-37,33)");
+ EXPECT_EQ (run_test135b (_this, db::Trans (db::Trans::m135)), "(-26,-78;-35,-33;-33,-36;-33,-37)");
+}
diff --git a/src/db/unit_tests/dbPolygonTools.cc b/src/db/unit_tests/dbPolygonTools.cc
index a690897f3..06224c533 100644
--- a/src/db/unit_tests/dbPolygonTools.cc
+++ b/src/db/unit_tests/dbPolygonTools.cc
@@ -1082,7 +1082,7 @@ TEST(31)
pout = minkowsky_sum (p, db::Edge (db::Point (10, 10), db::Point (210, -90)), true);
- EXPECT_EQ (pout.to_string (), "(210,-190;143,-157;110,-165;-90,-65;10,110;85,73;110,85;310,-15;310,-140)");
+ EXPECT_EQ (pout.to_string (), "(210,-190;143,-157;110,-165;-90,-65;10,110;85,72;110,85;310,-15;310,-140)");
std::vector c;
c.push_back (db::Point (10, 10));
diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc
index 1a80b94ad..cb67d5aad 100644
--- a/src/db/unit_tests/dbTilingProcessor.cc
+++ b/src/db/unit_tests/dbTilingProcessor.cc
@@ -26,10 +26,13 @@
#include "dbTilingProcessor.h"
#include "dbTextWriter.h"
#include "gsiExpression.h"
+#include "gsiDecl.h"
#include "dbWriter.h"
#include "dbSaveLayoutOptions.h"
#include
+#include
+#include
unsigned int get_rand()
{
@@ -385,3 +388,63 @@ TEST(4)
}
+class MyTilingOutputReceiver
+ : public db::TileOutputReceiver
+{
+public:
+ MyTilingOutputReceiver (double *sum, int *n)
+ : mp_sum (sum), mp_n (n)
+ { }
+
+ MyTilingOutputReceiver ()
+ : mp_sum (0), mp_n (0)
+ { }
+
+ void add (double x) const
+ {
+ static QMutex lock;
+ QMutexLocker locker (&lock);
+ *mp_sum += x;
+ *mp_n += 1;
+ }
+
+private:
+ double *mp_sum;
+ int *mp_n;
+};
+
+namespace gsi
+{
+DB_PUBLIC gsi::Class &dbdecl_TileOutputReceiverBase ();
+}
+
+gsi::Class decl_MyTilingOutputReceiver (gsi::dbdecl_TileOutputReceiverBase (), "db", "MyTileOutputReceiver",
+ gsi::method ("add", &MyTilingOutputReceiver::add)
+);
+
+// Multithreaded, access to _rec()
+// This will mainly test the ability of gsi::Proxy to manage references
+// in a multithreaded case.
+TEST(5)
+{
+ db::Layout ly1;
+ ly1.dbu (0.001);
+ unsigned int l11 = ly1.insert_layer (db::LayerProperties (1, 0));
+ db::cell_index_type top1 = ly1.add_cell ("TOP");
+ ly1.cell (top1).shapes (l11).insert (db::Box (0, 0, 50000, 50000));
+
+ double sum = 0.0;
+ int num = 0;
+ MyTilingOutputReceiver *rec = new MyTilingOutputReceiver (&sum, &num);
+
+ db::TilingProcessor tp;
+ tp.set_threads (4);
+ tp.tile_size (0.11, 0.17);
+ tp.input ("i1", db::RecursiveShapeIterator (ly1, ly1.cell (top1), l11));
+ tp.output ("o1", 0, rec, db::ICplxTrans ());
+ tp.queue ("_rec(o1).add((i1 & _tile).area)");
+ tp.execute ("test");
+
+ EXPECT_EQ (sum, 2500000000);
+ EXPECT_EQ (num, 134225);
+}
diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym
index 65fbd04e7..5bdb3de1b 100644
--- a/src/drc/drc/built-in-macros/drc.lym
+++ b/src/drc/drc/built-in-macros/drc.lym
@@ -896,7 +896,7 @@ CODE
#
# This method produces markers on the corners of the polygons. An angle criterion can be given which
# selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
- # while negative angles indicate a right turn. Since polygons are oriented clockwise, postive angles
+ # while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles
# indicate concave corners while negative ones indicate convex corners.
#
# The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc
index c2e5610f3..8543ed140 100644
--- a/src/edt/edt/edtPropertiesPages.cc
+++ b/src/edt/edt/edtPropertiesPages.cc
@@ -251,6 +251,8 @@ ShapePropertiesPage::do_apply (bool current_only)
for (std::vector::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
+ size_t index = p - m_selection_ptrs.begin ();
+
edt::Service::obj_iterator pos = *p;
// only update objects from the same layout - this is not practical limitation but saves a lot of effort for
@@ -259,6 +261,9 @@ ShapePropertiesPage::do_apply (bool current_only)
continue;
}
+ const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
+ db::Layout &layout = cv->layout ();
+
tl_assert (! pos->is_cell_inst ());
if (pos->shape ().is_array_member ()) {
@@ -271,10 +276,8 @@ ShapePropertiesPage::do_apply (bool current_only)
std::map::const_iterator s = shapes_seen.find (pos->shape ());
if (s == shapes_seen.end ()) {
- const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
-
- db::Shapes &shapes = cv->layout ().cell (pos->cell_index ()).shapes (pos->layer ());
- double dbu = cv->layout ().dbu ();
+ db::Shapes &shapes = layout.cell (pos->cell_index ()).shapes (pos->layer ());
+ double dbu = layout.dbu ();
if (!current_only || pos->shape () == current) {
new_shape = applicator->do_apply (shapes, pos->shape (), dbu, relative_mode);
@@ -288,8 +291,6 @@ ShapePropertiesPage::do_apply (bool current_only)
if (new_shape != pos->shape ()) {
- size_t index = p - m_selection_ptrs.begin ();
-
// change selection to new shape
new_sel[index].set_shape (new_shape);
@@ -298,21 +299,34 @@ ShapePropertiesPage::do_apply (bool current_only)
update_required = true;
- }
+ }
+
+ // handle the case of guiding shape updates
+ std::pair gs = mp_service->handle_guiding_shape_changes (new_sel[index]);
+ if (gs.first) {
+
+ new_sel[index] = gs.second;
+
+ mp_service->select (*pos, lay::Editable::Reset);
+ mp_service->select (new_sel [index], lay::Editable::Add);
+
+ update_required = true;
+
+ }
}
if (update_required) {
+ mp_service->view ()->cellview (cv_index)->layout ().cleanup ();
recompute_selection_ptrs (new_sel);
}
} catch (...) {
+ mp_service->view ()->cellview (cv_index)->layout ().cleanup ();
recompute_selection_ptrs (new_sel);
throw;
}
- mp_service->handle_guiding_shape_changes ();
-
update ();
}
diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc
index 18fd97934..554073692 100644
--- a/src/edt/edt/edtService.cc
+++ b/src/edt/edt/edtService.cc
@@ -1480,30 +1480,23 @@ Service::add_selection (const lay::ObjectInstPath &sel)
selection_to_view ();
}
-bool
-Service::handle_guiding_shape_changes ()
+std::pair
+Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const
{
- // just allow one guiding shape to be selected
- if (m_selection.empty ()) {
- return false;
- }
-
- objects::const_iterator s = m_selection.begin ();
-
- unsigned int cv_index = s->cv_index ();
+ unsigned int cv_index = obj.cv_index ();
lay::CellView cv = view ()->cellview (cv_index);
db::Layout *layout = &cv->layout ();
- if (s->is_cell_inst () || s->layer () != layout->guiding_shape_layer ()) {
- return false;
+ if (obj.is_cell_inst () || obj.layer () != layout->guiding_shape_layer ()) {
+ return std::make_pair (false, lay::ObjectInstPath ());
}
- if (! s->shape ().has_prop_id ()) {
- return false;
+ if (! obj.shape ().has_prop_id ()) {
+ return std::make_pair (false, lay::ObjectInstPath ());
}
- if (! layout->is_pcell_instance (s->cell_index ()).first) {
- return false;
+ if (! layout->is_pcell_instance (obj.cell_index ()).first) {
+ return std::make_pair (false, lay::ObjectInstPath ());
}
db::cell_index_type top_cell = std::numeric_limits::max ();
@@ -1512,37 +1505,38 @@ Service::handle_guiding_shape_changes ()
db::pcell_parameters_type parameters_for_pcell;
// determine parent cell and instance if required
- lay::ObjectInstPath::iterator e = s->end ();
- if (e == s->begin ()) {
- top_cell = s->cell_index ();
+ lay::ObjectInstPath::iterator e = obj.end ();
+ if (e == obj.begin ()) {
+ top_cell = obj.cell_index ();
} else {
--e;
- db::cell_index_type pc = s->topcell ();
- if (e != s->begin ()) {
+ db::cell_index_type pc = obj.topcell ();
+ if (e != obj.begin ()) {
--e;
pc = e->inst_ptr.cell_index ();
}
parent_cell = pc;
- parent_inst = s->back ().inst_ptr;
+ parent_inst = obj.back ().inst_ptr;
}
db::property_names_id_type pn = layout->properties_repository ().prop_name_id ("name");
- const db::PropertiesRepository::properties_set &input_props = layout->properties_repository ().properties (s->shape ().prop_id ());
+ const db::PropertiesRepository::properties_set &input_props = layout->properties_repository ().properties (obj.shape ().prop_id ());
db::PropertiesRepository::properties_set::const_iterator input_pv = input_props.find (pn);
if (input_pv == input_props.end ()) {
- return false;
+ return std::make_pair (false, lay::ObjectInstPath ());
}
std::string shape_name = input_pv->second.to_string ();
// Hint: get_parameters_from_pcell_and_guiding_shapes invalidates the shapes because it resets the changed
// guiding shapes. We must not access s->shape after that.
- if (! get_parameters_from_pcell_and_guiding_shapes (layout, s->cell_index (), parameters_for_pcell)) {
- return false;
+ if (! get_parameters_from_pcell_and_guiding_shapes (layout, obj.cell_index (), parameters_for_pcell)) {
+ return std::make_pair (false, lay::ObjectInstPath ());
}
- std::vector new_sel;
+ bool found = false;
+ lay::ObjectInstPath new_obj = obj;
if (parent_cell != std::numeric_limits ::max ()) {
@@ -1550,21 +1544,20 @@ Service::handle_guiding_shape_changes ()
// try to identify the selected shape in the new shapes and select this one
db::Shapes::shape_iterator sh = layout->cell (new_inst.cell_index ()).shapes (layout->guiding_shape_layer ()).begin (db::ShapeIterator::All);
- while (! sh.at_end ()) {
+ while (! sh.at_end () && !found) {
const db::PropertiesRepository::properties_set &props = layout->properties_repository ().properties (sh->prop_id ());
db::PropertiesRepository::properties_set::const_iterator pv = props.find (pn);
if (pv != props.end ()) {
if (pv->second.to_string () == shape_name) {
- new_sel.push_back (*s);
- new_sel.back ().back ().inst_ptr = new_inst;
- new_sel.back ().back ().array_inst = new_inst.begin ();
- new_sel.back ().set_shape (*sh);
- break;
+ new_obj.back ().inst_ptr = new_inst;
+ new_obj.back ().array_inst = new_inst.begin ();
+ new_obj.set_shape (*sh);
+ found = true;
}
}
++sh;
}
-
+
}
if (top_cell != std::numeric_limits ::max ()) {
@@ -1572,12 +1565,33 @@ Service::handle_guiding_shape_changes ()
// Currently there is not way to create such a configuration ...
}
- // remove superfluous proxies
- layout->cleanup ();
+ return std::make_pair (found, new_obj);
+}
- set_selection (new_sel.begin (), new_sel.end ());
+bool
+Service::handle_guiding_shape_changes ()
+{
+ // just allow one guiding shape to be selected
+ if (m_selection.empty ()) {
+ return false;
+ }
- return true;
+ std::pair gs = handle_guiding_shape_changes (*m_selection.begin ());
+ if (gs.first) {
+
+ // remove superfluous proxies
+ view ()->cellview (gs.second.cv_index ())->layout ().cleanup ();
+
+ // re-set the selection
+ std::vector new_sel;
+ new_sel.push_back (gs.second);
+ set_selection (new_sel.begin (), new_sel.end ());
+
+ return true;
+
+ } else {
+ return false;
+ }
}
diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h
index 6a02cb6f8..ba0855b08 100644
--- a/src/edt/edt/edtService.h
+++ b/src/edt/edt/edtService.h
@@ -342,9 +342,19 @@ public:
* @brief Handle changes in the guiding shapes, i.e. create PCell variants
*
* @return true, if PCell's have been updated, indicating that our selection is no longer valid
+ *
+ * This version assumes there is only one guiding shape selected and will update the selection.
+ * It will also call layout.cleanup() if required.
*/
bool handle_guiding_shape_changes ();
+ /**
+ * @brief Handle changes in a specific guiding shape, i.e. create new PCell variants if required
+ *
+ * @return A pair of bool (indicating that the object path has changed) and the new guiding shape path
+ */
+ std::pair handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const;
+
protected:
/**
* @brief Update m_markers to reflect the selection
diff --git a/src/gsi/gsi/gsiObject.cc b/src/gsi/gsi/gsiObject.cc
index 7d11f0582..0ed9bc3a4 100644
--- a/src/gsi/gsi/gsiObject.cc
+++ b/src/gsi/gsi/gsiObject.cc
@@ -27,9 +27,13 @@
#include "tlLog.h"
+#include
+
namespace gsi
{
+QMutex Proxy::m_lock;
+
Proxy::Proxy (const gsi::ClassBase *_cls_decl)
: m_cls_decl (_cls_decl),
m_obj (0),
@@ -43,21 +47,34 @@ Proxy::Proxy (const gsi::ClassBase *_cls_decl)
Proxy::~Proxy ()
{
- try {
- set (0, false, false, false);
- } catch (std::exception &ex) {
- tl::warn << "Caught exception in object destructor: " << ex.what ();
- } catch (tl::Exception &ex) {
- tl::warn << "Caught exception in object destructor: " << ex.msg ();
- } catch (...) {
- tl::warn << "Caught unspecified exception in object destructor";
+ void *prev_obj = 0;
+
+ {
+ QMutexLocker locker (&m_lock);
+ try {
+ prev_obj = set_internal (0, false, false, false);
+ } catch (std::exception &ex) {
+ tl::warn << "Caught exception in object destructor: " << ex.what ();
+ } catch (tl::Exception &ex) {
+ tl::warn << "Caught exception in object destructor: " << ex.msg ();
+ } catch (...) {
+ tl::warn << "Caught unspecified exception in object destructor";
+ }
+ m_destroyed = true;
+ }
+
+ // destroy outside the locker because the destructor may raise status
+ // changed events
+ if (prev_obj) {
+ m_cls_decl->destroy (prev_obj);
}
- m_destroyed = true;
}
void
Proxy::destroy ()
{
+ QMutexLocker locker (&m_lock);
+
if (! m_cls_decl) {
m_obj = 0;
return;
@@ -82,7 +99,7 @@ Proxy::destroy ()
if (m_owned || m_can_destroy) {
o = m_obj;
}
- detach ();
+ detach_internal ();
if (o) {
m_cls_decl->destroy (o);
}
@@ -91,28 +108,20 @@ Proxy::destroy ()
void
Proxy::detach ()
{
- if (! m_destroyed && m_cls_decl && m_cls_decl->is_managed ()) {
- gsi::ObjectBase *gsi_object = m_cls_decl->gsi_object (m_obj, false);
- if (gsi_object) {
- gsi_object->status_changed_event ().remove (this, &Proxy::object_status_changed);
- }
- }
-
- m_obj = 0;
- m_destroyed = true;
- m_const_ref = false;
- m_owned = false;
- m_can_destroy = false;
+ QMutexLocker locker (&m_lock);
+ detach_internal ();
}
void
Proxy::release ()
{
+ QMutexLocker locker (&m_lock);
+
// If the object is managed we first reset the ownership of all other clients
// and then make us the owner
const gsi::ClassBase *cls = m_cls_decl;
if (cls && cls->is_managed ()) {
- void *o = obj ();
+ void *o = obj_internal ();
if (o) {
cls->gsi_object (o)->keep ();
}
@@ -125,9 +134,11 @@ Proxy::release ()
void
Proxy::keep ()
{
+ QMutexLocker locker (&m_lock);
+
const gsi::ClassBase *cls = m_cls_decl;
if (cls) {
- void *o = obj ();
+ void *o = obj_internal ();
if (o) {
if (cls->is_managed ()) {
cls->gsi_object (o)->keep ();
@@ -142,12 +153,68 @@ Proxy::keep ()
void
Proxy::set (void *obj, bool owned, bool const_ref, bool can_destroy)
+{
+ void *prev_obj;
+
+ {
+ QMutexLocker locker (&m_lock);
+ prev_obj = set_internal (obj, owned, const_ref, can_destroy);
+ }
+
+ // destroy outside the locker because the destructor may raise status
+ // changed events
+ if (prev_obj) {
+ m_cls_decl->destroy (prev_obj);
+ }
+}
+
+void *
+Proxy::obj ()
+{
+ QMutexLocker locker (&m_lock);
+ return obj_internal ();
+}
+
+void *
+Proxy::obj_internal ()
+{
+ if (! m_obj) {
+ if (m_destroyed) {
+ throw tl::Exception (tl::to_string (QObject::tr ("Object has been destroyed already")));
+ } else {
+ // delayed creation of a detached C++ object ..
+ tl_assert (set_internal (m_cls_decl->create (), true, false, true) == 0);
+ }
+ }
+
+ return m_obj;
+}
+
+void
+Proxy::object_status_changed (gsi::ObjectBase::StatusEventType type)
+{
+ if (type == gsi::ObjectBase::ObjectDestroyed) {
+ QMutexLocker locker (&m_lock);
+ m_destroyed = true; // NOTE: must be set before detach and indicates that the object was destroyed externally.
+ detach_internal ();
+ } else if (type == gsi::ObjectBase::ObjectKeep) {
+ // NOTE: don't lock this as this will cause a deadlock from keep()
+ m_owned = false;
+ } else if (type == gsi::ObjectBase::ObjectRelease) {
+ // NOTE: don't lock this as this will cause a deadlock from release()
+ m_owned = true;
+ }
+}
+
+void *
+Proxy::set_internal (void *obj, bool owned, bool const_ref, bool can_destroy)
{
bool prev_owned = m_owned;
m_owned = owned;
m_can_destroy = can_destroy;
m_const_ref = const_ref;
+ void *prev_object = 0;
const gsi::ClassBase *cls = m_cls_decl;
if (! cls) {
@@ -169,9 +236,8 @@ Proxy::set (void *obj, bool owned, bool const_ref, bool can_destroy)
// Destroy the object if we are owner. We don't destroy the object if it was locked
// (either because we are not owner or from C++ side using keep())
if (prev_owned) {
- void *o = m_obj;
+ prev_object = m_obj;
m_obj = 0;
- cls->destroy (o);
}
}
@@ -196,34 +262,25 @@ Proxy::set (void *obj, bool owned, bool const_ref, bool can_destroy)
// now we have a valid object (or nil) - we can reset "destroyed" state. Note: this has to be done
// here because before detach might be called on *this which resets m_destroyed.
m_destroyed = false;
-}
-void *
-Proxy::obj ()
-{
- if (! m_obj) {
- if (m_destroyed) {
- throw tl::Exception (tl::to_string (tr ("Object has been destroyed already")));
- } else {
- // delayed creation of a detached C++ object ..
- set(m_cls_decl->create (), true, false, true);
- }
- }
-
- return m_obj;
+ return prev_object;
}
void
-Proxy::object_status_changed (gsi::ObjectBase::StatusEventType type)
+Proxy::detach_internal()
{
- if (type == gsi::ObjectBase::ObjectDestroyed) {
- m_destroyed = true; // NOTE: must be set before detach and indicates that the object was destroyed externally.
- detach ();
- } else if (type == gsi::ObjectBase::ObjectKeep) {
- m_owned = false;
- } else if (type == gsi::ObjectBase::ObjectRelease) {
- m_owned = true;
+ if (! m_destroyed && m_cls_decl && m_cls_decl->is_managed ()) {
+ gsi::ObjectBase *gsi_object = m_cls_decl->gsi_object (m_obj, false);
+ if (gsi_object) {
+ gsi_object->status_changed_event ().remove (this, &Proxy::object_status_changed);
+ }
}
+
+ m_obj = 0;
+ m_destroyed = true;
+ m_const_ref = false;
+ m_owned = false;
+ m_can_destroy = false;
}
}
diff --git a/src/gsi/gsi/gsiObject.h b/src/gsi/gsi/gsiObject.h
index cc39f2fb9..223c9f8d2 100644
--- a/src/gsi/gsi/gsiObject.h
+++ b/src/gsi/gsi/gsiObject.h
@@ -29,6 +29,7 @@
#include "gsiCommon.h"
#include
+#include
// For a comprehensive documentation see gsi.h
@@ -191,6 +192,17 @@ private:
*
* Using a proxy object allows having a gsi::ObjectBase that does not derive
* from tl::Object and can derive from any base class (specifically QObject).
+ *
+ * NOTE about MT safety: the model is:
+ *
+ * - the Proxy belongs to a thread
+ * - the original object (the proxy target) belongs to a different thread
+ * - there can be multiple Proxy objects for one target
+ * - the target object itself and the methods by which the proxy acts on
+ * the target object are thread safe
+ *
+ * This implies that all operations related to the manipulation of proxy
+ * to target relation need to be guarded by a lock.
*/
class GSI_PUBLIC Proxy
: public tl::Object
@@ -228,8 +240,12 @@ private:
bool m_const_ref : 1;
bool m_destroyed : 1;
bool m_can_destroy : 1;
+ static QMutex m_lock;
+ void *set_internal (void *obj, bool owned, bool const_ref, bool can_destroy);
void object_status_changed (gsi::ObjectBase::StatusEventType type);
+ void detach_internal ();
+ void *obj_internal ();
};
}
diff --git a/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc b/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc
index c6346d696..01e967cc1 100644
--- a/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc
+++ b/src/gsiqt/qt5/QtNetwork/gsiDeclQHostAddress.cc
@@ -70,25 +70,6 @@ static void _call_ctor_QHostAddress_1098 (const qt_gsi::GenericStaticMethod * /*
}
-// Constructor QHostAddress::QHostAddress(const quint8 *ip6Addr)
-
-
-static void _init_ctor_QHostAddress_1934 (qt_gsi::GenericStaticMethod *decl)
-{
- static gsi::ArgSpecBase argspec_0 ("ip6Addr");
- decl->add_arg (argspec_0);
- decl->set_return_new ();
-}
-
-static void _call_ctor_QHostAddress_1934 (const qt_gsi::GenericStaticMethod * /*decl*/, gsi::SerialArgs &args, gsi::SerialArgs &ret)
-{
- __SUPPRESS_UNUSED_WARNING(args);
- tl::Heap heap;
- const quint8 *arg1 = args.read (heap);
- ret.write (new QHostAddress (arg1));
-}
-
-
// Constructor QHostAddress::QHostAddress(const QString &address)
@@ -397,26 +378,6 @@ static void _call_f_setAddress_1098 (const qt_gsi::GenericMethod * /*decl*/, voi
}
-// void QHostAddress::setAddress(const quint8 *ip6Addr)
-
-
-static void _init_f_setAddress_1934 (qt_gsi::GenericMethod *decl)
-{
- static gsi::ArgSpecBase argspec_0 ("ip6Addr");
- decl->add_arg (argspec_0);
- decl->set_return ();
-}
-
-static void _call_f_setAddress_1934 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
-{
- __SUPPRESS_UNUSED_WARNING(args);
- tl::Heap heap;
- const quint8 *arg1 = args.read (heap);
- __SUPPRESS_UNUSED_WARNING(ret);
- ((QHostAddress *)cls)->setAddress (arg1);
-}
-
-
// bool QHostAddress::setAddress(const QString &address)
@@ -532,7 +493,6 @@ static gsi::Methods methods_QHostAddress () {
gsi::Methods methods;
methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QHostAddress::QHostAddress()\nThis method creates an object of class QHostAddress.", &_init_ctor_QHostAddress_0, &_call_ctor_QHostAddress_0);
methods += new qt_gsi::GenericStaticMethod ("new_ip4", "@brief Constructor QHostAddress::QHostAddress(quint32 ip4Addr)\nThis method creates an object of class QHostAddress.", &_init_ctor_QHostAddress_1098, &_call_ctor_QHostAddress_1098);
- methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QHostAddress::QHostAddress(const quint8 *ip6Addr)\nThis method creates an object of class QHostAddress.", &_init_ctor_QHostAddress_1934, &_call_ctor_QHostAddress_1934);
methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QHostAddress::QHostAddress(const QString &address)\nThis method creates an object of class QHostAddress.", &_init_ctor_QHostAddress_2025, &_call_ctor_QHostAddress_2025);
methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QHostAddress::QHostAddress(const QHostAddress ©)\nThis method creates an object of class QHostAddress.", &_init_ctor_QHostAddress_2518, &_call_ctor_QHostAddress_2518);
methods += new qt_gsi::GenericStaticMethod ("new_special", "@brief Constructor QHostAddress::QHostAddress(QHostAddress::SpecialAddress address)\nThis method creates an object of class QHostAddress.", &_init_ctor_QHostAddress_3172, &_call_ctor_QHostAddress_3172);
@@ -550,7 +510,6 @@ static gsi::Methods methods_QHostAddress () {
methods += new qt_gsi::GenericMethod ("protocol", "@brief Method QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol()\n", true, &_init_f_protocol_c0, &_call_f_protocol_c0);
methods += new qt_gsi::GenericMethod (":scopeId", "@brief Method QString QHostAddress::scopeId()\n", true, &_init_f_scopeId_c0, &_call_f_scopeId_c0);
methods += new qt_gsi::GenericMethod ("setAddress", "@brief Method void QHostAddress::setAddress(quint32 ip4Addr)\n", false, &_init_f_setAddress_1098, &_call_f_setAddress_1098);
- methods += new qt_gsi::GenericMethod ("setAddress", "@brief Method void QHostAddress::setAddress(const quint8 *ip6Addr)\n", false, &_init_f_setAddress_1934, &_call_f_setAddress_1934);
methods += new qt_gsi::GenericMethod ("setAddress", "@brief Method bool QHostAddress::setAddress(const QString &address)\n", false, &_init_f_setAddress_2025, &_call_f_setAddress_2025);
methods += new qt_gsi::GenericMethod ("setScopeId|scopeId=", "@brief Method void QHostAddress::setScopeId(const QString &id)\n", false, &_init_f_setScopeId_2025, &_call_f_setScopeId_2025);
methods += new qt_gsi::GenericMethod ("toIPv4Address", "@brief Method quint32 QHostAddress::toIPv4Address()\n", true, &_init_f_toIPv4Address_c0, &_call_f_toIPv4Address_c0);
diff --git a/src/lay/lay/layFontController.cc b/src/lay/lay/layFontController.cc
index ae4cbbd6b..40f5014dc 100644
--- a/src/lay/lay/layFontController.cc
+++ b/src/lay/lay/layFontController.cc
@@ -112,13 +112,11 @@ FontController::can_exit (lay::PluginRoot * /*root*/) const
void
FontController::sync_dirs ()
{
- if (! m_file_watcher) {
- return;
+ if (m_file_watcher) {
+ m_file_watcher->clear ();
+ m_file_watcher->enable (false);
}
- m_file_watcher->clear ();
- m_file_watcher->enable (false);
-
std::vector paths = lay::ApplicationBase::instance ()->klayout_path ();
// add the salt grains as potential sources for library definitions
@@ -137,14 +135,18 @@ FontController::sync_dirs ()
for (std::vector ::const_iterator p = paths.begin (); p != paths.end (); ++p) {
QDir fp = QDir (tl::to_qstring (*p)).filePath (tl::to_qstring ("fonts"));
if (fp.exists ()) {
- m_file_watcher->add_file (tl::to_string (fp.absolutePath ()));
+ if (m_file_watcher) {
+ m_file_watcher->add_file (tl::to_string (fp.absolutePath ()));
+ }
font_paths.push_back (tl::to_string (fp.absolutePath ()));
}
}
db::TextGenerator::set_font_paths (font_paths);
- m_file_watcher->enable (true);
+ if (m_file_watcher) {
+ m_file_watcher->enable (true);
+ }
}
void
diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc
index d2db3c047..065c24b61 100644
--- a/src/lay/lay/layLibraryController.cc
+++ b/src/lay/lay/layLibraryController.cc
@@ -117,13 +117,11 @@ LibraryController::can_exit (lay::PluginRoot * /*root*/) const
void
LibraryController::sync_files ()
{
- if (! m_file_watcher) {
- return;
+ if (m_file_watcher) {
+ m_file_watcher->clear ();
+ m_file_watcher->enable (false);
}
- m_file_watcher->clear ();
- m_file_watcher->enable (false);
-
std::map > new_lib_files;
// build a list of paths vs. technology
@@ -158,7 +156,9 @@ LibraryController::sync_files ()
QDir lp = QDir (tl::to_qstring (p->first)).filePath (tl::to_qstring ("libraries"));
if (lp.exists ()) {
- m_file_watcher->add_file (tl::to_string (lp.absolutePath ()));
+ if (m_file_watcher) {
+ m_file_watcher->add_file (tl::to_string (lp.absolutePath ()));
+ }
QStringList name_filters;
name_filters << QString::fromUtf8 ("*");
@@ -222,7 +222,9 @@ LibraryController::sync_files ()
}
- m_file_watcher->enable (true);
+ if (m_file_watcher) {
+ m_file_watcher->enable (true);
+ }
// remove libraries which are no longer present
diff --git a/src/lay/lay/layMacroController.cc b/src/lay/lay/layMacroController.cc
index d312e52eb..eb0ba1dbf 100644
--- a/src/lay/lay/layMacroController.cc
+++ b/src/lay/lay/layMacroController.cc
@@ -147,7 +147,10 @@ MacroController::finish ()
if (! m_no_implicit_macros) {
for (std::vector ::const_iterator p = m_external_paths.begin (); p != m_external_paths.end (); ++p) {
- lym::MacroCollection::root ().add_folder (p->description, p->path, p->cat, p->readonly);
+ lym::MacroCollection *mc = lym::MacroCollection::root ().add_folder (p->description, p->path, p->cat, p->readonly);
+ if (mc) {
+ mc->set_virtual_mode (p->type);
+ }
}
}
diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc
index f82350c44..eb5eceb26 100644
--- a/src/lay/lay/layMacroEditorPage.cc
+++ b/src/lay/lay/layMacroEditorPage.cc
@@ -1004,6 +1004,8 @@ MacroEditorPage::replace_all (const QString &replace)
int i = m_current_search.indexIn (b.text (), o);
if (i < 0) {
break;
+ } else if (m_current_search.matchedLength () == 0) {
+ break; // avoid an infinite loop
}
QString r = interpolate_string (replace, m_current_search);
diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc
index 469583247..94d3941c3 100644
--- a/src/lay/lay/layMainWindow.cc
+++ b/src/lay/lay/layMainWindow.cc
@@ -842,6 +842,7 @@ MainWindow::init_menu ()
MenuLayoutEntry ("show_grid", tl::to_string (QObject::tr ("Show Grid")), std::make_pair (cfg_grid_visible, "?")),
MenuLayoutEntry ("default_grid:default_grids_group", tl::to_string (QObject::tr ("Grid")), empty_menu),
MenuLayoutEntry::separator ("layout_group"),
+ MenuLayoutEntry ("show_markers", tl::to_string (QObject::tr ("Show Markers")), std::make_pair (cfg_markers_visible, "?")),
MenuLayoutEntry ("show_texts", tl::to_string (QObject::tr ("Show Texts")), std::make_pair (cfg_text_visible, "?")),
MenuLayoutEntry ("show_cell_boxes", tl::to_string (QObject::tr ("Show Cell Frames")), std::make_pair (cfg_cell_box_visible, "?")),
MenuLayoutEntry ("no_stipples", tl::to_string (QObject::tr ("Show Layers Without Fill")), std::make_pair (cfg_no_stipple, "?")),
@@ -1312,6 +1313,18 @@ MainWindow::about_to_exec ()
}
}
+ f = false;
+ config_get (cfg_markers_visible, f);
+ if (! f) {
+ TipDialog td (this,
+ tl::to_string (QObject::tr ("Markers are not visible because they have been turned off.\nYou may not see markers when using the marker browser feature.\n\nTo turn markers on, check \"Show Markers\" in the \"View\" menu.")),
+ "show-markers");
+ if (td.exec_dialog ()) {
+ // Don't bother the user with more dialogs.
+ return;
+ }
+ }
+
f = false;
config_get (cfg_hide_empty_layers, f);
if (f) {
@@ -3162,6 +3175,8 @@ MainWindow::do_save (bool as)
options.set_dbu (cv->layout ().dbu ());
options.set_format_from_filename (fn);
+ cv->update_save_options (options);
+
tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto;
if (as && ! mp_layout_save_as_options->get_options (current_view (), cv_index, fn, om, options)) {
diff --git a/src/lay/lay/laySaltDownloadManager.cc b/src/lay/lay/laySaltDownloadManager.cc
index 529880cf2..6794da470 100644
--- a/src/lay/lay/laySaltDownloadManager.cc
+++ b/src/lay/lay/laySaltDownloadManager.cc
@@ -68,7 +68,7 @@ ConfirmationDialog::add_info (const std::string &name, bool update, const std::s
item->setText (3, tl::to_qstring (url));
for (int column = 0; column < list->colorCount (); ++column) {
- item->setData (column, Qt::ForegroundRole, QVariant (QBrush (update ? Qt::blue : Qt::black)));
+ item->setData (column, Qt::ForegroundRole, QVariant (QBrush (update ? QColor (Qt::blue) : QColor (Qt::black))));
}
}
diff --git a/src/lay/lay/laySaltModel.cc b/src/lay/lay/laySaltModel.cc
index db9139800..3cce6c3a9 100644
--- a/src/lay/lay/laySaltModel.cc
+++ b/src/lay/lay/laySaltModel.cc
@@ -47,8 +47,15 @@ SaltItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option,
{
QStyleOptionViewItemV4 optionV4 = option;
initStyleOption (&optionV4, index);
+ // let the text take all the available space (fixes #144)
+ optionV4.showDecorationSelected = true;
bool is_enabled = (optionV4.state & QStyle::State_Enabled);
+ if ((index.flags () & 0x10000) != 0) {
+ // the item wants to be drawn "disabled"
+ is_enabled = false;
+ }
+
optionV4.state |= QStyle::State_Enabled;
QStyle *style = optionV4.widget ? optionV4.widget->style () : QApplication::style ();
@@ -121,6 +128,11 @@ SaltModel::flags (const QModelIndex &index) const
f &= ~Qt::ItemIsEnabled;
}
+ if (g && (! is_enabled (g->name ()) || g->is_hidden ())) {
+ // We use a custom flag to indicate "disabled" display without actually disabling the item
+ f |= Qt::ItemFlags (0x10000);
+ }
+
return f;
}
@@ -134,15 +146,9 @@ SaltModel::data (const QModelIndex &index, int role) const
return QVariant (tr ("There are no items to show in this list
%1
").arg (m_empty_explanation));
}
- bool en = is_enabled (g->name ());
bool hidden = g->is_hidden ();
std::string text = "";
- if (! en || hidden) {
- text += "";
- } else {
- text += "";
- }
if (hidden) {
text += "";
}
@@ -179,7 +185,6 @@ SaltModel::data (const QModelIndex &index, int role) const
text += tl::to_string (tr ("This package is an auxiliary package for use with other packages."));
text += "
";
}
- text += "";
text += "";
return tl::to_qstring (text);
diff --git a/src/laybasic/laybasic/gsiDeclLayMarker.cc b/src/laybasic/laybasic/gsiDeclLayMarker.cc
index 15dab23ed..0b78ce91d 100644
--- a/src/laybasic/laybasic/gsiDeclLayMarker.cc
+++ b/src/laybasic/laybasic/gsiDeclLayMarker.cc
@@ -164,6 +164,18 @@ Class decl_Marker ("lay", "Marker",
"@brief Returns a value indicating whether the marker has a specific frame color\n"
"The set method has been added in version 0.20.\n"
) +
+ gsi::method ("dismissable=", &lay::DMarker::set_dismissable,
+ "@brief Sets a value indicating whether the marker can be hidden\n"
+ "@args flag\n"
+ "Dismissable markers can be hidden setting \"View/Show Markers\" to \"off\". "
+ "The default setting is \"false\" meaning the marker can't be hidden.\n"
+ "\n"
+ "This attribute has been introduced in version 0.25.4."
+ ) +
+ gsi::method ("dismissable?", &lay::DMarker::get_dismissable,
+ "@brief Gets a value indicating whether the marker can be hidden\n"
+ "See \\dismissable= for a description of this predicate."
+ ) +
gsi::method ("line_width=", &lay::DMarker::set_line_width,
"@brief Sets the line width of the marker\n"
"@args width\n"
diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc
index 2647e3537..a1884acb0 100644
--- a/src/laybasic/laybasic/layCellView.cc
+++ b/src/laybasic/laybasic/layCellView.cc
@@ -258,6 +258,31 @@ LayoutHandle::set_save_options (const db::SaveLayoutOptions &options, bool valid
m_save_options_valid = valid;
}
+void
+LayoutHandle::update_save_options (db::SaveLayoutOptions &options)
+{
+ for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) {
+
+ const lay::StreamWriterPluginDeclaration *decl = dynamic_cast (&*cls);
+ if (! decl) {
+ continue;
+ }
+
+ std::auto_ptr specific_options;
+ if (options.get_options (decl->format_name ())) {
+ specific_options.reset (options.get_options (decl->format_name ())->clone ());
+ } else {
+ specific_options.reset (decl->create_specific_options ());
+ }
+
+ if (specific_options.get ()) {
+ decl->initialize_options_from_layout_handle (specific_options.get (), *this);
+ options.set_options (specific_options.release ());
+ }
+
+ }
+}
+
void
LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update)
{
diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h
index 497c1d1c8..f2ff917b2 100644
--- a/src/laybasic/laybasic/layCellView.h
+++ b/src/laybasic/laybasic/layCellView.h
@@ -234,6 +234,15 @@ public:
return m_save_options;
}
+ /**
+ * @brief Updates the given save options with attributes from this cell view
+ *
+ * Some formats will initialize attributes from the cell view and the layout's
+ * metadata (example: libname of GDS2). This method will update the options
+ * if the layout provides attributes for initializing the latter.
+ */
+ void update_save_options (db::SaveLayoutOptions &options);
+
/**
* @brief Gets a flag indicating whether the save options are valid
*
diff --git a/src/laybasic/laybasic/layDialogs.cc b/src/laybasic/laybasic/layDialogs.cc
index 73df4ae4f..31fb50d10 100644
--- a/src/laybasic/laybasic/layDialogs.cc
+++ b/src/laybasic/laybasic/layDialogs.cc
@@ -625,6 +625,18 @@ ReplaceCellOptionsDialog::~ReplaceCellOptionsDialog ()
mp_ui = 0;
}
+static std::pair
+find_cell_by_display_name (const db::Layout &layout, const std::string &cn)
+{
+ for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) {
+ if (layout.display_name (c->cell_index ()) == cn) {
+ return std::make_pair (true, c->cell_index ());
+ }
+ }
+
+ return std::make_pair (false, 0);
+}
+
bool
ReplaceCellOptionsDialog::exec_dialog (const lay::CellView &cv, int &replace_mode, db::cell_index_type &cell_index)
{
@@ -647,7 +659,7 @@ ReplaceCellOptionsDialog::exec_dialog (const lay::CellView &cv, int &replace_mod
}
std::string cn = tl::to_string (mp_ui->cell_selection_cbx->lineEdit ()->text ());
- std::pair cc = cv->layout ().cell_by_name (cn.c_str ());
+ std::pair cc = find_cell_by_display_name (cv->layout (), cn.c_str ());
cell_index = cc.second;
return cc.first;
@@ -665,7 +677,7 @@ BEGIN_PROTECTED;
lay::CellTreeModel *model = dynamic_cast (mp_ui->cell_selection_cbx->model ());
if (model) {
std::string cn = tl::to_string (mp_ui->cell_selection_cbx->lineEdit ()->text ());
- std::pair cc = model->layout ()->cell_by_name (cn.c_str ());
+ std::pair cc = find_cell_by_display_name (*model->layout (), cn.c_str ());
if (! cc.first) {
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell name: ")) + cn);
}
diff --git a/src/laybasic/laybasic/layDitherPattern.cc b/src/laybasic/laybasic/layDitherPattern.cc
index b4faa1e93..efe54d0ff 100644
--- a/src/laybasic/laybasic/layDitherPattern.cc
+++ b/src/laybasic/laybasic/layDitherPattern.cc
@@ -555,6 +555,7 @@ DitherPatternInfo::operator< (const DitherPatternInfo &d) const
return m_order_index < d.m_order_index;
}
+// TODO including a scaling algorithm in this formula, or give more resolution to the dither
QBitmap
DitherPatternInfo::get_bitmap (int width, int height) const
{
diff --git a/src/laybasic/laybasic/layLayerToolbox.cc b/src/laybasic/laybasic/layLayerToolbox.cc
index 583478775..b1e303714 100644
--- a/src/laybasic/laybasic/layLayerToolbox.cc
+++ b/src/laybasic/laybasic/layLayerToolbox.cc
@@ -200,16 +200,29 @@ LCPDitherPalette::create_pixmap_for (LCPActiveLabel *b, int n)
const unsigned int h = 24;
const unsigned int w = 24;
- QImage image (w, h, QImage::Format_RGB32);
- image.fill (color0.rgb ());
+#if QT_VERSION > 0x050000
+ unsigned int dpr = devicePixelRatio ();
+#else
+ unsigned int dpr = 1;
+#endif
- QBitmap bitmap = pattern.pattern (n).get_bitmap (w, h);
+ QImage image (w * dpr, h * dpr, QImage::Format_RGB32);
+ image.fill (color0.rgb ());
+#if QT_VERSION > 0x050000
+ image.setDevicePixelRatio (dpr);
+#endif
+
+ // TODO include a scaling algorithm in get_bitmap, because it looks small in highDPI screens
+ QBitmap bitmap = pattern.pattern (n).get_bitmap (w * dpr, h * dpr);
QPainter painter (&image);
painter.setPen (QPen (color1));
painter.setBackgroundMode (Qt::TransparentMode);
- painter.drawPixmap (0, 0, bitmap);
+ painter.drawPixmap (0, 0, w, h, bitmap);
QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround
+#if QT_VERSION > 0x050000
+ pixmap.setDevicePixelRatio (dpr);
+#endif
b->setPixmap (pixmap);
}
@@ -629,16 +642,28 @@ LCPStylePalette::create_pixmap_for_line_style (LCPActiveLabel *b, int n)
const unsigned int h = 14;
const unsigned int w = 24;
- QImage image (w, h, QImage::Format_RGB32);
- image.fill (color0.rgb ());
+#if QT_VERSION > 0x050000
+ unsigned int dpr = devicePixelRatio ();
+#else
+ unsigned int dpr = 1;
+#endif
- QBitmap bitmap = styles.style (n).get_bitmap (w, h);
+ QImage image (dpr * w, dpr * h, QImage::Format_RGB32);
+ image.fill (color0.rgb ());
+#if QT_VERSION > 0x050000
+ image.setDevicePixelRatio (dpr);
+#endif
+
+ QBitmap bitmap = styles.style (n).get_bitmap (dpr * w, dpr * h);
QPainter painter (&image);
painter.setPen (QPen (color1));
painter.setBackgroundMode (Qt::TransparentMode);
- painter.drawPixmap (0, 0, bitmap);
+ painter.drawPixmap (0, 0, w, h, bitmap);
QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround
+#if QT_VERSION > 0x050000
+ pixmap.setDevicePixelRatio (dpr);
+#endif
b->setPixmap (pixmap);
}
diff --git a/src/laybasic/laybasic/layLayoutCanvas.cc b/src/laybasic/laybasic/layLayoutCanvas.cc
index ce8293b35..ae6fa4379 100644
--- a/src/laybasic/laybasic/layLayoutCanvas.cc
+++ b/src/laybasic/laybasic/layLayoutCanvas.cc
@@ -291,8 +291,6 @@ LayoutCanvas::LayoutCanvas (QWidget *parent, lay::LayoutView *view, const char *
{
#if QT_VERSION > 0x050000
m_dpr = devicePixelRatio ();
-#else
- m_dpr = 1;
#endif
// The gamma value used for subsampling: something between 1.8 and 2.2.
diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc
index abd68dba5..3691ff491 100644
--- a/src/laybasic/laybasic/layLayoutView.cc
+++ b/src/laybasic/laybasic/layLayoutView.cc
@@ -384,6 +384,7 @@ LayoutView::init (db::Manager *mgr, lay::PluginRoot *root, QWidget * /*parent*/)
m_apply_text_trans = true;
m_default_text_size = 0.1;
m_text_font = 0;
+ m_show_markers = true;
m_no_stipples = false;
m_stipple_offset = true;
m_fit_new_cell = true;
@@ -1155,6 +1156,13 @@ LayoutView::configure (const std::string &name, const std::string &value)
apply_text_trans (flag);
return true;
+ } else if (name == cfg_markers_visible) {
+
+ bool flag;
+ tl::from_string (value, flag);
+ mp_canvas->set_dismiss_view_objects (! flag);
+ return true;
+
} else if (name == cfg_no_stipple) {
bool flag;
@@ -4923,7 +4931,16 @@ LayoutView::no_stipples (bool f)
}
}
-void
+void
+LayoutView::show_markers (bool f)
+{
+ if (m_show_markers != f) {
+ m_show_markers = f;
+ mp_canvas->update_image ();
+ }
+}
+
+void
LayoutView::text_color (QColor c)
{
if (m_text_color != c) {
diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h
index 5d5d2e4c3..a9e3ff5aa 100644
--- a/src/laybasic/laybasic/layLayoutView.h
+++ b/src/laybasic/laybasic/layLayoutView.h
@@ -1062,6 +1062,19 @@ public:
return m_default_text_size;
}
+ /**
+ * @brief Show or hide markers
+ */
+ void show_markers (bool f);
+
+ /**
+ * @brief "show_markers" property getter
+ */
+ bool show_markers () const
+ {
+ return m_show_markers;
+ }
+
/**
* @brief Don't show stipples
*/
@@ -2671,6 +2684,7 @@ private:
bool m_apply_text_trans;
double m_default_text_size;
unsigned int m_text_font;
+ bool m_show_markers;
bool m_no_stipples;
bool m_stipple_offset;
diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.cc b/src/laybasic/laybasic/layLayoutViewConfigPages.cc
index 9cfb81595..dd31d6856 100644
--- a/src/laybasic/laybasic/layLayoutViewConfigPages.cc
+++ b/src/laybasic/laybasic/layLayoutViewConfigPages.cc
@@ -819,9 +819,22 @@ LayoutViewConfigPage4::update ()
}
}
+#if QT_VERSION > 0x050000
+ unsigned int dpr = 1; //devicePixelRatio ();
+#else
+ unsigned int dpr = 1;
+#endif
+
QFontMetrics fm (font (), this);
QRect rt (fm.boundingRect (QString::fromUtf8 ("AA")));
- QPixmap pxmp (rt.width () + 10, rt.height () + 10);
+
+ const unsigned int h = rt.height () + 10;
+ const unsigned int w = rt.width () + 10;
+
+ QPixmap pxmp (w * dpr, h * dpr);
+#if QT_VERSION > 0x050000
+ pxmp.setDevicePixelRatio (dpr);
+#endif
QPainter pxpainter (&pxmp);
pxpainter.setPen (QPen (palette ().color (QPalette::Active, QPalette::Text)));
@@ -1159,29 +1172,29 @@ LayoutViewConfigPage6::update ()
const unsigned int h = rt.height () + 10;
const unsigned int w = rt.width () + 10;
- unsigned int color0 = palette ().color (QPalette::Active, QPalette::Button).rgb();
- unsigned int color1 = palette ().color (QPalette::Active, QPalette::Dark).rgb();
+ QColor color0 = palette ().color (QPalette::Active, QPalette::Button);
+ QColor color1 = palette ().color (QPalette::Active, QPalette::Dark);
- QImage image (w, h, QImage::Format_RGB32);
- if (s >= 0) {
- const uint32_t * const *dp = m_pattern.pattern ((unsigned int) s).pattern ();
- for (unsigned int l = 0; l < h; ++l, ++dp) {
- uint32_t m = **dp;
- if (l == 0 || l == h - 1) {
- m |= ((1 << w) - 1);
- } else {
- m |= ((1 << (w - 1)) | 1);
- }
- color_t *pt = (color_t *) image.scanLine (h - 1 - l);
- for (unsigned int b = 0; b < w; ++b) {
- *pt++ = (m & 1) ? color1 : color0;
- m >>= 1;
- }
- }
- }
+#if QT_VERSION > 0x050000
+ unsigned int dpr = 1; //devicePixelRatio ();
+#else
+ unsigned int dpr = 1;
+#endif
+
+ QImage image (w * dpr, h * dpr, QImage::Format_RGB32);
+#if QT_VERSION > 0x050000
+ image.setDevicePixelRatio (dpr);
+#endif
+ image.fill (color0.rgb ());
+
+ // copying code from layLayerToolbox.cc
+ QBitmap bitmap = m_pattern.pattern ((unsigned int) s).get_bitmap (w * dpr, h * dpr);
+ QPainter painter (&image);
+ painter.setPen (QPen (color1));
+ painter.setBackgroundMode (Qt::TransparentMode);
+ painter.drawPixmap (0, 0, w, h, bitmap);
QPixmap pxmp = QPixmap::fromImage (image); // Qt 4.6.0 workaround
-
QPainter pxpainter (&pxmp);
pxpainter.setPen (QPen (palette ().color (QPalette::Active, QPalette::Text)));
QRect r (0, 0, pxmp.width () - 1, pxmp.height () - 1);
@@ -1189,6 +1202,10 @@ LayoutViewConfigPage6::update ()
pxpainter.setFont (font ());
pxpainter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, text);
+#if QT_VERSION > 0x050000
+ pxmp.setDevicePixelRatio (dpr);
+#endif
+
(mp_ui->*(cfg6_buttons [i]))->setIconSize (pxmp.size ());
(mp_ui->*(cfg6_buttons [i]))->setIcon (QIcon (pxmp));
@@ -1366,16 +1383,28 @@ LayoutViewConfigPage6a::update ()
const unsigned int h = 26;
const unsigned int w = 26;
- QImage image (w, h, QImage::Format_RGB32);
- image.fill (color0.rgb ());
+#if QT_VERSION > 0x050000
+ unsigned int dpr = 1; //devicePixelRatio ();
+#else
+ unsigned int dpr = 1;
+#endif
- QBitmap bitmap = m_style.style (s).get_bitmap (w, h);
+ QImage image (w * dpr, h * dpr, QImage::Format_RGB32);
+ image.fill (color0.rgb ());
+#if QT_VERSION > 0x050000
+ image.setDevicePixelRatio (dpr);
+#endif
+
+ QBitmap bitmap = m_style.style (s).get_bitmap (w * dpr, h * dpr);
QPainter painter (&image);
painter.setPen (QPen (color1));
painter.setBackgroundMode (Qt::TransparentMode);
- painter.drawPixmap (0, 0, bitmap);
+ painter.drawPixmap (0, 0, w, h, bitmap);
QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround
+#if QT_VERSION > 0x050000
+ pixmap.setDevicePixelRatio (dpr);
+#endif
b->setIconSize (pixmap.size ());
b->setIcon (QIcon (pixmap));
@@ -1510,6 +1539,7 @@ public:
options.push_back (std::pair (cfg_stipple_offset, "true"));
options.push_back (std::pair (cfg_line_style_palette, lay::LineStylePalette ().to_string ()));
options.push_back (std::pair (cfg_no_stipple, "false"));
+ options.push_back (std::pair (cfg_markers_visible, "true"));
}
virtual std::vector > config_pages (QWidget *parent) const
diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc
index 80bd67f96..45a3d1554 100644
--- a/src/laybasic/laybasic/layMarker.cc
+++ b/src/laybasic/laybasic/layMarker.cc
@@ -270,7 +270,7 @@ MarkerBase::set_line_style (int line_style)
}
}
-void
+void
MarkerBase::get_bitmaps (const Viewport & /*vp*/, ViewObjectCanvas &canvas, lay::CanvasPlane *&fill, lay::CanvasPlane *&contour, lay::CanvasPlane *&vertex, lay::CanvasPlane *&text)
{
double resolution = canvas.resolution ();
diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc
index 7d67986e4..93d8ce130 100644
--- a/src/laybasic/laybasic/layRedrawThreadWorker.cc
+++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc
@@ -665,13 +665,11 @@ need_draw_box (const db::Layout *layout, const db::Cell &cell,
return int (cell.hierarchy_levels ()) + level >= to_level;
}
-void
-RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level)
+void
+RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level)
{
- lay::Renderer &r = *mp_renderer;
-
// do not draw, if there is nothing to draw
- if (mp_layout->cells () <= ci || vp.empty ()) {
+ if (mp_layout->cells () <= ci || redraw_regions.empty ()) {
return;
}
@@ -685,6 +683,17 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
return;
}
+ for (std::vector::const_iterator b = redraw_regions.begin (); b != redraw_regions.end (); ++b) {
+ draw_boxes (drawing_context, ci, trans, *b, level);
+ }
+}
+
+void
+RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level)
+{
+ lay::Renderer &r = *mp_renderer;
+ const db::Cell &cell = mp_layout->cell (ci);
+
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox ();
@@ -714,19 +723,19 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
} else {
db::box_convert bc (*mp_layout);
-
+
// create a set of boxes to look into
db::Coord aw = db::coord_traits::rounded (m_abstract_mode_width / mp_layout->dbu ());
std::vector vv;
if (level == 1 && m_abstract_mode_width > 0 && bbox.width () > db::Box::distance_type (aw * 2) && bbox.height () > db::Box::distance_type (aw * 2)) {
vv.reserve (4);
- vv.push_back (vp & db::Box (bbox.left (), bbox.bottom (), bbox.left () + aw, bbox.top ()));
- vv.push_back (vp & db::Box (bbox.right () - aw, bbox.bottom (), bbox.right (), bbox.top ()));
- vv.push_back (vp & db::Box (bbox.left () + aw, bbox.bottom (), bbox.right () - aw, bbox.bottom () + aw));
- vv.push_back (vp & db::Box (bbox.left () + aw, bbox.top () - aw, bbox.right () - aw, bbox.top ()));
+ vv.push_back (redraw_box & db::Box (bbox.left (), bbox.bottom (), bbox.left () + aw, bbox.top ()));
+ vv.push_back (redraw_box & db::Box (bbox.right () - aw, bbox.bottom (), bbox.right (), bbox.top ()));
+ vv.push_back (redraw_box & db::Box (bbox.left () + aw, bbox.bottom (), bbox.right () - aw, bbox.bottom () + aw));
+ vv.push_back (redraw_box & db::Box (bbox.left () + aw, bbox.top () - aw, bbox.right () - aw, bbox.top ()));
} else {
vv.reserve (1);
- vv.push_back (vp);
+ vv.push_back (redraw_box);
}
// dive down into the hierarchy ..
@@ -737,7 +746,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
bool anything = false;
db::cell_index_type last_ci = std::numeric_limits::max ();
- db::Cell::touching_iterator inst = cell.begin_touching (*v);
+ db::Cell::touching_iterator inst = cell.begin_touching (*v);
while (! inst.at_end ()) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
@@ -746,7 +755,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
db::Box new_cell_box = mp_layout->cell (new_ci).bbox ();
if (last_ci != new_ci) {
- // Hint: don't use any_cell_box on partially visible cells because that will degrade performance
+ // Hint: don't use any_cell_box on partially visible cells because that will degrade performance
if (new_cell_box.inside (*v)) {
last_ci = new_ci;
anything = any_cell_box (new_ci, m_to_level - (level + 1));
@@ -758,7 +767,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
if (anything) {
db::Vector a, b;
- unsigned long amax, bmax;
+ unsigned long amax, bmax;
bool simplify = false;
if (cell_inst.is_regular_array (a, b, amax, bmax)) {
@@ -768,8 +777,8 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
} else {
inst_box = trans * new_cell_box;
}
- if (((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
- inst_box.width () < 1.5 && inst_box.height () < 1.5 &&
+ if (((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
+ inst_box.width () < 1.5 && inst_box.height () < 1.5 &&
(amax <= 1 || trans.ctrans (a.length ()) < 1.5) &&
(bmax <= 1 || trans.ctrans (b.length ()) < 1.5)) {
simplify = true;
@@ -781,7 +790,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
// The array can be simplified if there are levels below to draw
if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1, m_to_level, m_hidden_cells, m_cv_index)) {
-
+
db::box_convert bc (*mp_layout);
unsigned int plane_group = 2;
@@ -801,8 +810,8 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
// The array (or single instance) must be iterated instance
// by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
-
- test_snapshot (0);
+
+ test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = db::Box (t.inverted () * *v);
draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1);
@@ -814,7 +823,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
}
++inst;
-
+
}
}
@@ -828,7 +837,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
}
void
-RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level)
+RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level)
{
if (! m_text_visible) {
return;
@@ -838,7 +847,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
}
void
-RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id)
+RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level, db::properties_id_type prop_id)
{
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty ()) {
@@ -855,6 +864,16 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
return;
}
+ for (std::vector::const_iterator b = vp.begin (); b != vp.end (); ++b) {
+ draw_box_properties (drawing_context, ci, trans, *b, level, prop_id);
+ }
+}
+
+void
+RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id)
+{
+ const db::Cell &cell = mp_layout->cell (ci);
+
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox ();
@@ -1193,14 +1212,12 @@ RedrawThreadWorker::search_regions (const db::Box &cell_bbox, const db::Box &vp,
}
void
-RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level)
+RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level)
{
if (! m_text_visible) {
return;
}
- lay::Renderer &r = *mp_renderer;
-
unsigned int plane_group = 2;
if (drawing_context) {
plane_group = 0;
@@ -1214,8 +1231,6 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
- test_snapshot (0);
-
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty ()) {
return;
@@ -1224,7 +1239,18 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c
return;
}
+ for (std::vector::const_iterator b = vp.begin (); b != vp.end (); ++b) {
+ draw_text_layer (drawing_context, ci, trans, *b, level, fill, frame, vertex, text);
+ }
+}
+
+void
+RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, CanvasPlane *fill, CanvasPlane *frame, CanvasPlane *vertex, CanvasPlane *text)
+{
+ test_snapshot (0);
+
const db::Cell &cell = mp_layout->cell (ci);
+ lay::Renderer &r = *mp_renderer;
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox (m_layer);
@@ -1422,7 +1448,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = db::Box (t.inverted () * *v);
- draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1);
+ draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text);
}
@@ -1751,11 +1777,9 @@ private:
};
void
-RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level,
+RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level,
lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot)
{
- test_snapshot (update_snapshot);
-
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty ()) {
return;
@@ -1764,6 +1788,17 @@ RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_typ
return;
}
+ for (std::vector::const_iterator b = vp.begin (); b != vp.end (); ++b) {
+ draw_layer (from_level, to_level, ci, trans, *b, level, fill, frame, vertex, text, update_snapshot);
+ }
+}
+
+void
+RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level,
+ lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot)
+{
+ test_snapshot (update_snapshot);
+
const db::Cell &cell = mp_layout->cell (ci);
db::Box bbox = cell.bbox (m_layer);
db::Box cell_bbox = cell.bbox ();
@@ -1891,7 +1926,7 @@ RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_typ
}
void
-RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level)
+RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level)
{
if (drawing_context) {
@@ -1904,7 +1939,7 @@ RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, co
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
- draw_layer (m_from_level, m_to_level, ci, trans, vp, level, fill, frame, vertex, text, 0);
+ draw_layer (m_from_level, m_to_level, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
@@ -1919,7 +1954,7 @@ RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, co
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
- draw_layer (m_from_level, m_to_level, ci, trans, vp, level, fill, frame, vertex, text, 0);
+ draw_layer (m_from_level, m_to_level, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
@@ -1934,7 +1969,7 @@ RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, co
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
- draw_layer (m_from_level, 1, ci, trans, vp, level, fill, frame, vertex, text, 0);
+ draw_layer (m_from_level, 1, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
@@ -1947,7 +1982,7 @@ RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, co
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
- draw_layer (1, m_to_level, ci, trans, vp, level, fill, frame, vertex, text, 0);
+ draw_layer (1, m_to_level, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
@@ -1990,7 +2025,7 @@ RedrawThreadWorker::cell_var_cached (db::cell_index_type ci, const db::CplxTrans
}
void
-RedrawThreadWorker::iterate_variants (const std::vector &redraw_regions, db::cell_index_type ci, db::CplxTrans trans, void (RedrawThreadWorker::*what) (bool, db::cell_index_type, const db::CplxTrans &, const db::Box &, int))
+RedrawThreadWorker::iterate_variants (const std::vector &redraw_regions, db::cell_index_type ci, db::CplxTrans trans, void (RedrawThreadWorker::*what) (bool, db::cell_index_type, const db::CplxTrans &, const std::vector &, int))
{
// save current state
int from_level = m_from_level;
@@ -2050,7 +2085,7 @@ RedrawThreadWorker::iterate_variants (const std::vector &redraw_region
}
void
-RedrawThreadWorker::iterate_variants_rec (const std::vector &redraw_regions, db::cell_index_type ci, const db::CplxTrans &trans, int level, void (RedrawThreadWorker::*what) (bool, db::cell_index_type, const db::CplxTrans &, const db::Box &, int), bool drawing_context)
+RedrawThreadWorker::iterate_variants_rec (const std::vector &redraw_regions, db::cell_index_type ci, const db::CplxTrans &trans, int level, void (RedrawThreadWorker::*what) (bool, db::cell_index_type, const db::CplxTrans &, const std::vector &, int), bool drawing_context)
{
db::Cell::parent_inst_iterator p = mp_layout->cell (ci).begin_parent_insts ();
int context_path_length = int (m_cellviews [m_cv_index].specific_path ().size ());
@@ -2092,19 +2127,25 @@ RedrawThreadWorker::iterate_variants_rec (const std::vector &redraw_re
} else {
+ std::vector actual_regions;
+ actual_regions.reserve (redraw_regions.size ());
+
for (std::vector::const_iterator rr = redraw_regions.begin (); rr != redraw_regions.end (); ++rr) {
db::Coord lim = std::numeric_limits::max ();
db::DBox world (trans * db::Box (db::Point (-lim, -lim), db::Point (lim, lim)));
db::Box vp = db::Box (trans.inverted () * (world & db::DBox (*rr)));
vp &= mp_layout->cell (ci).bbox (); // this avoids problems when accessing designs through very large viewports
-
if (! vp.empty ()) {
- (this->*what) (drawing_context, ci, trans, vp, level);
+ actual_regions.push_back (vp);
}
}
+ if (! actual_regions.empty ()) {
+ (this->*what) (drawing_context, ci, trans, actual_regions, level);
+ }
+
}
}
diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h
index 9f0e5b5c9..0b49a44d3 100644
--- a/src/laybasic/laybasic/layRedrawThreadWorker.h
+++ b/src/laybasic/laybasic/layRedrawThreadWorker.h
@@ -175,20 +175,24 @@ protected:
void perform_task (tl::Task *task);
private:
- void draw_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level);
- void draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot);
+ void draw_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
+ void draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot);
+ void draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot);
void draw_layer_wo_cache (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vv, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot);
- void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &box, int level);
- void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &box, int level);
- void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &box, int level);
- void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &box, int level, db::properties_id_type prop_id);
+ void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
+ void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text);
+ void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
+ void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level);
+ void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
+ void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, db::properties_id_type prop_id);
+ void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, db::properties_id_type prop_id);
void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const std::string &txt);
void draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id);
void draw_cell_shapes (const db::CplxTrans &trans, const db::Cell &cell, const db::Box &vp, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text);
void test_snapshot (const UpdateSnapshotCallback *update_snapshot);
void transfer ();
- void iterate_variants (const std::vector &redraw_regions, db::cell_index_type ci, db::CplxTrans trans, void (RedrawThreadWorker::*what) (bool, db::cell_index_type ci, const db::CplxTrans &, const db::Box &, int level));
- void iterate_variants_rec (const std::vector &redraw_regions, db::cell_index_type ci, const db::CplxTrans &trans, int level, void (RedrawThreadWorker::*what) (bool, db::cell_index_type ci, const db::CplxTrans &, const db::Box &, int level), bool spread);
+ void iterate_variants (const std::vector &redraw_regions, db::cell_index_type ci, db::CplxTrans trans, void (RedrawThreadWorker::*what) (bool, db::cell_index_type ci, const db::CplxTrans &, const std::vector &, int level));
+ void iterate_variants_rec (const std::vector &redraw_regions, db::cell_index_type ci, const db::CplxTrans &trans, int level, void (RedrawThreadWorker::*what) (bool, db::cell_index_type ci, const db::CplxTrans &, const std::vector &, int level), bool spread);
bool cell_var_cached (db::cell_index_type ci, const db::CplxTrans &trans);
bool drop_cell (const db::Cell &cell, const db::CplxTrans &trans);
std::vector search_regions (const db::Box &cell_bbox, const db::Box &vp, int level);
diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc
index 600b10cc5..ce90bb93f 100644
--- a/src/laybasic/laybasic/layViewObject.cc
+++ b/src/laybasic/laybasic/layViewObject.cc
@@ -165,7 +165,7 @@ BackgroundViewObject::z_order (int z)
// ViewObject implementation
ViewObject::ViewObject (ViewObjectWidget *widget, bool _static)
- : mp_widget (widget), m_static (_static), m_visible (true)
+ : mp_widget (widget), m_static (_static), m_visible (true), m_dismissable (false)
{
if (widget) {
widget->m_objects.push_back (this);
@@ -178,6 +178,15 @@ ViewObject::~ViewObject ()
redraw ();
}
+void
+ViewObject::set_dismissable (bool dismissable)
+{
+ if (m_dismissable != dismissable) {
+ m_dismissable = dismissable;
+ redraw ();
+ }
+}
+
void
ViewObject::visible (bool vis)
{
@@ -251,6 +260,7 @@ ViewService::set_cursor (lay::Cursor::cursor_shape cursor)
ViewObjectWidget::ViewObjectWidget (QWidget *parent, const char *name)
: QWidget (parent),
+ m_view_objects_dismissed (false),
m_needs_update_static (false),
m_needs_update_bg (false),
mp_active_service (0),
@@ -888,7 +898,7 @@ ViewObjectWidget::do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &can
}
for (object_iterator obj = begin_objects (); obj != end_objects (); ++obj) {
- if (obj->m_static == st && obj->is_visible ()) {
+ if (obj->m_static == st && obj->is_visible () && (! m_view_objects_dismissed || ! obj->get_dismissable ())) {
BEGIN_PROTECTED_SILENT
obj->render (vp, canvas);
END_PROTECTED_SILENT
@@ -963,6 +973,16 @@ ViewObjectWidget::touch_bg ()
}
}
+void
+ViewObjectWidget::set_dismiss_view_objects (bool dismiss)
+{
+ if (dismiss != m_view_objects_dismissed) {
+ m_view_objects_dismissed = dismiss;
+ touch ();
+ update ();
+ }
+}
+
void
ViewObjectWidget::objects_changed ()
{
diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h
index 2d4087919..6a274aab8 100644
--- a/src/laybasic/laybasic/layViewObject.h
+++ b/src/laybasic/laybasic/layViewObject.h
@@ -513,6 +513,23 @@ public:
return const_cast (mp_widget.get());
}
+ /**
+ * @brief Gets a value indicating whether the marker can be dismissed (made invisible)
+ *
+ * Markers with this flag set to true can be hidden by using ViewObjectCanvas::show_markers.
+ */
+ bool get_dismissable () const
+ {
+ return m_dismissable;
+ }
+
+ /**
+ * @brief Sets a value indicating whether the marker can be dismissed (made invisible)
+ *
+ * See \\get_dismissable for details.
+ */
+ void set_dismissable (bool f);
+
/**
* @brief Set the visibility state of the view object
*
@@ -561,6 +578,7 @@ private:
tl::weak_ptr mp_widget;
bool m_static;
bool m_visible;
+ bool m_dismissable;
};
/**
@@ -905,6 +923,21 @@ public:
*/
void set_default_cursor (lay::Cursor::cursor_shape cursor);
+ /**
+ * @brief Sets a value indicating whether dismissable view objects shall be drawn or not
+ *
+ * Markers with dismissable = false are always drawn. The default value is "false".
+ */
+ void set_dismiss_view_objects (bool dismissed);
+
+ /**
+ * @brief Gets a value indicating whether dismissable markers shall be drawn or not
+ */
+ bool dismiss_view_objects () const
+ {
+ return m_view_objects_dismissed;
+ }
+
protected:
/**
* @brief Qt focus event handler
@@ -985,6 +1018,7 @@ private:
tl::weak_collection m_background_objects;
std::list m_services;
std::list m_grabbed;
+ bool m_view_objects_dismissed;
bool m_needs_update_static;
bool m_needs_update_bg;
lay::ViewService *mp_active_service;
diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h
index 4926e8ca3..6f8aafaed 100644
--- a/src/laybasic/laybasic/laybasicConfig.h
+++ b/src/laybasic/laybasic/laybasicConfig.h
@@ -70,6 +70,8 @@ static const std::string cfg_sel_line_style ("sel-line-style");
static const std::string cfg_sel_transient_mode ("sel-transient-mode");
static const std::string cfg_sel_inside_pcells_mode ("sel-inside-pcells-mode");
+static const std::string cfg_markers_visible ("markers-visible");
+
static const std::string cfg_min_inst_label_size ("min-inst-label-size");
static const std::string cfg_cell_box_text_font ("inst-label-font");
static const std::string cfg_cell_box_text_transform ("inst-label-transform");
diff --git a/src/laybasic/laybasic/rdbMarkerBrowserPage.cc b/src/laybasic/laybasic/rdbMarkerBrowserPage.cc
index c43b5d060..b90dcf7a4 100644
--- a/src/laybasic/laybasic/rdbMarkerBrowserPage.cc
+++ b/src/laybasic/laybasic/rdbMarkerBrowserPage.cc
@@ -2177,36 +2177,42 @@ MarkerBrowserPage::do_update_markers ()
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set (trans * polygon_value->value ());
+ mp_markers.back ()->set_dismissable (true);
m_markers_bbox += trans * polygon_value->value ().box ();
} else if (edge_pair_value) {
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set (trans * edge_pair_value->value ());
+ mp_markers.back ()->set_dismissable (true);
m_markers_bbox += trans * db::DBox (edge_pair_value->value ().bbox ());
} else if (edge_value) {
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set (trans * edge_value->value ());
+ mp_markers.back ()->set_dismissable (true);
m_markers_bbox += trans * db::DBox (edge_value->value ().bbox ());
} else if (box_value) {
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set (trans * box_value->value ());
+ mp_markers.back ()->set_dismissable (true);
m_markers_bbox += trans * box_value->value ();
} else if (text_value) {
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set (trans * text_value->value ());
+ mp_markers.back ()->set_dismissable (true);
m_markers_bbox += trans * text_value->value ().box ();
} else if (path_value) {
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set (trans * path_value->value ());
+ mp_markers.back ()->set_dismissable (true);
m_markers_bbox += trans * path_value->value ().box ();
}
diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc
index 02d370f7c..b6fd483b4 100644
--- a/src/lym/lym/lymMacro.cc
+++ b/src/lym/lym/lymMacro.cc
@@ -32,6 +32,7 @@
#include "tlClassRegistry.h"
#include "tlLog.h"
#include "tlXMLParser.h"
+#include "tlGlobPattern.h"
#include "rba.h"
#include "pya.h"
@@ -213,15 +214,18 @@ void Macro::save_to (const std::string &path)
}
}
-void Macro::load_from (const std::string &path)
+void Macro::load_from (const std::string &fn)
{
m_format = NoFormat;
- if (tl::verbosity () >= 20) {
- tl::log << "Loading macro from " << path;
- }
+ std::pair f = format_from_filename (fn, m_interpreter, m_dsl_interpreter, m_autorun_default, m_format);
+ if (f.first) {
- if (format_from_suffix (path, m_interpreter, m_dsl_interpreter, m_autorun_default, m_format)) {
+ const std::string &path = f.second;
+
+ if (tl::verbosity () >= 20) {
+ tl::log << "Loading macro from " << path;
+ }
m_autorun = m_autorun_default;
@@ -242,7 +246,7 @@ void Macro::load_from (const std::string &path)
}
} else {
- throw tl::Exception (tl::to_string (QObject::tr ("Unable to determine format for file from suffix ")) + path);
+ throw tl::Exception (tl::to_string (QObject::tr ("Unable to determine format for file from suffix or format spec ")) + fn);
}
m_modified = true;
@@ -297,7 +301,24 @@ bool
Macro::format_from_suffix (const std::string &fn, Macro::Interpreter &interpreter, std::string &dsl_name, bool &autorun_pref, Macro::Format &format)
{
std::string suffix = tl::to_string (QFileInfo (tl::to_qstring (fn)).suffix ());
+ return format_from_suffix_string (suffix, interpreter, dsl_name, autorun_pref, format);
+}
+std::pair
+Macro::format_from_filename (const std::string &fn, Macro::Interpreter &interpreter, std::string &dsl_name, bool &autorun_pref, Macro::Format &format)
+{
+ tl::GlobPattern pat ("(*)\\[(*)\\]");
+ std::vector pat_parts;
+ if (pat.match (fn, pat_parts) && pat_parts.size () == 2) {
+ return std::make_pair (format_from_suffix_string (pat_parts[1], interpreter, dsl_name, autorun_pref, format), pat_parts[0]);
+ } else {
+ return std::make_pair (format_from_suffix (fn, interpreter, dsl_name, autorun_pref, format), fn);
+ }
+}
+
+bool
+Macro::format_from_suffix_string (const std::string &suffix, Macro::Interpreter &interpreter, std::string &dsl_name, bool &autorun_pref, Macro::Format &format)
+{
interpreter = None;
dsl_name = std::string ();
format = NoFormat;
diff --git a/src/lym/lym/lymMacro.h b/src/lym/lym/lymMacro.h
index 734921c81..46e813294 100644
--- a/src/lym/lym/lymMacro.h
+++ b/src/lym/lym/lymMacro.h
@@ -602,6 +602,8 @@ private:
void on_menu_needs_update ();
void on_changed ();
void save_to (const std::string &path);
+ static bool format_from_suffix_string (const std::string &suffix, Macro::Interpreter &interpreter, std::string &dsl_name, bool &autorun_pref, Macro::Format &format);
+ static std::pair format_from_filename (const std::string &fn, Macro::Interpreter &interpreter, std::string &dsl_name, bool &autorun_pref, Macro::Format &format);
void set_autorun_default (bool f)
{
diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc
index 0ada3f98b..d4943b63f 100644
--- a/src/pymod/unit_tests/pymod_tests.cc
+++ b/src/pymod/unit_tests/pymod_tests.cc
@@ -47,7 +47,9 @@ int run_pymodtest (tl::TestBase *_this, const std::string &fn)
std::string text;
{
- tl::InputPipe pipe (std::string (STRINGIFY (PYTHON)) + " " + fp + " 2>&1");
+ std::string cmd = std::string (STRINGIFY (PYTHON)) + " " + fp + " 2>&1";
+ tl::info << cmd;
+ tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
text = is.read_all ();
}
diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro
index e13891ebd..fbfda7416 100644
--- a/src/tl/tl/tl.pro
+++ b/src/tl/tl/tl.pro
@@ -57,7 +57,8 @@ SOURCES = \
tlThreadedWorkers.cc \
tlThreads.cc \
tlDeferredExecution.cc \
- tlUri.cc
+ tlUri.cc \
+ tlLongInt.cc
HEADERS = \
tlAlgorithm.h \
@@ -101,14 +102,14 @@ HEADERS = \
tlCommandLineParser.h \
tlUnitTest.h \
tlInt128Support.h \
- tlHttpStreamCurl.h \
tlDefs.h \
tlXMLParser.h \
tlXMLWriter.h \
tlThreadedWorkers.h \
tlThreads.h \
tlDeferredExecution.h \
- tlUri.h
+ tlUri.h \
+ tlLongInt.h
equals(HAVE_CURL, "1") {
diff --git a/src/tl/tl/tlLongInt.cc b/src/tl/tl/tlLongInt.cc
new file mode 100644
index 000000000..ac0f4bbe7
--- /dev/null
+++ b/src/tl/tl/tlLongInt.cc
@@ -0,0 +1,29 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2018 Matthias Koefferlein
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+
+#include "tlLongInt.h"
+
+namespace tl
+{
+ // .. nothing yet ..
+}
diff --git a/src/tl/tl/tlLongInt.h b/src/tl/tl/tlLongInt.h
new file mode 100644
index 000000000..7a56bd927
--- /dev/null
+++ b/src/tl/tl/tlLongInt.h
@@ -0,0 +1,1277 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2018 Matthias Koefferlein
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+
+#ifndef HDR_tlLongInt
+#define HDR_tlLongInt
+
+#include "tlCommon.h"
+
+#include
+
+namespace tl
+{
+
+/**
+ * @brief A universal long unsigned int
+ *
+ * The long unsigned int is composed of N chunks of B type.
+ * B can be any unsigned int type. BI is the working type
+ * which must be twice the size of B.
+ *
+ * Specifically, this universal int is intended to emulate
+ * __uint128 by using:
+ *
+ * typedef long_uint<4, uint32_t, uint64_t> __uint128;
+ */
+template
+class long_uint
+{
+public:
+ enum { bits = sizeof (B) * 8 };
+
+ typedef B basic_type;
+
+ /**
+ * @brief Default constructor
+ * This will initialize the value to 0.
+ */
+ long_uint ()
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ b[i] = 0;
+ }
+ }
+
+ /**
+ * @brief Initialize the universal unsigned int with a POD type
+ * If T is a signed type, the upper bit won't be sign-extended.
+ */
+ template
+ long_uint (T t)
+ {
+ unsigned int i = 0;
+ unsigned int tbits = sizeof (T) * 8;
+
+ while (tbits > 0) {
+ b[i] = B (t);
+ if (tbits > bits) {
+ t >>= bits;
+ }
+ tbits -= bits;
+ ++i;
+ }
+ for ( ; i < N; ++i) {
+ b[i] = 0;
+ }
+ }
+
+ /**
+ * @brief Convert the universal long int to the given type
+ * Casting will potentially reduce the number of significant
+ * bits of the value.
+ */
+ template
+ operator T () const
+ {
+ unsigned int tbits = sizeof (T) * 8;
+ T t = 0;
+
+ if (tbits <= bits) {
+ t = T (b[0]);
+ } else {
+ unsigned int i = sizeof (T) / sizeof (B);
+ for ( ; i > 0 && tbits > 0; ) {
+ --i;
+ t <<= bits;
+ t |= b[i];
+ tbits -= bits;
+ }
+ }
+
+ return t;
+ }
+
+ /**
+ * @brief Initialize the universal unsigned int from another one with a different width
+ */
+ template
+ long_uint (const long_uint &o)
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ b[i] = i < N2 ? o.b[i] : 0;
+ }
+ }
+
+ /**
+ * @brief zero predicate
+ * This method returns true, if the value is 0.
+ */
+ bool is_zero () const
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ if (b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @brief Equality
+ */
+ bool operator== (const long_uint &o) const
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ if (b[i] != o.b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @brief Inequality
+ */
+ bool operator!= (const long_uint &b) const
+ {
+ return !operator== (b);
+ }
+
+ /**
+ * @brief Less operator
+ */
+ bool operator< (const long_uint &o) const
+ {
+ for (unsigned int i = N; i > 0; ) {
+ --i;
+ if (b[i] != o.b[i]) {
+ return b[i] < o.b[i];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief Greater or equal
+ */
+ bool operator>= (const long_uint &b) const
+ {
+ return !operator< (b);
+ }
+
+ /**
+ * @brief Less or equal
+ */
+ bool operator<= (const long_uint &b) const
+ {
+ return b >= *this;
+ }
+
+ /**
+ * @brief Greater
+ */
+ bool operator> (const long_uint &b) const
+ {
+ return b < *this;
+ }
+
+ /**
+ * @brief Multiplication
+ */
+ template
+ long_uint operator* (const long_uint &o) const
+ {
+ long_uint res;
+
+ for (unsigned int i = 0; i < N; ++i) {
+ for (unsigned int j = 0; j < N2; ++j) {
+ BI p = BI (b[i]) * BI (o.b[j]);
+ unsigned int n = i + j;
+ while (p > 0) {
+ B &r = res.b[n];
+ B rold = r;
+ r += B (p);
+ p >>= bits;
+ if (r < rold) {
+ p += 1;
+ }
+ ++n;
+ }
+ }
+ }
+
+ return long_uint (res);
+ }
+
+ /**
+ * @brief In-place multiplication
+ */
+ template
+ long_uint &operator*= (const long_uint &o)
+ {
+ *this = *this * o;
+ return *this;
+ }
+
+ /**
+ * @brief Bitwise inversion
+ */
+ long_uint operator~ () const
+ {
+ long_uint res = *this;
+ for (unsigned int i = 0; i < N; ++i) {
+ res.b[i] = ~res.b[i];
+ }
+ return res;
+ }
+
+ /**
+ * @brief Boolean and operator
+ */
+ long_uint operator& (const long_uint &o) const
+ {
+ long_uint res = *this;
+ res &= o;
+ return res;
+ }
+
+ /**
+ * @brief In-place boolean and operator
+ */
+ long_uint &operator&= (const long_uint &o)
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ b[i] &= o.b[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Boolean xor operator
+ */
+ long_uint operator^ (const long_uint &o) const
+ {
+ long_uint res = *this;
+ res ^= o;
+ return res;
+ }
+
+ /**
+ * @brief In-place boolean xor operator
+ */
+ long_uint &operator^= (const long_uint &o)
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ b[i] ^= o.b[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Boolean or operator
+ */
+ long_uint operator| (const long_uint &o) const
+ {
+ long_uint res = *this;
+ res |= o;
+ return res;
+ }
+
+ /**
+ * @brief In-place boolean or operator
+ */
+ long_uint &operator|= (const long_uint &o)
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ b[i] |= o.b[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Left-shift operator
+ */
+ long_uint operator<< (unsigned int n) const
+ {
+ long_uint res = *this;
+ res.lshift (n);
+ return res;
+ }
+
+ /**
+ * @brief In-place left-shift operator
+ */
+ long_uint &operator<<= (unsigned int n)
+ {
+ lshift (n);
+ return *this;
+ }
+
+ /**
+ * @brief Bitwise left-shift
+ * This method will left-shift the value by the given number of bits
+ */
+ void lshift (unsigned int n)
+ {
+ unsigned int w = n / bits;
+ if (w > 0) {
+ n -= w * bits;
+ for (unsigned int i = N - w; i > 0; ) {
+ --i;
+ b[i + w] = b[i];
+ }
+ for (unsigned int i = 0; i < w; ++i) {
+ b[i] = 0;
+ }
+ }
+
+ B carry = 0;
+ for (unsigned int i = 0; i < N; ++i) {
+ BI p = b[i];
+ p <<= n;
+ p |= carry;
+ carry = B (p >> bits);
+ b[i] = B (p);
+ }
+ }
+
+ /**
+ * @brief right-shift operator
+ */
+ long_uint operator>> (unsigned int n) const
+ {
+ long_uint res = *this;
+ res.rshift (n);
+ return res;
+ }
+
+ /**
+ * @brief In-place right-shift operator
+ */
+ long_uint &operator>>= (unsigned int n)
+ {
+ rshift (n);
+ return *this;
+ }
+
+ /**
+ * @brief Bitwise right-shift
+ * This method will right-shift the value by the given number of bits.
+ * The sign flag will be used to fill the upper bits.
+ */
+ void rshift (unsigned int n, bool sign = false)
+ {
+ unsigned int w = n / bits;
+ if (w > 0) {
+ n -= w * bits;
+ for (unsigned int i = 0; i < N - w; ++i) {
+ b[i] = b[i + w];
+ }
+ for (unsigned int i = N - w; i < N; ++i) {
+ b[i] = sign ? ~B (0) : B (0);
+ }
+ }
+
+ if (n > 0) {
+ B carry = sign ? (~B (0) << (bits - n)) : 0;
+ for (unsigned int i = N; i > 0; ) {
+ --i;
+ BI p = BI (b[i]) << bits;
+ p >>= n;
+ p |= (BI (carry) << bits);
+ carry = B (p);
+ b[i] = B (p >> bits);
+ }
+ }
+ }
+
+ /**
+ * @brief Sets the given bit in the universal long int
+ * Bit 0 is the rightmost (LSB) one.
+ */
+ void set_bit (unsigned int n)
+ {
+ unsigned int i = n / bits;
+ n -= i * bits;
+ if (i < N) {
+ b[i] |= (1 << n);
+ }
+ }
+
+ /**
+ * @brief Gets the number of zero bits counted from the MSB to the right
+ */
+ unsigned int zero_bits_from_msb () const
+ {
+ unsigned int zb = 0;
+ for (unsigned int i = N; i > 0; ) {
+ --i;
+ if (!b[i]) {
+ zb += bits;
+ } else {
+ B m = 1 << (bits - 1);
+ B n = b[i];
+ while (! (n & m)) {
+ ++zb;
+ n <<= 1;
+ }
+ break;
+ }
+ }
+ return zb;
+ }
+
+ /**
+ * @brief Division and modulo operation
+ * This method returns the division of *this and d (the divider) in the first
+ * element of the returned pair and the modulo value (remainder) in the second.
+ */
+ std::pair, long_uint > divmod (const long_uint &d) const
+ {
+ long_uint rem = *this;
+ long_uint div;
+
+ if (d.is_zero ()) {
+ // should assert?
+ return std::make_pair (div, rem);
+ }
+
+ unsigned int bd = d.zero_bits_from_msb ();
+
+ while (rem >= d) {
+
+ unsigned int brem = rem.zero_bits_from_msb ();
+ unsigned int shift = bd - brem;
+
+ if (shift == 0) {
+ rem -= d;
+ div.set_bit (0);
+ } else {
+ long_uint sub = d;
+ sub.lshift (shift);
+ if (sub > rem) {
+ shift -= 1;
+ sub.rshift (1);
+ }
+ div.set_bit (shift);
+ rem -= sub;
+ }
+
+ }
+
+ return std::make_pair (div, rem);
+ }
+
+ /**
+ * @brief Division operator
+ */
+ long_uint operator/ (const long_uint &b) const
+ {
+ return divmod (b).first;
+ }
+
+ /**
+ * @brief In-place division operator
+ */
+ long_uint &operator/= (const long_uint &b)
+ {
+ *this = divmod (b).first;
+ return *this;
+ }
+
+ /**
+ * @brief Modulo operator
+ */
+ long_uint operator% (const long_uint &b) const
+ {
+ return divmod (b).second;
+ }
+
+ /**
+ * @brief In-place modulo operator
+ */
+ long_uint &operator%= (const long_uint &b)
+ {
+ *this = divmod (b).second;
+ return *this;
+ }
+
+ /**
+ * @brief Addition with the basic type
+ */
+ long_uint operator+ (B o) const
+ {
+ long_uint res;
+
+ B carry = o;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = res.b[i];
+ r = b[i];
+ B rold = r;
+ r += carry;
+ carry = 0;
+ if (r < rold) {
+ carry = 1;
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @brief In-place addition with the basic type
+ */
+ long_uint &operator+= (B o)
+ {
+ B carry = o;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = b[i];
+ B rold = r;
+ r += carry;
+ carry = 0;
+ if (r < rold) {
+ carry = 1;
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Addition
+ */
+ long_uint operator+ (const long_uint &o) const
+ {
+ long_uint res;
+
+ B carry = 0;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = res.b[i];
+ r = b[i];
+ B rold = r;
+ r += carry;
+ carry = 0;
+ if (r < rold) {
+ carry = 1;
+ }
+ rold = r;
+ r += o.b[i];
+ if (r < rold) {
+ carry = 1;
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @brief In-place addition
+ */
+ long_uint &operator+= (const long_uint &o)
+ {
+ B carry = 0;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = b[i];
+ B rold = r;
+ r += carry;
+ carry = 0;
+ if (r < rold) {
+ carry = 1;
+ }
+ rold = r;
+ r += o.b[i];
+ if (r < rold) {
+ carry = 1;
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Subtraction
+ */
+ long_uint operator- (const long_uint &o) const
+ {
+ long_uint res;
+
+ B carry = 0;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = res.b[i];
+ r = b[i];
+ B rold = r;
+ r -= carry;
+ carry = 0;
+ if (r > rold) {
+ carry = 1;
+ }
+ rold = r;
+ r -= o.b[i];
+ if (r > rold) {
+ carry = 1;
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @brief In-place subtraction
+ */
+ long_uint &operator-= (const long_uint &o)
+ {
+ B carry = 0;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = b[i];
+ B rold = r;
+ r -= carry;
+ carry = 0;
+ if (r > rold) {
+ carry = 1;
+ }
+ rold = r;
+ r -= o.b[i];
+ if (r > rold) {
+ carry = 1;
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Subtraction with the basic type
+ */
+ long_uint operator- (B o) const
+ {
+ long_uint res;
+
+ B carry = o;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = res.b[i];
+ r = b[i];
+ B rold = r;
+ r -= carry;
+ carry = 0;
+ if (r > rold) {
+ carry = 1;
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @brief In-place subtraction with the basic type
+ */
+ long_uint &operator-= (B o)
+ {
+ B carry = o;
+ for (unsigned int i = 0; i < N; ++i) {
+ B &r = b[i];
+ B rold = r;
+ r -= carry;
+ carry = 0;
+ if (r > rold) {
+ carry = 1;
+ }
+ }
+
+ return *this;
+ }
+
+ B b[N];
+};
+
+/**
+ * @brief A universal long signed int
+ *
+ * The long unsigned int is composed of N chunks of B type.
+ * B can be any unsigned int type. BI is the working type
+ * which must be twice the size of B.
+ *
+ * B and BI must be unsigned types.
+ *
+ * Specifically, this universal int is intended to emulate
+ * __int128 by using:
+ *
+ * typedef long_int<4, uint32_t, uint64_t> __int128;
+ */
+template
+class long_int
+ : public long_uint
+{
+public:
+ /**
+ * @brief Default constructor
+ * This will initialize the value to 0.
+ */
+ long_int ()
+ : long_uint ()
+ {
+ // .. nothing yet ..
+ }
+
+ /**
+ * @brief Initialize the universal int with a POD type
+ * If T is a signed type, the upper bit will be sign-extended.
+ */
+ template
+ long_int (T t)
+ {
+ unsigned int tbits = sizeof (T) * 8;
+
+ for (unsigned int i = 0; i < N; ++i) {
+ long_uint::b[i] = B (t);
+ if (tbits <= long_uint::bits) {
+ t = (t < 0 ? ~B (0) : 0);
+ } else {
+ t >>= long_uint::bits;
+ }
+ }
+ }
+
+ /**
+ * @brief Convert the universal long int to the given type
+ * Casting will potentially reduce the number of significant
+ * bits of the value.
+ */
+ template
+ operator T () const
+ {
+ unsigned int tbits = sizeof (T) * 8;
+ T t = 0;
+
+ if (tbits <= long_uint::bits) {
+ t = T (long_uint::b[0]);
+ } else {
+ unsigned int i = sizeof (T) / sizeof (B);
+ for ( ; i > 0 && tbits > 0; ) {
+ --i;
+ t <<= long_uint::bits;
+ t |= long_uint::b[i];
+ tbits -= long_uint::bits;
+ }
+ }
+
+ return t;
+ }
+
+ /**
+ * @brief Initialize the universal unsigned int from an unsigned one with a different width
+ * Sign inversion will happen, if the unsigned int is bigger than the maximum value
+ * representable by our type.
+ */
+ template
+ long_int (const long_uint &o)
+ : long_uint (o)
+ {
+ // .. nothing yet ..
+ }
+
+ /**
+ * @brief Initialize the universal int from another one with a different width
+ */
+ template
+ long_int (const long_int &o)
+ : long_uint (o)
+ {
+ // .. nothing yet ..
+ }
+
+ /**
+ * @brief negative predicate
+ * This method returns true, if the value is negative.
+ */
+ bool is_neg () const
+ {
+ return (long_uint::b[N - 1] & (1 << (long_uint::bits - 1))) != 0;
+ }
+
+ /**
+ * @brief zero predicate
+ * This method returns true, if the value is 0.
+ */
+ bool is_zero () const
+ {
+ for (unsigned int i = 0; i < N; ++i) {
+ if (long_uint::b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @brief Less operator
+ */
+ bool operator< (const long_uint &o) const
+ {
+ // we cast both arguments to unsigned as C++ does
+ return long_uint::operator< (o);
+ }
+
+ /**
+ * @brief Less operator
+ */
+ bool operator< (const long_int &o) const
+ {
+ if (is_neg () != o.is_neg ()) {
+ return is_neg () > o.is_neg ();
+ } else {
+ return long_uint::operator< (o);
+ }
+ }
+
+ /**
+ * @brief Greater or equal
+ */
+ bool operator>= (const long_uint &b) const
+ {
+ return !operator< (b);
+ }
+
+ /**
+ * @brief Greater or equal
+ */
+ bool operator>= (const long_int &b) const
+ {
+ return !operator< (b);
+ }
+
+ /**
+ * @brief Less or equal
+ */
+ bool operator<= (const long_uint &b) const
+ {
+ return b >= *this;
+ }
+
+ /**
+ * @brief Less or equal
+ */
+ bool operator<= (const long_int &b) const
+ {
+ return b >= *this;
+ }
+
+ /**
+ * @brief Greater
+ */
+ bool operator> (const long_uint &b) const
+ {
+ return b < *this;
+ }
+
+ /**
+ * @brief Greater
+ */
+ bool operator> (const long_int &b) const
+ {
+ return b < *this;
+ }
+
+ /**
+ * @brief Sign inversion (two's complement)
+ */
+ long_int operator- () const
+ {
+ long_uint res = ~*this;
+ res += B (1);
+ return long_int (res);
+ }
+
+ /**
+ * @brief Multiplication with unsigned
+ */
+ template
+ long_uint operator* (const long_uint &o) const
+ {
+ return long_uint::operator* (o);
+ }
+
+ /**
+ * @brief Multiplication with signed
+ */
+ template
+ long_int operator* (const long_int &o) const
+ {
+ if (is_neg () && ! o.is_neg ()) {
+ return -long_int ((-*this).long_uint::operator* (o));
+ } else if (! is_neg () && o.is_neg ()) {
+ return -long_int (long_uint::operator* (-o));
+ } else if (is_neg () && o.is_neg ()) {
+ return long_int ((-*this).long_uint::operator* (-o));
+ } else {
+ return long_int (long_uint::operator* (o));
+ }
+ }
+
+ /**
+ * @brief In-place multiplication with unsigned
+ */
+ template
+ long_int &operator*= (const long_uint &o)
+ {
+ long_uint::operator= (*this * o);
+ return *this;
+ }
+
+ /**
+ * @brief In-place multiplication with signed
+ */
+ template
+ long_int &operator*= (const long_int &o)
+ {
+ *this = *this * o;
+ return *this;
+ }
+
+ /**
+ * @brief Left-shift operator
+ */
+ long_int operator<< (unsigned int n) const
+ {
+ long_int res = *this;
+ res.lshift (n);
+ return res;
+ }
+
+ /**
+ * @brief In-place left-shift operator
+ */
+ long_int &operator<<= (unsigned int n)
+ {
+ long_uint::lshift (n);
+ return *this;
+ }
+
+ /**
+ * @brief right-shift operator
+ */
+ long_int operator>> (unsigned int n) const
+ {
+ long_uint res = *this;
+ res.rshift (n, is_neg ());
+ return res;
+ }
+
+ /**
+ * @brief In-place right-shift operator
+ */
+ long_int &operator>>= (unsigned int n)
+ {
+ long_uint::rshift (n, is_neg ());
+ return *this;
+ }
+
+ /**
+ * @brief Division and modulo operation with unsigned type
+ * This method returns the division of *this and d (the divider) in the first
+ * element of the returned pair and the modulo value (remainder) in the second.
+ */
+ std::pair, long_uint > divmod (const long_uint &d) const
+ {
+ return long_uint::divmod (d);
+ }
+
+ /**
+ * @brief Division operator with unsigned
+ */
+ long_uint operator/ (const long_uint &b) const
+ {
+ return divmod (b).first;
+ }
+
+ /**
+ * @brief In-place division operator with unsigned
+ */
+ long_int &operator/= (const long_uint &b)
+ {
+ long_uint::operator= (divmod (b).first);
+ return *this;
+ }
+
+ /**
+ * @brief Modulo operator with unsigned
+ */
+ long_uint operator% (const long_uint &b) const
+ {
+ return divmod (b).second;
+ }
+
+ /**
+ * @brief In-place modulo operator with unsigned
+ */
+ long_int &operator%= (const long_uint &b)
+ {
+ long_uint::operator= (divmod (b).second);
+ return *this;
+ }
+
+ /**
+ * @brief Division and modulo operation
+ * This method returns the division of *this and d (the divider) in the first
+ * element of the returned pair and the modulo value (remainder) in the second.
+ */
+ std::pair, long_int > divmod (const long_int &d) const
+ {
+ if (is_neg () && !d.is_neg ()) {
+ std::pair, long_uint > res = (-*this).long_uint::divmod (d);
+ return std::make_pair (-long_int (res.first), -long_int (res.second));
+ } else if (! is_neg () && d.is_neg ()) {
+ std::pair, long_uint > res = long_uint::divmod (-d);
+ // The definition of the modulo sign is consistent with int arithmetics
+ return std::make_pair (-long_int (res.first), long_int (res.second));
+ } else if (is_neg () && d.is_neg ()) {
+ std::pair, long_uint > res = (-*this).long_uint::divmod (-d);
+ // The definition of the modulo sign is consistent with int arithmetics
+ return std::make_pair (long_int (res.first), -long_int (res.second));
+ } else {
+ std::pair, long_uint > res = long_uint::divmod (d);
+ return std::make_pair (long_int (res.first), long_int (res.second));
+ }
+ }
+
+ /**
+ * @brief Division operator
+ */
+ long_int operator/ (const long_int &b) const
+ {
+ return divmod (b).first;
+ }
+
+ /**
+ * @brief In-place division operator
+ */
+ long_int &operator/= (const long_int &b)
+ {
+ *this = divmod (b).first;
+ return *this;
+ }
+
+ /**
+ * @brief Modulo operator
+ */
+ long_int operator% (const long_int &b) const
+ {
+ return divmod (b).second;
+ }
+
+ /**
+ * @brief In-place modulo operator
+ */
+ long_int &operator%= (const long_int &b)
+ {
+ *this = divmod (b).second;
+ return *this;
+ }
+
+ /**
+ * @brief Addition with basic type
+ */
+ long_int operator+ (B o) const
+ {
+ return long_int (long_uint::operator+ (o));
+ }
+
+ /**
+ * @brief Addition with unsigned
+ */
+ long_uint operator+ (const long_uint