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 &o) const + { + return long_uint::operator+ (o); + } + + /** + * @brief Addition + */ + long_int operator+ (const long_int &o) const + { + return long_int (long_uint::operator+ (o)); + } + + /** + * @brief In-place addition with basic_type + */ + long_int &operator+= (B o) + { + long_uint::operator+= (o); + return *this; + } + + /** + * @brief In-place addition + */ + long_int &operator+= (const long_int &o) + { + long_uint::operator+= (o); + return *this; + } + + /** + * @brief In-place addition with unsigned + */ + long_int &operator+= (const long_uint &o) + { + long_uint::operator+= (o); + return *this; + } + + /** + * @brief Subtraction with basic_type + */ + long_int operator- (B o) const + { + return long_int (long_uint::operator- (o)); + } + + /** + * @brief Subtraction with unsigned + */ + long_uint operator- (const long_uint &o) const + { + return long_uint::operator- (o); + } + + /** + * @brief Subtraction + */ + long_int operator- (const long_int &o) const + { + return long_int (long_uint::operator- (o)); + } + + /** + * @brief In-place subtraction with basic_type + */ + long_int &operator-= (B o) + { + long_uint::operator-= (o); + return *this; + } + + /** + * @brief In-place subtraction with unsigned + */ + long_int &operator-= (const long_uint &o) + { + long_uint::operator-= (o); + return *this; + } + + /** + * @brief In-place subtraction + */ + long_int &operator-= (const long_int &o) + { + long_uint::operator-= (o); + return *this; + } +}; + +/** + * @brief Less operator with unsigned and signed + */ +template +bool operator< (const long_uint &a, const long_int &b) +{ + // we cast both arguments to unsigned (as C++ does) + return a.operator< (b); +} + +/** + * @brief Less or equal operator with unsigned and signed + */ +template +bool operator<= (const long_uint &a, const long_int &b) +{ + return !(b < a); +} + +/** + * @brief Greater operator with unsigned and signed + */ +template +bool operator> (const long_uint &a, const long_int &b) +{ + return b < a; +} + +/** + * @brief Greater or equal operator with unsigned and signed + */ +template +bool operator>= (const long_uint &a, const long_int &b) +{ + return !(a < b); +} + +/** + * @brief Multiplication with unsigned and signed + */ +template +long_uint operator* (const long_uint &a, const long_int &b) +{ + return a.operator* (b); +} + +/** + * @brief Division with unsigned and signed + */ +template +long_uint operator/ (const long_uint &a, const long_int &b) +{ + return a.operator/ (b); +} + +/** + * @brief Modulo with unsigned and signed + */ +template +long_uint operator% (const long_uint &a, const long_int &b) +{ + return a.operator% (b); +} + +/** + * @brief Addition with unsigned and signed + */ +template +long_uint operator+ (const long_uint &a, const long_int &b) +{ + return a.operator+ (b); +} + +/** + * @brief Subtraction with unsigned and signed + */ +template +long_uint operator- (const long_uint &a, const long_int &b) +{ + return a.operator- (b); +} + +} + +#endif diff --git a/src/tl/unit_tests/tlLongInt.cc b/src/tl/unit_tests/tlLongInt.cc new file mode 100644 index 000000000..3620e68e9 --- /dev/null +++ b/src/tl/unit_tests/tlLongInt.cc @@ -0,0 +1,178 @@ + +/* + + 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 +#include +#include + +#include "tlLongInt.h" +#include "tlUnitTest.h" + +typedef tl::long_int<4, uint8_t, uint16_t> li_type; +typedef int32_t i_type; + +typedef tl::long_uint<4, uint8_t, uint16_t> lui_type; +typedef uint32_t ui_type; + +i_type to_i (li_type x) +{ + return i_type (x); +} + +ui_type to_i (lui_type x) +{ + return ui_type (x); +} + +li_type to_e (i_type x) +{ + return li_type (x); +} + +lui_type to_e (ui_type x) +{ + return lui_type (x); +} + +template +static void run_test_int (tl::TestBase *_this, I1 a, I2 b) +{ + typedef typename LI1::basic_type basic_type; + if (tl::verbose ()) { + printf("Long int test with pair (%ld,%ld)\n", long (a), long (b)); + } + + LI1 ae = to_e (a); + LI2 be = to_e (b); + LI1 r; + + EXPECT_EQ (to_i (ae), a); + EXPECT_EQ (to_i (be), b); + + EXPECT_EQ (to_i (ae + be), a + b); + r = ae; + r += be; + EXPECT_EQ (to_i (r), a + b); + EXPECT_EQ (to_i (ae + basic_type (2)), a + basic_type (2)); + r = ae; + r += basic_type (2); + EXPECT_EQ (to_i (r), a + basic_type (2)); + + EXPECT_EQ (to_i (ae - be), a - b); + r = ae; + r -= be; + EXPECT_EQ (to_i (r), a - b); + EXPECT_EQ (to_i (ae - basic_type (2)), a - basic_type (2)); + r = ae; + r -= basic_type (2); + EXPECT_EQ (to_i (r), a - basic_type (2)); + + EXPECT_EQ (ae == be, a == b); + EXPECT_EQ (ae != be, a != b); + EXPECT_EQ (ae < be, a < b); + EXPECT_EQ (ae <= be, a <= b); + EXPECT_EQ (ae > be, a > b); + EXPECT_EQ (ae >= be, a >= b); + EXPECT_EQ (ae.is_zero (), a == 0); + + EXPECT_EQ (to_i (ae * be), a * b); + r = ae; + r *= be; + EXPECT_EQ (to_i (r), a * b); + + if (b != 0) { + EXPECT_EQ (to_i (ae / be), a / b); + r = ae; + r /= be; + EXPECT_EQ (to_i (r), a / b); + EXPECT_EQ (to_i (ae % be), a % b); + r = ae; + r %= be; + EXPECT_EQ (to_i (r), a % b); + } +} + +template +static void run_test (tl::TestBase *_this, I1 a, I2 b) +{ + run_test_int (_this, a, b); + run_test_int (_this, a, a); + run_test_int (_this, b, b); + run_test_int (_this, b, a); +} + +TEST(1) +{ + run_test (_this, 0, 1); + run_test (_this, 256, 257); + run_test (_this, 256, 2); + run_test (_this, 65535, 65536); + run_test (_this, 65535, 2); + run_test (_this, 0xfffffffe, 0xffffffff); + run_test (_this, 0xfffffffe, 2); + for (unsigned int i = 0; i < 100000; ++i) { + run_test (_this, rand () * rand (), rand () * rand ()); + } +} + +TEST(2) +{ + run_test (_this, 0, 1); + run_test (_this, 256, 257); + run_test (_this, 256, 2); + run_test (_this, 65535, 65536); + run_test (_this, 65535, 2); + run_test (_this, 0xfffffffe, 0xffffffff); + run_test (_this, 0xfffffffe, 2); + for (unsigned int i = 0; i < 100000; ++i) { + run_test (_this, rand () * rand (), rand () * rand ()); + } +} + +TEST(3) +{ + run_test (_this, 0, 1); + run_test (_this, 256, 257); + run_test (_this, 256, 2); + run_test (_this, 65535, 65536); + run_test (_this, 65535, 2); + run_test (_this, 0xfffffffe, 0xffffffff); + run_test (_this, 0xfffffffe, 2); + for (unsigned int i = 0; i < 100000; ++i) { + run_test (_this, rand () * rand (), rand () * rand ()); + } +} + +TEST(4) +{ + run_test (_this, 0, 1); + run_test (_this, 256, 257); + run_test (_this, 256, 2); + run_test (_this, 65535, 65536); + run_test (_this, 65535, 2); + run_test (_this, 0xfffffffe, 0xffffffff); + run_test (_this, 0xfffffffe, 2); + for (unsigned int i = 0; i < 100000; ++i) { + run_test (_this, rand () * rand (), rand () * rand ()); + } +} diff --git a/src/tl/unit_tests/tlThreadedWorkers.cc b/src/tl/unit_tests/tlThreadedWorkers.cc index c5e2c5671..a9d757461 100644 --- a/src/tl/unit_tests/tlThreadedWorkers.cc +++ b/src/tl/unit_tests/tlThreadedWorkers.cc @@ -357,7 +357,7 @@ void run_thread_tests (tl::TestBase *_this, int wait) s_sum[3].reset (); for (int i = 0; i < 10000; ++i) { - job.schedule (new MyTask (10000)); + job.schedule (new MyTask (100000)); } job.start (); diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index e6a32108d..59776da0c 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -28,11 +28,12 @@ SOURCES = \ tlThreads.cc \ tlUtils.cc \ tlVariant.cc \ - tlInt128Support.cc \ tlXMLParser.cc \ tlUri.cc \ tlWebDAV.cc \ tlHttpStream.cc \ + tlInt128Support.cc \ + tlLongInt.cc \ !equals(HAVE_QT, "0") { diff --git a/test-pydeploy.py b/test-pydeploy.py new file mode 100644 index 000000000..f6a8165aa --- /dev/null +++ b/test-pydeploy.py @@ -0,0 +1,98 @@ +import os, subprocess +import sys +from macbuild.build4mac_util import WalkFrameworkPaths, PerformChanges, DetectChanges +from pathlib import Path + +# bundlePath = AbsMacPkgDir +bundlePath = os.getcwd() + '/qt5.pkg.macos-HighSierra-release/klayout.app' +bundleExecPathAbs = '%s/Contents/MacOS/' % bundlePath +pythonOriginalFrameworkPath = '/usr/local/opt/python/Frameworks/Python.framework' +pythonFrameworkPath = '%s/Contents/Frameworks/Python.framework' % bundlePath + + +print("[1] Copying Python Framework") +shell_commands = list() +shell_commands.append(f"rm -rf {pythonFrameworkPath}") +shell_commands.append(f"rsync -a --safe-links {pythonOriginalFrameworkPath}/ {pythonFrameworkPath}") +shell_commands.append(f"mkdir {pythonFrameworkPath}/Versions/3.6/lib/python3.6/site-packages/") +shell_commands.append(f"cp -RL {pythonOriginalFrameworkPath}/Versions/3.6/lib/python3.6/site-packages/{{pip*,pkg_resources,setuptools*,wheel*}} " + + f"{pythonFrameworkPath}/Versions/3.6/lib/python3.6/site-packages/") +shell_commands.append(f"rm -rf {pythonFrameworkPath}/Versions/3.6/lib/python3.6/test") +shell_commands.append(f"rm -rf {pythonFrameworkPath}/Versions/3.6/Resources") +shell_commands.append(f"rm -rf {pythonFrameworkPath}/Versions/3.6/bin") + +for command in shell_commands: + if subprocess.call( command, shell=True ) != 0: + msg = "command failed: %s" + print( msg % command, file=sys.stderr ) + exit(1) + +print("[2] Relinking dylib dependencies inside Python.framework") +depdict = WalkFrameworkPaths(pythonFrameworkPath) +appPythonFrameworkPath = '@executable_path/../Frameworks/Python.framework/' +PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath)], bundleExecPathAbs) + +usrLocalPath = '/usr/local/opt/' +appUsrLocalPath = '@executable_path/../Frameworks/' +depdict = WalkFrameworkPaths(pythonFrameworkPath) +PerformChanges(depdict, [(usrLocalPath, appUsrLocalPath)], bundleExecPathAbs, libdir=True) + +print("[3] Relinking dylib dependencies for klayout") +klayoutPath = bundleExecPathAbs +depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$') +PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath)], bundleExecPathAbs) + +libKlayoutPath = bundleExecPathAbs + '../Frameworks' +depdict = WalkFrameworkPaths(libKlayoutPath, filter_regex=r'libklayout') +PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath)], bundleExecPathAbs) + +print("[4] Patching site.py, pip/, and distutils/") +site_module = f"{pythonFrameworkPath}/Versions/3.6/lib/python3.6/site.py" +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 = f"{pythonFrameworkPath}/Versions/3.6/lib/python3.6/site-packages/pip/__init__.py" +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 = f"{pythonFrameworkPath}/Versions/3.6/lib/python3.6/distutils/distutils.cfg" +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) + + +# pythonPath = bundleExecPathAbs + '../Frameworks/Python.framework/Versions/3.6/bin/' +# # pythonOriginalPrefixPath = '/usr/local/opt/python/Frameworks/Python.framework/Versions/3.6' +# # appPythonBinPath = '@executable_path/../' +# depdict = WalkFrameworkPaths(pythonPath, filter_regex=r'python') +# print(depdict) +# PerformChanges(depdict, [(pythonOriginalFrameworkPath, appPythonFrameworkPath), +# (Path(pythonOriginalFrameworkPath).resolve(), appPythonFrameworkPath)], bundleExecPathAbs) + +# usrLocalPath = '/usr/local/lib/' +# appUsrLocalPath = '@executable_path/../Frameworks/' +# depdict = WalkFrameworkPaths(pythonFrameworkPath) +# PerformChanges(depdict, [(usrLocalPath, appUsrLocalPath)], bundleExecPathAbs) diff --git a/test-pylib-script.py b/test-pylib-script.py new file mode 100644 index 000000000..52d1c0673 --- /dev/null +++ b/test-pylib-script.py @@ -0,0 +1,29 @@ +import site; print(site.getsitepackages()) + +import pip +installed_packages = pip.get_installed_distributions() +installed_packages_list = sorted(["%s==%s" % (i.key, i.version) + for i in installed_packages]) +print(installed_packages_list) + +print("-------------") + +if pip.main(['install', '--upgrade', 'numpy']) > 0: + exit(1) +print("-------------") + + +print("Importing numpy") +import numpy +print("-------------") + + +import sys; +print("Executing from: ", sys.executable) +print("-------------") + +import os +print("Environment variables:") +for variable, value in os.environ.items(): + print(variable, ":", value) + diff --git a/testdata/bool/and5.oas b/testdata/bool/and5.oas index f5024e6ed..ae8b6e9b0 100644 Binary files a/testdata/bool/and5.oas and b/testdata/bool/and5.oas differ diff --git a/testdata/bool/and5_tz.oas b/testdata/bool/and5_tz.oas index 2641e071c..be6d332ec 100644 Binary files a/testdata/bool/and5_tz.oas and b/testdata/bool/and5_tz.oas differ diff --git a/testdata/bool/and6.oas b/testdata/bool/and6.oas index ee22c6308..e87596f2c 100644 Binary files a/testdata/bool/and6.oas and b/testdata/bool/and6.oas differ diff --git a/testdata/bool/and6_tz.oas b/testdata/bool/and6_tz.oas index a89c61f5c..c5c9e2f4b 100644 Binary files a/testdata/bool/and6_tz.oas and b/testdata/bool/and6_tz.oas differ diff --git a/testdata/bool/anotb5.oas b/testdata/bool/anotb5.oas index 774df860e..fe8e6f051 100644 Binary files a/testdata/bool/anotb5.oas and b/testdata/bool/anotb5.oas differ diff --git a/testdata/bool/anotb6.oas b/testdata/bool/anotb6.oas index c9279ee05..0573a3b89 100644 Binary files a/testdata/bool/anotb6.oas and b/testdata/bool/anotb6.oas differ diff --git a/testdata/bool/bnota5.oas b/testdata/bool/bnota5.oas index 92d051928..ee62916be 100644 Binary files a/testdata/bool/bnota5.oas and b/testdata/bool/bnota5.oas differ diff --git a/testdata/bool/bnota6.oas b/testdata/bool/bnota6.oas index f0e8754b6..f028aec52 100644 Binary files a/testdata/bool/bnota6.oas and b/testdata/bool/bnota6.oas differ diff --git a/testdata/bool/or5.oas b/testdata/bool/or5.oas index 78294b9de..cec054220 100644 Binary files a/testdata/bool/or5.oas and b/testdata/bool/or5.oas differ diff --git a/testdata/bool/or6.oas b/testdata/bool/or6.oas index f911c0434..ae8775394 100644 Binary files a/testdata/bool/or6.oas and b/testdata/bool/or6.oas differ diff --git a/testdata/bool/size1_au.oas b/testdata/bool/size1_au.oas index c6bcdd6d2..6d53c7d42 100644 Binary files a/testdata/bool/size1_au.oas and b/testdata/bool/size1_au.oas differ diff --git a/testdata/bool/size5_au10.oas b/testdata/bool/size5_au10.oas index 373b7e290..d1b547c46 100644 Binary files a/testdata/bool/size5_au10.oas and b/testdata/bool/size5_au10.oas differ diff --git a/testdata/bool/size6_au1.oas b/testdata/bool/size6_au1.oas index 1f036c931..d23735267 100644 Binary files a/testdata/bool/size6_au1.oas and b/testdata/bool/size6_au1.oas differ diff --git a/testdata/bool/size8_au3.oas b/testdata/bool/size8_au3.oas index 335cd8faf..4313fc222 100644 Binary files a/testdata/bool/size8_au3.oas and b/testdata/bool/size8_au3.oas differ diff --git a/testdata/bool/special2_au1.oas b/testdata/bool/special2_au1.oas index 4ede68bd4..c55543d9f 100644 Binary files a/testdata/bool/special2_au1.oas and b/testdata/bool/special2_au1.oas differ diff --git a/testdata/bool/special2_au1_tz.oas b/testdata/bool/special2_au1_tz.oas index b677cc72d..86229ca34 100644 Binary files a/testdata/bool/special2_au1_tz.oas and b/testdata/bool/special2_au1_tz.oas differ diff --git a/testdata/bool/special2_au2.oas b/testdata/bool/special2_au2.oas index 64f535f29..9885b6e32 100644 Binary files a/testdata/bool/special2_au2.oas and b/testdata/bool/special2_au2.oas differ diff --git a/testdata/bool/special2_au2_tz.oas b/testdata/bool/special2_au2_tz.oas index 76d37a8c9..9ca99c96f 100644 Binary files a/testdata/bool/special2_au2_tz.oas and b/testdata/bool/special2_au2_tz.oas differ diff --git a/testdata/bool/special2_au3.oas b/testdata/bool/special2_au3.oas index ba4797a8a..7410b1c99 100644 Binary files a/testdata/bool/special2_au3.oas and b/testdata/bool/special2_au3.oas differ diff --git a/testdata/bool/special2_au3_tz.oas b/testdata/bool/special2_au3_tz.oas index 0025f84a2..b2d93d569 100644 Binary files a/testdata/bool/special2_au3_tz.oas and b/testdata/bool/special2_au3_tz.oas differ diff --git a/testdata/bool/special2_au4.oas b/testdata/bool/special2_au4.oas index cb6e9e7e7..0e2d0b2d6 100644 Binary files a/testdata/bool/special2_au4.oas and b/testdata/bool/special2_au4.oas differ diff --git a/testdata/bool/special2_au4_tz.oas b/testdata/bool/special2_au4_tz.oas index 1528b10ea..381af30ae 100644 Binary files a/testdata/bool/special2_au4_tz.oas and b/testdata/bool/special2_au4_tz.oas differ diff --git a/testdata/bool/special2_au5.oas b/testdata/bool/special2_au5.oas index e8f38fa72..045eac670 100644 Binary files a/testdata/bool/special2_au5.oas and b/testdata/bool/special2_au5.oas differ diff --git a/testdata/bool/special2_au5_tz.oas b/testdata/bool/special2_au5_tz.oas index e4bea08c4..6081a28b4 100644 Binary files a/testdata/bool/special2_au5_tz.oas and b/testdata/bool/special2_au5_tz.oas differ diff --git a/testdata/bool/xor5.oas b/testdata/bool/xor5.oas index f35773fdc..e84433c0d 100644 Binary files a/testdata/bool/xor5.oas and b/testdata/bool/xor5.oas differ diff --git a/testdata/bool/xor5_max.oas b/testdata/bool/xor5_max.oas index a8a5c9553..bb1881ff3 100644 Binary files a/testdata/bool/xor5_max.oas and b/testdata/bool/xor5_max.oas differ diff --git a/testdata/bool/xor5_tz.oas b/testdata/bool/xor5_tz.oas index 7d4aeb77c..b2dc9a489 100644 Binary files a/testdata/bool/xor5_tz.oas and b/testdata/bool/xor5_tz.oas differ diff --git a/testdata/bool/xor6.oas b/testdata/bool/xor6.oas index 445a0ed79..a4232d0be 100644 Binary files a/testdata/bool/xor6.oas and b/testdata/bool/xor6.oas differ diff --git a/testdata/bool/xor6_max.oas b/testdata/bool/xor6_max.oas index 9dbdfc74d..f393482ba 100644 Binary files a/testdata/bool/xor6_max.oas and b/testdata/bool/xor6_max.oas differ diff --git a/testdata/bool/xor6_tz.oas b/testdata/bool/xor6_tz.oas index 4dd260178..8ed23c143 100644 Binary files a/testdata/bool/xor6_tz.oas and b/testdata/bool/xor6_tz.oas differ diff --git a/testdata/bool/xor7_au1.oas b/testdata/bool/xor7_au1.oas index 6ffc97315..285983e05 100644 Binary files a/testdata/bool/xor7_au1.oas and b/testdata/bool/xor7_au1.oas differ diff --git a/testdata/bool/xor7_au2.oas b/testdata/bool/xor7_au2.oas index 6ffc97315..285983e05 100644 Binary files a/testdata/bool/xor7_au2.oas and b/testdata/bool/xor7_au2.oas differ diff --git a/testdata/bool/xor7_au_tz.oas b/testdata/bool/xor7_au_tz.oas index 0cb98f913..35bd5e3b1 100644 Binary files a/testdata/bool/xor7_au_tz.oas and b/testdata/bool/xor7_au_tz.oas differ diff --git a/testdata/bool/xor7_max_au1.oas b/testdata/bool/xor7_max_au1.oas index bcec6b636..fce726952 100644 Binary files a/testdata/bool/xor7_max_au1.oas and b/testdata/bool/xor7_max_au1.oas differ diff --git a/testdata/bool/xor7_max_au2.oas b/testdata/bool/xor7_max_au2.oas index bcec6b636..fce726952 100644 Binary files a/testdata/bool/xor7_max_au2.oas and b/testdata/bool/xor7_max_au2.oas differ diff --git a/testdata/bool/xor8_au1.oas b/testdata/bool/xor8_au1.oas index b14e9cdf4..b01d34871 100644 Binary files a/testdata/bool/xor8_au1.oas and b/testdata/bool/xor8_au1.oas differ diff --git a/testdata/bool/xor8_au2.oas b/testdata/bool/xor8_au2.oas index b14e9cdf4..b01d34871 100644 Binary files a/testdata/bool/xor8_au2.oas and b/testdata/bool/xor8_au2.oas differ diff --git a/testdata/bool/xor8_au_tz.oas b/testdata/bool/xor8_au_tz.oas index c238d222f..36eb029cf 100644 Binary files a/testdata/bool/xor8_au_tz.oas and b/testdata/bool/xor8_au_tz.oas differ diff --git a/testdata/klayout_main/main.rb b/testdata/klayout_main/main.rb index 1601ce803..526167824 100644 --- a/testdata/klayout_main/main.rb +++ b/testdata/klayout_main/main.rb @@ -30,10 +30,14 @@ load("test_prologue.rb") class KLayoutMain_TestClass < TestBase + def klayout_bin + File.join(RBA::Application::instance.inst_path, "klayout") + end + def test_1 # Basic - version = `./klayout -v` + version = `#{self.klayout_bin} -v` assert_equal(version, "#{RBA::Application.instance.version}\n") end @@ -41,10 +45,10 @@ class KLayoutMain_TestClass < TestBase def test_2 # Basic Ruby - out = `./klayout -b -rd v1=42 -rd v2=hello -r #{File.join(File.dirname(__FILE__), "test.rb")}` + out = `#{self.klayout_bin} -b -rd v1=42 -rd v2=hello -r #{File.join(File.dirname(__FILE__), "test.rb")}` assert_equal(out, "Variable v1=42 v2=hello\n") - out = `./klayout -b -rd v1=42 -rd v2=hello -r #{File.join(File.dirname(__FILE__), "test.rb")} -rm #{File.join(File.dirname(__FILE__), "test2.rb")} -rm #{File.join(File.dirname(__FILE__), "test3.rb")}` + out = `#{self.klayout_bin} -b -rd v1=42 -rd v2=hello -r #{File.join(File.dirname(__FILE__), "test.rb")} -rm #{File.join(File.dirname(__FILE__), "test2.rb")} -rm #{File.join(File.dirname(__FILE__), "test3.rb")}` assert_equal(out, "test2\ntest3\nVariable v1=42 v2=hello\n") end @@ -52,7 +56,7 @@ class KLayoutMain_TestClass < TestBase def test_3 # Basic Python - out = `./klayout -b -rd v1=42 -rd v2=hello -r #{File.join(File.dirname(__FILE__), "test.py")}` + out = `#{self.klayout_bin} -b -rd v1=42 -rd v2=hello -r #{File.join(File.dirname(__FILE__), "test.py")}` assert_equal(out, "Variable v1=42 v2=hello\n") end @@ -62,20 +66,20 @@ class KLayoutMain_TestClass < TestBase # Application class if !RBA.constants.find { |x| x == :QDialog || x == "QDialog" } - out = `./klayout -b -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` + out = `#{self.klayout_bin} -b -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` assert_equal(out, "RBA::Application superclass Object\nMainWindow is not there\n") - out = `./klayout -z -nc -rx -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` + out = `#{self.klayout_bin} -z -nc -rx -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` assert_equal(out, "RBA::Application superclass Object\nMainWindow is there\n") else # QCoreApplication for (headless) mode - out = `./klayout -b -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` + out = `#{self.klayout_bin} -b -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` assert_equal(out, "RBA::Application superclass RBA::QCoreApplication_Native\nMainWindow is not there\n") # QApplication for GUI mode - out = `./klayout -z -nc -rx -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` + out = `#{self.klayout_bin} -z -nc -rx -r #{File.join(File.dirname(__FILE__), "test_app.rb")}` assert_equal(out, "RBA::Application superclass RBA::QApplication_Native\nMainWindow is there\n") end @@ -85,7 +89,7 @@ class KLayoutMain_TestClass < TestBase def test_5 # Script variables - out = `./klayout -b -wd tv1=17 -wd tv2=25 -wd tv3 -r #{File.join(File.dirname(__FILE__), "test_script.rb")}` + out = `#{self.klayout_bin} -b -wd tv1=17 -wd tv2=25 -wd tv3 -r #{File.join(File.dirname(__FILE__), "test_script.rb")}` assert_equal(out, "42\ntrue\n") end @@ -93,10 +97,10 @@ class KLayoutMain_TestClass < TestBase def test_6 # Editable / Non-editable mode - out = `./klayout -b -ne -r #{File.join(File.dirname(__FILE__), "test_em.rb")}` + out = `#{self.klayout_bin} -b -ne -r #{File.join(File.dirname(__FILE__), "test_em.rb")}` assert_equal(out, "false\n") - out = `./klayout -b -e -r #{File.join(File.dirname(__FILE__), "test_em.rb")}` + out = `#{self.klayout_bin} -b -e -r #{File.join(File.dirname(__FILE__), "test_em.rb")}` assert_equal(out, "true\n") end @@ -107,27 +111,27 @@ class KLayoutMain_TestClass < TestBase File.open(cfg_file, "w") { |file| file.puts("") } # Special configuration file - `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config1.rb")}` + `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config1.rb")}` - out = `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` + out = `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` assert_equal(out, "42\n") # Update - `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config2.rb")}` + `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config2.rb")}` - out = `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` + out = `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` assert_equal(out, "17\n") # Reset - `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config1.rb")}` + `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config1.rb")}` - out = `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` + out = `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` assert_equal(out, "42\n") # No update - `./klayout -b -t -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config2.rb")}` + `#{self.klayout_bin} -b -t -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_set_config2.rb")}` - out = `./klayout -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` + out = `#{self.klayout_bin} -b -c #{cfg_file} -r #{File.join(File.dirname(__FILE__), "test_read_config.rb")}` assert_equal(out, "42\n") end @@ -135,13 +139,13 @@ class KLayoutMain_TestClass < TestBase def test_8 # Loading layouts - out = `./klayout -z -nc -rx #{File.join(File.dirname(__FILE__), "test1.gds")} -r #{File.join(File.dirname(__FILE__), "test_lay.rb")}` + out = `#{self.klayout_bin} -z -nc -rx #{File.join(File.dirname(__FILE__), "test1.gds")} -r #{File.join(File.dirname(__FILE__), "test_lay.rb")}` assert_equal(out, "TOP1\n") - out = `./klayout -z -nc -rx #{File.join(File.dirname(__FILE__), "test1.gds")} #{File.join(File.dirname(__FILE__), "test2.gds")} -r #{File.join(File.dirname(__FILE__), "test_lay2.rb")}` + out = `#{self.klayout_bin} -z -nc -rx #{File.join(File.dirname(__FILE__), "test1.gds")} #{File.join(File.dirname(__FILE__), "test2.gds")} -r #{File.join(File.dirname(__FILE__), "test_lay2.rb")}` assert_equal(out, "TOP1\nTOP2\n") - out = `./klayout -z -nc -rx #{File.join(File.dirname(__FILE__), "test1.gds")} #{File.join(File.dirname(__FILE__), "test2.gds")} -s -r #{File.join(File.dirname(__FILE__), "test_lay2.rb")}` + out = `#{self.klayout_bin} -z -nc -rx #{File.join(File.dirname(__FILE__), "test1.gds")} #{File.join(File.dirname(__FILE__), "test2.gds")} -s -r #{File.join(File.dirname(__FILE__), "test_lay2.rb")}` assert_equal(out, "TOP1;TOP2\n") end @@ -149,7 +153,7 @@ class KLayoutMain_TestClass < TestBase def test_9 # Sessions - out = `./klayout -z -nc -rx -u #{File.join(File.dirname(__FILE__), "session.lys")} -r #{File.join(File.dirname(__FILE__), "test_lay2.rb")}` + out = `#{self.klayout_bin} -z -nc -rx -u #{File.join(File.dirname(__FILE__), "session.lys")} -r #{File.join(File.dirname(__FILE__), "test_lay2.rb")}` assert_equal(out, "TOP2;TOP1\n") end diff --git a/testdata/oasis/issue_152.oas b/testdata/oasis/issue_152.oas new file mode 100644 index 000000000..44eb8f084 Binary files /dev/null and b/testdata/oasis/issue_152.oas differ diff --git a/testdata/pymod/bridge.py b/testdata/pymod/bridge.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtCore.py b/testdata/pymod/import_QtCore.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtDesigner.py b/testdata/pymod/import_QtDesigner.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtGui.py b/testdata/pymod/import_QtGui.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtMultimedia.py b/testdata/pymod/import_QtMultimedia.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtNetwork.py b/testdata/pymod/import_QtNetwork.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtPrintSupport.py b/testdata/pymod/import_QtPrintSupport.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtSql.py b/testdata/pymod/import_QtSql.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtSvg.py b/testdata/pymod/import_QtSvg.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtWidgets.py b/testdata/pymod/import_QtWidgets.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtXml.py b/testdata/pymod/import_QtXml.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_QtXmlPatterns.py b/testdata/pymod/import_QtXmlPatterns.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_db.py b/testdata/pymod/import_db.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_lay.py b/testdata/pymod/import_lay.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_lay_noqt.py b/testdata/pymod/import_lay_noqt.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_rdb.py b/testdata/pymod/import_rdb.py old mode 100644 new mode 100755 diff --git a/testdata/pymod/import_tl.py b/testdata/pymod/import_tl.py old mode 100644 new mode 100755 diff --git a/testdata/ruby/dbLayout.rb b/testdata/ruby/dbLayout.rb index 9b100d8ba..5527a99db 100644 --- a/testdata/ruby/dbLayout.rb +++ b/testdata/ruby/dbLayout.rb @@ -340,6 +340,17 @@ class DBLayout_TestClass < TestBase i1copy.region = RBA::Box.new(0, 0, 100, 101) assert_equal(i1copy.region.to_s, "(0,0;100,101)") assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Region::new(RBA::Box.new(0, 0, 100, 101)) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) + i1copy.confine_region(RBA::Box.new(0, 0, 100, 101)) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) + i1copy.confine_region(RBA::Region::new(RBA::Box.new(0, 0, 100, 101))) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 101, 101)); assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)"); i1o = c0.begin_shapes_rec_overlapping(0, RBA::Box.new(0, 0, 101, 101)); diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 9e8e77d92..00ce28bf6 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -1361,6 +1361,23 @@ class DBShapes_TestClass < TestBase end + # issue 152 (number of shapes count wrong in viewer mode) + def test_10 + + ly = RBA::Layout::new(true) + ly.read(File.join($ut_testsrc, "testdata", "oasis", "issue_152.oas")) + assert_equal(ly.top_cell.shapes(ly.layer(1, 0)).size, 200) + ly.top_cell.shapes(ly.layer(1, 0)).insert(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 100))) + assert_equal(ly.top_cell.shapes(ly.layer(1, 0)).size, 201) + + ly = RBA::Layout::new(false) + ly.read(File.join($ut_testsrc, "testdata", "oasis", "issue_152.oas")) + assert_equal(ly.top_cell.shapes(ly.layer(1, 0)).size, 200) + ly.top_cell.shapes(ly.layer(1, 0)).insert(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 100))) + assert_equal(ly.top_cell.shapes(ly.layer(1, 0)).size, 201) + + end + end load("test_epilogue.rb")