diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..674f488a3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# Set update schedule for GitHub Actions + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "monthly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8fb19811..135cc3295 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,8 +4,11 @@ name: Build Python Wheels on: pull_request: - # Running on PRs is enough - # push: + # Running on PRs and main branch only + # pull requests should be able to access cache from main branch + push: + branches: + - master release: types: [published] @@ -28,12 +31,13 @@ jobs: if: matrix.os == 'ubuntu-latest' uses: jlumbroso/free-disk-space@main with: - android: false - dotnet: false - haskell: false + android: true + dotnet: true + haskell: true + large-packages: false # not working currently - uses: hmarr/debug-action@v2 - name: Cancel Workflow Action - uses: styfle/cancel-workflow-action@0.9.1 + uses: styfle/cancel-workflow-action@0.11.0 - uses: actions/checkout@v3 - name: ccache uses: hendrikmuhs/ccache-action@v1.2 @@ -48,12 +52,13 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.12.0 + uses: pypa/cibuildwheel@v2.13.1 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value env: CIBW_BUILD: ${{ matrix.cibuild }} + - name: Download Cache from Docker (linux only) if: ${{ runner.os == 'Linux' }} # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed @@ -65,24 +70,21 @@ jobs: mv ./wheelhouse/.ccache $HOST_CCACHE_DIR ls -la $HOST_CCACHE_DIR ccache -s - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl -# The following was taken from https://cibuildwheel.readthedocs.io/en/stable/deliver-to-pypi/ + # The following was taken from https://cibuildwheel.readthedocs.io/en/stable/deliver-to-pypi/ make_sdist: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 # Optional, use if you use setuptools_scm - submodules: true # Optional, use if you have submodules + - uses: actions/checkout@v3 - name: Build SDist run: pipx run build --sdist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: dist/*.tar.gz @@ -90,29 +92,29 @@ jobs: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.4.2 + - uses: pypa/gh-action-pypi-publish@v1.8.7 continue-on-error: true # might fail if we don't bump the version with: user: __token__ password: ${{ secrets.test_pypi_password }} - repository_url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ upload_to_pypi: needs: [build, make_sdist] runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.4.2 + - uses: pypa/gh-action-pypi-publish@v1.8.7 with: user: __token__ password: ${{ secrets.pypi_password }} diff --git a/.github/workflows/cache-maintenance.yml b/.github/workflows/cache-maintenance.yml new file mode 100644 index 000000000..ec933b1f1 --- /dev/null +++ b/.github/workflows/cache-maintenance.yml @@ -0,0 +1,35 @@ +name: cleanup caches by a branch +# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries +# Caches are very large and must be deleted to prioritize main branch's caches. +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + REPO=${{ github.repository }} + BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/Changelog b/Changelog index 8c8eff60f..95dc314f7 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,67 @@ +0.28.10 (2023-07-05): +* Bugfix: %GITHUB%/issues/1397 LayoutMetaInfo serialization/deserialization problem +* Bugfix: %GITHUB%/issues/1404 Better support for editing mode in Qt-less LayoutView +* Bugfix: %GITHUB%/issues/1407 Clip functions do not support polygons with holes +* Bugfix: %GITHUB%/issues/1409 Text edit issue (internal error) +* Enhancements: + - Made 'time' parameter optional for MainWindow#message + - strmxor performance with --deep enhanced + +0.28.9 (2023-06-10): +* Enhancement: %GITHUB%/issues/1281 Layout diff can ignore shape or instance duplicates +* Bugfix: %GITHUB%/issues/1393 GDS2Text format not supported in Python module +* Bugfix: "add meta info" did not take "persisted" flag +* Enhancement: "profile" feature for DRC and LVS +* Enhancement: DRC can write to multiple targets now + - new functions "new_report", "new_target": create output channel objects + that can be used to redirect "output" to specific other channels +* Bugfix: GDS2 reader should not segfault on certain broken files +* Enhancement: performance improvement of hierarchical XOR in certain cases +* Enhancement: New methods + - Layout#copy_layer, Layout#move_layer and Layout#clear_layer with shape type selector + - Shapes#clear with shape type selector + +0.28.8 (2023-05-23): +* Enhancement: %GITHUB%/issues/1314 Storing (arbitrary) data in metadata + - Meta information can be attached to layout and cells + - Meta information has a string key and arbitrary value (hash, list supported) + - Optionally, meta information can be made persistent and + is stored in the (KLayout specific) context section of GDS2 and OASIS + - Meta information is shown in the user properties dialog, but is not editable + - Meta information is similar, but not the same than user properties +* Enhancement: %GITHUB%/issues/1324 Feature request: hide empty groups +* Enhancement: %GITHUB%/issues/1345 feature request: create a def single/mulitpart path import option +* Enhancement: %GITHUB%/issues/1348 "Reload Files" popup prevents closing KLayout +* Enhancement: %GITHUB%/issues/1349 Add application events for indicating start/finish of restoring session +* Bugfix: %GITHUB%/issues/1353 Bug in RecursiveInstanceIterator +* Enhancement: %GITHUB%/issues/1357 Add binding for QObject::findChildren +* Bugfix: %GITHUB%/issues/1360 LayoutView not promoted to correct class after Plugin initialization +* Bugfix: %GITHUB%/issues/1366 Slow merging of polygons from width check edge pairs +* Bugfix: %GITHUB%/issues/1373 Region "+" skips shapes with user properties on second input +* Enhancement: Ruby debugger performance improvement - with debugger enabled, Ruby script execution was very slow +* Enhancement: Polygon "decompose_convex" tries harder to avoid generating thin slivers +* (Significant) Enhancement: Selection of labels now considers label area, not just label origin point +* Bugfix: Making 'R', 'L' and 'C' parameters for the respective Spice elements beside "value" +* Enhancement: Zoom In/Out menu functions now use the current mouse position for the zoom fixpoint (important when binding them to a key) +* Bugfix: More consistent behavior of RBA/pya: enum classes are properly made available (was for example RBA::Qt::Qt_Keys instead of RBA::Qt_Keys and pya.Qt.Keys was no fully initialized type object) +* Bugfix: Netlist reader: anonymous circuits (without definition) will not fail because of unknown parameters +* Bugfix: Closed paths lost last point after editing in partial edit mode +* Enhancement: Somewhat better grid snapping in partial edit mode +* Bugfixes/enhancements: Macro editor + - fixed missing icons for "back" and "forward" + - enhancements for "search & replace" + - Ctrl+R and Ctrl+Shift+R for replace and "replace all" + - Fixed tool tips for buttons + +0.28.7 (2023-04-22): +* Enhancement: %GITHUB%/issues/1320 Support for .lib statement in Spice +* Bugfix: %GITHUB%/issues/1321 Tilde expansion in paths (e.g. "~/test.gds") on Linux +* Enhancement: %GITHUB%/issues/1322 RBA/pya: Manipulation of NetTracerTechnologComponent +* Bugfix: %GITHUB%/issues/1327 Python module: segfault on exit when hierarchy iterators are alive +* Bugfix: %GITHUB%/issues/1328 Width of layer selection boxes fixed and too small for long layer names +* Enhancement: %GITHUB%/issues/1339 RBA/pya: A method to get the QWidget for a LayoutView +* Enhancement: Better compatibility of Spice reader with ngspice + 0.28.6 (2023-03-16): * Enhancement: %GITHUB%/issues/1249 Include expanded/collapsed state of layer properties into session * Bugfix: %GITHUB%/issues/1265 Issues installing klayout with pip on macOS related to libpng diff --git a/Changelog.Debian b/Changelog.Debian index 3b8e4ab7e..246065377 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,31 @@ +klayout (0.28.10-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Wed, 05 Jul 2023 18:37:13 +0200 + +klayout (0.28.9-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Sat, 10 Jun 2023 09:27:25 +0200 + +klayout (0.28.8-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Mon, 22 May 2023 23:10:56 +0200 + +klayout (0.28.7-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Sat, 22 Apr 2023 15:18:27 +0200 + klayout (0.28.6-1) unstable; urgency=low * New features and bugfixes diff --git a/MANIFEST.in b/MANIFEST.in index c2baf9196..3ef1c7d19 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -14,10 +14,8 @@ recursive-include src/rdb/rdb *.cc *.h recursive-include src/tl/tl *.cc *.h recursive-include src/version *.cc *.h recursive-include src/pymod *.pyi -include src/plugins/*/db_plugin/*.cc -include src/plugins/*/*/db_plugin/*.cc -include src/plugins/*/db_plugin/*.h -include src/plugins/*/*/db_plugin/*.h +recursive-include src/plugins/*/db_plugin *.cc *.h +recursive-include src/plugins/*/*/db_plugin *.cc *.h recursive-include src/plugins/common *.h include version.sh include src/py.typed diff --git a/ci-scripts/macos/libpng-patch.sh b/ci-scripts/macos/libpng-patch.sh new file mode 100644 index 000000000..8cd7973a5 --- /dev/null +++ b/ci-scripts/macos/libpng-patch.sh @@ -0,0 +1,29 @@ +# Solves issue #1346 +# Deprecate patch when Github Actions provides Apple Silicon runners. +# WARNING: Only run this in a CI runner + +# Issue: klayout depends on libpng, which is loaded by homebrew. +# But M1 wheels are cross-compiled in an intel host, so by default +# libpng.dylib has an x86 architecture. This prevents linking the library +# in the tl library. + +# Patch solution: manually download libpng arm64 pre-built library from homebrew + +# Reading +# https://stackoverflow.com/questions/55110564/how-can-i-install-a-homebrew-bottle-designed-for-a-different-previous-version +# https://github.com/Homebrew/homebrew-core/blob/6abea16e917b12d6af4912736f66eb8d42bf089b/Formula/libpng.rb + +export HOMEBREW_NO_INSTALL_CLEANUP=1 +export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 + +# Check if we're about to build an arm64 wheel. WARNING universal wheels not supported +if [ "$_PYTHON_HOST_PLATFORM" == "macosx-11.0-arm64" ] +then + # Exit early if done + test -f /tmp/libpng-apple-silicon-installed && exit 0 + curl -L -H "Authorization: Bearer QQ==" -o /tmp/libpng-1.6.39.arm64_big_sur.bottle.1.tar.gz https://ghcr.io/v2/homebrew/core/libpng/blobs/sha256:cf59cedc91afc6f2f3377567ba82b99b97744c60925a5d1df6ecf923fdb2f234 + brew reinstall -f /tmp/libpng-1.6.39.arm64_big_sur.bottle.1.tar.gz || exit 1 + touch /tmp/libpng-apple-silicon-installed +elif [ -f "/tmp/libpng-apple-silicon-installed" ]; then + brew reinstall -f libpng && rm -f /tmp/libpng-apple-silicon-installed +fi \ No newline at end of file diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index 86d44bb0d..ef2e8ff0d 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -196,7 +196,7 @@ Ruby31MacPorts = { 'exe': '/opt/local/bin/ruby3.1', # install with 'sudo port install ruby32' # [Key Type Name] = 'MP32' Ruby32MacPorts = { 'exe': '/opt/local/bin/ruby3.2', - 'inc': '/opt/local/include/ruby-3.2.1', + 'inc': '/opt/local/include/ruby-3.2.2', 'lib': '/opt/local/lib/libruby.3.2.dylib' } diff --git a/pyproject.toml b/pyproject.toml index e30cc2354..c3c614d63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,9 @@ test-command = [ # Disable building PyPy wheels on all platforms skip = "pp*" +# Skip trying to test arm64 builds on Intel Macs +test-skip = "*-macosx_arm64 *-macosx_universal2:arm64" + [tool.cibuildwheel.linux] # beware: the before-all script does not persist environment variables! # No point in defining $PATH or $CCACHE_DIR @@ -28,5 +31,13 @@ before-all = [ "brew install libpng", "brew deps --tree --installed", ] + +# TEMP while Github Actions does not provide an Apple Silicon runner +# TODO Simply remove it when it's available +archs = ["x86_64", "arm64"] +before-build = [ + "ccache -s", + "bash {project}/ci-scripts/macos/libpng-patch.sh", +] # Repair macOS wheels (include libpng with the wheel, for example) repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v --ignore-missing-dependencies {wheel}" \ No newline at end of file diff --git a/scripts/extract_user_doc.rb b/scripts/extract_user_doc.rb index d83b99a78..ee3aeba7a 100755 --- a/scripts/extract_user_doc.rb +++ b/scripts/extract_user_doc.rb @@ -231,7 +231,7 @@ hs.urls.each do |url| t = HEADER + xml2html(t) + TAIL tt = nil - if File.exists?(fn) + if File.exist?(fn) File.open(fn, "rb") { |f| tt = f.read } end diff --git a/scripts/make_stubs.sh b/scripts/make_stubs.sh index 1df806a28..b5fc4a9f9 100755 --- a/scripts/make_stubs.sh +++ b/scripts/make_stubs.sh @@ -1,29 +1,40 @@ #!/bin/bash -e -# Generates LVS and DRC documentation +# Generates pyi stubs # # Run this script from a valid build below the repository root, e.g. -# /build-debug. It needs to have a "pymod" installation in +# /build-debug. It needs to have a "pymod" installation in # current directory. inst=$(realpath $(dirname $0)) -scripts=${inst}/drc_lvs_doc -ld=$(realpath .) -pymod="$ld/pymod" +## Detect if klayout pymod libraries are installed +python= +for try_python in python python3; do + if $try_python -c "import klayout.tl" >/dev/null 2>&1; then + python=$try_python + fi +done -export LD_LIBRARY_PATH=$ld -export PYTHONPATH=$pymod +if [ "$python" = "" ]; then + echo "*** Searching for pymod..." -pymod_src=$ld/../src/pymod -if ! [ -e $pymod_src ]; then - echo "*** ERROR: missing pymod sources ($pymod_src) - did you run the script from the build folder below the source tree?" - exit 1 -fi + ld=$(realpath .) + pymod="$ld/pymod" -if ! [ -e $pymod ]; then - echo "*** ERROR: missing pymod folder ($pymod) - did you run the script from the build folder?" - exit 1 + export LD_LIBRARY_PATH=$ld + export PYTHONPATH=$pymod + + pymod_src=$ld/../src/pymod + if ! [ -e $pymod_src ]; then + echo "*** ERROR: missing pymod sources ($pymod_src) - did you run the script from the build folder below the source tree?" + exit 1 + fi + + if ! [ -e $pymod ]; then + echo "*** ERROR: missing pymod folder ($pymod) - did you run the script from the build folder?" + exit 1 + fi fi python= @@ -38,20 +49,21 @@ if [ "$python" = "" ]; then exit 1 fi +pyi_srcdir="$inst/../src/pymod/distutils_src/klayout" + echo "Generating stubs for tl .." -$python $inst/stubgen.py tl >$pymod_src/distutils_src/klayout/tlcore.pyi +$python $inst/stubgen.py tl >$pyi_srcdir/tlcore.pyi echo "Generating stubs for db .." -$python $inst/stubgen.py db tl >$pymod_src/distutils_src/klayout/dbcore.pyi +$python $inst/stubgen.py db tl >$pyi_srcdir/dbcore.pyi echo "Generating stubs for rdb .." -$python $inst/stubgen.py rdb tl,db >$pymod_src/distutils_src/klayout/rdbcore.pyi +$python $inst/stubgen.py rdb tl,db >$pyi_srcdir/rdbcore.pyi echo "Generating stubs for lay .." -$python $inst/stubgen.py lay tl,db,rdb >$pymod_src/distutils_src/klayout/laycore.pyi +$python $inst/stubgen.py lay tl,db,rdb >$pyi_srcdir/laycore.pyi echo "Generating stubs for lib .." -$python $inst/stubgen.py lib tl,db >$pymod_src/distutils_src/klayout/libcore.pyi +$python $inst/stubgen.py lib tl,db >$pyi_srcdir/libcore.pyi echo "Done." - diff --git a/scripts/mkqtdecl_common/common.conf b/scripts/mkqtdecl_common/common.conf index a54cf6694..1e66dd860 100644 --- a/scripts/mkqtdecl_common/common.conf +++ b/scripts/mkqtdecl_common/common.conf @@ -62,15 +62,56 @@ DECL end # -------------------------------------------------------------- -# Provides the definitions for QObject::findChild +# Provides the definitions for QObject::findChild and QObject::findChildren def add_native_impl_QObject_findChild() # alternative implementation for QObject::findChild using QObject for T add_native_impl("QObject", <<'CODE', <<'DECL') - QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild (name); } + +#if QT_VERSION < 0x50000 + +#include + + QObject *find_child_impl (QObject *object, const QString &name) + { + return object->findChild (name); + } + QList find_children_impl (QObject *object, const QString &name) + { + return object->findChildren (name); + } + QList find_children_impl2 (QObject *object, const QRegExp &re) + { + return object->findChildren (re); + } +#else + +#include + + QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChild (name, options); + } + QList find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChildren (name, options); + } + QList find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options) + { + return object->findChildren (re, options); + } +#endif CODE - gsi::method_ext("findChild", &find_child_impl, "@brief Specialisation for findChild (uses QObject as T).") +#if QT_VERSION < 0x50000 + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), "@brief Specialisation for findChildren (uses QObject as T).") +#else + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") +#endif DECL end diff --git a/scripts/regenerate_stubs.sh b/scripts/regenerate_stubs.sh new file mode 100755 index 000000000..644f594c8 --- /dev/null +++ b/scripts/regenerate_stubs.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e + +# This script regenerates the Python stubs from the sources + +# clean up +rm -rf build dist + +python3 setup.py build +python3 setup.py bdist_wheel +python3 -m venv create python3-venv-make_stubs + +. python3-venv-make_stubs/bin/activate + +pip3 install ./dist/*.whl + +./scripts/make_stubs.sh + diff --git a/scripts/rpm-data/klayout.spec b/scripts/rpm-data/klayout.spec index e261645a0..217a53524 100644 --- a/scripts/rpm-data/klayout.spec +++ b/scripts/rpm-data/klayout.spec @@ -85,15 +85,12 @@ Requires: libqt4-x11 >= 4.8.6 # OpenSuSE Leap 15 requirements Requires: ruby >= 2.5 Requires: python3 >= 3.6 -Requires: libqt4-x11 >= 4.8.7 -%define buildopt -j2 -%endif - -%if "%{target_system}" == "opensuse15" -# OpenSuSE Leap 15 requirements -Requires: ruby >= 2.5 -Requires: python3 >= 3.6 -Requires: libqt4-x11 >= 4.8.7 +Requires: libqt5-qtbase >= 5.15.2 +Requires: libQt5PrintSupport5 >= 5.15.2 +Requires: libQt5Designer5 >= 5.15.2 +Requires: libQt5Multimedia5 >= 5.15.2 +Requires: libQt5Svg5 >= 5.15.2 +Requires: libQt5XmlPatterns5 >= 5.15.2 %define buildopt -j2 %endif diff --git a/setup.py b/setup.py index 748182bdd..6d071b9b1 100644 --- a/setup.py +++ b/setup.py @@ -805,11 +805,13 @@ for pi in dbpi_dirs: mod_name = "_" + os.path.split(os.path.split(pi)[-2])[-1] + "_dbpi" pi_sources = glob.glob(os.path.join(pi, "*.cc")) + pi_sources += glob.glob(os.path.join(pi, "contrib", "*.cc")) pi_ext = Library( config.root + ".db_plugins." + mod_name, define_macros=config.macros() + [("MAKE_DB_PLUGIN_LIBRARY", 1)], include_dirs=[ + pi, os.path.join("src", "plugins", "common"), _db_path, _tl_path, diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index e10ab80dc..3d3b9f75e 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -118,6 +118,7 @@ GenericReaderOptions::GenericReaderOptions () m_lefdef_read_lef_with_def = load_options.get_option_by_name ("lefdef_config.read_lef_with_def").to_bool (); m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool (); + m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool (); m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); } @@ -441,6 +442,11 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd) "This option is used together with '--" + m_long_prefix + "lefdef-produce-regions'. If given, the region polygons will be put " "into a cell hierarchy where the cells indicate the region groups.\n" ) + << tl::arg (group + + "#--" + m_long_prefix + "lefdef-joined-paths", &m_lefdef_joined_paths, "Specifies to produce joined paths for wires", + "If given, multi-segment paths are created for wires if possible (this will fail for 45 degree segments for example). " + "By default, individual straight segments will be produced." + ) << tl::arg (group + "#!--" + m_long_prefix + "lefdef-dont-produce-via-geometry", &m_lefdef_produce_via_geometry, "Skips vias when producing geometry", "If this option is given, no via geometry will be produced." @@ -793,6 +799,7 @@ GenericReaderOptions::configure (db::LoadLayoutOptions &load_options) load_options.set_option_by_name ("lefdef_config.lef_files", tl::Variant (m_lefdef_lef_files.begin (), m_lefdef_lef_files.end ())); load_options.set_option_by_name ("lefdef_config.read_lef_with_def", m_lefdef_read_lef_with_def); load_options.set_option_by_name ("lefdef_config.separate_groups", m_lefdef_separate_groups); + load_options.set_option_by_name ("lefdef_config.joined_paths", m_lefdef_joined_paths); load_options.set_option_by_name ("lefdef_config.map_file", m_lefdef_map_file); load_options.set_option_by_name ("lefdef_config.macro_resolution_mode", m_lefdef_macro_resolution_mode); load_options.set_option_by_name ("lefdef_config.macro_resolution_mode", m_lefdef_macro_resolution_mode); diff --git a/src/buddies/src/bd/bdReaderOptions.h b/src/buddies/src/bd/bdReaderOptions.h index a03b94481..1ca6e8dcd 100644 --- a/src/buddies/src/bd/bdReaderOptions.h +++ b/src/buddies/src/bd/bdReaderOptions.h @@ -185,6 +185,7 @@ private: std::vector m_lefdef_lef_files; bool m_lefdef_read_lef_with_def; bool m_lefdef_separate_groups; + bool m_lefdef_joined_paths; std::string m_lefdef_map_file; int m_lefdef_macro_resolution_mode; std::vector m_lefdef_lef_layout_files; diff --git a/src/buddies/src/bd/strmcmp.cc b/src/buddies/src/bd/strmcmp.cc index 637baaab0..aefcefe6f 100644 --- a/src/buddies/src/bd/strmcmp.cc +++ b/src/buddies/src/bd/strmcmp.cc @@ -41,6 +41,7 @@ BD_PUBLIC int strmcmp (int argc, char *argv[]) std::string infile_a, infile_b; std::string top_a, top_b; bool silent = false; + bool ignore_duplicates = false; bool no_text_orientation = true; bool no_text_details = true; bool no_properties = false; @@ -106,6 +107,10 @@ BD_PUBLIC int strmcmp (int argc, char *argv[]) << tl::arg ("--expand-arrays", &flatten_array_insts, "Expands array instances before compare", "With this option, arrays are equivalent single instances are treated identical." ) + << tl::arg ("-1|--ignore-duplicates", &ignore_duplicates, "Ignore duplicate instances and shapes", + "With this option, duplicate instances or shapes are ignored and duplication " + "does not count as a difference." + ) << tl::arg ("-l|--layer-details", &dont_summarize_missing_layers, "Prints details about differences for missing layers", "With this option, missing layers are treated as \"empty\" and details about differences to " "other, non-empty layers are printed. Essentially the content of the non-empty counterpart " @@ -155,6 +160,9 @@ BD_PUBLIC int strmcmp (int argc, char *argv[]) if (silent) { flags |= db::layout_diff::f_silent; } + if (ignore_duplicates) { + flags |= db::layout_diff::f_ignore_duplicates; + } if (no_text_orientation) { flags |= db::layout_diff::f_no_text_orientation; } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index ccac991fd..a6d8487e6 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -754,6 +754,7 @@ bool run_deep_xor (const XORData &xor_data) { db::DeepShapeStore dss; dss.set_threads (xor_data.threads); + dss.set_wants_all_cells (true); // saves time for less cell mapping operations double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 066dbb5d2..6914e12fa 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -124,6 +124,7 @@ SOURCES = \ gsiDeclDbLibrary.cc \ gsiDeclDbManager.cc \ gsiDeclDbMatrix.cc \ + gsiDeclDbMetaInfo.cc \ gsiDeclDbPath.cc \ gsiDeclDbPoint.cc \ gsiDeclDbPolygon.cc \ diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 5b562bee2..d329316f4 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -538,7 +538,7 @@ public: * @brief Default ctor */ box_scanner2 (bool report_progress = false, const std::string &progress_desc = std::string ()) - : m_fill_factor (2), m_scanner_thr (100), + : m_fill_factor (2), m_scanner_thr (100), m_scanner_thr1 (10), m_report_progress (report_progress), m_progress_desc (progress_desc) { // .. nothing yet .. @@ -564,6 +564,26 @@ public: return m_scanner_thr; } + /** + * @brief Sets the scanner threshold per class + * + * This value determines for how many elements in one class the implementation switches to the scanner + * implementation instead of the plain element-by-element interaction test. + * The default value is 10. + */ + void set_scanner_threshold1 (size_t n) + { + m_scanner_thr1 = n; + } + + /** + * @brief Gets the scanner threshold per class + */ + size_t scanner_threshold1 () const + { + return m_scanner_thr1; + } + /** * @brief Sets the fill factor * @@ -667,7 +687,7 @@ private: container_type1 m_pp1; container_type2 m_pp2; double m_fill_factor; - size_t m_scanner_thr; + size_t m_scanner_thr, m_scanner_thr1; bool m_report_progress; std::string m_progress_desc; @@ -732,7 +752,7 @@ private: rec.finish2 (i->first, i->second); } - } else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr) { + } else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr || m_pp2.size () <= m_scanner_thr1 || m_pp1.size () <= m_scanner_thr1) { // below m_scanner_thr elements use the brute force approach which is faster in that case diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 64efb2aaf..83acc1182 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -187,6 +187,17 @@ Cell::clear (unsigned int index) } } +void +Cell::clear (unsigned int index, unsigned int types) +{ + shapes_map::iterator s = m_shapes_map.find(index); + if (s != m_shapes_map.end() && ! s->second.empty ()) { + mp_layout->invalidate_bboxes (index); // HINT: must come before the change is done! + s->second.clear (types); + m_bbox_needs_update = true; + } +} + Cell::shapes_type & Cell::shapes (unsigned int index) { @@ -344,6 +355,20 @@ Cell::copy (unsigned int src, unsigned int dest) } } +void +Cell::copy (unsigned int src, unsigned int dest, unsigned int types) +{ + if (src != dest) { + shapes (dest).insert (shapes (src), types); + } else { + // When duplicating the layer, first create a copy to avoid problems with non-stable containers + // Hint: using the assignment and not the copy ctor does not copy the db::Manager association. + db::Shapes shape_copy; + shape_copy.insert (shapes (src), types); + shapes (dest).insert (shape_copy); + } +} + void Cell::move (unsigned int src, unsigned int dest) { @@ -353,6 +378,15 @@ Cell::move (unsigned int src, unsigned int dest) } } +void +Cell::move (unsigned int src, unsigned int dest, unsigned int types) +{ + if (src != dest) { + copy (src, dest, types); + clear (src, types); + } +} + void Cell::swap (unsigned int i1, unsigned int i2) { diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index 00319ee08..2d04bc0ba 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -182,6 +182,13 @@ public: */ void copy (unsigned int src, unsigned int dest); + /** + * @brief Copy the shapes from layer src to dest (only shapes from given classes) + * + * The target layer is not overwritten. Instead, the shapes are added to the target layer's shapes. + */ + void copy (unsigned int src, unsigned int dest, unsigned int types); + /** * @brief Move the shapes from layer src to dest * @@ -189,6 +196,13 @@ public: */ void move (unsigned int src, unsigned int dest); + /** + * @brief Move the shapes from layer src to dest (only shapes from given classes) + * + * The target layer is not overwritten. Instead, the shapes are added to the target layer's shapes. + */ + void move (unsigned int src, unsigned int dest, unsigned int types); + /** * @brief Swap the layers given */ @@ -199,7 +213,12 @@ public: */ void clear (unsigned int index); - /** + /** + * @brief Clear the shapes on the given layer (only the shapes from the given classes) + */ + void clear (unsigned int index, unsigned int types); + + /** * @brief Erase a cell instance given by a instance proxy * * Erasing a cell instance will destroy the sorting order and invalidate diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index c4255f762..de115e629 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -151,10 +151,6 @@ public: } - std::set callers_b; - m_layout_b.cell (cell_b).collect_caller_cells (callers_b, selection_cone_b, -1); - callers_b.insert (cell_b); - m_repr_set = false; std::map::const_iterator r = m_repr.find (cell_b); @@ -164,7 +160,11 @@ public: return false; } } - + + std::set callers_b; + m_layout_b.cell (cell_b).collect_caller_cells (callers_b, selection_cone_b, -1); + callers_b.insert (cell_b); + trans_set_t trans (m_trans); double mag = m_layout_b.dbu () / m_layout_a.dbu (); @@ -356,7 +356,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ()) && (! include_cells || include_cells->find (*b) != include_cells->end ())) { - db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b)); + db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); new_cells.push_back (new_cell); new_cells_b.push_back (*b); diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 741dded37..a71d9eb52 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -275,6 +275,7 @@ VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell var_name += "$VAR" + tl::to_string (index); ci_var = layout.add_cell (var_name.c_str ()); + layout.add_meta_info (ci_var, layout.begin_meta (*c), layout.end_meta (*c)); copy_shapes (layout, ci_var, *c); // a new entry for the variant diff --git a/src/db/db/dbClip.cc b/src/db/db/dbClip.cc index 72c926afc..c57283992 100644 --- a/src/db/db/dbClip.cc +++ b/src/db/db/dbClip.cc @@ -363,7 +363,7 @@ clip_cell (const db::Layout &layout, target_cell.shapes (l).insert (db::PathRef (path, target_layout.shape_repository ())); } - } else if (sh->is_polygon () || sh->is_simple_polygon () || sh->is_path ()) { + } else if (sh->is_simple_polygon () || sh->is_path ()) { db::SimplePolygon poly; @@ -371,17 +371,12 @@ clip_cell (const db::Layout &layout, db::Path path; sh->path (path); poly = path.simple_polygon (); - } else if (sh->is_polygon ()) { - db::Polygon ppoly; - sh->polygon (ppoly); - poly = db::SimplePolygon (ppoly); } else { sh->simple_polygon (poly); } - std::vector clipped_polygons; - if (! poly.box ().inside (clip_box)) { + std::vector clipped_polygons; clip_poly (poly, clip_box, clipped_polygons); for (std::vector ::const_iterator cp = clipped_polygons.begin (); cp != clipped_polygons.end (); ++cp) { if (sh->has_prop_id ()) { @@ -398,6 +393,29 @@ clip_cell (const db::Layout &layout, } } + } else if (sh->is_polygon ()) { + + db::Polygon poly; + sh->polygon (poly); + + if (! poly.box ().inside (clip_box)) { + std::vector clipped_polygons; + clip_poly (poly, clip_box, clipped_polygons); + for (std::vector ::const_iterator cp = clipped_polygons.begin (); cp != clipped_polygons.end (); ++cp) { + if (sh->has_prop_id ()) { + target_cell.shapes (l).insert (db::PolygonRefWithProperties (db::PolygonRef (*cp, target_layout.shape_repository ()), sh->prop_id ())); + } else { + target_cell.shapes (l).insert (db::PolygonRef (*cp, target_layout.shape_repository ())); + } + } + } else { + if (sh->has_prop_id ()) { + target_cell.shapes (l).insert (db::PolygonRefWithProperties (db::PolygonRef (poly, target_layout.shape_repository ()), sh->prop_id ())); + } else { + target_cell.shapes (l).insert (db::PolygonRef (poly, target_layout.shape_repository ())); + } + } + } else if (sh->is_text ()) { if (sh->bbox ().inside (clip_box)) { @@ -556,7 +574,7 @@ make_clip_variants (const db::Layout &layout, for (std::map , db::cell_index_type>::iterator v = variants.begin (); v != variants.end (); ++v) { if (v->first.second != layout.cell (v->first.first).bbox () || &layout != &target_layout) { // need for a new cell - v->second = target_layout.add_cell (layout.cell_name (v->first.first)); + v->second = target_layout.add_cell (layout, v->first.first); } else { v->second = v->first.first; } diff --git a/src/db/db/dbClipboardData.cc b/src/db/db/dbClipboardData.cc index 36c515333..c8ade46af 100644 --- a/src/db/db/dbClipboardData.cc +++ b/src/db/db/dbClipboardData.cc @@ -201,7 +201,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C cell_map.insert (std::make_pair (c->cell_index (), pc->cell_index ())); } else { // fallback: create a new cell - cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ())))); + db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ()); + cell_map.insert (std::make_pair (c->cell_index (), ci)); } } else { @@ -217,7 +218,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C cell_map.insert (std::make_pair (c->cell_index (), tc)); } } else { - cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ())))); + db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ()); + cell_map.insert (std::make_pair (c->cell_index (), ci)); } } @@ -313,7 +315,7 @@ ClipboardData::cell_for_cell (const db::Layout &layout, db::cell_index_type cell return cm->second; } - db::cell_index_type target_cell_index = m_layout.add_cell (layout.cell_name (cell_index)); + db::cell_index_type target_cell_index = m_layout.add_cell (layout, cell_index); m_cell_index_map.insert (std::make_pair (cell_index, target_cell_index)); if (incomplete) { diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc index 738d9dea9..8cc57aacd 100644 --- a/src/db/db/dbColdProxy.cc +++ b/src/db/db/dbColdProxy.cc @@ -49,8 +49,8 @@ ColdProxy::cold_proxies_per_lib_name (const std::string &libname) } } -ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info) - : Cell (ci, layout), mp_context_info (new ProxyContextInfo (info)) +ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info) + : Cell (ci, layout), mp_context_info (new LayoutOrCellContextInfo (info)) { if (! info.lib_name.empty ()) { tl::MutexLocker locker (&s_map_mutex); diff --git a/src/db/db/dbColdProxy.h b/src/db/db/dbColdProxy.h index 4c2681e29..5c642a9a1 100644 --- a/src/db/db/dbColdProxy.h +++ b/src/db/db/dbColdProxy.h @@ -35,7 +35,7 @@ namespace db { -struct ProxyContextInfo; +struct LayoutOrCellContextInfo; /** * @brief A cell specialization: a cold proxy representing a library or PCell which has gone out of scope @@ -53,7 +53,7 @@ public: * * Creates a cold proxy represented by the ProxyContextInfo data. */ - ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info); + ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info); /** * @brief The destructor @@ -68,7 +68,7 @@ public: /** * @brief Get the library id */ - const ProxyContextInfo &context_info () const + const LayoutOrCellContextInfo &context_info () const { return *mp_context_info; } @@ -102,7 +102,7 @@ public: virtual std::string get_qualified_name () const; private: - ProxyContextInfo *mp_context_info; + LayoutOrCellContextInfo *mp_context_info; ColdProxy (const ColdProxy &d); ColdProxy &operator= (const ColdProxy &d); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 0d7d725de..1ab9d322e 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -962,10 +962,31 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const // Implement XOR as (A-B)+(B-A) - only this implementation // is compatible with the local processor scheme - DeepLayer n1 (and_or_not_with (other_deep, false, property_constraint)); - DeepLayer n2 (other_deep->and_or_not_with (this, false, property_constraint)); + // Prepare a version of "other_deep" that is mapped into the hierarchy space + // of "this" + std::unique_ptr other_deep_mapped; + if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) { + // shallow copy for reconfiguration (progress etc.) + other_deep_mapped.reset (new DeepRegion (other_deep->deep_layer ())); + } else { + // deep copy with mapped hierarchy + other_deep_mapped.reset (new DeepRegion (deep_layer ().derived ())); + other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ()); + } + + other_deep_mapped->set_strict_handling (strict_handling ()); + other_deep_mapped->set_base_verbosity (base_verbosity ()); + if (report_progress ()) { + other_deep_mapped->enable_progress (progress_desc () + tl::to_string (tr (" - reverse part"))); + } else { + other_deep_mapped->disable_progress (); + } + + DeepLayer n1 (and_or_not_with (other_deep_mapped.get (), false, property_constraint)); + DeepLayer n2 (other_deep_mapped->and_or_not_with (this, false, property_constraint)); n1.add_from (n2); + return new DeepRegion (n1); } @@ -1895,6 +1916,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons // force different polygons in the different properties case to skip intra-polygon checks if (pc_always_different (options.prop_constraint)) { + // TODO: this forces merged primaries, so maybe that is not a good optimization? different_polygons = true; } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index bc9dc11d2..7fb293523 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -487,13 +487,13 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter } DeepShapeStore::DeepShapeStore () - : m_keep_layouts (true) + : m_keep_layouts (true), m_wants_all_cells (false) { ++s_instance_count; } DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu) - : m_keep_layouts (true) + : m_keep_layouts (true), m_wants_all_cells (false) { ++s_instance_count; @@ -765,6 +765,16 @@ double DeepShapeStore::max_area_ratio () const return m_state.max_area_ratio (); } +void DeepShapeStore::set_wants_all_cells (bool f) +{ + m_wants_all_cells = f; +} + +bool DeepShapeStore::wants_all_cells () const +{ + return m_wants_all_cells; +} + void DeepShapeStore::set_reject_odd_polygons (bool f) { m_state.set_reject_odd_polygons (f); @@ -929,6 +939,8 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator db::Layout &layout = m_layouts[layout_index]->layout; db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + builder.set_wants_all_cells (m_wants_all_cells); + unsigned int layer_index = init_layer (layout, si); builder.set_target_layer (layer_index); diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 1f12063b5..98c53c8fa 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -727,6 +727,23 @@ public: */ int threads () const; + /** + * @brief Sets a flag indicating whether the working layouts will store the whole original hierarchy + * + * Setting this flag to true will make the deep shape store copy the + * hierarchy exactly from the origin layouts. This will take somewhat + * more memory but avoid future cell hierarchy mapping operations. + * + * If set to false, only the needed parts of the hierarchy are copied. + * This part may need to grow when further operations are triggered. + */ + void set_wants_all_cells (bool f); + + /** + * @brief Gets a flag indicating whether the working layouts will store the whole original hierarchy + */ + bool wants_all_cells () const; + /** * @brief Sets a flag indicating whether to reject odd polygons * @@ -878,6 +895,7 @@ private: DeepShapeStoreState m_state; std::list m_state_stack; bool m_keep_layouts; + bool m_wants_all_cells; tl::Mutex m_lock; struct DeliveryMappingCacheKey diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index e4745add0..287ee022e 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -44,6 +44,8 @@ namespace db { +const double fill_factor = 1.5; + // ------------------------------------------------------------------------------- // Some utilities .. @@ -1095,14 +1097,14 @@ EdgeProcessor::clear () } static void -add_hparallel_cutpoints (WorkEdge &e1, WorkEdge &e2, std::vector &cutpoints) +add_hparallel_cutpoints (WorkEdge &e1, WorkEdge &e2, const db::Box &cell, std::vector &cutpoints) { db::Coord e1_xmin = std::min (e1.x1 (), e1.x2 ()); db::Coord e1_xmax = std::max (e1.x1 (), e1.x2 ()); - if (e2.x1 () > e1_xmin && e2.x1 () < e1_xmax) { + if (e2.x1 () > e1_xmin && e2.x1 () < e1_xmax && cell.contains (e2.p1 ())) { e1.make_cutpoints (cutpoints)->add (e2.p1 (), &cutpoints, false); } - if (e2.x2 () > e1_xmin && e2.x2 () < e1_xmax) { + if (e2.x2 () > e1_xmin && e2.x2 () < e1_xmax && cell.contains (e2.p2 ())) { e1.make_cutpoints (cutpoints)->add (e2.p2 (), &cutpoints, false); } } @@ -1125,14 +1127,28 @@ get_intersections_per_band_90 (std::vector &cutpoints, std::vector < std::vector ::iterator f = current; for (std::vector ::iterator c = current; c != future; ) { - while (f != future && edge_xmin (*f) <= x) { - ++f; - } + size_t n = 0; + db::Coord xx = x; - db::Coord xx = std::numeric_limits ::max (); - if (f != future) { - xx = edge_xmin (*f); - } + // fetch as many cells as to fill in roughly 50% more + // (this is an empirical performance improvement factor) + do { + + while (f != future && edge_xmin (*f) <= xx) { + ++f; + } + + if (f != future) { + xx = edge_xmin (*f); + } else { + xx = std::numeric_limits ::max (); + } + + if (n == 0) { + n = std::distance (c, f); + } + + } while (f != future && std::distance (c, f) < long (n * fill_factor)); #ifdef DEBUG_EDGE_PROCESSOR printf ("edges %d..%d:", x, xx); @@ -1165,15 +1181,15 @@ get_intersections_per_band_90 (std::vector &cutpoints, std::vector < // parallel horizontal edges: produce the end points of each other edge as cutpoints if (c1->p1 ().y () == c2->p1 ().y ()) { - add_hparallel_cutpoints (*c1, *c2, cutpoints); - add_hparallel_cutpoints (*c2, *c1, cutpoints); + add_hparallel_cutpoints (*c1, *c2, cell, cutpoints); + add_hparallel_cutpoints (*c2, *c1, cell, cutpoints); } } else if (c1->p1 () != c2->p1 () && c1->p2 () != c2->p1 () && c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) { std::pair cp = c1->intersect_point (*c2); - if (cp.first) { + if (cp.first && cell.contains (cp.second)) { // add a cut point to c1 and c2 (c2 only if necessary) c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true); @@ -1197,7 +1213,7 @@ get_intersections_per_band_90 (std::vector &cutpoints, std::vector < c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) { std::pair cp = c1->intersect_point (*c2); - if (cp.first) { + if (cp.first && cell.contains (cp.second)) { // add a cut point to c1 and c2 c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true); @@ -1336,10 +1352,9 @@ public: static void get_intersections_per_band_any (std::vector &cutpoints, std::vector ::iterator current, std::vector ::iterator future, db::Coord y, db::Coord yy, bool with_h) { - std::vector p1_weak; // holds weak interactions of edge endpoints with other edges - std::vector ip_weak; double dy = y - 0.5; double dyy = yy + 0.5; + std::vector > p1_weak; // holds weak interactions of edge endpoints with other edges std::sort (current, future, edge_xmin_at_yinterval_double_compare (dy, dyy)); @@ -1356,14 +1371,28 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector std::vector ::iterator f = current; for (std::vector ::iterator c = current; c != future; ) { - while (f != future && edge_xmin_at_yinterval_double (*f, dy, dyy) <= x) { - ++f; - } + size_t n = 0; + db::Coord xx = x; - db::Coord xx = std::numeric_limits ::max (); - if (f != future) { - xx = edge_xmin_at_yinterval_double (*f, dy, dyy); - } + // fetch as many cells as to fill in roughly 50% more + // (this is an empirical performance improvement factor) + do { + + while (f != future && edge_xmin_at_yinterval_double (*f, dy, dyy) <= xx) { + ++f; + } + + if (f != future) { + xx = edge_xmin_at_yinterval_double (*f, dy, dyy); + } else { + xx = std::numeric_limits ::max (); + } + + if (n == 0) { + n = std::distance (c, f); + } + + } while (f != future && std::distance (c, f) < long (n * fill_factor)); #ifdef DEBUG_EDGE_PROCESSOR printf ("edges %d..%d:", x, xx); @@ -1377,9 +1406,10 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector db::Box cell (x, y, xx, yy); - for (std::vector ::iterator c1 = c; c1 != f; ++c1) { + std::set weak_points; // holds points that need to go in all other edges + p1_weak.clear (); - p1_weak.clear (); + for (std::vector ::iterator c1 = c; c1 != f; ++c1) { bool c1p1_in_cell = cell.contains (c1->p1 ()); bool c1p2_in_cell = cell.contains (c1->p2 ()); @@ -1398,46 +1428,17 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector // parallel horizontal edges: produce the end points of each other edge as cutpoints if (c1->p1 ().y () == c2->p1 ().y ()) { - add_hparallel_cutpoints (*c1, *c2, cutpoints); - add_hparallel_cutpoints (*c2, *c1, cutpoints); + add_hparallel_cutpoints (*c1, *c2, cell, cutpoints); + add_hparallel_cutpoints (*c2, *c1, cell, cutpoints); } } else if (c1->p1 () != c2->p1 () && c1->p2 () != c2->p1 () && c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) { std::pair cp = safe_intersect_point (*c1, *c2); - if (cp.first) { - - bool on_edge1 = is_point_on_exact (*c1, cp.second); - - // add a cut point to c1 and c2 (points not on the edge give strong attractors) - c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, !on_edge1); - if (with_h) { - c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, false); - } - -#ifdef DEBUG_EDGE_PROCESSOR - if (on_edge1) { - printf ("weak intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ()); - } else { - printf ("intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ()); - } -#endif - - // The new cutpoint must be inserted into other edges as well. - ip_weak.clear (); - 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); - } - } - for (std::vector ::iterator icc = ip_weak.begin (); icc != ip_weak.end (); ++icc) { - (*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 ()); -#endif - } - + if (cp.first && cell.contains (cp.second)) { + // Stash the cutpoint as it must be inserted into other edges as well. + weak_points.insert (cp.second); } } @@ -1478,41 +1479,9 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) { std::pair cp = safe_intersect_point (*c1, *c2); - if (cp.first) { - - bool on_edge1 = true; - bool on_edge2 = is_point_on_exact (*c2, cp.second); - - // add a cut point to c1 and c2 - if (with_h || c1->dy () != 0) { - on_edge1 = is_point_on_exact (*c1, cp.second); - c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, !on_edge1); - } - - c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, !on_edge2); - -#ifdef DEBUG_EDGE_PROCESSOR - if (!on_edge1 || !on_edge2) { - printf ("intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ()); - } else { - printf ("weak intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ()); - } -#endif - - // The new cutpoint must be inserted into other edges as well. - ip_weak.clear (); - 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); - } - } - for (std::vector ::iterator icc = ip_weak.begin (); icc != ip_weak.end (); ++icc) { - (*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 ()); -#endif - } - + if (cp.first && cell.contains (cp.second)) { + // Stash the cutpoint as it must be inserted into other edges as well. + weak_points.insert (cp.second); } } @@ -1532,7 +1501,7 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector #endif c2->make_cutpoints (cutpoints)->add (c1->p1 (), &cutpoints, true); } else { - p1_weak.push_back (&*c2); + p1_weak.push_back (std::make_pair (c1.operator-> (), c2.operator-> ())); } } @@ -1540,39 +1509,71 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector } - if (! p1_weak.empty ()) { + } - bool strong = false; - for (std::vector::const_iterator cp = p1_weak.begin (); cp != p1_weak.end () && ! strong; ++cp) { - if ((*cp)->data > 0 && cutpoints [(*cp)->data - 1].strong_cutpoints) { - strong = true; - } - } + // insert weak intersection points into all relevant edges - weak into edges + // where the point is on and strong into edges where the point is on in a fuzzy way. - p1_weak.back ()->make_cutpoints (cutpoints); - size_t n = p1_weak.back ()->data - 1; - for (std::vector::const_iterator cp = p1_weak.begin (); cp != p1_weak.end (); ++cp) { + for (auto wp = weak_points.begin (); wp != weak_points.end (); ++wp) { - (*cp)->make_cutpoints (cutpoints); - size_t nn = (*cp)->data - 1; - if (strong) { - cutpoints [nn].add (c1->p1 (), &cutpoints); + for (std::vector ::iterator cc = c; cc != f; ++cc) { + if ((with_h || cc->dy () != 0) && is_point_on_fuzzy (*cc, *wp)) { + bool on_edge = is_point_on_exact (*cc, *wp); + cc->make_cutpoints (cutpoints)->add (*wp, &cutpoints, !on_edge); #ifdef DEBUG_EDGE_PROCESSOR - printf ("Insert strong attractor %s in %s.\n", c1->p1 ().to_string ().c_str (), (*cp)->to_string ().c_str ()); -#endif + if (!on_edge) { + printf ("intersection point %s gives strong cutpoint in %s.\n", wp->to_string ().c_str (), cc->to_string ().c_str ()); } else { - cutpoints [nn].add_attractor (c1->p1 (), n); -#ifdef DEBUG_EDGE_PROCESSOR - printf ("Insert weak attractor %s in %s.\n", c1->p1 ().to_string ().c_str (), (*cp)->to_string ().c_str ()); -#endif + printf ("intersection point %s gives weak cutpoint in %s.\n", wp->to_string ().c_str (), cc->to_string ().c_str ()); } - - n = nn; - +#endif } + } + + } + + // go through the list of "p1 to other edges" and insert p1 either as cutpoint + // (if there are other strong cutpoints already) or weak attractor. + + auto p1w_from = p1_weak.begin (); + while (p1w_from != p1_weak.end ()) { + + bool strong = false; + auto p1w_to = p1w_from; + while (p1w_to != p1_weak.end () && p1w_to->first == p1w_from->first) { + if (p1w_to->second->data > 0 && cutpoints [p1w_to->second->data - 1].strong_cutpoints) { + strong = true; + } + ++p1w_to; + } + + db::Point p1 = p1w_from->first->p1 (); + + p1w_to [-1].second->make_cutpoints (cutpoints); + size_t n = p1w_to [-1].second->data - 1; + + for (auto cp = p1w_from; cp != p1w_to; ++cp) { + + cp->second->make_cutpoints (cutpoints); + size_t nn = cp->second->data - 1; + if (strong) { + cutpoints [nn].add (p1, &cutpoints); +#ifdef DEBUG_EDGE_PROCESSOR + printf ("Insert strong attractor %s in %s.\n", cp->first->p1 ().to_string ().c_str (), cp->second->to_string ().c_str ()); +#endif + } else { + cutpoints [nn].add_attractor (p1, n); +#ifdef DEBUG_EDGE_PROCESSOR + printf ("Insert weak attractor %s in %s.\n", cp->first->p1 ().to_string ().c_str (), cp->second->to_string ().c_str ()); +#endif + } + + n = nn; } + p1w_from = p1w_to; + } } @@ -2259,7 +2260,7 @@ EdgeProcessor::redo_or_process (const std::vectorset (size_t (double (todo_next - todo) * p) + todo); } - size_t n = std::distance (current, future); + size_t n = 0; db::Coord yy = y; // Use as many scanlines as to fetch approx. 50% new edges into the scanline (this @@ -2276,7 +2277,11 @@ EdgeProcessor::redo_or_process (const std::vector::max (); } - } while (future != mp_work_edges->end () && std::distance (current, future) < long (n + n / 2)); + if (n == 0) { + n = std::distance (current, future); + } + + } while (future != mp_work_edges->end () && std::distance (current, future) < long (n * fill_factor)); bool is90 = true; diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index e8b006992..09b9e17c2 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -384,6 +384,8 @@ struct DetectTagEdgeSink DetectTagEdgeSink (int tag) : fail_tag (tag), result (true) { } + virtual void put (const db::Edge &) { } + virtual void put (const db::Edge &, int tag) { if (tag == fail_tag) { diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index e512258b6..db411328b 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -313,6 +313,7 @@ RegionDelegate *FlatRegion::add (const Region &other) const if (other_flat) { new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); } else { @@ -343,6 +344,7 @@ RegionDelegate *FlatRegion::add_in_place (const Region &other) if (other_flat) { polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); } else { diff --git a/src/db/db/dbHershey.cc b/src/db/db/dbHershey.cc index 62b311d37..7e862d4a2 100644 --- a/src/db/db/dbHershey.cc +++ b/src/db/db/dbHershey.cc @@ -101,17 +101,22 @@ hershey_count_edges (const std::string &s, unsigned int f) HersheyFont *fp = fonts [f]; size_t n = 0; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; + if (tl::skip_newline (cp)) { + + // skip new line - they don't contribute edges + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start; } - } else if (c < fp->end_char && c >= fp->start_char) { - n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start; + } } @@ -142,22 +147,27 @@ hershey_text_box (const std::string &s, unsigned int f) int w = 0; int h = fp->ymax; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { + + if (tl::skip_newline (cp)) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; - } if (w > wl) { wl = w; } + hl += line_spacing + h - fp->ymin; w = 0; - } else if (c < fp->end_char && c >= fp->start_char) { - w += fp->chars [c - fp->start_char].width; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - w += fp->chars [invalid_char - fp->start_char].width; + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + w += fp->chars [c - fp->start_char].width; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + w += fp->chars [invalid_char - fp->start_char].width; + } + } } @@ -167,32 +177,39 @@ hershey_text_box (const std::string &s, unsigned int f) } hl += h; - return db::DBox (0, 0, wl, hl); + return db::DBox (0, fp->ymin, wl, hl); } -void -hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts) +void +hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts, double &left, double &bottom) { + left = 0.0; + bottom = 0.0; + HersheyFont *fp = fonts [f]; int hl = 0; int w = 0; int h = fp->ymax; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { + + if (tl::skip_newline (cp)) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; - } linestarts.push_back (db::DPoint (w, -hl)); hl += line_spacing + h - fp->ymin; w = 0; - } else if (c < fp->end_char && c >= fp->start_char) { - w += fp->chars [c - fp->start_char].width; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - w += fp->chars [invalid_char - fp->start_char].width; + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + w += fp->chars [c - fp->start_char].width; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + w += fp->chars [invalid_char - fp->start_char].width; + } + } } @@ -220,21 +237,26 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig p += db::DVector (0, l->y ()); } *l = p; + if (l == linestarts.begin ()) { + left = l->x (); + bottom = l->y (); + } else { + left = std::min (left, l->x ()); + bottom = std::min (bottom, l->y ()); + } } - } // ---------------------------------------------------------------------------- // basic_hershey_edge_iterator implementation basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, unsigned int f, const std::vector &line_starts) - : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_index (0), m_linestarts (line_starts) + : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_linestarts (line_starts) { m_fp = fonts [f]; - m_end = (unsigned int) m_string.size (); - m_new_char = true; + mp_cp = m_string.c_str (); - if (m_linestarts.size () == 0) { + if (m_linestarts.empty ()) { m_linestarts.push_back (db::DPoint (0.0, 0.0)); } m_pos = m_linestarts [0]; @@ -243,66 +265,47 @@ basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, bool basic_hershey_edge_iterator::at_end () const { - return m_index >= m_end; + return *mp_cp == 0 && m_edge == m_edge_end; } db::DEdge basic_hershey_edge_iterator::get () { - while (m_new_char && !at_end ()) { - - unsigned char c = (unsigned char) (m_index < m_end ? m_string [m_index] : ' '); + while (m_edge == m_edge_end && *mp_cp) { m_pos += m_delta; - if (c < m_fp->end_char && c >= m_fp->start_char) { + m_edge = m_edge_end = 0; + m_delta = db::DVector (); - m_edge = m_fp->chars [c - m_fp->start_char].edge_start; - m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end; - m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0); + if (tl::skip_newline (mp_cp)) { - } else if (c != '\012' && c != '\015' - && invalid_char < m_fp->end_char - && invalid_char >= m_fp->start_char) { + ++m_line; - m_edge = m_fp->chars [invalid_char - m_fp->start_char].edge_start; - m_edge_end = m_fp->chars [invalid_char - m_fp->start_char].edge_end; - m_delta = db::DVector (m_fp->chars [invalid_char - m_fp->start_char].width, 0); + if (m_line >= m_linestarts.size ()) { + db::DPoint last; + last = m_linestarts.back (); + last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing)); + m_linestarts.push_back (last); + } + m_pos = m_linestarts [m_line]; } else { - m_edge = m_edge_end = 0; - m_delta = db::DVector (); + uint32_t c = tl::utf32_from_utf8 (mp_cp); - } - - if (m_edge == m_edge_end) { - - if (c == '\012' || c == '\015') { - if (c == '\015' && m_string.size () > m_index + 1 && m_string [m_index] == '\012') { - ++m_index; - } - ++m_line; - if (m_line >= m_linestarts.size ()) { - m_linestarts.push_back (m_linestarts.back () + db::DVector (0.0, -(m_fp->ymax - m_fp->ymin + line_spacing))); - } - m_pos = m_linestarts [m_line]; + if (c < m_fp->start_char || c >= m_fp->end_char) { + c = invalid_char; } - ++m_index; + if (c < m_fp->end_char && c >= m_fp->start_char) { + m_edge = m_fp->chars [c - m_fp->start_char].edge_start; + m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end; + m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0); + } - } else { - m_new_char = false; } - } - while (m_line >= m_linestarts.size ()) { - db::DPoint last; - if (m_linestarts.size () > 0) { - last = m_linestarts.back (); - last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing)); - } - m_linestarts.push_back (last); } if (!at_end ()) { @@ -316,16 +319,9 @@ basic_hershey_edge_iterator::get () void basic_hershey_edge_iterator::inc () { - if (m_new_char) { - get (); - } - if (! at_end ()) { ++m_edge; - if (m_edge == m_edge_end) { - ++m_index; - m_new_char = true; - } + get (); } } diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index 1e6b4990e..f747bb186 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -38,7 +38,7 @@ struct HersheyFont; DB_PUBLIC int hershey_font_width (unsigned int f); DB_PUBLIC int hershey_font_height (unsigned int f); DB_PUBLIC db::DBox hershey_text_box (const std::string &s, unsigned int f); -DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts); +DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts, double &left, double &bottom); DB_PUBLIC std::vector hershey_font_names (); DB_PUBLIC size_t hershey_count_edges (const std::string &s, unsigned int f); @@ -51,11 +51,10 @@ public: void inc (); private: - bool m_new_char; unsigned int m_line; + const char *mp_cp; std::string m_string; unsigned int m_edge, m_edge_end; - unsigned int m_index, m_end; std::vector m_linestarts; db::DPoint m_pos; db::DVector m_delta; @@ -138,7 +137,8 @@ struct DB_PUBLIC_TEMPLATE hershey hershey () : m_string (), m_font (DefaultFont), - m_scale (1.0) + m_scale (1.0), + m_left (0.0), m_bottom (0.0) { // .. nothing yet .. } @@ -151,7 +151,8 @@ struct DB_PUBLIC_TEMPLATE hershey hershey (const std::string &s, Font f) : m_string (s), m_font (f), - m_scale (1.0) + m_scale (1.0), + m_left (0.0), m_bottom (0.0) { // .. nothing yet .. } @@ -177,14 +178,13 @@ struct DB_PUBLIC_TEMPLATE hershey /** * @brief Obtain the size of the text * - * @return The bounding box of the text with the scaling applied + * @return The bounding box of the text with the scaling and justification applied */ - box bbox () const + db::DBox bbox () const { db::DBox b = hershey_text_box (m_string, m_font); - db::point p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale)); - db::point p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale)); - return box (p1, p2); + b.move (db::DVector (m_left, m_bottom)); + return b * m_scale; } /** @@ -220,11 +220,11 @@ struct DB_PUBLIC_TEMPLATE hershey db::DPoint p1 (b.p1 ().x () / m_scale, b.p1 ().y () / m_scale); db::DPoint p2 (b.p2 ().x () / m_scale, b.p2 ().y () / m_scale); - hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts); + hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts, m_left, m_bottom); } else { - if (b.width () > 0 && b.height () > 0) { + if (coord_traits::less (0, b.width ()) && coord_traits::less (0, b.height ())) { db::DBox tbx (hershey_text_box (m_string, m_font)); double fx = double (b.width ()) / double (tbx.width ()); @@ -232,11 +232,11 @@ struct DB_PUBLIC_TEMPLATE hershey double f = std::min (fx, fy); m_scale = f * (1.0 - 2.0 * margin); - } else if (b.width () > 0) { + } else if (coord_traits::less (0, b.width ())) { m_scale = double (b.width ()) / double (hershey_font_width (m_font)); - } else if (b.height () > 0) { + } else if (coord_traits::less (0, b.height ())) { m_scale = double (b.height ()) / double (hershey_font_height (m_font)); @@ -245,7 +245,7 @@ struct DB_PUBLIC_TEMPLATE hershey if (m_scale > 1e-6) { db::DPoint p1 (b.p1 ().x () / m_scale, b.p1 ().y () / m_scale); db::DPoint p2 (b.p2 ().x () / m_scale, b.p2 ().y () / m_scale); - hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts); + hershey_justify (m_string, m_font, db::DBox (p1, p2), halign, valign, m_linestarts, m_left, m_bottom); } } @@ -294,6 +294,7 @@ private: Font m_font; double m_scale; std::vector m_linestarts; + double m_left, m_bottom; }; /** diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index e69057b84..9987451e2 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -64,6 +64,11 @@ cronology::events::event_collection +static void dump_cell_contexts (local_processor_contexts &contexts, const db::Layout *subject_layout, const db::Layout *intruder_layout) +{ + for (auto cc = contexts.begin (); cc != contexts.end (); ++cc) { + tl::info << "Cell " << subject_layout->cell_name (cc->first->cell_index ()) << ":"; + int i = 0; + for (auto c = cc->second.begin (); c != cc->second.end (); ++c) { + tl::info << " Context #" << ++i; + tl::info << " Instances:"; + for (auto i = c->first.first.begin (); i != c->first.first.end (); ++i) { + const db::CellInstArray &ci = *i; + tl::info << " " << intruder_layout->cell_name (ci.object ().cell_index ()) << " @ " << ci.complex_trans (*ci.begin ()).to_string () << " (" << ci.size () << ")"; + } + tl::info << " Shapes:"; + for (auto i = c->first.second.begin (); i != c->first.second.end (); ++i) { + for (auto s = i->second.begin (); s != i->second.end (); ++s) { + tl::info << " " << intruder_layout->get_properties (i->first).to_string () << ": " << s->to_string (); + } + } + } + } +} + // --------------------------------------------------------------------------------------------- // LocalProcessorCellContext implementation @@ -1208,7 +1239,8 @@ private: void collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2) { - // TODO: this algorithm is not in particular effective for identical arrays + // TODO: this algorithm is not in particular effective for identical arrays or for arrays + // vs. single instances const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ()); const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ()); @@ -1247,7 +1279,7 @@ private: db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist)); db::Box cbox = ibox1 & ibox2; - if (! cbox.empty () && cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1))) { + if (! cbox.empty () && (cbox == ibox1 || cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1)))) { db::ICplxTrans tn21 = tni1 * tn2; @@ -1282,9 +1314,11 @@ private: db::ICplxTrans tni2 = tn21.inverted () * tni1; db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1); - if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) { + // do not recurse further if we're overlapping with shapes from the intruder + // or the intruder cell is not much bigger than the region of interest (cbox) + if (intruder_cell.bbox (m_intruder_layer).area () < area_ratio_for_recursion * cbox.area () + || ! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) { - // we're overlapping with shapes from the intruder - do not recursive further interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21)); return; @@ -1782,6 +1816,11 @@ template void local_processor::compute_results (local_processor_contexts &contexts, const local_operation *op, const std::vector &output_layers) const { +#if 0 + // debugging + dump_cell_contexts (contexts, mp_subject_layout, mp_intruder_layout ? mp_intruder_layout : mp_subject_layout); +#endif + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing results for ")) + description (op)); // avoids updates while we work on the layout diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index c8a77cbb6..d8698a83e 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -152,13 +152,13 @@ static std::pair > compute_clip_variant (const db::Box & } HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_trans (trans) + : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); } HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_trans (trans) + : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 4cd47efce..b3f2104ef 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -295,6 +295,7 @@ public: */ void set_shape_receiver (HierarchyBuilderShapeReceiver *pipe); + virtual bool wants_all_cells () const { return m_wants_all_cells; } virtual void begin (const RecursiveShapeIterator *iter); virtual void end (const RecursiveShapeIterator *iter); virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); @@ -311,6 +312,14 @@ public: m_target_layer = target_layer; } + /** + * @brief Sets the target layer - shapes will be put there + */ + void set_wants_all_cells (bool f) + { + m_wants_all_cells = f; + } + /** * @brief Reset the builder - performs a new initial pass */ @@ -416,6 +425,7 @@ private: cell_map_type::const_iterator m_cm_entry; bool m_cm_new_entry; unsigned int m_target_layer; + bool m_wants_all_cells; std::vector > > m_cell_stack; db::Cell *mp_initial_cell; diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index edeeb0800..32bba3042 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -258,12 +258,12 @@ private: // ----------------------------------------------------------------- // Implementation of the ProxyContextInfo class -ProxyContextInfo -ProxyContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) +LayoutOrCellContextInfo +LayoutOrCellContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) { - ProxyContextInfo info; + LayoutOrCellContextInfo info; - for (std::vector::const_iterator i = from; i != to; ++i) { + for (auto i = from; i != to; ++i) { tl::Extractor ex (i->c_str ()); @@ -290,6 +290,20 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st info.cell_name = ex.skip (); + } else if (ex.test ("META(")) { + + std::pair > vv; + + ex.read_word_or_quoted (vv.first); + if (ex.test (",")) { + ex.read_word_or_quoted (vv.second.second); + } + ex.test (")"); + ex.test ("="); + ex.read (vv.second.first); + + info.meta_info.insert(vv); + } } @@ -298,12 +312,12 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st } void -ProxyContextInfo::serialize (std::vector &strings) +LayoutOrCellContextInfo::serialize (std::vector &strings) { if (! lib_name.empty ()) { strings.push_back ("LIB=" + lib_name); } - for (std::map ::const_iterator p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) { + for (auto p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) { strings.push_back ("P(" + tl::to_word_or_quoted_string (p->first) + ")=" + p->second.to_parsable_string ()); } if (! pcell_name.empty ()) { @@ -312,6 +326,32 @@ ProxyContextInfo::serialize (std::vector &strings) if (! cell_name.empty ()) { strings.push_back ("CELL=" + cell_name); } + + std::string mv; + for (auto m = meta_info.begin (); m != meta_info.end (); ++m) { + mv.clear (); + mv += "META("; + mv += tl::to_word_or_quoted_string (m->first); + if (! m->second.second.empty ()) { + mv += ","; + mv += tl::to_word_or_quoted_string (m->second.second); + } + mv += ")="; + mv += m->second.first.to_parsable_string (); + strings.push_back (mv); + } +} + +bool +LayoutOrCellContextInfo::has_proxy_info () const +{ + return !pcell_name.empty () || !lib_name.empty (); +} + +bool +LayoutOrCellContextInfo::has_meta_info () const +{ + return !meta_info.empty (); } // ----------------------------------------------------------------- @@ -481,7 +521,13 @@ Layout::operator= (const Layout &d) } m_dbu = d.m_dbu; + m_meta_info = d.m_meta_info; + m_meta_info_by_cell = d.m_meta_info_by_cell; + m_meta_info_names = d.m_meta_info_names; + m_meta_info_name_map = d.m_meta_info_name_map; + + m_tech_name = d.m_tech_name; } return *this; @@ -593,7 +639,7 @@ Layout::set_technology_name (const std::string &tech) if (! pn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -606,7 +652,7 @@ Layout::set_technology_name (const std::string &tech) if (! old_pcell_decl || ! new_pcell_decl) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -633,7 +679,7 @@ Layout::set_technology_name (const std::string &tech) if (! cn.first) { // unlink this proxy: substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -650,7 +696,7 @@ Layout::set_technology_name (const std::string &tech) db::cell_index_type ci = (*lp)->Cell::cell_index (); // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -1204,6 +1250,11 @@ Layout::take_cell (cell_index_type ci) m_cell_ptrs [ci] = 0; + auto mi = m_meta_info_by_cell.find (ci); + if (mi != m_meta_info_by_cell.end ()) { + m_meta_info_by_cell.erase (mi); + } + // Using free cell indices does have one significant drawback: // The cellview references cannot be uniquely classified as being invalid - because the // ID might be reused. This causes problems, when a cell is being deleted and subsequently a @@ -1252,7 +1303,24 @@ Layout::uniquify_cell_name (const char *name) const } } -cell_index_type +cell_index_type +Layout::add_cell (const db::Layout &other, db::cell_index_type ci) +{ + cell_index_type ci_new = add_cell (other.cell_name (ci)); + cell (ci_new).set_ghost_cell (other.cell (ci).is_ghost_cell ()); + + if (&other == this) { + add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci)); + } else { + for (auto m = other.begin_meta (ci); m != other.end_meta (ci); ++m) { + add_meta_info (ci_new, meta_info_name_id (other.meta_info_name (m->first)), m->second); + } + } + + return ci_new; +} + +cell_index_type Layout::add_cell (const char *name) { std::string b; @@ -1748,6 +1816,58 @@ Layout::do_update () delete pr; } +static Layout::meta_info_map s_empty_meta; + +Layout::meta_info_iterator +Layout::begin_meta (db::cell_index_type ci) const +{ + auto m = m_meta_info_by_cell.find (ci); + if (m != m_meta_info_by_cell.end ()) { + return m->second.begin (); + } else { + return s_empty_meta.begin (); + } +} + +Layout::meta_info_iterator +Layout::end_meta (db::cell_index_type ci) const +{ + auto m = m_meta_info_by_cell.find (ci); + if (m != m_meta_info_by_cell.end ()) { + return m->second.end (); + } else { + return s_empty_meta.end (); + } +} + +const std::string & +Layout::meta_info_name (Layout::meta_info_name_id_type name_id) const +{ + static std::string empty; + return name_id < m_meta_info_names.size () ? m_meta_info_names[name_id] : empty; +} + +Layout::meta_info_name_id_type +Layout::meta_info_name_id (const std::string &name) +{ + auto n = m_meta_info_name_map.find (name); + if (n != m_meta_info_name_map.end ()) { + return n->second; + } else { + size_t id = m_meta_info_names.size (); + m_meta_info_names.push_back (name); + m_meta_info_name_map.insert (std::make_pair (name, id)); + return id; + } +} + +Layout::meta_info_name_id_type +Layout::meta_info_name_id (const std::string &name) const +{ + auto n = m_meta_info_name_map.find (name); + return n != m_meta_info_name_map.end () ? n->second : std::numeric_limits::max (); +} + void Layout::clear_meta () { @@ -1755,42 +1875,79 @@ Layout::clear_meta () } void -Layout::add_meta_info (const MetaInfo &i) +Layout::add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i) { - for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) { - if (m->name == i.name) { - *m = i; - return; - } - } - m_meta_info.push_back (i); + m_meta_info[name_id] = i; } void -Layout::remove_meta_info (const std::string &name) +Layout::remove_meta_info (meta_info_name_id_type name_id) { - for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) { - if (m->name == name) { - m_meta_info.erase (m); - return; - } + m_meta_info.erase (name_id); +} + +const MetaInfo & +Layout::meta_info (meta_info_name_id_type name_id) const +{ + auto n = m_meta_info.find (name_id); + static MetaInfo null_value; + return n != m_meta_info.end () ? n->second : null_value; +} + +bool +Layout::has_meta_info (meta_info_name_id_type name_id) const +{ + return m_meta_info.find (name_id) != m_meta_info.end (); +} + +void +Layout::clear_meta (db::cell_index_type ci) +{ + m_meta_info_by_cell.erase (ci); +} + +void +Layout::add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i) +{ + m_meta_info_by_cell[ci][name_id] = i; +} + +void +Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) +{ + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + c->second.erase (name_id); } } -const std::string & -Layout::meta_info_value (const std::string &name) const +const MetaInfo & +Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const { - for (meta_info::const_iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) { - if (m->name == name) { - return m->value; + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + auto i = c->second.find (name_id); + if (i != c->second.end ()) { + return i->second; } } - static const std::string s_empty; - return s_empty; + static MetaInfo null_value; + return null_value; } -void +bool +Layout::has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const +{ + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + return c->second.find (name_id) != c->second.end (); + } else { + return false; + } +} + +void Layout::swap_layers (unsigned int a, unsigned int b) { tl_assert (m_layers.layer_state (a) != LayoutLayers::Free); @@ -1814,7 +1971,19 @@ Layout::move_layer (unsigned int src, unsigned int dest) } } -void +void +Layout::move_layer (unsigned int src, unsigned int dest, unsigned int flags) +{ + tl_assert (m_layers.layer_state (src) != LayoutLayers::Free); + tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free); + + // move the shapes + for (iterator c = begin (); c != end (); ++c) { + c->move (src, dest, flags); + } +} + +void Layout::copy_layer (unsigned int src, unsigned int dest) { tl_assert (m_layers.layer_state (src) != LayoutLayers::Free); @@ -1826,7 +1995,19 @@ Layout::copy_layer (unsigned int src, unsigned int dest) } } -void +void +Layout::copy_layer (unsigned int src, unsigned int dest, unsigned int flags) +{ + tl_assert (m_layers.layer_state (src) != LayoutLayers::Free); + tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free); + + // copy the shapes + for (iterator c = begin (); c != end (); ++c) { + c->copy (src, dest, flags); + } +} + +void Layout::clear_layer (unsigned int n) { tl_assert (m_layers.layer_state (n) != LayoutLayers::Free); @@ -1837,7 +2018,18 @@ Layout::clear_layer (unsigned int n) } } -void +void +Layout::clear_layer (unsigned int n, unsigned int flags) +{ + tl_assert (m_layers.layer_state (n) != LayoutLayers::Free); + + // clear the shapes + for (iterator c = begin (); c != end (); ++c) { + c->clear (n, flags); + } +} + +void Layout::delete_layer (unsigned int n) { tl_assert (m_layers.layer_state (n) != LayoutLayers::Free); @@ -2369,10 +2561,85 @@ Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vectorsecond.persisted) { + return true; + } + } + + return false; +} + +bool +Layout::has_context_info (cell_index_type cell_index) const +{ + auto c = m_meta_info_by_cell.find (cell_index); + if (c != m_meta_info_by_cell.end ()) { + for (auto i = c->second.begin (); i != c->second.end (); ++i) { + if (i->second.persisted) { + return true; + } + } + } + + const db::Cell &cref = cell (cell_index); + if (cref.is_proxy () && ! cref.is_top ()) { + return true; + } else { + return false; + } +} + +bool +Layout::get_context_info (std::vector &strings) const +{ + LayoutOrCellContextInfo info; + if (! get_context_info (info)) { + return false; + } else { + info.serialize (strings); + return true; + } +} + +bool +Layout::get_context_info (LayoutOrCellContextInfo &info) const +{ + for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) { + if (i->second.persisted) { + std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; + mi.first = i->second.value; + mi.second = i->second.description; + } + } + + return true; +} + +void +Layout::fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to) +{ + fill_meta_info_from_context (LayoutOrCellContextInfo::deserialize (from, to)); +} + +void +Layout::fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info) +{ + if (! context_info.meta_info.empty ()) { + for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) { + meta_info_name_id_type name_id = meta_info_name_id (i->first); + m_meta_info [name_id] = MetaInfo (i->second.second, i->second.first, true); + } + } +} + bool Layout::get_context_info (cell_index_type cell_index, std::vector &strings) const { - ProxyContextInfo info; + LayoutOrCellContextInfo info; if (! get_context_info (cell_index, info)) { return false; } else { @@ -2382,8 +2649,22 @@ Layout::get_context_info (cell_index_type cell_index, std::vector } bool -Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) const +Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &info) const { + bool any_meta = false; + + auto cmi = m_meta_info_by_cell.find (cell_index); + if (cmi != m_meta_info_by_cell.end ()) { + for (auto i = cmi->second.begin (); i != cmi->second.end (); ++i) { + if (i->second.persisted) { + std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; + mi.first = i->second.value; + mi.second = i->second.description; + any_meta = true; + } + } + } + const db::Cell *cptr = &cell (cell_index); const db::ColdProxy *cold_proxy = dynamic_cast (cptr); @@ -2399,7 +2680,7 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); if (! lib) { - return false; // abort + return any_meta; // abort } else { // one level of library indirection @@ -2432,6 +2713,27 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co return true; } +void +Layout::fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to) +{ + fill_meta_info_from_context (cell_index, LayoutOrCellContextInfo::deserialize (from, to)); +} + +void +Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info) +{ + if (! context_info.meta_info.empty ()) { + + meta_info_map &mi = m_meta_info_by_cell [cell_index]; + + for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) { + meta_info_name_id_type name_id = meta_info_name_id (i->first); + mi [name_id] = MetaInfo (i->second.second, i->second.first, true); + } + + } +} + void Layout::restore_proxies (ImportLayerMapping *layer_mapping) { @@ -2463,11 +2765,11 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector : return false; } - return recover_proxy_as (cell_index, ProxyContextInfo::deserialize (from, to), layer_mapping); + return recover_proxy_as (cell_index, LayoutOrCellContextInfo::deserialize (from, to), layer_mapping); } bool -Layout::recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &info, ImportLayerMapping *layer_mapping) +Layout::recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &info, ImportLayerMapping *layer_mapping) { if (! info.lib_name.empty ()) { @@ -2517,11 +2819,11 @@ Layout::recover_proxy (std::vector ::const_iterator from, std::vect return 0; } - return recover_proxy (ProxyContextInfo::deserialize (from, to)); + return recover_proxy (LayoutOrCellContextInfo::deserialize (from, to)); } db::Cell * -Layout::recover_proxy (const ProxyContextInfo &info) +Layout::recover_proxy (const LayoutOrCellContextInfo &info) { if (! info.lib_name.empty ()) { @@ -2549,7 +2851,7 @@ Layout::recover_proxy (const ProxyContextInfo &info) } db::Cell * -Layout::recover_proxy_no_lib (const ProxyContextInfo &info) +Layout::recover_proxy_no_lib (const LayoutOrCellContextInfo &info) { if (! info.pcell_name.empty ()) { @@ -2646,7 +2948,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index) } cell_index_type -Layout::create_cold_proxy (const db::ProxyContextInfo &info) +Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info) { // create a new unique name std::string b; @@ -2677,7 +2979,7 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info) } void -Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index) +Layout::create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type target_cell_index) { tl_assert (m_cell_ptrs [target_cell_index] != 0); diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 87f76b965..353ad2ad9 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -417,15 +417,19 @@ public: /** * @brief A binary object representing context information for regenerating library proxies and PCells */ -struct DB_PUBLIC ProxyContextInfo +struct DB_PUBLIC LayoutOrCellContextInfo { std::string lib_name; std::string cell_name; std::string pcell_name; std::map pcell_parameters; + std::map > meta_info; - static ProxyContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); + static LayoutOrCellContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); void serialize (std::vector &strings); + + bool has_proxy_info () const; + bool has_meta_info () const; }; /** @@ -465,8 +469,9 @@ public: typedef pcell_name_map::const_iterator pcell_iterator; typedef std::map, cell_index_type> lib_proxy_map; typedef LayerIterator layer_iterator; - typedef std::vector meta_info; - typedef meta_info::const_iterator meta_info_iterator; + typedef size_t meta_info_name_id_type; + typedef std::map meta_info_map; + typedef meta_info_map::const_iterator meta_info_iterator; /** * @brief A helper functor to compare "const char *" by the content @@ -756,6 +761,14 @@ public: */ cell_index_type add_cell (const char *name = 0); + /** + * @brief Adds a cell using another cell as a template + * + * This method will use the name of the other cell and initialize the + * new cell with the meta info from the other cell. + */ + cell_index_type add_cell (const db::Layout &other, db::cell_index_type ci); + /** * @brief Add a cell without a name * @@ -1014,27 +1027,69 @@ public: /** * @brief Creates a cold proxy representing the given context information */ - cell_index_type create_cold_proxy (const db::ProxyContextInfo &info); + cell_index_type create_cold_proxy (const db::LayoutOrCellContextInfo &info); /** * @brief Subsitutes the given cell by a cold proxy representing the given context information */ - void create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type cell_index); + void create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type cell_index); + + /** + * @brief Gets a value indicating whether layout context info is provided / needed + */ + bool has_context_info() const; + + /** + * @brief Gets a value indicating whether layout context info is provided / needed + */ + bool has_context_info(cell_index_type cell_index) const; + + /** + * @brief Get the context information for the layout (for writing into a file) + * + * The context information is a sequence of strings which is pushed onto the given + * vector. It can be used to fill meta information with fill_meta_info_from_context. + */ + bool get_context_info (std::vector &strings) const; + + /** + * @brief Gets the context information as a binary object + */ + bool get_context_info (LayoutOrCellContextInfo &context_info) const; + + /** + * @brief Fills the layout's meta information from the context + */ + void fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to); + + /** + * @brief Fills the layout's meta information from the binary context + */ + void fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info); /** * @brief Get the context information for a given cell (for writing into a file) * * The context information is a sequence of strings which is pushed onto the given - * vector. It can be used to recover a respective proxy cell with the recover_proxy method. - * If the given cell is not a valid proxy or library references are missing, the method - * will return false. + * vector. It can be used to recover a respective proxy cell with the recover_proxy method + * or to fill meta information using fill_meta_info_from_context. */ bool get_context_info (cell_index_type cell_index, std::vector &context_info) const; /** * @brief Gets the context information as a binary object */ - bool get_context_info (cell_index_type cell_index, ProxyContextInfo &context_info) const; + bool get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &context_info) const; + + /** + * @brief Fills the layout's meta information from the context + */ + void fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to); + + /** + * @brief Fills the layout's meta information from the binary context + */ + void fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info); /** * @brief Recover a proxy cell from the given context info. @@ -1050,7 +1105,7 @@ public: /** * @brief Recover a proxy cell from the given binary context info object. */ - db::Cell *recover_proxy (const ProxyContextInfo &context_info); + db::Cell *recover_proxy (const LayoutOrCellContextInfo &context_info); /** * @brief Recover a proxy cell from the given context info. @@ -1072,7 +1127,7 @@ public: * * See the string-based version of "recover_proxy_as" for details. */ - bool recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &context_info, ImportLayerMapping *layer_mapping = 0); + bool recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info, ImportLayerMapping *layer_mapping = 0); /** * @brief Restores proxies as far as possible @@ -1322,7 +1377,15 @@ public: */ void move_layer (unsigned int src, unsigned int dest); - /** + /** + * @brief Move a layer (selected shape types only) + * + * Move a layer from the source to the target. The target is not cleared before, so that this method + * merges shapes from the source with the target layer. The source layer is empty after that operation. + */ + void move_layer (unsigned int src, unsigned int dest, unsigned int flags); + + /** * @brief Copy a layer * * Copy a layer from the source to the target. The target is not cleared before, so that this method @@ -1330,14 +1393,29 @@ public: */ void copy_layer (unsigned int src, unsigned int dest); - /** + /** + * @brief Copy a layer (selected shape types only) + * + * Copy a layer from the source to the target. The target is not cleared before, so that this method + * merges shapes from the source with the target layer. + */ + void copy_layer (unsigned int src, unsigned int dest, unsigned int flags); + + /** * @brief Clear a layer * * Clears the layer: removes all shapes. */ void clear_layer (unsigned int n); - /** + /** + * @brief Clear a layer (selected shapes only) + * + * Clears the layer: removes the shapes of the type given the flags (ShapeIterator::shapes_type) + */ + void clear_layer (unsigned int n, unsigned int flags); + + /** * @brief Delete a layer * * This does free the shapes of the cells and remembers the @@ -1812,6 +1890,31 @@ public: return m_meta_info.end (); } + /** + * @brief Delivers the meta information (begin iterator) per cell + */ + meta_info_iterator begin_meta (db::cell_index_type ci) const; + + /** + * @brief Delivers the meta information (end iterator) per cell + */ + meta_info_iterator end_meta (db::cell_index_type ci) const; + + /** + * @brief Gets the meta informatio name by ID + */ + const std::string &meta_info_name (meta_info_name_id_type name_id) const; + + /** + * @brief Gets the meta information name ID for a specific string + */ + meta_info_name_id_type meta_info_name_id (const std::string &name) const; + + /** + * @brief Gets the meta information name ID for a specific string (const version) + */ + meta_info_name_id_type meta_info_name_id (const std::string &name); + /** * @brief Clears the meta information */ @@ -1819,22 +1922,146 @@ public: /** * @brief Adds meta information - * The given meta information object is appended at the end of the meta information list. + * The given meta information object is added to the meta information list. * If a meta info object with the same name already exists it is overwritten. */ - void add_meta_info (const MetaInfo &i); + void add_meta_info (const std::string &name, const MetaInfo &i) + { + add_meta_info (meta_info_name_id (name), i); + } + + /** + * @brief Adds meta information (variant with name ID) + */ + void add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i); + + /** + * @brief Adds meta information from a sequence + */ + template + void add_meta_info (const I &b, const I &e) + { + for (I i = b; i != e; ++i) { + m_meta_info.insert (b, e); + } + } /** * @brief Removes the meta information object with the given name * The method will do nothing if no object with that name exists. */ - void remove_meta_info (const std::string &name); + void remove_meta_info (const std::string &name) + { + remove_meta_info (meta_info_name_id (name)); + } + + /** + * @brief Removes the meta information object with the given name ID + */ + void remove_meta_info (meta_info_name_id_type name_id); /** * @brief Gets the meta info value for a meta info object with the given name * If no object with that name exists, an empty string is returned */ - const std::string &meta_info_value (const std::string &name) const; + const MetaInfo &meta_info (const std::string &name) const + { + return meta_info (meta_info_name_id (name)); + } + + /** + * @brief Gets the meta info value for a meta info object with the given name ID + */ + const MetaInfo &meta_info (meta_info_name_id_type name_id) const; + + /** + * @brief Gets a value indicating whether a meta info with the given name is present + */ + bool has_meta_info (const std::string &name) const + { + return has_meta_info (meta_info_name_id (name)); + } + + /** + * @brief Gets a value indicating whether a meta info with the given name is present + */ + bool has_meta_info (meta_info_name_id_type name_id) const; + + /** + * @brief Clears the meta information for a specific cell + */ + void clear_meta (db::cell_index_type ci); + + /** + * @brief Adds meta information for a given cell + * The given meta information object is to the meta information list for the given cell. + * If a meta info object with the same name already exists it is overwritten. + */ + void add_meta_info (db::cell_index_type ci, const std::string &name, const MetaInfo &i) + { + add_meta_info (ci, meta_info_name_id (name), i); + } + + /** + * @brief Adds meta information for a given cell (version with name ID) + * The given meta information object is appended at the end of the meta information list. + * If a meta info object with the same name already exists it is overwritten. + */ + void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i); + + /** + * @brief Adds meta information from a sequence + */ + template + void add_meta_info (db::cell_index_type ci, const I &b, const I &e) + { + for (I i = b; i != e; ++i) { + m_meta_info_by_cell [ci].insert (b, e); + } + } + + /** + * @brief Gets a value indicating whether a meta info with the given name is present for the given cell + */ + bool has_meta_info (db::cell_index_type ci, const std::string &name) const + { + return has_meta_info (ci, meta_info_name_id (name)); + } + + /** + * @brief Gets a value indicating whether a meta info with the given name is present for the given cell + */ + bool has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const; + + /** + * @brief Removes the meta information object with the given name from the given cell + * The method will do nothing if no object with that name exists. + */ + void remove_meta_info (db::cell_index_type ci, const std::string &name) + { + remove_meta_info (ci, meta_info_name_id (name)); + } + + /** + * @brief Removes the meta information object with the given name ID from the given cell + * The method will do nothing if no object with that name exists. + */ + void remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id); + + /** + * @brief Gets the meta info value for a meta info object with the given name for the given cell + * If no object with that name exists, an empty string is returned + */ + const MetaInfo &meta_info (db::cell_index_type ci, const std::string &name) const + { + return meta_info (ci, meta_info_name_id (name)); + } + + /** + * @brief Gets the meta info value for a meta info object with the given name ID for the given cell + * If no object with that name exists, an empty string is returned + */ + const MetaInfo &meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const; /** * @brief This event is triggered when the technology changes @@ -1876,7 +2103,11 @@ private: lib_proxy_map m_lib_proxy_map; bool m_do_cleanup; bool m_editable; - meta_info m_meta_info; + std::map m_meta_info_name_map; + std::vector m_meta_info_names; + meta_info_map m_meta_info; + std::map m_meta_info_by_cell; + std::string m_tech_name; tl::Mutex m_lock; @@ -1920,7 +2151,7 @@ private: /** * @brief Recovers a proxy without considering the library from context_info */ - db::Cell *recover_proxy_no_lib (const ProxyContextInfo &context_info); + db::Cell *recover_proxy_no_lib (const LayoutOrCellContextInfo &context_info); }; /** @@ -1952,7 +2183,7 @@ public: explicit LayoutLocker (db::Layout *layout = 0, bool no_update = false) : mp_layout (layout), m_no_update (no_update) { - if (mp_layout) { + if (mp_layout.get ()) { mp_layout->start_changes (); } } @@ -1965,7 +2196,7 @@ public: LayoutLocker (const LayoutLocker &other) : mp_layout (other.mp_layout), m_no_update (other.m_no_update) { - if (mp_layout) { + if (mp_layout.get ()) { mp_layout->start_changes (); } } @@ -1976,17 +2207,17 @@ public: return *this; } - set (other.mp_layout, other.m_no_update); + set (const_cast (other.mp_layout.get ()), other.m_no_update); return *this; } private: - db::Layout *mp_layout; + tl::weak_ptr mp_layout; bool m_no_update; void set (db::Layout *layout, bool no_update) { - if (mp_layout) { + if (mp_layout.get ()) { if (m_no_update) { mp_layout->end_changes_no_update (); } else { @@ -1995,7 +2226,7 @@ private: } mp_layout = layout; m_no_update = no_update; - if (mp_layout) { + if (mp_layout.get ()) { mp_layout->start_changes (); } } diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index d52ec900e..be1e005e7 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -69,8 +69,10 @@ collect_cells (const db::Layout &l, const db::Cell *top, std::map &cci, std::vector &insts) +collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell, unsigned int /*flags*/, const std::map &cci, std::vector &insts, bool no_duplicates) { + size_t n_before = insts.size (); + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { std::map ::const_iterator ccii = cci.find (i->cell_index ()); @@ -81,6 +83,13 @@ collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell, } } + + if (no_duplicates) { + + std::sort (insts.begin () + n_before, insts.end ()); + insts.erase (std::unique (insts.begin () + n_before, insts.end ()), insts.end ()); + + } } static void @@ -102,7 +111,7 @@ rewrite_instances_to (std::vector &insts, unsi } static void -collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flags, const std::map &cci, std::vector &insts, PropertyMapper &pn) +collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flags, const std::map &cci, std::vector &insts, PropertyMapper &pn, bool no_duplicates) { insts.clear (); @@ -148,6 +157,10 @@ collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flag } std::sort (insts.begin (), insts.end ()); + + if (no_duplicates) { + insts.erase (std::unique (insts.begin (), insts.end ()), insts.end ()); + } } /** @@ -178,10 +191,10 @@ int compare_seq (I b1, I e1, I b2, I e2, Op op) /** * @brief Reduces two vectors to the common objects as determined by the compare operator * If the iterate parameter is true, the reduction is repeated until no more reduction can be - * achieved. This is useful with tolerances since the sorted is not strict in that case. + * achieved. This is useful with tolerances since the sorting is not strict in that case. */ template -void reduce (std::vector &a, std::vector &b, Op op, bool iterate) +void reduce (std::vector &a, std::vector &b, Op op, bool iterate, bool no_duplicates) { do { @@ -196,12 +209,29 @@ void reduce (std::vector &a, std::vector &b, Op op, bool iterate) while (ra != a.end () && rb != b.end ()) { if (op (*ra, *rb)) { - *wa++ = *ra++; + typename std::vector::const_iterator r = ra++; + *wa = *r; + while (no_duplicates && ra != a.end () && !op (*ra, *r) && !op(*r, *ra)) { + ++ra; + } + ++wa; } else if (op (*rb, *ra)) { - *wb++ = *rb++; + typename std::vector::const_iterator r = rb++; + *wb = *r; + while (no_duplicates && rb != b.end () && !op (*rb, *r) && !op(*r, *rb)) { + ++rb; + } + ++wb; } else { - ++ra; - ++rb; + typename std::vector::const_iterator r; + r = ra++; + while (no_duplicates && ra != a.end () && !op (*ra, *r) && !op(*r, *ra)) { + ++ra; + } + r = rb++; + while (no_duplicates && rb != b.end () && !op (*rb, *r) && !op(*r, *rb)) { + ++rb; + } } } @@ -211,14 +241,22 @@ void reduce (std::vector &a, std::vector &b, Op op, bool iterate) if (ra != wa) { while (ra != a.end ()) { - *wa++ = *ra++; + typename std::vector::const_iterator r = ra++; + *wa++ = *r; + while (no_duplicates && ra != a.end () && !op (*ra, *r) && !op(*r, *ra)) { + ++ra; + } } a.erase (wa, a.end ()); } if (rb != wb) { while (rb != b.end ()) { - *wb++ = *rb++; + typename std::vector::const_iterator r = rb++; + *wb++ = *r; + while (no_duplicates && rb != b.end () && !op (*rb, *r) && !op(*r, *rb)) { + ++rb; + } } b.erase (wb, b.end ()); } @@ -405,7 +443,7 @@ struct PolygonCompareOpWithTolerance m_eb.push_back (*e); } - reduce (m_ea, m_eb, EdgeCompareOpWithTolerance (m_tolerance), m_tolerance > 0); + reduce (m_ea, m_eb, EdgeCompareOpWithTolerance (m_tolerance), m_tolerance > 0, false); return compare_seq (m_ea.begin (), m_ea.end (), m_eb.begin (), m_eb.end (), EdgeCompareOpWithTolerance (m_tolerance)) < 0; } @@ -665,6 +703,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout } bool verbose = (flags & layout_diff::f_verbose); + bool no_duplicates = (flags & layout_diff::f_ignore_duplicates); db::Layout n, na, nb; na.properties_repository () = a.properties_repository (); @@ -897,20 +936,20 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout r.bbox_differs (cell_a->bbox (), cell_b->bbox ()); } - collect_insts (a, cell_a, flags, common_cell_indices_a, insts_a, prop_normalize_a); - collect_insts (b, cell_b, flags, common_cell_indices_b, insts_b, prop_normalize_b); + collect_insts (a, cell_a, flags, common_cell_indices_a, insts_a, prop_normalize_a, no_duplicates); + collect_insts (b, cell_b, flags, common_cell_indices_b, insts_b, prop_normalize_b, no_duplicates); std::vector anotb; std::set_difference (insts_a.begin (), insts_a.end (), insts_b.begin (), insts_b.end (), std::back_inserter (anotb)); rewrite_instances_to (anotb, flags, common_cells_a, prop_remap_to_a); - collect_insts_of_unmapped_cells (a, cell_a, flags, common_cell_indices_a, anotb); + collect_insts_of_unmapped_cells (a, cell_a, flags, common_cell_indices_a, anotb, no_duplicates); std::vector bnota; std::set_difference (insts_b.begin (), insts_b.end (), insts_a.begin (), insts_a.end (), std::back_inserter (bnota)); rewrite_instances_to (bnota, flags, common_cells_b, prop_remap_to_b); - collect_insts_of_unmapped_cells (b, cell_b, flags, common_cell_indices_b, bnota); + collect_insts_of_unmapped_cells (b, cell_b, flags, common_cell_indices_b, bnota, no_duplicates); if (! anotb.empty () || ! bnota.empty ()) { @@ -979,7 +1018,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout collect_polygons (b, cell_b, layer_b, flags, polygons_b, prop_normalize_b); } - reduce (polygons_a, polygons_b, make_polygon_compare_func (tolerance), tolerance > 0); + reduce (polygons_a, polygons_b, make_polygon_compare_func (tolerance), tolerance > 0, no_duplicates); if (!polygons_a.empty () || !polygons_b.empty ()) { differs = true; @@ -1007,7 +1046,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout collect_paths (b, cell_b, layer_b, flags, paths_b, prop_normalize_b); } - reduce (paths_a, paths_b, make_path_compare_func (tolerance), tolerance > 0); + reduce (paths_a, paths_b, make_path_compare_func (tolerance), tolerance > 0, no_duplicates); if (!paths_a.empty () || !paths_b.empty ()) { differs = true; @@ -1034,7 +1073,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout collect_texts (b, cell_b, layer_b, flags, texts_b, prop_normalize_b); } - reduce (texts_a, texts_b, make_text_compare_func (tolerance), tolerance > 0); + reduce (texts_a, texts_b, make_text_compare_func (tolerance), tolerance > 0, no_duplicates); if (!texts_a.empty () || !texts_b.empty ()) { differs = true; @@ -1061,7 +1100,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout collect_boxes (b, cell_b, layer_b, flags, boxes_b, prop_normalize_b); } - reduce (boxes_a, boxes_b, make_box_compare_func (tolerance), tolerance > 0); + reduce (boxes_a, boxes_b, make_box_compare_func (tolerance), tolerance > 0, no_duplicates); if (!boxes_a.empty () || !boxes_b.empty ()) { differs = true; @@ -1088,7 +1127,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout collect_edges (b, cell_b, layer_b, flags, edges_b, prop_normalize_b); } - reduce (edges_a, edges_b, make_edge_compare_func (tolerance), tolerance > 0); + reduce (edges_a, edges_b, make_edge_compare_func (tolerance), tolerance > 0, no_duplicates); if (!edges_a.empty () || !edges_b.empty ()) { differs = true; @@ -1113,7 +1152,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout collect_edge_pairs (b, cell_b, layer_b, flags, edge_pairs_b, prop_normalize_b); } - reduce (edge_pairs_a, edge_pairs_b, make_edge_pair_compare_func (tolerance), tolerance > 0); + reduce (edge_pairs_a, edge_pairs_b, make_edge_pair_compare_func (tolerance), tolerance > 0, no_duplicates); if (!edge_pairs_a.empty () || !edge_pairs_b.empty ()) { differs = true; diff --git a/src/db/db/dbLayoutDiff.h b/src/db/db/dbLayoutDiff.h index 865883af1..56f0114b6 100644 --- a/src/db/db/dbLayoutDiff.h +++ b/src/db/db/dbLayoutDiff.h @@ -74,12 +74,15 @@ const unsigned int f_paths_as_polygons = 0x100; // Derive smart cell mapping instead of name mapping (available only if top cells are specified) const unsigned int f_smart_cell_mapping = 0x200; -// Don't summarize missing layers +// Don't summarize missing layers - print them in detail const unsigned int f_dont_summarize_missing_layers = 0x400; // Ignore text details (font, size, presentation) const unsigned int f_no_text_details = 0x800; +// Ignore duplicate instances or shapes +const unsigned int f_ignore_duplicates = 0x1000; + } /** diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index b22464d51..c3ec9efc2 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -218,7 +218,7 @@ merge_layouts (db::Layout &target, std::map new_cell_mapping; for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { if (cell_mapping.find (*c) == cell_mapping.end ()) { - new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source.cell_name (*c)))); + new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source, *c))); } } @@ -234,7 +234,7 @@ merge_layouts (db::Layout &target, // provide the property mapper db::PropertyMapper pm (&target, &source); - tl::RelativeProgress progress (tl::to_string (tr ("Merge cells")), all_cells_to_copy.size (), 1); + tl::RelativeProgress progress (tl::to_string (tr ("Merge layouts")), all_cells_to_copy.size (), 1); // actually to the mapping for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { @@ -346,7 +346,7 @@ copy_or_move_shapes (db::Layout &target, // provide the property mapper db::PropertyMapper pm (&target, &source); - tl::RelativeProgress progress (tl::to_string (tr ("Merge cells")), all_cells_to_copy.size () * layer_mapping.size (), 1); + tl::RelativeProgress progress (tl::to_string (tr ("Copy shapes")), all_cells_to_copy.size () * layer_mapping.size (), 1); // and copy for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index a6075f5d6..59acfa95f 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -205,7 +205,7 @@ Library::remap_to (db::Library *other) if (! pn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); @@ -216,7 +216,7 @@ Library::remap_to (db::Library *other) if (! old_pcell_decl || ! new_pcell_decl) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); @@ -254,7 +254,7 @@ Library::remap_to (db::Library *other) if (! cn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index e5410751e..50de04291 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -31,31 +31,34 @@ namespace db { +// Switch for version-agnostic code +#define KLAYOUT_META_INFO_V2 + /** * @brief A structure describing the meta information from the reader * * In the meta information block, the reader provides additional information * about the file and content etc. - * "name" is a unique name that can be used to identify the information. * "description" is a "speaking" description of the information. * "value" is the value of the specific part of meta information. */ struct DB_PUBLIC MetaInfo { - MetaInfo (const std::string &n, const std::string &d, const std::string &v) - : name (n), description (d), value (v) + MetaInfo (const std::string &d, const tl::Variant &v, bool p = false) + : description (d), value (v), persisted (p) { // .. nothing else .. } MetaInfo () + : persisted (false) { // .. nothing else .. } - std::string name; std::string description; - std::string value; + tl::Variant value; + bool persisted; }; /** @@ -63,7 +66,6 @@ struct DB_PUBLIC MetaInfo */ inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const MetaInfo &v, bool no_self, void *parent) { - db::mem_stat (stat, purpose, cat, v.name, no_self, parent); db::mem_stat (stat, purpose, cat, v.description, no_self, parent); db::mem_stat (stat, purpose, cat, v.value, no_self, parent); } diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 8765e9d64..7d5bf2805 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -75,6 +75,7 @@ class SpiceReaderStream { public: SpiceReaderStream (); + SpiceReaderStream (const std::string &lib); ~SpiceReaderStream (); void set_stream (tl::InputStream &stream); @@ -85,9 +86,11 @@ public: int line_number () const; std::string source () const; bool at_end () const; + const std::string &lib () const { return m_lib; } void swap (SpiceReaderStream &other) { + std::swap (m_lib, other.m_lib); std::swap (mp_stream, other.mp_stream); std::swap (m_owns_stream, other.m_owns_stream); std::swap (mp_text_stream, other.mp_text_stream); @@ -103,6 +106,7 @@ private: int m_line_number; std::string m_stored_line; bool m_has_stored_line; + std::string m_lib; }; @@ -112,6 +116,12 @@ SpiceReaderStream::SpiceReaderStream () // .. nothing yet .. } +SpiceReaderStream::SpiceReaderStream (const std::string &lib) + : mp_stream (0), m_owns_stream (false), mp_text_stream (0), m_line_number (0), m_stored_line (), m_has_stored_line (false), m_lib (lib) +{ + // .. nothing yet .. +} + SpiceReaderStream::~SpiceReaderStream () { close (); @@ -383,6 +393,7 @@ private: std::vector m_paths; std::map m_file_id_per_path; std::list m_streams; + std::list m_in_lib; SpiceReaderStream m_stream; int m_file_id; std::map m_cached_circuits; @@ -393,7 +404,7 @@ private: std::set m_global_net_names; std::vector m_global_nets; - void push_stream (const std::string &path); + void push_stream (const std::string &path, const std::string &lib = std::string ()); void pop_stream (); bool at_end (); void read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector &nets); @@ -500,7 +511,7 @@ SpiceCircuitDict::read (tl::InputStream &stream) } void -SpiceCircuitDict::push_stream (const std::string &path) +SpiceCircuitDict::push_stream (const std::string &path, const std::string &lib) { tl::URI current_uri (m_stream.source ()); tl::URI new_uri (path); @@ -516,7 +527,7 @@ SpiceCircuitDict::push_stream (const std::string &path) istream = new tl::InputStream (current_uri.resolved (new_uri).to_abstract_path ()); } - m_streams.push_back (SpiceReaderStream ()); + m_streams.push_back (SpiceReaderStream (lib)); m_streams.back ().swap (m_stream); m_stream.set_stream (istream); @@ -568,24 +579,73 @@ SpiceCircuitDict::get_line () if (m_streams.empty ()) { break; } else { + if (! m_stream.lib ().empty ()) { + m_in_lib.pop_back (); + } pop_stream (); } } else { + bool consider_line = m_in_lib.empty () || (! m_stream.lib ().empty () && m_stream.lib () == m_in_lib.back ()); + tl::Extractor ex (lp.first.c_str ()); if (ex.test_without_case (".include") || ex.test_without_case (".inc")) { std::string path; ex.read_word_or_quoted (path, allowed_name_chars); - push_stream (path); + if (consider_line) { + std::string libname = m_stream.lib (); + push_stream (path, libname); + if (! libname.empty ()) { + m_in_lib.push_back (libname); + } + } + + ex.expect_end (); + + } else if (ex.test_without_case (".lib")) { + + std::string path_or_libname; + + ex.read_word_or_quoted (path_or_libname, allowed_name_chars); + if (! ex.at_end ()) { + + std::string libname; + ex.read_word_or_quoted (libname, allowed_name_chars); + + if (consider_line) { + libname = mp_netlist->normalize_name (libname); + push_stream (path_or_libname, libname); + if (! libname.empty ()) { + m_in_lib.push_back (std::string ()); + } + } + + } else { + + std::string libname = mp_netlist->normalize_name (path_or_libname); + m_in_lib.push_back (libname); + ex.expect_end (); + + } + + } else if (ex.test_without_case (".endl")) { + + if (! m_in_lib.empty ()) { + m_in_lib.pop_back (); + } else { + warn (tl::to_string (tr ("Ignoring .endl without .lib"))); + } + + ex.expect_end (); } else if (ex.at_end () || ex.test ("*")) { // skip empty and comment lines - } else { + } else if (consider_line) { break; } diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.cc b/src/db/db/dbNetlistSpiceReaderDelegate.cc index 326d920aa..88d559050 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.cc +++ b/src/db/db/dbNetlistSpiceReaderDelegate.cc @@ -225,7 +225,7 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, // a parameter std::string pn = mp_netlist ? mp_netlist->normalize_name (n) : tl::to_upper_case (n); - pv [pn] = read_value (ex, variables); + pv [pn] = read_value (ex, variables, pv); } else { @@ -304,46 +304,86 @@ void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std: // (same for C, L instead of R) if (nn.size () < 2) { - error (tl::to_string (tr ("Not enough specs for a R, C or L device"))); + error (tl::to_string (tr ("Not enough specs (nodes, value, model) for a R, C or L device"))); + } else if (nn.size () > 5) { + error (tl::to_string (tr ("Too many specs (nodes, value, model) for a R, C or L device"))); } + // Variations are (here for "C" element): + // (1) Cname n1 n2 C=value [other params] + // (2) Cname n1 n2 value [params] + // (3) Cname n1 n2 model C=value [other params] + // Cname n1 n2 n3 C=value [other params] -> not supported, cannot tell from (3) without further analysis + // (4) Cname n1 n2 model value [params] + // Cname n1 n2 n3 value [params] -> not supported, cannot tell from (4) without further analysis + // (5) Cname n1 n2 n3 model C=value [other params] + // (6) Cname n1 n2 value model [params] + // (7) Cname n1 n2 n3 model value [params] + // (8) Cname n1 n2 n3 value model [params] + auto rv = pv.find (element); - if (rv != pv.end ()) { - // value given by parameter - value = rv->second.to_double (); - - if (nn.size () >= 3) { - // Rname n1 n2 model [params] - // Rname n1 n2 n3 model [params] - model = nn.back (); - nn.pop_back (); + bool has_value = false; + if (nn.size () == 2) { + if (rv != pv.end ()) { + value = rv->second.to_double (); // (1) + has_value = true; } - - } else if (nn.size () >= 3) { - + } else if (nn.size () == 3) { if (try_read_value (nn.back (), value, variables)) { - - // Rname n1 n2 value - // Rname n1 n2 n3 value + has_value = true; // (2) nn.pop_back (); - } else { - - // Rname n1 n2 value model [params] - // Rname n1 n2 n3 value model [params] + model = nn.back (); // (3) + nn.pop_back (); + if (rv != pv.end ()) { + value = rv->second.to_double (); + has_value = true; + } + } + } else if (nn.size () == 4) { + if (try_read_value (nn.back (), value, variables)) { + has_value = true; // (4) + nn.pop_back (); + } else if (rv != pv.end ()) { + value = rv->second.to_double (); // (5) + has_value = true; model = nn.back (); nn.pop_back (); - if (! try_read_value (nn.back (), value, variables)) { - error (tl::to_string (tr ("Can't find a value for a R, C or L device"))); - } else { - nn.pop_back (); - } - + } else if (try_read_value (nn[2], value, variables)) { + has_value = true; // (6) + model = nn.back (); + nn.pop_back (); + nn.pop_back (); + } else { + model = nn.back (); // fall back to (5) + nn.pop_back (); + } + } else { + if (try_read_value (nn.back (), value, variables)) { + has_value = true; // (7) + nn.pop_back (); + model = nn.back (); + nn.pop_back (); + } else if (try_read_value (nn[3], value, variables)) { + has_value = true; // (8) + model = nn.back (); + nn.pop_back (); + nn.pop_back (); } - } + if (rv != pv.end ()) { + pv.erase (rv); + } + + if (! has_value) { + error (tl::to_string (tr ("Can't find a value for a R, C or L device"))); + } + + // store the value under the element name always + pv[element] = tl::Variant (value); + } else { // others: n-terminal devices with a model (last node) @@ -379,8 +419,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin std::map params = pv; std::vector terminal_order; - size_t defp = std::numeric_limits::max (); - double mult = 1.0; auto mp = params.find ("M"); if (mp != params.end ()) { @@ -424,8 +462,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // Apply multiplier (divider, according to ngspice manual) value /= mult; - - defp = db::DeviceClassResistor::param_id_R; + params["R"] = tl::Variant (value); // Apply multiplier to other parameters static const char *scale_params[] = { "A", "P", "W" }; @@ -455,8 +492,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // Apply multiplier (divider, according to ngspice manual) value /= mult; - - defp = db::DeviceClassInductor::param_id_L; + params["L"] = tl::Variant (value); } else if (element == "C") { @@ -488,8 +524,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // Apply multiplier value *= mult; - - defp = db::DeviceClassCapacitor::param_id_C; + params["C"] = tl::Variant (value); // Apply multiplier to other parameters static const char *scale_params[] = { "A", "P" }; @@ -620,8 +655,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin double pv = 0.0; if (v != params.end ()) { pv = v->second.to_double (); - } else if (i->id () == defp) { - pv = value; } else { continue; } @@ -653,6 +686,13 @@ NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map &variables1, const std::map &variables2) +{ + NetlistSpiceReaderExpressionParser parser (&variables1, &variables2); + return parser.read (ex); +} + bool NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &v, const std::map &variables) { diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.h b/src/db/db/dbNetlistSpiceReaderDelegate.h index 6e304ed80..75e47824f 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.h +++ b/src/db/db/dbNetlistSpiceReaderDelegate.h @@ -154,6 +154,11 @@ public: */ static tl::Variant read_value(tl::Extractor &ex, const std::map &variables); + /** + * @brief Reads a value from the extractor (with formula evaluation and two levels of variables) + */ + static tl::Variant read_value(tl::Extractor &ex, const std::map &variables1, const std::map &variables2); + /** * @brief Tries to read a value from the extractor (with formula evaluation) */ diff --git a/src/db/db/dbNetlistSpiceReaderExpressionParser.cc b/src/db/db/dbNetlistSpiceReaderExpressionParser.cc index ef2ad63d4..dcfd46332 100644 --- a/src/db/db/dbNetlistSpiceReaderExpressionParser.cc +++ b/src/db/db/dbNetlistSpiceReaderExpressionParser.cc @@ -47,8 +47,15 @@ static bool to_bool (const tl::Variant &v) NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale) : m_def_scale (def_scale) { - static variables_type empty_variables; - mp_variables = vars ? vars : &empty_variables; + mp_variables1 = vars; + mp_variables2 = 0; +} + +NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars1, const variables_type *vars2, double def_scale) + : m_def_scale (def_scale) +{ + mp_variables1 = vars1; + mp_variables2 = vars2; } // expression syntax taken from ngspice: @@ -264,13 +271,20 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool * } else { - auto vi = mp_variables->find (var); - if (vi != mp_variables->end ()) { - return vi->second; - } else { - // keep word as string value - return tl::Variant (var); + if (mp_variables1) { + auto vi = mp_variables1->find (var); + if (vi != mp_variables1->end ()) { + return vi->second; + } } + if (mp_variables2) { + auto vi = mp_variables2->find (var); + if (vi != mp_variables2->end ()) { + return vi->second; + } + } + // keep word as string value + return tl::Variant (var); } diff --git a/src/db/db/dbNetlistSpiceReaderExpressionParser.h b/src/db/db/dbNetlistSpiceReaderExpressionParser.h index 06684e124..822788587 100644 --- a/src/db/db/dbNetlistSpiceReaderExpressionParser.h +++ b/src/db/db/dbNetlistSpiceReaderExpressionParser.h @@ -46,6 +46,7 @@ public: typedef std::map variables_type; NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale = 1.0); + NetlistSpiceReaderExpressionParser (const variables_type *vars1, const variables_type *vars2, double def_scale = 1.0); tl::Variant read (tl::Extractor &ex) const; tl::Variant read (const std::string &s) const; @@ -53,7 +54,7 @@ public: bool try_read (const std::string &s, tl::Variant &v) const; private: - const variables_type *mp_variables; + const variables_type *mp_variables1, *mp_variables2; double m_def_scale; tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const; diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 9eceb0a46..0b3a1f603 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -1044,6 +1044,19 @@ private: } }; +} // namespace db + +namespace std +{ + +// injecting a global std::swap for polygons into the +// std namespace +template +void swap (db::polygon_contour &a, db::polygon_contour &b) +{ + a.swap (b); +} + } namespace db @@ -2047,7 +2060,7 @@ public: * @param remove_reflected True, if reflecting spikes shall be removed on compression */ template - void insert_hole (I start, I end, bool compress = default_compression (), bool remove_reflected = false) + void insert_hole (I start, I end, bool compress = default_compression (), bool remove_reflected = false) { insert_hole (start, end, db::unit_trans (), compress, remove_reflected); } @@ -2073,21 +2086,17 @@ public: // add the hole contour_type &h = add_hole (); h.assign (start, end, op, true, compress, true /*normalize*/, remove_reflected); + } - // and keep the list sorted by swapping the - // elements and move the last one to the right - // position - // KLUDGE: this is probably a performance bottleneck from applications - // with polygons with may holes .. - if (holes () > 1) { - typename contour_list_type::iterator ins_pos = std::lower_bound (m_ctrs.begin () + 1, m_ctrs.end () - 1, h); - typename contour_list_type::iterator p = m_ctrs.end () - 1; - if (ins_pos != p) { - while (p != ins_pos) { - p->swap (p [-1]); - --p; - } - } + /** + * @brief Sort the holes + * + * Sorting the holes makes certain algorithms more effective. + */ + void sort_holes () + { + if (! m_ctrs.empty ()) { + std::sort (m_ctrs.begin () + 1, m_ctrs.end ()); } } @@ -2884,6 +2893,16 @@ public: tl_assert (false); } + /** + * @brief A dummy implementation of "sort_holes" provided for template instantiation + * + * Asserts, if begin called. + */ + void sort_holes () + { + tl_assert (false); + } + /** * @brief A dummy implementation of "hole" provided for template instantiation * @@ -3530,7 +3549,7 @@ inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int namespace std { -// injecting a global std::swap for polygons into the +// injecting a global std::swap for polygons into the // std namespace template void swap (db::polygon &a, db::polygon &b) diff --git a/src/db/db/dbPolygonGenerators.cc b/src/db/db/dbPolygonGenerators.cc index 8849e3c1f..e14934415 100644 --- a/src/db/db/dbPolygonGenerators.cc +++ b/src/db/db/dbPolygonGenerators.cc @@ -865,6 +865,8 @@ PolygonGenerator::produce_poly (const PGPolyContour &c) } + m_poly.sort_holes (); + } mp_psink->put (m_poly); diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index db18b69d3..9a11356d6 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -456,6 +456,8 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, c } } + hull->sort_holes (); + right_of_line->put (*hull); } @@ -859,6 +861,8 @@ smooth (const db::Polygon &polygon, db::Coord d, bool keep_hv) } } + new_poly.sort_holes (); + } return new_poly; @@ -1351,6 +1355,8 @@ do_extract_rad (const db::polygon &polygon, double &rinner, double &router, u } + new_polygon->sort_holes (); + } else { if (! do_extract_rad_from_contour (polygon.begin_hull (), polygon.end_hull (), rinner, router, n, (std::vector > *) 0, false)) { @@ -1542,6 +1548,8 @@ do_compute_rounded (const db::polygon &polygon, double rinner, double router, new_poly.insert_hole (new_pts.begin (), new_pts.end (), false /*don't compress*/); } + new_poly.sort_holes (); + return new_poly; } @@ -2300,9 +2308,11 @@ static void decompose_convex_helper (int depth, PreferredOrientation po, const d db::Box bbox = sp.box (); db::coord_traits::area_type atot = 0; + db::coord_traits::distance_type min_edge = std::numeric_limits::distance_type>::max (); for (size_t i = 0; i < n; ++i) { db::Edge ep (sp.hull ()[(i + n - 1) % n], sp.hull ()[i]); atot += db::vprod (ep.p2 () - db::Point (), ep.p1 () - db::Point ()); + min_edge = std::min (min_edge, ep.length ()); } std::set skipped; @@ -2449,9 +2459,14 @@ static void decompose_convex_helper (int depth, PreferredOrientation po, const d int cr = 0; if (x.second == efc.p1 ()) { if (db::vprod (efc, efp) < 0) { - cr = 2; // cut terminates at another concave corner + cr = 3; // cut terminates at another concave corner } else { - cr = 1; // cut terminates at a convex corner + cr = 2; // cut terminates at a convex corner + } + } else { + db::coord_traits::distance_type el = std::min (x.second.distance (efc.p1 ()), x.second.distance (efc.p2 ())); + if (el >= min_edge) { + cr = 1; // does not induce shorter edge than we have so far } } @@ -2882,6 +2897,8 @@ snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vecto } + pnew.sort_holes (); + return pnew; } @@ -2921,6 +2938,8 @@ scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, } + pnew.sort_holes (); + return pnew; } diff --git a/src/db/db/dbRecursiveInstanceIterator.cc b/src/db/db/dbRecursiveInstanceIterator.cc index 31943d66c..d4a6b6aff 100644 --- a/src/db/db/dbRecursiveInstanceIterator.cc +++ b/src/db/db/dbRecursiveInstanceIterator.cc @@ -447,8 +447,8 @@ RecursiveInstanceIterator::next (RecursiveInstanceReceiver *receiver) } else { ++m_inst; new_inst (receiver); - next_instance (receiver); } + next_instance (receiver); } } @@ -463,35 +463,42 @@ RecursiveInstanceIterator::next_instance (RecursiveInstanceReceiver *receiver) c { while (true) { - if (! m_inst.at_end ()) { + while (true) { - if (int (m_inst_iterators.size ()) < m_max_depth && (m_all_targets || m_target_tree.find (m_inst->cell_index ()) != m_target_tree.end ())) { - down (receiver); - } + if (! m_inst.at_end ()) { - } else { + if (int (m_inst_iterators.size ()) < m_max_depth && (m_all_targets || m_target_tree.find (m_inst->cell_index ()) != m_target_tree.end ())) { + down (receiver); + } else { + break; + } - if (! m_inst_iterators.empty ()) { - // no more instances: up and next instance - up (receiver); } else { + + if (! m_inst_iterators.empty ()) { + // no more instances: up and next instance + up (receiver); + } break; + } } - if (! m_inst.at_end ()) { - if (! needs_visit ()) { - ++m_inst_array; - if (! m_inst_array.at_end ()) { - new_inst_member (receiver); - } else { - ++m_inst; - new_inst (receiver); - } + if (m_inst.at_end ()) { + break; + } + + if (! needs_visit ()) { + ++m_inst_array; + if (! m_inst_array.at_end ()) { + new_inst_member (receiver); } else { - break; + ++m_inst; + new_inst (receiver); } + } else { + break; } } diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index af551b6e2..979ab6d78 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -693,7 +693,11 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const // determine whether the cell is empty with respect to the layers specified bool is_empty = false; - if (! m_has_layers) { + if (receiver && receiver->wants_all_cells ()) { + + // don't skip empty cells in that case + + } else if (! m_has_layers) { is_empty = mp_layout->cell (m_inst->cell_index ()).bbox (m_layer).empty (); diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 99ffeb1fc..281a7ce82 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -913,6 +913,11 @@ public: */ virtual ~RecursiveShapeReceiver () { } + /** + * @brief Returns true, if the receivers wants the full hierarchy and not just non-empty cells + */ + virtual bool wants_all_cells () const { return false; } + /** * @brief Called once when the iterator begins pushing */ diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index d8b439404..48736115d 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -81,6 +81,12 @@ inline bool needs_translate (object_tag /*tag*/) return tl::is_equal_type::can_deref, tl::True> () || tl::is_equal_type::is_array, tl::True> (); } +inline bool type_mask_applies (const db::LayerBase *layer, unsigned int flags) +{ + unsigned int tm = layer->type_mask (); + return (((flags & db::ShapeIterator::Properties) == 0 || (tm & db::ShapeIterator::Properties) != 0) && (flags & tm) != 0); +} + // --------------------------------------------------------------------------------------- // layer_op implementation @@ -214,7 +220,13 @@ Shapes::insert (const Shapes &d) } void -Shapes::do_insert (const Shapes &d) +Shapes::insert (const Shapes &d, unsigned int flags) +{ + do_insert (d, flags); +} + +void +Shapes::do_insert (const Shapes &d, unsigned int flags) { // shortcut for "nothing to do" if (d.empty ()) { @@ -228,10 +240,12 @@ Shapes::do_insert (const Shapes &d) m_layers.reserve (d.m_layers.size ()); for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - m_layers.push_back ((*l)->clone ()); - if (manager () && manager ()->transacting ()) { - check_is_editable_for_undo_redo (); - manager ()->queue (this, new FullLayerOp (true, m_layers.back ())); + if (type_mask_applies (*l, flags)) { + m_layers.push_back ((*l)->clone ()); + if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + manager ()->queue (this, new FullLayerOp (true, m_layers.back ())); + } } } @@ -239,7 +253,9 @@ Shapes::do_insert (const Shapes &d) } else { for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - (*l)->insert_into (this); + if (type_mask_applies (*l, flags)) { + (*l)->insert_into (this); + } } } @@ -247,14 +263,18 @@ Shapes::do_insert (const Shapes &d) // the target is standalone - dereference for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - (*l)->deref_into (this); + if (type_mask_applies (*l, flags)) { + (*l)->deref_into (this); + } } } else { // both shape containers are in separate spaces - translate for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - (*l)->translate_into (this, shape_repository (), array_repository ()); + if (type_mask_applies (*l, flags)) { + (*l)->translate_into (this, shape_repository (), array_repository ()); + } } } @@ -1046,6 +1066,41 @@ Shapes::clear () } } +void +Shapes::clear (unsigned int flags) +{ + if (!m_layers.empty ()) { + + invalidate_state (); // HINT: must come before the change is done! + + tl::vector new_layers; + + for (tl::vector::const_iterator l = m_layers.end (); l != m_layers.begin (); ) { + + // because the undo stack will do a push, we need to remove layers from the back (this is the last undo + // element to be executed) + --l; + + if (type_mask_applies (*l, flags)) { + + if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + manager ()->queue (this, new FullLayerOp (false, (*l))); + } else { + delete *l; + } + + } else { + new_layers.push_back (*l); + } + + } + + m_layers.swap (new_layers); + + } +} + void Shapes::reset_bbox_dirty () { set_dirty (false); diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index b8114e360..bffb08315 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -628,6 +628,18 @@ public: */ void insert (const Shapes &d); + /** + * @brief Insert all shapes from another container using the given shape types only + * + * This method insert all shapes from the given shape container. + * + * HINT: This method can duplicate shape containers from one layout to another. + * The current implementation does not translate property Id's. + * It is mainly intended for 1-to-1 copies of layouts where the whole + * property repository is copied. + */ + void insert (const Shapes &d, unsigned int types); + /** * @brief Assignment operator with transformation * @@ -1219,6 +1231,11 @@ public: */ void clear (); + /** + * @brief Clears the collection (given shape types only) + */ + void clear (unsigned int types); + /** * @brief Report the type mask of the objects stored herein * @@ -1515,7 +1532,7 @@ private: db::Cell *mp_cell; // HINT: contains "dirty" in bit 0 and "editable" in bit 1 void invalidate_state (); - void do_insert (const Shapes &d); + void do_insert (const Shapes &d, unsigned int flags = db::ShapeIterator::All); void check_is_editable_for_undo_redo () const; // gets the layers array diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 00db50666..9fc3c76a2 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -20,9 +20,8 @@ */ - - #include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" #include "gsiDeclDbHelpers.h" #include "dbLayout.h" @@ -984,6 +983,58 @@ static db::Layout *layout (db::Cell *cell) return cell->layout (); } +static void cell_clear_meta_info (db::Cell *cell) +{ + if (cell->layout ()) { + cell->layout ()->clear_meta (cell->cell_index ()); + } +} + +static void cell_remove_meta_info (db::Cell *cell, const std::string &name) +{ + if (cell->layout ()) { + cell->layout ()->remove_meta_info (cell->cell_index (), name); + } +} + +static void cell_add_meta_info (db::Cell *cell, const MetaInfo &mi) +{ + if (cell->layout ()) { + cell->layout ()->add_meta_info (cell->cell_index (), mi.name, db::MetaInfo (mi.description, mi.value, mi.persisted)); + } +} + +static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::string &name) +{ + if (! cell->layout ()) { + static tl::Variant null_value; + return null_value; + } else { + return cell->layout ()->meta_info (cell->cell_index (), name).value; + } +} + +static MetaInfo *cell_meta_info (db::Cell *cell, const std::string &name) +{ + if (! cell->layout ()) { + return 0; + } else if (cell->layout ()->has_meta_info (cell->cell_index (), name)) { + const db::MetaInfo &value = cell->layout ()->meta_info (cell->cell_index (), name); + return new MetaInfo (name, value); + } else { + return 0; + } +} + +static gsi::MetaInfoIterator cell_each_meta_info (const db::Cell *cell) +{ + if (! cell->layout ()) { + return gsi::MetaInfoIterator (); + } else { + return gsi::MetaInfoIterator (cell->layout (), cell->layout ()->begin_meta (cell->cell_index ()), cell->layout ()->end_meta (cell->cell_index ())); + } +} + static bool cell_has_prop_id (const db::Cell *c) { return c->prop_id () != 0; @@ -1780,6 +1831,47 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.23." ) + + gsi::method_ext ("add_meta_info", &cell_add_meta_info, gsi::arg ("info"), + "@brief Adds meta information to the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method_ext ("clear_meta_info", &cell_clear_meta_info, + "@brief Clears the meta information of the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method_ext ("remove_meta_info", &cell_remove_meta_info, gsi::arg ("name"), + "@brief Removes meta information from the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method_ext ("meta_info_value", &cell_meta_info_value, gsi::arg ("name"), + "@brief Gets the meta information value for a given name\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "If no meta information with the given name exists, a nil value will be returned.\n" + "A more generic version that delivers all fields of the meta information is \\meta_info.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::factory_ext ("meta_info", &cell_meta_info, gsi::arg ("name"), + "@brief Gets the meta information for a given name\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "If no meta information with the given name exists, a default object with empty fields will be returned.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::iterator_ext ("each_meta_info", &cell_each_meta_info, + "@brief Iterates over the meta information of the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + gsi::method_ext ("write", &write_simple, gsi::arg ("file_name"), "@brief Writes the cell to a layout file\n" "The format of the file will be determined from the file name. Only the cell and " diff --git a/src/db/db/gsiDeclDbDeepShapeStore.cc b/src/db/db/gsiDeclDbDeepShapeStore.cc index ad395cda3..ca4197def 100644 --- a/src/db/db/gsiDeclDbDeepShapeStore.cc +++ b/src/db/db/gsiDeclDbDeepShapeStore.cc @@ -111,6 +111,21 @@ Class decl_dbDeepShapeStore ("db", "DeepShapeStore", gsi::method ("threads", &db::DeepShapeStore::threads, "@brief Gets the number of threads.\n" ) + + gsi::method ("wants_all_cells=", &db::DeepShapeStore::set_wants_all_cells, gsi::arg ("flag"), + "@brief Sets a flag wether to copy the full hierarchy for the working layouts\n" + "\n" + "The DeepShapeStore object keeps a copy of the original hierarchy internally for the working layouts.\n" + "By default, this hierarchy is mapping only non-empty cells. While the operations proceed, more cells " + "may need to be added. This conservative approach saves some memory, but the update operations may " + "reduce overall performance. Setting this flag to 'true' switches to a mode where the full " + "hierarchy is copied always. This will take more memory but may save CPU time.\n" + "\n" + "This attribute has been introduced in version 0.28.10." + ) + + gsi::method ("wants_all_cells", &db::DeepShapeStore::wants_all_cells, + "@brief Gets a flag wether to copy the full hierarchy for the working layouts\n" + "This attribute has been introduced in version 0.28.10." + ) + gsi::method ("reject_odd_polygons=", &db::DeepShapeStore::set_reject_odd_polygons, gsi::arg ("count"), "@brief Sets a flag indicating whether to reject odd polygons\n" "\n" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index bff85e070..9da66889d 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" #include "dbLayout.h" #include "dbClip.h" #include "dbRecursiveShapeIterator.h" @@ -902,39 +903,30 @@ static db::Cell *create_cell4 (db::Layout *layout, const std::string &name, cons return &layout->cell (layout->get_lib_proxy (lib, lib_cell)); } -static db::MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description) +static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi) { - return new db::MetaInfo (name, description, value); + layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value, mi.persisted)); } -static void layout_meta_set_name (db::MetaInfo *mi, const std::string &n) +static MetaInfo *layout_get_meta_info (db::Layout *layout, const std::string &name) { - mi->name = n; + if (layout->has_meta_info (name)) { + const db::MetaInfo &value = layout->meta_info (name); + return new MetaInfo (name, value); + } else { + return 0; + } } -static const std::string &layout_meta_get_name (const db::MetaInfo *mi) +static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name) { - return mi->name; + const db::MetaInfo &value = layout->meta_info (name); + return value.value; } -static void layout_meta_set_value (db::MetaInfo *mi, const std::string &n) +static MetaInfoIterator layout_each_meta_info (const db::Layout *layout) { - mi->value = n; -} - -static const std::string &layout_meta_get_value (const db::MetaInfo *mi) -{ - return mi->value; -} - -static void layout_meta_set_description (db::MetaInfo *mi, const std::string &n) -{ - mi->description = n; -} - -static const std::string &layout_meta_get_description (const db::MetaInfo *mi) -{ - return mi->description; + return MetaInfoIterator (layout, layout->begin_meta (), layout->end_meta ()); } static void scale_and_snap1 (db::Layout *layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d) @@ -997,45 +989,6 @@ static void move_tree_shapes3 (db::Layout *layout, db::Layout &source_layout, co db::move_shapes (*layout, source_layout, trans, cm.source_cells (), cm.table (), lm.table ()); } -Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", - gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), - "@brief Creates a layout meta info object\n" - "@param name The name\n" - "@param value The value\n" - "@param description An optional description text\n" - ) + - gsi::method_ext ("name", &layout_meta_get_name, - "@brief Gets the name of the layout meta info object\n" - ) + - gsi::method_ext ("name=", &layout_meta_set_name, - "@brief Sets the name of the layout meta info object\n" - ) + - gsi::method_ext ("value", &layout_meta_get_value, - "@brief Gets the value of the layout meta info object\n" - ) + - gsi::method_ext ("value=", &layout_meta_set_value, - "@brief Sets the value of the layout meta info object\n" - ) + - gsi::method_ext ("description", &layout_meta_get_description, - "@brief Gets the description of the layout meta info object\n" - ) + - gsi::method_ext ("description=", &layout_meta_set_description, - "@brief Sets the description of the layout meta info object\n" - ), - "@brief A piece of layout meta information\n" - "Layout meta information is basically additional data that can be attached to a layout. " - "Layout readers may generate meta information and some writers will add layout information to " - "the layout object. Some writers will also read meta information to determine certain attributes.\n" - "\n" - "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. " - "Meta information is identified by a unique name and carries a string value plus an optional description string. " - "The description string is for information only and is not evaluated by code.\n" - "\n" - "See also \\Layout#each_meta_info and \\Layout#meta_info_value and \\Layout#remove_meta_info" - "\n" - "This class has been introduced in version 0.25." -); - static void dtransform (db::Layout *layout, const db::DTrans &trans) { db::CplxTrans dbu_trans (layout->dbu ()); @@ -1097,27 +1050,42 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.27.9." ) + - gsi::method ("add_meta_info", &db::Layout::add_meta_info, gsi::arg ("info"), + gsi::method_ext ("add_meta_info", &layout_add_meta_info, gsi::arg ("info"), "@brief Adds meta information to the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("remove_meta_info", &db::Layout::remove_meta_info, gsi::arg ("name"), + gsi::method ("clear_meta_info", static_cast (&db::Layout::clear_meta), + "@brief Clears the meta information of the layout\n" + "See \\LayoutMetaInfo for details about layouts and meta information." + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method ("remove_meta_info", static_cast (&db::Layout::remove_meta_info), gsi::arg ("name"), "@brief Removes meta information from the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("meta_info_value", &db::Layout::meta_info_value, gsi::arg ("name"), + gsi::method_ext ("meta_info_value", &layout_get_meta_info_value, gsi::arg ("name"), "@brief Gets the meta information value for a given name\n" "See \\LayoutMetaInfo for details about layouts and meta information.\n" "\n" - "If no meta information with the given name exists, an empty string will be returned.\n" + "If no meta information with the given name exists, a nil value will be returned.\n" + "A more generic version that delivers all fields of the meta information is \\meta_info.\n" "\n" - "This method has been introduced in version 0.25." + "This method has been introduced in version 0.25. Starting with version 0.28.8, the value is of variant type instead of string only.\n" ) + - gsi::iterator ("each_meta_info", &db::Layout::begin_meta, &db::Layout::end_meta, + gsi::factory_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"), + "@brief Gets the meta information for a given name\n" + "See \\LayoutMetaInfo for details about layouts and meta information.\n" + "\n" + "If no meta information with the given name exists, nil is returned.\n" + "\n" + "This method has been introduced in version 0.28.8.\n" + ) + + gsi::iterator_ext ("each_meta_info", &layout_each_meta_info, "@brief Iterates over the meta information of the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information.\n" "\n" @@ -1714,29 +1682,53 @@ Class decl_Layout ("db", "Layout", "@param a The first of the layers to swap.\n" "@param b The second of the layers to swap.\n" ) + - gsi::method ("move_layer", &db::Layout::move_layer, gsi::arg ("src"), gsi::arg ("dest"), + gsi::method ("move_layer", static_cast (&db::Layout::move_layer), gsi::arg ("src"), gsi::arg ("dest"), "@brief Moves a layer\n" "\n" - "This method was introduced in version 0.19.\n" - "\n" - "Move a layer from the source to the target. The target is not cleared before, so that this method \n" - "merges shapes from the source with the target layer. The source layer is empty after that operation.\n" + "Moves a layer from the source to the destination layer. The target is not cleared before, so that this method \n" + "merges shapes from the source with the destination layer. The source layer is empty after that operation.\n" "\n" "@param src The layer index of the source layer.\n" "@param dest The layer index of the destination layer.\n" + "\n" + "This method was introduced in version 0.19.\n" ) + - gsi::method ("copy_layer", &db::Layout::copy_layer, gsi::arg ("src"), gsi::arg ("dest"), + gsi::method ("move_layer", static_cast (&db::Layout::move_layer), gsi::arg ("src"), gsi::arg ("dest"), gsi::arg ("flags"), + "@brief Moves a layer (selected shape types only)\n" + "\n" + "Moves a layer from the source to the destination layer. The target is not cleared before, so that this method \n" + "merges shapes from the source with the destination layer. The copied shapes are removed from the source layer.\n" + "\n" + "@param src The layer index of the source layer.\n" + "@param dest The layer index of the destination layer.\n" + "@param flags A combination of the shape type flags from \\Shapes, S... constants\n" + "\n" + "This method variant has been introduced in version 0.28.9.\n" + ) + + gsi::method ("copy_layer", static_cast (&db::Layout::copy_layer), gsi::arg ("src"), gsi::arg ("dest"), "@brief Copies a layer\n" "\n" - "This method was introduced in version 0.19.\n" - "\n" - "Copy a layer from the source to the target. The target is not cleared before, so that this method \n" - "merges shapes from the source with the target layer.\n" + "Copies a layer from the source to the destination layer. The destination layer is not cleared before, so that this method \n" + "merges shapes from the source with the destination layer.\n" "\n" "@param src The layer index of the source layer.\n" "@param dest The layer index of the destination layer.\n" + "\n" + "This method was introduced in version 0.19.\n" ) + - gsi::method ("clear_layer", &db::Layout::clear_layer, gsi::arg ("layer_index"), + gsi::method ("copy_layer", static_cast (&db::Layout::copy_layer), gsi::arg ("src"), gsi::arg ("dest"), gsi::arg ("flags"), + "@brief Copies a layer (selected shape types only)\n" + "\n" + "Copies a layer from the source to the destination layer. The destination layer is not cleared before, so that this method \n" + "merges shapes from the source with the destination layer.\n" + "\n" + "@param src The layer index of the source layer.\n" + "@param dest The layer index of the destination layer.\n" + "@param flags A combination of the shape type flags from \\Shapes, S... constants\n" + "\n" + "This method variant has been introduced in version 0.28.9.\n" + ) + + gsi::method ("clear_layer", static_cast (&db::Layout::clear_layer), gsi::arg ("layer_index"), "@brief Clears a layer\n" "\n" "Clears the layer: removes all shapes.\n" @@ -1744,6 +1736,16 @@ Class decl_Layout ("db", "Layout", "This method was introduced in version 0.19.\n" "\n" "@param layer_index The index of the layer to delete.\n" + ) + + gsi::method ("clear_layer", static_cast (&db::Layout::clear_layer), gsi::arg ("layer_index"), gsi::arg ("flags"), + "@brief Clears a layer (given shape types only)\n" + "\n" + "Clears the layer: removes all shapes for the given shape types.\n" + "\n" + "This method was introduced in version 0.28.9.\n" + "\n" + "@param layer_index The index of the layer to delete.\n" + "@param flags The type selector for the shapes to delete (see \\Shapes class, S... constants).\n" ) + gsi::method ("delete_layer", &db::Layout::delete_layer, gsi::arg ("layer_index"), "@brief Deletes a layer\n" diff --git a/src/db/db/gsiDeclDbLayoutDiff.cc b/src/db/db/gsiDeclDbLayoutDiff.cc index 1d50c6857..1ed1335ca 100644 --- a/src/db/db/gsiDeclDbLayoutDiff.cc +++ b/src/db/db/gsiDeclDbLayoutDiff.cc @@ -397,6 +397,10 @@ static unsigned int f_silent () { return db::layout_diff::f_silent; } +static unsigned int f_ignore_duplicates () { + return db::layout_diff::f_ignore_duplicates; +} + static unsigned int f_no_text_orientation () { return db::layout_diff::f_no_text_orientation; } @@ -448,6 +452,13 @@ gsi::Class decl_LayoutDiff ("db", "LayoutDiff", "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " "compared with other constants to form a flag set." ) + + gsi::constant ("IgnoreDuplicates", &f_ignore_duplicates, + "@brief Ignore duplicate instances or shapes\n" + "With this option present, duplicate instances or shapes are ignored and " + "duplication does not count as a difference.\n" + "\n" + "This option has been introduced in version 0.28.9." + ) + gsi::constant ("NoTextOrientation", &f_no_text_orientation, "@brief Ignore text orientation\n" "This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be " diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc new file mode 100644 index 000000000..ef760d438 --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -0,0 +1,168 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" + +namespace gsi +{ + +static MetaInfo *layout_meta_info_ctor (const std::string &name, const tl::Variant &value, const std::string &description, bool persisted) +{ + return new MetaInfo (name, description, value, persisted); +} + +static void layout_meta_set_name (MetaInfo *mi, const std::string &n) +{ + mi->name = n; +} + +static const std::string &layout_meta_get_name (const MetaInfo *mi) +{ + return mi->name; +} + +static void layout_meta_set_value (MetaInfo *mi, const tl::Variant &n) +{ + mi->value = n; +} + +static const tl::Variant &layout_meta_get_value (const MetaInfo *mi) +{ + return mi->value; +} + +static void layout_meta_set_description (MetaInfo *mi, const std::string &n) +{ + mi->description = n; +} + +static const std::string &layout_meta_get_description (const MetaInfo *mi) +{ + return mi->description; +} + +static void layout_meta_set_persisted (MetaInfo *mi, bool f) +{ + mi->persisted = f; +} + +static bool layout_meta_get_persisted (const MetaInfo *mi) +{ + return mi->persisted; +} + + +Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", + gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), gsi::arg ("persisted", false), + "@brief Creates a layout meta info object\n" + "@param name The name\n" + "@param value The value\n" + "@param description An optional description text\n" + "@param persisted If true, the meta information will be persisted in some file formats, like GDS2\n" + "\n" + "The 'persisted' attribute has been introduced in version 0.28.8.\n" + ) + + gsi::method_ext ("name", &layout_meta_get_name, + "@brief Gets the name of the layout meta info object\n" + ) + + gsi::method_ext ("name=", &layout_meta_set_name, gsi::arg ("name"), + "@brief Sets the name of the layout meta info object\n" + ) + + gsi::method_ext ("value", &layout_meta_get_value, + "@brief Gets the value of the layout meta info object\n" + ) + + gsi::method_ext ("value=", &layout_meta_set_value, gsi::arg ("value"), + "@brief Sets the value of the layout meta info object\n" + ) + + gsi::method_ext ("description", &layout_meta_get_description, + "@brief Gets the description of the layout meta info object\n" + ) + + gsi::method_ext ("description=", &layout_meta_set_description, gsi::arg ("description"), + "@brief Sets the description of the layout meta info object\n" + ) + + gsi::method_ext ("is_persisted?", &layout_meta_get_persisted, + "@brief Gets a value indicating whether the meta information will be persisted\n" + "This predicate was introduced in version 0.28.8.\n" + ) + + gsi::method_ext ("persisted=", &layout_meta_set_persisted, gsi::arg ("flag"), + "@brief Sets a value indicating whether the meta information will be persisted\n" + "This predicate was introduced in version 0.28.8.\n" + ), + "@brief A piece of layout meta information\n" + "Layout meta information is basically additional data that can be attached to a layout. " + "Layout readers may generate meta information and some writers will add layout information to " + "the layout object. Some writers will also read meta information to determine certain attributes.\n" + "\n" + "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. " + "Meta information is identified by a unique name and carries a string value plus an optional description string. " + "The description string is for information only and is not evaluated by code.\n" + "\n" + "Meta information can be attached to the layout object and to cells. It is similar to " + "user properties. The differences are:\n" + "\n" + "@ul\n" + "@li Meta information is stored differently in GDS and OASIS files using the context information added " + " by KLayout to annotated PCell or library cells too. Hence meta information does not pollute " + " the standard user properties space. @/li\n" + "@li The value of meta information can be complex serializable types such as lists, hashes and elementary " + " objects such as \\Box or \\DBox. Scalar types include floats and booleans. @/li\n" + "@li Meta information keys are strings and are supported also for GDS which only accepts integer number " + " keys for user properties. @/li\n" + "@/ul\n" + "\n" + "Elementary (serializable) objects are: \\Box, \\DBox, \\Edge, \\DEdge, \\EdgePair, \\DEdgePair, " + "\\EdgePairs, \\Edges, \\LayerProperties, \\Matrix2d, \\Matrix3d, \\Path, \\DPath, \\Point, \\DPoint, " + "\\Polygon, \\DPolygon, \\SimplePolygon, \\DSimplePolygon, \\Region, \\Text, \\DText, \\Texts, " + "\\Trans, \\DTrans, \\CplxTrans, \\ICplxTrans, \\DCplxTrans, \\VCplxTrans, \\Vector, \\DVector " + "(list may not be complete).\n" + "\n" + "KLayout itself also generates meta information with specific keys. " + "For disambiguation, namespaces can be established by prefixing " + "the key strings with some unique identifier in XML fashion, like a domain name - " + "e.g. 'example.com:key'.\n" + "\n" + "@b Note: @/b only meta information marked with \\is_persisted? == true is stored in GDS or OASIS files. " + "This is not the default setting, so you need to explicitly set that flag.\n" + "\n" + "See also \\Layout#each_meta_info, \\Layout#meta_info_value, \\Layout#meta_info and \\Layout#remove_meta_info as " + "well as the corresponding \\Cell methods.\n" + "\n" + "An example of how to attach persisted meta information to a cell is here:\n" + "\n" + "@code\n" + "ly = RBA::Layout::new\n" + "c1 = ly.create_cell(\"C1\")\n" + "\n" + "mi = RBA::LayoutMetaInfo::new(\"the-answer\", 42.0)\n" + "mi.persisted = true\n" + "c1.add_meta_info(mi)\n" + "\n" + "# will now hold this piece of meta information attached to cell 'C1':\n" + "ly.write(\"to.gds\")\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.25 and was extended in version 0.28.8." +); + +} diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h new file mode 100644 index 000000000..84bbadc0a --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.h @@ -0,0 +1,100 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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_gsiDeclDbMetaInfo +#define _HDR_gsiDeclDbMetaInfo + +#include "dbLayout.h" +#include "tlVariant.h" +#include "tlObject.h" + +#include +#include + +namespace gsi +{ + +struct MetaInfo +{ + MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v, bool p) + : name (n), description (d), value (v), persisted (p) + { } + + MetaInfo (const std::string &n, const db::MetaInfo &mi) + : name (n), description (mi.description), value (mi.value), persisted (mi.persisted) + { } + + MetaInfo () + : name (), description (), value (), persisted (false) + { } + + std::string name; + std::string description; + tl::Variant value; + bool persisted; +}; + +struct MetaInfoIterator +{ + typedef std::forward_iterator_tag iterator_category; + typedef MetaInfo value_type; + typedef void difference_type; + typedef MetaInfo reference; + typedef void pointer; + + MetaInfoIterator () + : mp_layout (), m_b (), m_e () + { } + + MetaInfoIterator (const db::Layout *layout, db::Layout::meta_info_iterator b, db::Layout::meta_info_iterator e) + : mp_layout (const_cast (layout)), m_b (b), m_e (e) + { } + + bool at_end () const + { + return !mp_layout || m_b == m_e; + } + + void operator++ () + { + if (mp_layout) { + ++m_b; + } + } + + MetaInfo operator* () const + { + if (mp_layout) { + return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second); + } else { + return MetaInfo (); + } + } + +private: + tl::weak_ptr mp_layout; + db::Layout::meta_info_iterator m_b, m_e; +}; + +} + +#endif diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 4b3d1e7bd..eaa36e98a 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -926,6 +926,11 @@ struct polygon_defs return c->insert_hole (&pts[0], &pts[0] + sizeof (pts) / sizeof (pts[0])); } + static void sort_holes (C *c) + { + c->sort_holes (); + } + static C *from_string (const char *s) { tl::Extractor ex (s); @@ -1249,6 +1254,14 @@ struct polygon_defs "@brief Iterates over the points that make up the nth hole\n" "The hole number must be less than the number of holes (see \\holes)" ) + + method_ext ("sort_holes", &sort_holes, + "@brief Brings the holes in a specific order\n" + "This function is normalize the hole order so the comparison of two " + "polygons does not depend on the order the holes were inserted. " + "Polygons generated by KLayout's alorithms have their holes sorted.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + method_ext ("size", &size_xy, gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("mode"), "@brief Sizes the polygon (biasing)\n" "\n" diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index e711fe0c6..33f1a90c5 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -108,47 +108,47 @@ static gsi::layout_locking_iterator1 begin (const db return gsi::layout_locking_iterator1 (s->layout (), s->begin (flags)); } -static gsi::layout_locking_iterator1begin_all (const db::Shapes *s) +static gsi::layout_locking_iterator1 begin_all (const db::Shapes *s) { return gsi::layout_locking_iterator1 (s->layout (), s->begin (db::ShapeIterator::All)); } -static gsi::layout_locking_iterator1begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box ®ion) +static gsi::layout_locking_iterator1 begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (region, flags)); } -static gsi::layout_locking_iterator1begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) +static gsi::layout_locking_iterator1 begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags)); } -static gsi::layout_locking_iterator1begin_overlapping_all (const db::Shapes *s, const db::Box ®ion) +static gsi::layout_locking_iterator1 begin_overlapping_all (const db::Shapes *s, const db::Box ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (region, db::ShapeIterator::All)); } -static gsi::layout_locking_iterator1begin_doverlapping_all (const db::Shapes *s, const db::DBox ®ion) +static gsi::layout_locking_iterator1 begin_doverlapping_all (const db::Shapes *s, const db::DBox ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All)); } -static gsi::layout_locking_iterator1begin_touching (const db::Shapes *s, unsigned int flags, const db::Box ®ion) +static gsi::layout_locking_iterator1 begin_touching (const db::Shapes *s, unsigned int flags, const db::Box ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (region, flags)); } -static gsi::layout_locking_iterator1begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) +static gsi::layout_locking_iterator1 begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags)); } -static gsi::layout_locking_iterator1begin_touching_all (const db::Shapes *s, const db::Box ®ion) +static gsi::layout_locking_iterator1 begin_touching_all (const db::Shapes *s, const db::Box ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (region, db::ShapeIterator::All)); } -static gsi::layout_locking_iterator1begin_dtouching_all (const db::Shapes *s, const db::DBox ®ion) +static gsi::layout_locking_iterator1 begin_dtouching_all (const db::Shapes *s, const db::DBox ®ion) { return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All)); } @@ -251,12 +251,7 @@ static void insert_shapes (db::Shapes *sh, const db::Shapes &s) static void insert_shapes_with_flags (db::Shapes *sh, const db::Shapes &s, unsigned int flags) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); - for (db::Shapes::shape_iterator i = s.begin (flags); !i.at_end(); ++i) { - sh->insert (*i); - } + sh->insert (s, flags); } static void insert_shapes_with_trans (db::Shapes *sh, const db::Shapes &s, const db::ICplxTrans &trans) @@ -1264,9 +1259,15 @@ Class decl_Shapes ("db", "Shapes", "@brief Returns a value indicating whether the shapes container is empty\n" "This method has been introduced in version 0.20.\n" ) + - gsi::method ("clear", &db::Shapes::clear, + gsi::method ("clear", static_cast (&db::Shapes::clear), "@brief Clears the shape container\n" - "This method has been introduced in version 0.16. It can only be used in editable mode." + "This method has been introduced in version 0.16." + ) + + gsi::method ("clear", static_cast (&db::Shapes::clear), gsi::arg ("flags"), + "@brief Clears certain shape types from the shape container\n" + "Only shapes matching the shape types from 'flags' are removed. 'flags' is a combination of the S... constants.\n" + "\n" + "This method has been introduced in version 0.28.9." ) + gsi::method_ext ("size", &shapes_size, "@brief Gets the number of shapes in this container\n" @@ -1296,10 +1297,19 @@ Class decl_Shapes ("db", "Shapes", "returned for future references." ) + gsi::method ("SAll|#s_all", &s_all, - "@brief Indicates that all shapes shall be retrieved" + "@brief Indicates that all shapes shall be retrieved\n" + "You can use this constant to construct 'except' classes - e.g. " + "to specify 'all shape types except boxes' use\n" + "\n" + "@code SAll - SBoxes @/code\n" ) + gsi::method ("SAllWithProperties|#s_all_with_properties", &s_all_with_properties, - "@brief Indicates that all shapes with properties shall be retrieved" + "@brief Indicates that all shapes with properties shall be retrieved\n" + "Using this selector means to retrieve only shapes with properties." + "You can use this constant to construct 'except' classes - e.g. " + "to specify 'all shape types with properties except boxes' use\n" + "\n" + "@code SAllWithProperties - SBoxes @/code\n" ) + gsi::method ("SPolygons|#s_polygons", &s_polygons, "@brief Indicates that polygons shall be retrieved" @@ -1333,7 +1343,10 @@ Class decl_Shapes ("db", "Shapes", "@brief Indicates that user objects shall be retrieved" ) + gsi::method ("SProperties|#s_properties", &s_properties, - "@brief Indicates that only shapes with properties shall be retrieved" + "@brief Indicates that only shapes with properties shall be retrieved\n" + "You can or-combine this flag with the plain shape types to select a " + "certain shape type, but only those shapes with properties. For example to " + "select boxes with properties, use 'SProperties | SBoxes'." ) + gsi::method_ext ("dump_mem_statistics", &dump_mem_statistics, gsi::arg ("detailed", false), "@hide" diff --git a/src/db/unit_tests/dbBoxScannerTests.cc b/src/db/unit_tests/dbBoxScannerTests.cc index 4d6de4959..9d772f340 100644 --- a/src/db/unit_tests/dbBoxScannerTests.cc +++ b/src/db/unit_tests/dbBoxScannerTests.cc @@ -938,6 +938,7 @@ TEST(two_1) db::box_convert bc1; db::box_convert bc2; bs.set_scanner_threshold (0); + bs.set_scanner_threshold1 (0); bs.process (tr, 1, bc1, bc2); EXPECT_EQ (tr.str, "[i](2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>[f]"); } @@ -974,6 +975,7 @@ TEST(two_1a) db::box_convert bc1; db::box_convert bc2; bs.set_scanner_threshold (0); + bs.set_scanner_threshold1 (0); bs.process (tr, 1, bc1, bc2); EXPECT_EQ (tr.str, "[i](2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>[f]"); } @@ -1010,6 +1012,7 @@ TEST(two_1b) db::box_convert bc1; db::box_convert bc2; bs.set_scanner_threshold (0); + bs.set_scanner_threshold1 (0); EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); EXPECT_EQ (tr.str, "[i](1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>[f]"); @@ -1046,14 +1049,15 @@ TEST(two_1c) db::box_convert bc1; db::box_convert bc2; bs.set_scanner_threshold (0); + bs.set_scanner_threshold1 (0); EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); EXPECT_EQ (tr.str, "[i]<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>[f]"); } -void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) +void run_test2_two (tl::TestBase *_this, size_t n1, size_t n2, double ff, db::Coord spread, bool touch = true, bool no_shortcut = true) { std::vector bb; - for (size_t i = 0; i < n; ++i) { + for (size_t i = 0; i < n1; ++i) { db::Coord x = rand () % spread; db::Coord y = rand () % spread; bb.push_back (db::Box (x, y, x + 100, y + 100)); @@ -1061,7 +1065,7 @@ void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, } std::vector bb2; - for (size_t i = 0; i < n; ++i) { + for (size_t i = 0; i < n2; ++i) { db::Coord x = rand () % spread; db::Coord y = rand () % spread; bb2.push_back (db::SimplePolygon (db::Box (x, y, x + 100, y + 100))); @@ -1082,7 +1086,10 @@ void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, db::box_convert bc2; { tl::SelfTimer timer ("box-scanner"); - bs.set_scanner_threshold (0); + if (no_shortcut) { + bs.set_scanner_threshold (0); + bs.set_scanner_threshold1 (0); + } bs.process (tr, touch ? 1 : 0, bc1, bc2); } @@ -1118,45 +1125,60 @@ void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, TEST(two_2a) { - run_test2_two(_this, 10, 0.0, 1000); + run_test2_two(_this, 10, 10, 0.0, 1000); + run_test2_two(_this, 10, 10, 0.0, 1000, true, false /*sub-threshold*/); } TEST(two_2b) { - run_test2_two(_this, 10, 0.0, 100); + run_test2_two(_this, 10, 10, 0.0, 100); + run_test2_two(_this, 10, 10, 0.0, 100, true, false /*sub-threshold*/); } TEST(two_2c) { - run_test2_two(_this, 10, 0.0, 10); + run_test2_two(_this, 10, 10, 0.0, 10); + run_test2_two(_this, 10, 10, 0.0, 10, true, false /*sub-threshold*/); } TEST(two_2d) { - run_test2_two(_this, 1000, 0.0, 1000); + run_test2_two(_this, 1000, 1000, 0.0, 1000); } TEST(two_2e) { - run_test2_two(_this, 1000, 2, 1000); + run_test2_two(_this, 1000, 1000, 2, 1000); } TEST(two_2f) { - run_test2_two(_this, 1000, 2, 1000, false); + run_test2_two(_this, 1000, 1000, 2, 1000, false); } TEST(two_2g) { - run_test2_two(_this, 1000, 2, 500); + run_test2_two(_this, 1000, 1000, 2, 500); } TEST(two_2h) { - run_test2_two(_this, 1000, 2, 100); + run_test2_two(_this, 1000, 1000, 2, 100); } TEST(two_2i) { - run_test2_two(_this, 10000, 2, 10000); + run_test2_two(_this, 10000, 1000, 2, 10000); +} + +TEST(two_2j) +{ + run_test2_two(_this, 3, 1000, 0.0, 1000); + run_test2_two(_this, 3, 1000, 0.0, 1000, true, false /*sub-threshold*/); +} + +TEST(two_2k) +{ + run_test2_two(_this, 1000, 3, 0.0, 1000); + run_test2_two(_this, 1000, 3, 0.0, 1000, true, false /*sub-threshold*/); } diff --git a/src/db/unit_tests/dbEdgeProcessorTests.cc b/src/db/unit_tests/dbEdgeProcessorTests.cc index 238b854c9..ccadcd57e 100644 --- a/src/db/unit_tests/dbEdgeProcessorTests.cc +++ b/src/db/unit_tests/dbEdgeProcessorTests.cc @@ -2781,3 +2781,40 @@ TEST(135b) 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)"); } + +// issue #1366 +TEST(136) +{ + db::Layout layout_1; + unsigned int l_l20000d0; + + { + std::string fn (tl::testdata ()); + fn += "/bool/"; + fn += "issue_1366.oas"; + tl::InputStream stream (fn); + db::Reader reader (stream); + + db::LoadLayoutOptions options; + reader.read (layout_1, options); + + l_l20000d0 = layout_1.get_layer (db::LayerProperties (20000, 0)); + } + + db::ShapeProcessor proc; + + db::Layout lr; + lr.dbu (0.0001); + db::Cell *lr_top = &lr.cell (lr.add_cell ("TOP")); + + unsigned int lr_l100d0 = lr.insert_layer (db::LayerProperties (100, 0)); + + proc.merge (layout_1, layout_1.cell (*layout_1.begin_top_down ()), l_l20000d0, + lr_top->shapes (lr_l100d0), false /*hierarchical*/, 0, true /*resolve holes*/, true /*min coherence*/); + + std::string au_fn (tl::testdata ()); + au_fn += "/bool/"; + au_fn += "issue_1366_au.gds"; + + db::compare_layouts (_this, lr, au_fn); +} diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 7e9fe165f..504f36c2e 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -1278,3 +1278,9 @@ TEST(FlatOperation) run_test_bool22_flat (_this, "hlp17_flat.oas", TMAndNot, 100, 101); } +TEST(Arrays) +{ + // Large arrays, NOT + run_test_bool2 (_this, "hlp18.oas", TMNot, 100); +} + diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc index 9419bacdb..739b9a77d 100644 --- a/src/db/unit_tests/dbLayoutDiffTests.cc +++ b/src/db/unit_tests/dbLayoutDiffTests.cc @@ -66,6 +66,9 @@ public: void begin_edge_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_edge_differences (); + void begin_edge_pair_differences (); + void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); + void end_edge_pair_differences (); void begin_text_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_text_differences (); @@ -102,6 +105,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties & } if (ci.properties_id () != 0) { m_os << " [" << ci.properties_id () << "]" << std::endl; + } else { + m_os << "" << std::endl; } } @@ -339,6 +344,26 @@ TestDifferenceReceiver::end_edge_differences () { } +void +TestDifferenceReceiver::begin_edge_pair_differences () +{ + m_os << "layout_diff: edge pairs differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl; +} + +void +TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b) +{ + m_os << "Not in b but in a:" << std::endl; + print_diffs (pr, a, b); + m_os << "Not in a but in b:" << std::endl; + print_diffs (pr, b, a); +} + +void +TestDifferenceReceiver::end_edge_pair_differences () +{ +} + void TestDifferenceReceiver::begin_text_differences () { @@ -466,7 +491,9 @@ TEST(1) " c4 m45 *1 -10,20\n" " c4 m45 *1 -10,20\n" "Not in b but in a:\n" - " c5x r0 *1 10,-20 c5x m45 *1 -10,20Not in a but in b:\n" + " c5x r0 *1 10,-20\n" + " c5x m45 *1 -10,20\n" + "Not in a but in b:\n" ); g = h; @@ -928,7 +955,7 @@ TEST(3) c2h.shapes (0).insert (db::Polygon (db::Box (1, 2, 1003, 1006))); r.clear (); - eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); + eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); EXPECT_EQ (eq, false); EXPECT_EQ (r.text (), @@ -942,7 +969,7 @@ TEST(3) ); r.clear (); - eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); + eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); EXPECT_EQ (eq, false); EXPECT_EQ (r.text (), @@ -1476,4 +1503,205 @@ TEST(7) EXPECT_EQ (r.text (), ""); } +TEST(8) +{ + db::Layout g; + g.insert_layer (0); + g.set_properties (0, db::LayerProperties (17, 0)); + g.insert_layer (1); + g.set_properties (1, db::LayerProperties (42, 1)); + + db::cell_index_type c1i = g.add_cell ("c1"); + db::cell_index_type c2i = g.add_cell ("c2x"); + db::cell_index_type c3i = g.add_cell ("c3"); + db::cell_index_type c4i = g.add_cell ("c4"); + db::cell_index_type c5i = g.add_cell ("c5x"); + + { + + db::Cell &c1 (g.cell (c1i)); + db::Cell &c2 (g.cell (c2i)); + db::Cell &c3 (g.cell (c3i)); + db::Cell &c4 (g.cell (c4i)); + db::Cell &c5 (g.cell (c5i)); + c2.shapes (0).insert (db::Box (0, 1, 2, 3)); + + db::FTrans f (1, true); + db::Vector p (-10, 20); + db::Trans t (f.rot (), p); + db::Vector pp (10, -20); + db::Trans tt (0, pp); + + // c4->c1 (aref) + c4.insert (db::array (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3)); + // c5->c1 + c5.insert (db::array (db::CellInst (c1.cell_index ()), t)); + // c3->c5 (3x) + c3.insert (db::array (db::CellInst (c5.cell_index ()), t)); + c3.insert (db::array (db::CellInst (c5.cell_index ()), tt)); + c3.insert (db::array (db::CellInst (c5.cell_index ()), t)); + // c4->c3 + c4.insert (db::array (db::CellInst (c3.cell_index ()), t)); + // c4->c1 + c4.insert (db::array (db::CellInst (c1.cell_index ()), tt)); + // c2->c1 (2x) + c2.insert (db::array (db::CellInst (c1.cell_index ()), t)); + c2.insert (db::array (db::CellInst (c1.cell_index ()), tt)); + // c2->c4 (2x) + c2.insert (db::array (db::CellInst (c4.cell_index ()), t)); + c2.insert (db::array (db::CellInst (c4.cell_index ()), t)); + + } + + db::Layout h = g; + + TestDifferenceReceiver r; + bool eq; + + g.cell (c2i).shapes (0).insert (db::Box (1, 2, 1001, 1002)); + g.cell (c2i).shapes (0).insert (db::Box (2, 3, 1002, 1003)); + g.cell (c2i).shapes (0).insert (db::Box (2, 3, 1002, 1003)); + g.cell (c2i).shapes (0).insert (db::Box (3, 4, 1003, 1004)); + g.cell (c2i).shapes (0).insert (db::Box (3, 4, 1003, 1004)); + + h.cell (c2i).shapes (0).insert (db::Box (1, 2, 1001, 1002)); + h.cell (c2i).shapes (0).insert (db::Box (1, 2, 1001, 1002)); + h.cell (c2i).shapes (0).insert (db::Box (2, 3, 1002, 1003)); + h.cell (c2i).shapes (0).insert (db::Box (4, 5, 1004, 1005)); + h.cell (c2i).shapes (0).insert (db::Box (4, 5, 1004, 1005)); + + r.clear (); + eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); + + EXPECT_EQ (eq, false); + EXPECT_EQ (r.text (), + "layout_diff: boxes differ for layer 17/0 in cell c2x\n" + "Not in b but in a:\n" + " (2,3;1002,1003)\n" + " (3,4;1003,1004)\n" + " (3,4;1003,1004)\n" + "Not in a but in b:\n" + " (1,2;1001,1002)\n" + " (4,5;1004,1005)\n" + " (4,5;1004,1005)\n" + ); + + r.clear (); + eq = db::compare_layouts (g, h, db::layout_diff::f_verbose + db::layout_diff::f_ignore_duplicates, 0, r); + + EXPECT_EQ (eq, false); + EXPECT_EQ (r.text (), + "layout_diff: boxes differ for layer 17/0 in cell c2x\n" + "Not in b but in a:\n" + " (3,4;1003,1004)\n" + "Not in a but in b:\n" + " (4,5;1004,1005)\n" + ); + + // duplicate instances + { + db::FTrans f (1, true); + db::Vector p (-10, 20); + db::Trans t (f.rot (), p); + + h.cell(c4i).insert (db::array (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3)); + h.cell(c4i).insert (db::array (db::CellInst (c1i), t)); + h.cell(c4i).insert (db::array (db::CellInst (c1i), t)); + + g.cell(c5i).insert (db::array (db::CellInst (c1i), t)); + g.cell(c5i).insert (db::array (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3)); + g.cell(c5i).insert (db::array (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3)); + + db::cell_index_type c6i = g.add_cell ("c6"); + g.cell(c5i).insert (db::array (db::CellInst (c6i), t)); + g.cell(c5i).insert (db::array (db::CellInst (c6i), t)); + + } + + r.clear (); + eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); + + EXPECT_EQ (eq, false); + EXPECT_EQ (r.text (), + "layout_diff: cell c6 is not present in layout b, but in a\n" + "layout_diff: boxes differ for layer 17/0 in cell c2x\n" + "Not in b but in a:\n" + " (2,3;1002,1003)\n" + " (3,4;1003,1004)\n" + " (3,4;1003,1004)\n" + "Not in a but in b:\n" + " (1,2;1001,1002)\n" + " (4,5;1004,1005)\n" + " (4,5;1004,1005)\n" + "layout_diff: instances differ in cell c4\n" + "list for a:\n" + " c1 r0 *1 10,-20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c3 m45 *1 -10,20\n" + "list for b:\n" + " c1 r0 *1 10,-20\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c3 m45 *1 -10,20\n" + "Not in b but in a:\n" + "Not in a but in b:\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + "layout_diff: instances differ in cell c5x\n" + "list for a:\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + "list for b:\n" + " c1 m45 *1 -10,20\n" + "Not in b but in a:\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c6 m45 *1 -10,20\n" + " c6 m45 *1 -10,20\n" + "Not in a but in b:\n" + ); + + r.clear (); + eq = db::compare_layouts (g, h, db::layout_diff::f_verbose + db::layout_diff::f_ignore_duplicates, 0, r); + + EXPECT_EQ (eq, false); + EXPECT_EQ (r.text (), + "layout_diff: cell c6 is not present in layout b, but in a\n" + "layout_diff: boxes differ for layer 17/0 in cell c2x\n" + "Not in b but in a:\n" + " (3,4;1003,1004)\n" + "Not in a but in b:\n" + " (4,5;1004,1005)\n" + "layout_diff: instances differ in cell c4\n" + "list for a:\n" + " c1 r0 *1 10,-20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c3 m45 *1 -10,20\n" + "list for b:\n" + " c1 r0 *1 10,-20\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c3 m45 *1 -10,20\n" + "Not in b but in a:\n" + "Not in a but in b:\n" + " c1 m45 *1 -10,20\n" + "layout_diff: instances differ in cell c5x\n" + "list for a:\n" + " c1 m45 *1 -10,20\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + "list for b:\n" + " c1 m45 *1 -10,20\n" + "Not in b but in a:\n" + " c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n" + " c6 m45 *1 -10,20\n" + "Not in a but in b:\n" + ); +} + diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index e159ec41c..3b438bce5 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -543,7 +543,7 @@ TEST(5) db::Layout l (&m); EXPECT_EQ (l.technology_name (), ""); - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; info.lib_name = "LIB"; info.cell_name = "LIBCELL"; @@ -624,7 +624,7 @@ TEST(6) EXPECT_EQ (l.technology_name (), ""); - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; info.lib_name = "Basic"; info.pcell_name = "CIRCLE"; info.pcell_parameters ["actual_radius"] = tl::Variant (10.0); @@ -644,7 +644,7 @@ TEST(6) EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n"); - db::ProxyContextInfo info2; + db::LayoutOrCellContextInfo info2; l.get_context_info (cell->cell_index (), info2); info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0); @@ -677,7 +677,7 @@ TEST(7_LayerProperties) db::Layout l (&m); EXPECT_EQ (l.is_valid_layer (0), false); - EXPECT_EQ (l.guiding_shape_layer (), 0); + EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0); EXPECT_EQ (l.is_special_layer (0), true); EXPECT_EQ (int (l.layers ()), 1); @@ -737,3 +737,61 @@ TEST(7_LayerProperties) EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1); EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1); } + +TEST(8_MetaInfo) +{ + db::Layout ly; + + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1); + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.has_context_info (), false); + + ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false)); + ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true)); + + EXPECT_EQ (ly.has_context_info (), true); + + EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info ("x").description, ""); + EXPECT_EQ (ly.meta_info ("x").persisted, false); + + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (ly.meta_info ("a").description, "description"); + EXPECT_EQ (ly.meta_info ("a").persisted, false); + + EXPECT_EQ (ly.meta_info (1).value.to_string (), "value"); + EXPECT_EQ (ly.meta_info (1).description, ""); + EXPECT_EQ (ly.meta_info (1).persisted, true); + + db::cell_index_type ci = ly.add_cell ("X"); + + EXPECT_EQ (ly.has_context_info (ci), false); + + ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false)); + ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true)); + + EXPECT_EQ (ly.has_context_info (ci), true); + + EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info (ci, "x").description, ""); + EXPECT_EQ (ly.meta_info (ci, "x").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1"); + EXPECT_EQ (ly.meta_info (ci, "a").description, "dd"); + EXPECT_EQ (ly.meta_info (ci, "a").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u"); + EXPECT_EQ (ly.meta_info (ci, 1).description, "d"); + EXPECT_EQ (ly.meta_info (ci, 1).persisted, true); + + EXPECT_EQ (ly.has_context_info (), true); + ly.clear_meta (); + EXPECT_EQ (ly.has_context_info (), false); + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil"); + + EXPECT_EQ (ly.has_context_info (ci), true); + ly.clear_meta (ci); + EXPECT_EQ (ly.has_context_info (ci), false); + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil"); +} diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index d82d3b02e..a2da1d6ce 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -500,7 +500,7 @@ TEST(11_ErrorOnCircuitRedefinition) msg = ex.msg (); } - EXPECT_EQ (tl::replaced (msg, path, "?"), "Redefinition of circuit SUBCKT in ?, line 20"); + EXPECT_EQ (tl::replaced (msg, tl::absolute_file_path (path), "?"), "Redefinition of circuit SUBCKT in ?, line 20"); } TEST(12_IgnoreDuplicateGlobals) @@ -580,7 +580,7 @@ TEST(14_IncludeWithError) reader.read (is, nl); EXPECT_EQ (true, false); // must not happen } catch (tl::Exception &ex) { - EXPECT_EQ (ex.msg (), "'M' element must have four nodes in " + std::string (tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader14x.cir")) + ", line 3"); + EXPECT_EQ (ex.msg (), "'M' element must have four nodes in " + std::string (tl::absolute_file_path (tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader14x.cir"))) + ", line 3"); } } @@ -752,6 +752,90 @@ TEST(19_ngspice_ref) ); } +// using parameters evaluated before inside formulas +TEST(19b_ngspice_ref) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader19b.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit .TOP ();\n" + " subcircuit 'PMOS4_STANDARD(L=0.15,NF=4,V=1.5)' XPMOS (D=Q,G=I,S=VDD,B=VDD);\n" + " subcircuit 'NMOS4_STANDARD(L=0.15,NF=4,V=1.5)' XNMOS (D=Q,G=I,S=VSS,B=VSS);\n" + " subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY0 (D=VSS,G=VSS,S=VSS,B=VSS);\n" + " subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY1 (D=VSS,G=VSS,S=VSS,B=VSS);\n" + " subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY2 (D=VDD,G=VDD,S=VDD,B=VDD);\n" + " subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n" + "end;\n" + "circuit 'PMOS4_STANDARD(L=0.15,NF=4,V=1.5)' (D=D,G=G,S=S,B=B);\n" + " device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=3.99,PD=2.66);\n" + "end;\n" + "circuit 'NMOS4_STANDARD(L=0.15,NF=4,V=1.5)' (D=D,G=G,S=S,B=B);\n" + " device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=3.99,PD=2.66);\n" + "end;\n" + "circuit 'NMOS4_STANDARD(L=0.15,NF=2,V=1.5)' (D=D,G=G,S=S,B=B);\n" + " device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=4.16,PD=2.08);\n" + "end;\n" + "circuit 'PMOS4_STANDARD(L=0.15,NF=2,V=1.5)' (D=D,G=G,S=S,B=B);\n" + " device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=4.16,PD=2.08);\n" + "end;\n" + ); +} + +// issue #1319, clarification +TEST(20_precendence) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader20.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit TEST ();\n" + " device CAP '1' (A=A,B=B) (C=5e-12,A=0,P=0);\n" + " device CAP '2A' (A=A,B=B) (C=2.5e-12,A=0,P=0);\n" + " device CAP '2B' (A=A,B=B) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL1 '3' (A=A,B=B) (C=5e-12,A=0,P=0);\n" + " device CAP3 '4' (A=A,B=B,W=CAP_MODEL1) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '5' (A=A,B=B,W=C) (C=5e-12,A=0,P=0);\n" + " device CAP_MODEL1 '6' (A=A,B=B) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '7A' (A=A,B=B,W=C) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '7B' (A=A,B=B,W=C) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '8' (A=A,B=B,W=C) (C=2.5e-12,A=0,P=0);\n" + "end;\n" + ); +} + +// issue #1320, .lib support +TEST(21_lib) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader21.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit .TOP ();\n" + " device CAP '10' (A='1',B='2') (C=1e-12,A=0,P=0);\n" + " device CAP '1' (A='1',B='2') (C=1e-10,A=0,P=0);\n" + " device CAP '2A' (A='1',B='2') (C=1.01e-10,A=0,P=0);\n" + " device CAP '2B' (A='1',B='2') (C=1.02e-10,A=0,P=0);\n" + " device CAP '100' (A='1',B='2') (C=1.5e-11,A=0,P=0);\n" + "end;\n" + ); +} + TEST(100_ExpressionParser) { std::map vars; diff --git a/src/db/unit_tests/dbPolygonTests.cc b/src/db/unit_tests/dbPolygonTests.cc index c76e9630f..000b50f02 100644 --- a/src/db/unit_tests/dbPolygonTests.cc +++ b/src/db/unit_tests/dbPolygonTests.cc @@ -128,6 +128,7 @@ TEST(1) db::Polygon pp; pp.insert_hole (c3.begin (), c3.end ()); pp.insert_hole (c2.begin (), c2.end ()); + pp.sort_holes (); pp.assign_hull (c1.begin (), c1.end ()); EXPECT_EQ (pp.area (), 1000*100-2*380*80); EXPECT_EQ (pp.area2 (), 2*(1000*100-2*380*80)); diff --git a/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc b/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc index d95615e07..07cb5fa26 100644 --- a/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc @@ -24,12 +24,14 @@ #include "dbRecursiveInstanceIterator.h" #include "dbRegion.h" #include "dbLayoutDiff.h" +#include "dbReader.h" #include "tlString.h" #include "tlUnitTest.h" +#include "tlStream.h" #include -std::string collect(db::RecursiveInstanceIterator &s, const db::Layout &layout) +std::string collect (db::RecursiveInstanceIterator &s, const db::Layout &layout) { std::string res; while (! s.at_end ()) { @@ -47,13 +49,26 @@ std::string collect(db::RecursiveInstanceIterator &s, const db::Layout &layout) return res; } -std::string collect_with_copy(db::RecursiveInstanceIterator s, const db::Layout &layout) +std::string collect_with_copy (db::RecursiveInstanceIterator s, const db::Layout &layout) { s.reset (); return collect (s, layout); } -TEST(1) +std::string collect2 (db::RecursiveInstanceIterator &s, const db::Layout &layout) +{ + std::string res; + while (! s.at_end ()) { + if (! res.empty ()) { + res += "\n"; + } + res += std::string (layout.cell_name (s->inst_ptr.cell_index ())) + "@" + (s.trans () * s.instance ().complex_trans ()).to_string (); + ++s; + } + return res; +} + +TEST(1) { db::Manager m (true); db::Layout g (&m); @@ -660,3 +675,117 @@ TEST(5) x = collect(i1, *g); EXPECT_EQ (x, ""); } + +// issue-1353 +TEST(6) +{ + db::Layout layout; + + { + std::string fn (tl::testdata ()); + fn += "/gds/issue-1353.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout); + } + + std::string x; + + db::cell_index_type c1 = layout.cell_by_name ("TOP_CELL_3_C").second; + db::cell_index_type c2 = layout.cell_by_name ("TOP_CELL_3_B").second; + db::RecursiveInstanceIterator i1 (layout, layout.cell (c1)); + + x = collect2(i1, layout); + EXPECT_EQ (x, + // depth-first traversal + "CHILD_CELL_3_1_1@r0 *1 30000,0\n" + "CHILD_CELL_3_1@r0 *1 30000,0\n" + "CHILD_CELL_3@r0 *1 30000,0\n" + "CHILD_CELL_3_1_1@r0 *1 55000,0\n" + "CHILD_CELL_3_1@r0 *1 55000,0\n" + "CHILD_CELL_3@r0 *1 55000,0\n" + "CHILD_CELL_3_1_1@r0 *1 55000,20000\n" + "CHILD_CELL_3_1@r0 *1 55000,20000\n" + "CHILD_CELL_3@r0 *1 55000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 55000,40000\n" + "CHILD_CELL_3_1@r0 *1 55000,40000\n" + "CHILD_CELL_3@r0 *1 55000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 75000,0\n" + "CHILD_CELL_3_1@r0 *1 75000,0\n" + "CHILD_CELL_3@r0 *1 75000,0\n" + "CHILD_CELL_3_1_1@r0 *1 75000,20000\n" + "CHILD_CELL_3_1@r0 *1 75000,20000\n" + "CHILD_CELL_3@r0 *1 75000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 75000,40000\n" + "CHILD_CELL_3_1@r0 *1 75000,40000\n" + "CHILD_CELL_3@r0 *1 75000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 95000,0\n" + "CHILD_CELL_3_1@r0 *1 95000,0\n" + "CHILD_CELL_3@r0 *1 95000,0\n" + "CHILD_CELL_3_1_1@r0 *1 95000,20000\n" + "CHILD_CELL_3_1@r0 *1 95000,20000\n" + "CHILD_CELL_3@r0 *1 95000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 95000,40000\n" + "CHILD_CELL_3_1@r0 *1 95000,40000\n" + "CHILD_CELL_3@r0 *1 95000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 30000,20000\n" + "CHILD_CELL_3_1@r0 *1 30000,20000\n" + "CHILD_CELL_3@r0 *1 30000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 30000,40000\n" + "CHILD_CELL_3_1@r0 *1 30000,40000\n" + "CHILD_CELL_3@r0 *1 30000,40000" + ); + + std::set t; + t.insert (layout.cell_by_name ("TOP_CELL_3_1_1").second); + i1.set_targets (t); + + x = collect2(i1, layout); + EXPECT_EQ (x, + "CHILD_CELL_3_1_1@r0 *1 30000,0\n" + "CHILD_CELL_3_1_1@r0 *1 55000,0\n" + "CHILD_CELL_3_1_1@r0 *1 55000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 55000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 75000,0\n" + "CHILD_CELL_3_1_1@r0 *1 75000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 75000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 95000,0\n" + "CHILD_CELL_3_1_1@r0 *1 95000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 95000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 30000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 30000,40000" + ); + + db::RecursiveInstanceIterator i2 (layout, layout.cell (c2)); + i2.set_targets (t); + + x = collect2(i2, layout); + EXPECT_EQ (x, + "CHILD_CELL_3_1_1@r0 *1 30000,0\n" + "CHILD_CELL_3_1_1@r0 *1 55000,0\n" + "CHILD_CELL_3_1_1@r0 *1 55000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 55000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 75000,0\n" + "CHILD_CELL_3_1_1@r0 *1 75000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 75000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 95000,0\n" + "CHILD_CELL_3_1_1@r0 *1 95000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 95000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 30000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 30000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 120000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 120000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 120000,0" + ); + + std::set sc; + sc.insert (layout.cell_by_name ("CHILD_CELL_3").second); + i2.unselect_cells (sc); + + x = collect2(i2, layout); + EXPECT_EQ (x, + "CHILD_CELL_3_1_1@r0 *1 120000,20000\n" + "CHILD_CELL_3_1_1@r0 *1 120000,40000\n" + "CHILD_CELL_3_1_1@r0 *1 120000,0" + ); +} diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index d198d9dac..5aff93212 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2081,6 +2081,24 @@ TEST(50_PropertiesFlat) EXPECT_EQ (s.at_end (), true); } +// "+" operator with properties (issue #1373) +TEST(50b_PropertiesFlat) +{ + db::Region r, rr; + + r.insert (db::Box (0, 0, 10, 20)); + rr.insert (db::Box (0, 0, 100, 200)); + rr.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1)); + + EXPECT_EQ ((db::Region () + rr).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); + EXPECT_EQ ((rr + db::Region ()).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); + EXPECT_EQ ((r + rr).to_string (), "(0,0;0,20;10,20;10,0);(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); + + r += rr; + + EXPECT_EQ (r.to_string (), "(0,0;0,20;10,20;10,0);(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); +} + TEST(51_PropertiesFlatFromLayout) { db::Layout ly; diff --git a/src/db/unit_tests/dbShapesTests.cc b/src/db/unit_tests/dbShapesTests.cc index b2fce8945..b18e02bf3 100644 --- a/src/db/unit_tests/dbShapesTests.cc +++ b/src/db/unit_tests/dbShapesTests.cc @@ -1925,6 +1925,51 @@ TEST(7) } } +// copy, move, clear with shape types +TEST(8) +{ + db::Manager m (true); + + db::Layout layout (true, &m); + unsigned int lindex1 = layout.insert_layer (); + unsigned int lindex2 = layout.insert_layer (); + + db::Cell &topcell = layout.cell (layout.add_cell ("TOP")); + + topcell.shapes (lindex1).insert (db::Box (1, 2, 3, 4)); + topcell.shapes (lindex1).insert (db::Polygon (db::Box (1, 2, 3, 4))); + + { + db::Transaction trans (&m, "T1"); + topcell.shapes (lindex2).insert (topcell.shapes (lindex1)); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "polygon (1,2;1,4;3,4;3,2) #0\nbox (1,2;3,4) #0\n"); + } + + m.undo (); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), ""); + + { + db::Transaction trans (&m, "T1"); + topcell.shapes (lindex2).insert (topcell.shapes (lindex1), db::ShapeIterator::Boxes); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "box (1,2;3,4) #0\n"); + } + + m.undo (); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), ""); + + topcell.shapes (lindex2).insert (topcell.shapes (lindex1)); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "polygon (1,2;1,4;3,4;3,2) #0\nbox (1,2;3,4) #0\n"); + + { + db::Transaction trans (&m, "T1"); + topcell.shapes (lindex2).clear (db::ShapeIterator::Polygons); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "box (1,2;3,4) #0\n"); + } + + m.undo (); + EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), shapes_to_string (_this, topcell.shapes (lindex1))); +} + TEST(10A) { if (db::default_editable_mode ()) { diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 91fa1a06e..3deac6889 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1182,6 +1182,36 @@ See Netter#netlist for a descrip

See Netter for more details

+

"new_report" - Creates a new report database object for use in "output"

+ +

Usage:

+
    +
  • new_report(description [, filename [, cellname ] ])
  • +
+

+This method creates an independent report object. This object +can be used in "output" to send a layer to a different report than +the default report or target. +

+Arguments are the same than for report. +

+See Layer#output for details about this feature. +

+

"new_target" - Creates a new layout target object for use in "output"

+ +

Usage:

+
    +
  • new_target(what [, cellname])
  • +
+

+This method creates an independent target object. This object +can be used in "output" to send a layer to a different layout file than +the default report or target. +

+Arguments are the same than for target. +

+See Layer#output for details about this feature. +

"no_borders" - Reset the tile borders

Usage:

@@ -1413,6 +1443,21 @@ See
Source#polygons for a descr The primary input of the universal DRC function is the layer the Layer#drc function is called on.

+

"profile" - Profiles the script and provides a runtime + memory statistics

+ +

Usage:

+
    +
  • profile
  • +
  • profile(n)
  • +
+

+Turns profiling on or off (default). In profiling mode, the +system will collect statistics about rules executed, their execution time +and memory information. The argument specifies how many operations to +print at the end of the run. Without an argument, all operations are +printed. Passing "false" for the argument will disable profiling. This is the +default. +

"props_copy" - Specifies "copy properties" on operations supporting user properties constraints

diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 4dbd218fa..6efff2012 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -2217,6 +2217,26 @@ a single LayerInfo object.

See report and target on how to configure output to a target layout or report database. +

+See also new_target and new_report on how to create additional +targets for output. This allows saving certain layers to different files than +the standard target or report. To do so, create a new target or report using one +of these functions and pass that object to the corresponding "output" call as +an additional argument. +

+Example: +

+

+check1 = ...
+check2 = ...
+check3 = ...
+
+second_report = new_report("Only for check2", "check2.lyrdb")
+
+check1.output("Check 1")
+check2.output("Check 2", second_report)
+check3.output("Check 3")
+

"outside" - Selects edges or polygons of self which are outside edges or polygons from the other layer

diff --git a/src/doc/doc/manual/drc_runsets.xml b/src/doc/doc/manual/drc_runsets.xml index 971bf9b79..7642c4c16 100644 --- a/src/doc/doc/manual/drc_runsets.xml +++ b/src/doc/doc/manual/drc_runsets.xml @@ -185,7 +185,7 @@ l.output("OUT") l.output(17, 0, "OUT")

- Output can be sent to other layouts using the "target" function: + Output can be sent to other layouts using the target function:

@@ -196,7 +196,7 @@ target("@2")
 target("out.gds", "OUT_TOP")

- Output can also be sent to a report database: + Output can also be sent to a report database using the report function:

@@ -234,6 +234,38 @@ l.output("check1", "The first check")
unpredictable.

+

+ It is possible to open "side" reports and targets and send layers to these + outputs without closing the default output. +

+ +

+ To open a "side report", use new_report + in the same way you use "report". Instead of switching the output, this function will return a + new report object that can be included in the argument list of "output" + for the layer that is to be sent to that side report: +

+ +
+# opens a new side report
+side_report = new_report("Another report")
+...
+# Send data from layer l to new category "check1" to the side report
+l.output(side_report, "check1", "The first check")
+ +

+ In the same way, "side targets" can be opened using new_target. + Such side targets open a way to write certain layers to other layout files. + This is very handy for debugging: +

+ +
+# opens a new side target for debugging
+debug_out = new_target("debug.gds")
+...
+# Send data from layer l to the debug output, layer 100/0
+l.output(debug_out, 100, 0)
+

Dimension specifications

@@ -710,12 +742,12 @@ overlaps = layer.size(0.2).raw.merged(2)

-  layer = input(1, 0)
-  layer.raw.sized(0.1).output(100, 0)
+layer = input(1, 0)
+layer.raw.sized(0.1).output(100, 0)
 
-  # this check will now be done on a raw layer, since the 
-  # previous raw call was putting the layer into raw mode
-  layer.width(0.2).ouput(101, 0)
+# this check will now be done on a raw layer, since the +# previous raw call was putting the layer into raw mode +layer.width(0.2).ouput(101, 0)

The following two images show the effect of raw and clean mode: @@ -792,20 +824,18 @@ overlaps = layer.size(0.2).raw.merged(2)

-  ...
-  drc_w = input(1, 0).width(0.2)
-  ...
-  
+... +drc_w = input(1, 0).width(0.2) +...

can be written as:

-  ...
-  drc_w = input(1, 0).drc(width < 0.2)
-  ...
-  
+... +drc_w = input(1, 0).drc(width < 0.2) +...

The drc method is the "universal DRC" method. @@ -845,12 +875,11 @@ overlaps = layer.size(0.2).raw.merged(2)

-  ...
-  l1 = input(1, 0)
-  l2 = input(2, 0)
-  drc_sep = l1.drc(separation(l2) <= 0.5)
-  ...
-  
+... +l1 = input(1, 0) +l2 = input(2, 0) +drc_sep = l1.drc(separation(l2) <= 0.5) +...

Options are also specified together with the measurement and follow the same notation @@ -858,10 +887,9 @@ overlaps = layer.size(0.2).raw.merged(2)

-  ...
-  drc_w = input(1, 0).drc(width(projection) < 0.2)
-  ...
-  
+... +drc_w = input(1, 0).drc(width(projection) < 0.2) +...

However, the universal DRC is much more than a convenient way to write checks: @@ -879,10 +907,9 @@ overlaps = layer.size(0.2).raw.merged(2)

-  ...
-  drc_ws = input(1, 0).drc((width < 0.2) & (space < 0.3))
-  ...
-  
+... +drc_ws = input(1, 0).drc((width < 0.2) & (space < 0.3)) +...

The boolean AND is computed between the edges on the primary shape and returns the @@ -891,12 +918,11 @@ overlaps = layer.size(0.2).raw.merged(2)

-  ...
-  drc_ws1 = input(1, 0).width(0.2).edges
-  drc_ws2 = input(1, 0).space(0.3).edges
-  drc_ws = drc_ws1 & drc_ws2
-  ...
-  
+... +drc_ws1 = input(1, 0).width(0.2).edges +drc_ws2 = input(1, 0).space(0.3).edges +drc_ws = drc_ws1 & drc_ws2 +...

The reason is that performing the boolean computation in the local loop can be @@ -933,11 +959,10 @@ overlaps = layer.size(0.2).raw.merged(2)

-  ...
-  drc_w = input(1, 0).width(0.2)
-  log("Number of width violations: #{drc_w.data.size}")
-  ...
-  
+... +drc_w = input(1, 0).width(0.2) +log("Number of width violations: #{drc_w.data.size}") +...

The error function can be used to output error messages @@ -946,11 +971,23 @@ overlaps = layer.size(0.2).raw.merged(2)

-  log_file("drc_log.txt")
-  verbose(true)
-  info("This message will be sent to the log file")
-  ...
-  
+log_file("drc_log.txt") +verbose(true) +info("This message will be sent to the log file") +... + +

+ The profile function will collect profiling + information during the DRC run. At the end of the script, the operations are printed to the log + output, sorted by their CPU time and approximate memory footprint. "profile" can be given a + numerical argument indicating the number of operations to print. Lower-ranking operations are + skipped in that case. By default, all operations are printed. +

+ +
+# enables profiling
+profile
+...

The tiling option

@@ -1003,8 +1040,7 @@ threads(4) # Disable tiling flat -... non-tiled operations ... - +... non-tiled operations ...

Some operations implicitly specify a tile border. If the tile border is known (see length example above), explicit borders diff --git a/src/doc/doc/manual/lvs_io.xml b/src/doc/doc/manual/lvs_io.xml index 3cc347f56..6a88d8be4 100644 --- a/src/doc/doc/manual/lvs_io.xml +++ b/src/doc/doc/manual/lvs_io.xml @@ -155,17 +155,12 @@ X$2 VSS IN OUT SUBSTRATE NMOS PARAMS: L=0.25 W=0.9 AS=0.405 AD=0.405 PS=2.7

Currently SPICE is understood with some limitations: + Only a subset of elements is implemented by default. These are + "M" (gives "MOS4" device classes), "Q" (gives BJT3 or BJT4 device + classes), "R" (gives Resistor device classes), "C" (gives + Capacitor device classes) and "D" (gives diode device classes).

-
    -
  • Parametrized circuits are not permitted except for device subcircuits - (with a delegate)
  • -
  • Only a subset of elements is implemented by default. These are - "M" (gives "MOS4" device classes), "Q" (gives BJT3 or BJT4 device - classes), "R" (gives Resistor device classes), "C" (gives - Capacitor device classes) and "D" (gives diode device classes).
  • -
-

As for the SPICE reader, a delegate can be provided to customize the reader. For doing so, subclass the diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 17edb9e2e..6bc5f4c72 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -13,6 +13,125 @@ module DRC end end + class OutputChannel + def initialize(engine) + @engine = engine + end + def is_rdb? + false + end + def cellname=(cellname) + # reimplement + end + def cell + # reimplement + end + def write + # reimplement + end + def finish(final) + # reimplement + end + def write + # reimplement + end + def layout + nil + end + def rdb + nil + end + end + + class LayoutOutputChannel < OutputChannel + + def initialize(engine, layout, cell, file_name) + super(engine) + @layout = layout + @cell = cell + @file_name = file_name + end + + def cellname=(cellname) + @cell = @layout.cell(cellname) || @layout.create_cell(cellname) + end + + def is_rdb? + false + end + + def finish(final, view) + write + end + + def write + if @file_name + opt = RBA::SaveLayoutOptions::new + gzip = opt.set_format_from_filename(@file_name) + @engine.info("Writing layout file: #{@file_name} ..") + @layout.write(@file_name, gzip, opt) + end + end + + def layout + @layout + end + + def cell + @cell + end + + end + + class RDBOutputChannel < OutputChannel + + def initialize(engine, rdb, rdb_index, cellname, file_name) + super(engine) + @rdb = rdb + @rdb_index = rdb_index + @cell = cellname && rdb.create_cell(cellname) + @file_name = file_name + end + + def cellname=(cellname) + @cell = nil + @rdb.each_cell do |c| + if c.name == cellname + @cell = c + end + end + @cell ||= @rdb.create_cell(cellname) + end + + def is_rdb? + true + end + + def finish(final, view) + write + if final && view + view.show_rdb(@rdb_index, view.active_cellview_index) + end + end + + def write + if @file_name + rdb_file = @engine._make_path(@file_name) + @engine.info("Writing report database: #{rdb_file} ..") + @rdb.save(rdb_file) + end + end + + def rdb + @rdb + end + + def cell + @cell + end + + end + # The DRC engine # %DRC% @@ -40,11 +159,9 @@ module DRC @def_source = nil @dbu_read = false use_dbu(@def_layout && @def_layout.dbu) - @output_layout = nil - @output_rdb = nil - @output_rdb_file = nil - @output_rdb_cell = nil @show_l2ndb = nil + @def_output = nil + @other_outputs = [] @output_l2ndb_file = nil @target_netlist_file = nil @target_netlist_format = nil @@ -70,6 +187,9 @@ module DRC dss._destroy @verbose = false + @profile = false + @profile_n = 0 + @profile_info = {} @in_context = nil @@ -711,6 +831,26 @@ module DRC end end + # %DRC% + # @name profile + # @brief Profiles the script and provides a runtime + memory statistics + # @synopsis profile + # @synopsis profile(n) + # Turns profiling on or off (default). In profiling mode, the + # system will collect statistics about rules executed, their execution time + # and memory information. The argument specifies how many operations to + # print at the end of the run. Without an argument, all operations are + # printed. Passing "false" for the argument will disable profiling. This is the + # default. + + def profile(n = 0) + if !n.is_a?(1.class) && n != nil && n != false + raise("Argument to 'profile' must be either an integer number or nil") + end + @profile = !!n + @profile_n = [n || 0, 0].max + end + # %DRC% # @name verbose? # @brief Returns true, if verbose mode is enabled @@ -1340,46 +1480,42 @@ module DRC self._context("report") do - @output_rdb_file = filename + # finish what we got so far + _finish(false) - name = filename && File::basename(filename) - name ||= "DRC" - - @output_rdb_index = nil - - view = RBA::LayoutView::current - if view - if self._rdb_index - @output_rdb = RBA::ReportDatabase::new("") # reuse existing name - @output_rdb_index = view.replace_rdb(self._rdb_index, @output_rdb) - else - @output_rdb = RBA::ReportDatabase::new(name) - @output_rdb_index = view.add_rdb(@output_rdb) - end - else - @output_rdb = RBA::ReportDatabase::new(name) - end - - @output_layout = nil - @output_cell = nil - @output_layout_file = nil - - cn = nil - cn ||= @def_cell && @def_cell.name - cn ||= source && source.cell_name - cn ||= cellname - - cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter") - - @output_rdb_cell = @output_rdb.create_cell(cn) - @output_rdb.generator = self._generator - @output_rdb.top_cell_name = cn - @output_rdb.description = description + @def_output = nil + @def_output = _make_report(description, filename, cellname) end end + # %DRC% + # @name new_report + # @brief Creates a new report database object for use in "output" + # @synopsis new_report(description [, filename [, cellname ] ]) + # + # This method creates an independent report object. This object + # can be used in "output" to send a layer to a different report than + # the default report or target. + # + # Arguments are the same than for \global#report. + # + # See \Layer#output for details about this feature. + + def new_report(description, filename = nil, cellname = nil) + + output = nil + + self._context("new_report") do + output = _make_report(description, filename, cellname) + @other_outputs << output + end + + output + + end + # %DRC% # @name report_netlist # @brief Specifies an extracted netlist report for output @@ -1456,25 +1592,13 @@ module DRC # finish what we got so far _flush - if @output_rdb - - cell = nil - @output_rdb.each_cell do |c| - if c.name == cellname - cell = c - end + if ! @def_output + if @def_layout + # establish a new default output from the default layout on this occasion + @def_output = LayoutOutputChannel::new(self, @def_layout, cellname.to_s, nil) end - - cell ||= @output_rdb.create_cell(cellname) - @output_rdb_cell = cell - else - - @output_layout ||= @def_layout - if @output_layout - @output_cell = @output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s) - end - + @def_output.cellname = cellname.to_s end end @@ -1512,51 +1636,40 @@ module DRC # finish what we got so far _finish(false) - - if arg.is_a?(String) - - if arg =~ /^@(\d+|\+)/ - view = RBA::LayoutView::current - view || raise("No view open") - if $1 == "+" - n = view.create_layout(true) - cellname ||= (@def_cell ? @def_cell.name : "TOP") - else - n = $1.to_i - 1 - end - (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") - cv = view.cellview(n) - cv.is_valid? || raise("Invalid layout @#{n + 1}") - @output_layout = cv.layout - @output_cell = cellname ? (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) : cv.cell - cv.cell = @output_cell - @output_layout_file = nil - else - @output_layout = RBA::Layout::new - @output_cell = cellname && @output_layout.create_cell(cellname.to_s) - @output_layout_file = arg - end - - elsif arg.is_a?(RBA::Layout) - - @output_layout = arg - @output_cell = cellname && (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) - @output_layout_file = nil - - elsif arg.is_a?(RBA::Cell) - - @output_layout = arg.layout - @output_cell = arg - @output_layout_file = nil - else - raise("Invalid argument '" + arg.inspect + "'") - end - + @def_output = nil + @def_output = _make_target(arg, cellname) + end end - + + # %DRC% + # @name new_target + # @brief Creates a new layout target object for use in "output" + # @synopsis new_target(what [, cellname]) + # + # This method creates an independent target object. This object + # can be used in "output" to send a layer to a different layout file than + # the default report or target. + # + # Arguments are the same than for \global#target. + # + # See \Layer#output for details about this feature. + + def new_target(arg, cellname = nil) + + output = nil + + self._context("new_target") do + output = _make_target(arg, cellname) + @other_outputs << output + end + + output + + end + # %DRC% # @name box # @brief Creates a box object @@ -2267,13 +2380,16 @@ CODE end t = RBA::Timer::new + t.start self._process_events - if @force_gc || Time::now - @time > 0.5 + if @force_gc || Time::now - @time > 0.5 || @profile GC.start # force a garbage collection before the operation to free unused memory @time = Time::now end + mem_before = RBA::Timer::memory_size res = yield + mem_after = RBA::Timer::memory_size t.stop begin @@ -2283,15 +2399,25 @@ CODE # Report result statistics _result_info(res, 1) - mem = RBA::Timer::memory_size - if mem > 0 - info("Elapsed: #{'%.3f'%(t.sys+t.user)}s Memory: #{'%.2f'%(mem/(1024*1024))}M", 1) + if mem_after > 0 + info("Elapsed: #{'%.3f'%(t.sys+t.user)}s Memory: #{'%.2f'%(mem_after/(1024*1024))}M", 1) else info("Elapsed: #{'%.3f'%(t.sys+t.user)}s", 1) end end + if @profile + + # calls, sys time (in sec), user time (in sec), memory added (in bytes) + info = (@profile_info[desc] ||= [ 0, 0.0, 0.0, 0 ]) + info[0] += 1 + info[1] += t.sys + info[2] += t.user + info[3] += mem_after - mem_before + + end + ensure # disable progress again @@ -2477,8 +2603,8 @@ CODE end def _output_layout - if @output_layout - output = @output_layout + if @def_output && @def_output.layout + output = @def_output.layout else output = @def_layout output || raise("No output layout specified") @@ -2487,19 +2613,79 @@ CODE end def _output_cell - if @output_layout - if @output_cell - output_cell = @output_cell + if @def_output && @def_output.layout + if @def_output.cell + output_cell = @def_output.cell elsif @def_cell - output_cell = @output_layout.cell(@def_cell.name) || @output_layout.create_cell(@def_cell.name) + output_layout = @def_output.layout + output_cell = output_layout.cell(@def_cell.name) || output_layout.create_cell(@def_cell.name) end output_cell || raise("No output cell specified (see 'target' instruction)") else - output_cell = @output_cell || @def_cell + output_cell = @def_cell output_cell || raise("No output cell specified") end output_cell end + + def _dump_profile + + if @profile_info.empty? + return + end + + desc_title = "Operation" + calls_title = "# calls" + time_title = "Time (s)" + memory_title = "Memory (k)" + titles = [ desc_title, calls_title, time_title, memory_title ] + + max_len_desc = [ @profile_info.keys.collect { |s| s.size }.max, desc_title.size ].max + max_len_calls = [ @profile_info.values.collect { |v| v[0].to_s.size }.max, calls_title.size ].max + max_len_time = [ @profile_info.values.collect { |v| ("%.3f" % (v[1] + v[2])).to_s.size }.max, time_title.size ].max + max_len_mem = [ @profile_info.values.collect { |v| v[3].to_s.size }.max, memory_title.size ].max + + fmt = "%-" + max_len_desc.to_s + "s " + + "%-" + max_len_calls.to_s + "d " + + "%-" + max_len_time.to_s + ".3f " + + "%-" + max_len_mem.to_s + ".0f" + + fmt_title = "%-" + max_len_desc.to_s + "s " + + "%-" + max_len_calls.to_s + "s " + + "%-" + max_len_time.to_s + "s " + + "%-" + max_len_mem.to_s + "s" + + pi = @profile_info.keys.collect { |s| [s] + @profile_info[s] }.collect do |desc,calls,sys_time,user_time,memory| + [ desc, calls, sys_time + user_time, memory.to_f / 1024.0 ] + end + + self.log("") + self.log("Operations by execution time\n") + self.log(fmt_title % titles) + n = 1 + pi.sort { |a,b| b[2] <=> a[2] }.each do |info| + self.log(fmt % info) + n += 1 + if @profile_n > 0 && n > @profile_n + self.log("... (%d entries skipped)" % (pi.size - @profile_n)) + break + end + end + + self.log("") + self.log("Operations by memory adder\n") + self.log(fmt_title % titles) + n = 1 + pi.sort { |a,b| b[3] <=> a[3] }.each do |info| + self.log(fmt % info) + n += 1 + if @profile_n > 0 && n > @profile_n + self.log("... (%d entries skipped)" % (pi.size - @profile_n)) + break + end + end + + end def _start(job_description) @@ -2528,39 +2714,32 @@ CODE begin _flush - + view = RBA::LayoutView::current - # save the report database if requested - if @output_rdb_file && final - rdb_file = _make_path(@output_rdb_file) - info("Writing report database: #{rdb_file} ..") - @output_rdb.save(rdb_file) + @def_output && @def_output.finish(final, view) + + if final + @other_outputs.each do |output| + output.finish(final, view) + end end - if @output_rdb && final && view - view.show_rdb(@output_rdb_index, view.active_cellview_index) - end - - # save the output file if requested - if @output_layout && @output_layout_file - opt = RBA::SaveLayoutOptions::new - gzip = opt.set_format_from_filename(@output_layout_file) - info("Writing layout file: #{@output_layout_file} ..") - @output_layout.write(@output_layout_file, gzip, opt) + + # dump the profile information + if @profile && final + _dump_profile end # create the new layers as visual layers if necessary if view - output = @output_layout || @def_layout + output = ( @def_output && @def_output.layout ) || @def_layout cv_index = nil view.cellviews.times do |cvi| view.cellview(cvi).layout == output && cv_index = cvi end if cv_index - view = RBA::LayoutView::current - # clear selection view.cancel @@ -2634,13 +2813,10 @@ CODE ensure @output_layers = [] - @output_layout = nil - @output_layout_file = nil - @output_cell = nil - @output_rdb_file = nil - @output_rdb_cell = nil - @output_rdb = nil - @output_rdb_index = nil + @def_output = nil + if final + @other_outputs = [] + end @show_l2ndb = nil @output_l2ndb_file = nil @@ -2933,21 +3109,36 @@ CODE def _output(data, *args) - if @output_rdb + channel = args.find { |a| a.is_a?(OutputChannel) } + if ! channel + if ! @def_output + @def_output = LayoutOutputChannel::new(self, self._output_layout, self._output_cell, nil) + end + channel = @def_output + end + + args = args.select { |a| !a.is_a?(OutputChannel) } + + if channel.rdb if args.size < 1 raise("Invalid number of arguments - category name and optional description expected") end - cat = @output_rdb.create_category(args[0].to_s) + output_rdb = channel.rdb + output_cell = channel.cell + + cat = output_rdb.create_category(args[0].to_s) args[1] && cat.description = args[1] - cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data) + cat.scan_collection(output_cell, RBA::CplxTrans::new(self.dbu), data) - else + end - output = self._output_layout - output_cell = self._output_cell + if channel.layout + + output = channel.layout + output_cell = channel.cell info = nil if args.size == 1 @@ -2977,14 +3168,16 @@ CODE begin - if !@used_output_layers[li] - @output_layers.push(li) - # Note: to avoid issues with output onto the input layer, we - # output to a temp layer and later swap both. The simple implementation - # did a clear here and the effect of that was that the data potentially - # got invalidated. - tmp = output.insert_layer(RBA::LayerInfo::new) - @used_output_layers[li] = true + if channel == @def_output + if !@used_output_layers[li] + @output_layers.push(li) + # Note: to avoid issues with output onto the input layer, we + # output to a temp layer and later swap both. The simple implementation + # did a clear here and the effect of that was that the data potentially + # got invalidated. + tmp = output.insert_layer(RBA::LayerInfo::new) + @used_output_layers[li] = true + end end # insert the data into the output layer @@ -3020,7 +3213,111 @@ CODE @layout_sources[name] = src src end + + def _make_report(description, filename, cellname) + output_rdb_file = filename + + name = filename && File::basename(filename) + name ||= "DRC" + + output_rdb_index = nil + + view = RBA::LayoutView::current + if view + if self._rdb_index + output_rdb = RBA::ReportDatabase::new("") # reuse existing name + output_rdb_index = view.replace_rdb(self._rdb_index, output_rdb) + else + output_rdb = RBA::ReportDatabase::new(name) + output_rdb_index = view.add_rdb(output_rdb) + end + else + output_rdb = RBA::ReportDatabase::new(name) + end + + cn = cellname && cellname.to_s + cn ||= @def_cell && @def_cell.name + cn ||= source && source.cell_name + + cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter") + + output_rdb.generator = self._generator + output_rdb.top_cell_name = cn + output_rdb.description = description + + RDBOutputChannel::new(self, output_rdb, output_rdb_index, cn, output_rdb_file) + + end + + def _make_target(arg, cellname = nil) + + if arg.is_a?(String) + + if arg =~ /^@(\d+|\+)/ + + view = RBA::LayoutView::current + view || raise("No view open") + if $1 == "+" + n = view.create_layout(true) + cellname ||= (@def_cell ? @def_cell.name : "TOP") + else + n = $1.to_i - 1 + end + + (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") + + cv = view.cellview(n) + cv.is_valid? || raise("Invalid layout @#{n + 1}") + + output_layout = cv.layout + output_cell = cellname ? (output_layout.cell(cellname.to_s) || output_layout.create_cell(cellname.to_s)) : cv.cell + cv.cell = output_cell + output_layout_file = nil + + else + + cn = cellname && cellname.to_s + cn ||= @def_cell && @def_cell.name + cn ||= @def_source && @def_source.cell_obj && @def_source.cell_obj.name + + cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter") + + output_layout = RBA::Layout::new + output_cell = output_layout.create_cell(cn) + output_layout_file = arg + + end + + elsif arg.is_a?(RBA::Layout) + + output_layout = arg + + output_cell = cellname && (output_layout.cell(cellname.to_s) || output_layout.create_cell(cellname.to_s)) + if ! output_cell + begin + output_cell = output_layout.top_cell + rescue + raise("No cell name specified and the layout does not have a unique top cell - specify the name of the top cell to write the output to") + end + end + + output_layout_file = nil + + elsif arg.is_a?(RBA::Cell) + + output_layout = arg.layout + output_cell = arg + output_layout_file = nil + + else + raise("Invalid argument '" + arg.inspect + "'") + end + + output = LayoutOutputChannel::new(self, output_layout, output_cell, output_layout_file) + + end + end end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index e434c9d1d..75bb29735 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -2630,7 +2630,7 @@ CODE # @brief Returns the intersection points of intersecting edge segments for two edge collections # @synopsis layer.intersections(edges) # This operation is similar to the "&" operator, but it does also report intersection points - # between non-colinear, but intersection edges. Such points are reported as point-like, + # between non-colinear, but intersecting edges. Such points are reported as point-like, # degenerated edge objects. # # This method is available for edge layers. The argument must be an edge layer. @@ -5007,6 +5007,26 @@ CODE # # See \global#report and \global#target on how to configure output to a target layout # or report database. + # + # See also \global#new_target and \global#new_report on how to create additional + # targets for output. This allows saving certain layers to different files than + # the standard target or report. To do so, create a new target or report using one + # of these functions and pass that object to the corresponding "output" call as + # an additional argument. + # + # Example: + # + # @code + # check1 = ... + # check2 = ... + # check3 = ... + # + # second_report = new_report("Only for check2", "check2.lyrdb") + # + # check1.output("Check 1") + # check2.output("Check 2", second_report) + # check3.output("Check 3") + # @/code def output(*args) @engine._context("output") do diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 8b24c918d..c0825e751 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -731,6 +731,75 @@ TEST(14_SwitchingTargets) db::compare_layouts (_this, layout2, au2, db::NoNormalization); } +TEST(14b_SideTargetsAndReports) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_14b.drc"; + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_14b.gds"; + + std::string au = tl::testdata (); + au += "/drc/drcSimpleTests_au14b.gds"; + + std::string au2 = tl::testdata (); + au2 += "/drc/drcSimpleTests_au14b_2.gds"; + + std::string au_report = tl::testdata (); + au_report += "/drc/drcSimpleTests_au14b.lyrdb"; + + std::string au_report2 = tl::testdata (); + au_report2 += "/drc/drcSimpleTests_au14b_2.lyrdb"; + + std::string output = this->tmp_file ("tmp.gds"); + std::string output2 = this->tmp_file ("tmp2.gds"); + std::string report = this->tmp_file ("tmp.lydrc"); + std::string report2 = this->tmp_file ("tmp2.lydrc"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_force_gc = true\n" + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target2 = '%s'\n" + "$drc_test_report = '%s'\n" + "$drc_test_report2 = '%s'\n" + , input, output, output2, report, report2) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); + + db::Layout layout2; + + { + tl::InputStream stream (output2); + db::Reader reader (stream); + reader.read (layout2); + } + + db::compare_layouts (_this, layout2, au2, db::NoNormalization); + + compare_text_files (report, au_report); + compare_text_files (report2, au_report2); +} + TEST(15_issue548) { std::string rs = tl::testdata (); diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index ec1e8bdc5..b1f4b2df5 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -427,7 +427,9 @@ MainService::cm_flatten_insts () view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Flatten instances"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Flatten instances"))); + } std::set needs_cleanup; @@ -470,7 +472,9 @@ MainService::cm_flatten_insts () // The selection is no longer valid view ()->clear_selection (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -479,7 +483,9 @@ MainService::cm_move_hier_up () view ()->cancel_edits (); check_no_guiding_shapes (); - manager ()->transaction (tl::to_string (tr ("Move up in hierarchy"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Move up in hierarchy"))); + } std::vector edt_services = view ()->get_plugins (); for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { @@ -529,7 +535,9 @@ MainService::cm_move_hier_up () } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } /** @@ -735,7 +743,9 @@ MainService::cm_make_cell_variants () view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Make cell variants for selection"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Make cell variants for selection"))); + } std::vector new_selection; for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { @@ -800,7 +810,7 @@ MainService::cm_make_cell_variants () if (needs_variant) { // need to create a variant: create a new cell - db::cell_index_type new_cell_index = layout.add_cell (layout.cell_name (elem.inst_ptr.cell_index ())); + db::cell_index_type new_cell_index = layout.add_cell (layout, elem.inst_ptr.cell_index ()); // prepare a new variant cell db::Cell &new_cell = layout.cell (new_cell_index); @@ -906,7 +916,9 @@ MainService::cm_make_cell_variants () i0 += n; } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -947,7 +959,9 @@ MainService::cm_resolve_arefs () std::vector new_selection; - manager ()->transaction (tl::to_string (tr ("Resolve array references"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Resolve array references"))); + } for (std::vector::const_iterator p = insts_to_resolve.begin (); p != insts_to_resolve.end (); ++p) { @@ -986,7 +1000,9 @@ MainService::cm_resolve_arefs () inst_service->set_selection (new_selection.begin (), new_selection.end ()); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -1041,7 +1057,9 @@ MainService::cm_make_cell () view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Make cell from selection"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Make cell from selection"))); + } db::cell_index_type target_ci = cv->layout ().add_cell (m_make_cell_name.c_str ()); // create target cell @@ -1090,7 +1108,9 @@ MainService::cm_make_cell () } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } @@ -1106,7 +1126,9 @@ MainService::cm_convert_to_cell () try { - manager ()->transaction (tl::to_string (tr ("Convert to static cell"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Convert to static cell"))); + } std::vector edt_services = view ()->get_plugins (); @@ -1158,10 +1180,14 @@ MainService::cm_convert_to_cell () // The selection might no longer be valid view ()->clear_selection (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } catch (...) { - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } throw; } } @@ -1265,7 +1291,9 @@ MainService::cm_convert_to_pcell () try { - manager ()->transaction (tl::to_string (tr ("Convert to PCell"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Convert to PCell"))); + } std::vector to_delete; std::vector new_selection; @@ -1351,10 +1379,14 @@ MainService::cm_convert_to_pcell () #endif } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } catch (...) { - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } throw; } } @@ -1546,7 +1578,9 @@ MainService::cm_round_corners () ep.merge (out, primary, 0 /*min_wc*/, true /*resolve holes*/, true /*min coherence*/); view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Corner rounding operation on selection"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Corner rounding operation on selection"))); + } // Delete the current selection for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { @@ -1586,7 +1620,9 @@ MainService::cm_round_corners () } } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -1672,7 +1708,9 @@ MainService::cm_size () ep.size (primary, idx, idy, out, 2 /*mode, TODO: make variable*/, true /*resolve holes*/, true /*min coherence*/); view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Sizing operation on selection"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Sizing operation on selection"))); + } // Delete the current selection for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { @@ -1712,7 +1750,9 @@ MainService::cm_size () } } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -1792,7 +1832,9 @@ MainService::boolean_op (int mode) } view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Boolean operation on selection"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Boolean operation on selection"))); + } // Delete the current selection // NOTE: we delete only those shapes from the primary layer and keep shapes from other layers. @@ -1835,7 +1877,9 @@ MainService::boolean_op (int mode) } } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -1955,7 +1999,9 @@ MainService::cm_align () if (! prim_box.empty ()) { view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Alignment"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Alignment"))); + } // do the alignment for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { @@ -1996,7 +2042,9 @@ MainService::cm_align () } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } } @@ -2096,7 +2144,9 @@ MainService::cm_distribute () { view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Distribution"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Distribution"))); + } // do the distribution for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { @@ -2111,7 +2161,9 @@ MainService::cm_distribute () } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } } @@ -2151,10 +2203,12 @@ MainService::cm_make_array () bool has_undo = (m_array_na * m_array_nb < 1000); // No undo support currently - the undo buffering is pretty inefficient right now. - if (! has_undo) { - manager ()->clear (); - } else { - manager ()->transaction (tl::to_string (tr ("Make array"))); + if (manager ()) { + if (! has_undo) { + manager ()->clear (); + } else { + manager ()->transaction (tl::to_string (tr ("Make array"))); + } } tl::RelativeProgress progress (tl::to_string (tr ("Make array")), (size_t (m_array_na) * size_t (m_array_nb) - 1) * n, 1000); @@ -2221,7 +2275,7 @@ MainService::cm_make_array () } - if (has_undo) { + if (has_undo && manager ()) { manager ()->commit (); } } @@ -2383,7 +2437,9 @@ MainService::cm_change_layer () view ()->cancel_edits (); - manager ()->transaction (tl::to_string (tr ("Change layer"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Change layer"))); + } db::Layout &layout = view ()->cellview (cv_index)->layout (); @@ -2441,7 +2497,9 @@ MainService::cm_change_layer () // The selection is no longer valid view ()->clear_selection (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } else { throw tl::Exception (tl::to_string (tr ("Nothing selected to switch layers for"))); diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 18b5e98ad..649be7f3f 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -243,9 +243,14 @@ insert_point_path (const db::Path &p, const std::set &sel, db::Po ctr.push_back (p1); if (! found && sel.find (EdgeWithIndex (db::Edge (p1, p2), n, n + 1, 0)) != sel.end ()) { // project the point onto the edge - std::pair projected = db::Edge (p1, p2).projected (ins); + db::Edge e (p1, p2); + std::pair projected = e.projected (ins); if (projected.first) { - ins = projected.second; + if (e.is_ortho ()) { + // NOTE: for skew edges we use the original point as the projected one usually + // is off-grid. + ins = projected.second; + } ctr.push_back (ins); found = true; } @@ -262,22 +267,28 @@ insert_point_path (const db::Path &p, const std::set &sel, db::Po } static void -remove_redundant_points (std::vector &ctr) +remove_redundant_points (std::vector &ctr, bool cyclic) { // compress contour (remove redundant points) // and assign to path std::vector::iterator wp = ctr.begin (); std::vector::const_iterator rp = ctr.begin (); - db::Point pm1 = *rp; - if (wp != ctr.end ()) { - ++wp; - ++rp; + db::Point pm1; + if (rp != ctr.end ()) { + if (cyclic) { + pm1 = ctr.back (); + } else { + pm1 = ctr.front (); + ++wp; + ++rp; + } while (rp != ctr.end ()) { db::Point p0 = *rp; if (p0 != pm1) { *wp++ = p0; } + pm1 = p0; ++rp; } } @@ -304,7 +315,7 @@ del_points_path (const db::Path &p, const std::set &sel) } } - remove_redundant_points (ctr); + remove_redundant_points (ctr, false); new_path.assign (ctr.begin (), ctr.end ()); return new_path; @@ -357,7 +368,7 @@ modify_path (db::Path &p, const std::map &new_points } if (compress) { - remove_redundant_points (ctr); + remove_redundant_points (ctr, false); } p.assign (ctr.begin (), ctr.end ()); @@ -384,10 +395,14 @@ insert_point_poly (const db::Polygon &p, const std::set &sel, db: ctr.push_back ((*e).p1 ()); if (! found && sel.find (EdgeWithIndex (*e, n, nn, c)) != sel.end ()) { - // project the point onto the edge + // project the point onto the edge - use the first edge the point projects to std::pair projected = (*e).projected (ins); if (projected.first) { - ins = projected.second; + if ((*e).is_ortho ()) { + // NOTE: for skew edges we use the original point as the projected one usually + // is off-grid. + ins = projected.second; + } ctr.push_back (ins); found = true; } @@ -397,7 +412,7 @@ insert_point_poly (const db::Polygon &p, const std::set &sel, db: if (found) { - remove_redundant_points (ctr); + remove_redundant_points (ctr, true); new_poly = p; if (c == 0) { @@ -433,7 +448,7 @@ del_points_poly (const db::Polygon &p, const std::set &sel) } } - remove_redundant_points (ctr); + remove_redundant_points (ctr, true); if (c == 0) { new_poly.assign_hull (ctr.begin (), ctr.end (), false /*compress*/); @@ -495,7 +510,7 @@ modify_polygon (db::Polygon &p, } if (compress) { - remove_redundant_points (ctr); + remove_redundant_points (ctr, true); } if (c == 0) { @@ -727,7 +742,7 @@ public: } private: - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/); + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level); founds_vector_type m_founds; }; @@ -736,25 +751,25 @@ private: // PartialShapeFinder implementation PartialShapeFinder::PartialShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator::flags_type flags) - : lay::ShapeFinder (point_mode, top_level_sel, flags) + : lay::ShapeFinder (point_mode, top_level_sel, flags, 0) { set_test_count (point_sel_tests); } void -PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/) +PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int /*level*/) { if (! point_mode ()) { for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { checkpoint (); const db::Shapes &shapes = cell.shapes (*l); - db::ShapeIterator shape = shapes.begin_touching (search_box, flags (), prop_sel (), inv_prop_sel ()); + db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ()); while (! shape.at_end ()) { checkpoint (); @@ -784,9 +799,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, ++ee; unsigned int nn = ee.at_end () ? 0 : n + 1; - if (search_box.contains ((*e).p1 ())) { + if (hit_box.contains ((*e).p1 ())) { edges.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, c)); - if (search_box.contains ((*e).p2 ())) { + if (hit_box.contains ((*e).p2 ())) { edges.push_back (EdgeWithIndex (*e, n, nn, c)); } } @@ -801,9 +816,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, db::Point pl; unsigned int n = 0; for (db::Shape::point_iterator pt = shape->begin_point (); pt != shape->end_point (); ++pt, ++n) { - if (search_box.contains (*pt)) { + if (hit_box.contains (*pt)) { edges.push_back (EdgeWithIndex (db::Edge (*pt, *pt), n, n, 0)); - if (pl_set && search_box.contains (pl)) { + if (pl_set && hit_box.contains (pl)) { edges.push_back (EdgeWithIndex (db::Edge (pl, *pt), n - 1, n, 0)); } } @@ -825,9 +840,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, ++ee; unsigned int nn = ee.at_end () ? 0 : n + 1; - if (search_box.contains ((*e).p1 ())) { + if (hit_box.contains ((*e).p1 ())) { edges.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, 0)); - if (search_box.contains ((*e).p2 ())) { + if (hit_box.contains ((*e).p2 ())) { edges.push_back (EdgeWithIndex (*e, n, nn, 0)); } } @@ -837,8 +852,23 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, } else if (shape->is_text ()) { db::Point tp (shape->text_trans () * db::Point ()); - if (search_box.contains (tp)) { - edges.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + + if (text_info ()) { + + db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; + db::Text text; + shape->text (text); + db::Box tb = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp); + if (tb.inside (hit_box)) { + edges.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + } + + } else { + + if (hit_box.contains (tp)) { + edges.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + } + } } @@ -860,14 +890,14 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (hit_box))) { checkpoint (); const db::Shapes &shapes = cell.shapes (*l); std::vector edge_sel; - db::ShapeIterator shape = shapes.begin_touching (search_box, flags (), prop_sel (), inv_prop_sel ()); + db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ()); while (! shape.at_end ()) { bool match = false; @@ -968,11 +998,29 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, } else if (shape->is_text ()) { db::Point tp (shape->text_trans () * db::Point ()); - if (search_box.contains (tp)) { - d = tp.distance (search_box.center ()); - edge_sel.clear (); - edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); - match = true; + + if (text_info ()) { + + db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; + db::Text text; + shape->text (text); + db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp)); + if (tb.contains (hit_box.center ())) { + d = tp.distance (hit_box.center ()); + edge_sel.clear (); + edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + match = true; + } + + } else { + + if (hit_box.contains (tp)) { + d = tp.distance (hit_box.center ()); + edge_sel.clear (); + edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0)); + match = true; + } + } } @@ -1740,7 +1788,9 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo // stop dragging ui ()->ungrab_mouse (this); - manager ()->transaction (tl::to_string (tr ("Partial move"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Partial move"))); + } // heuristically, if there is just one edge selected: do not confine to the movement // angle constraint - the edge usually is confined enough @@ -1748,7 +1798,9 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo transform_selection (move_trans); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } @@ -1935,7 +1987,9 @@ PartialService::mouse_double_click_event (const db::DPoint &p, unsigned int butt partial_objects::iterator r = m_selection.begin (); // we assert above that we have at least one selected element if (! r->first.is_cell_inst ()) { - manager ()->transaction (tl::to_string (tr ("Insert point"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Insert point"))); + } // snap the point db::DPoint new_point_d = snap (p); @@ -2002,7 +2056,9 @@ PartialService::mouse_double_click_event (const db::DPoint &p, unsigned int butt handle_guiding_shape_changes (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } selection_to_view (); diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 188f115f0..aefcf86dd 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -472,6 +472,8 @@ Service::selection_bbox () // TODO: this is done multiple times - once for each service! TransformationVariants tv (view ()); + lay::TextInfo text_info (view ()); + db::DBox box; for (objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) { @@ -486,7 +488,13 @@ Service::selection_bbox () const std::vector *tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ()); if (tv_list != 0) { for (std::vector::const_iterator t = tv_list->begin (); t != tv_list->end (); ++t) { - box += *t * (ctx_trans * r->shape ().bbox ()); + if (r->shape ().is_text ()) { + db::Text text; + r->shape ().text (text); + box += *t * text_info.bbox (ctx_trans * text, *t); + } else { + box += *t * (ctx_trans * r->shape ().bbox ()); + } } } diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 9108c77a5..14e16c22f 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -32,6 +32,7 @@ #include "layMarker.h" #include "laySnap.h" #include "layObjectInstPath.h" +#include "layTextInfo.h" #include "tlColor.h" #include "dbLayout.h" #include "dbShape.h" diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index f11315018..a219d38bb 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -266,9 +266,13 @@ ShapeEditService::deliver_shape (const db::Polygon &poly) { if (m_combine_mode == CM_Add) { - manager ()->transaction (tl::to_string (tr ("Create polygon"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Create polygon"))); + } cell ().shapes (layer ()).insert (poly); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } else { @@ -326,7 +330,9 @@ ShapeEditService::deliver_shape (const db::Polygon &poly) result = input; } - manager ()->transaction (tl::to_string (tr ("Combine shape with background"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Combine shape with background"))); + } // Erase existing shapes for (std::vector::const_iterator s = shapes.begin (); s != shapes.end (); ++s) { @@ -341,7 +347,9 @@ ShapeEditService::deliver_shape (const db::Polygon &poly) cell ().shapes (layer ()).insert (*p); } - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } } @@ -350,9 +358,13 @@ void ShapeEditService::deliver_shape (const db::Path &path) { if (m_combine_mode == CM_Add) { - manager ()->transaction (tl::to_string (tr ("Create path"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Create path"))); + } cell ().shapes (layer ()).insert (path); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } else { deliver_shape (path.polygon ()); } @@ -362,9 +374,13 @@ void ShapeEditService::deliver_shape (const db::Box &box) { if (m_combine_mode == CM_Add) { - manager ()->transaction (tl::to_string (tr ("Create box"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Create box"))); + } cell ().shapes (layer ()).insert (box); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } else { deliver_shape (db::Polygon (box)); } @@ -912,9 +928,13 @@ TextService::do_finish_edit () { get_edit_layer (); - manager ()->transaction (tl::to_string (tr ("Create text"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Create text"))); + } cell ().shapes (layer ()).insert (get_text ()); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } commit_recent (view ()); @@ -1634,11 +1654,15 @@ InstService::do_finish_edit () throw tl::Exception (tl::to_string (tr ("Inserting this instance would create a recursive hierarchy"))); } - manager ()->transaction (tl::to_string (tr ("Create instance")), m_reference_transaction_id); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Create instance")), m_reference_transaction_id); + } m_reference_transaction_id = 0; db::Instance i = cv->layout ().cell (cv.cell_index ()).insert (inst); cv->layout ().cleanup (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } commit_recent (view ()); diff --git a/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc b/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc index 81e440d14..2ab3add1e 100644 --- a/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc +++ b/src/gsiqt/qt4/QtCore/gsiDeclQObject.cc @@ -51,7 +51,40 @@ static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, g ret.write (QObject::staticMetaObject); } - QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild (name); } + +#if QT_VERSION < 0x50000 + +#include + + QObject *find_child_impl (QObject *object, const QString &name) + { + return object->findChild (name); + } + QList find_children_impl (QObject *object, const QString &name) + { + return object->findChildren (name); + } + QList find_children_impl2 (QObject *object, const QRegExp &re) + { + return object->findChildren (re); + } +#else + +#include + + QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChild (name, options); + } + QList find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChildren (name, options); + } + QList find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options) + { + return object->findChildren (re, options); + } +#endif // bool QObject::blockSignals(bool b) @@ -757,7 +790,15 @@ static gsi::Methods methods_QObject () { } qt_gsi::QtNativeClass decl_QObject ("QtCore", "QObject_Native", - gsi::method_ext("findChild", &find_child_impl, "@brief Specialisation for findChild (uses QObject as T).") +#if QT_VERSION < 0x50000 + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), "@brief Specialisation for findChildren (uses QObject as T).") +#else + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") +#endif + methods_QObject (), "@hide\n@alias QObject"); diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc b/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc index a36f4b1ac..77bf0c21b 100644 --- a/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc +++ b/src/gsiqt/qt5/QtCore/gsiDeclQObject.cc @@ -52,7 +52,40 @@ static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, g ret.write (QObject::staticMetaObject); } - QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild (name); } + +#if QT_VERSION < 0x50000 + +#include + + QObject *find_child_impl (QObject *object, const QString &name) + { + return object->findChild (name); + } + QList find_children_impl (QObject *object, const QString &name) + { + return object->findChildren (name); + } + QList find_children_impl2 (QObject *object, const QRegExp &re) + { + return object->findChildren (re); + } +#else + +#include + + QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChild (name, options); + } + QList find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChildren (name, options); + } + QList find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options) + { + return object->findChildren (re, options); + } +#endif // bool QObject::blockSignals(bool b) @@ -847,7 +880,15 @@ static gsi::Methods methods_QObject () { } qt_gsi::QtNativeClass decl_QObject ("QtCore", "QObject_Native", - gsi::method_ext("findChild", &find_child_impl, "@brief Specialisation for findChild (uses QObject as T).") +#if QT_VERSION < 0x50000 + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), "@brief Specialisation for findChildren (uses QObject as T).") +#else + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") +#endif + methods_QObject (), "@hide\n@alias QObject"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQObject.cc b/src/gsiqt/qt6/QtCore/gsiDeclQObject.cc index 5220ea53d..f4a89d144 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQObject.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQObject.cc @@ -52,7 +52,40 @@ static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, g ret.write (QObject::staticMetaObject); } - QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild (name); } + +#if QT_VERSION < 0x50000 + +#include + + QObject *find_child_impl (QObject *object, const QString &name) + { + return object->findChild (name); + } + QList find_children_impl (QObject *object, const QString &name) + { + return object->findChildren (name); + } + QList find_children_impl2 (QObject *object, const QRegExp &re) + { + return object->findChildren (re); + } +#else + +#include + + QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChild (name, options); + } + QList find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options) + { + return object->findChildren (name, options); + } + QList find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options) + { + return object->findChildren (re, options); + } +#endif // bool QObject::blockSignals(bool b) @@ -771,7 +804,15 @@ static gsi::Methods methods_QObject () { } qt_gsi::QtNativeClass decl_QObject ("QtCore", "QObject_Native", - gsi::method_ext("findChild", &find_child_impl, "@brief Specialisation for findChild (uses QObject as T).") +#if QT_VERSION < 0x50000 + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), "@brief Specialisation for findChildren (uses QObject as T).") +#else + gsi::method_ext("findChild", &find_child_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChild (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl, gsi::arg("name", QString(), "null"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") + + gsi::method_ext("findChildren", &find_children_impl2, gsi::arg("re"), gsi::arg("options", Qt::FindChildrenRecursively, "Qt::FindChildrenRecursively"), "@brief Specialisation for findChildren (uses QObject as T).") +#endif + methods_QObject (), "@hide\n@alias QObject"); diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 9724f902a..a21a80f7c 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -1481,9 +1481,13 @@ Service::menu_activated (const std::string &symbol) { if (symbol == "img::clear_all_images") { - manager ()->transaction (tl::to_string (tr ("Clear all images"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Clear all images"))); + } clear_images (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } else if (symbol == "img::add_image") { @@ -1601,10 +1605,14 @@ Service::add_image () clear_selection (); - manager ()->transaction (tl::to_string (tr ("Add image"))); + if (manager ()) { + manager ()->transaction (tl::to_string (tr ("Add image"))); + } new_image->set_z_position (top_z_position ()); mp_view->annotation_shapes ().insert (db::DUserObject (new_image)); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } else { delete new_image; diff --git a/src/lay/lay/ClipDialog.ui b/src/lay/lay/ClipDialog.ui index 59640fc55..075131738 100644 --- a/src/lay/lay/ClipDialog.ui +++ b/src/lay/lay/ClipDialog.ui @@ -1,7 +1,8 @@ - + + ClipDialog - - + + 0 0 @@ -9,49 +10,47 @@ 421 - + Clip Tool - - - 9 - - + + 6 + + 9 + - - + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + - - + + Clip result cell name - - - - 7 - 0 + + + 0 0 - + CLIP @@ -61,13 +60,13 @@ - + Qt::Vertical - + QSizePolicy::Fixed - + 20 10 @@ -76,48 +75,48 @@ - - + + Clip Box Specification - - + + 9 - + 6 - - - + + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + - - + + Single box with ... - + false - + Qt::Vertical - + 20 40 @@ -128,40 +127,40 @@ - - - + + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + - - + + Single box with ... - + true - + false - + Qt::Vertical - + 20 40 @@ -172,12 +171,12 @@ - + - + Qt::Horizontal - + 231 20 @@ -185,176 +184,180 @@ - - - + + + Shapes on layer (multi clip) - + false - - - + + + Box Boundaries - - + + 9 - + 6 - - - + + + y = - - - + + + 2nd corner - - - + + + x = - - - + + + 1st corner - - - + + + x = - - - + + + y = - - + + - - + + - - + + - - + + - - - + + + Box Center And Dimensions - - + + 9 - + 6 - - - + + + h = - - - + + + Dimensions - - - + + + w = - - - + + + Center - - - + + + x = - - - + + + y = - - + + - - + + - - + + - - + + - - - + + + From ruler start and end points (multi clip) - + false - - + + + + QComboBox::AdjustToContents + + - + Qt::Vertical - + 20 40 @@ -363,12 +366,12 @@ - - + + Qt::Horizontal - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -406,11 +409,11 @@ ClipDialog accept() - + 248 254 - + 157 274 @@ -422,11 +425,11 @@ ClipDialog reject() - + 316 260 - + 286 274 diff --git a/src/lay/lay/FillDialog.ui b/src/lay/lay/FillDialog.ui index f0853556c..592435ffb 100644 --- a/src/lay/lay/FillDialog.ui +++ b/src/lay/lay/FillDialog.ui @@ -49,7 +49,7 @@ - + 0 0 @@ -70,8 +70,30 @@ 6 + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 15 + 20 + + + + + + + 0 + 0 + + QFrame::NoFrame @@ -131,10 +153,13 @@ Qt::Vertical + + QSizePolicy::Expanding + 20 - 40 + 20 @@ -142,20 +167,7 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + QFrame::NoFrame @@ -203,19 +215,6 @@ 0 - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -251,6 +250,12 @@ + + + 1 + 0 + + 1 @@ -277,10 +282,13 @@ Qt::Vertical + + QSizePolicy::Expanding + 241 - 40 + 20 @@ -303,6 +311,9 @@ true + + QComboBox::AdjustToContents + @@ -409,6 +420,22 @@ + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + diff --git a/src/lay/lay/MacroEditorDialog.ui b/src/lay/lay/MacroEditorDialog.ui index 95b142033..80f62e53c 100644 --- a/src/lay/lay/MacroEditorDialog.ui +++ b/src/lay/lay/MacroEditorDialog.ui @@ -94,10 +94,7 @@ - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">New folder</p></body></html> + New folder ... @@ -121,10 +118,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;">New</p></body></html> + New ... @@ -141,10 +135,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;">Delete</p></body></html> + Delete ... @@ -161,10 +152,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Rename</p></body></html> + Rename Rename @@ -181,10 +169,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Import file</p></body></html> + Import file Import @@ -208,7 +193,7 @@ p, li { white-space: pre-wrap; } - <html><head/><body><p>Save all files (Ctrl+Shift+S)</p></body></html> + Save all files (Ctrl+Shift+S) ... @@ -228,7 +213,7 @@ p, li { white-space: pre-wrap; } - <html><head/><body><p>Save current file (Ctrl+S)</p></body></html> + Save current file (Ctrl+S) ... @@ -333,8 +318,8 @@ p, li { white-space: pre-wrap; } ... - - :/back_16.png:/back_16.png + + :/back_16px.png:/back_16px.png true @@ -347,8 +332,8 @@ p, li { white-space: pre-wrap; } ... - - :/forward_16.png:/forward_16.png + + :/forward_16px.png:/forward_16px.png true @@ -365,10 +350,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Close tab</p></body></html> + Close tab close @@ -432,10 +414,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Stop script</p></body></html> + Stop script ... @@ -452,10 +431,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pause script (Ctrl+F5)</p></body></html> + Pause script (Ctrl+F5) ... @@ -482,10 +458,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set breakpoint (F9)</p></body></html> + Set breakpoint (F9) ... @@ -505,10 +478,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Clear all breakpoints (Ctrl+Shift+F9)</p></body></html> + Clear all breakpoints (Ctrl+Shift+F9) ... @@ -535,10 +505,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Step into procedure (F11)</p></body></html> + Step into procedure (F11) S @@ -558,10 +525,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Step over procedure or block (F10)</p></body></html> + Step over procedure or block (F10) N @@ -588,10 +552,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Enable or disable debugging</p></body></html> + Enable or disable debugging DBG @@ -618,10 +579,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Edit properties of macro</p></body></html> + Edit properties of macro P @@ -638,10 +596,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Setup colors, formats, debugger</p></body></html> + Setup colors, formats, debugger prop @@ -674,10 +629,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Find next</p></body></html> + Find next (Ctrl+F) N @@ -687,7 +639,7 @@ p, li { white-space: pre-wrap; } :/find_16px.png:/find_16px.png - F3 + Ctrl+F true @@ -709,10 +661,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Show replace mode</p></body></html> + Show replace mode ... @@ -764,10 +713,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Replace and find next</p></body></html> + Replace and find next (Ctrl+R) RN @@ -777,7 +723,7 @@ p, li { white-space: pre-wrap; } :/replace_16px.png:/replace_16px.png - + Ctrl+R true @@ -787,10 +733,7 @@ p, li { white-space: pre-wrap; } - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Replace all</p></body></html> + Replace all (Ctrl+Shift+R) All @@ -799,6 +742,9 @@ p, li { white-space: pre-wrap; } :/replace_all_16px.png:/replace_all_16px.png + + Ctrl+Shift+R + true @@ -1497,14 +1443,6 @@ p, li { white-space: pre-wrap; } Case Sensitive - - - Search & Replace - - - Ctrl+F - - Save As diff --git a/src/lay/lay/ReplacePropertiesBox.ui b/src/lay/lay/ReplacePropertiesBox.ui index 3a7b3ce70..2c667f89f 100644 --- a/src/lay/lay/ReplacePropertiesBox.ui +++ b/src/lay/lay/ReplacePropertiesBox.ui @@ -1,7 +1,8 @@ - + + ReplacePropertiesBox - - + + 0 0 @@ -9,32 +10,32 @@ 265 - + Form - - + + 9 - + 6 - - - + + + Height - - + + - + - + Qt::Vertical - + 200 40 @@ -42,69 +43,79 @@ - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - + + - - - + + + Width - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - - + + + Layer - - - + + + µm - - - + + + µm - - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + @@ -115,6 +126,8 @@

layWidgets.h
- + + + diff --git a/src/lay/lay/ReplacePropertiesPath.ui b/src/lay/lay/ReplacePropertiesPath.ui index 9be6091e7..c03d9224a 100644 --- a/src/lay/lay/ReplacePropertiesPath.ui +++ b/src/lay/lay/ReplacePropertiesPath.ui @@ -1,37 +1,38 @@ - + + ReplacePropertiesPath - - + + 0 0 - 239 + 246 241 - + Form - - + + 9 - + 6 - - - + + + Width - + - + Qt::Vertical - + 200 40 @@ -39,42 +40,52 @@ - - - + + + Layer - - - - - - - + + + + + 0 + 0 + - - :/right_16px@2x.png + + QComboBox::AdjustToContents - - - - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - - + + + + + + + + + + :/right_16px@2x.png + + + + + + µm @@ -88,6 +99,8 @@
layWidgets.h
- + + +
diff --git a/src/lay/lay/ReplacePropertiesShape.ui b/src/lay/lay/ReplacePropertiesShape.ui index 0493ba1f5..fd4a258ab 100644 --- a/src/lay/lay/ReplacePropertiesShape.ui +++ b/src/lay/lay/ReplacePropertiesShape.ui @@ -1,7 +1,8 @@ - + + ReplacePropertiesShape - - + + 0 0 @@ -9,39 +10,19 @@ 187 - + Form - - - 9 - - + + 6 - - - - - - - :/right_16px@2x.png - - - - - - - Layer - - - - + - + Qt::Vertical - + 10 281 @@ -49,18 +30,49 @@ - - - - - 7 - 5 + + + + Layer + + + + + + + 0 0 + + QComboBox::AdjustToContents + + + + + + + + :/right_16px@2x.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -70,6 +82,8 @@
layWidgets.h
- + + +
diff --git a/src/lay/lay/ReplacePropertiesText.ui b/src/lay/lay/ReplacePropertiesText.ui index a5b6da715..312800040 100644 --- a/src/lay/lay/ReplacePropertiesText.ui +++ b/src/lay/lay/ReplacePropertiesText.ui @@ -1,7 +1,8 @@ - + + ReplacePropertiesText - - + + 0 0 @@ -9,125 +10,133 @@ 269 - + Form - - + + 9 - + 6 - - + + - + - + R0 - - :/r0_24px.png + + + :/r0_24px.png:/r0_24px.png - + R90 - - :/r90_24px.png + + + :/r90_24px.png:/r90_24px.png - + R180 - - :/r180_24px.png + + + :/r180_24px.png:/r180_24px.png - + R270 - - :/r270_24px.png + + + :/r270_24px.png:/r270_24px.png - + M0 - - :/m0_24px.png + + + :/m0_24px.png:/m0_24px.png - + M45 - - :/m45_24px.png + + + :/m45_24px.png:/m45_24px.png - + M90 - - :/m90_24px.png + + + :/m90_24px.png:/m90_24px.png - + M135 - - :/m135_24px.png + + + :/m135_24px.png:/m135_24px.png - - - + + + Text - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - + + - + - + Qt::Vertical - + 200 40 @@ -135,56 +144,66 @@ - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - - + + + Orientation - - - + + + - - :/right_16px@2x.png + + :/right_16px@2x.png - - - + + + Size - - + + - - - + + + Layer - - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + - - - + + + µm @@ -198,6 +217,8 @@
layWidgets.h
- + + +
diff --git a/src/lay/lay/SearchPropertiesBox.ui b/src/lay/lay/SearchPropertiesBox.ui index 7afa3a6dd..96170e2f3 100644 --- a/src/lay/lay/SearchPropertiesBox.ui +++ b/src/lay/lay/SearchPropertiesBox.ui @@ -1,7 +1,8 @@ - + + SearchPropertiesBox - - + + 0 0 @@ -9,80 +10,80 @@ 331 - + Form - - + + 9 - + 6 - - + + - - - + + + µm - - - + + + µm² - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - - + + + Area - + - + Qt::Vertical - + 200 281 @@ -90,158 +91,168 @@ - - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - - + + + µm - - - + + + Width - - + + - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - + + - - - + + + Height - - - + + + Layer - - - + + + Perimeter - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - + + - - - + + + µm diff --git a/src/lay/lay/SearchPropertiesPath.ui b/src/lay/lay/SearchPropertiesPath.ui index 5fa5740d9..3ebb041b0 100644 --- a/src/lay/lay/SearchPropertiesPath.ui +++ b/src/lay/lay/SearchPropertiesPath.ui @@ -1,7 +1,8 @@ - + + SearchPropertiesPath - - + + 0 0 @@ -9,134 +10,144 @@ 260 - + Form - - + + 9 - + 6 - - - + + + Length - - + + - - - + + + Width - - - + + + Layer - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - + + - - - + + + µm - - - + + + µm - - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + - + - + Qt::Vertical - + 200 40 diff --git a/src/lay/lay/SearchPropertiesShape.ui b/src/lay/lay/SearchPropertiesShape.ui index 06e9b47e9..67ad79319 100644 --- a/src/lay/lay/SearchPropertiesShape.ui +++ b/src/lay/lay/SearchPropertiesShape.ui @@ -1,7 +1,8 @@ - + + SearchPropertiesShape - - + + 0 0 @@ -9,134 +10,144 @@ 287 - + Form - - + + 9 - + 6 - - - + + + Layer - - - + + + Area - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - - + + + Perimeter - - + + - - + + - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - - + + + µm² - - - + + + µm - - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + - + - + Qt::Vertical - + 200 281 diff --git a/src/lay/lay/SearchPropertiesText.ui b/src/lay/lay/SearchPropertiesText.ui index 7631b1aa4..2a2f311a8 100644 --- a/src/lay/lay/SearchPropertiesText.ui +++ b/src/lay/lay/SearchPropertiesText.ui @@ -1,7 +1,8 @@ - + + SearchPropertiesText - - + + 0 0 @@ -9,201 +10,219 @@ 308 - + Form - - + + 9 - + 6 - - - + + + Layer - - - + + + Size - - + + - - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + - - + + - + ~ - + !~ - - + + - - + + - + - + R0 - - :/r0_24px.png + + + :/r0_24px.png:/r0_24px.png - + R90 - - :/r90_24px.png + + + :/r90_24px.png:/r90_24px.png - + R180 - - :/r180_24px.png + + + :/r180_24px.png:/r180_24px.png - + R270 - - :/r270_24px.png + + + :/r270_24px.png:/r270_24px.png - + M0 - - :/m0_24px.png + + + :/m0_24px.png:/m0_24px.png - + M45 - - :/m45_24px.png + + + :/m45_24px.png:/m45_24px.png - + M90 - - :/m90_24px.png + + + :/m90_24px.png:/m90_24px.png - + M135 - - :/m135_24px.png + + + :/m135_24px.png:/m135_24px.png - - + + - + == - + != - + < - + <= - - > + + > - - >= + + >= - - - + + + Text - - - + + + Orientation - - + + - + == - + != - - - + + + µm - + - + Qt::Vertical - + 200 101 @@ -229,6 +248,8 @@ text_size_op text_size_value - + + + diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index e449f08b6..e6faed12b 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -472,15 +472,15 @@ Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M "\n" "This method has been added in version 0.24." ) + - gsi::method ("message", &lay::MainWindow::message, gsi::arg ("message"), gsi::arg ("time"), + gsi::method ("message", &lay::MainWindow::message, gsi::arg ("message"), gsi::arg ("time", -1, "infinite"), "@brief Displays a message in the status bar\n" "\n" "@param message The message to display\n" - "@param time The time how long to display the message in ms\n" + "@param time The time how long to display the message in ms. A negative value means 'infinitely'.\n" "\n" "This given message is shown in the status bar for the given time.\n" "\n" - "This method has been added in version 0.18." + "This method has been added in version 0.18. The 'time' parameter was made optional in version 0.28.10." ) + gsi::method ("resize", (void (lay::MainWindow::*)(int, int)) &lay::MainWindow::resize, gsi::arg ("width"), gsi::arg ("height"), "@brief Resizes the window\n" @@ -760,6 +760,16 @@ Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M "\n" "This event has been added in version 0.25.\n" ) + + gsi::event ("on_session_about_to_be_restored", &lay::MainWindow::begin_restore_session, + "@brief An event indicating that a session is about to be restored\n" + "\n" + "This event has been added in version 0.28.8.\n" + ) + + gsi::event ("on_session_restored", &lay::MainWindow::end_restore_session, + "@brief An event indicating that a session was restored\n" + "\n" + "This event has been added in version 0.28.8.\n" + ) + gsi::method ("show_macro_editor", &lay::MainWindow::show_macro_editor, gsi::arg ("cat", std::string ()), gsi::arg ("add", false), "@brief Shows the macro editor\n" "If 'cat' is given, this category will be selected in the category tab. " diff --git a/src/lay/lay/layClipDialog.cc b/src/lay/lay/layClipDialog.cc index 23a103d45..7ddf31d3c 100644 --- a/src/lay/lay/layClipDialog.cc +++ b/src/lay/lay/layClipDialog.cc @@ -91,9 +91,11 @@ ClipDialog::menu_activated (const std::string &symbol) { if (symbol == "clip_tool::show") { - lay::CellView cv = view ()->cellview (view ()->active_cellview_index ()); + int cv_index = view ()->active_cellview_index (); + + lay::CellView cv = view ()->cellview (cv_index); if (cv.is_valid ()) { - cb_layer->set_layout (&cv->layout ()); + cb_layer->set_view (view (), cv_index); show (); activate (); } diff --git a/src/lay/lay/layFillDialog.cc b/src/lay/lay/layFillDialog.cc index 8ba9b9a3d..119d60232 100644 --- a/src/lay/lay/layFillDialog.cc +++ b/src/lay/lay/layFillDialog.cc @@ -102,10 +102,12 @@ FillDialog::menu_activated (const std::string &symbol) { if (symbol == "fill_tool::show") { - lay::CellView cv = mp_view->cellview (mp_view->active_cellview_index ()); + int cv_index = mp_view->active_cellview_index (); + + lay::CellView cv = mp_view->cellview (cv_index); if (cv.is_valid ()) { - cb_layer->set_layout (&cv->layout ()); - fc_boundary_layer->set_layout (&cv->layout ()); + cb_layer->set_view (mp_view, cv_index); + fc_boundary_layer->set_view (mp_view, cv_index); show (); } @@ -551,13 +553,19 @@ BEGIN_PROTECTED FillParameters fp = get_fill_parameters (); - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Fill"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Fill"))); + } try { generate_fill (fp); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } } catch (...) { - mp_view->manager ()->cancel (); + if (mp_view->manager ()) { + mp_view->manager ()->cancel (); + } throw; } diff --git a/src/lay/lay/layGSIHelpProvider.cc b/src/lay/lay/layGSIHelpProvider.cc index eda190c67..c79df181f 100644 --- a/src/lay/lay/layGSIHelpProvider.cc +++ b/src/lay/lay/layGSIHelpProvider.cc @@ -118,6 +118,10 @@ std::string escape_xml_with_formatting (const std::string &s, bool &in_code) r += ""; } else if (sc.test ("@/u")) { r += ""; + } else if (sc.test ("@tt")) { + r += ""; + } else if (sc.test ("@/tt")) { + r += ""; } else if (sc.test ("@i")) { r += ""; } else if (sc.test ("@/i")) { diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index 0a42bcb37..a07b857a1 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -200,9 +200,10 @@ LibraryController::sync_files () reader.read (lib->layout ()); // Use the libname if there is one + db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) { - if (m->name == "libname" && ! m->value.empty ()) { - lib->set_name (m->value); + if (m->first == libname_name_id && ! m->second.value.is_nil ()) { + lib->set_name (m->second.value.to_string ()); break; } } diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 4924798da..e75c60dfa 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -386,9 +386,6 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection connect (actionUseRegularExpressions, SIGNAL (triggered ()), this, SLOT (search_editing ())); connect (actionCaseSensitive, SIGNAL (triggered ()), this, SLOT (search_editing ())); - addAction (actionSearchReplace); - connect (actionSearchReplace, SIGNAL (triggered ()), this, SLOT (search_replace ())); - searchEditBox->set_clear_button_enabled (true); searchEditBox->set_options_button_enabled (true); searchEditBox->set_options_menu (m); @@ -402,6 +399,7 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection replaceText->setPlaceholderText (tr ("Replace text ...")); #endif + connect (closeButton, SIGNAL (clicked ()), this, SLOT (close_button_clicked ())); connect (forwardButton, SIGNAL (clicked ()), this, SLOT (forward ())); connect (backwardButton, SIGNAL (clicked ()), this, SLOT (backward ())); @@ -1974,7 +1972,7 @@ MacroEditorDialog::find_next_button_clicked () apply_search (true); page->find_next (); - if (sender () != searchEditBox && sender () != replaceText) { + if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) { set_editor_focus (); } } @@ -1989,7 +1987,7 @@ MacroEditorDialog::find_prev_button_clicked () apply_search (true); page->find_prev (); - if (sender () != searchEditBox && sender () != replaceText) { + if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) { set_editor_focus (); } } @@ -2004,7 +2002,7 @@ MacroEditorDialog::replace_next_button_clicked () apply_search (true); page->replace_and_find_next (replaceText->text ()); - if (sender () != replaceText) { + if (! searchEditBox->hasFocus () && ! replaceText->hasFocus ()) { set_editor_focus (); } } @@ -2034,12 +2032,6 @@ MacroEditorDialog::search_requested (const QString &s) search_editing (); } -void -MacroEditorDialog::search_replace () -{ - searchEditBox->setFocus (Qt::TabFocusReason); -} - void MacroEditorDialog::search_editing () { @@ -2050,7 +2042,9 @@ MacroEditorDialog::search_editing () apply_search (); page->find_reset (); // search from the initial position - page->find_next (); + if (! page->has_multi_block_selection ()) { + page->find_next (); + } } void @@ -2083,7 +2077,9 @@ MacroEditorDialog::do_search_edited () apply_search (); page->find_reset (); // search from the initial position - page->find_next (); + if (! page->has_multi_block_selection ()) { + page->find_next (); + } set_editor_focus (); } @@ -2367,7 +2363,7 @@ END_PROTECTED void MacroEditorDialog::tab_close_requested (int index) { - if (m_in_exec) { + if (m_in_exec || index < 0) { return; } @@ -3741,8 +3737,12 @@ MacroEditorDialog::run (int stop_stack_depth, lym::Macro *macro) set_run_macro (macro); try { + + write_str (tl::sprintf (tl::to_string (tr ("Running macro %s\n")), macro->path ()).c_str (), OS_echo); + macro->run (); m_stop_stack_depth = -1; + } catch (tl::ExitException &) { m_stop_stack_depth = -1; // .. ignore exit exceptions .. diff --git a/src/lay/lay/layMacroEditorDialog.h b/src/lay/lay/layMacroEditorDialog.h index 7461f2a2c..54d9428df 100644 --- a/src/lay/lay/layMacroEditorDialog.h +++ b/src/lay/lay/layMacroEditorDialog.h @@ -245,7 +245,6 @@ protected slots: void file_changed (const QString &path); void file_removed (const QString &path); void clear_log (); - void search_replace (); void apply_search () { apply_search (false); diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc index 1bb507820..408949cd2 100644 --- a/src/lay/lay/layMacroEditorPage.cc +++ b/src/lay/lay/layMacroEditorPage.cc @@ -1196,66 +1196,121 @@ MacroEditorPage::replace_and_find_next (const QString &replace) return; } - QTextCursor c = mp_text->textCursor (); - if (c.hasSelection ()) { - QTextBlock b = c.block (); - int o = std::max (0, c.position () - b.position ()); - if (m_current_search.indexIn (b.text (), o) == o) { - c.insertText (interpolate_string (replace, m_current_search)); - } - } - + replace_in_selection (replace, true); find_next (); } -void +void MacroEditorPage::replace_all (const QString &replace) { if (! mp_macro || mp_macro->is_readonly ()) { return; } + replace_in_selection (replace, false); +} + +void +MacroEditorPage::replace_in_selection (const QString &replace, bool first) +{ const QTextDocument *doc = mp_text->document (); QTextBlock bs = doc->begin (), be = doc->end (); + int ps = 0; + int pe = be.length (); QTextCursor c = mp_text->textCursor (); - if (c.hasSelection ()) { - QTextBlock s = mp_text->document ()->findBlock (mp_text->textCursor ().selectionStart ()); - QTextBlock e = mp_text->document ()->findBlock (mp_text->textCursor ().selectionEnd ()); - if (e != s) { - bs = s; - be = e; - } + bool has_selection = c.hasSelection (); + bool anchor_at_end = false; + + if (has_selection) { + + anchor_at_end = (c.selectionStart () == c.position ()); + + ps = c.selectionStart (); + pe = c.selectionEnd (); + + bs = mp_text->document ()->findBlock (ps); + be = mp_text->document ()->findBlock (pe); + + } else if (first) { + + // don't replace first entry without selection + return; + } + ps -= bs.position (); + pe -= be.position (); + c.beginEditBlock (); - for (QTextBlock b = bs; b != be; b = b.next()) { + bool done = false; + + for (QTextBlock b = bs; ; b = b.next()) { int o = 0; - while (true) { + while (!done) { + + bool substitute = false; 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 + } else if (b == bs && i < ps) { + // ignore + } else if (b == be && i + m_current_search.matchedLength () > pe) { + // ignore + done = true; + } else { + substitute = true; } - QString r = interpolate_string (replace, m_current_search); + if (substitute) { - c.setPosition (i + b.position () + m_current_search.matchedLength ()); - c.setPosition (i + b.position (), QTextCursor::KeepAnchor); - c.insertText (r); + QString r = interpolate_string (replace, m_current_search); - o = i + r.size (); + c.setPosition (i + b.position () + m_current_search.matchedLength ()); + c.setPosition (i + b.position (), QTextCursor::KeepAnchor); + c.insertText (r); - } + o = i + r.size (); + + if (first) { + + // in single-selection mode, put cursor past substitution + c.setPosition (i + b.position ()); + has_selection = false; + done = true; + + } else if (b == be) { + pe += int (r.size ()) - int (m_current_search.matchedLength ()); + } + + } else { + + o = i + m_current_search.matchedLength (); + + } + + } + + if (b == be || done) { + break; + } } + if (has_selection) { + // restore selection which might have changed due to insert + c.setPosition (anchor_at_end ? be.position () + pe : bs.position () + ps); + c.setPosition (!anchor_at_end ? be.position () + pe : bs.position () + ps, QTextCursor::KeepAnchor); + } + + mp_text->setTextCursor (c); c.endEditBlock (); } @@ -1381,6 +1436,19 @@ MacroEditorPage::current_pos () const return mp_text->textCursor ().position () - mp_text->textCursor ().block ().position (); } +bool +MacroEditorPage::has_multi_block_selection () const +{ + QTextCursor c = mp_text->textCursor (); + if (c.selectionStart () != c.selectionEnd ()) { + QTextBlock s = mp_text->document ()->findBlock (c.selectionStart ()); + QTextBlock e = mp_text->document ()->findBlock (c.selectionEnd ()); + return e != s; + } else { + return false; + } +} + bool MacroEditorPage::tab_key_pressed () { diff --git a/src/lay/lay/layMacroEditorPage.h b/src/lay/lay/layMacroEditorPage.h index 960a13538..a689aefe5 100644 --- a/src/lay/lay/layMacroEditorPage.h +++ b/src/lay/lay/layMacroEditorPage.h @@ -246,6 +246,7 @@ public: int current_line () const; int current_pos () const; + bool has_multi_block_selection () const; void set_debugging_on (bool debugging_on); @@ -313,6 +314,7 @@ private: void fill_completer_list (); void complete (); QTextCursor get_completer_cursor (int &pos0, int &pos); + void replace_in_selection (const QString &replace, bool first); bool eventFilter (QObject *watched, QEvent *event); }; diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index be55259cc..390263bb6 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -590,12 +590,7 @@ MainWindow::file_changed_timer () } - // Prevent recursive signals - m_file_changed_timer.blockSignals (true); - - std::set reloaded_files; - -BEGIN_PROTECTED + std::set notified_files; // Make the names unique std::sort (m_changed_files.begin (), m_changed_files.end ()); @@ -605,68 +600,38 @@ BEGIN_PROTECTED return; } - QString msg; - - if (m_changed_files.size () == 1) { - msg = QObject::tr ("The following file has been changed on disk:\n\n"); - for (std::vector::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) { - msg += QString::fromUtf8 (" %1\n").arg (*f); - } - msg += tr ("\nReload this file?"); - } else { - msg = QObject::tr ("The following files have been changed on disk:\n\n"); - for (std::vector::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) { - msg += QString::fromUtf8 (" %1\n").arg (*f); - } - msg += tr ("\nReload these files?"); - } + // adds a notification to the involved views that the file has changed std::vector changed_files; changed_files.swap (m_changed_files); - if (QMessageBox::question (this, tr ("Reload Files"), msg, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + std::map > > views_per_file; - m_file_changed_timer.blockSignals (false); - - std::map > views_per_file; - - for (std::vector::iterator v = mp_views.begin (); v != mp_views.end (); ++v) { - for (int cv = 0; cv < int ((*v)->view ()->cellviews ()); ++cv) { - views_per_file [tl::to_qstring ((*v)->view ()->cellview (cv)->filename ())] = std::make_pair (*v, cv); - } + for (auto v = mp_views.begin (); v != mp_views.end (); ++v) { + for (int cv = 0; cv < int ((*v)->view ()->cellviews ()); ++cv) { + views_per_file [tl::to_qstring ((*v)->view ()->cellview (cv)->filename ())].push_back (std::make_pair (*v, cv)); } - - for (std::vector::const_iterator f = changed_files.begin (); f != changed_files.end (); ++f) { - std::map >::const_iterator v = views_per_file.find (*f); - if (v != views_per_file.end ()) { - v->second.first->view ()->reload_layout (v->second.second); - reloaded_files.insert (*f); - } - } - } -END_PROTECTED + for (auto f = changed_files.begin (); f != changed_files.end (); ++f) { - m_file_changed_timer.blockSignals (false); + auto v = views_per_file.find (*f); + if (v != views_per_file.end ()) { - // While the message box was open, new request might have collected - remove - // the ones we just reloaded and restart the timer - if (! m_changed_files.empty ()) { + for (auto w = v->second.begin (); w != v->second.end (); ++w) { + + std::string title; + if (w->first->view ()->cellviews () > 1) { + title = tl::sprintf (tl::to_string (tr ("Layout file @%d (%s) has changed on disk")), w->second + 1, tl::filename (tl::to_string (*f))); + } else { + title = tl::sprintf (tl::to_string (tr ("Layout file (%s) has changed on disk")), tl::filename (tl::to_string (*f))); + } + + lay::LayoutViewNotification n ("reload", title, tl::Variant (tl::to_string (*f))); + n.add_action ("reload", tl::to_string (tr ("Reload"))); + w->first->add_notification (n); - std::vector changed_files; - changed_files.swap (m_changed_files); - for (std::vector::const_iterator f = changed_files.begin (); f != changed_files.end (); ++f) { - if (reloaded_files.find (*f) == reloaded_files.end ()) { - m_changed_files.push_back (*f); } - } - - if (! m_changed_files.empty ()) { - - // Wait a little to let more to allow for more reload requests to collect - m_file_changed_timer.setInterval (300); - m_file_changed_timer.start (); } @@ -2062,8 +2027,17 @@ MainWindow::restore_session (const std::string &fn) m_current_session = fn; lay::Session session; session.load (fn); - session.restore (*this); - read_dock_widget_state (); + + begin_restore_session (); + try { + session.restore (*this); + read_dock_widget_state (); + end_restore_session (); + } catch (...) { + read_dock_widget_state (); + end_restore_session (); + throw; + } } void diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index e6b4d76aa..73fd6e574 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -532,28 +532,6 @@ public: */ std::string all_layout_file_formats () const; - /** - * @brief An event indicating that the current view has changed - * This event is fired if the current view was changed. - * The current view's reference is updated if the event is issued. - */ - tl::Event current_view_changed_event; - - /** - * @brief An event indicating that a view was closed - * If a view is closed, this event is triggered. When the signal is sent, - * the view still points to the view being closed which is still valid. - * The integer parameter will receive the index of the view about to be closed. - */ - tl::event view_closed_event; - - /** - * @brief An event indicating that a new view is created - * If a new view is created, this event will be triggered. - * The integer parameter will receive the index of the view that was created. - */ - tl::event view_created_event; - /** * @brief Adds an entry to the MRU list with the initial technology */ @@ -595,6 +573,38 @@ public: */ void dropEvent(QDropEvent *event); + /** + * @brief An event indicating that the current view has changed + * This event is fired if the current view was changed. + * The current view's reference is updated if the event is issued. + */ + tl::Event current_view_changed_event; + + /** + * @brief An event indicating that a view was closed + * If a view is closed, this event is triggered. When the signal is sent, + * the view still points to the view being closed which is still valid. + * The integer parameter will receive the index of the view about to be closed. + */ + tl::event view_closed_event; + + /** + * @brief An event indicating that a new view is created + * If a new view is created, this event will be triggered. + * The integer parameter will receive the index of the view that was created. + */ + tl::event view_created_event; + + /** + * @brief An event indicating the start of a session restore + */ + tl::Event begin_restore_session; + + /** + * @brief An event indicating the end of a session restore + */ + tl::Event end_restore_session; + signals: void closed (); diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 91f35fe62..dc856b88b 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -1814,10 +1814,14 @@ BEGIN_PROTECTED cancel_exec (); - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Delete all"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Delete all"))); + } mp_view->cancel (); issue_query (build_delete_expression (), 0, false); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } END_PROTECTED } @@ -1862,15 +1866,19 @@ BEGIN_PROTECTED if (! sel.empty ()) { - if (sender () == delete_selected_button) { - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Delete selected"))); - } else { - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Replace selected"))); + if (mp_view->manager ()) { + if (sender () == delete_selected_button) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Delete selected"))); + } else { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Replace selected"))); + } } mp_view->cancel (); issue_query (m_execute_query, &selected_items, false); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } issue_query (m_find_query, 0, true); @@ -1889,10 +1897,14 @@ BEGIN_PROTECTED m_execute_query.clear (); m_find_query.clear (); - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Replace all"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Replace all"))); + } mp_view->cancel (); issue_query (build_replace_expression (), 0, false); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } END_PROTECTED } @@ -1907,10 +1919,14 @@ BEGIN_PROTECTED m_execute_query.clear (); m_find_query.clear (); - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Execute custom query"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Execute custom query"))); + } mp_view->cancel (); issue_query (tl::to_string (custom_query->toPlainText ()), 0, true); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } END_PROTECTED } diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index 54478e7ef..43b8af32b 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -335,11 +335,17 @@ static void save_image_with_options (lay::LayoutViewBase *view, const std::strin view->save_image_with_options (fn, width, height, linewidth, oversampling, resolution, tl::Color (), tl::Color (), tl::Color (), target_box, monochrome); } -#if defined(HAVE_QTBINDINGS) +#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS) static QImage get_image_with_options (lay::LayoutViewBase *view, unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, const db::DBox &target_box, bool monochrome) { return view->get_image_with_options (width, height, linewidth, oversampling, resolution, tl::Color (), tl::Color (), tl::Color (), target_box, monochrome); } + +static QWidget *widget (lay::LayoutViewBase *view) +{ + return view->widget (); +} + #endif static std::vector @@ -1158,7 +1164,12 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutVi "The image is drawn synchronously with the given width and height. Drawing may take some time. " "Monochrome images don't have background or annotation objects currently.\n" "\n" - "This method has been introduced in 0.23.10.\n" + "This method has been introduced in version 0.23.10.\n" + ) + + gsi::method_ext ("widget", &widget, + "@brief Gets the QWidget object of the view\n" + "\n" + "This method has been introduced in version 0.28.7.\n" ) + #endif gsi::method ("get_screenshot_pixels", static_cast (&lay::LayoutViewBase::get_screenshot_pb), diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index af0a981f1..73725629b 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -791,35 +791,6 @@ Bitmap::render_contour (std::vector &edges) } } -static unsigned char next_char_latin1_from_utf8 (const char *&cp, const char *cpf = 0) -{ - unsigned char c = *cp; - if ((c & 0xe0) == 0xc0) { - if ((cp[1] & 0xc0) == 0x80 && (! cpf || cpf > cp + 1)) { - unsigned int x = ((unsigned int) ((unsigned char) c & 0x1f) << 6) | (unsigned int) (cp[1] & 0x3f); - cp += 1; - if (x < 255) { - c = x; - } else { - c = '?'; - } - } else { - c = '?'; - } - } else if ((c & 0xf0) == 0xe0) { - if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (! cpf || cpf > cp + 2)) { - cp += 2; - } - c = '?'; - } else if ((c & 0xf8) == 0xf0) { - if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (cp[3] & 0xc0) == 0x80 && (! cpf || cpf > cp + 3)) { - cp += 3; - } - c = '?'; - } - return c; -} - void Bitmap::render_text (const lay::RenderText &text) { @@ -830,12 +801,11 @@ Bitmap::render_text (const lay::RenderText &text) // count the lines and max. characters per line unsigned int lines = 1; - for (const char *cp = text.text.c_str (); *cp; ++cp) { - if (*cp == '\012' || *cp == '\015') { - if (*cp == '\015' && cp[1] == '\012') { - ++cp; - } + for (const char *cp = text.text.c_str (); *cp; ) { + if (tl::skip_newline (cp)) { ++lines; + } else { + ++cp; } } @@ -858,10 +828,9 @@ Bitmap::render_text (const lay::RenderText &text) unsigned int length = 0; const char *cp = cp1; - while (*cp && *cp != '\012' && *cp != '\015') { - next_char_latin1_from_utf8 (cp); + while (*cp && !tl::is_newline (*cp)) { + tl::utf32_from_utf8 (cp); ++length; - ++cp; } double xx; @@ -878,13 +847,15 @@ Bitmap::render_text (const lay::RenderText &text) if (y > -0.5 && y < double (height () + ff.height () - 1) - 0.5) { - for ( ; cp1 != cp; ++cp1) { + while (cp1 != cp) { - unsigned char c = next_char_latin1_from_utf8 (cp1, cp); + uint32_t c = tl::utf32_from_utf8 (cp1, cp); + if (c < uint32_t (ff.first_char ()) || c >= uint32_t (ff.n_chars ()) + ff.first_char ()) { + // NOTE: '?' needs to be a valid character always + c = uint32_t ('?'); + } - size_t cc = c; // to suppress a compiler warning .. - if (c >= ff.first_char () && cc < size_t (ff.n_chars ()) + size_t (ff.first_char ()) - && xx > -100.0 && xx < double (width ())) { + if (xx > -100.0 && xx < double (width ())) { fill_pattern (int (y + 0.5), int (floor (xx)), ff.data () + (c - ff.first_char ()) * ff.height () * ff.stride (), ff.stride (), ff.height ()); } @@ -897,11 +868,7 @@ Bitmap::render_text (const lay::RenderText &text) } // next line - if (*cp1 == '\012' || *cp1 == '\015') { - if (*cp1 == '\015' && cp1[1] == '\012') { - ++cp1; - } - ++cp1; + if (tl::skip_newline (cp1)) { y -= double (ff.line_height ()); } diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 6fa5b6661..168efce12 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -23,6 +23,7 @@ #include "tlProgress.h" #include "layFinder.h" +#include "layTextInfo.h" namespace lay { @@ -86,10 +87,11 @@ Finder::closer (double d) } void -Finder::start (lay::LayoutViewBase *view, const lay::CellView &cv, unsigned int cv_index, const std::vector &trans, const db::Box ®ion, int min_level, int max_level, const std::vector &layers) +Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers) { + const lay::CellView &cv = view->cellview (cv_index); + m_layers = layers; - m_region = region; mp_layout = &cv->layout (); mp_view = view; m_cv_index = cv_index; @@ -97,17 +99,27 @@ Finder::start (lay::LayoutViewBase *view, const lay::CellView &cv, unsigned int m_max_level = std::max (m_min_level, std::min (max_level, m_top_level_sel ? ((int) cv.specific_path ().size () + 1) : max_level)); if (layers.size () == 1) { + m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]); m_cell_box_convert = db::box_convert ((unsigned int) layers [0]); + } else { + m_box_convert = db::box_convert (*mp_layout); m_cell_box_convert = db::box_convert (); + } m_path.erase (m_path.begin (), m_path.end ()); - for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) { - do_find (*cv.cell (), int (cv.specific_path ().size ()), *t * cv.context_trans ()); + for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) { + + db::VCplxTrans it = (*t * db::CplxTrans (mp_layout->dbu ())).inverted (); + m_region = it * region; + m_scan_region = it * scan_region; + + do_find (*cv.cell (), int (cv.specific_path ().size ()), view->viewport ().trans () * *t, cv.context_trans ()); + } } @@ -125,8 +137,11 @@ Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, double &dis double d1 = p1.double_distance (m_region.center ()); double d2 = p2.double_distance (m_region.center ()); - // snap to the point - nothing can get closer - distance = 0.0; + double d = std::min (d1, d2); + if (! match || d < distance) { + distance = d; + } + if (d1 < d2) { ret = 1; } else { @@ -155,7 +170,7 @@ Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, double &dis } void -Finder::do_find (const db::Cell &cell, int level, const db::ICplxTrans &t) +Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t) { if (level <= m_max_level /*take level of cell itself*/ && cell.is_proxy () @@ -164,33 +179,38 @@ Finder::do_find (const db::Cell &cell, int level, const db::ICplxTrans &t) // when looking at the guiding shape layer, we can visit this cell as well allowing to find the guiding shapes - db::Box touch_box (t.inverted () * m_region); + db::ICplxTrans it = t.inverted (); + db::Box scan_box (it * m_scan_region); + db::Box hit_box (it * m_region); if (level >= m_min_level) { - visit_cell (cell, touch_box, t, level); + visit_cell (cell, hit_box, scan_box, vp, t, level); } } else if (level < m_max_level - && (t * m_cell_box_convert (cell)).touches (m_region) + && (t * m_cell_box_convert (cell)).touches (m_scan_region) && (mp_view->select_inside_pcells_mode () || !cell.is_proxy ()) && !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index)) { - db::Box touch_box (t.inverted () * m_region); + db::ICplxTrans it = t.inverted (); + db::Box scan_box (it * m_scan_region); + db::Box hit_box (it * m_region); if (level >= m_min_level) { - visit_cell (cell, touch_box, t, level); + visit_cell (cell, hit_box, scan_box, vp, t, level); } - db::Cell::touching_iterator inst = cell.begin_touching (touch_box); + db::Cell::touching_iterator inst = cell.begin_touching (scan_box); while (! inst.at_end ()) { const db::CellInstArray &cell_inst = inst->cell_inst (); - for (db::CellInstArray::iterator p = cell_inst.begin_touching (touch_box, m_box_convert); ! p.at_end (); ++p) { + for (db::CellInstArray::iterator p = cell_inst.begin_touching (scan_box, m_box_convert); ! p.at_end (); ++p) { m_path.push_back (db::InstElement (*inst, p)); do_find (mp_layout->cell (cell_inst.object ().cell_index ()), level + 1, + vp, t * cell_inst.complex_trans (*p)); m_path.pop_back (); @@ -211,6 +231,7 @@ ShapeFinder::ShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator : Finder (point_mode, top_level_sel), mp_excludes ((excludes && !excludes->empty ()) ? excludes : 0), m_flags (flags), m_cv_index (0), m_topcell (0), + mp_text_info (0), mp_prop_sel (0), m_inv_prop_sel (false), mp_progress (0) { m_tries = point_sel_tests; @@ -275,6 +296,9 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu) m_context_layers.clear (); m_cells_with_context.clear (); + lay::TextInfo text_info (view); + mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0; + std::vector lprops; for (lay::LayerPropertiesConstIterator lp = view->begin_layers (); ! lp.at_end (); ++lp) { if (lp->is_visual ()) { @@ -340,6 +364,9 @@ ShapeFinder::find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops m_cells_with_context.clear (); m_context_layers.clear (); + lay::TextInfo text_info (view); + mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0; + std::vector layers; layers.push_back (lprops.layer_index ()); bool result = find_internal (view, lprops.cellview_index (), &lprops.prop_sel (), lprops.inverse_prop_sel (), lprops.hier_levels (), lprops.trans (), layers, region_mu); @@ -360,14 +387,6 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co m_topcell = cv.cell_index (); - double dbu = cv->layout ().dbu (); - db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu; - std::vector trans; - trans.reserve(trans_mu.size()); - for (std::vector::const_iterator t = trans_mu.begin(); t != trans_mu.end(); ++t) { - trans.push_back(db::VCplxTrans(1.0 / dbu) * *t * db::CplxTrans(dbu)); - } - mp_prop_sel = prop_sel; m_inv_prop_sel = inv_prop_sel; @@ -382,13 +401,34 @@ ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, co max_level = hier_sel.to_level (ctx_path_length, max_level); } - // actually find + auto flags_saved = m_flags; + try { - start (view, cv, m_cv_index, trans, region, min_level, max_level, layers); + + if ((m_flags & db::ShapeIterator::Texts) != 0 && mp_text_info) { + + m_flags = db::ShapeIterator::Texts; + + // for catching all labels we search the whole view area + db::DBox scan_region_mu = view->viewport ().box (); + start (view, m_cv_index, trans_mu, region_mu, scan_region_mu, min_level, max_level, layers); + + m_flags = db::ShapeIterator::flags_type (flags_saved - db::ShapeIterator::Texts); + + } + + // another pass with tight search box and without texts + start (view, m_cv_index, trans_mu, region_mu, region_mu, min_level, max_level, layers); + } catch (StopException) { - // .. + // ... + } catch (...) { + m_flags = flags_saved; + throw; } + m_flags = flags_saved; + // return true if anything was found return ! m_founds.empty (); } @@ -406,7 +446,7 @@ ShapeFinder::checkpoint () } void -ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/) +ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int /*level*/) { if (! m_context_layers.empty ()) { @@ -436,17 +476,27 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { const db::Shapes &shapes = cell.shapes ((unsigned int) *l); - db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel); + db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel); while (! shape.at_end ()) { checkpoint (); + db::Box bbox; + if (text_info () && shape->is_text ()) { + db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; + db::Text text; + shape->text (text); + bbox = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp); + } else { + bbox = shape->bbox (); + } + // in box mode, just test the boxes - if (shape->bbox ().inside (search_box)) { + if (bbox.inside (hit_box)) { m_founds.push_back (lay::ObjectInstPath ()); m_founds.back ().set_cv_index (m_cv_index); @@ -474,13 +524,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { checkpoint (); const db::Shapes &shapes = cell.shapes (*l); - db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel); + db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel); while (! shape.at_end ()) { bool match = false; @@ -488,7 +538,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const checkpoint (); - db::Point point (search_box.center ()); + db::Point point (hit_box.center ()); // in point mode, test the edges and use a "closest" criterion if (shape->is_polygon ()) { @@ -529,9 +579,15 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const match = true; } - } else if (shape->is_box ()) { + } else if (shape->is_box () || shape->is_text ()) { - const db::Box &box = shape->box (); + db::Box box = shape->bbox (); + if (text_info () && shape->is_text ()) { + db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t; + db::Text text; + shape->text (text); + box = t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp); + } // point-like boxes are handles which attract the finder if (box.width () == 0 && box.height () == 0) { @@ -545,21 +601,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const test_edge (t, *e, d, match); } - if (! match && box.contains (search_box.center ())) { - d = t.ctrans (poly_dist (poly.begin_edge (), point)); + if (! match && box.contains (hit_box.center ())) { + d = t.ctrans (poly_dist (poly.begin_edge (), point)); match = true; } } - } else if (shape->is_text ()) { - - db::Point tp (shape->text_trans () * db::Point ()); - if (search_box.contains (tp)) { - d = t.ctrans (tp.distance (search_box.center ())); - match = true; - } - } if (match) { @@ -679,14 +727,11 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db m_topcell = cv.cell ()->cell_index (); mp_view = view; - double dbu = cv->layout ().dbu (); - db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu; - // actually find try { - std::vector tv; - tv.push_back (db::VCplxTrans (1.0 / dbu) * trans_mu * db::CplxTrans (dbu)); - start (view, cv, cv_index, tv, region, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ()); + std::vector tv; + tv.push_back (trans_mu); + start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ()); } catch (StopException) { // .. } @@ -696,7 +741,7 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db } void -InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level) +InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::Box & /*scan_box*/, const db::DCplxTrans & /*vp*/, const db::ICplxTrans &t, int level) { if (! point_mode ()) { @@ -894,5 +939,5 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d } -} // namespace edt +} // namespace lay diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index e269fa3e0..065e69741 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -44,6 +44,8 @@ namespace tl namespace lay { +class TextInfo; + /** * @brief A generic finder class * @@ -152,8 +154,17 @@ protected: * are used). For each matching cell, the "visit_cell" method is called. A * path of instantiations up to the top cell is maintained and accessible by * the path() accessor. + * + * @param view The layout view to run the scan on + * @param cv_index The cell view to run the scan on + * @param trans A set of visual transformations applied to the display (layer properties transformations) in micron space + * @param region The hit region which the object is checked against + * @param scan_region The region where the object is looked up (can be bigger than the hit region for visual label box detection) + * @param min_level The minimum hierarchy level to check + * @param max_level The maximum hierarchy level to check + * @param layers A set of layers to check */ - void start (LayoutViewBase *view, const lay::CellView &cv, unsigned int cv_index, const std::vector &trans, const db::Box ®ion, int min_level, int max_level, const std::vector &layers = std::vector ()); + void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ()); /** * @brief Provide a basic edge test facility @@ -172,7 +183,7 @@ protected: unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match); private: - void do_find (const db::Cell &cell, int level, const db::ICplxTrans &t); + void do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t); /** * @brief Visitor sugar function @@ -181,7 +192,7 @@ private: * cell. It may use the "closer" method to determine if something is closer * to whatever. */ - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level) = 0; + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level) = 0; int m_min_level, m_max_level; std::vector m_path; @@ -189,6 +200,7 @@ private: lay::LayoutViewBase *mp_view; unsigned int m_cv_index; db::Box m_region; + db::Box m_scan_region; std::vector m_layers; double m_distance; bool m_point_mode; @@ -233,7 +245,12 @@ protected: return m_flags; } - unsigned int cv_index () const + const lay::TextInfo *text_info () const + { + return mp_text_info; + } + + unsigned int cv_index () const { return m_cv_index; } @@ -261,7 +278,8 @@ protected: void checkpoint (); private: - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/); + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level); + bool find_internal (LayoutViewBase *view, unsigned int cv_index, const std::set *prop_sel, @@ -276,6 +294,7 @@ private: db::ShapeIterator::flags_type m_flags; unsigned int m_cv_index; db::cell_index_type m_topcell; + const lay::TextInfo *mp_text_info; const std::set *mp_prop_sel; bool m_inv_prop_sel; int m_tries; @@ -314,7 +333,8 @@ public: } private: - virtual void visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level); + virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level); + bool find_internal (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox ®ion_mu); unsigned int m_cv_index; diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h index f92a48855..938219763 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.h +++ b/src/laybasic/laybasic/layLayoutCanvas.h @@ -320,6 +320,11 @@ public: return m_line_styles; } + /** + * @brief Reimplementation of ViewObjectCanvas: Resolution + */ + double resolution () const; + /** * @brief Reimplementation of ViewObjectCanvas: Background color */ @@ -444,8 +449,6 @@ private: void do_redraw_all (bool force_redraw = true); void prepare_drawing (); - virtual double resolution () const; - const std::vector &scaled_view_ops (unsigned int lw); }; diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c697441fc..fb4ffd020 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -260,8 +260,6 @@ LayoutViewBase::LayoutViewBase (lay::LayoutView *ui, db::Manager *manager, bool { // either it's us or the parent has a dispatcher tl_assert (dispatcher () != 0); - - init (manager); } void @@ -3097,7 +3095,7 @@ LayoutViewBase::reload_layout (unsigned int cv_index) // when reading the file, it must have the layers created as well lay::CellView cv_empty; - handle = new lay::LayoutHandle (new db::Layout (manager ()), filename); + handle = new lay::LayoutHandle (new db::Layout (is_editable (), manager ()), filename); handle->set_tech_name (technology); cv_empty.set (handle); @@ -3110,7 +3108,7 @@ LayoutViewBase::reload_layout (unsigned int cv_index) // create a new handle lay::CellView cv; - handle = new lay::LayoutHandle (new db::Layout (manager ()), filename); + handle = new lay::LayoutHandle (new db::Layout (is_editable (), manager ()), filename); cv.set (handle); try { @@ -3202,6 +3200,25 @@ LayoutViewBase::reload_layout (unsigned int cv_index) goto_view (state); } +static void +get_lyp_from_meta_info (const db::Layout &layout, std::string &lyp_file, bool &add_other_layers) +{ + db::Layout::meta_info_name_id_type layer_properties_file_name_id = layout.meta_info_name_id ("layer-properties-file"); + db::Layout::meta_info_name_id_type layer_properties_add_other_layers_name_id = layout.meta_info_name_id ("layer-properties-add-other-layers"); + + for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { + if (meta->first == layer_properties_file_name_id) { + lyp_file = meta->second.value.to_string (); + } + if (meta->first == layer_properties_add_other_layers_name_id) { + try { + add_other_layers = meta->second.value.to_bool (); + } catch (...) { + } + } + } +} + unsigned int LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers) { @@ -3266,17 +3283,7 @@ LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, } // Give the layout object a chance to specify a certain layer property file - for (db::Layout::meta_info_iterator meta = cv->layout ().begin_meta (); meta != cv->layout ().end_meta (); ++meta) { - if (meta->name == "layer-properties-file") { - lyp_file = meta->value; - } - if (meta->name == "layer-properties-add-other-layers") { - try { - tl::from_string (meta->value, add_other_layers); - } catch (...) { - } - } - } + get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers); // interpolate the layout properties file name tl::Eval expr; @@ -3330,7 +3337,7 @@ LayoutViewBase::create_layout (const std::string &technology, bool add_cellview, { const db::Technology *tech = db::Technologies::instance ()->technology_by_name (technology); - db::Layout *layout = new db::Layout (m_editable, manager ()); + db::Layout *layout = new db::Layout (is_editable (), manager ()); if (tech) { layout->dbu (tech->dbu ()); } @@ -3357,7 +3364,7 @@ LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOp // create a new layout handle lay::CellView cv; - lay::LayoutHandle *handle = new lay::LayoutHandle (new db::Layout (manager ()), filename); + lay::LayoutHandle *handle = new lay::LayoutHandle (new db::Layout (is_editable (), manager ()), filename); cv.set (handle); unsigned int cv_index; @@ -3438,17 +3445,7 @@ LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOp } // Give the layout object a chance to specify a certain layer property file - for (db::Layout::meta_info_iterator meta = cv->layout().begin_meta (); meta != cv->layout().end_meta (); ++meta) { - if (meta->name == "layer-properties-file") { - lyp_file = meta->value; - } - if (meta->name == "layer-properties-add-other-layers") { - try { - tl::from_string (meta->value, add_other_layers); - } catch (...) { - } - } - } + get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers); // interpolate the layout properties file name tl::Eval expr; @@ -3853,13 +3850,26 @@ LayoutViewBase::pan_center (const db::DPoint &p) void LayoutViewBase::zoom_in () { - shift_window (zoom_factor, 0.0, 0.0); + zoom_by (zoom_factor); } void LayoutViewBase::zoom_out () { - shift_window (1.0 / zoom_factor, 0.0, 0.0); + zoom_by (1.0 / zoom_factor); +} + +void +LayoutViewBase::zoom_by (double f) +{ + db::DBox b = mp_canvas->viewport ().box (); + + db::DPoint c = b.center (); + if (mp_canvas->mouse_in_window ()) { + c = mp_canvas->mouse_position_um (); + } + + zoom_box ((b.moved (db::DPoint () - c) * f).moved (c - db::DPoint ())); } void diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 0263a208a..f22e4cc34 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -54,6 +54,11 @@ #include "tlDeferredExecution.h" #include "dbInstElement.h" +#if defined(HAVE_QT) +# include +class QWidget; +#endif + namespace rdb { class Database; } @@ -187,11 +192,6 @@ public: */ LayoutViewBase (db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, unsigned int options = (unsigned int) LV_Normal); - /** - * @brief Constructor - */ - LayoutViewBase (lay::LayoutView *ui, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, unsigned int options = (unsigned int) LV_Normal); - /** * @brief Destructor */ @@ -980,6 +980,16 @@ public: */ tl::BitmapBuffer get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box); +#if defined(HAVE_QT) + /** + * @brief Gets the widget object that view is embedded in + */ + virtual QWidget *widget () + { + return 0; + } +#endif + /** * @brief Hierarchy level selection setter */ @@ -1706,6 +1716,14 @@ public: return mp_canvas; } + /** + * @brief Gets the canvas object (const version) + */ + const lay::LayoutCanvas *canvas () const + { + return mp_canvas; + } + #if defined(HAVE_QT) /** * @brief Gets the layer control panel @@ -2846,8 +2864,6 @@ private: tl::Clock m_clock, m_last_checked; - void init (db::Manager *mgr); - void do_prop_changed (); void do_redraw (int layer); void do_redraw (); @@ -2866,6 +2882,8 @@ private: bool has_max_hier () const; int max_hier_level () const; + void zoom_by (double f); + void update_event_handlers (); void viewport_changed (); void cellview_changed (unsigned int index); @@ -2876,6 +2894,13 @@ private: void merge_dither_pattern (lay::LayerPropertiesList &props); protected: + /** + * @brief Constructor for calling from a LayoutView + */ + LayoutViewBase (lay::LayoutView *ui, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, unsigned int options = (unsigned int) LV_Normal); + + void init (db::Manager *mgr); + lay::Plugin *active_plugin () const { return mp_active_plugin; diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc index b540e2ad2..16d0ca19b 100644 --- a/src/laybasic/laybasic/layMarker.cc +++ b/src/laybasic/laybasic/layMarker.cc @@ -30,11 +30,19 @@ #include "layViewOp.h" #include "layRenderer.h" #include "layLayoutViewBase.h" +#include "layTextInfo.h" #include "tlAssert.h" namespace lay { +static db::DVector text_box_enlargement (const db::DCplxTrans &vp_trans) +{ + // 4.0 is the text box border in pixels + double b = 4.0 / vp_trans.mag (); + return db::DVector (b, b); +} + // ------------------------------------------------------------------------ void render_cell_inst (const db::Layout &layout, const db::CellInstArray &inst, const db::CplxTrans &trans, lay::Renderer &r, @@ -625,11 +633,28 @@ ShapeMarker::render (const Viewport &vp, ViewObjectCanvas &canvas) if (trans_vector ()) { for (std::vector::const_iterator tr = trans_vector ()->begin (); tr != trans_vector ()->end (); ++tr) { db::CplxTrans t = vp.trans () * *tr * trans (); + if (m_shape.is_text () && text) { + // draw a frame around the text + lay::TextInfo ti (view ()); + db::DCplxTrans vp_trans = vp.trans () * *tr; + db::Text t; + m_shape.text (t); + db::DBox box = ti.bbox (trans () * t, vp_trans).enlarged (text_box_enlargement (vp_trans)); + r.draw (box, vp_trans, 0, text, 0, 0); + } r.draw (m_shape, t, fill, contour, vertex, text); r.draw_propstring (m_shape, &ly->properties_repository (), text, t); } } else { db::CplxTrans t = vp.trans () * trans (); + if (m_shape.is_text () && text) { + // draw a frame around the text + lay::TextInfo ti (view ()); + db::Text t; + m_shape.text (t); + db::DBox box = ti.bbox (trans () * t, vp.trans ()).enlarged (text_box_enlargement (vp.trans ())); + r.draw (box, vp.trans (), 0, text, 0, 0); + } r.draw (m_shape, t, fill, contour, vertex, text); r.draw_propstring (m_shape, &ly->properties_repository (), text, t); } @@ -1081,8 +1106,16 @@ Marker::draw (lay::Renderer &r, const db::CplxTrans &t, lay::CanvasPlane *fill, } else if (m_type == DPath) { r.draw (*m_object.dpath, db::DCplxTrans (t), fill, contour, vertex, text); } else if (m_type == Text) { + // TODO: in order to draw the box we'd need a separation of dbu-to-micron and micron-to-pixel transformations ... r.draw (*m_object.text, t, fill, contour, vertex, text); } else if (m_type == DText) { + if (view () && text) { + // draw a frame around the text + lay::TextInfo ti (view ()); + db::DCplxTrans dt (t); + db::DBox box = ti.bbox (*m_object.dtext, dt).enlarged (text_box_enlargement (dt)); + r.draw (box, dt, 0, text, 0, 0); + } r.draw (*m_object.dtext, db::DCplxTrans (t), fill, contour, vertex, text); } else if (m_type == Edge) { r.draw (*m_object.edge, t, fill, contour, vertex, text); @@ -1279,6 +1312,12 @@ DMarker::render (const Viewport &vp, ViewObjectCanvas &canvas) } else if (m_type == Path) { r.draw (*m_object.path, t, fill, contour, vertex, text); } else if (m_type == Text) { + if (view () && text) { + // draw a frame around the text + lay::TextInfo ti (view ()); + db::DBox box = ti.bbox (*m_object.text, t).enlarged (text_box_enlargement (t)); + r.draw (box, t, 0, text, 0, 0); + } r.draw (*m_object.text, t, fill, contour, vertex, text); } else if (m_type == Edge) { r.draw (*m_object.edge, t, fill, contour, vertex, text); diff --git a/src/laybasic/laybasic/layMarker.h b/src/laybasic/laybasic/layMarker.h index c687e4eff..d807dbf24 100644 --- a/src/laybasic/laybasic/layMarker.h +++ b/src/laybasic/laybasic/layMarker.h @@ -226,6 +226,12 @@ public: protected: void get_bitmaps (const Viewport &vp, ViewObjectCanvas &canvas, lay::CanvasPlane *&fill, lay::CanvasPlane *&frame, lay::CanvasPlane *&vertex, lay::CanvasPlane *&text); + lay::LayoutViewBase *view () + { + return mp_view; + } + +private: tl::Color m_color; tl::Color m_frame_color; char m_line_width, m_vertex_size, m_halo; diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc new file mode 100644 index 000000000..8b700c127 --- /dev/null +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -0,0 +1,167 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "layTextInfo.h" +#include "layFixedFont.h" +#include "layLayoutViewBase.h" +#include "layLayoutCanvas.h" + +namespace lay +{ + +TextInfo::TextInfo (const LayoutViewBase *view) + : m_default_text_size (view->default_text_size ()), + m_default_font (db::Font (view->text_font ())), + m_apply_text_trans (view->apply_text_trans ()), + m_resolution (view->canvas ()->resolution ()) +{ + // .. nothing yet .. +} + +TextInfo::TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution) + : m_default_text_size (default_text_size), + m_default_font (default_font), + m_apply_text_trans (apply_text_trans), + m_resolution (resolution) +{ + // .. nothing yet .. +} + +db::DBox +TextInfo::bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const +{ + // offset in pixels (space between origin and text) + const double offset = 2.0 / vp_trans.mag (); + + db::DTrans tt = text.trans (); + db::DCoord h; + db::Font font = text.font () == db::NoFont ? m_default_font : text.font (); + + if (m_apply_text_trans && font != db::NoFont && font != db::DefaultFont) { + h = text.size () > 0 ? text.size () : m_default_text_size; + } else { + tt = db::DTrans (vp_trans.fp_trans ().inverted ().angle (), tt.disp ()); + h = m_default_text_size; + } + + db::HAlign halign = text.halign (); + db::VAlign valign = text.valign (); + + double fy = 0.0; + if (valign == db::VAlignBottom || valign == db::NoVAlign) { + fy = 1.0; + } else if (valign == db::VAlignTop) { + fy = -1.0; + } + + double fx = 0.0; + if (halign == db::HAlignLeft || halign == db::NoHAlign) { + fx = 1.0; + } else if (halign == db::HAlignRight) { + fx = -1.0; + } + + db::DPoint dp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h); + db::DPoint dp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h); + + if (font == db::DefaultFont) { + + db::DBox b (dp1 * vp_trans.mag (), dp2 * vp_trans.mag ()); + + const lay::FixedFont &ff = lay::FixedFont::get_font (m_resolution); + + // count the lines + + unsigned int lines = 1; + for (const char *cp = text.string (); *cp; ) { + if (tl::skip_newline (cp)) { + ++lines; + } else { + tl::utf32_from_utf8 (cp); + } + } + + // compute the actual top left position + double ytop; + double htot = double (ff.line_height () * (lines - 1) + ff.height ()); + if (valign == db::VAlignBottom || valign == db::NoVAlign) { + ytop = b.bottom (); + ytop += htot; + } else if (valign == db::VAlignCenter) { + ytop = b.center ().y (); + ytop += htot * 0.5; + } else { + ytop = b.top (); + } + + // compute the bottom position + double ybottom = ytop - htot; + + // left and right position + bool first = true; + double xleft = 0.0, xright = 0.0; + + const char *cp = text.string (); + while (*cp) { + + unsigned int length = 0; + while (*cp && !tl::skip_newline (cp)) { + tl::utf32_from_utf8 (cp); + ++length; + } + + double xl; + if (halign == db::HAlignRight) { + xl = b.right (); + xl -= double (ff.width () * length); + } else if (halign == db::HAlignCenter) { + xl = b.center ().x (); + xl -= double (ff.width () * length / 2); + } else { + xl = b.left (); + } + + double xr = xl + double (ff.width () * length); + + if (first || xl < xleft) { + xleft = xl; + } + if (first || xr > xright) { + xright = xr; + } + first = false; + + } + + return (db::DBox (xleft, ybottom, xright, ytop) * (1.0 / vp_trans.mag ())).transformed (tt); + + } else { + + db::DHershey ht (text.string (), font); + ht.justify (db::DBox (dp1, dp2), halign, valign); + return ht.bbox ().transformed (tt); + + } +} + +} diff --git a/src/laybasic/laybasic/layTextInfo.h b/src/laybasic/laybasic/layTextInfo.h new file mode 100644 index 000000000..c99a47563 --- /dev/null +++ b/src/laybasic/laybasic/layTextInfo.h @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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_layTextInfo +#define HDR_layTextInfo + +#include "laybasicCommon.h" + +#include "dbText.h" +#include "dbBox.h" + +namespace lay +{ + +class LayoutViewBase; + +/** + * @brief A class providing information about a text's visual bounding box + * + * The class can act as a BoxConverter. + */ +class LAYBASIC_PUBLIC TextInfo +{ +public: + /** + * @brief Constructor + * + * @param view The LayoutView from which to take the text display parameters + */ + TextInfo (const LayoutViewBase *view); + + /** + * @brief Constructor + * + * @param default_text_size The default text size in micron + * @param default_font The default font + * @param apply_text_trans True if text transformations are to be applied + * @param resolution The resolution value (logical pixel size per physical unit pixel) + * @param vp_trans The effective micron-to-pixel transformation + */ + TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution); + + /** + * @brief Gets the visual bounding box of the given DText object + * + * The visual bounding box is returned in micrometer units. + * It encloses the glyphs of the text, taking into account the + * text view settings by the view. + * + * @param text The text object + * @param vp_trans The effective micron-to-pixel transformation + */ + db::DBox bbox (const db::DText &text, const db::DCplxTrans &vp_trans) const; + +private: + double m_default_text_size; + db::Font m_default_font; + bool m_apply_text_trans; + double m_resolution; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index f3b530330..bcd01c6d1 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -52,6 +52,7 @@ SOURCES += \ layEditable.cc \ layEditorServiceBase.cc \ layFinder.cc \ + layTextInfo.cc \ layFixedFont.cc \ layLayoutCanvas.cc \ layLineStylePalette.cc \ @@ -104,6 +105,7 @@ HEADERS += \ layEditorServiceBase.h \ layLayoutCanvas.h \ layFinder.h \ + layTextInfo.h \ layFixedFont.h \ layLayoutViewBase.h \ layLineStylePalette.h \ diff --git a/src/laybasic/unit_tests/layTextInfoTests.cc b/src/laybasic/unit_tests/layTextInfoTests.cc new file mode 100644 index 000000000..ef30de467 --- /dev/null +++ b/src/laybasic/unit_tests/layTextInfoTests.cc @@ -0,0 +1,126 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "layTextInfo.h" +#include "layLayoutViewBase.h" + +#include "tlUnitTest.h" + +TEST(1) +{ + lay::LayoutViewBase lv (0, false, 0); + lv.resize (200, 100); + lv.zoom_box (db::DBox (0, 0, 200, 100)); + + lv.default_text_size (21); + lv.text_font (db::Font::DefaultFont); + + db::DText text; + text.string ("ABC"); + text.trans (db::DTrans (db::DVector (10.0, 20.0))); + + db::DText text2; + text2.string ("ABC\nCDEFGH"); + text2.trans (db::DTrans (db::DVector (10.0, 20.0))); + + db::DText text3; + + // Default font + lay::TextInfo ti (&lv); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,22;36,37)"); + // global transformation changes the dimension as the default font is not scaled or rotated + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,21;23,28.5)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (db::DFTrans (1))).to_string (), "(12,-6;27,18)"); + // long text + EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,22;60,52)"); + + // valign + text3 = text2; + text3.valign (db::VAlignCenter); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,5;60,35)"); + text3.valign (db::VAlignTop); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-12;60,18)"); + + // halign + text3 = text2; + text3.halign (db::HAlignCenter); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-14,22;34,52)"); + text3.halign (db::HAlignRight); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-40,22;8,52)"); + + // Herschey font + lv.text_font (db::Font::StickFont); + ti = lay::TextInfo (&lv); + + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,15;72,47)"); + // global trans only scales pixel-based border but does not modify the outline in + // "apply transformation" mode + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,14;71,46)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (db::DFTrans (1))).to_string (), "(12,15;72,47)"); + // long text + EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,15;134,83)"); + + // valign + text3 = text2; + text3.valign (db::VAlignCenter); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-17.5;134,50.5)"); + text3.valign (db::VAlignTop); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,-50;134,18)"); + + // halign + text3 = text2; + text3.halign (db::HAlignCenter); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-51,15;71,83)"); + text3.halign (db::HAlignRight); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-114,15;8,83)"); + + // smaller size as default + lv.default_text_size (4.2); + ti = lay::TextInfo (&lv); + + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,20.6;24,27)"); + EXPECT_EQ (ti.bbox (text2, db::DCplxTrans ()).to_string (), "(12,20.6;36.4,34.2)"); + + // text with explicit size + text3 = text2; + text3.size (21); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,15;134,83)"); + + // text with rotation + text3.trans (db::DTrans (1, db::DVector (10.0, 20.0))); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(-53,22;15,144)"); + + // text with rotation and default font (-> rotation ignored) + text3.font (db::Font::DefaultFont); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,22;60,52)"); + text3.font (db::Font::StickFont); + + // apply_text_trans = false + lv.apply_text_trans (false); + ti = lay::TextInfo (&lv); + EXPECT_EQ (ti.bbox (text3, db::DCplxTrans ()).to_string (), "(12,20.6;36.4,34.2)"); + // with apply_text_trans false, the global transformation does change the text + // bounding box. + EXPECT_EQ (ti.bbox (text, db::DCplxTrans ()).to_string (), "(12,20.6;24,27)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (2.0)).to_string (), "(11,19.6;23,26)"); + EXPECT_EQ (ti.bbox (text, db::DCplxTrans (db::DFTrans (1))).to_string (), "(10.6,6;17,18)"); +} diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro index 3e585c8ca..face73fbb 100644 --- a/src/laybasic/unit_tests/unit_tests.pro +++ b/src/laybasic/unit_tests/unit_tests.pro @@ -14,6 +14,7 @@ SOURCES = \ layParsedLayerSource.cc \ layRenderer.cc \ layAbstractMenuTests.cc \ + layTextInfoTests.cc \ laySnapTests.cc INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic diff --git a/src/layui/layui/DuplicateLayerDialog.ui b/src/layui/layui/DuplicateLayerDialog.ui index d289f052b..e3b39a7c7 100644 --- a/src/layui/layui/DuplicateLayerDialog.ui +++ b/src/layui/layui/DuplicateLayerDialog.ui @@ -1,7 +1,8 @@ - + + DuplicateLayerDialog - - + + 0 0 @@ -9,86 +10,82 @@ 266 - + Copy Layer - - - 9 - - + + 6 + + 9 + - - + + Copy Layer - - + + 9 - + 6 - - - + + + Qt::Horizontal - - - - - 5 - 0 + + + + 1 0 - - - - - 5 - 0 + + + + 1 0 - - - + + + Copy to - - - + + + Source layer - - - + + + Layer - + - + Qt::Horizontal - + 40 20 @@ -96,35 +93,43 @@ - - + + + + QComboBox::AdjustToContents + + - - + + + + QComboBox::AdjustToContents + + - - - + + + Layout and cell - - - + + + Replace content with source layer - + - + Qt::Horizontal - + QSizePolicy::Fixed - + 16 20 @@ -132,28 +137,28 @@ - - + + - + Flatten into target cell - + Top cell only - + Cell by cell below the current cell - - - + + + Hierarchy @@ -163,10 +168,10 @@ - + Qt::Vertical - + 623 51 @@ -175,12 +180,12 @@ - - + + Qt::Horizontal - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -188,12 +193,12 @@ - lay::CellViewSelectionComboBox + lay::LayerSelectionComboBox QComboBox
layWidgets.h
- lay::LayerSelectionComboBox + lay::CellViewSelectionComboBox QComboBox
layWidgets.h
@@ -206,11 +211,11 @@ DuplicateLayerDialog accept() - + 248 254 - + 157 274 @@ -222,11 +227,11 @@ DuplicateLayerDialog reject() - + 316 260 - + 286 274 diff --git a/src/layui/layui/UserPropertiesForm.ui b/src/layui/layui/UserPropertiesForm.ui index 76ba34ca4..8faf01105 100644 --- a/src/layui/layui/UserPropertiesForm.ui +++ b/src/layui/layui/UserPropertiesForm.ui @@ -49,7 +49,7 @@ - 1 + 0 @@ -186,6 +186,48 @@
+ + + Meta Info + + + + + + Meta Info is additional system data shown here for information. Entries marked with a "*" are persisted in the layout file. + + + true + + + + + + + false + + + true + + + + Key + + + + + Description + + + + + Value + + + + + +
diff --git a/src/layui/layui/layCellSelectionForm.cc b/src/layui/layui/layCellSelectionForm.cc index 4fb40bb5f..75aa879eb 100644 --- a/src/layui/layui/layCellSelectionForm.cc +++ b/src/layui/layui/layCellSelectionForm.cc @@ -510,9 +510,13 @@ CellSelectionForm::show_cell () QModelIndexList sel = mp_ui->lv_cells->selectionModel ()->selectedIndexes (); for (QModelIndexList::const_iterator s = sel.begin (); s != sel.end (); ++s) { db::cell_index_type ci = model->cell (*s)->cell_index (); - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Show cells"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Show cells"))); + } mp_view->show_cell (ci, m_current_cv); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } } model->signal_data_changed (); @@ -532,9 +536,13 @@ CellSelectionForm::hide_cell () QModelIndexList sel = mp_ui->lv_cells->selectionModel ()->selectedIndexes (); for (QModelIndexList::const_iterator s = sel.begin (); s != sel.end (); ++s) { db::cell_index_type ci = model->cell (*s)->cell_index (); - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Hide cells"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Hide cells"))); + } mp_view->hide_cell (ci, m_current_cv); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } } model->signal_data_changed (); diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index da242e201..dae96cc00 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -1186,8 +1186,34 @@ UserPropertiesForm::set_properties (const db::PropertiesRepository::properties_s mp_ui->text_edit->setPlainText (tl::to_qstring (text)); } +void +UserPropertiesForm::set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout) +{ + m_begin_meta = begin_meta; + m_end_meta = end_meta; + +#if QT_VERSION >= 0x50F00 + mp_ui->mode_tab->setTabVisible (2, m_begin_meta != m_end_meta); +#endif + + mp_ui->meta_info_list->clear (); + + for (auto m = m_begin_meta; m != m_end_meta; ++m) { + QTreeWidgetItem *entry = new QTreeWidgetItem (mp_ui->meta_info_list); + entry->setText (0, tl::to_qstring ((m->second.persisted ? "*" : "") + layout.meta_info_name (m->first))); + entry->setText (1, tl::to_qstring (m->second.description)); + entry->setText (2, tl::to_qstring (m->second.value.to_parsable_string ())); + } +} + bool UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id) +{ + return show (view, cv_index, prop_id, db::Layout::meta_info_iterator (), db::Layout::meta_info_iterator ()); +} + +bool +UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta) { bool ret = false; @@ -1209,6 +1235,8 @@ BEGIN_PROTECTED const db::PropertiesRepository::properties_set &props = mp_prep->properties (prop_id); set_properties (props); + set_meta_info (begin_meta, end_meta, cv->layout ()); + if (exec ()) { if (m_editable) { diff --git a/src/layui/layui/layDialogs.h b/src/layui/layui/layDialogs.h index 133e612dc..6cdd11b27 100644 --- a/src/layui/layui/layDialogs.h +++ b/src/layui/layui/layDialogs.h @@ -25,10 +25,7 @@ #ifndef HDR_layDialogs #define HDR_layDialogs -#include "dbPoint.h" -#include "dbVector.h" -#include "dbTypes.h" -#include "dbPropertiesRepository.h" +#include "dbLayout.h" #include "layuiCommon.h" #include @@ -36,12 +33,6 @@ class QTreeWidgetItem; -namespace db -{ - class Layout; - struct LayerProperties; -} - namespace lay { class GenericSyntaxHighlighterAttributes; @@ -431,6 +422,7 @@ public: virtual ~UserPropertiesForm (); bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id); + bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta); public slots: void add (); @@ -442,11 +434,13 @@ public slots: private: db::PropertiesRepository::properties_set get_properties (int tab); void set_properties (const db::PropertiesRepository::properties_set &props); + void set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout); void accept (); bool m_editable; db::PropertiesRepository *mp_prep; Ui::UserPropertiesForm *mp_ui; + db::Layout::meta_info_iterator m_begin_meta, m_end_meta; std::unique_ptr mp_hl_attributes, mp_hl_basic_attributes; }; diff --git a/src/layui/layui/layEditLineStylesForm.cc b/src/layui/layui/layEditLineStylesForm.cc index d600508ac..67fcf7dcf 100644 --- a/src/layui/layui/layEditLineStylesForm.cc +++ b/src/layui/layui/layEditLineStylesForm.cc @@ -203,9 +203,11 @@ EditLineStylesForm::sel_changed (QListWidgetItem *, QListWidgetItem *) return; } - manager ()->transaction (tl::to_string (QObject::tr ("Current style"))); - manager ()->queue (this, new CurrentStyleOp (m_selected, mp_ui->style_items->currentRow ())); - manager ()->commit (); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Current style"))); + manager ()->queue (this, new CurrentStyleOp (m_selected, mp_ui->style_items->currentRow ())); + manager ()->commit (); + } update_current_item (); } @@ -257,7 +259,9 @@ EditLineStylesForm::select_item (int index) void EditLineStylesForm::new_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("New style"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("New style"))); + } lay::LineStyleInfo s; s.set_pattern (0x55555555, 32); @@ -266,13 +270,17 @@ EditLineStylesForm::new_button_clicked () update (); select_item (oi + std::distance (m_styles.begin (), m_styles.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditLineStylesForm::clone_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Clone style"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Clone style"))); + } lay::LineStyles::iterator c = current (); @@ -297,7 +305,9 @@ EditLineStylesForm::clone_button_clicked () update (); select_item (oi + std::distance (m_styles.begin (), m_styles.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -315,7 +325,9 @@ EditLineStylesForm::delete_button_clicked () } } - manager ()->transaction (tl::to_string (QObject::tr ("Delete style"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Delete style"))); + } if (mp_ui->style_items->currentRow () + 1 == mp_ui->style_items->count ()) { select_item (mp_ui->style_items->currentRow () - 1); @@ -327,7 +339,9 @@ EditLineStylesForm::delete_button_clicked () m_styles.renumber (); update (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } @@ -347,7 +361,9 @@ EditLineStylesForm::up_button_clicked () for (lay::LineStyles::iterator i = m_styles.begin_custom (); i != m_styles.end (); ++i) { if (i->order_index () == oi - 1) { - manager ()->transaction (tl::to_string (QObject::tr ("Move style up"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Move style up"))); + } lay::LineStyleInfo info; info = *i; @@ -361,7 +377,9 @@ EditLineStylesForm::up_button_clicked () update (); select_item (oi - 2 + std::distance (m_styles.begin (), m_styles.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } return; @@ -385,7 +403,9 @@ EditLineStylesForm::down_button_clicked () for (lay::LineStyles::iterator i = m_styles.begin_custom (); i != m_styles.end (); ++i) { if (i->order_index () == oi + 1) { - manager ()->transaction (tl::to_string (QObject::tr ("Move style down"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Move style down"))); + } lay::LineStyleInfo info; info = *i; @@ -399,7 +419,9 @@ EditLineStylesForm::down_button_clicked () update (); select_item (oi + std::distance (m_styles.begin (), m_styles.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } return; @@ -420,49 +442,73 @@ EditLineStylesForm::editor_size_changed () void EditLineStylesForm::size_changed () { - manager ()->transaction (tl::to_string (QObject::tr ("Change style size"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Change style size"))); + } mp_ui->editor->set_size (mp_ui->w_spin_box->value ()); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditLineStylesForm::invert_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Invert style"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Invert style"))); + } mp_ui->editor->invert (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditLineStylesForm::clear_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Clear style"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Clear style"))); + } mp_ui->editor->clear (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditLineStylesForm::fliph_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Flip style"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Flip style"))); + } mp_ui->editor->fliph (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditLineStylesForm::sleft_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Shift left"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Shift left"))); + } mp_ui->editor->shift (-1); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditLineStylesForm::sright_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Shift right"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Shift right"))); + } mp_ui->editor->shift (1); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void diff --git a/src/layui/layui/layEditStipplesForm.cc b/src/layui/layui/layEditStipplesForm.cc index f51826dc1..21feb17cc 100644 --- a/src/layui/layui/layEditStipplesForm.cc +++ b/src/layui/layui/layEditStipplesForm.cc @@ -231,9 +231,11 @@ EditStipplesForm::sel_changed (QListWidgetItem *, QListWidgetItem *) return; } - manager ()->transaction (tl::to_string (QObject::tr ("Current pattern"))); - manager ()->queue (this, new CurrentPatternOp (m_selected, mp_ui->stipple_items->currentRow ())); - manager ()->commit (); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Current pattern"))); + manager ()->queue (this, new CurrentPatternOp (m_selected, mp_ui->stipple_items->currentRow ())); + manager ()->commit (); + } update_current_item (); } @@ -280,7 +282,9 @@ EditStipplesForm::select_item (int index) mp_ui->stipple_items->setCurrentItem (mp_ui->stipple_items->item (index)); mp_ui->stipple_items->scrollToItem (mp_ui->stipple_items->currentItem ()); - manager ()->queue (this, new CurrentPatternOp (m_selected, index)); + if (manager ()) { + manager ()->queue (this, new CurrentPatternOp (m_selected, index)); + } update_current_item (); @@ -290,7 +294,9 @@ EditStipplesForm::select_item (int index) void EditStipplesForm::new_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("New pattern"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("New pattern"))); + } lay::DitherPatternInfo p; unsigned int oi = m_pattern.begin ()[m_pattern.add_pattern (p)].order_index () - 1; @@ -298,13 +304,17 @@ EditStipplesForm::new_button_clicked () update (); select_item (oi + std::distance (m_pattern.begin (), m_pattern.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::clone_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Clone pattern"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Clone pattern"))); + } lay::DitherPattern::iterator c = current (); @@ -329,7 +339,9 @@ EditStipplesForm::clone_button_clicked () update (); select_item (oi + std::distance (m_pattern.begin (), m_pattern.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void @@ -347,7 +359,9 @@ EditStipplesForm::delete_button_clicked () } } - manager ()->transaction (tl::to_string (QObject::tr ("Delete pattern"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Delete pattern"))); + } if (mp_ui->stipple_items->currentRow () + 1 == mp_ui->stipple_items->count ()) { select_item (mp_ui->stipple_items->currentRow () - 1); @@ -359,7 +373,9 @@ EditStipplesForm::delete_button_clicked () m_pattern.renumber (); update (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } @@ -379,7 +395,9 @@ EditStipplesForm::up_button_clicked () for (lay::DitherPattern::iterator i = m_pattern.begin_custom (); i != m_pattern.end (); ++i) { if (i->order_index () == oi - 1) { - manager ()->transaction (tl::to_string (QObject::tr ("Move pattern up"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Move pattern up"))); + } lay::DitherPatternInfo info; info = *i; @@ -393,7 +411,9 @@ EditStipplesForm::up_button_clicked () update (); select_item (oi - 2 + std::distance (m_pattern.begin (), m_pattern.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } return; @@ -417,7 +437,9 @@ EditStipplesForm::down_button_clicked () for (lay::DitherPattern::iterator i = m_pattern.begin_custom (); i != m_pattern.end (); ++i) { if (i->order_index () == oi + 1) { - manager ()->transaction (tl::to_string (QObject::tr ("Move pattern down"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Move pattern down"))); + } lay::DitherPatternInfo info; info = *i; @@ -431,7 +453,9 @@ EditStipplesForm::down_button_clicked () update (); select_item (oi + std::distance (m_pattern.begin (), m_pattern.begin_custom ())); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } return; @@ -455,81 +479,121 @@ EditStipplesForm::editor_size_changed () void EditStipplesForm::size_changed () { - manager ()->transaction (tl::to_string (QObject::tr ("Change pattern size"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Change pattern size"))); + } mp_ui->editor->set_size (mp_ui->w_spin_box->value (), mp_ui->h_spin_box->value ()); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::invert_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Invert pattern"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Invert pattern"))); + } mp_ui->editor->invert (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::clear_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Clear pattern"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Clear pattern"))); + } mp_ui->editor->clear (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::rotate_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Rotate pattern"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Rotate pattern"))); + } mp_ui->editor->rotate (90); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::fliph_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Flip horizontal"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Flip horizontal"))); + } mp_ui->editor->fliph (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::flipv_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Flip vertical"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Flip vertical"))); + } mp_ui->editor->flipv (); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::sleft_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Shift left"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Shift left"))); + } mp_ui->editor->shift (-1, 0); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::sup_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Shift up"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Shift up"))); + } mp_ui->editor->shift (0, 1); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::sright_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Shift right"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Shift right"))); + } mp_ui->editor->shift (1, 0); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void EditStipplesForm::sdown_button_clicked () { - manager ()->transaction (tl::to_string (QObject::tr ("Shift down"))); + if (manager ()) { + manager ()->transaction (tl::to_string (QObject::tr ("Shift down"))); + } mp_ui->editor->shift (0, -1); - manager ()->commit (); + if (manager ()) { + manager ()->commit (); + } } void diff --git a/src/layui/layui/layLayerControlPanel.cc b/src/layui/layui/layLayerControlPanel.cc index 31b3bacf2..8f9eb165c 100644 --- a/src/layui/layui/layLayerControlPanel.cc +++ b/src/layui/layui/layLayerControlPanel.cc @@ -1752,8 +1752,21 @@ set_hidden_flags_rec (LayerTreeModel *model, QTreeView *tree_view, const QModelI } } else { - tree_view->setRowHidden (r, parent, false); + set_hidden_flags_rec (model, tree_view, index); + + // hide a group entry if all children are hidden + + bool hide = true; + int rrows = model->rowCount (index); + for (int rr = 0; rr < rrows; ++rr) { + if (! tree_view->isRowHidden (rr, index)) { + hide = false; + } + } + + tree_view->setRowHidden (r, parent, hide); + } } diff --git a/src/layui/layui/layLayoutPropertiesForm.cc b/src/layui/layui/layLayoutPropertiesForm.cc index 1083d6b1a..f247aea22 100644 --- a/src/layui/layui/layLayoutPropertiesForm.cc +++ b/src/layui/layui/layLayoutPropertiesForm.cc @@ -103,9 +103,13 @@ LayoutPropertiesForm::commit () } if (fabs (dbu - layout.dbu ()) > 1e-6) { - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Change layout's database unit"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Change layout's database unit"))); + } layout.dbu (dbu); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } } // get the selected technology name @@ -154,11 +158,15 @@ LayoutPropertiesForm::prop_pb_clicked () db::properties_id_type prop_id = layout.prop_id (); lay::UserPropertiesForm props_form (this); - if (props_form.show (mp_view, m_index, prop_id)) { + if (props_form.show (mp_view, m_index, prop_id, layout.begin_meta (), layout.end_meta ())) { - mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Edit layout's user properties"))); + if (mp_view->manager ()) { + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Edit layout's user properties"))); + } layout.prop_id (prop_id); - mp_view->manager ()->commit (); + if (mp_view->manager ()) { + mp_view->manager ()->commit (); + } } } diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index b5d8af8cb..77c883bd2 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -28,6 +28,8 @@ #include "tlString.h" #include "tlExpression.h" #include "tlTimer.h" +#include "tlUri.h" +#include "tlFileUtils.h" #include "dbLayoutQuery.h" #include "dbCellGraphUtils.h" @@ -135,10 +137,10 @@ static std::string format_tech_name (const std::string &s) class StatisticsTemplateProcessor { public: - StatisticsTemplateProcessor (const QUrl &url, const db::Layout *layout) + StatisticsTemplateProcessor (const tl::URI &url, const db::Layout *layout) : mp_layout (layout) { - QResource res (QString::fromUtf8 (":/st/") + url.path ()); + QResource res (QString::fromUtf8 (":/st/") + QString::fromUtf8 (url.path ().c_str ())); #if QT_VERSION >= 0x60000 if (res.compressionAlgorithm () == QResource::ZlibCompression) { #else @@ -149,13 +151,9 @@ public: m_temp = QByteArray ((const char *)res.data (), (int)res.size ()); } -#if QT_VERSION >= 0x050000 - QList > queryItems = QUrlQuery (url.query ()).queryItems (); -#else - QList > queryItems = url.queryItems (); -#endif - for (QList >::const_iterator q = queryItems.begin (); q != queryItems.end (); ++q) { - m_top_eval.set_var (tl::to_string (q->first), tl::to_string (q->second)); + auto query_items = url.query (); + for (auto q = query_items.begin (); q != query_items.end (); ++q) { + m_top_eval.set_var (q->first, q->second); } } @@ -496,11 +494,27 @@ public: return count (db::Shape::Edge); } + size_t edge_pair_total () const + { + return count (db::Shape::EdgePair); + } + size_t user_total () const { return count (db::Shape::UserObject); } + size_t all_total () const + { + return box_total () + + polygon_total () + + path_total () + + text_total () + + edge_total () + + edge_pair_total () + + user_total (); + } + private: std::map m_count; }; @@ -517,278 +531,357 @@ public: // .. nothing yet .. } - std::string get (const std::string &url); + std::string get (const std::string &url) + { + auto p = m_page_cache.find (url); + if (p != m_page_cache.end ()) { + return p->second; + } else { + std::string t = get_impl (url); + m_page_cache [url] = t; + return t; + } + } + + void clear_cache () + { + m_page_cache.clear (); + } private: const lay::LayoutHandleRef m_h; + std::map m_page_cache; + + std::string index_page (const tl::URI &uri) const; + std::string per_layer_stat_page (const tl::URI &uri) const; + std::string get_impl (const std::string &url); }; +static std::string s_per_layer_stat_path_ld = "per-layer-stat-ld"; +static std::string s_per_layer_stat_path_name = "per-layer-stat-name"; + std::string -StatisticsSource::get (const std::string &url) +StatisticsSource::per_layer_stat_page (const tl::URI &uri) const { - static QString s_per_layer_stat_path_ld = QString::fromUtf8 ("per-layer-stat-ld"); - static QString s_per_layer_stat_path_name = QString::fromUtf8 ("per-layer-stat-name"); + // This is the default top level page + // TODO: handle other input as well - QUrl qurl (tl::to_qstring (url)); - QFileInfo fi (qurl.path ()); + const db::Layout &layout = m_h->layout (); - if (fi.suffix () == QString::fromUtf8 ("stxml")) { + std::ostringstream os; + os.imbue (std::locale ("C")); - StatisticsTemplateProcessor tp (qurl, &m_h->layout ()); - tp.process (); - std::string r = tp.get ().constData (); - return r; + std::vector layers; + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + layers.push_back (i); + } + } - } else if (fi.baseName () == s_per_layer_stat_path_ld || fi.baseName () == s_per_layer_stat_path_name) { + if (tl::basename (uri.path ()) == s_per_layer_stat_path_ld) { + std::sort (layers.begin (), layers.end (), CompareLDName (layout)); + } else { + std::sort (layers.begin (), layers.end (), CompareNameLD (layout)); + } - // This is the default top level page - // TODO: handle other input as well + os << "" << std::endl + << "" << std::endl + << "

" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'

" << std::endl - const db::Layout &layout = m_h->layout (); + << "

" << std::endl + << "" << std::endl - std::ostringstream os; - os.imbue (std::locale ("C")); + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + ; + + db::CellCounter cc (&layout); + + tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000); + for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) { + + ShapeStatistics st_hier; + ShapeStatistics st_flat; + + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + + ShapeStatistics st; + st.compute (layout.cell (*c).shapes (*l)); + + st_hier += st; + st *= cc.weight (*c); + st_flat += st; + + ++progress; - std::vector layers; - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - layers.push_back (i); - } } - if (fi.baseName () == s_per_layer_stat_path_ld) { - std::sort (layers.begin (), layers.end (), CompareLDName (layout)); - } else { - std::sort (layers.begin (), layers.end (), CompareNameLD (layout)); - } - - os << "" << std::endl - << "" << std::endl - << "

" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'

" << std::endl - - << "

" << std::endl - << "

" << tl::to_string (QObject::tr ("Layer")) << "" << tl::to_string (QObject::tr ("All")) << "" << tl::to_string (QObject::tr ("Boxes")) << "" << tl::to_string (QObject::tr ("Polygons")) << "" << tl::to_string (QObject::tr ("Paths")) << "" << tl::to_string (QObject::tr ("Texts")) << "" << tl::to_string (QObject::tr ("Edges")) << "" << tl::to_string (QObject::tr ("Edge Pairs")) << "" << tl::to_string (QObject::tr ("User objects")) << "
" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "
" << std::endl - - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl - << "" << std::endl + os << "" << std::endl + << "" << std::endl + << "" << std::endl + // Boxes (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Polygons (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Paths (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Texts (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Edges (total) + << "" << std::endl + // EdgePairs (total) + << "" << std::endl + // User objects (total) + << "" << std::endl + // ... + << "" << std::endl << "" << std::endl ; - db::CellCounter cc (&layout); + } - tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000); - for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) { + os << "
" << tl::to_string (QObject::tr ("Layer")) << "" << tl::to_string (QObject::tr ("Boxes")) << "" << tl::to_string (QObject::tr ("Polygons")) << "" << tl::to_string (QObject::tr ("Paths")) << "" << tl::to_string (QObject::tr ("Texts")) << "" << tl::to_string (QObject::tr ("Edges")) << "" << tl::to_string (QObject::tr ("User objects")) << "
" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "
" << tl::escaped_to_html (layout.get_properties (*l).to_string (), true) << "" << st_hier.all_total () << "

" << st_flat.all_total () << "
" << st_hier.box_total () << "

" << st_flat.box_total () << "
" << st_hier.box_single () << "

" << st_flat.box_single () << "
" << st_hier.box_array () << "

" << st_flat.box_array () << "
" << st_hier.polygon_total () << "

" << st_flat.polygon_total () << "
" << st_hier.polygon_single () << "

" << st_flat.polygon_single () << "
" << st_hier.polygon_array () << "

" << st_flat.polygon_array () << "
" << st_hier.path_total () << "

" << st_flat.path_total () << "
" << st_hier.path_single () << "

" << st_flat.path_single () << "
" << st_hier.path_array () << "

" << st_flat.path_array () << "
" << st_hier.text_total () << "

" << st_flat.text_total () << "
" << st_hier.text_single () << "

" << st_flat.text_single () << "
" << st_hier.text_array () << "

" << st_flat.text_array () << "
" << st_hier.edge_total () << "

" << st_flat.edge_total () << "
" << st_hier.edge_pair_total () << "

" << st_flat.edge_pair_total () << "
" << st_hier.user_total () << "

" << st_flat.user_total () << "
" << tl::to_string (QObject::tr ("(hier)")) << "

" << tl::to_string (QObject::tr ("(flat)")) << "
" << std::endl + << "

" << std::endl + << tl::to_string (QObject::tr ("

Note

" + "

" + "\"(hier)\" is the object count where each cell counts once. " + "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells." + "

" + "

" + "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. " + "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"." + "

" + )) + << "" << std::endl + << ""; + + return os.str (); +} + +std::string +StatisticsSource::index_page (const tl::URI & /*uri*/) const +{ + // maybe later ... + bool with_shape_statistics = false; + + // This is the default top level page + // TODO: handle other input as well + + const db::Layout &layout = m_h->layout (); + + std::ostringstream os; + os.imbue (std::locale ("C")); + + size_t num_cells = layout.cells (); + + size_t num_layers = 0; + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + ++num_layers; + } + } + + db::CellCounter cc (&layout); + + os << "" << std::endl + << "" << std::endl + << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << tl::escaped_to_html (m_h->name (), true) << "'

" << std::endl + << "

" << std::endl + << "" << std::endl + << "" + << "" + << "" << std::endl; + if (! m_h->save_options ().format ().empty ()) { + os << "" + << "" + << "" << std::endl; + } + os << "" + << "" + << "" << std::endl + << "" + << "" + << "" << std::endl + << "" + << "" + << "" << std::endl + << "" + << "" + << "" << std::endl; + for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { + std::string d = meta->second.description; + if (!d.empty ()) { + d = layout.meta_info_name (meta->first); + } + os << "" << std::endl; + } + os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << tl::escaped_to_html (m_h->filename (), true) << "
" << tl::to_string (QObject::tr ("Format")) << ": " << tl::escaped_to_html (m_h->save_options ().format (), true) << "
" << tl::to_string (QObject::tr ("Technology")) << ": " << tl::escaped_to_html (m_h->technology ()->description (), true) << tl::escaped_to_html (format_tech_name (m_h->tech_name ()), true) << "
" << tl::to_string (QObject::tr ("Database unit")) << ": " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "
" << tl::to_string (QObject::tr ("Number of cells")) << ": " << num_cells << "
" << tl::to_string (QObject::tr ("Number of layers")) << ": " << num_layers << "
" << tl::escaped_to_html (d, true) << "" << tl::escaped_to_html (meta->second.value.to_string (), true) << "
" << std::endl + << "

" << tl::to_string (QObject::tr ("Top Cells")) << "

" << std::endl + << "" << std::endl; + for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) { + os << "" << std::endl; + } + os << "
" << tl::escaped_to_html (layout.cell_name (*tc), true) << "
" << std::endl; + os << "

" << std::endl; + + std::vector layers_with_oasis_names; + + std::vector layers_sorted_by_ld; + layers_sorted_by_ld.reserve (num_layers); + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + layers_sorted_by_ld.push_back (i); + const db::LayerProperties &lp = layout.get_properties (i); + if (! lp.name.empty ()) { + layers_with_oasis_names.push_back (i); + } + } + } + + std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout)); + std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout)); + + if (! layers_sorted_by_ld.empty ()) { + + os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl + << "

Detailed layer statistics

" << std::endl + << "

" << std::endl + << "" << std::endl + << ""; + if (! layers_with_oasis_names.empty ()) { + os << ""; + } + if (with_shape_statistics) { + os << ""; + os << ""; + } + os << "" << std::endl; + + tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers_sorted_by_ld.size () * layout.cells (), 100000); + for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) { + + if (! layout.is_valid_layer (*i)) { + continue; + } ShapeStatistics st_hier; ShapeStatistics st_flat; - for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + if (with_shape_statistics) { + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { - ShapeStatistics st; - st.compute (layout.cell (*c).shapes (*l)); + ShapeStatistics st; + st.compute (layout.cell (*c).shapes (*i)); - st_hier += st; - st *= cc.weight (*c); - st_flat += st; + st_hier += st; + st *= cc.weight (*c); + st_flat += st; - ++progress; + ++progress; - } - - os << "" << std::endl - << "" << std::endl - // Boxes (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Polygons (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Paths (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Texts (total, single, array) - << "" << std::endl - << "" << std::endl - << "" << std::endl - // Edges (total) - << "" << std::endl - // User objects (total) - << "" << std::endl - // ... - << "" << std::endl - << "" << std::endl - ; - - } - - os << "
" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  " << tl::to_string (QObject::tr ("Layer name")) << "" << tl::to_string (QObject::tr ("Shape count (hier)")) << "" << tl::to_string (QObject::tr ("Shape count (flat)")) << "
" << layout.get_properties (*l).to_string () << "" << st_hier.box_total () << "

" << st_flat.box_total () << "
" << st_hier.box_single () << "

" << st_flat.box_single () << "
" << st_hier.box_array () << "

" << st_flat.box_array () << "
" << st_hier.polygon_total () << "

" << st_flat.polygon_total () << "
" << st_hier.polygon_single () << "

" << st_flat.polygon_single () << "
" << st_hier.polygon_array () << "

" << st_flat.polygon_array () << "
" << st_hier.path_total () << "

" << st_flat.path_total () << "
" << st_hier.path_single () << "

" << st_flat.path_single () << "
" << st_hier.path_array () << "

" << st_flat.path_array () << "
" << st_hier.text_total () << "

" << st_flat.text_total () << "
" << st_hier.text_single () << "

" << st_flat.text_single () << "
" << st_hier.text_array () << "

" << st_flat.text_array () << "
" << st_hier.edge_total () << "

" << st_flat.edge_total () << "
" << st_hier.user_total () << "

" << st_flat.user_total () << "
" << tl::to_string (QObject::tr ("(hier)")) << "

" << tl::to_string (QObject::tr ("(flat)")) << "
" << std::endl - << "

" << std::endl - << tl::to_string (QObject::tr ("

Note

" - "

" - "\"(hier)\" is the object count where each cell counts once. " - "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells." - "

" - "

" - "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. " - "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"." - "

" - )) - << "" << std::endl - << ""; - - return os.str (); - - } else { - - // This is the default top level page - // TODO: handle other input as well - - const db::Layout &layout = m_h->layout (); - - std::ostringstream os; - os.imbue (std::locale ("C")); - - size_t num_cells = layout.cells (); - - size_t num_layers = 0; - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - ++num_layers; - } - } - - os << "" << std::endl - << "" << std::endl - << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'

" << std::endl - << "

" << std::endl - << "" << std::endl - << "" - << "" - << "" << std::endl; - if (! m_h->save_options ().format ().empty ()) { - os << "" - << "" - << "" << std::endl; - } - os << "" - << "" - << "" << std::endl - << "" - << "" - << "" << std::endl - << "" - << "" - << "" << std::endl - << "" - << "" - << "" << std::endl; - for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { - os << "" << std::endl; - } - os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << m_h->filename () << "
" << tl::to_string (QObject::tr ("Format")) << ": " << m_h->save_options ().format () << "
" << tl::to_string (QObject::tr ("Technology")) << ": " << m_h->technology ()->description () << format_tech_name (m_h->tech_name ()) << "
" << tl::to_string (QObject::tr ("Database unit")) << ": " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "
" << tl::to_string (QObject::tr ("Number of cells")) << ": " << num_cells << "
" << tl::to_string (QObject::tr ("Number of layers")) << ": " << num_layers << "
" << meta->description << "" << meta->value << "
" << std::endl - << "

" << tl::to_string (QObject::tr ("Top Cells")) << "

" << std::endl - << "" << std::endl; - for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) { - os << "" << std::endl; - } - os << "
" << layout.cell_name (*tc) << "
" << std::endl; - os << "

" << std::endl; - - std::vector layers_with_oasis_names; - - std::vector layers_sorted_by_ld; - layers_sorted_by_ld.reserve (num_layers); - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - layers_sorted_by_ld.push_back (i); - const db::LayerProperties &lp = layout.get_properties (i); - if (! lp.name.empty ()) { - layers_with_oasis_names.push_back (i); } } - } - std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout)); - std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout)); - - if (! layers_sorted_by_ld.empty ()) { - - os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl - << "

" << std::endl - << "" << std::endl - << ""; + const db::LayerProperties &lp = layout.get_properties (*i); + os << "" + << ""; if (! layers_with_oasis_names.empty ()) { - os << ""; + os << ""; + } + if (with_shape_statistics) { + os << ""; + os << ""; } os << "" << std::endl; - for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) { - if (layout.is_valid_layer (*i)) { - const db::LayerProperties &lp = layout.get_properties (*i); + } + + os << "
" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << tl::to_string (QObject::tr ("Layer name")) << "" << tl::escaped_to_html (lp.name, true) << "" << st_hier.all_total () << "" << st_flat.all_total () << "
" << std::endl; + os << "

" << std::endl; + + } + + if (! layers_with_oasis_names.empty ()) { + + os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl + << "

Detailed layer statistics

" << std::endl + << "

" << std::endl + << "" << std::endl + << "" << std::endl; + + for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) { + if (layout.is_valid_layer (*i)) { + const db::LayerProperties &lp = layout.get_properties (*i); + if (! lp.name.empty ()) { os << "" - << ""; - if (! layers_with_oasis_names.empty ()) { - os << ""; - } - os << "" << std::endl; + << "" + << "" + << "" << std::endl; } } - - os << "
" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << lp.name << "
" << tl::escaped_to_html (lp.name, true) << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "
" << std::endl; - os << "

" << std::endl; - } - if (! layers_with_oasis_names.empty ()) { + os << "" << std::endl; + os << "

" << std::endl; - os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl - << "

" << std::endl - << "" << std::endl - << "" << std::endl; - - for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) { - if (layout.is_valid_layer (*i)) { - const db::LayerProperties &lp = layout.get_properties (*i); - if (! lp.name.empty ()) { - os << "" - << "" - << "" - << "" << std::endl; - } - } - } + } - os << "
" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << lp.name << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "
" << std::endl; - os << "

" << std::endl; + os << "" << std::endl + << "" << std::endl; + ; - } + return os.str (); +} - os << "" << std::endl - << "" << std::endl; - ; +std::string +StatisticsSource::get_impl (const std::string &url) +{ + tl::URI uri (url); + std::string page = tl::basename (uri.path ()); - return os.str (); + if (tl::extension (page) == "stxml") { + + StatisticsTemplateProcessor tp (uri, &m_h->layout ()); + tp.process (); + std::string r = tp.get ().constData (); + return r; + + } else if (page == s_per_layer_stat_path_ld || page == s_per_layer_stat_path_name) { + + return per_layer_stat_page (uri); + + } else { + + return index_page (uri); } } diff --git a/src/layui/layui/layLayoutViewFunctions.cc b/src/layui/layui/layLayoutViewFunctions.cc index b29cdc693..36bc3d6ad 100644 --- a/src/layui/layui/layLayoutViewFunctions.cc +++ b/src/layui/layui/layLayoutViewFunctions.cc @@ -440,7 +440,7 @@ LayoutViewFunctions::cm_cell_user_properties () db::properties_id_type prop_id = cell.prop_id (); lay::UserPropertiesForm props_form (parent_widget ()); - if (props_form.show (view (), cv_index, prop_id)) { + if (props_form.show (view (), cv_index, prop_id, layout.begin_meta (cell.cell_index ()), layout.end_meta (cell.cell_index ()))) { view ()->transaction (tl::to_string (tr ("Edit cell's user properties"))); cell.prop_id (prop_id); diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc index 93367db93..fafdf6b53 100644 --- a/src/layui/layui/layWidgets.cc +++ b/src/layui/layui/layWidgets.cc @@ -403,7 +403,9 @@ BEGIN_PROTECTED } } - mp_private->view->manager ()->transaction (tl::to_string (QObject::tr ("New layer"))); + if (mp_private->view->manager ()) { + mp_private->view->manager ()->transaction (tl::to_string (QObject::tr ("New layer"))); + } unsigned int l = cv->layout ().insert_layer (lp); std::vector nl; @@ -411,7 +413,9 @@ BEGIN_PROTECTED mp_private->view->add_new_layers (nl, mp_private->cv_index); mp_private->view->update_content (); - mp_private->view->manager ()->commit (); + if (mp_private->view->manager ()) { + mp_private->view->manager ()->commit (); + } // NOTE: add_new_layers has triggered update_layer_list which already added the new layer set_current_layer (lp); diff --git a/src/layview/layview/layLayoutView_noqt.cc b/src/layview/layview/layLayoutView_noqt.cc index b43775a3a..fd8e8b028 100644 --- a/src/layview/layview/layLayoutView_noqt.cc +++ b/src/layview/layview/layLayoutView_noqt.cc @@ -30,6 +30,8 @@ namespace lay LayoutView::LayoutView (db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, unsigned int options) : LayoutViewBase (this, mgr, editable, plugin_parent, options) { + // NOTE: it's important to call LayoutViewBase::init from a fully constructed LayoutView (issue #1360) + LayoutViewBase::init (mgr); config_setup (); finish (); } @@ -37,6 +39,8 @@ LayoutView::LayoutView (db::Manager *mgr, bool editable, lay::Plugin *plugin_par LayoutView::LayoutView (lay::LayoutView *source, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, unsigned int options) : LayoutViewBase (this, mgr, editable, plugin_parent, options) { + // NOTE: it's important to call LayoutViewBase::init from a fully constructed LayoutView (issue #1360) + LayoutViewBase::init (mgr); copy_from (source); config_setup (); finish (); diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index fe9010206..038890594 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include "tlInternational.h" #include "tlExpression.h" @@ -76,6 +78,7 @@ #include "layEditorOptionsPages.h" #include "layUtils.h" #include "layPropertiesDialog.h" +#include "layQtTools.h" #include "dbClipboard.h" #include "dbLayout.h" #include "dbLayoutUtils.h" @@ -94,12 +97,70 @@ namespace lay { +// ------------------------------------------------------------- +// LayoutViewNotificationWidget implementation + +LayoutViewNotificationWidget::LayoutViewNotificationWidget (LayoutViewWidget *parent, const LayoutViewNotification *notification) + : QFrame (parent), mp_parent (parent), mp_notification (notification) +{ + setBackgroundRole (QPalette::ToolTipBase); + setAutoFillBackground (true); + + QHBoxLayout *layout = new QHBoxLayout (this); + layout->setContentsMargins (4, 4, 4, 4); + + QLabel *title_label = new QLabel (this); + layout->addWidget (title_label, 1); + title_label->setText (tl::to_qstring (notification->title ())); + title_label->setForegroundRole (QPalette::ToolTipText); + title_label->setWordWrap (true); + activate_help_links (title_label); + + for (auto a = notification->actions ().begin (); a != notification->actions ().end (); ++a) { + + QPushButton *pb = new QPushButton (this); + layout->addWidget (pb); + + pb->setText (tl::to_qstring (a->second)); + m_action_buttons.insert (std::make_pair (pb, a->first)); + connect (pb, SIGNAL (clicked ()), this, SLOT (action_triggered ())); + + } + + QToolButton *close_button = new QToolButton (); + close_button->setIcon (QIcon (":clear_edit_16px.png")); + close_button->setAutoRaise (true); + layout->addWidget (close_button); + + connect (close_button, SIGNAL (clicked ()), this, SLOT (close_triggered ())); +} + +void +LayoutViewNotificationWidget::action_triggered () +{ + auto a = m_action_buttons.find (sender ()); + if (a != m_action_buttons.end ()) { + mp_parent->notification_action (*mp_notification, a->second); + } +} + +void +LayoutViewNotificationWidget::close_triggered () +{ + mp_parent->remove_notification (*mp_notification); +} + // ------------------------------------------------------------- // LayoutViewWidget implementation LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options) : QFrame (parent), mp_view (0) { + mp_layout = new QVBoxLayout (this); + mp_layout->setContentsMargins (0, 0, 0, 0); + mp_layout->setSpacing (0); + mp_layout->addStretch (1); + // NOTE: construction the LayoutView may trigger events (script code executed etc.) which must // not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only // after the LayoutView is successfully constructed. @@ -110,6 +171,11 @@ LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin LayoutViewWidget::LayoutViewWidget (lay::LayoutView *source, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options) : QFrame (parent), mp_view (0) { + mp_layout = new QVBoxLayout (this); + mp_layout->setContentsMargins (0, 0, 0, 0); + mp_layout->setSpacing (0); + mp_layout->addStretch (1); + // NOTE: construction the LayoutView may trigger events (script code executed etc.) which must // not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only // after the LayoutView is successfully constructed. @@ -124,6 +190,55 @@ LayoutViewWidget::~LayoutViewWidget () delete view; } +void +LayoutViewWidget::add_notification (const LayoutViewNotification ¬ificaton) +{ + if (m_notification_widgets.find (¬ificaton) == m_notification_widgets.end ()) { + m_notifications.push_back (notificaton); + QWidget *w = new LayoutViewNotificationWidget (this, &m_notifications.back ()); + m_notification_widgets.insert (std::make_pair (&m_notifications.back (), w)); + mp_layout->insertWidget (0, w); + } +} + +void +LayoutViewWidget::remove_notification (const LayoutViewNotification ¬ification) +{ + auto nw = m_notification_widgets.find (¬ification); + if (nw != m_notification_widgets.end ()) { + + nw->second->deleteLater (); + m_notification_widgets.erase (nw); + + for (auto n = m_notifications.begin (); n != m_notifications.end (); ++n) { + if (*n == notification) { + m_notifications.erase (n); + break; + } + } + + } +} + +void +LayoutViewWidget::notification_action (const LayoutViewNotification ¬ification, const std::string &action) +{ + if (action == "reload") { + + std::string fn = notification.parameter ().to_string (); + + for (unsigned int cvi = 0; cvi < mp_view->cellviews (); ++cvi) { + const lay::CellView &cv = mp_view->cellview (cvi); + if (cv->filename () == fn) { + mp_view->reload_layout (cvi); + } + } + + remove_notification (notification); + + } +} + void LayoutViewWidget::view_deleted (lay::LayoutView *view) { @@ -135,6 +250,14 @@ LayoutViewWidget::view_deleted (lay::LayoutView *view) mp_view = new LayoutView (view->manager (), view->is_editable (), view->plugin_parent (), this, view->options ()); } +void +LayoutViewWidget::resizeEvent (QResizeEvent *) +{ + if (mp_view && mp_view->canvas ()) { + mp_view->canvas ()->resize (width (), height ()); + } +} + QSize LayoutViewWidget::sizeHint () const { @@ -279,7 +402,7 @@ LayoutView::LayoutView (db::Manager *manager, bool editable, lay::Plugin *plugin // ensures the deferred method scheduler is present tl::DeferredMethodScheduler::instance (); - init_ui (); + init_ui (manager); } LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool editable, lay::Plugin *plugin_parent, unsigned int options) @@ -290,7 +413,7 @@ LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool edit // ensures the deferred method scheduler is present tl::DeferredMethodScheduler::instance (); - init_ui (); + init_ui (manager); copy_from (source); @@ -306,7 +429,7 @@ LayoutView::LayoutView (db::Manager *manager, bool editable, lay::Plugin *plugin // ensures the deferred method scheduler is present tl::DeferredMethodScheduler::instance (); - init_ui (); + init_ui (manager); } LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool editable, lay::Plugin *plugin_parent, LayoutViewWidget *widget, unsigned int options) @@ -317,7 +440,7 @@ LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool edit // ensures the deferred method scheduler is present tl::DeferredMethodScheduler::instance (); - init_ui (); + init_ui (manager); copy_from (source); @@ -349,7 +472,7 @@ LayoutView::event_filter (QObject *obj, QEvent *event, bool &taken) } void -LayoutView::init_ui () +LayoutView::init_ui (db::Manager *mgr) { m_activated = true; m_always_show_source = false; @@ -373,17 +496,16 @@ LayoutView::init_ui () mp_min_hier_spbx = 0; mp_max_hier_spbx = 0; + // NOTE: it's important to call LayoutViewBase::init from LayoutView because creating the plugins will need a + // fully constructed LayoutView (issue #1360) + LayoutViewBase::init (mgr); + if (mp_widget) { canvas ()->init_ui (mp_widget); mp_connector = new LayoutViewSignalConnector (mp_widget, this); - QVBoxLayout *vbl = new QVBoxLayout (mp_widget); - vbl->setContentsMargins (0, 0, 0, 0); - vbl->setSpacing (0); - vbl->addWidget (canvas ()->widget ()); - if ((options () & LV_NoHierarchyPanel) == 0 && (options () & LV_Naked) == 0) { QFrame *hierarchy_frame = new QFrame (0); diff --git a/src/layview/layview/layLayoutView_qt.h b/src/layview/layview/layLayoutView_qt.h index b32ec4f12..b956c9348 100644 --- a/src/layview/layview/layLayoutView_qt.h +++ b/src/layview/layview/layLayoutView_qt.h @@ -57,6 +57,7 @@ #include #include +#include class QSpinBox; @@ -627,7 +628,7 @@ private: bool event_filter (QObject *obj, QEvent *ev, bool &taken); QSize size_hint () const; - void init_ui (); + void init_ui (db::Manager *manager); void do_setup_editor_options_pages (); QWidget *layer_control_frame () { return mp_control_frame; } @@ -683,6 +684,91 @@ private: void activate_editor_option_pages (); }; +/** + * @brief Descriptor for a notification inside the layout view + * + * Notifications are popups added at the top of the view to indicate need for reloading for example. + * Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload). + * Actions are mapped to QPushButtons. + */ +class LAYVIEW_PUBLIC LayoutViewNotification +{ +public: + LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ()) + : m_name (name), m_title (title), m_parameter (parameter) + { + // .. nothing yet .. + } + + void add_action (const std::string &name, const std::string &title) + { + m_actions.push_back (std::make_pair (name, title)); + } + + const std::vector > &actions () const + { + return m_actions; + } + + const std::string &name () const + { + return m_name; + } + + const std::string &title () const + { + return m_title; + } + + const tl::Variant ¶meter () const + { + return m_parameter; + } + + bool operator<(const LayoutViewNotification &other) const + { + if (m_name != other.name ()) { + return m_name < other.name (); + } + return m_parameter < other.parameter (); + } + + bool operator==(const LayoutViewNotification &other) const + { + if (m_name != other.name ()) { + return false; + } + return m_parameter == other.parameter (); + } + +private: + std::string m_name; + std::string m_title; + tl::Variant m_parameter; + std::vector > m_actions; +}; + +/** + * @brief A widget representing a notification + */ +class LAYVIEW_PUBLIC LayoutViewNotificationWidget + : public QFrame +{ +Q_OBJECT + +public: + LayoutViewNotificationWidget (LayoutViewWidget *parent, const LayoutViewNotification *notification); + +private slots: + void action_triggered (); + void close_triggered (); + +private: + LayoutViewWidget *mp_parent; + const LayoutViewNotification *mp_notification; + std::map m_action_buttons; +}; + /** * @brief The layout view widget * @@ -710,6 +796,16 @@ public: */ ~LayoutViewWidget (); + /** + * @brief Adds a notification + */ + void add_notification (const LayoutViewNotification ¬ificaton); + + /** + * @brief Removes a notification + */ + void remove_notification (const LayoutViewNotification ¬ificaton); + /** * @brief Gets the LayoutView embedded into this widget */ @@ -817,10 +913,25 @@ signals: private: friend class LayoutView; + friend class LayoutViewNotificationWidget; void view_deleted (lay::LayoutView *view); + void notification_action (const LayoutViewNotification ¬ification, const std::string &action); + + void resizeEvent (QResizeEvent *event); + + struct CompareNotificationPointers + { + bool operator() (const LayoutViewNotification *a, const LayoutViewNotification *b) const + { + return *a < *b; + } + }; LayoutView *mp_view; + QVBoxLayout *mp_layout; + std::list m_notifications; + std::map m_notification_widgets; }; /** diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc index 8167b02d5..64253e5e0 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc @@ -127,11 +127,19 @@ GDS2Reader::get_record () return rec_id; } +void +GDS2Reader::record_underflow_error () +{ + error (tl::to_string (tr ("Record too short"))); +} + inline int GDS2Reader::get_int () { unsigned char *b = mp_rec_buf + m_recptr; - m_recptr += 4; + if ((m_recptr += 4) > m_reclen) { + record_underflow_error (); + } int32_t l = *((int32_t *)b); gds2h (l); @@ -142,7 +150,9 @@ inline short GDS2Reader::get_short () { unsigned char *b = mp_rec_buf + m_recptr; - m_recptr += 2; + if ((m_recptr += 2) > m_reclen) { + record_underflow_error (); + } int16_t s = *((int16_t *)b); gds2h (s); @@ -153,7 +163,9 @@ inline unsigned short GDS2Reader::get_ushort () { unsigned char *b = mp_rec_buf + m_recptr; - m_recptr += 2; + if ((m_recptr += 2) > m_reclen) { + record_underflow_error (); + } uint16_t s = *((uint16_t *)b); gds2h ((int16_t &) s); @@ -164,7 +176,9 @@ inline double GDS2Reader::get_double () { unsigned char *b = mp_rec_buf + m_recptr; - m_recptr += 8; + if ((m_recptr += 8) > m_reclen) { + record_underflow_error (); + } uint32_t l0 = ((uint32_t *)b) [0]; gds2h ((int32_t &) l0); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h index 33cce1401..17eb2b504 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h @@ -106,6 +106,8 @@ private: virtual void get_time (unsigned int *mod_time, unsigned int *access_time); virtual GDS2XY *get_xy_data (unsigned int &length); virtual void progress_checkpoint (); + + void record_underflow_error (); }; } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index afc4e95fe..ad6eed607 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -185,8 +185,8 @@ GDS2ReaderBase::do_read (db::Layout &layout) unsigned int mod_time[6] = { 0, 0, 0, 0, 0, 0 }; unsigned int access_time[6] = { 0, 0, 0, 0, 0, 0 }; get_time (mod_time, access_time); - layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5]))); - layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5]))); + layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5]))); + layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5]))); long attr = 0; db::PropertiesRepository::properties_set layout_properties; @@ -234,9 +234,9 @@ GDS2ReaderBase::do_read (db::Layout &layout) double dbuu = get_double (); double dbum = get_double (); - layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu))); - layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum))); - layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), m_libname)); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum))); + layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), m_libname)); m_dbuu = dbuu; m_dbu = dbum * 1e6; /*in micron*/ @@ -278,6 +278,10 @@ GDS2ReaderBase::do_read (db::Layout &layout) get_string (m_cellname); + if (m_cellname.empty ()) { + error (tl::to_string (tr ("Empty cell name"))); + } + // if the first cell is the dummy cell containing the context information // read this cell in a special way and store the context information separately. if (first_cell && m_cellname == "$$$CONTEXT_INFO$$$") { @@ -289,13 +293,19 @@ GDS2ReaderBase::do_read (db::Layout &layout) db::cell_index_type cell_index = make_cell (layout, m_cellname); bool ignore_cell = false; - std::map >::const_iterator ctx = m_context_info.find (m_cellname); + auto ctx = m_context_info.find (m_cellname); if (ctx != m_context_info.end ()) { + CommonReaderLayerMapping layer_mapping (this, &layout); - if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) { + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + + if (ci.has_proxy_info () && layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { // ignore everything in that cell since it is created by the import: ignore_cell = true; } + + layout.fill_meta_info_from_context (cell_index, ci); + } db::Cell *cell = 0; @@ -386,6 +396,13 @@ GDS2ReaderBase::do_read (db::Layout &layout) } + // deserialize global context information + auto ctx = m_context_info.find (std::string ()); + if (ctx != m_context_info.end ()) { + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + layout.fill_meta_info_from_context (ci); + } + // check, if the last record is a ENDLIB if (rec_id != sENDLIB) { error (tl::to_string (tr ("ENDLIB record expected"))); @@ -396,12 +413,16 @@ void GDS2ReaderBase::read_context_info_cell () { short rec_id = 0; + std::string cn; // read cell content while ((rec_id = get_record ()) != sENDSTR) { progress_checkpoint (); + bool valid_hook = false; + cn.clear (); + if (rec_id == sSREF) { do { @@ -411,7 +432,7 @@ GDS2ReaderBase::read_context_info_cell () error (tl::to_string (tr ("SNAME record expected"))); } - std::string cn = get_string (); + cn = get_string (); rec_id = get_record (); while (rec_id == sSTRANS || rec_id == sANGLE || rec_id == sMAG) { @@ -421,6 +442,24 @@ GDS2ReaderBase::read_context_info_cell () error (tl::to_string (tr ("XY record expected"))); } + valid_hook = true; + + } else if (rec_id == sBOUNDARY) { + + rec_id = get_record (); + while (rec_id == sLAYER || rec_id == sDATATYPE) { + rec_id = get_record (); + } + if (rec_id != sXY) { + error (tl::to_string (tr ("XY record expected"))); + } + + valid_hook = true; + + } + + if (valid_hook) { + std::vector &strings = m_context_info.insert (std::make_pair (cn, std::vector ())).first->second; size_t attr = 0; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 8fdf46fde..048ded3d6 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -72,6 +72,109 @@ inline int scale (double sf, int value) } } +void +GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells) +{ + write_record_size (4 + 12 * 2); + write_record (sBGNSTR); + write_time (time_data); + write_time (time_data); + + write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); + + std::vector context_prop_strings; + + if (layout.has_context_info ()) { + + // Use a dummy BOUNDARY element to attach the global context + + write_record_size (4); + write_record (sBOUNDARY); + + write_record_size (6); + write_record (sLAYER); + write_short (0); + + write_record_size (6); + write_record (sDATATYPE); + write_short (0); + + write_record_size (4 + 5 * 2 * 4); + write_record (sXY); + for (unsigned int i = 0; i < 10; ++i) { + write_int (0); + } + + context_prop_strings.clear (); + + if (layout.get_context_info (context_prop_strings)) { + + // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings + // will arrive) + for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { + + --s; + + write_record_size (6); + write_record (sPROPATTR); + write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + + write_string_record (sPROPVALUE, *s); + + } + + } + + write_record_size (4); + write_record (sENDEL); + + } + + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + + if (layout.has_context_info (*cell)) { + + write_record_size (4); + write_record (sSREF); + + write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); + + write_record_size (12); + write_record (sXY); + write_int (0); + write_int (0); + + context_prop_strings.clear (); + + if (layout.get_context_info (*cell, context_prop_strings)) { + + // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings + // will arrive) + for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { + + --s; + + write_record_size (6); + write_record (sPROPATTR); + write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + + write_string_record (sPROPVALUE, *s); + + } + + } + + write_record_size (4); + write_record (sENDEL); + + } + + } + + write_record_size (4); + write_record (sENDSTR); +} + void GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -86,9 +189,9 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S db::GDS2WriterOptions gds2_options = options.get_options (); - layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); - layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); - layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), gds2_options.libname)); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); + layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname)); std::vector > layers; options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber); @@ -123,8 +226,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S } std::string str_time = tl::sprintf ("%d/%d/%d %d:%02d:%02d", time_data[1], time_data[2], time_data[0], time_data[3], time_data[4], time_data[5]); - layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), str_time)); - layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), str_time)); + layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time)); + layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time)); bool multi_xy = gds2_options.multi_xy_records; size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8); @@ -178,73 +281,17 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // write context info - bool any_proxy = false; + bool has_context = false; if (options.write_context_info ()) { - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !any_proxy; ++cell) { - const db::Cell &cref = layout.cell (*cell); - if (cref.is_proxy () && ! cref.is_top ()) { - any_proxy = true; - } + has_context = layout.has_context_info (); + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !has_context; ++cell) { + has_context = layout.has_context_info (*cell); } } - if (any_proxy) { - - write_record_size (4 + 12 * 2); - write_record (sBGNSTR); - write_time (time_data); - write_time (time_data); - - write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); - - std::vector context_prop_strings; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref = layout.cell (*cell); - if (cref.is_proxy () && ! cref.is_top ()) { - - write_record_size (4); - write_record (sSREF); - - write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); - - write_record_size (12); - write_record (sXY); - write_int (0); - write_int (0); - - context_prop_strings.clear (); - - if (layout.get_context_info (*cell, context_prop_strings)) { - - // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings - // will arrive) - for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { - - --s; - - write_record_size (6); - write_record (sPROPATTR); - write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string - - write_string_record (sPROPVALUE, *s); - - } - - } - - write_record_size (4); - write_record (sENDEL); - - } - - } - - write_record_size (4); - write_record (sENDSTR); - + if (has_context) { + write_context_cell (layout, time_data, cells); } // body diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index 52ef2b7ee..94a4a81d5 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -168,6 +168,7 @@ private: db::WriterCellNameMap m_cell_name_map; void write_properties (const db::Layout &layout, db::properties_id_type prop_id); + void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells); }; } // namespace db diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc index 443f63027..59a259d6b 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc @@ -145,9 +145,10 @@ public: // Initialize the libname property from meta data with key "libname". db::GDS2WriterOptions *options = dynamic_cast (o); if (options) { + db::Layout::meta_info_name_id_type libname_name_id = lh.layout().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator meta = lh.layout().begin_meta (); meta != lh.layout().end_meta (); ++meta) { - if (meta->name == "libname" && !meta->value.empty ()) { - options->libname = meta->value; + if (meta->first == libname_name_id && !meta->second.value.is_nil ()) { + options->libname = meta->second.value.to_string (); } } } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index 18f3c860e..33b2e3e2b 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -229,7 +229,7 @@ TEST(1) db::LayerMap map = reader.read (layout); EXPECT_EQ (fabs (layout.dbu () / 0.001 - 1.0) < 1e-6, true); - EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB"); + EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB"); EXPECT_EQ (layout.layers (), size_t (11)); EXPECT_EQ (map.mapping_str (0), "2/0 : 2/0"); @@ -289,7 +289,7 @@ TEST(2) db::Reader reader (file); map = reader.read (layout_none, options); EXPECT_EQ (fabs (layout_none.dbu () / 0.001 - 1.0) < 1e-6, true); - EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB"); + EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB"); } EXPECT_EQ (layout_none.layers (), size_t (0)); diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index d2eb315d6..20499fc60 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1193,6 +1193,104 @@ TEST(121) run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt); } +// Meta info +TEST(130a) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); + + // complex type + tl::Variant v2; + v2.set_array (); + v2.insert ("x", "value_for_x"); + v2.insert ("y", db::DBox (1.5, 2.5, 3.5, 4.5)); + tl::Variant v1; + v1.set_list (0); + v1.push (-1.5); + v1.push (v2); + layout_org.add_meta_info (ci, "complex", db::MetaInfo ("", v1, true)); + layout_org.add_meta_info ("complex", db::MetaInfo ("", v1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130a.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.has_meta_info ("x"), false); + EXPECT_EQ (layout_read.has_meta_info ("a"), true); + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.has_meta_info ("b"), true); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + EXPECT_EQ (layout_read.has_meta_info ("complex"), true); + EXPECT_EQ (layout_read.meta_info ("complex").value.is_list (), true); + EXPECT_EQ (layout_read.meta_info ("complex").value.size (), size_t (2)); + EXPECT_EQ (layout_read.meta_info ("complex").value.begin () [1].is_array (), true); + EXPECT_EQ (layout_read.meta_info ("complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)"); + + db::cell_index_type ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.is_list (), true); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.size (), size_t (2)); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.begin () [1].is_array (), true); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)"); + + tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} + // Extreme fracturing by max. points TEST(166) { diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index 989081b88..c72efceee 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -424,25 +424,25 @@ DEFImporter::produce_routing_geometry (db::Cell &design, const Polygon *style, u } } -#if 0 - // single path - db::Path p (pt0, pt + 1, wxy, be, ee, false); - if (prop_id != 0) { - design.shapes (layer).insert (db::object_with_properties (p, prop_id)); - } else { - design.shapes (layer).insert (p); - } -#else - // multipart paths - for (std::vector::const_iterator i = pt0; i != pt; ++i) { - db::Path p (i, i + 2, wxy, i == pt0 ? be : wxy / 2, i + 1 != pt ? wxy / 2 : ee, false); + if (options ().joined_paths ()) { + // single path + db::Path p (pt0, pt + 1, wxy, be, ee, false); if (prop_id != 0) { design.shapes (layer).insert (db::object_with_properties (p, prop_id)); } else { design.shapes (layer).insert (p); } + } else { + // multipart paths + for (std::vector::const_iterator i = pt0; i != pt; ++i) { + db::Path p (i, i + 2, wxy, i == pt0 ? be : wxy / 2, i + 1 != pt ? wxy / 2 : ee, false); + if (prop_id != 0) { + design.shapes (layer).insert (db::object_with_properties (p, prop_id)); + } else { + design.shapes (layer).insert (p); + } + } } -#endif was_path_before = true; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 031b34726..dc706400e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -515,6 +515,7 @@ LEFDEFReaderOptions::LEFDEFReaderOptions () m_special_routing_suffix (""), m_special_routing_datatype (0), m_separate_groups (false), + m_joined_paths (false), m_map_file (), m_macro_resolution_mode (0), m_read_lef_with_def (true), @@ -592,6 +593,7 @@ LEFDEFReaderOptions &LEFDEFReaderOptions::operator= (const LEFDEFReaderOptions & m_special_routing_datatype = d.m_special_routing_datatype; m_special_routing_datatypes = d.m_special_routing_datatypes; m_separate_groups = d.m_separate_groups; + m_joined_paths = d.m_joined_paths; m_map_file = d.m_map_file; m_macro_resolution_mode = d.m_macro_resolution_mode; m_lef_files = d.m_lef_files; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 84fdc7525..5b9518d1e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -874,6 +874,16 @@ public: m_separate_groups = f; } + bool joined_paths () const + { + return m_joined_paths; + } + + void set_joined_paths (bool f) + { + m_joined_paths = f; + } + const std::string &map_file () const { return m_map_file; @@ -1009,6 +1019,7 @@ private: std::map m_special_routing_suffixes; std::map m_special_routing_datatypes; bool m_separate_groups; + bool m_joined_paths; std::string m_map_file; unsigned int m_macro_resolution_mode; bool m_read_lef_with_def; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 758c19725..4d551936f 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -424,6 +424,7 @@ class LEFDEFFormatDeclaration tl::make_member (&LEFDEFReaderOptions::read_lef_with_def, &LEFDEFReaderOptions::set_read_lef_with_def, "read-lef-with-def") + tl::make_member (&LEFDEFReaderOptions::macro_resolution_mode, &LEFDEFReaderOptions::set_macro_resolution_mode, "macro-resolution-mode", MacroResolutionModeConverter ()) + tl::make_member (&LEFDEFReaderOptions::separate_groups, &LEFDEFReaderOptions::set_separate_groups, "separate-groups") + + tl::make_member (&LEFDEFReaderOptions::joined_paths, &LEFDEFReaderOptions::set_joined_paths, "joined-paths") + tl::make_member (&LEFDEFReaderOptions::map_file, &LEFDEFReaderOptions::set_map_file, "map-file") ); } diff --git a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc index 6c1700eb6..1d2e685f1 100644 --- a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc +++ b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc @@ -880,6 +880,20 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "\n" "This property has been added in version 0.27.\n" ) + + gsi::method ("joined_paths", &db::LEFDEFReaderOptions::joined_paths, + "@brief Gets a value indicating whether to create joined paths for wires.\n" + "If this property is set to true, wires are represented by multi-segment paths as far as possible " + "(this will fail for 45 degree path segments for example). By defauls, wires are represented " + "by multiple straight segments.\n" + "\n" + "This property has been added in version 0.28.8.\n" + ) + + gsi::method ("joined_paths=", &db::LEFDEFReaderOptions::set_joined_paths, gsi::arg ("flag"), + "@brief Sets a value indicating whether to create joined paths for wires.\n" + "See \\joined_paths for details about this property.\n" + "\n" + "This property has been added in version 0.28.8.\n" + ) + gsi::method ("map_file", &db::LEFDEFReaderOptions::map_file, "@brief Gets the layer map file to use.\n" "If a layer map file is given, the reader will pull the layer mapping from this file. The layer mapping rules " diff --git a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui index d00f96cf6..2909c8638 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui +++ b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui @@ -6,7 +6,7 @@ 0 0 - 638 + 664 868
@@ -35,7 +35,7 @@ - 2 + 0 @@ -425,34 +425,21 @@ 0 - + Via cell name prefix - - - - - 0 - 0 - - + + - µm + Produce a parent cell per group - - - - Groups - - - - + Qt::Horizontal @@ -465,41 +452,7 @@ - - - - Produce a parent cell per group - - - - - - - Layout DBU - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - + QFrame::NoFrame @@ -523,6 +476,67 @@
+ + + + + 0 + 0 + + + + µm + + + + + + + + 0 + 0 + + + + + + + + Layout DBU + + + + + + + Groups + + + + + + + Join paths + + + + + + + Produce multi-segment wire paths + + + + + + + + 0 + 0 + + + +
@@ -598,7 +612,6 @@ - 50 false false @@ -773,7 +786,6 @@ - 50 false false false @@ -1011,8 +1023,8 @@ Otherwise it's looked up relative to the LEF or DEF file. 0 0 - 609 - 591 + 679 + 582 @@ -1595,6 +1607,7 @@ type ...
move_lef_files_down dbu separate_groups + joined_paths prefix_via_cellname produce_net_names net_prop_name diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc index 4715181c6..e721d4458 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc @@ -521,6 +521,7 @@ LEFDEFReaderOptionsEditor::commit (db::FormatSpecificReaderOptions *options, con data->set_lef_labels_suffix (tl::to_string (suffix_lef_labels->text ())); data->set_lef_labels_datatype (datatype_lef_labels->text ().toInt ()); data->set_separate_groups (separate_groups->isChecked ()); + data->set_joined_paths (joined_paths->isChecked ()); data->set_read_lef_with_def (read_lef_with_def->isChecked ()); data->set_map_file (tl::to_string (mapfile_path->text ())); data->set_macro_resolution_mode (macro_resolution_mode->currentIndex ()); @@ -595,6 +596,7 @@ LEFDEFReaderOptionsEditor::setup (const db::FormatSpecificReaderOptions *options suffix_lef_labels->setText (tl::to_qstring (data->lef_labels_suffix ())); datatype_lef_labels->setText (QString::number (data->lef_labels_datatype ())); separate_groups->setChecked (data->separate_groups ()); + joined_paths->setChecked (data->joined_paths ()); read_lef_with_def->setChecked (data->read_lef_with_def ()); mapfile_path->setText (tl::to_qstring (data->map_file ())); layer_map_mode->setCurrentIndex (data->map_file ().empty () ? 1 : 0); diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 0dcc0c646..142b1d92d 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -976,3 +976,13 @@ TEST(206_lef_spacing) run_test (_this, "issue-1282", "read:a.lef", 0, default_options (), false); } +// issue-1345 +TEST(207_joined_paths) +{ + db::LEFDEFReaderOptions lefdef_opt = default_options (); + lefdef_opt.set_joined_paths (true); + run_test (_this, "issue-1345", "lef:in.lef+def:in.def", "au.oas.gz", lefdef_opt, false); + + run_test (_this, "issue-1345", "lef:in.lef+def:in.def", "au-nojoin.oas.gz", default_options (), false); +} + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc index d76dd434f..3346c6c6f 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc @@ -316,7 +316,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: error (tl::to_string (tr ("Could not find 'magic' header line - is this a MAGIC file?"))); } - layout.add_meta_info (db::MetaInfo ("lambda", "lambda value (tech scaling)", tl::to_string (m_lambda))); + layout.add_meta_info ("lambda", db::MetaInfo ("lambda value (tech scaling)", tl::to_string (m_lambda))); bool valid_layer = false; unsigned int current_layer = 0; @@ -339,11 +339,11 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: if (&m_stream == &stream) { // initial file - store technology - layout.add_meta_info (db::MetaInfo ("magic_technology", tl::to_string (tr ("MAGIC technology string")), m_tech)); + layout.add_meta_info ("magic_technology", db::MetaInfo (tl::to_string (tr ("MAGIC technology string")), m_tech)); // propose this is the KLayout technology unless a good one is given if (! mp_klayout_tech) { - layout.add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), m_tech)); + layout.add_meta_info ("technology", db::MetaInfo (tl::to_string (tr ("Technology name")), m_tech)); } } @@ -357,7 +357,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: if (&m_stream == &stream) { // initial file - store timestamp - layout.add_meta_info (db::MetaInfo ("magic_timestamp", "MAGIC main file timestamp", tl::to_string (ts))); + layout.add_meta_info ("magic_timestamp", db::MetaInfo ("MAGIC main file timestamp", tl::to_string (ts))); } ex.expect_end (); diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc index 8b899164d..a104e5fef 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc @@ -82,11 +82,14 @@ MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa double lambda = m_options.lambda; if (lambda <= 0.0) { - const std::string &lv = layout.meta_info_value ("lambda"); - if (lv.empty ()) { + const tl::Variant &lv = layout.meta_info ("lambda").value; + if (lv.is_nil ()) { throw tl::Exception (tl::to_string (tr ("No lambda value configured for MAG writer and no 'lambda' metadata present in layout."))); + } else if (lv.is_a_string ()) { + tl::from_string (lv.to_string (), lambda); + } else if (lv.can_convert_to_double ()) { + lambda = lv.to_double (); } - tl::from_string (lv, lambda); } m_sf = layout.dbu () / lambda; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h index 05cc203ea..12e68c3de 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h @@ -101,7 +101,8 @@ public: * @brief The constructor */ OASISWriterOptions () - : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*") + : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), + write_std_properties (1), subst_char ("*"), tables_at_end (false) { // .. nothing yet .. } @@ -166,6 +167,11 @@ public: */ std::string subst_char; + /** + * @brief Hidden option, for testing mainly: write tables at end to force forward references + */ + bool tables_at_end; + /** * @brief Implementation of FormatSpecificWriterOptions */ diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 613e50a82..d87c74e6c 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -36,13 +36,11 @@ namespace db { -// --------------------------------------------------------------- - // --------------------------------------------------------------- // OASISReader OASISReader::OASISReader (tl::InputStream &s) - : m_stream (s), + : m_stream (s), m_progress (tl::to_string (tr ("Reading OASIS file")), 10000), m_dbu (0.001), m_expect_strict_mode (-1), @@ -113,7 +111,7 @@ OASISReader::init (const db::LoadLayoutOptions &options) m_expect_strict_mode = oasis_options.expect_strict_mode; } -inline long long +inline long long OASISReader::get_long_long () { unsigned long long u = get_ulong_long (); @@ -124,13 +122,13 @@ OASISReader::get_long_long () } } -inline unsigned long long +inline unsigned long long OASISReader::get_ulong_long () { unsigned long long v = 0; unsigned long long vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -138,7 +136,7 @@ OASISReader::get_ulong_long () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned long long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } @@ -149,7 +147,7 @@ OASISReader::get_ulong_long () return v; } -inline long +inline long OASISReader::get_long () { unsigned long u = get_ulong (); @@ -170,13 +168,13 @@ OASISReader::get_ulong_for_divider () return l; } -inline unsigned long +inline unsigned long OASISReader::get_ulong () { unsigned long v = 0; unsigned long vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -184,7 +182,7 @@ OASISReader::get_ulong () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } @@ -195,7 +193,7 @@ OASISReader::get_ulong () return v; } -inline int +inline int OASISReader::get_int () { unsigned int u = get_uint (); @@ -206,13 +204,13 @@ OASISReader::get_int () } } -inline unsigned int +inline unsigned int OASISReader::get_uint () { unsigned int v = 0; unsigned int vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -220,7 +218,7 @@ OASISReader::get_uint () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned int) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned integer value overflow"))); } @@ -231,7 +229,7 @@ OASISReader::get_uint () return v; } -std::string +std::string OASISReader::get_str () { std::string s; @@ -260,11 +258,11 @@ OASISReader::get_real () if (t == 0) { - return double (get_ulong ()); + return double (get_ulong ()); } else if (t == 1) { - return -double (get_ulong ()); + return -double (get_ulong ()); } else if (t == 2) { @@ -447,7 +445,7 @@ OASISReader::get_gdelta (long grid) ly > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } - + return db::Vector (db::Coord (lx), db::Coord (ly)); } else { @@ -482,13 +480,13 @@ OASISReader::get_gdelta (long grid) } } -void +void OASISReader::error (const std::string &msg) { throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str ()); } -void +void OASISReader::warn (const std::string &msg, int wl) { if (warn_level () < wl) { @@ -499,7 +497,7 @@ OASISReader::warn (const std::string &msg, int wl) error (msg); } else { // TODO: compress - tl::warn << msg + tl::warn << msg << tl::to_string (tr (" (position=")) << m_stream.pos () << tl::to_string (tr (", cell=")) << m_cellname << ")"; @@ -518,7 +516,7 @@ struct LNameJoinOp1 }; /** - * @brief A helper class to join two layer map members + * @brief A helper class to join two layer map members * This implementation basically merged the datatype maps. */ struct LNameJoinOp2 @@ -535,16 +533,16 @@ struct LNameJoinOp2 * * This method will update m_table_start which is the location used as * the start position of a strict mode table. Every record except CBLOCK - * will update this position to point after the record. Hence m_table_start - * points to the beginning of a table when PROPNAME, CELLNAME or any + * will update this position to point after the record. Hence m_table_start + * points to the beginning of a table when PROPNAME, CELLNAME or any * other table-contained record is encountered. * Since CBLOCK does not update this record, the position of the table will * be the location of CBLOCK rather than that of the name record itself. - * PAD records will also call this method, so the beginning of a table + * PAD records will also call this method, so the beginning of a table * is right after any preceding PAD records and exactly at the location * of the first name record after PADs. */ -void +void OASISReader::mark_start_table () { // we need to this this to really finish a CBLOCK - this is a flaw @@ -556,7 +554,7 @@ OASISReader::mark_start_table () m_table_start = m_stream.pos (); } -void +void OASISReader::read_offset_table () { unsigned int of = 0; @@ -599,15 +597,29 @@ OASISReader::read_offset_table () static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; -void +static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; +static const char *s_gds_property_propname = "S_GDS_PROPERTY"; + +static LayoutOrCellContextInfo +make_context_info (const std::vector &context_properties) +{ + std::vector context_strings; + context_strings.reserve (context_properties.size ()); + for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { + context_strings.push_back (s->to_string ()); + } + return LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); +} + +void OASISReader::do_read (db::Layout &layout) { unsigned char r; char *mb; // prepare - m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY"); - m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT"); + m_s_gds_property_name_id = layout.properties_repository ().prop_name_id (s_gds_property_propname); + m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id (klayout_context_propname); // read magic bytes mb = (char *) m_stream.get (sizeof (magic_bytes) - 1); @@ -676,10 +688,13 @@ OASISReader::do_read (db::Layout &layout) m_propstrings.clear (); m_propnames.clear (); + m_context_strings_per_cell.clear (); + m_instances.clear (); m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; + std::vector context_properties; mark_start_table (); @@ -827,41 +842,118 @@ OASISReader::do_read (db::Layout &layout) std::map ::iterator pf = m_propname_forward_references.find (id); if (pf != m_propname_forward_references.end ()) { - if (name == "S_GDS_PROPERTY") { + bool is_s_gds_property = false; + bool is_klayout_context_property = false; + + if (name == s_gds_property_propname) { + is_s_gds_property = true; + } else if (name == klayout_context_propname) { + is_klayout_context_property = true; + } + + // handle special case of forward references to S_GDS_PROPERTY and KLAYOUT_CONTEXT + if (is_s_gds_property || is_klayout_context_property) { db::PropertiesRepository &rep = layout.properties_repository (); - db::property_names_id_type s_gds_name_id = pf->second; - // exchange the properties in the repository: first locate all - // property sets that are affected - std::vector pids; - for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { - if (p->second.find (s_gds_name_id) != p->second.end ()) { - pids.push_back (p->first); + // exchange properties in layout_properties + db::PropertiesRepository::properties_set new_set; + + for (db::PropertiesRepository::properties_set::const_iterator s = layout_properties.begin (); s != layout_properties.end (); ++s) { + if (s->first == pf->second && is_s_gds_property) { + + // S_GDS_PROPERTY translation + if (!s->second.is_list () || s->second.get_list ().size () != 2) { + error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); + } + + new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); + + } else if (s->first == pf->second && is_klayout_context_property) { + + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); + } + + } else { + new_set.insert (*s); + } + } + + new_set.swap (layout_properties); + + // exchange the properties in the repository + + // first locate all property sets that are affected + std::map > cells_by_pid; + for (auto p = rep.begin (); p != rep.end (); ++p) { + if (p->second.find (pf->second) != p->second.end ()) { + cells_by_pid.insert (std::make_pair (p->first, std::vector ())); + } + } + + // find cells using a specific pid + for (auto i = layout.begin (); i != layout.end (); ++i) { + auto cc = cells_by_pid.find (i->prop_id ()); + if (cc != cells_by_pid.end ()) { + cc->second.push_back (i->cell_index ()); } } // create new property sets for the ones we found - for (std::vector ::const_iterator pid = pids.begin (); pid != pids.end (); ++pid) { + for (auto pid = cells_by_pid.begin (); pid != cells_by_pid.end (); ++pid) { - const db::PropertiesRepository::properties_set &old_set = rep.properties (*pid); + const db::PropertiesRepository::properties_set &old_set = rep.properties (pid->first); db::PropertiesRepository::properties_set new_set; - for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) { - if (s->first == s_gds_name_id) { + for (auto s = old_set.begin (); s != old_set.end (); ++s) { + if (s->first == pf->second && is_s_gds_property) { + // S_GDS_PROPERTY translation if (!s->second.is_list () || s->second.get_list ().size () != 2) { error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); } new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); + } else if (s->first == pf->second && is_klayout_context_property) { + + auto pid2c = cells_by_pid.find (pid->first); + + if (pid->first == layout.prop_id ()) { + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); + } + } + + // feed cell-specific context strings from klayout context property + for (auto c = pid2c->second.begin (); c != pid2c->second.end (); ++c) { + std::vector &vl = m_context_strings_per_cell [*c]; + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + vl.push_back (*l); + } + } else { + vl.push_back (s->second); + } + } + } else { new_set.insert (*s); } } - rep.change_properties (*pid, new_set); + rep.change_properties (pid->first, new_set); } @@ -940,7 +1032,7 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } - // read a layer name + // read a layer name std::string name = get_str (); db::ld_type dt1 = 0, dt2 = std::numeric_limits::max () - 1; @@ -1000,7 +1092,12 @@ OASISReader::do_read (db::Layout &layout) read_properties (layout.properties_repository ()); } - store_last_properties (layout.properties_repository (), layout_properties, true); + if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { + context_properties.insert (context_properties.end (), mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ()); + } else { + // store cell properties + store_last_properties (layout.properties_repository (), layout_properties, true); + } mark_start_table (); @@ -1138,54 +1235,19 @@ OASISReader::do_read (db::Layout &layout) // resolve all propvalue forward referenced if (! m_propvalue_forward_references.empty ()) { - for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { - - for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { - - if (ps->second.is_id ()) { - - unsigned long id = (unsigned long) ps->second.to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - ps->second = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - - } else if (ps->second.is_list ()) { - - // Replace list elements as well - // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant - - const std::vector &l = ps->second.get_list (); - bool needs_replacement = false; - for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { - needs_replacement = ll->is_id (); - } - - if (needs_replacement) { - - std::vector new_list (l); - for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { - if (ll->is_id ()) { - unsigned long id = (unsigned long) ll->to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - *ll = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - } - } - - ps->second = tl::Variant (new_list.begin (), new_list.end ()); - - } - - } - + for (auto i = context_properties.begin (); i != context_properties.end (); ++i) { + replace_forward_references_in_variant (*i); + } + for (auto c = m_context_strings_per_cell.begin (); c != m_context_strings_per_cell.end (); ++c) { + for (auto i = c->second.begin (); i != c->second.end (); ++i) { + replace_forward_references_in_variant (*i); } + } + for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { + for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { + replace_forward_references_in_variant (ps->second); + } } m_propvalue_forward_references.clear (); @@ -1213,6 +1275,24 @@ OASISReader::do_read (db::Layout &layout) } + // Restore layout meta info + if (! context_properties.empty ()) { + LayoutOrCellContextInfo info = make_context_info (context_properties); + layout.fill_meta_info_from_context (info); + } + + // Restore proxy cell (link to PCell or Library) and cell meta info + if (! m_context_strings_per_cell.empty ()) { + CommonReaderLayerMapping layer_mapping (this, &layout); + for (auto cc = m_context_strings_per_cell.begin (); cc != m_context_strings_per_cell.end (); ++cc) { + LayoutOrCellContextInfo info = make_context_info (cc->second); + if (info.has_proxy_info ()) { + layout.recover_proxy_as (cc->first, info, &layer_mapping); + } + layout.fill_meta_info_from_context (cc->first, info); + } + } + // Check the table offsets vs. real occurrence if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname)); @@ -1231,6 +1311,52 @@ OASISReader::do_read (db::Layout &layout) } } +void +OASISReader::replace_forward_references_in_variant (tl::Variant &v) +{ + if (v.is_id ()) { + + unsigned long id = (unsigned long) v.to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + v = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + + } else if (v.is_list ()) { + + // Replace list elements as well + // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant + + const std::vector &l = v.get_list (); + bool needs_replacement = false; + for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { + needs_replacement = ll->is_id (); + } + + if (needs_replacement) { + + std::vector new_list (l); + for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { + if (ll->is_id ()) { + unsigned long id = (unsigned long) ll->to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + *ll = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + } + } + + v = tl::Variant (new_list.begin (), new_list.end ()); + + } + + } +} + void OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special) { @@ -1251,7 +1377,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie // Special properties are not turned into user properties except S_GDS_PROPERTY. // This is mode is used for cells and layouts so the standard properties do not appear as user properties. // For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names. - + } else if (mm_last_value_list.get ().size () == 0) { properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant ())); } else if (mm_last_value_list.get ().size () == 1) { @@ -1261,7 +1387,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie } } -std::pair +std::pair OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore_special) { db::PropertiesRepository::properties_set properties; @@ -1276,7 +1402,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore // skip PAD. mark_start_table (); - + } else if (m == 34 /*CBLOCK*/) { unsigned int type = get_uint (); @@ -1311,7 +1437,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } - } + } if (! properties.empty ()) { return std::make_pair (true, rep.properties_id (properties)); @@ -1320,7 +1446,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } } -void +void OASISReader::read_properties (db::PropertiesRepository &rep) { unsigned char m = get_byte (); @@ -1429,12 +1555,12 @@ OASISReader::read_properties (db::PropertiesRepository &rep) } - -void + +void OASISReader::read_pointlist (modal_variable > &pointlist, bool for_polygon) { unsigned int type = get_uint (); - + unsigned long n = 0; get (n); if (n == 0) { @@ -1525,13 +1651,13 @@ OASISReader::read_repetition () { unsigned int type = get_uint (); if (type == 0) { - + // reuse modal variable } else if (type == 1) { unsigned long nx = 0, ny = 0; - get (nx); + get (nx); get (ny); db::Coord dx = get_ucoord (); @@ -1542,7 +1668,7 @@ OASISReader::read_repetition () } else if (type == 2) { unsigned long nx = 0; - get (nx); + get (nx); db::Coord dx = get_ucoord (); @@ -1558,7 +1684,7 @@ OASISReader::read_repetition () mm_repetition = new RegularRepetition (db::Vector (0, 0), db::Vector (0, dy), 1, dy == 0 ? 1 : ny + 2); } else if (type == 4 || type == 5) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1583,7 +1709,7 @@ OASISReader::read_repetition () } } else if (type == 6 || type == 7) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1611,7 +1737,7 @@ OASISReader::read_repetition () unsigned long n = 0, m = 0; - get (n); + get (n); get (m); db::Vector dn = get_gdelta (); db::Vector dm = get_gdelta (); @@ -1621,7 +1747,7 @@ OASISReader::read_repetition () } else if (type == 9) { unsigned long n = 0; - get (n); + get (n); db::Vector dn = get_gdelta (); mm_repetition = new RegularRepetition (dn, db::Vector (0, 0), dn == db::Vector () ? 1 : n + 2, 1); @@ -1658,10 +1784,10 @@ OASISReader::read_repetition () return mm_repetition.get ().size () > 1; } -void +void OASISReader::do_read_placement (unsigned char r, bool xy_absolute, - db::Layout &layout, + db::Layout &layout, tl::vector &instances, tl::vector &instances_with_props) { @@ -1688,7 +1814,7 @@ OASISReader::do_read_placement (unsigned char r, } - } + } double mag = 1.0; bool mag_set = false; @@ -1726,7 +1852,7 @@ OASISReader::do_read_placement (unsigned char r, } else { angle = ((m & 0x06) >> 1); } - + mirror = (m & 0x01) != 0; if (m & 0x20) { @@ -1764,10 +1890,10 @@ OASISReader::do_read_placement (unsigned char r, db::CellInstArray inst; if (mag_set || angle < 0) { - inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::ICplxTrans (mag, angle_deg, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } else { - inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } @@ -1859,9 +1985,9 @@ OASISReader::do_read_placement (unsigned char r, } } -void +void OASISReader::do_read_text (bool xy_absolute, - db::cell_index_type cell_index, + db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -1905,7 +2031,7 @@ OASISReader::do_read_text (bool xy_absolute, mm_text_string = get_str (); } - } + } if (m & 0x1) { mm_textlayer = get_uint (); @@ -1984,7 +2110,7 @@ OASISReader::do_read_text (bool xy_absolute, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2032,9 +2158,9 @@ OASISReader::do_read_text (bool xy_absolute, } } -void +void OASISReader::do_read_rectangle (bool xy_absolute, - db::cell_index_type cell_index, + db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2049,13 +2175,13 @@ OASISReader::do_read_rectangle (bool xy_absolute, if (m & 0x40) { mm_geometry_w = get_ucoord_as_distance (); - } + } if (m & 0x80) { mm_geometry_h = mm_geometry_w; // TODO: really? } else { if (m & 0x20) { mm_geometry_h = get_ucoord_as_distance (); - } + } } if (m & 0x10) { @@ -2114,7 +2240,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2139,7 +2265,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } else { - + std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { @@ -2156,7 +2282,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } -void +void OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2244,7 +2370,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2264,7 +2390,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } ++p; } - + } } @@ -2299,7 +2425,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } } -void +void OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2413,7 +2539,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2435,7 +2561,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } - + } } @@ -2470,7 +2596,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } -void +void OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2582,7 +2708,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2629,7 +2755,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index } } -void +void OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2690,21 +2816,21 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index }, // type 1 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 2 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 3 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } @@ -2718,147 +2844,147 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index }, // type 5 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 6 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 7 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, -1, 0, 1 }, { 1, 0, 0, 0 } }, // type 8 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 9 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 10 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 11 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 12 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 1, 0 } }, // type 13 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 14 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 15 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 16 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 17 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 18 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 19 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 1, 0 } }, // type 20 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 0, 2, 0, 0 }, { 0, 0, 0, 0 } }, // type 21 { - { 0, 0, 0, 1 }, + { 0, 0, 0, 1 }, { 0, 2, 0, 1 }, { 0, 1, 0, 0 }, { 0, 0, 0, 1 } }, // type 22 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 2, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 23 { - { 1, 0, 0, 0 }, + { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 2, 0 }, { 1, 0, 0, 0 } }, // type 24 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 25 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 } @@ -2884,7 +3010,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index if (m[3] != 0) y += m[3] * mm_geometry_h.get (); pts [i] = db::Point (x, y); - + if (x > w) w = x; if (y > h) h = y; @@ -2941,7 +3067,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2988,7 +3114,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index } } -void +void OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -3077,7 +3203,7 @@ OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, d array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -3160,10 +3286,10 @@ OASISReader::reset_modal_variables () mm_last_value_list.reset (); } -void +void OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) { - // clears current instance list + // clears current instance list m_instances.clear (); m_instances_with_props.clear (); @@ -3172,7 +3298,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) bool xy_absolute = true; bool has_context = false; - std::vector context_strings; + std::vector context_strings; db::PropertiesRepository::properties_set cell_properties; // read next record @@ -3243,10 +3369,10 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) has_context = true; context_strings.reserve (mm_last_value_list.get ().size ()); for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { - context_strings.push_back (v->to_string ()); + context_strings.push_back (*v); } } else { - // store cell properties + // store layout properties store_last_properties (layout.properties_repository (), cell_properties, true); } @@ -3330,11 +3456,11 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) layout.cell (cell_index).prop_id (layout.properties_repository ().properties_id (cell_properties)); } - // insert all instances collected (inserting them once is + // insert all instances collected (inserting them once is // more effective than doing this every time) if (! m_instances.empty ()) { layout.cell (cell_index).insert (m_instances.begin (), m_instances.end ()); - // clear immediately, because if the cell is cleared before the instances are deleted, the + // clear immediately, because if the cell is cleared before the instances are deleted, the // array pointers (living in the repository) may no longer be valid m_instances.clear (); } @@ -3344,10 +3470,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) m_instances_with_props.clear (); } - // Restore proxy cell (link to PCell or Library) + // store the context strings for later if (has_context) { - CommonReaderLayerMapping layer_mapping (this, &layout); - layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping); + m_context_strings_per_cell [cell_index].swap (context_strings); } m_cellname = ""; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index f9aae8537..3e9ee8bf9 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -172,6 +172,8 @@ private: std::map m_propstrings; std::map m_propnames; + std::map > m_context_strings_per_cell; + tl::vector m_instances; tl::vector m_instances_with_props; @@ -210,6 +212,7 @@ private: void read_properties (db::PropertiesRepository &rep); void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special); std::pair read_element_properties (db::PropertiesRepository &rep, bool ignore_special); + void replace_forward_references_in_variant (tl::Variant &v); unsigned char get_byte () { diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 8e8da55ad..6e54e9bfd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -666,9 +666,11 @@ OASISWriter::OASISWriter () mp_layout (0), mp_cell (0), m_layer (0), m_datatype (0), + m_write_context_info (false), m_in_cblock (false), m_propname_id (0), m_propstring_id (0), + m_textstring_id (0), m_proptables_written (false), m_progress (tl::to_string (tr ("Writing OASIS file")), 10000) { @@ -1150,6 +1152,313 @@ OASISWriter::reset_modal_variables () mm_last_value_list.reset (); } +void +OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write the property names collected so far in the order of the ID's. + + std::vector > rev_pn; + rev_pn.reserve (m_propnames.size ()); + for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) { + rev_pn.push_back (std::make_pair (p->second, p->first)); + } + std::sort (rev_pn.begin (), rev_pn.end ()); + + for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) { + tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); + begin_table (propnames_table_pos); + write_record_id (7); + write_nstring (p->second.c_str ()); + } + + // collect and write the future property names + + std::set prop_ids_done; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + + if (cref.prop_id () != 0) { + begin_table (propnames_table_pos); + emit_propname_def (cref.prop_id ()); + } + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (inst->prop_id ()); + begin_table (propnames_table_pos); + emit_propname_def (inst->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (shape->prop_id ()); + begin_table (propnames_table_pos); + emit_propname_def (shape->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + shape.finish_array (); + } + } + + } + + // if needed, emit property name required for the PCell or meta info context information + + if (m_write_context_info && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { + + bool has_context = false; + for (auto cell = cells.begin (); cell != cells.end () && ! has_context; ++cell) { + LayoutOrCellContextInfo ci; + has_context = layout.has_context_info (*cell) && layout.get_context_info (*cell, ci); + } + + if (has_context) { + m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id++)); + begin_table (propnames_table_pos); + write_record_id (7); + write_nstring (klayout_context_name); + } + + } + + end_table (propnames_table_pos); +} + +void +OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write the property strings collected so far in the order of the ID's. + + std::vector > rev_ps; + rev_ps.reserve (m_propstrings.size ()); + for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { + rev_ps.push_back (std::make_pair (p->second, &p->first)); + } + std::sort (rev_ps.begin (), rev_ps.end ()); + + tl_assert (rev_ps.size () == size_t (m_propstring_id)); + + for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) { + tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (p->second->c_str ()); + } + + // collect and write the future property strings + + std::set prop_ids_done; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + + if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (cref.prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (cref.prop_id ()); + } + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (inst->prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (inst->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (shape->prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (shape->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + shape.finish_array (); + } + } + + } + + if (m_write_context_info) { + + // emit property string id's required for the PCell and meta info context information + std::vector context_prop_strings; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + m_progress.set (mp_stream->pos ()); + context_prop_strings.clear (); + + if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) { + + for (auto c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { + if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (c->c_str ()); + ++m_propstring_id; + } + } + + } + + } + + } + + end_table (propstrings_table_pos); +} + +void +OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const db::Layout &layout) +{ + bool sequential = true; + for (auto cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { + sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); + } + + // CELLNAME (implicit or explicit) + for (auto cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { + + begin_table (cellnames_table_pos); + + write_record_id (sequential ? 3 : 4); + write_nstring (layout.cell_name (*cell)); + if (! sequential) { + write ((unsigned long) *cell); + } + + if (m_options.write_std_properties > 1) { + + reset_modal_variables (); + + // write S_BOUNDING_BOX entries + + std::vector values; + + // TODO: how to set the "depends on external cells" flag? + db::Box bbox = layout.cell (*cell).bbox (); + if (bbox.empty ()) { + // empty box + values.push_back (tl::Variant ((unsigned int) 0x2)); + bbox = db::Box (0, 0, 0, 0); + } else { + values.push_back (tl::Variant ((unsigned int) 0x0)); + } + + values.push_back (tl::Variant (bbox.left ())); + values.push_back (tl::Variant (bbox.bottom ())); + values.push_back (tl::Variant (bbox.width ())); + values.push_back (tl::Variant (bbox.height ())); + + write_property_def (s_bounding_box_name, values, true); + + // PROPERTY record with S_CELL_OFFSET + if (cell_positions) { + std::map::const_iterator pp = cell_positions->find (*cell); + if (pp != cell_positions->end ()) { + write_property_def (s_cell_offset_name, tl::Variant (pp->second), true); + } else { + write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true); + } + } + + } + + } + + end_table (cellnames_table_pos); +} + +void +OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write present text strings + + // collect present strings by ID + std::vector > rev_ts; + rev_ts.reserve (m_textstrings.size ()); + for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) { + rev_ts.push_back (std::make_pair (p->second, &p->first)); + } + std::sort (rev_ts.begin (), rev_ts.end ()); + + tl_assert (rev_ts.size () == size_t (m_textstring_id)); + + for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) { + tl_assert (t->first == (unsigned long)(t - rev_ts.begin ())); + begin_table (textstrings_table_pos); + write_record_id (5); + write_nstring (t->second->c_str ()); + } + + // collect future test strings + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (m_textstrings.insert (std::make_pair (shape->text_string (), m_textstring_id)).second) { + begin_table (textstrings_table_pos); + write_record_id (5); + write_astring (shape->text_string ()); + ++m_textstring_id; + m_progress.set (mp_stream->pos ()); + } + ++shape; + } + } + + } + + end_table (textstrings_table_pos); +} + +void +OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vector > &layers) +{ + for (auto l = layers.begin (); l != layers.end (); ++l) { + + if (! l->second.name.empty ()) { + + begin_table (layernames_table_pos); + + // write mappings to text layer and shape layers + write_record_id (11); + write_nstring (l->second.name.c_str ()); + write_byte (3); + write ((unsigned long) l->second.layer); + write_byte (3); + write ((unsigned long) l->second.datatype); + + write_record_id (12); + write_nstring (l->second.name.c_str ()); + write_byte (3); + write ((unsigned long) l->second.layer); + write_byte (3); + write ((unsigned long) l->second.datatype); + + m_progress.set (mp_stream->pos ()); + + } + + } + + end_table (layernames_table_pos); +} + static bool must_write_cell (const db::Cell &cref) { // Don't write proxy cells which are not employed @@ -1162,6 +1471,7 @@ static bool skip_cell_body (const db::Cell &cref) return cref.is_ghost_cell () && cref.empty (); } + void OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -1172,6 +1482,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save m_layer = m_datatype = 0; m_in_cblock = false; m_cblock_buffer.clear (); + m_write_context_info = options.write_context_info (); m_options = options.get_options (); mp_stream = &stream; @@ -1252,6 +1563,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // We will collect the standard properties here: m_propstring_id = m_propname_id = 0; + m_textstring_id = 0; m_proptables_written = false; std::vector > init_props; @@ -1303,306 +1615,47 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_props (layout.prop_id ()); } - // build property name and value string tables - - { - - // write the property names collected so far in the order of the ID's. - - std::vector > rev_pn; - rev_pn.reserve (m_propnames.size ()); - for (std::map ::const_iterator p = m_propnames.begin (); p != m_propnames.end (); ++p) { - rev_pn.push_back (std::make_pair (p->second, p->first)); - } - std::sort (rev_pn.begin (), rev_pn.end ()); - - for (std::vector >::const_iterator p = rev_pn.begin (); p != rev_pn.end (); ++p) { - tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); - begin_table (propnames_table_pos); - write_record_id (7); - write_nstring (p->second.c_str ()); - } - - // collect and write the future property names - - std::set prop_ids_done; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - - if (cref.prop_id () != 0) { - begin_table (propnames_table_pos); - emit_propname_def (cref.prop_id ()); - } - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (inst->prop_id ()); - begin_table (propnames_table_pos); - emit_propname_def (inst->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - } - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (shape->prop_id ()); - begin_table (propnames_table_pos); - emit_propname_def (shape->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - shape.finish_array (); - } - } - - } - - if (options.write_context_info ()) { - - // emit property name required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top () && layout.get_context_info (*cell, context_prop_strings)) { - - if (m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id)).second) { - begin_table (propnames_table_pos); - write_record_id (7); - write_nstring (klayout_context_name); - ++m_propname_id; - } - break; - - } - - } - - } - - end_table (propnames_table_pos); - - } - - { - - // write the property strings collected so far in the order of the ID's. - - std::vector > rev_ps; - rev_ps.reserve (m_propstrings.size ()); - for (std::map ::const_iterator p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { - rev_ps.push_back (std::make_pair (p->second, p->first)); - } - std::sort (rev_ps.begin (), rev_ps.end ()); - - for (std::vector >::const_iterator p = rev_ps.begin (); p != rev_ps.end (); ++p) { - tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); - begin_table (propstrings_table_pos); - write_record_id (9); - write_nstring (p->second.c_str ()); - } - - // collect and write the future property strings - - std::set prop_ids_done; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - - if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (cref.prop_id ()); - begin_table (propnames_table_pos); - emit_propstring_def (cref.prop_id ()); - } - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (inst->prop_id ()); - begin_table (propstrings_table_pos); - emit_propstring_def (inst->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - } - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (shape->prop_id ()); - begin_table (propstrings_table_pos); - emit_propstring_def (shape->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - shape.finish_array (); - } - } - - } - - if (options.write_context_info ()) { - - // emit property string id's required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - m_progress.set (mp_stream->pos ()); - - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top ()) { - - context_prop_strings.clear (); - if (layout.get_context_info (*cell, context_prop_strings)) { - - for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { - if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { - begin_table (propstrings_table_pos); - write_record_id (9); - write_bstring (c->c_str ()); - ++m_propstring_id; - } - } - - } - - } - - } - - } - - end_table (propstrings_table_pos); - - } - - // Now we cannot open new property ID's in strict mode - m_proptables_written = true; - - // build cell name table now in non-strict mode (in strict mode it is written at the - // end because then we have the cell positions fo S_CELL_OFFSET) - - if (! m_options.strict_mode) { - - size_t pos = 0; - - bool sequential = true; - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { - sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); - } - - // CELLNAME (implicit or explicit) - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { - - begin_table (pos); - - write_record_id (sequential ? 3 : 4); - write_nstring (layout.cell_name (*cell)); - if (! sequential) { - write ((unsigned long) *cell); - } - - if (m_options.write_std_properties > 1) { - - reset_modal_variables (); - - // write S_BOUNDING_BOX entries - - std::vector values; - - // TODO: how to set the "depends on external cells" flag? - db::Box bbox = layout.cell (*cell).bbox (); - if (bbox.empty ()) { - // empty box - values.push_back (tl::Variant ((unsigned int) 0x2)); - bbox = db::Box (0, 0, 0, 0); - } else { - values.push_back (tl::Variant ((unsigned int) 0x0)); - } - - values.push_back (tl::Variant (bbox.left ())); - values.push_back (tl::Variant (bbox.bottom ())); - values.push_back (tl::Variant (bbox.width ())); - values.push_back (tl::Variant (bbox.height ())); - - write_property_def (s_bounding_box_name, values, true); - - } - - } - - end_table (pos); - - } - - // build text string table - - { - - unsigned int id = 0; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (m_textstrings.insert (std::make_pair (shape->text_string (), id)).second) { - begin_table (textstrings_table_pos); - write_record_id (5); - write_astring (shape->text_string ()); - ++id; - m_progress.set (mp_stream->pos ()); - } - ++shape; - } - } - - } - - end_table (textstrings_table_pos); - - } - - // write layernames table - - { - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (! l->second.name.empty ()) { - - begin_table (layernames_table_pos); - - // write mappings to text layer and shape layers - write_record_id (11); - write_nstring (l->second.name.c_str ()); - write_byte (3); - write ((unsigned long) l->second.layer); - write_byte (3); - write ((unsigned long) l->second.datatype); - - write_record_id (12); - write_nstring (l->second.name.c_str ()); - write_byte (3); - write ((unsigned long) l->second.layer); - write_byte (3); - write ((unsigned long) l->second.datatype); - - m_progress.set (mp_stream->pos ()); - - } - - } - - end_table (layernames_table_pos); - - } - std::vector context_prop_strings; + // write the global layout context information + + if (options.write_context_info () && layout.has_context_info () && layout.get_context_info (context_prop_strings)) { + + std::vector values; + values.reserve (context_prop_strings.size ()); + for (auto i = context_prop_strings.begin (); i != context_prop_strings.end (); ++i) { + values.push_back (tl::Variant (*i)); + } + + write_property_def (klayout_context_name, values, false); + + context_prop_strings.clear (); + + } + + // write the tables + + if (! m_options.tables_at_end) { + + write_propname_table (propnames_table_pos, cells, layout, layers); + write_propstring_table (propstrings_table_pos, cells, layout, layers); + + // Now we cannot open new property ID's in strict mode + m_proptables_written = true; + + // build cell name table now in non-strict mode (in strict mode it is written at the + // end because then we have the cell positions fo S_CELL_OFFSET) + if (! m_options.strict_mode) { + write_cellname_table (cellnames_table_pos, cells_by_index, 0, layout); + } + + write_textstring_table (textstrings_table_pos, cells, layout, layers); + write_layername_table (layernames_table_pos, layers); + + } + + // write cells + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); @@ -1630,7 +1683,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } // context information as property named KLAYOUT_CONTEXT - if (cref.is_proxy () && options.write_context_info ()) { + if (options.write_context_info () && layout.has_context_info (*cell)) { context_prop_strings.clear (); @@ -1638,17 +1691,29 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_record_id (28); write_byte (char (0xf6)); + unsigned long pnid = 0; std::map ::const_iterator pni = m_propnames.find (klayout_context_name); - tl_assert (pni != m_propnames.end ()); - write (pni->second); + if (pni == m_propnames.end ()) { + pnid = m_propname_id++; + m_propnames.insert (std::make_pair (klayout_context_name, pnid)); + } else { + pnid = pni->second; + } + write (pnid); write ((unsigned long) context_prop_strings.size ()); for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { write_byte (14); // b-string by reference number + unsigned long psid = 0; std::map ::const_iterator psi = m_propstrings.find (*c); - tl_assert (psi != m_propstrings.end ()); - write (psi->second); + if (psi == m_propstrings.end ()) { + psid = m_propstring_id++; + m_propstrings.insert (std::make_pair (*c, psid)).second; + } else { + psid = psi->second; + } + write (psid); } mm_last_property_name = klayout_context_name; @@ -1686,66 +1751,31 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } + // write the tables if at end + + if (m_options.tables_at_end) { + + // do not consider future items as everything has been collected + std::vector no_cells; + std::vector > no_layers; + + write_propname_table (propnames_table_pos, no_cells, layout, no_layers); + write_propstring_table (propstrings_table_pos, no_cells, layout, no_layers); + + // Now we cannot open new property ID's in strict mode + m_proptables_written = true; + + write_textstring_table (textstrings_table_pos, no_cells, layout, no_layers); + + // write all layers here + write_layername_table (layernames_table_pos, layers); + + } + // write cell table at the end in strict mode (in that mode we need the cell positions // for the S_CELL_OFFSET properties) - - if (m_options.strict_mode) { - - bool sequential = true; - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { - sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); - } - - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { - - begin_table (cellnames_table_pos); - - // CELLNAME (explicit) - write_record_id (sequential ? 3 : 4); - write_nstring (layout.cell_name (*cell)); - if (! sequential) { - write ((unsigned long) *cell); - } - - reset_modal_variables (); - - if (m_options.write_std_properties > 1) { - - // write S_BOUNDING_BOX entries - - std::vector values; - - // TODO: how to set the "depends on external cells" flag? - db::Box bbox = layout.cell (*cell).bbox (); - if (bbox.empty ()) { - // empty box - values.push_back (tl::Variant ((unsigned int) 0x2)); - bbox = db::Box (0, 0, 0, 0); - } else { - values.push_back (tl::Variant ((unsigned int) 0x0)); - } - - values.push_back (tl::Variant (bbox.left ())); - values.push_back (tl::Variant (bbox.bottom ())); - values.push_back (tl::Variant (bbox.width ())); - values.push_back (tl::Variant (bbox.height ())); - - write_property_def (s_bounding_box_name, values, true); - - } - - // PROPERTY record with S_CELL_OFFSET - std::map::const_iterator pp = cell_positions.find (*cell); - if (pp != cell_positions.end ()) { - write_property_def (s_cell_offset_name, tl::Variant (pp->second), true); - } else { - write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true); - } - - } - - end_table (cellnames_table_pos); - + if (m_options.tables_at_end || m_options.strict_mode) { + write_cellname_table (cellnames_table_pos, cells_by_index, &cell_positions, layout); } // END record @@ -2341,9 +2371,15 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const m_progress.set (mp_stream->pos ()); db::Trans trans = text.trans (); + + unsigned long text_id = 0; std::map ::const_iterator ts = m_textstrings.find (text.string ()); - tl_assert (ts != m_textstrings.end ()); - unsigned long text_id = ts->second; + if (ts == m_textstrings.end ()) { + text_id = m_textstring_id++; + m_textstrings.insert (std::make_pair (text.string (), text_id)); + } else { + text_id = ts->second; + } unsigned char info = 0x20; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h index 6a853f370..0dd319515 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h @@ -203,12 +203,14 @@ private: const db::Cell *mp_cell; int m_layer; int m_datatype; + bool m_write_context_info; std::vector m_pointlist; tl::OutputMemoryStream m_cblock_buffer; tl::OutputMemoryStream m_cblock_compressed; bool m_in_cblock; unsigned long m_propname_id; unsigned long m_propstring_id; + unsigned long m_textstring_id; bool m_proptables_written; std::map m_textstrings; @@ -308,6 +310,12 @@ private: void write_pointlist (const std::vector &pointlist, bool for_polygons); void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep); + + void write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const Layout &layout); + void write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_layername_table (size_t &layernames_table_pos, const std::vector > &layers); }; } // namespace db diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 91566f61a..ac574affb 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -33,7 +33,7 @@ #include -void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress) +void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end) { { db::Manager m (false); @@ -60,6 +60,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = false; oasis_options.strict_mode = false; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); writer.write (layout, stream, options); } @@ -115,6 +116,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = true; oasis_options.strict_mode = true; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); writer.write (layout, stream, options); } @@ -164,6 +166,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = false; oasis_options.strict_mode = false; + oasis_options.tables_at_end = tables_at_end; oasis_options.write_std_properties = 2; options.set_options (oasis_options); writer.write (layout, stream, options); @@ -214,6 +217,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = true; oasis_options.strict_mode = true; + oasis_options.tables_at_end = tables_at_end; oasis_options.write_std_properties = 2; options.set_options (oasis_options); writer.write (layout, stream, options); @@ -255,6 +259,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.compression_level = compr; oasis_options.recompress = recompress; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); options.set_scale_factor (3.0); options.set_dbu (0.0005); @@ -308,11 +313,14 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com void run_test (tl::TestBase *_this, const char *file, bool scaling_test = true) { for (int recompress = 0; recompress < 2; ++recompress) { - run_test (_this, file, scaling_test, 0, recompress); - run_test (_this, file, scaling_test, 1, recompress); - run_test (_this, file, scaling_test, 2, recompress); - run_test (_this, file, scaling_test, 10, recompress); + run_test (_this, file, scaling_test, 0, recompress, false); + run_test (_this, file, scaling_test, 1, recompress, false); + run_test (_this, file, scaling_test, 2, recompress, false); + run_test (_this, file, scaling_test, 10, recompress, false); } + + // tables at end + run_test (_this, file, scaling_test, 2, false, true); } TEST(1) @@ -1860,3 +1868,111 @@ TEST(120_IrregularInstRepetitions) } } + +// Meta info +static void +run_test130 (tl::TestBase *_this, bool strict, bool tables_at_end) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); + + std::string tmp_file = _this->tmp_file ("tmp_OASISWriter1.oas"); + + { + tl::OutputStream out (tmp_file); + db::OASISWriterOptions oasis_options; + oasis_options.strict_mode = strict; + oasis_options.tables_at_end = tables_at_end; + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_options (oasis_options); + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + + db::cell_index_type ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); + + tmp_file = _this->tmp_file ("tmp_OASISWriter2.oas"); + + { + tl::OutputStream out (tmp_file); + db::OASISWriterOptions oasis_options; + oasis_options.strict_mode = strict; + oasis_options.tables_at_end = tables_at_end; + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_options (oasis_options); + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} + +// Meta info + +TEST(130a) +{ + run_test130 (_this, false, false); +} + +TEST(130b) +{ + run_test130 (_this, true, false); +} + +TEST(130c) +{ + run_test130 (_this, false, true); +} + +TEST(130d) +{ + run_test130 (_this, true, true); +} + diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc index d85ca8173..8b8982d24 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc @@ -1146,7 +1146,7 @@ public: std::string lyr_file = data.get_layer_properties_file (); if (! lyr_file.empty ()) { - layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file)); + layout.add_meta_info ("layer-properties-file", db::MetaInfo ("Layer Properties File", lyr_file)); } return m_layers; diff --git a/src/plugins/tools/bool/lay_plugin/BooleanOptionsDialog.ui b/src/plugins/tools/bool/lay_plugin/BooleanOptionsDialog.ui index f1a90a510..e01d5acf5 100644 --- a/src/plugins/tools/bool/lay_plugin/BooleanOptionsDialog.ui +++ b/src/plugins/tools/bool/lay_plugin/BooleanOptionsDialog.ui @@ -1,42 +1,43 @@ - + + BooleanOptionsDialog - - + + 0 0 - 535 + 578 349 - + Boolean Operation - - - 9 - - + + 6 + + 9 + - - + + Boolean Operation Setup - - + + 9 - + 6 - + - + Qt::Horizontal - + 40 20 @@ -44,158 +45,170 @@ - - - + + + Qt::Horizontal - - + + - + Union (OR) - + Intersection (AND) - + Difference (A NOT B) - + Difference (B NOT A) - + Symmetric Difference (XOR) - - - + + + Source B - - - + + + Source A - - - + + + Mode - - - + + + Result - - - + + + Hierarchy - - - + + + Qt::Horizontal - - - + + + Layout and cell - - - + + + Layer - - - + + + Layer - - - + + + Layout and cell - - + + - - + + - - + + - - + + + + QComboBox::AdjustToContents + + - - + + + + QComboBox::AdjustToContents + + - - + + + + QComboBox::AdjustToContents + + - - - + + + (Layer is overwritten) - - - + + + Minimum coherence (for touching corners) - - + + - + Flat - + Top cell only - + Individually for current and subcells (semi hierarchical) @@ -206,10 +219,10 @@ - + Qt::Vertical - + 506 51 @@ -218,12 +231,12 @@ - - + + Qt::Horizontal - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -231,12 +244,12 @@ - lay::CellViewSelectionComboBox + lay::LayerSelectionComboBox QComboBox
layWidgets.h
- lay::LayerSelectionComboBox + lay::CellViewSelectionComboBox QComboBox
layWidgets.h
@@ -249,11 +262,11 @@ BooleanOptionsDialog accept() - + 248 254 - + 157 274 @@ -265,11 +278,11 @@ BooleanOptionsDialog reject() - + 316 260 - + 286 274 diff --git a/src/plugins/tools/bool/lay_plugin/MergeOptionsDialog.ui b/src/plugins/tools/bool/lay_plugin/MergeOptionsDialog.ui index f0981ea28..06592fbb6 100644 --- a/src/plugins/tools/bool/lay_plugin/MergeOptionsDialog.ui +++ b/src/plugins/tools/bool/lay_plugin/MergeOptionsDialog.ui @@ -1,7 +1,8 @@ - + + MergeOptionsDialog - - + + 0 0 @@ -9,125 +10,125 @@ 331 - + Merge Operation - - + + 6 - + 9 - - + + Merge Operation Setup - - + + 9 - + 6 - - + + - + Flat - + Top cell only - + Individually for current and subcells (semi hierarchical) - - - + + + (Layer is overwritten) - - - + + + Layer - - + + - - + + - - - + + + Qt::Horizontal - - - + + + Hierarchy - - + + - - - + + + Result - - - + + + Source - - - + + + Layer - - - + + + Qt::Horizontal - - - + + + Minimum coherence (for touching corners) - + - + Qt::Horizontal - + 40 20 @@ -135,36 +136,44 @@ - - + + + + QComboBox::AdjustToContents + + - - + + + + QComboBox::AdjustToContents + + - - - + + + (0: all polygons, 1: at least two overlapping ...) - - - + + + Layout and cell - - - + + + Layout and cell - - - + + + Overlap threshold @@ -174,10 +183,10 @@ - + Qt::Vertical - + 506 51 @@ -186,11 +195,11 @@ - - + + Qt::Horizontal - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -199,12 +208,12 @@ - lay::CellViewSelectionComboBox + lay::LayerSelectionComboBox QComboBox
layWidgets.h
- lay::LayerSelectionComboBox + lay::CellViewSelectionComboBox QComboBox
layWidgets.h
@@ -217,11 +226,11 @@ MergeOptionsDialog accept() - + 248 254 - + 157 274 @@ -233,11 +242,11 @@ MergeOptionsDialog reject() - + 316 260 - + 286 274 diff --git a/src/plugins/tools/bool/lay_plugin/SizingOptionsDialog.ui b/src/plugins/tools/bool/lay_plugin/SizingOptionsDialog.ui index c6e666936..746e27e0c 100644 --- a/src/plugins/tools/bool/lay_plugin/SizingOptionsDialog.ui +++ b/src/plugins/tools/bool/lay_plugin/SizingOptionsDialog.ui @@ -1,133 +1,134 @@ - + + SizingOptionsDialog - - + + 0 0 - 567 - 331 + 680 + 341 - + Sizing Operation - - - 9 - - + + 6 + + 9 + - - + + Sizing Operation Setup - - + + 9 - + 6 - - + + - + Flat - + Top cell only - + Individually for current and subcells (semi hierarchical) - - - + + + (Layer is overwritten) - - - + + + Layer - - + + - - + + - - - + + + Qt::Horizontal - - - + + + Hierarchy - - + + - - - + + + Result - - - + + + Source - - - + + + Layer - - - + + + Qt::Horizontal - - - + + + Minimum coherence (for touching corners) - + - + Qt::Horizontal - + 40 20 @@ -135,76 +136,84 @@ - - + + + + QComboBox::AdjustToContents + + - - + + + + QComboBox::AdjustToContents + + - - - + + + Single value or pair (one per direction: sx,sy) - - - + + + Layout and cell - - - + + + Layout and cell - - - + + + Sizing value - - - + + + Cutoff mode - - + + - + Strict (diagonal) - + Strong (octagon) - + Moderate (sharp bends only) - - Weak (sharps bends >135 deg.) + + Weak (sharps bends >135 deg.) - + Long limit (x10) - + Extreme limit (x100) @@ -215,10 +224,10 @@ - + Qt::Vertical - + 506 51 @@ -227,12 +236,12 @@ - - + + Qt::Horizontal - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -240,12 +249,12 @@ - lay::CellViewSelectionComboBox + lay::LayerSelectionComboBox QComboBox
layWidgets.h
- lay::LayerSelectionComboBox + lay::CellViewSelectionComboBox QComboBox
layWidgets.h
@@ -258,11 +267,11 @@ SizingOptionsDialog accept() - + 248 254 - + 157 274 @@ -274,11 +283,11 @@ SizingOptionsDialog reject() - + 316 260 - + 286 274 diff --git a/src/plugins/tools/diff/lay_plugin/DiffToolDialog.ui b/src/plugins/tools/diff/lay_plugin/DiffToolDialog.ui index a14df4d1a..1995401fc 100644 --- a/src/plugins/tools/diff/lay_plugin/DiffToolDialog.ui +++ b/src/plugins/tools/diff/lay_plugin/DiffToolDialog.ui @@ -1,76 +1,73 @@ - + + DiffToolDialog - - + + 0 0 - 498 + 503 404 - + Diff Tool - - - 9 - - + + 6 + + 9 + - - + + Input - - + + 9 - + 6 - - - + + + Layout A - - - + + + Layout B - - - - - 7 - 5 + + + + 0 0 - + QComboBox::AdjustToContentsOnFirstShow - - - - - 7 - 5 + + + + 0 0 - + QComboBox::AdjustToContentsOnFirstShow @@ -79,77 +76,84 @@ - - + + Options - - - 9 - - + + 6 + + 9 + - - + + Don't use names to match cells (use geometrical properties) - + true - - + + Run XOR on differences - - + + Summarize missing layers - + true - - + + Detailed information - + true - - + + Expand cell arrays (compare single instance by instance) - - + + Exact compare (includes properties, text orientation and similar) + + + + Ignore duplicate instances and shapes + + + - + Qt::Vertical - + 472 16 @@ -158,12 +162,12 @@ - - + + Qt::Horizontal - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -187,11 +191,11 @@ DiffToolDialog accept() - + 248 254 - + 157 274 @@ -203,11 +207,11 @@ DiffToolDialog reject() - + 316 260 - + 286 274 diff --git a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc index 1285f72bb..5ae9dcafb 100644 --- a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc +++ b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc @@ -46,6 +46,7 @@ std::string cfg_diff_smart ("diff-smart"); std::string cfg_diff_summarize ("diff-summarize"); std::string cfg_diff_expand_cell_arrays ("diff-expand-cell-arrays"); std::string cfg_diff_exact ("diff-exact"); +std::string cfg_diff_ignore_duplicates ("diff-ignore-duplicates"); // ------------------------------------------------------------------------------ // RdbDifferenceReceiver definition @@ -650,6 +651,9 @@ DiffToolDialog::exec_dialog (lay::LayoutViewBase *view) if (config_root->config_get (cfg_diff_exact, f)) { mp_ui->exact_cbx->setChecked (f); } + if (config_root->config_get (cfg_diff_ignore_duplicates, f)) { + mp_ui->ignore_duplicates_cbx->setChecked (f); + } update (); @@ -686,6 +690,7 @@ BEGIN_PROTECTED config_root->config_set (cfg_diff_summarize, mp_ui->summarize_cbx->isChecked ()); config_root->config_set (cfg_diff_expand_cell_arrays, mp_ui->expand_cell_arrays_cbx->isChecked ()); config_root->config_set (cfg_diff_exact, mp_ui->exact_cbx->isChecked ()); + config_root->config_set (cfg_diff_ignore_duplicates, mp_ui->ignore_duplicates_cbx->isChecked ()); config_root->config_end (); QDialog::accept (); @@ -712,6 +717,7 @@ DiffToolDialog::run_diff () bool summarize = !run_xor && mp_ui->summarize_cbx->isChecked (); bool expand_cell_arrays = !run_xor && mp_ui->expand_cell_arrays_cbx->isChecked (); bool exact = !run_xor && mp_ui->exact_cbx->isChecked (); + bool ignore_duplicates = mp_ui->ignore_duplicates_cbx->isChecked (); int cv_index_a = mp_ui->layouta->current_cv_index (); int cv_index_b = mp_ui->layoutb->current_cv_index (); @@ -740,6 +746,9 @@ DiffToolDialog::run_diff () if (smart) { flags |= db::layout_diff::f_smart_cell_mapping; } + if (ignore_duplicates) { + flags |= db::layout_diff::f_ignore_duplicates; + } // TODO: make an parameter db::Coord tolerance = 0; diff --git a/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc index 90fdab1ad..f9bc8c693 100644 --- a/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc +++ b/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc @@ -147,6 +147,14 @@ DB_PUBLIC gsi::Class &decl_dbTechnologyComponent (); gsi::Class decl_NetTracerTechnologyComponent (decl_dbTechnologyComponent (), "db", "NetTracerTechnologyComponent", gsi::iterator ("each", static_cast (&db::NetTracerTechnologyComponent::begin), static_cast (&db::NetTracerTechnologyComponent::end), "@brief Gets the connectivity definitions from the net tracer technology component.\n" + ) + + gsi::method ("clear", &db::NetTracerTechnologyComponent::clear, + "@brief Removes all connectivity definitions.\n" + "This method has been introduced in version 0.28.7" + ) + + gsi::method ("add", &db::NetTracerTechnologyComponent::push_back, gsi::arg ("connection"), + "@brief Adds a connectivity definition.\n" + "This method has been introduced in version 0.28.7" ), "@brief Represents the technology information for the net tracer.\n" "This class has been redefined in version 0.28 and re-introduced in version 0.28.3. Since version 0.28, " diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index 5da36d62e..afcd90379 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -137,6 +137,16 @@ public: virtual size_t scope_index () const { + static int consider_scope = -1; + + // disable scoped debugging (e.g. DRC script lines) if $KLAYOUT_PYA_DEBUG_SCOPE is set. + if (consider_scope < 0) { + consider_scope = tl::app_flag ("pya-debug-scope") ? 0 : 1; + } + if (! consider_scope) { + return 0; + } + if (! m_scope.empty ()) { for (size_t i = 0; i < m_stack_trace.size (); ++i) { if (m_stack_trace [i].file == m_scope) { @@ -856,10 +866,12 @@ gsi::Console *PythonInterpreter::current_console () const void PythonInterpreter::begin_execution () { - m_file_id_map.clear (); m_block_exceptions = false; - if (m_current_exec_level++ == 0 && mp_current_exec_handler) { - mp_current_exec_handler->start_exec (this); + if (m_current_exec_level++ == 0) { + m_file_id_map.clear (); + if (mp_current_exec_handler) { + mp_current_exec_handler->start_exec (this); + } } } diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 3444432d0..0d1274d6c 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -690,6 +690,11 @@ PyObject *object_from_variant (tl::Variant &var, PYAObjectBase *self, const gsi: } + } else { + + // This is the case for return values that prefer to be copied (e.g. from const &) + prefer_copy = atype.prefer_copy (); + } return object_to_python (obj, self, cls, pass_obj, is_const, prefer_copy, can_destroy); diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 36e46e0f4..7bd4429f0 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -306,6 +306,15 @@ class Box: @brief Returns true if this box is not equal to the other box Returns true, if this box and the given box are not equal """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Returns a string representing this box + + This string can be turned into a box again by using \from_s + . If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ @overload def __rmul__(self, box: Box) -> Box: r""" @@ -812,6 +821,13 @@ class Cell: Usually it's not required to call this method. It has been introduced in version 0.24. """ + def add_meta_info(self, info: LayoutMetaInfo) -> None: + r""" + @brief Adds meta information to the cell + See \LayoutMetaInfo for details about cells and meta information. + + This method has been introduced in version 0.28.8. + """ def basic_name(self) -> str: r""" @brief Returns the name of the library or PCell or the real name of the cell @@ -1061,6 +1077,13 @@ class Cell: r""" @brief Clears the instance list """ + def clear_meta_info(self) -> None: + r""" + @brief Clears the meta information of the cell + See \LayoutMetaInfo for details about cells and meta information. + + This method has been introduced in version 0.28.8. + """ def clear_shapes(self) -> None: r""" @brief Clears all shapes in the cell @@ -1281,6 +1304,13 @@ class Cell: Starting with version 0.15, this iterator delivers \Instance objects rather than \CellInstArray objects. """ + def each_meta_info(self) -> Iterator[LayoutMetaInfo]: + r""" + @brief Iterates over the meta information of the cell + See \LayoutMetaInfo for details about cells and meta information. + + This method has been introduced in version 0.28.8. + """ @overload def each_overlapping_inst(self, b: Box) -> Iterator[Instance]: r""" @@ -1741,6 +1771,25 @@ class Cell: This method has been introduced in version 0.22. """ + def meta_info(self, name: str) -> LayoutMetaInfo: + r""" + @brief Gets the meta information for a given name + See \LayoutMetaInfo for details about cells and meta information. + + If no meta information with the given name exists, a default object with empty fields will be returned. + + This method has been introduced in version 0.28.8. + """ + def meta_info_value(self, name: str) -> Any: + r""" + @brief Gets the meta information value for a given name + See \LayoutMetaInfo for details about cells and meta information. + + If no meta information with the given name exists, a nil value will be returned. + A more generic version that delivers all fields of the meta information is \meta_info. + + This method has been introduced in version 0.28.8. + """ @overload def move(self, src: int, dest: int) -> None: r""" @@ -2073,6 +2122,13 @@ class Cell: This method has been introduced in version 0.22. """ + def remove_meta_info(self, name: str) -> None: + r""" + @brief Removes meta information from the cell + See \LayoutMetaInfo for details about cells and meta information. + + This method has been introduced in version 0.28.8. + """ @overload def replace(self, instance: Instance, cell_inst_array: CellInstArray) -> Instance: r""" @@ -2731,6 +2787,12 @@ class CellInstArray: r""" @brief Compares two arrays for inequality """ + def __repr__(self) -> str: + r""" + @brief Converts the array to a string + + This method was introduced in version 0.22. + """ def __str__(self) -> str: r""" @brief Converts the array to a string @@ -3864,12 +3926,12 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -4078,12 +4140,12 @@ class CompoundRegionOperationNode: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -4138,12 +4200,12 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -4168,12 +4230,12 @@ class CompoundRegionOperationNode: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -4228,12 +4290,12 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -5402,6 +5464,14 @@ class CplxTrans: r""" @brief Tests for inequality """ + def __repr__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If 'lazy' is true, some parts are omitted when not required. + If a DBU is given, the output units will be micrometers. + + The lazy and DBU arguments have been added in version 0.27.6. + """ @overload def __rmul__(self, box: Box) -> DBox: r""" @@ -6088,6 +6158,15 @@ class DBox: @brief Returns true if this box is not equal to the other box Returns true, if this box and the given box are not equal """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Returns a string representing this box + + This string can be turned into a box again by using \from_s + . If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ @overload def __rmul__(self, box: DBox) -> DBox: r""" @@ -6850,6 +6929,10 @@ class DCellInstArray: r""" @brief Compares two arrays for inequality """ + def __repr__(self) -> str: + r""" + @brief Converts the array to a string + """ def __str__(self) -> str: r""" @brief Converts the array to a string @@ -7538,6 +7621,14 @@ class DCplxTrans: r""" @brief Tests for inequality """ + def __repr__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If 'lazy' is true, some parts are omitted when not required. + If a DBU is given, the output units will be micrometers. + + The lazy and DBU arguments have been added in version 0.27.6. + """ @overload def __rmul__(self, box: DBox) -> DBox: r""" @@ -8111,6 +8202,13 @@ class DEdge: @brief Inequality test @param e The object to compare against """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Returns a string representing the edge + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __rmul__(self, scale_factor: float) -> DEdge: r""" @brief Scale edge @@ -8755,6 +8853,13 @@ class DEdgePair: This method has been introduced in version 0.25. """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Returns a string representing the edge pair + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __str__(self, dbu: Optional[float] = ...) -> str: r""" @brief Returns a string representing the edge pair @@ -9175,6 +9280,10 @@ class DPath: @brief Inequality test @param p The object to compare against """ + def __repr__(self) -> str: + r""" + @brief Convert to a string + """ def __rmul__(self, f: float) -> DPath: r""" @brief Scaling by some factor @@ -9595,6 +9704,13 @@ class DPoint: This method has been added in version 0.23. """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion. + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __rmul__(self, f: float) -> DPoint: r""" @brief Scaling by some factor @@ -9950,6 +10066,10 @@ class DPolygon: @brief Returns a value indicating whether the polygons are not equal @param p The object to compare against """ + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ def __rmul__(self, f: float) -> DPolygon: r""" @brief Scales the polygon by some factor @@ -10397,6 +10517,13 @@ class DPolygon: This method has been introduced in version 0.23. """ + def sort_holes(self) -> None: + r""" + @brief Brings the holes in a specific order + This function is normalize the hole order so the comparison of two polygons does not depend on the order the holes were inserted. Polygons generated by KLayout's alorithms have their holes sorted. + + This method has been introduced in version 0.28.8. + """ def split(self) -> List[DPolygon]: r""" @brief Splits the polygon into two or more parts @@ -10684,6 +10811,10 @@ class DSimplePolygon: @brief Returns a value indicating whether self is not equal to p @param p The object to compare against """ + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ def __rmul__(self, f: float) -> DSimplePolygon: r""" @brief Scales the polygon by some factor @@ -11177,7 +11308,8 @@ class DText: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: float r""" @@ -11331,6 +11463,13 @@ class DText: Return true, if this text object and the given text are not equal """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Converts the object to a string. + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __str__(self, dbu: Optional[float] = ...) -> str: r""" @brief Converts the object to a string. @@ -11959,6 +12098,13 @@ class DTrans: r""" @brief Tests for inequality """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ @overload def __rmul__(self, box: DBox) -> DBox: r""" @@ -12448,6 +12594,13 @@ class DVector: Returns a new vector with -x,-y. """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ @overload def __rmul__(self, f: float) -> DVector: r""" @@ -12728,6 +12881,19 @@ class DeepShapeStore: Setter: @brief Sets the number of threads to allocate for the hierarchical processor """ + wants_all_cells: bool + r""" + Getter: + @brief Gets a flag wether to copy the full hierarchy for the working layouts + This attribute has been introduced in version 0.28.10. + Setter: + @brief Sets a flag wether to copy the full hierarchy for the working layouts + + The DeepShapeStore object keeps a copy of the original hierarchy internally for the working layouts. + By default, this hierarchy is mapping only non-empty cells. While the operations proceed, more cells may need to be added. This conservative approach saves some memory, but the update operations may reduce overall performance. Setting this flag to 'true' switches to a mode where the full hierarchy is copied always. This will take more memory but may save CPU time. + + This attribute has been introduced in version 0.28.10. + """ @classmethod def instance_count(cls) -> int: r""" @@ -13087,7 +13253,7 @@ class Device(NetlistObject): @overload def net_for_terminal(self, terminal_name: str) -> Net: r""" - @brief Gets the net connected to the specified terminal. + @brief Gets the net connected to the specified terminal (non-const version). If the terminal is not connected, nil is returned for the net. This convenience method has been introduced in version 0.27.3. @@ -13095,7 +13261,7 @@ class Device(NetlistObject): @overload def net_for_terminal(self, terminal_name: str) -> Net: r""" - @brief Gets the net connected to the specified terminal (non-const version). + @brief Gets the net connected to the specified terminal. If the terminal is not connected, nil is returned for the net. This convenience method has been introduced in version 0.27.3. @@ -15380,6 +15546,19 @@ class DeviceParameterDefinition: Setter: @brief Sets the description of the parameter. """ + geo_scaling_exponent: float + r""" + Getter: + @brief Gets the geometry scaling exponent. + This value is used when applying '.options scale' in the SPICE reader for example. It is zero for 'no scaling', 1.0 for linear scaling and 2.0 for quadratic scaling. + + This attribute has been added in version 0.28.6. + Setter: + @brief Sets the geometry scaling exponent. + See \geo_scaling_exponent for details. + + This attribute has been added in version 0.28.6. + """ is_primary: bool r""" Getter: @@ -15397,8 +15576,18 @@ class DeviceParameterDefinition: Setter: @brief Sets the name of the parameter. """ + si_scaling: float + r""" + Getter: + @brief Gets the scaling factor to SI units. + For parameters in micrometers - for example W and L of MOS devices - this factor can be set to 1e-6 to reflect the unit. + Setter: + @brief Sets the scaling factor to SI units. + + This setter has been added in version 0.28.6. + """ @classmethod - def new(cls, name: str, description: Optional[str] = ..., default_value: Optional[float] = ..., is_primary: Optional[bool] = ..., si_scaling: Optional[float] = ...) -> DeviceParameterDefinition: + def new(cls, name: str, description: Optional[str] = ..., default_value: Optional[float] = ..., is_primary: Optional[bool] = ..., si_scaling: Optional[float] = ..., geo_scaling_exponent: Optional[float] = ...) -> DeviceParameterDefinition: r""" @brief Creates a new parameter definition. @param name The name of the parameter @@ -15406,6 +15595,7 @@ class DeviceParameterDefinition: @param default_value The initial value @param is_primary True, if the parameter is a primary parameter (see \is_primary=) @param si_scaling The scaling factor to SI units + @param geo_scaling_exponent Indicates how the parameter scales with geometrical scaling (0: no scaling, 1.0: linear, 2.0: quadratic) """ def __copy__(self) -> DeviceParameterDefinition: r""" @@ -15415,7 +15605,7 @@ class DeviceParameterDefinition: r""" @brief Creates a copy of self """ - def __init__(self, name: str, description: Optional[str] = ..., default_value: Optional[float] = ..., is_primary: Optional[bool] = ..., si_scaling: Optional[float] = ...) -> None: + def __init__(self, name: str, description: Optional[str] = ..., default_value: Optional[float] = ..., is_primary: Optional[bool] = ..., si_scaling: Optional[float] = ..., geo_scaling_exponent: Optional[float] = ...) -> None: r""" @brief Creates a new parameter definition. @param name The name of the parameter @@ -15423,6 +15613,7 @@ class DeviceParameterDefinition: @param default_value The initial value @param is_primary True, if the parameter is a primary parameter (see \is_primary=) @param si_scaling The scaling factor to SI units + @param geo_scaling_exponent Indicates how the parameter scales with geometrical scaling (0: no scaling, 1.0: linear, 2.0: quadratic) """ def _create(self) -> None: r""" @@ -15497,11 +15688,6 @@ class DeviceParameterDefinition: This method returns true, if self is a const reference. In that case, only const methods may be called on self. """ - def si_scaling(self) -> float: - r""" - @brief Gets the scaling factor to SI units. - For parameters in micrometers for example, this factor will be 1e-6. - """ class DeviceReconnectedTerminal: r""" @@ -15919,6 +16105,13 @@ class Edge: @brief Inequality test @param e The object to compare against """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Returns a string representing the edge + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __rmul__(self, scale_factor: float) -> Edge: r""" @brief Scale edge @@ -16567,6 +16760,13 @@ class EdgePair: This method has been introduced in version 0.25. """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Returns a string representing the edge pair + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __str__(self, dbu: Optional[float] = ...) -> str: r""" @brief Returns a string representing the edge pair @@ -17053,6 +17253,11 @@ class EdgePairs(ShapeCollection): Starting with version 0.27, the method is called 'count' for consistency with \Region. 'size' is still provided as an alias. """ + def __repr__(self) -> str: + r""" + @brief Converts the edge pair collection to a string + The length of the output is limited to 20 edge pairs to avoid giant strings on large regions. For full output use "to_s" with a maximum count parameter. + """ def __str__(self) -> str: r""" @brief Converts the edge pair collection to a string @@ -19359,6 +19564,11 @@ class Edges(ShapeCollection): The boolean OR is implemented by merging the edges of both edge sets. To simply join the edge collections without merging, the + operator is more efficient. """ + def __repr__(self) -> str: + r""" + @brief Converts the edge collection to a string + The length of the output is limited to 20 edges to avoid giant strings on large regions. For full output use "to_s" with a maximum count parameter. + """ def __str__(self) -> str: r""" @brief Converts the edge collection to a string @@ -21150,12 +21360,12 @@ class GenericNetlistCompareLogger(NetlistCompareLogger): @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -21289,12 +21499,12 @@ class HAlign: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -21319,12 +21529,12 @@ class HAlign: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -21948,6 +22158,14 @@ class ICplxTrans: r""" @brief Tests for inequality """ + def __repr__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If 'lazy' is true, some parts are omitted when not required. + If a DBU is given, the output units will be micrometers. + + The lazy and DBU arguments have been added in version 0.27.6. + """ @overload def __rmul__(self, box: Box) -> Box: r""" @@ -22490,6 +22708,11 @@ class IMatrix2d: @param v The vector to transform. @return The transformed vector """ + def __repr__(self) -> str: + r""" + @brief Convert the matrix to a string. + @return The string representing this matrix + """ @overload def __rmul__(self, box: Box) -> Box: r""" @@ -22899,6 +23122,11 @@ class IMatrix3d: @param v The vector to transform. @return The transformed vector """ + def __repr__(self) -> str: + r""" + @brief Convert the matrix to a string. + @return The string representing this matrix + """ @overload def __rmul__(self, box: Box) -> Box: r""" @@ -23383,10 +23611,9 @@ class Instance: @brief Gets the complex transformation of the instance or the first instance in the array This method is always valid compared to \trans, since simple transformations can be expressed as complex transformations as well. Setter: - @brief Sets the complex transformation of the instance or the first instance in the array (in micrometer units) - This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. + @brief Sets the complex transformation of the instance or the first instance in the array - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ da: DVector r""" @@ -23562,6 +23789,12 @@ class Instance: @brief Tests for inequality of two Instance objects Warning: this operator returns true if both objects refer to the same instance, not just identical ones. """ + def __repr__(self) -> str: + r""" + @brief Creates a string showing the contents of the reference + + This method has been introduced with version 0.16. + """ def __setitem__(self, key: Any, value: Any) -> None: r""" @brief Sets the user property with the given key or, if available, the PCell parameter with the name given by the key @@ -23807,14 +24040,14 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ @overload def layout(self) -> Layout: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ def pcell_declaration(self) -> PCellDeclaration_Native: r""" @@ -24073,6 +24306,20 @@ class LEFDEFReaderConfiguration: This method has been introduced in version 0.26.4. """ + joined_paths: bool + r""" + Getter: + @brief Gets a value indicating whether to create joined paths for wires. + If this property is set to true, wires are represented by multi-segment paths as far as possible (this will fail for 45 degree path segments for example). By defauls, wires are represented by multiple straight segments. + + This property has been added in version 0.28.8. + + Setter: + @brief Sets a value indicating whether to create joined paths for wires. + See \joined_paths for details about this property. + + This property has been added in version 0.28.8. + """ labels_datatype: int r""" Getter: @@ -25107,6 +25354,16 @@ class LayerInfo: This method was added in version 0.18. """ + def __repr__(self, as_target: Optional[bool] = ...) -> str: + r""" + @brief Convert the layer info object to a string + @return The string + + If 'as_target' is true, wildcard and relative specifications are formatted such such. + + This method was added in version 0.18. + The 'as_target' argument has been added in version 0.26.5. + """ def __str__(self, as_target: Optional[bool] = ...) -> str: r""" @brief Convert the layer info object to a string @@ -26246,6 +26503,7 @@ class Layout: Clears the layout completely. """ + @overload def clear_layer(self, layer_index: int) -> None: r""" @brief Clears a layer @@ -26257,6 +26515,24 @@ class Layout: @param layer_index The index of the layer to delete. """ @overload + def clear_layer(self, layer_index: int, flags: int) -> None: + r""" + @brief Clears a layer (given shape types only) + + Clears the layer: removes all shapes for the given shape types. + + This method was introduced in version 0.28.9. + + @param layer_index The index of the layer to delete. + @param flags The type selector for the shapes to delete (see \Shapes class, S... constants). + """ + def clear_meta_info(self) -> None: + r""" + @brief Clears the meta information of the layout + See \LayoutMetaInfo for details about layouts and meta information. + This method has been introduced in version 0.28.8. + """ + @overload def clip(self, cell: Cell, box: Box) -> Cell: r""" @brief Clips the given cell by the given rectangle and produce a new cell with the clip @@ -26352,17 +26628,32 @@ class Layout: This method has been added in version 0.23. """ + @overload def copy_layer(self, src: int, dest: int) -> None: r""" @brief Copies a layer - This method was introduced in version 0.19. - - Copy a layer from the source to the target. The target is not cleared before, so that this method - merges shapes from the source with the target layer. + Copies a layer from the source to the destination layer. The destination layer is not cleared before, so that this method + merges shapes from the source with the destination layer. @param src The layer index of the source layer. @param dest The layer index of the destination layer. + + This method was introduced in version 0.19. + """ + @overload + def copy_layer(self, src: int, dest: int, flags: int) -> None: + r""" + @brief Copies a layer (selected shape types only) + + Copies a layer from the source to the destination layer. The destination layer is not cleared before, so that this method + merges shapes from the source with the destination layer. + + @param src The layer index of the source layer. + @param dest The layer index of the destination layer. + @param flags A combination of the shape type flags from \Shapes, S... constants + + This method variant has been introduced in version 0.28.9. """ @overload def copy_tree_shapes(self, source_layout: Layout, cell_mapping: CellMapping) -> None: @@ -26837,26 +27128,51 @@ class Layout: @brief Gets the library this layout lives in or nil if the layout is not part of a library This attribute has been introduced in version 0.27.5. """ - def meta_info_value(self, name: str) -> str: + def meta_info(self, name: str) -> LayoutMetaInfo: + r""" + @brief Gets the meta information for a given name + See \LayoutMetaInfo for details about layouts and meta information. + + If no meta information with the given name exists, nil is returned. + + This method has been introduced in version 0.28.8. + """ + def meta_info_value(self, name: str) -> Any: r""" @brief Gets the meta information value for a given name See \LayoutMetaInfo for details about layouts and meta information. - If no meta information with the given name exists, an empty string will be returned. + If no meta information with the given name exists, a nil value will be returned. + A more generic version that delivers all fields of the meta information is \meta_info. - This method has been introduced in version 0.25. + This method has been introduced in version 0.25. Starting with version 0.28.8, the value is of variant type instead of string only. """ + @overload def move_layer(self, src: int, dest: int) -> None: r""" @brief Moves a layer - This method was introduced in version 0.19. - - Move a layer from the source to the target. The target is not cleared before, so that this method - merges shapes from the source with the target layer. The source layer is empty after that operation. + Moves a layer from the source to the destination layer. The target is not cleared before, so that this method + merges shapes from the source with the destination layer. The source layer is empty after that operation. @param src The layer index of the source layer. @param dest The layer index of the destination layer. + + This method was introduced in version 0.19. + """ + @overload + def move_layer(self, src: int, dest: int, flags: int) -> None: + r""" + @brief Moves a layer (selected shape types only) + + Moves a layer from the source to the destination layer. The target is not cleared before, so that this method + merges shapes from the source with the destination layer. The copied shapes are removed from the source layer. + + @param src The layer index of the source layer. + @param dest The layer index of the destination layer. + @param flags A combination of the shape type flags from \Shapes, S... constants + + This method variant has been introduced in version 0.28.9. """ @overload def move_tree_shapes(self, source_layout: Layout, cell_mapping: CellMapping) -> None: @@ -27338,6 +27654,13 @@ class LayoutDiff: @brief Compare array instances instance by instance This constant can be used for the flags parameter of \compare_layouts and \compare_cells. It can be compared with other constants to form a flag set. """ + IgnoreDuplicates: ClassVar[int] + r""" + @brief Ignore duplicate instances or shapes + With this option present, duplicate instances or shapes are ignored and duplication does not count as a difference. + + This option has been introduced in version 0.28.9. + """ NoLayerNames: ClassVar[int] r""" @brief Do not compare layer names @@ -27889,8 +28212,37 @@ class LayoutMetaInfo: Multiple layout meta information objects can be attached to one layout using \Layout#add_meta_info. Meta information is identified by a unique name and carries a string value plus an optional description string. The description string is for information only and is not evaluated by code. - See also \Layout#each_meta_info and \Layout#meta_info_value and \Layout#remove_meta_info - This class has been introduced in version 0.25. + Meta information can be attached to the layout object and to cells. It is similar to user properties. The differences are: + + @ul + @li Meta information is stored differently in GDS and OASIS files using the context information added by KLayout to annotated PCell or library cells too. Hence meta information does not pollute the standard user properties space. @/li + @li The value of meta information can be complex serializable types such as lists, hashes and elementary objects such as \Box or \DBox. Scalar types include floats and booleans. @/li + @li Meta information keys are strings and are supported also for GDS which only accepts integer number keys for user properties. @/li + @/ul + + Elementary (serializable) objects are: \Box, \DBox, \Edge, \DEdge, \EdgePair, \DEdgePair, \EdgePairs, \Edges, \LayerProperties, \Matrix2d, \Matrix3d, \Path, \DPath, \Point, \DPoint, \Polygon, \DPolygon, \SimplePolygon, \DSimplePolygon, \Region, \Text, \DText, \Texts, \Trans, \DTrans, \CplxTrans, \ICplxTrans, \DCplxTrans, \VCplxTrans, \Vector, \DVector (list may not be complete). + + KLayout itself also generates meta information with specific keys. For disambiguation, namespaces can be established by prefixing the key strings with some unique identifier in XML fashion, like a domain name - e.g. 'example.com:key'. + + @b Note: @/b only meta information marked with \is_persisted? == true is stored in GDS or OASIS files. This is not the default setting, so you need to explicitly set that flag. + + See also \Layout#each_meta_info, \Layout#meta_info_value, \Layout#meta_info and \Layout#remove_meta_info as well as the corresponding \Cell methods. + + An example of how to attach persisted meta information to a cell is here: + + @code + ly = RBA::Layout::new + c1 = ly.create_cell("C1") + + mi = RBA::LayoutMetaInfo::new("the-answer", 42.0) + mi.persisted = true + c1.add_meta_info(mi) + + # will now hold this piece of meta information attached to cell 'C1': + ly.write("to.gds") + @/code + + This class has been introduced in version 0.25 and was extended in version 0.28.8. """ description: str r""" @@ -27908,7 +28260,17 @@ class LayoutMetaInfo: Setter: @brief Sets the name of the layout meta info object """ - value: str + persisted: bool + r""" + Getter: + @brief Gets a value indicating whether the meta information will be persisted + This predicate was introduced in version 0.28.8. + + Setter: + @brief Sets a value indicating whether the meta information will be persisted + This predicate was introduced in version 0.28.8. + """ + value: Any r""" Getter: @brief Gets the value of the layout meta info object @@ -27917,12 +28279,15 @@ class LayoutMetaInfo: @brief Sets the value of the layout meta info object """ @classmethod - def new(cls, name: str, value: str, description: Optional[str] = ...) -> LayoutMetaInfo: + def new(cls, name: str, value: Any, description: Optional[str] = ..., persisted: Optional[bool] = ...) -> LayoutMetaInfo: r""" @brief Creates a layout meta info object @param name The name @param value The value @param description An optional description text + @param persisted If true, the meta information will be persisted in some file formats, like GDS2 + + The 'persisted' attribute has been introduced in version 0.28.8. """ def __copy__(self) -> LayoutMetaInfo: r""" @@ -27932,12 +28297,15 @@ class LayoutMetaInfo: r""" @brief Creates a copy of self """ - def __init__(self, name: str, value: str, description: Optional[str] = ...) -> None: + def __init__(self, name: str, value: Any, description: Optional[str] = ..., persisted: Optional[bool] = ...) -> None: r""" @brief Creates a layout meta info object @param name The name @param value The value @param description An optional description text + @param persisted If true, the meta information will be persisted in some file formats, like GDS2 + + The 'persisted' attribute has been introduced in version 0.28.8. """ def _create(self) -> None: r""" @@ -28007,6 +28375,11 @@ class LayoutMetaInfo: This method returns true, if self is a const reference. In that case, only const methods may be called on self. """ + def is_persisted(self) -> bool: + r""" + @brief Gets a value indicating whether the meta information will be persisted + This predicate was introduced in version 0.28.8. + """ class LayoutQuery: r""" @@ -28348,12 +28721,12 @@ class LayoutToNetlist: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -28378,12 +28751,12 @@ class LayoutToNetlist: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -29665,12 +30038,12 @@ class LoadLayoutOptions: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -30428,15 +30801,6 @@ class LoadLayoutOptions: This attribute has been added in version 0.28. """ @classmethod - def from_technology(cls, technology: str) -> LoadLayoutOptions: - r""" - @brief Gets the reader options of a given technology - @param technology The name of the technology to apply - Returns the reader options of a specific technology. If the technology name is not valid or an empty string, the reader options of the default technology are returned. - - This method has been introduced in version 0.25 - """ - @classmethod def new(cls) -> LoadLayoutOptions: r""" @brief Creates a new object of this class @@ -30954,6 +31318,11 @@ class Matrix2d: @param v The vector to transform. @return The transformed vector """ + def __repr__(self) -> str: + r""" + @brief Convert the matrix to a string. + @return The string representing this matrix + """ @overload def __rmul__(self, box: DBox) -> DBox: r""" @@ -31395,6 +31764,11 @@ class Matrix3d: @param v The vector to transform. @return The transformed vector """ + def __repr__(self) -> str: + r""" + @brief Convert the matrix to a string. + @return The string representing this matrix + """ @overload def __rmul__(self, box: DBox) -> DBox: r""" @@ -31821,6 +32195,12 @@ class Net(NetlistObject): @brief Sets the name of the net. The name of the net is used for naming the net in schematic files for example. The name of the net has to be unique. """ + def __repr__(self) -> str: + r""" + @brief Gets the qualified name. + The qualified name is like the expanded name, but the circuit's name is preceded + (i.e. 'CIRCUIT:NET') if available. + """ def __str__(self) -> str: r""" @brief Gets the qualified name. @@ -32445,14 +32825,14 @@ class NetTerminalRef: @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to. + @brief Gets the net this terminal reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this terminal reference is attached to. """ def terminal_def(self) -> DeviceTerminalDefinition: r""" @@ -33081,10 +33461,20 @@ class NetTracerTechnologyComponent(TechnologyComponent): Usually it's not required to call this method. It has been introduced in version 0.24. """ + def add(self, connection: NetTracerConnectivity) -> None: + r""" + @brief Adds a connectivity definition. + This method has been introduced in version 0.28.7 + """ def assign(self, other: TechnologyComponent) -> None: r""" @brief Assigns another object to self """ + def clear(self) -> None: + r""" + @brief Removes all connectivity definitions. + This method has been introduced in version 0.28.7 + """ def dup(self) -> NetTracerTechnologyComponent: r""" @brief Creates a copy of self @@ -33131,6 +33521,11 @@ class Netlist: r""" @brief Creates a new object of this class """ + def __repr__(self) -> str: + r""" + @brief Converts the netlist to a string representation. + This method is intended for test purposes mainly. + """ def __str__(self) -> str: r""" @brief Converts the netlist to a string representation. @@ -33310,12 +33705,6 @@ class Netlist: Iterating bottom-up means the parent circuits come after the child circuits. This is the basically the reverse order as delivered by \each_circuit_top_down. """ @overload - def each_circuit_top_down(self) -> Iterator[Circuit]: - r""" - @brief Iterates over the circuits top-down - Iterating top-down means the parent circuits come before the child circuits. The first \top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits. - """ - @overload def each_circuit_top_down(self) -> Iterator[Circuit]: r""" @brief Iterates over the circuits top-down (const version) @@ -33324,6 +33713,12 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload + def each_circuit_top_down(self) -> Iterator[Circuit]: + r""" + @brief Iterates over the circuits top-down + Iterating top-down means the parent circuits come before the child circuits. The first \top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits. + """ + @overload def each_device_class(self) -> Iterator[DeviceClass]: r""" @brief Iterates over the device classes of the netlist @@ -34760,14 +35155,6 @@ class NetlistSpiceReader(NetlistReader): r""" @brief Creates a new reader with a delegate. """ - def __copy__(self) -> NetlistSpiceReader: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> NetlistSpiceReader: - r""" - @brief Creates a copy of self - """ @overload def __init__(self) -> None: r""" @@ -34815,14 +35202,6 @@ class NetlistSpiceReader(NetlistReader): Usually it's not required to call this method. It has been introduced in version 0.24. """ - def assign(self, other: NetlistReader) -> None: - r""" - @brief Assigns another object to self - """ - def dup(self) -> NetlistSpiceReader: - r""" - @brief Creates a copy of self - """ class NetlistSpiceReaderDelegate: r""" @@ -34887,6 +35266,15 @@ class NetlistSpiceReaderDelegate: Usually it's not required to call this method. It has been introduced in version 0.24. """ + def apply_parameter_scaling(self, device: Device) -> None: + r""" + @brief Applies parameter scaling to the given device + Applies SI scaling (according to the parameter's si_scaling attribute) and geometry scaling (according to the parameter's geo_scale_exponent attribute) to the device parameters. Use this method of finish the device when you have created a custom device yourself. + + The geometry scale is taken from the '.options scale=...' control statement. + + This method has been introduced in version 0.28.6. + """ def assign(self, other: NetlistSpiceReaderDelegate) -> None: r""" @brief Assigns another object to self @@ -34929,6 +35317,11 @@ class NetlistSpiceReaderDelegate: r""" @hide """ + def get_scale(self) -> float: + r""" + @brief Gets the scale factor set with '.options scale=...' + This method has been introduced in version 0.28.6. + """ def is_const_object(self) -> bool: r""" @brief Returns a value indicating whether the reference is a const reference @@ -35915,12 +36308,12 @@ class PCellParameterState: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -36798,6 +37191,10 @@ class Path: @brief Inequality test @param p The object to compare against """ + def __repr__(self) -> str: + r""" + @brief Convert to a string + """ def __rmul__(self, f: float) -> Path: r""" @brief Scaling by some factor @@ -37284,6 +37681,13 @@ class Point: This method has been added in version 0.23. """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion. + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __rmul__(self, f: float) -> Point: r""" @brief Scaling by some factor @@ -37686,6 +38090,10 @@ class Polygon: @brief Returns a value indicating whether the polygons are not equal @param p The object to compare against """ + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ def __rmul__(self, f: float) -> Polygon: r""" @brief Scales the polygon by some factor @@ -38294,6 +38702,13 @@ class Polygon: This method was introduced in version 0.23. The 'keep_hv' optional parameter was added in version 0.27. """ + def sort_holes(self) -> None: + r""" + @brief Brings the holes in a specific order + This function is normalize the hole order so the comparison of two polygons does not depend on the order the holes were inserted. Polygons generated by KLayout's alorithms have their holes sorted. + + This method has been introduced in version 0.28.8. + """ def split(self) -> List[Polygon]: r""" @brief Splits the polygon into two or more parts @@ -38485,12 +38900,12 @@ class PreferredOrientation: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -38670,12 +39085,12 @@ class PropertyConstraint: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -38700,12 +39115,12 @@ class PropertyConstraint: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -40138,12 +40553,12 @@ class Region(ShapeCollection): @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -40168,12 +40583,12 @@ class Region(ShapeCollection): @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -40890,6 +41305,11 @@ class Region(ShapeCollection): The boolean OR is implemented by merging the polygons of both regions. To simply join the regions without merging, the + operator is more efficient. """ + def __repr__(self) -> str: + r""" + @brief Converts the region to a string + The length of the output is limited to 20 polygons to avoid giant strings on large regions. For full output use "to_s" with a maximum count parameter. + """ def __str__(self) -> str: r""" @brief Converts the region to a string @@ -44672,6 +45092,12 @@ class Shape: r""" @brief Inequality operator """ + def __repr__(self) -> str: + r""" + @brief Create a string showing the contents of the reference + + This method has been introduced with version 0.16. + """ def __str__(self) -> str: r""" @brief Create a string showing the contents of the reference @@ -45543,10 +45969,16 @@ class Shapes: SAll: ClassVar[int] r""" @brief Indicates that all shapes shall be retrieved + You can use this constant to construct 'except' classes - e.g. to specify 'all shape types except boxes' use + + @code SAll - SBoxes @/code """ SAllWithProperties: ClassVar[int] r""" @brief Indicates that all shapes with properties shall be retrieved + Using this selector means to retrieve only shapes with properties.You can use this constant to construct 'except' classes - e.g. to specify 'all shape types with properties except boxes' use + + @code SAllWithProperties - SBoxes @/code """ SBoxes: ClassVar[int] r""" @@ -45576,6 +46008,7 @@ class Shapes: SProperties: ClassVar[int] r""" @brief Indicates that only shapes with properties shall be retrieved + You can or-combine this flag with the plain shape types to select a certain shape type, but only those shapes with properties. For example to select boxes with properties, use 'SProperties | SBoxes'. """ SRegions: ClassVar[int] r""" @@ -45600,11 +46033,17 @@ class Shapes: def s_all(cls) -> int: r""" @brief Indicates that all shapes shall be retrieved + You can use this constant to construct 'except' classes - e.g. to specify 'all shape types except boxes' use + + @code SAll - SBoxes @/code """ @classmethod def s_all_with_properties(cls) -> int: r""" @brief Indicates that all shapes with properties shall be retrieved + Using this selector means to retrieve only shapes with properties.You can use this constant to construct 'except' classes - e.g. to specify 'all shape types with properties except boxes' use + + @code SAllWithProperties - SBoxes @/code """ @classmethod def s_boxes(cls) -> int: @@ -45641,6 +46080,7 @@ class Shapes: def s_properties(cls) -> int: r""" @brief Indicates that only shapes with properties shall be retrieved + You can or-combine this flag with the plain shape types to select a certain shape type, but only those shapes with properties. For example to select boxes with properties, use 'SProperties | SBoxes'. """ @classmethod def s_regions(cls) -> int: @@ -45731,10 +46171,19 @@ class Shapes: This method has been added in version 0.28. """ + @overload def clear(self) -> None: r""" @brief Clears the shape container - This method has been introduced in version 0.16. It can only be used in editable mode. + This method has been introduced in version 0.16. + """ + @overload + def clear(self, flags: int) -> None: + r""" + @brief Clears certain shape types from the shape container + Only shapes matching the shape types from 'flags' are removed. 'flags' is a combination of the S... constants. + + This method has been introduced in version 0.28.9. """ def create(self) -> None: r""" @@ -46959,6 +47408,10 @@ class SimplePolygon: @brief Returns a value indicating whether self is not equal to p @param p The object to compare against """ + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ def __rmul__(self, f: float) -> SimplePolygon: r""" @brief Scales the polygon by some factor @@ -47533,12 +47986,6 @@ class SubCircuit(NetlistObject): Usually it's not required to call this method. It has been introduced in version 0.24. """ @overload - def circuit(self) -> Circuit: - r""" - @brief Gets the circuit the subcircuit lives in. - This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - """ - @overload def circuit(self) -> Circuit: r""" @brief Gets the circuit the subcircuit lives in (non-const version). @@ -47547,6 +47994,12 @@ class SubCircuit(NetlistObject): This constness variant has been introduced in version 0.26.8 """ @overload + def circuit(self) -> Circuit: + r""" + @brief Gets the circuit the subcircuit lives in. + This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + """ + @overload def circuit_ref(self) -> Circuit: r""" @brief Gets the circuit referenced by the subcircuit (non-const version). @@ -48119,7 +48572,8 @@ class Text: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: int r""" @@ -48271,6 +48725,13 @@ class Text: Return true, if this text object and the given text are not equal """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief Converts the object to a string. + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ def __str__(self, dbu: Optional[float] = ...) -> str: r""" @brief Converts the object to a string. @@ -48997,6 +49458,11 @@ class Texts(ShapeCollection): Starting with version 0.27, the method is called 'count' for consistency with \Region. 'size' is still provided as an alias. """ + def __repr__(self) -> str: + r""" + @brief Converts the text collection to a string + The length of the output is limited to 20 texts to avoid giant strings on large collections. For full output use "to_s" with a maximum count parameter. + """ def __str__(self) -> str: r""" @brief Converts the text collection to a string @@ -49066,27 +49532,10 @@ class Texts(ShapeCollection): Starting with version 0.27, the method is called 'count' for consistency with \Region. 'size' is still provided as an alias. """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ def data_id(self) -> int: r""" @brief Returns the data ID (a unique identifier for the underlying data storage) """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ def disable_progress(self) -> None: r""" @brief Disable progress reporting @@ -49187,12 +49636,6 @@ class Texts(ShapeCollection): @return A new text collection containing the texts inside or on the edge of polygons from the region """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ def is_deep(self) -> bool: r""" @brief Returns true if the edge pair collection is a deep (hierarchical) one @@ -49922,14 +50365,6 @@ class TilingProcessor: @param edges The \Edges object to which the data is sent """ @overload - def output(self, name: str, image: lay.BasicImage) -> None: - r""" - @brief Specifies output to an image - This method will establish an output channel which delivers float data to image data. The image is a monochrome image where each pixel corresponds to a single tile. This method for example is useful to collect density information into an image. The image is configured such that each pixel covers one tile. - - The name is the name which must be used in the _output function of the scripts in order to address that channel. - """ - @overload def output(self, name: str, layout: Layout, cell: int, layer_index: int) -> None: r""" @brief Specifies output to a layout layer @@ -49956,14 +50391,6 @@ class TilingProcessor: @param lp The layer specification where the output will be sent to """ @overload - def output(self, name: str, rdb: rdb.ReportDatabase, cell_id: int, category_id: int) -> None: - r""" - @brief Specifies output to a report database - This method will establish an output channel for the processor. The output sent to that channel will be put into the report database given by the "rdb" parameter. "cell_id" specifies the cell and "category_id" the category to use. - - The name is the name which must be used in the _output function of the scripts in order to address that channel. - """ - @overload def output(self, name: str, rec: TileOutputReceiverBase) -> None: r""" @brief Specifies output for the tiling processor @@ -50504,6 +50931,13 @@ class Trans: r""" @brief Tests for inequality """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ @overload def __rmul__(self, box: Box) -> Box: r""" @@ -50866,12 +51300,12 @@ class TrapezoidDecompositionMode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __init__(self, i: int) -> None: @@ -51175,12 +51609,12 @@ class VAlign: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __init__(self, i: int) -> None: @@ -51820,6 +52254,14 @@ class VCplxTrans: r""" @brief Tests for inequality """ + def __repr__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If 'lazy' is true, some parts are omitted when not required. + If a DBU is given, the output units will be micrometers. + + The lazy and DBU arguments have been added in version 0.27.6. + """ @overload def __rmul__(self, box: DBox) -> Box: r""" @@ -52369,6 +52811,13 @@ class Vector: Returns a new vector with -x,-y. """ + def __repr__(self, dbu: Optional[float] = ...) -> str: + r""" + @brief String conversion + If a DBU is given, the output units will be micrometers. + + The DBU argument has been added in version 0.27.6. + """ @overload def __rmul__(self, f: float) -> Vector: r""" diff --git a/src/pymod/distutils_src/klayout/laycore.pyi b/src/pymod/distutils_src/klayout/laycore.pyi index 83fd6ba69..e7e81e9c5 100644 --- a/src/pymod/distutils_src/klayout/laycore.pyi +++ b/src/pymod/distutils_src/klayout/laycore.pyi @@ -71,6 +71,14 @@ class AbstractMenu: This method has been introduced in version 0.26. """ + def __copy__(self) -> AbstractMenu: + r""" + @brief Creates a copy of self + """ + def __deepcopy__(self) -> AbstractMenu: + r""" + @brief Creates a copy of self + """ def __init__(self) -> None: r""" @hide @@ -119,6 +127,10 @@ class AbstractMenu: @param path The path to the item. @return A reference to a Action object associated with this path or nil if the path is not valid """ + def assign(self, other: AbstractMenu) -> None: + r""" + @brief Assigns another object to self + """ def clear_menu(self, path: str) -> None: r""" @brief Deletes the children of the item given by the path @@ -152,6 +164,10 @@ class AbstractMenu: This method returns true, if the object was destroyed, either explicitly or by the C++ side. The latter may happen, if the object is owned by a C++ object which got destroyed itself. """ + def dup(self) -> AbstractMenu: + r""" + @brief Creates a copy of self + """ def group(self, group: str) -> List[str]: r""" @brief Gets the group members @@ -601,14 +617,6 @@ class ActionBase: @brief Gets a value indicating whether the item is visible The visibility combines with \is_hidden?. To get the true visiblity, use \is_effective_visible?. """ - def macro(self) -> Macro: - r""" - @brief Gets the macro associated with the action - If the action is associated with a macro, this method returns a reference to the \Macro object. Otherwise, this method returns nil. - - - This method has been added in version 0.25. - """ def trigger(self) -> None: r""" @brief Triggers the action programmatically @@ -1166,6 +1174,11 @@ class Annotation(BasicAnnotation): r""" @brief Inequality operator """ + def __repr__(self) -> str: + r""" + @brief Returns the string representation of the ruler + This method was introduced in version 0.19. + """ def __str__(self) -> str: r""" @brief Returns the string representation of the ruler @@ -1682,442 +1695,6 @@ class BitmapBuffer: This method may not be available if PNG support is not compiled into KLayout. """ -class BrowserDialog: - r""" - @brief A HTML display and browser dialog - - The browser dialog displays HTML code in a browser panel. The HTML code is delivered through a separate object of class \BrowserSource which acts as a "server" for a specific kind of URL scheme. Whenever the browser sees a URL starting with "int:" it will ask the connected BrowserSource object for the HTML code of that page using it's 'get' method. The task of the BrowserSource object is to format the data requested in HTML and deliver it. - - One use case for that class is the implementation of rich data browsers for structured information. In a simple scenario, the browser dialog can be instantiated with a static HTML page. In that case, only the content of that page is shown. - - Here's a simple example: - - @code - html = "Hello, world!" - RBA::BrowserDialog::new(html).exec - @/code - - And that is an example for the use case with a \BrowserSource as the "server": - - @code - class MySource < RBA::BrowserSource - def get(url) - if (url =~ /b.html$/) - return "The second page" - else - return "The first page with a link" - end - end - end - - source = MySource::new - RBA::BrowserDialog::new(source).exec - @/code - """ - @property - def caption(self) -> None: - r""" - WARNING: This variable can only be set, not retrieved. - @brief Sets the caption of the window - """ - @property - def home(self) -> None: - r""" - WARNING: This variable can only be set, not retrieved. - @brief Sets the browser's initial and current URL which is selected if the "home" location is chosen - The home URL is the one shown initially and the one which is selected when the "home" button is pressed. The default location is "int:/index.html". - """ - @property - def label(self) -> None: - r""" - WARNING: This variable can only be set, not retrieved. - @brief Sets the label text - - The label is shown left of the navigation buttons. - By default, no label is specified. - - This method has been introduced in version 0.23. - """ - @property - def source(self) -> None: - r""" - WARNING: This variable can only be set, not retrieved. - @brief Connects to a source object - - Setting the source should be the first thing done after the BrowserDialog object is created. It will not have any effect after the browser has loaded the first page. In particular, \home= should be called after the source was set. - """ - @overload - @classmethod - def new(cls, html: str) -> BrowserDialog: - r""" - @brief Creates a HTML browser window with a static HTML content - This method has been introduced in version 0.23. - """ - @overload - @classmethod - def new(cls, source: BrowserSource) -> BrowserDialog: - r""" - @brief Creates a HTML browser window with a \BrowserSource as the source of HTML code - This method has been introduced in version 0.23. - """ - @overload - def __init__(self, html: str) -> None: - r""" - @brief Creates a HTML browser window with a static HTML content - This method has been introduced in version 0.23. - """ - @overload - def __init__(self, source: BrowserSource) -> None: - r""" - @brief Creates a HTML browser window with a \BrowserSource as the source of HTML code - This method has been introduced in version 0.23. - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def exec_(self) -> int: - r""" - @brief Executes the HTML browser dialog as a modal window - """ - def execute(self) -> int: - r""" - @brief Executes the HTML browser dialog as a modal window - """ - def hide(self) -> None: - r""" - @brief Hides the HTML browser window - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def load(self, url: str) -> None: - r""" - @brief Loads the given URL into the browser dialog - Typically the URL has the "int:" scheme so the HTML code is taken from the \BrowserSource object. - """ - def reload(self) -> None: - r""" - @brief Reloads the current page - """ - def resize(self, width: int, height: int) -> None: - r""" - @brief Sets the size of the dialog window - """ - def search(self, search_item: str) -> None: - r""" - @brief Issues a search request using the given search item and the search URL specified with \set_search_url - - See \set_search_url for a description of the search mechanism. - """ - def set_caption(self, caption: str) -> None: - r""" - @brief Sets the caption of the window - """ - def set_home(self, home_url: str) -> None: - r""" - @brief Sets the browser's initial and current URL which is selected if the "home" location is chosen - The home URL is the one shown initially and the one which is selected when the "home" button is pressed. The default location is "int:/index.html". - """ - def set_search_url(self, url: str, query_item: str) -> None: - r""" - @brief Enables the search field and specifies the search URL generated for a search - - If a search URL is set, the search box right to the navigation bar will be enabled. When a text is entered into the search box, the browser will navigate to an URL composed of the search URL, the search item and the search text, i.e. "myurl?item=search_text". - - This method has been introduced in version 0.23. - """ - def set_size(self, width: int, height: int) -> None: - r""" - @brief Sets the size of the dialog window - """ - def set_source(self, source: BrowserSource) -> None: - r""" - @brief Connects to a source object - - Setting the source should be the first thing done after the BrowserDialog object is created. It will not have any effect after the browser has loaded the first page. In particular, \home= should be called after the source was set. - """ - def show(self) -> None: - r""" - @brief Shows the HTML browser window in a non-modal way - """ - -class BrowserSource: - r""" - @brief The BrowserDialog's source for "int" URL's - - The source object basically acts as a "server" for special URL's using "int" as the scheme. - Classes that want to implement such functionality must derive from BrowserSource and reimplement - the \get method. This method is supposed to deliver a HTML page for the given URL. - - Alternatively to implementing this functionality, a source object may be instantiated using the - constructor with a HTML code string. This will create a source object that simply displays the given string - as the initial and only page. - """ - @classmethod - def new(cls, arg0: str) -> BrowserSource: - r""" - @brief Constructs a BrowserSource object with a default HTML string - - The default HTML string is sent when no specific implementation is provided. - """ - @classmethod - def new_html(cls, arg0: str) -> BrowserSource: - r""" - @brief Constructs a BrowserSource object with a default HTML string - - The default HTML string is sent when no specific implementation is provided. - """ - def __copy__(self) -> BrowserSource: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> BrowserSource: - r""" - @brief Creates a copy of self - """ - def __init__(self, arg0: str) -> None: - r""" - @brief Constructs a BrowserSource object with a default HTML string - - The default HTML string is sent when no specific implementation is provided. - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: BrowserSource) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> BrowserSource: - r""" - @brief Creates a copy of self - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def next_topic(self, url: str) -> str: - r""" - @brief Gets the next topic URL from a given URL - An empty string will be returned if no next topic is available. - - This method has been introduced in version 0.28. - """ - def prev_topic(self, url: str) -> str: - r""" - @brief Gets the previous topic URL from a given URL - An empty string will be returned if no previous topic is available. - - This method has been introduced in version 0.28. - """ - -class BrowserSource_Native: - r""" - @hide - @alias BrowserSource - """ - @classmethod - def new(cls) -> BrowserSource_Native: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> BrowserSource_Native: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> BrowserSource_Native: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: BrowserSource_Native) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> BrowserSource_Native: - r""" - @brief Creates a copy of self - """ - def get(self, arg0: str) -> str: - r""" - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def next_topic(self, url: str) -> str: - r""" - """ - def prev_topic(self, url: str) -> str: - r""" - """ - class ButtonState: r""" @brief The namespace for the button state flags in the mouse events of the Plugin class. @@ -2928,496 +2505,6 @@ class Dispatcher: is returned but no exception is thrown. """ -class DoubleValue: - r""" - @brief Encapsulate a floating point value - @hide - This class is provided as a return value of \InputDialog::get_double. - By using an object rather than a pure value, an object with \has_value? = false can be returned indicating that - the "Cancel" button was pressed. Starting with version 0.22, the InputDialog class offers new method which do no - longer requires to use this class. - """ - @classmethod - def new(cls) -> DoubleValue: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> DoubleValue: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> DoubleValue: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: DoubleValue) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> DoubleValue: - r""" - @brief Creates a copy of self - """ - def has_value(self) -> bool: - r""" - @brief True, if a value is present - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def to_f(self) -> float: - r""" - @brief Get the actual value (a synonym for \value) - """ - def value(self) -> float: - r""" - @brief Get the actual value - """ - -class FileDialog: - r""" - @brief Various methods to request a file name - - This class provides some basic dialogs to select a file or directory. This functionality is provided through the static (class) methods ask_... - - Here are some examples: - - @code - # get an existing directory: - v = RBA::FileDialog::ask_existing_dir("Dialog Title", ".") - # get multiple files: - v = RBA::FileDialog::ask_open_file_names("Title", ".", "All files (*)") - # ask for one file name to save a file: - v = RBA::FileDialog::ask_save_file_name("Title", ".", "All files (*)") - @/code - - All these examples return the "nil" value if "Cancel" is pressed. - - If you have enabled the Qt binding, you can use \QFileDialog directly. - """ - @classmethod - def ask_existing_dir(cls, title: str, dir: str) -> Any: - r""" - @brief Open a dialog to select a directory - - @param title The title of the dialog - @param dir The directory selected initially - @return The directory path selected or "nil" if "Cancel" was pressed - - This method has been introduced in version 0.23. It is somewhat easier to use than the get_... equivalent. - """ - @classmethod - def ask_open_file_name(cls, title: str, dir: str, filter: str) -> Any: - r""" - @brief Select one file for opening - - @param title The title of the dialog - @param dir The directory selected initially - @param filter The filters available, for example "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" - @return The path of the file selected or "nil" if "Cancel" was pressed - - This method has been introduced in version 0.23. It is somewhat easier to use than the get_... equivalent. - """ - @classmethod - def ask_open_file_names(cls, title: str, dir: str, filter: str) -> Any: - r""" - @brief Select one or multiple files for opening - - @param title The title of the dialog - @param dir The directory selected initially - @param filter The filters available, for example "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" - @return An array with the file paths selected or "nil" if "Cancel" was pressed - - This method has been introduced in version 0.23. It is somewhat easier to use than the get_... equivalent. - """ - @classmethod - def ask_save_file_name(cls, title: str, dir: str, filter: str) -> Any: - r""" - @brief Select one file for writing - - @param title The title of the dialog - @param dir The directory selected initially - @param filter The filters available, for example "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" - @return The path of the file chosen or "nil" if "Cancel" was pressed - - This method has been introduced in version 0.23. It is somewhat easier to use than the get_... equivalent. - """ - @classmethod - def get_existing_dir(cls, title: str, dir: str) -> StringValue: - r""" - @brief Open a dialog to select a directory - - @param title The title of the dialog - @param dir The directory selected initially - @return A \StringValue object that contains the directory path selected or with has_value? = false if "Cancel" was pressed - - Starting with version 0.23 this method is deprecated. Use \ask_existing_dir instead. - """ - @classmethod - def get_open_file_name(cls, title: str, dir: str, filter: str) -> StringValue: - r""" - @brief Select one file for opening - - @param title The title of the dialog - @param dir The directory selected initially - @param filter The filters available, for example "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" - @return A \StringValue object that contains the files selected or with has_value? = false if "Cancel" was pressed - - Starting with version 0.23 this method is deprecated. Use \ask_open_file_name instead. - """ - @classmethod - def get_open_file_names(cls, title: str, dir: str, filter: str) -> StringListValue: - r""" - @brief Select one or multiple files for opening - - @param title The title of the dialog - @param dir The directory selected initially - @param filter The filters available, for example "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" - @return A \StringListValue object that contains the files selected or with has_value? = false if "Cancel" was pressed - - Starting with version 0.23 this method is deprecated. Use \ask_open_file_names instead. - """ - @classmethod - def get_save_file_name(cls, title: str, dir: str, filter: str) -> StringValue: - r""" - @brief Select one file for writing - - @param title The title of the dialog - @param dir The directory selected initially - @param filter The filters available, for example "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" - @return A \StringValue object that contains the files selected or with has_value? = false if "Cancel" was pressed - - Starting with version 0.23 this method is deprecated. Use \ask_save_file_name instead. - """ - @classmethod - def new(cls) -> FileDialog: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> FileDialog: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> FileDialog: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: FileDialog) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> FileDialog: - r""" - @brief Creates a copy of self - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - -class HelpDialog: - r""" - @brief The help dialog - - This class makes the help dialog available as an individual object. - - This class has been added in version 0.25. - """ - @classmethod - def new(cls, modal: bool) -> HelpDialog: - r""" - @brief Creates a new help dialog - If the modal flag is true, the dialog will be shown as a modal window. - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def exec_(self) -> int: - r""" - @brief Executes the dialog (shows it modally) - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def load(self, url: str) -> None: - r""" - @brief Loads the specified URL - This method will call the page with the given URL. - """ - def search(self, topic: str) -> None: - r""" - @brief Issues a search on the specified topic - This method will call the search page with the given topic. - """ - def show(self) -> None: - r""" - @brief Shows the dialog - """ - -class HelpSource(BrowserSource_Native): - r""" - @brief A BrowserSource implementation delivering the help text for the help dialog - This class can be used together with a \BrowserPanel or \BrowserDialog object to implement custom help systems. - - The basic URL's served by this class are: "int:/index.xml" for the index page and "int:/search.xml?string=..." for the search topic retrieval. - - This class has been added in version 0.25. - """ - @classmethod - def create_index_file(cls, path: str) -> None: - r""" - @brief Reserved internal use - """ - @classmethod - def plain(cls) -> HelpSource: - r""" - @brief Reserved for internal use - """ - def _assign(self, other: BrowserSource_Native) -> None: - r""" - @brief Assigns another object to self - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _dup(self) -> HelpSource: - r""" - @brief Creates a copy of self - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def get_option(self, key: str) -> Any: - r""" - @brief Reserved for internal use - """ - def parent_of(self, path: str) -> str: - r""" - @brief Reserved internal use - """ - def scan(self) -> None: - r""" - @brief Reserved internal use - """ - def set_option(self, key: str, value: Any) -> None: - r""" - @brief Reserved for internal use - """ - def title_for(self, path: str) -> str: - r""" - @brief Reserved internal use - """ - def urls(self) -> List[str]: - r""" - @brief Reserved for internal use - """ - class Image(BasicImage): r""" @brief An image to be stored as a layout annotation @@ -3817,6 +2904,12 @@ class Image(BasicImage): @param green The green channel data set which will become owned by the image @param blue The blue channel data set which will become owned by the image """ + def __repr__(self) -> str: + r""" + @brief Converts the image to a string + The string returned can be used to create an image object using \from_s. + @return The string + """ def __str__(self) -> str: r""" @brief Converts the image to a string @@ -4338,374 +3431,6 @@ class ImageDataMapping: @return The number of entries. """ -class InputDialog: - r""" - @brief Various methods to open a dialog requesting data entry - This class provides some basic dialogs to enter a single value. Values can be strings floating-point values, integer values or an item from a list. - This functionality is provided through the static (class) methods ask_... - - Here are some examples: - - @code - # get a double value between -10 and 10 (initial value is 0): - v = RBA::InputDialog::ask_double_ex("Dialog Title", "Enter the value here:", 0, -10, 10, 1) - # get an item from a list: - v = RBA::InputDialog::ask_item("Dialog Title", "Select one:", [ "item 1", "item 2", "item 3" ], 1) - @/code - - All these examples return the "nil" value if "Cancel" is pressed. - - If you have enabled the Qt binding, you can use \QInputDialog directly. - """ - @classmethod - def ask_double(cls, title: str, label: str, value: float, digits: int) -> Any: - r""" - @brief Open an input dialog requesting a floating-point value - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @param digits The number of digits allowed - @return The value entered if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def ask_double_ex(cls, title: str, label: str, value: float, min: float, max: float, digits: int) -> Any: - r""" - @brief Open an input dialog requesting a floating-point value with enhanced capabilities - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @param min The minimum value allowed - @param max The maximum value allowed - @param digits The number of digits allowed - @return The value entered if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def ask_int(cls, title: str, label: str, value: int) -> Any: - r""" - @brief Open an input dialog requesting an integer value - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @return The value entered if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def ask_int_ex(cls, title: str, label: str, value: int, min: int, max: int, step: int) -> Any: - r""" - @brief Open an input dialog requesting an integer value with enhanced capabilities - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @param min The minimum value allowed - @param max The maximum value allowed - @param step The step size for the spin buttons - @return The value entered if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def ask_item(cls, title: str, label: str, items: Sequence[str], value: int) -> Any: - r""" - @brief Open an input dialog requesting an item from a list - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param items The list of items to show in the selection element - @param selection The initial selection (index of the element selected initially) - @return The string of the item selected if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def ask_string(cls, title: str, label: str, value: str) -> Any: - r""" - @brief Open an input dialog requesting a string - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @return The string entered if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def ask_string_password(cls, title: str, label: str, value: str) -> Any: - r""" - @brief Open an input dialog requesting a string without showing the actual characters entered - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @return The string entered if "Ok" was pressed or nil if "Cancel" was pressed - This method has been introduced in 0.22 and is somewhat easier to use than the get_.. equivalent. - """ - @classmethod - def get_double(cls, title: str, label: str, value: float, digits: int) -> DoubleValue: - r""" - @brief Open an input dialog requesting a floating-point value - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @param digits The number of digits allowed - @return A \DoubleValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def get_double_ex(cls, title: str, label: str, value: float, min: float, max: float, digits: int) -> DoubleValue: - r""" - @brief Open an input dialog requesting a floating-point value with enhanced capabilities - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @param min The minimum value allowed - @param max The maximum value allowed - @param digits The number of digits allowed - @return A \DoubleValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def get_int(cls, title: str, label: str, value: int) -> IntValue: - r""" - @brief Open an input dialog requesting an integer value - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @return A \IntValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def get_int_ex(cls, title: str, label: str, value: int, min: int, max: int, step: int) -> IntValue: - r""" - @brief Open an input dialog requesting an integer value with enhanced capabilities - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @param min The minimum value allowed - @param max The maximum value allowed - @param step The step size for the spin buttons - @return A \IntValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def get_item(cls, title: str, label: str, items: Sequence[str], value: int) -> StringValue: - r""" - @brief Open an input dialog requesting an item from a list - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param items The list of items to show in the selection element - @param selection The initial selection (index of the element selected initially) - @return A \StringValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def get_string(cls, title: str, label: str, value: str) -> StringValue: - r""" - @brief Open an input dialog requesting a string - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @return A \StringValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def get_string_password(cls, title: str, label: str, value: str) -> StringValue: - r""" - @brief Open an input dialog requesting a string without showing the actual characters entered - @param title The title to display for the dialog - @param label The label text to display for the dialog - @param value The initial value for the input field - @return A \StringValue object with has_value? set to true, if "Ok" was pressed and the value given in it's value attribute - Starting from 0.22, this method is deprecated and it is recommended to use the ask_... equivalent. - """ - @classmethod - def new(cls) -> InputDialog: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> InputDialog: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> InputDialog: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: InputDialog) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> InputDialog: - r""" - @brief Creates a copy of self - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - -class IntValue: - r""" - @brief Encapsulate an integer value - @hide - This class is provided as a return value of \InputDialog::get_int. - By using an object rather than a pure value, an object with \has_value? = false can be returned indicating that - the "Cancel" button was pressed. Starting with version 0.22, the InputDialog class offers new method which do no - longer requires to use this class. - """ - @classmethod - def new(cls) -> IntValue: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> IntValue: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> IntValue: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: IntValue) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> IntValue: - r""" - @brief Creates a copy of self - """ - def has_value(self) -> bool: - r""" - @brief True, if a value is present - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def to_i(self) -> int: - r""" - @brief Get the actual value (a synonym for \value) - """ - def value(self) -> int: - r""" - @brief Get the actual value - """ - class KeyCode: r""" @brief The namespace for the some key codes. @@ -6107,51 +4832,41 @@ class LayoutView(LayoutViewBase): The visual part of the view is the tab panel in the main window. The non-visual part are the redraw thread, the layout handles, cell lists, layer view lists etc. This object controls these aspects of the view and controls the appearance of the data. """ - on_close: None + current: ClassVar[LayoutView] + r""" + @brief Returns the current view + The current view is the one that is made current by using \current=. + + This variation has been introduced for the non-Qt case in version 0.28. + """ + on_drawing_finished_event: None r""" Getter: - @brief A event indicating that the view is about to close + @brief An event indicating that the image is fully drawn - This event is triggered when the view is going to be closed entirely. + This event is triggered when calling \timer. Before this event is issue, a final \on_image_updated_event may be issued. - It has been added in version 0.25. + This event has been introduced in version 0.28. Setter: - @brief A event indicating that the view is about to close + @brief An event indicating that the image is fully drawn - This event is triggered when the view is going to be closed entirely. + This event is triggered when calling \timer. Before this event is issue, a final \on_image_updated_event may be issued. - It has been added in version 0.25. + This event has been introduced in version 0.28. """ - on_hide: None + on_image_updated_event: None r""" Getter: - @brief A event indicating that the view is going to become invisible + @brief An event indicating that the image ("screenshot") was updated - It has been added in version 0.25. + This event is triggered when calling \timer. + This event has been introduced in version 0.28. Setter: - @brief A event indicating that the view is going to become invisible + @brief An event indicating that the image ("screenshot") was updated - It has been added in version 0.25. + This event is triggered when calling \timer. + This event has been introduced in version 0.28. """ - on_show: None - r""" - Getter: - @brief A event indicating that the view is going to become visible - - It has been added in version 0.25. - Setter: - @brief A event indicating that the view is going to become visible - - It has been added in version 0.25. - """ - @classmethod - def current(cls) -> LayoutView: - r""" - @brief Returns the current view - The current view is the one that is shown in the current tab. Returns nil if no layout is loaded. - - This method has been introduced in version 0.23. - """ @classmethod def new(cls, editable: Optional[bool] = ..., manager: Optional[db.Manager] = ..., options: Optional[int] = ...) -> LayoutView: r""" @@ -6161,7 +4876,7 @@ class LayoutView(LayoutViewBase): @param editable True to make the view editable @param manager The \Manager object to enable undo/redo - @param options A combination of the values in the LV_... constants from \LayoutViewBase + @param options A combination of the values in the LV_... constants This constructor has been introduced in version 0.25. It has been enhanced with the arguments in version 0.27. @@ -6174,7 +4889,7 @@ class LayoutView(LayoutViewBase): @param editable True to make the view editable @param manager The \Manager object to enable undo/redo - @param options A combination of the values in the LV_... constants from \LayoutViewBase + @param options A combination of the values in the LV_... constants This constructor has been introduced in version 0.25. It has been enhanced with the arguments in version 0.27. @@ -6216,39 +4931,14 @@ class LayoutView(LayoutViewBase): Usually it's not required to call this method. It has been introduced in version 0.24. """ - def bookmark_view(self, name: str) -> None: + def timer(self) -> None: r""" - @brief Bookmarks the current view under the given name + @brief A callback required to be called regularily in the non-Qt case. - @param name The name under which to bookmark the current state - """ - def close(self) -> None: - r""" - @brief Closes the view + This callback eventually implements the event loop in the non-Qt case. The main task is to indicate new versions of the layout image while it is drawn. When a new image has arrived, this method will issue an \on_image_updated_event. In the implementation of the latter, "screenshot" may be called to retrieve the current image. + When drawing has finished, the \on_drawing_finished_event will be triggered. - This method has been added in version 0.27. - """ - def show_l2ndb(self, l2ndb_index: int, cv_index: int) -> None: - r""" - @brief Shows a netlist database in the marker browser on a certain layout - The netlist browser is opened showing the netlist database with the index given by "l2ndb_index". - It will be attached (i.e. navigate to) the layout with the given cellview index in "cv_index". - - This method has been added in version 0.26. - """ - def show_lvsdb(self, lvsdb_index: int, cv_index: int) -> None: - r""" - @brief Shows a netlist database in the marker browser on a certain layout - The netlist browser is opened showing the netlist database with the index given by "lvsdb_index". - It will be attached (i.e. navigate to) the layout with the given cellview index in "cv_index". - - This method has been added in version 0.26. - """ - def show_rdb(self, rdb_index: int, cv_index: int) -> None: - r""" - @brief Shows a report database in the marker browser on a certain layout - The marker browser is opened showing the report database with the index given by "rdb_index". - It will be attached (i.e. navigate to) the layout with the given cellview index in "cv_index". + This method has been introduced in version 0.28. """ class LayoutViewBase: @@ -7815,13 +6505,6 @@ class LayoutViewBase: This method has been introduced in version 0.28. """ - def netlist_browser(self) -> NetlistBrowserDialog: - r""" - @brief Gets the netlist browser object for the given layout view - - - This method has been added in version 0.27. - """ def num_l2ndbs(self) -> int: r""" @brief Gets the number of netlist databases loaded into this view @@ -9426,1299 +8109,6 @@ class MacroInterpreter: Registration of the interpreter makes the object known to the system. After registration, macros whose interpreter is set to 'dsl' can use this object to run the script. For executing a script, the system will call the interpreter's \execute method. """ -class MainWindow: - r""" - @brief The main application window and central controller object - - This object first is the main window but also the main controller. The main controller is the port by which access can be gained to all the data objects, view and other aspects of the program. - """ - current_view_index: int - r""" - Getter: - @brief Returns the current view's index - - @return The index of the current view - - This method will return the index of the current view. - Setter: - @brief Selects the view with the given index - - @param index The index of the view to select (0 is the first) - - This method will make the view with the given index the current (front) view. - - This method was renamed from select_view to current_view_index= in version 0.25. The old name is still available, but deprecated. - """ - @property - def synchronous(self) -> None: - r""" - WARNING: This variable can only be set, not retrieved. - @brief Puts the main window into synchronous mode - - @param sync_mode 'true' if the application should behave synchronously - - In synchronous mode, an application is allowed to block on redraw. While redrawing, no user interactions are possible. Although this is not desirable for smooth operation, it can be beneficial for test or automation purposes, i.e. if a screenshot needs to be produced once the application has finished drawing. - """ - initial_technology: str - r""" - Getter: - @brief Gets the technology used for creating or loading layouts (unless explicitly specified) - - @return The current initial technology - This method was added in version 0.22. - Setter: - @brief Sets the technology used for creating or loading layouts (unless explicitly specified) - - Setting the technology will have an effect on the next load_layout or create_layout operation which does not explicitly specify the technology but might not be reflected correctly in the reader options dialog and changes will be reset when the application is restarted. - @param tech The new initial technology - - This method was added in version 0.22. - """ - on_current_view_changed: None - r""" - Getter: - @brief An event indicating that the current view has changed - - This event is triggered after the current view has changed. This happens, if the user switches the layout tab. - - Before version 0.25 this event was based on the observer pattern obsolete now. The corresponding methods (add_current_view_observer/remove_current_view_observer) have been removed in 0.25. - - Setter: - @brief An event indicating that the current view has changed - - This event is triggered after the current view has changed. This happens, if the user switches the layout tab. - - Before version 0.25 this event was based on the observer pattern obsolete now. The corresponding methods (add_current_view_observer/remove_current_view_observer) have been removed in 0.25. - """ - on_view_closed: None - r""" - Getter: - @brief An event indicating that a view was closed - @param index The index of the view that was closed - - This event is triggered after a view was closed. For example, because the tab was closed. - - This event has been added in version 0.25. - - Setter: - @brief An event indicating that a view was closed - @param index The index of the view that was closed - - This event is triggered after a view was closed. For example, because the tab was closed. - - This event has been added in version 0.25. - """ - on_view_created: None - r""" - Getter: - @brief An event indicating that a new view was created - @param index The index of the view that was created - - This event is triggered after a new view was created. For example, if a layout is loaded into a new panel. - - Before version 0.25 this event was based on the observer pattern obsolete now. The corresponding methods (add_new_view_observer/remove_new_view_observer) have been removed in 0.25. - - Setter: - @brief An event indicating that a new view was created - @param index The index of the view that was created - - This event is triggered after a new view was created. For example, if a layout is loaded into a new panel. - - Before version 0.25 this event was based on the observer pattern obsolete now. The corresponding methods (add_new_view_observer/remove_new_view_observer) have been removed in 0.25. - """ - @classmethod - def instance(cls) -> MainWindow: - r""" - @brief Gets application's main window instance - - This method has been added in version 0.24. - """ - @classmethod - def menu_symbols(cls) -> List[str]: - r""" - @brief Gets all available menu symbols (see \call_menu). - NOTE: currently this method delivers a superset of all available symbols. Depending on the context, no all symbols may trigger actual functionality. - - This method has been introduced in version 0.27. - """ - @classmethod - def new(cls) -> MainWindow: - r""" - @brief Creates a new object of this class - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def call_menu(self, symbol: str) -> None: - r""" - @brief Calls the menu item with the provided symbol. - To obtain all symbols, use menu_symbols. - - This method has been introduced in version 0.27 and replaces the previous cm_... methods. Instead of calling a specific cm_... method, use LayoutView#call_menu with 'cm_...' as the symbol. - """ - def cancel(self) -> None: - r""" - @brief Cancels current editing operations - - This method call cancels all current editing operations and restores normal mouse mode. - """ - def clear_config(self) -> None: - r""" - @brief Clears the configuration parameters - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().clear_config()'. See \Dispatcher#clear_config for details. - - This method has been introduced in version 0.27. - """ - def clone_current_view(self) -> None: - r""" - @brief Clones the current view and make it current - """ - def close_all(self) -> None: - r""" - @brief Closes all views - - This method unconditionally closes all views. No dialog will be opened if unsaved edits exist. - - This method was added in version 0.18. - """ - def close_current_view(self) -> None: - r""" - @brief Closes the current view - - This method does not open a dialog to ask which cell view to close if multiple cells are opened in the view, but rather closes all cells. - """ - def cm_adjust_origin(self) -> None: - r""" - @brief 'cm_adjust_origin' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_adjust_origin')" instead. - """ - def cm_bookmark_view(self) -> None: - r""" - @brief 'cm_bookmark_view' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_bookmark_view')" instead. - """ - def cm_cancel(self) -> None: - r""" - @brief 'cm_cancel' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cancel')" instead. - """ - def cm_cell_copy(self) -> None: - r""" - @brief 'cm_cell_copy' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_copy')" instead. - """ - def cm_cell_cut(self) -> None: - r""" - @brief 'cm_cell_cut' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_cut')" instead. - """ - def cm_cell_delete(self) -> None: - r""" - @brief 'cm_cell_delete' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_delete')" instead. - """ - def cm_cell_flatten(self) -> None: - r""" - @brief 'cm_cell_flatten' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_flatten')" instead. - """ - def cm_cell_hide(self) -> None: - r""" - @brief 'cm_cell_hide' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_hide')" instead. - """ - def cm_cell_paste(self) -> None: - r""" - @brief 'cm_cell_paste' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_paste')" instead. - """ - def cm_cell_rename(self) -> None: - r""" - @brief 'cm_cell_rename' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_rename')" instead. - """ - def cm_cell_select(self) -> None: - r""" - @brief 'cm_cell_select' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_select')" instead. - """ - def cm_cell_show(self) -> None: - r""" - @brief 'cm_cell_show' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_show')" instead. - """ - def cm_cell_show_all(self) -> None: - r""" - @brief 'cm_cell_show_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cell_show_all')" instead. - """ - def cm_clear_layer(self) -> None: - r""" - @brief 'cm_clear_layer' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_clear_layer')" instead. - """ - def cm_clone(self) -> None: - r""" - @brief 'cm_clone' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_clone')" instead. - """ - def cm_close(self) -> None: - r""" - @brief 'cm_close' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_close')" instead. - """ - def cm_close_all(self) -> None: - r""" - @brief 'cm_close_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_close_all')" instead. - """ - def cm_copy(self) -> None: - r""" - @brief 'cm_copy' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_copy')" instead. - """ - def cm_copy_layer(self) -> None: - r""" - @brief 'cm_copy_layer' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_copy_layer')" instead. - """ - def cm_cut(self) -> None: - r""" - @brief 'cm_cut' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_cut')" instead. - """ - def cm_dec_max_hier(self) -> None: - r""" - @brief 'cm_dec_max_hier' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_dec_max_hier')" instead. - """ - def cm_delete(self) -> None: - r""" - @brief 'cm_delete' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_delete')" instead. - """ - def cm_delete_layer(self) -> None: - r""" - @brief 'cm_delete_layer' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_delete_layer')" instead. - """ - def cm_edit_layer(self) -> None: - r""" - @brief 'cm_edit_layer' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_edit_layer')" instead. - """ - def cm_exit(self) -> None: - r""" - @brief 'cm_exit' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_exit')" instead. - """ - def cm_goto_position(self) -> None: - r""" - @brief 'cm_goto_position' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_goto_position')" instead. - """ - def cm_help_about(self) -> None: - r""" - @brief 'cm_help_about' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_help_about')" instead. - """ - def cm_inc_max_hier(self) -> None: - r""" - @brief 'cm_inc_max_hier' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_inc_max_hier')" instead. - """ - def cm_last_display_state(self) -> None: - r""" - @brief 'cm_prev_display_state|#cm_last_display_state' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_prev_display_state|#cm_last_display_state')" instead. - """ - def cm_layout_props(self) -> None: - r""" - @brief 'cm_layout_props' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_layout_props')" instead. - """ - def cm_load_bookmarks(self) -> None: - r""" - @brief 'cm_load_bookmarks' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_load_bookmarks')" instead. - """ - def cm_load_layer_props(self) -> None: - r""" - @brief 'cm_load_layer_props' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_load_layer_props')" instead. - """ - def cm_lv_add_missing(self) -> None: - r""" - @brief 'cm_lv_add_missing' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_add_missing')" instead. - """ - def cm_lv_delete(self) -> None: - r""" - @brief 'cm_lv_delete' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_delete')" instead. - """ - def cm_lv_expand_all(self) -> None: - r""" - @brief 'cm_lv_expand_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_expand_all')" instead. - """ - def cm_lv_group(self) -> None: - r""" - @brief 'cm_lv_group' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_group')" instead. - """ - def cm_lv_hide(self) -> None: - r""" - @brief 'cm_lv_hide' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_hide')" instead. - """ - def cm_lv_hide_all(self) -> None: - r""" - @brief 'cm_lv_hide_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_hide_all')" instead. - """ - def cm_lv_insert(self) -> None: - r""" - @brief 'cm_lv_insert' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_insert')" instead. - """ - def cm_lv_new_tab(self) -> None: - r""" - @brief 'cm_lv_new_tab' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_new_tab')" instead. - """ - def cm_lv_regroup_by_datatype(self) -> None: - r""" - @brief 'cm_lv_regroup_by_datatype' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_regroup_by_datatype')" instead. - """ - def cm_lv_regroup_by_index(self) -> None: - r""" - @brief 'cm_lv_regroup_by_index' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_regroup_by_index')" instead. - """ - def cm_lv_regroup_by_layer(self) -> None: - r""" - @brief 'cm_lv_regroup_by_layer' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_regroup_by_layer')" instead. - """ - def cm_lv_regroup_flatten(self) -> None: - r""" - @brief 'cm_lv_regroup_flatten' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_regroup_flatten')" instead. - """ - def cm_lv_remove_tab(self) -> None: - r""" - @brief 'cm_lv_remove_tab' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_remove_tab')" instead. - """ - def cm_lv_remove_unused(self) -> None: - r""" - @brief 'cm_lv_remove_unused' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_remove_unused')" instead. - """ - def cm_lv_rename(self) -> None: - r""" - @brief 'cm_lv_rename' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_rename')" instead. - """ - def cm_lv_rename_tab(self) -> None: - r""" - @brief 'cm_lv_rename_tab' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_rename_tab')" instead. - """ - def cm_lv_select_all(self) -> None: - r""" - @brief 'cm_lv_select_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_select_all')" instead. - """ - def cm_lv_show(self) -> None: - r""" - @brief 'cm_lv_show' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_show')" instead. - """ - def cm_lv_show_all(self) -> None: - r""" - @brief 'cm_lv_show_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_show_all')" instead. - """ - def cm_lv_show_only(self) -> None: - r""" - @brief 'cm_lv_show_only' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_show_only')" instead. - """ - def cm_lv_sort_by_dli(self) -> None: - r""" - @brief 'cm_lv_sort_by_dli' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_sort_by_dli')" instead. - """ - def cm_lv_sort_by_idl(self) -> None: - r""" - @brief 'cm_lv_sort_by_idl' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_sort_by_idl')" instead. - """ - def cm_lv_sort_by_ild(self) -> None: - r""" - @brief 'cm_lv_sort_by_ild' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_sort_by_ild')" instead. - """ - def cm_lv_sort_by_ldi(self) -> None: - r""" - @brief 'cm_lv_sort_by_ldi' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_sort_by_ldi')" instead. - """ - def cm_lv_sort_by_name(self) -> None: - r""" - @brief 'cm_lv_sort_by_name' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_sort_by_name')" instead. - """ - def cm_lv_source(self) -> None: - r""" - @brief 'cm_lv_source' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_source')" instead. - """ - def cm_lv_ungroup(self) -> None: - r""" - @brief 'cm_lv_ungroup' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_lv_ungroup')" instead. - """ - def cm_macro_editor(self) -> None: - r""" - @brief 'cm_macro_editor' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_macro_editor')" instead. - """ - def cm_manage_bookmarks(self) -> None: - r""" - @brief 'cm_manage_bookmarks' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_manage_bookmarks')" instead. - """ - def cm_max_hier(self) -> None: - r""" - @brief 'cm_max_hier' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_max_hier')" instead. - """ - def cm_max_hier_0(self) -> None: - r""" - @brief 'cm_max_hier_0' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_max_hier_0')" instead. - """ - def cm_max_hier_1(self) -> None: - r""" - @brief 'cm_max_hier_1' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_max_hier_1')" instead. - """ - def cm_navigator_close(self) -> None: - r""" - @brief 'cm_navigator_close' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_navigator_close')" instead. - """ - def cm_new_cell(self) -> None: - r""" - @brief 'cm_new_cell' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_new_cell')" instead. - """ - def cm_new_layer(self) -> None: - r""" - @brief 'cm_new_layer' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_new_layer')" instead. - """ - def cm_new_layout(self) -> None: - r""" - @brief 'cm_new_layout' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_new_layout')" instead. - """ - def cm_new_panel(self) -> None: - r""" - @brief 'cm_new_panel' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_new_panel')" instead. - """ - def cm_next_display_state(self) -> None: - r""" - @brief 'cm_next_display_state' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_next_display_state')" instead. - """ - def cm_open(self) -> None: - r""" - @brief 'cm_open' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_open')" instead. - """ - def cm_open_current_cell(self) -> None: - r""" - @brief 'cm_open_current_cell' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_open_current_cell')" instead. - """ - def cm_open_new_view(self) -> None: - r""" - @brief 'cm_open_new_view' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_open_new_view')" instead. - """ - def cm_open_too(self) -> None: - r""" - @brief 'cm_open_too' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_open_too')" instead. - """ - def cm_packages(self) -> None: - r""" - @brief 'cm_packages' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_packages')" instead. - """ - def cm_pan_down(self) -> None: - r""" - @brief 'cm_pan_down' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_pan_down')" instead. - """ - def cm_pan_left(self) -> None: - r""" - @brief 'cm_pan_left' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_pan_left')" instead. - """ - def cm_pan_right(self) -> None: - r""" - @brief 'cm_pan_right' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_pan_right')" instead. - """ - def cm_pan_up(self) -> None: - r""" - @brief 'cm_pan_up' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_pan_up')" instead. - """ - def cm_paste(self) -> None: - r""" - @brief 'cm_paste' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_paste')" instead. - """ - def cm_prev_display_state(self) -> None: - r""" - @brief 'cm_prev_display_state|#cm_last_display_state' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_prev_display_state|#cm_last_display_state')" instead. - """ - def cm_print(self) -> None: - r""" - @brief 'cm_print' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_print')" instead. - """ - def cm_pull_in(self) -> None: - r""" - @brief 'cm_pull_in' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_pull_in')" instead. - """ - def cm_reader_options(self) -> None: - r""" - @brief 'cm_reader_options' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_reader_options')" instead. - """ - def cm_redo(self) -> None: - r""" - @brief 'cm_redo' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_redo')" instead. - """ - def cm_redraw(self) -> None: - r""" - @brief 'cm_redraw' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_redraw')" instead. - """ - def cm_reload(self) -> None: - r""" - @brief 'cm_reload' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_reload')" instead. - """ - def cm_reset_window_state(self) -> None: - r""" - @brief 'cm_reset_window_state' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_reset_window_state')" instead. - """ - def cm_restore_session(self) -> None: - r""" - @brief 'cm_restore_session' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_restore_session')" instead. - """ - def cm_save(self) -> None: - r""" - @brief 'cm_save' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save')" instead. - """ - def cm_save_all(self) -> None: - r""" - @brief 'cm_save_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save_all')" instead. - """ - def cm_save_as(self) -> None: - r""" - @brief 'cm_save_as' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save_as')" instead. - """ - def cm_save_bookmarks(self) -> None: - r""" - @brief 'cm_save_bookmarks' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save_bookmarks')" instead. - """ - def cm_save_current_cell_as(self) -> None: - r""" - @brief 'cm_save_current_cell_as' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save_current_cell_as')" instead. - """ - def cm_save_layer_props(self) -> None: - r""" - @brief 'cm_save_layer_props' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save_layer_props')" instead. - """ - def cm_save_session(self) -> None: - r""" - @brief 'cm_save_session' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_save_session')" instead. - """ - def cm_screenshot(self) -> None: - r""" - @brief 'cm_screenshot' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_screenshot')" instead. - """ - def cm_screenshot_to_clipboard(self) -> None: - r""" - @brief 'cm_screenshot_to_clipboard' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_screenshot_to_clipboard')" instead. - """ - def cm_sel_flip_x(self) -> None: - r""" - @brief 'cm_sel_flip_x' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_flip_x')" instead. - """ - def cm_sel_flip_y(self) -> None: - r""" - @brief 'cm_sel_flip_y' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_flip_y')" instead. - """ - def cm_sel_free_rot(self) -> None: - r""" - @brief 'cm_sel_free_rot' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_free_rot')" instead. - """ - def cm_sel_move(self) -> None: - r""" - @brief 'cm_sel_move' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_move')" instead. - """ - def cm_sel_move_to(self) -> None: - r""" - @brief 'cm_sel_move_to' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_move_to')" instead. - """ - def cm_sel_rot_ccw(self) -> None: - r""" - @brief 'cm_sel_rot_ccw' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_rot_ccw')" instead. - """ - def cm_sel_rot_cw(self) -> None: - r""" - @brief 'cm_sel_rot_cw' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_rot_cw')" instead. - """ - def cm_sel_scale(self) -> None: - r""" - @brief 'cm_sel_scale' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_sel_scale')" instead. - """ - def cm_select_all(self) -> None: - r""" - @brief 'cm_select_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_select_all')" instead. - """ - def cm_select_cell(self) -> None: - r""" - @brief 'cm_select_cell' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_select_cell')" instead. - """ - def cm_select_current_cell(self) -> None: - r""" - @brief 'cm_select_current_cell' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_select_current_cell')" instead. - """ - def cm_setup(self) -> None: - r""" - @brief 'cm_setup' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_setup')" instead. - """ - def cm_show_properties(self) -> None: - r""" - @brief 'cm_show_properties' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_show_properties')" instead. - """ - def cm_technologies(self) -> None: - r""" - @brief 'cm_technologies' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_technologies')" instead. - """ - def cm_undo(self) -> None: - r""" - @brief 'cm_undo' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_undo')" instead. - """ - def cm_unselect_all(self) -> None: - r""" - @brief 'cm_unselect_all' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_unselect_all')" instead. - """ - def cm_view_log(self) -> None: - r""" - @brief 'cm_view_log' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_view_log')" instead. - """ - def cm_zoom_fit(self) -> None: - r""" - @brief 'cm_zoom_fit' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_zoom_fit')" instead. - """ - def cm_zoom_fit_sel(self) -> None: - r""" - @brief 'cm_zoom_fit_sel' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_zoom_fit_sel')" instead. - """ - def cm_zoom_in(self) -> None: - r""" - @brief 'cm_zoom_in' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_zoom_in')" instead. - """ - def cm_zoom_out(self) -> None: - r""" - @brief 'cm_zoom_out' action. - This method is deprecated in version 0.27. - Use "call_menu('cm_zoom_out')" instead. - """ - def commit_config(self) -> None: - r""" - @brief Commits the configuration settings - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().commit_config(...)'. See \Dispatcher#commit_config for details. - - This method has been introduced in version 0.27. - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - @overload - def create_layout(self, mode: int) -> CellView: - r""" - @brief Creates a new, empty layout - - @param mode An integer value of 0, 1 or 2 that determines how the layout is created - @return The cellview of the layout that was created - - Create the layout in the current view, replacing the current layouts (mode 0), in a new view (mode 1) or adding it to the current view (mode 2). - In mode 1, the new view is made the current one. - - This version uses the initial technology and associates it with the new layout. - - Starting with version 0.25, this method returns a cellview object that can be modified to configure the cellview. - """ - @overload - def create_layout(self, tech: str, mode: int) -> CellView: - r""" - @brief Creates a new, empty layout with the given technology - - @param mode An integer value of 0, 1 or 2 that determines how the layout is created - @param tech The name of the technology to use for that layout. - @return The cellview of the layout that was created - - Create the layout in the current view, replacing the current layouts (mode 0), in a new view (mode 1) or adding it to the current view (mode 2). - In mode 1, the new view is made the current one. - - If the technology name is not a valid technology name, the default technology will be used. - - This version was introduced in version 0.22. - Starting with version 0.25, this method returns a cellview object that can be modified to configure the cellview. - """ - def create_view(self) -> int: - r""" - @brief Creates a new, empty view - - @return The index of the view that was created - - Creates an empty view that can be filled with layouts using the load_layout and create_layout methods on the view object. Use the \view method to obtain the view object from the view index. - This method has been added in version 0.22. - """ - def current_view(self) -> LayoutView: - r""" - @brief Returns a reference to the current view's object - - @return A reference to a \LayoutView object representing the current view. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dispatcher(self) -> Dispatcher: - r""" - @brief Gets the dispatcher interface (the plugin root configuration space) - This method has been introduced in version 0.27. - """ - def enable_edits(self, enable: bool) -> None: - r""" - @brief Enables or disables editing - - @param enable Enable edits if set to true - - Starting from version 0.25, this method enables/disables edits on the current view only. - Use LayoutView#enable_edits instead. - """ - def exit(self) -> None: - r""" - @brief Schedules an exit for the application - - This method does not immediately exit the application but sends an exit request to the application which will cause a clean shutdown of the GUI. - """ - def get_config(self, name: str) -> Any: - r""" - @brief Gets the value of a local configuration parameter - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().get_config(...)'. See \Dispatcher#get_config for details. - - This method has been introduced in version 0.27. - """ - def get_config_names(self) -> List[str]: - r""" - @brief Gets the configuration parameter names - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().get_config_names(...)'. See \Dispatcher#get_config_names for details. - - This method has been introduced in version 0.27. - """ - def get_default_key_bindings(self) -> Dict[str, str]: - r""" - @brief Gets the default key bindings - This method returns a hash with the default key binding vs. menu item path. - You can use this hash with \set_key_bindings to reset all key bindings to the default ones. - - This method has been introduced in version 0.27. - """ - def get_default_menu_items_hidden(self) -> Dict[str, bool]: - r""" - @brief Gets the flags indicating whether menu items are hidden by default - You can use this hash with \set_menu_items_hidden to restore the visibility of all menu items. - - This method has been introduced in version 0.27. - """ - def get_key_bindings(self) -> Dict[str, str]: - r""" - @brief Gets the current key bindings - This method returns a hash with the key binding vs. menu item path. - - This method has been introduced in version 0.27. - """ - def get_menu_items_hidden(self) -> Dict[str, bool]: - r""" - @brief Gets the flags indicating whether menu items are hidden - This method returns a hash with the hidden flag vs. menu item path. - You can use this hash with \set_menu_items_hidden. - - This method has been introduced in version 0.27. - """ - def grid_micron(self) -> float: - r""" - @brief Gets the global grid in micron - - @return The global grid in micron - - The global grid is used at various places, i.e. for ruler snapping, for grid display etc. - """ - def index_of(self, view: LayoutView) -> int: - r""" - @brief Gets the index of the given view - - @return The index of the view that was given - - If the given view is not a view object within the main window, a negative value will be returned. - - This method has been added in version 0.25. - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - @overload - def load_layout(self, filename: str, mode: Optional[int] = ...) -> CellView: - r""" - @brief Loads a new layout - - @param filename The name of the file to load - @param mode An integer value of 0, 1 or 2 that determines how the file is loaded - @return The cellview into which the layout was loaded - - Loads the given file into the current view, replacing the current layouts (mode 0), into a new view (mode 1) or adding the layout to the current view (mode 2). - In mode 1, the new view is made the current one. - - This version will use the initial technology and the default reader options. Others versions are provided which allow specification of technology and reader options explicitly. - - Starting with version 0.25, this method returns a cellview object that can be modified to configure the cellview. The 'mode' argument has been made optional in version 0.28. - """ - @overload - def load_layout(self, filename: str, options: db.LoadLayoutOptions, mode: Optional[int] = ...) -> CellView: - r""" - @brief Loads a new layout with the given options - - @param filename The name of the file to load - @param options The reader options to use. - @param mode An integer value of 0, 1 or 2 that determines how the file is loaded - @return The cellview into which the layout was loaded - - Loads the given file into the current view, replacing the current layouts (mode 0), into a new view (mode 1) or adding the layout to the current view (mode 2). - In mode 1, the new view is made the current one. - - This version was introduced in version 0.22. - Starting with version 0.25, this method returns a cellview object that can be modified to configure the cellview. The 'mode' argument has been made optional in version 0.28. - """ - @overload - def load_layout(self, filename: str, options: db.LoadLayoutOptions, tech: str, mode: Optional[int] = ...) -> CellView: - r""" - @brief Loads a new layout with the given options and associate it with the given technology - - @param filename The name of the file to load - @param options The reader options to use. - @param tech The name of the technology to use for that layout. - @param mode An integer value of 0, 1 or 2 that determines how the file is loaded - @return The cellview into which the layout was loaded - - Loads the given file into the current view, replacing the current layouts (mode 0), into a new view (mode 1) or adding the layout to the current view (mode 2). - In mode 1, the new view is made the current one. - - If the technology name is not a valid technology name, the default technology will be used. - - This version was introduced in version 0.22. - Starting with version 0.25, this method returns a cellview object that can be modified to configure the cellview. The 'mode' argument has been made optional in version 0.28. - """ - @overload - def load_layout(self, filename: str, tech: str, mode: Optional[int] = ...) -> CellView: - r""" - @brief Loads a new layout and associate it with the given technology - - @param filename The name of the file to load - @param tech The name of the technology to use for that layout. - @param mode An integer value of 0, 1 or 2 that determines how the file is loaded - @return The cellview into which the layout was loaded - - Loads the given file into the current view, replacing the current layouts (mode 0), into a new view (mode 1) or adding the layout to the current view (mode 2). - In mode 1, the new view is made the current one. - - If the technology name is not a valid technology name, the default technology will be used. The 'mode' argument has been made optional in version 0.28. - - This version was introduced in version 0.22. - Starting with version 0.25, this method returns a cellview object that can be modified to configure the cellview. - """ - def manager(self) -> db.Manager: - r""" - @brief Gets the \Manager object of this window - - The manager object is responsible to managing the undo/redo stack. Usually this object is not required. It's more convenient and safer to use the related methods provided by \LayoutView (\LayoutView#transaction, \LayoutView#commit) and \MainWindow (such as \MainWindow#cm_undo and \MainWindow#cm_redo). - - This method has been added in version 0.24. - """ - def menu(self) -> AbstractMenu: - r""" - @brief Returns a reference to the abstract menu - - @return A reference to an \AbstractMenu object representing the menu system - """ - def message(self, message: str, time: int) -> None: - r""" - @brief Displays a message in the status bar - - @param message The message to display - @param time The time how long to display the message in ms - - This given message is shown in the status bar for the given time. - - This method has been added in version 0.18. - """ - def read_config(self, file_name: str) -> bool: - r""" - @brief Reads the configuration from a file - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().read_config(...)'. See \Dispatcher#read_config for details. - - This method has been introduced in version 0.27. - """ - def redraw(self) -> None: - r""" - @brief Redraws the current view - - Issues a redraw request to the current view. This usually happens automatically, so this method does not need to be called in most relevant cases. - """ - def resize(self, width: int, height: int) -> None: - r""" - @brief Resizes the window - - @param width The new width of the window - @param height The new width of the window - - This method resizes the window to the given target size including decoration such as menu bar and control panels - """ - def restore_session(self, fn: str) -> None: - r""" - @brief Restores a session from the given file - - @param fn The path to the session file - - The session stored in the given session file is restored. All existing views are closed and all layout edits are discarded without notification. - - This method was added in version 0.18. - """ - def save_session(self, fn: str) -> None: - r""" - @brief Saves the session to the given file - - @param fn The path to the session file - - The session is saved to the given session file. Any existing layout edits are not automatically saved together with the session. The session just holds display settings and annotation objects. If layout edits exist, they have to be saved explicitly in a separate step. - - This method was added in version 0.18. - """ - def select_view(self, index: int) -> None: - r""" - @brief Selects the view with the given index - - @param index The index of the view to select (0 is the first) - - This method will make the view with the given index the current (front) view. - - This method was renamed from select_view to current_view_index= in version 0.25. The old name is still available, but deprecated. - """ - def set_config(self, name: str, value: str) -> None: - r""" - @brief Set a local configuration parameter with the given name to the given value - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().set_config(...)'. See \Dispatcher#set_config for details. - - This method has been introduced in version 0.27. - """ - def set_key_bindings(self, bindings: Dict[str, str]) -> None: - r""" - @brief Sets key bindings. - Sets the given key bindings. Pass a hash listing the key bindings per menu item paths. Key strings follow the usual notation, e.g. 'Ctrl+A', 'Shift+X' or just 'F2'. - Use an empty value to remove a key binding from a menu entry. - - \get_key_bindings will give you the current key bindings, \get_default_key_bindings will give you the default ones. - - Examples: - - @code - # reset all key bindings to default: - mw = RBA::MainWindow.instance() - mw.set_key_bindings(mw.get_default_key_bindings()) - - # disable key binding for 'copy': - RBA::MainWindow.instance.set_key_bindings({ "edit_menu.copy" => "" }) - - # configure 'copy' to use Shift+K and 'cut' to use Ctrl+K: - RBA::MainWindow.instance.set_key_bindings({ "edit_menu.copy" => "Shift+K", "edit_menu.cut" => "Ctrl+K" }) - @/code - - This method has been introduced in version 0.27. - """ - def set_menu_items_hidden(self, arg0: Dict[str, bool]) -> None: - r""" - @brief sets the flags indicating whether menu items are hidden - This method allows hiding certain menu items. It takes a hash with hidden flags vs. menu item paths. - Examples: - - @code - # show all menu items: - mw = RBA::MainWindow.instance() - mw.set_menu_items_hidden(mw.get_default_menu_items_hidden()) - - # hide the 'copy' entry from the 'Edit' menu: - RBA::MainWindow.instance().set_menu_items_hidden({ "edit_menu.copy" => true }) - @/code - - This method has been introduced in version 0.27. - """ - def show_macro_editor(self, cat: Optional[str] = ..., add: Optional[bool] = ...) -> None: - r""" - @brief Shows the macro editor - If 'cat' is given, this category will be selected in the category tab. If 'add' is true, the 'new macro' dialog will be opened. - - This method has been introduced in version 0.26. - """ - def view(self, n: int) -> LayoutView: - r""" - @brief Returns a reference to a view object by index - - @return The view object's reference for the view with the given index. - """ - def views(self) -> int: - r""" - @brief Returns the number of views - - @return The number of views available so far. - """ - def write_config(self, file_name: str) -> bool: - r""" - @brief Writes configuration to a file - This method is provided for using MainWindow without an Application object. It's a convience method which is equivalent to 'dispatcher().write_config(...)'. See \Dispatcher#write_config for details. - - This method has been introduced in version 0.27. - """ - class Marker: r""" @brief The floating-point coordinate marker object @@ -10975,579 +8365,6 @@ class Marker: The set method has been added in version 0.20. """ -class MessageBox: - r""" - @brief Various methods to display message boxes - This class provides some basic message boxes. This functionality is provided through the static (class) methods \warning, \question and so on. - - Here is some example: - - @code - # issue a warning and ask whether to continue: - v = RBA::MessageBox::warning("Dialog Title", "Something happened. Continue?", RBA::MessageBox::Yes + RBA::MessageBox::No) - if v == RBA::MessageBox::Yes - ... continue ... - end - @/code - - If you have enabled the Qt binding, you can use \QMessageBox directly. - """ - Abort: ClassVar[int] - r""" - @brief A constant describing the 'Abort' button - """ - Cancel: ClassVar[int] - r""" - @brief A constant describing the 'Cancel' button - """ - Ignore: ClassVar[int] - r""" - @brief A constant describing the 'Ignore' button - """ - No: ClassVar[int] - r""" - @brief A constant describing the 'No' button - """ - Ok: ClassVar[int] - r""" - @brief A constant describing the 'Ok' button - """ - Retry: ClassVar[int] - r""" - @brief A constant describing the 'Retry' button - """ - Yes: ClassVar[int] - r""" - @brief A constant describing the 'Yes' button - """ - @classmethod - def b_abort(cls) -> int: - r""" - @brief A constant describing the 'Abort' button - """ - @classmethod - def b_cancel(cls) -> int: - r""" - @brief A constant describing the 'Cancel' button - """ - @classmethod - def b_ignore(cls) -> int: - r""" - @brief A constant describing the 'Ignore' button - """ - @classmethod - def b_no(cls) -> int: - r""" - @brief A constant describing the 'No' button - """ - @classmethod - def b_ok(cls) -> int: - r""" - @brief A constant describing the 'Ok' button - """ - @classmethod - def b_retry(cls) -> int: - r""" - @brief A constant describing the 'Retry' button - """ - @classmethod - def b_yes(cls) -> int: - r""" - @brief A constant describing the 'Yes' button - """ - @classmethod - def critical(cls, title: str, text: str, buttons: int) -> int: - r""" - @brief Open a critical (error) message box - @param title The title of the window - @param text The text to show - @param buttons A combination (+) of button constants (\Ok and so on) describing the buttons to show for the message box - @return The button constant describing the button that was pressed - """ - @classmethod - def info(cls, title: str, text: str, buttons: int) -> int: - r""" - @brief Open a information message box - @param title The title of the window - @param text The text to show - @param buttons A combination (+) of button constants (\Ok and so on) describing the buttons to show for the message box - @return The button constant describing the button that was pressed - """ - @classmethod - def new(cls) -> MessageBox: - r""" - @brief Creates a new object of this class - """ - @classmethod - def question(cls, title: str, text: str, buttons: int) -> int: - r""" - @brief Open a question message box - @param title The title of the window - @param text The text to show - @param buttons A combination (+) of button constants (\Ok and so on) describing the buttons to show for the message box - @return The button constant describing the button that was pressed - """ - @classmethod - def warning(cls, title: str, text: str, buttons: int) -> int: - r""" - @brief Open a warning message box - @param title The title of the window - @param text The text to show - @param buttons A combination (+) of button constants (\Ok and so on) describing the buttons to show for the message box - @return The button constant describing the button that was pressed - """ - def __copy__(self) -> MessageBox: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> MessageBox: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: MessageBox) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> MessageBox: - r""" - @brief Creates a copy of self - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - -class NetlistBrowserDialog: - r""" - @brief Represents the netlist browser dialog. - This dialog is a part of the \LayoutView class and can be obtained through \LayoutView#netlist_browser. - This interface allows to interact with the browser - mainly to get information about state changes. - - This class has been introduced in version 0.27. - """ - on_current_db_changed: None - r""" - Getter: - @brief This event is triggered when the current database is changed. - The current database can be obtained with \db. - Setter: - @brief This event is triggered when the current database is changed. - The current database can be obtained with \db. - """ - on_probe: None - r""" - Getter: - @brief This event is triggered when a net is probed. - The first path will indicate the location of the probed net in terms of two paths: one describing the instantiation of the net in layout space and one in schematic space. Both objects are \NetlistObjectPath objects which hold the root circuit, the chain of subcircuits leading to the circuit containing the net and the net itself. - Setter: - @brief This event is triggered when a net is probed. - The first path will indicate the location of the probed net in terms of two paths: one describing the instantiation of the net in layout space and one in schematic space. Both objects are \NetlistObjectPath objects which hold the root circuit, the chain of subcircuits leading to the circuit containing the net and the net itself. - """ - on_selection_changed: None - r""" - Getter: - @brief This event is triggered when the selection changed. - The selection can be obtained with \current_path_first, \current_path_second, \selected_nets, \selected_devices, \selected_subcircuits and \selected_circuits. - Setter: - @brief This event is triggered when the selection changed. - The selection can be obtained with \current_path_first, \current_path_second, \selected_nets, \selected_devices, \selected_subcircuits and \selected_circuits. - """ - @classmethod - def new(cls) -> NetlistBrowserDialog: - r""" - @brief Creates a new object of this class - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def current_path(self) -> NetlistObjectsPath: - r""" - @brief Gets the path of the current object as a path pair (combines layout and schematic object paths in case of a LVS database view). - """ - def current_path_first(self) -> NetlistObjectPath: - r""" - @brief Gets the path of the current object on the first (layout in case of LVS database) side. - """ - def current_path_second(self) -> NetlistObjectPath: - r""" - @brief Gets the path of the current object on the second (schematic in case of LVS database) side. - """ - def db(self) -> db.LayoutToNetlist: - r""" - @brief Gets the database the browser is connected to. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def selected_paths(self) -> List[NetlistObjectsPath]: - r""" - @brief Gets the nets currently selected objects (paths) in the netlist database browser. - The result is an array of path pairs. See \NetlistObjectsPath for details about these pairs. - """ - -class NetlistObjectPath: - r""" - @brief An object describing the instantiation of a netlist object. - This class describes the instantiation of a net or a device or a circuit in terms of a root circuit and a subcircuit chain leading to the indicated object. - - See \net= or \device= for the indicated object, \path= for the subcircuit chain. - - This class has been introduced in version 0.27. - """ - device: db.Device - r""" - Getter: - @brief Gets the device the path points to. - - Setter: - @brief Sets the device the path points to. - If the path describes the location of a device, this member will indicate it. - The other way to describe a final object is \net=. If neither a device nor net is given, the path describes a circuit and how it is referenced from the root. - """ - net: db.Net - r""" - Getter: - @brief Gets the net the path points to. - - Setter: - @brief Sets the net the path points to. - If the path describes the location of a net, this member will indicate it. - The other way to describe a final object is \device=. If neither a device nor net is given, the path describes a circuit and how it is referenced from the root. - """ - path: List[db.SubCircuit] - r""" - Getter: - @brief Gets the path. - - Setter: - @brief Sets the path. - The path is a list of subcircuits leading from the root to the final object. The final (net, device) object is located in the circuit called by the last subcircuit of the subcircuit chain. If the subcircuit list is empty, the final object is located inside the root object. - """ - root: db.Circuit - r""" - Getter: - @brief Gets the root circuit of the path. - - Setter: - @brief Sets the root circuit of the path. - The root circuit is the circuit from which the path starts. - """ - @classmethod - def new(cls) -> NetlistObjectPath: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> NetlistObjectPath: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> NetlistObjectPath: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: NetlistObjectPath) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> NetlistObjectPath: - r""" - @brief Creates a copy of self - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def is_null(self) -> bool: - r""" - @brief Returns a value indicating whether the path is an empty one. - """ - -class NetlistObjectsPath: - r""" - @brief An object describing the instantiation of a single netlist object or a pair of those. - This class is basically a pair of netlist object paths (see \NetlistObjectPath). When derived from a single netlist view, only the first path is valid and will point to the selected object (a net, a device or a circuit). The second path is null. - - If the path is derived from a paired netlist view (a LVS report view), the first path corresponds to the object in the layout netlist, the second one to the object in the schematic netlist. - If the selected object isn't a matched one, either the first or second path may be a null or a partial path without a final net or device object or a partial path. - - This class has been introduced in version 0.27. - """ - @classmethod - def new(cls) -> NetlistObjectsPath: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> NetlistObjectsPath: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> NetlistObjectsPath: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: NetlistObjectsPath) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> NetlistObjectsPath: - r""" - @brief Creates a copy of self - """ - def first(self) -> NetlistObjectPath: - r""" - @brief Gets the first object's path. - In cases of paired netlists (LVS database), the first path points to the layout netlist object. - For the single netlist, the first path is the only path supplied. - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def second(self) -> NetlistObjectPath: - r""" - @brief Gets the second object's path. - In cases of paired netlists (LVS database), the first path points to the schematic netlist object. - For the single netlist, the second path is always a null path. - """ - class ObjectInstPath: r""" @brief A class describing a selected shape or instance @@ -12425,217 +9242,3 @@ class PluginFactory: Registration of the plugin factory makes the object known to the system. Registration requires that the menu items have been set already. Hence it is recommended to put the registration at the end of the initialization method of the factory class. """ -class StringListValue: - r""" - @brief Encapsulate a string list - @hide - This class is provided as a return value of \FileDialog. - By using an object rather than a pure string list, an object with \has_value? = false can be returned indicating that - the "Cancel" button was pressed. Starting with version 0.22, the InputDialog class offers new method which do no - longer requires to use this class. - """ - @classmethod - def new(cls) -> StringListValue: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> StringListValue: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> StringListValue: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: StringListValue) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> StringListValue: - r""" - @brief Creates a copy of self - """ - def has_value(self) -> bool: - r""" - @brief True, if a value is present - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def value(self) -> List[str]: - r""" - @brief Get the actual value (a list of strings) - """ - -class StringValue: - r""" - @brief Encapsulate a string value - @hide - This class is provided as a return value of \InputDialog::get_string, \InputDialog::get_item and \FileDialog. - By using an object rather than a pure value, an object with \has_value? = false can be returned indicating that - the "Cancel" button was pressed. Starting with version 0.22, the InputDialog class offers new method which do no - longer requires to use this class. - """ - @classmethod - def new(cls) -> StringValue: - r""" - @brief Creates a new object of this class - """ - def __copy__(self) -> StringValue: - r""" - @brief Creates a copy of self - """ - def __deepcopy__(self) -> StringValue: - r""" - @brief Creates a copy of self - """ - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - def __str__(self) -> str: - r""" - @brief Get the actual value (a synonym for \value) - """ - def _create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def _destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def _destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def _is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def _manage(self) -> None: - r""" - @brief Marks the object as managed by the script side. - After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def _unmanage(self) -> None: - r""" - @brief Marks the object as no longer owned by the script side. - Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. - - Usually it's not required to call this method. It has been introduced in version 0.24. - """ - def assign(self, other: StringValue) -> None: - r""" - @brief Assigns another object to self - """ - def create(self) -> None: - r""" - @brief Ensures the C++ object is created - Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. - """ - def destroy(self) -> None: - r""" - @brief Explicitly destroys the object - Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. - If the object is not owned by the script, this method will do nothing. - """ - def destroyed(self) -> bool: - r""" - @brief Returns a value indicating whether the object was already destroyed - This method returns true, if the object was destroyed, either explicitly or by the C++ side. - The latter may happen, if the object is owned by a C++ object which got destroyed itself. - """ - def dup(self) -> StringValue: - r""" - @brief Creates a copy of self - """ - def has_value(self) -> bool: - r""" - @brief True, if a value is present - """ - def is_const_object(self) -> bool: - r""" - @brief Returns a value indicating whether the reference is a const reference - This method returns true, if self is a const reference. - In that case, only const methods may be called on self. - """ - def to_s(self) -> str: - r""" - @brief Get the actual value (a synonym for \value) - """ - def value(self) -> str: - r""" - @brief Get the actual value - """ - diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 2a9b7fc41..53dbdc195 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -701,6 +701,12 @@ class RdbItemValue: This method has been introduced in version 0.22. """ + def __repr__(self) -> str: + r""" + @brief Converts a value to a string + The string can be used by the string constructor to create another object from it. + @return The string + """ def __str__(self) -> str: r""" @brief Converts a value to a string diff --git a/src/pymod/distutils_src/klayout/tlcore.pyi b/src/pymod/distutils_src/klayout/tlcore.pyi index fb1b74b09..ae853b477 100644 --- a/src/pymod/distutils_src/klayout/tlcore.pyi +++ b/src/pymod/distutils_src/klayout/tlcore.pyi @@ -320,6 +320,10 @@ class ArgType: r""" @brief Inequality of two types """ + def __repr__(self) -> str: + r""" + @brief Convert to a string + """ def __str__(self) -> str: r""" @brief Convert to a string @@ -2268,6 +2272,10 @@ class Timer: r""" @brief Creates a new object of this class """ + def __repr__(self) -> str: + r""" + @brief Produces a string with the currently elapsed times + """ def __str__(self) -> str: r""" @brief Produces a string with the currently elapsed times @@ -2412,6 +2420,10 @@ class Value: @brief Constructs a non-nil object with the given value. This constructor has been introduced in version 0.22. """ + def __repr__(self) -> str: + r""" + @brief Convert this object to a string + """ def __str__(self) -> str: r""" @brief Convert this object to a string diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc index 3a5887407..4f1f20fd6 100644 --- a/src/pymod/unit_tests/pymod_tests.cc +++ b/src/pymod/unit_tests/pymod_tests.cc @@ -90,6 +90,9 @@ PYMODTEST (import_lay, "import_lay.py") PYMODTEST (import_lib, "import_lib.py") PYMODTEST (pya_tests, "pya_tests.py") +// others +PYMODTEST (issue1327, "issue1327.py") + #if defined(HAVE_QT) && defined(HAVE_QTBINDINGS) PYMODTEST (import_QtCore, "import_QtCore.py") diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index bec6c3ad4..7b79e58e3 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -111,7 +111,7 @@ RubyStackTraceProvider::scope_index (const std::vector &bt // disable scoped debugging (e.g. DRC script lines) if $KLAYOUT_RBA_DEBUG_SCOPE is set. if (consider_scope < 0) { - consider_scope = tl::has_env ("KLAYOUT_RBA_DEBUG_SCOPE") ? 0 : 1; + consider_scope = tl::app_flag ("rba-debug-scope") ? 0 : 1; } if (! consider_scope) { return 0; @@ -2490,9 +2490,11 @@ RubyInterpreter::begin_exec () { d->exit_on_next = false; d->block_exceptions = false; - d->file_id_map.clear (); - if (d->current_exec_level++ == 0 && d->current_exec_handler) { - d->current_exec_handler->start_exec (this); + if (d->current_exec_level++ == 0) { + d->file_id_map.clear (); + if (d->current_exec_handler) { + d->current_exec_handler->start_exec (this); + } } } diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index eded06a07..7478ecbc2 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -711,6 +711,11 @@ static VALUE object_from_variant (tl::Variant &var, Proxy *self, const gsi::ArgT } + } else { + + // This is the case for return values that prefer to be copied (e.g. from const &) + prefer_copy = atype.prefer_copy (); + } return object_to_ruby (obj, self, cls, pass_obj, is_const, prefer_copy, can_destroy); diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 74d0ba77d..914907551 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -24,6 +24,7 @@ #include "tlStream.h" #include "tlLog.h" #include "tlInternational.h" +#include "tlEnv.h" #include @@ -52,6 +53,7 @@ # include # include # include +# include #else @@ -59,6 +61,7 @@ # include # include # include +# include #endif @@ -629,7 +632,7 @@ bool chdir (const std::string &path) static std::pair absolute_path_of_existing (const std::string &s) { -#if defined (_WIN32) +#if defined(_WIN32) wchar_t *fp = _wfullpath (NULL, tl::to_wstring (s).c_str (), 0); if (fp == NULL) { @@ -657,6 +660,11 @@ static std::pair absolute_path_of_existing (const std::string bool is_absolute (const std::string &s) { + // ~ paths are always absolute, because the home directory is + if (s.size () > 0 && s[0] == '~') { + return true; + } + std::vector parts = split_path (s); if (parts.size () > 1 && is_drive (parts [0])) { return is_part_with_separator (parts [1]); @@ -669,6 +677,11 @@ bool is_absolute (const std::string &s) std::string absolute_file_path (const std::string &s) { + // ~ paths are always absolute, because the home directory is + if (s.size () > 0 && s[0] == '~') { + return get_home_path () + std::string (s, 1); + } + std::vector parts = split_path (s); if (parts.empty ()) { return current_dir (); @@ -854,10 +867,36 @@ bool is_same_file (const std::string &a, const std::string &b) #endif } +std::string +get_home_path () +{ +#if !defined(_WIN32) + if (tl::has_env ("HOME")) { + return tl::get_env ("HOME"); + } else { + struct passwd *pwd = getpwuid (getuid ()); + if (pwd) { + return std::string (pwd->pw_dir); + } + } + tl::warn << tl::to_string (tr ("Unable to get home directory (set HOME environment variable)")); +#else + if (tl::has_env ("HOMEDRIVE") && tl::has_env ("HOMEPATH")) { + return tl::get_env ("HOMEDRIVE") + tl::get_env ("HOMEPATH"); + } else if (tl::has_env ("HOMESHARE") && tl::has_env ("HOMEPATH")) { + return tl::get_env ("HOMESHARE") + tl::get_env ("HOMEPATH"); + } else if (tl::has_env ("USERPROFILE")) { + return tl::get_env ("USERPROFILE"); + } + tl::warn << tl::to_string (tr ("Unable to get home directory (no HOMEDRIVE/HOMEPATH, HOMESHARE/HOMEPATH or USERPROFILE environment variables)")); +#endif + return std::string ("."); +} + static std::string get_inst_path_internal () { -#ifdef _WIN32 +#if defined(_WIN32) wchar_t buffer[MAX_PATH]; int len; diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index 95727b9dd..ebf2b0cf0 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -194,6 +194,11 @@ bool TL_PUBLIC chdir (const std::string &path); */ std::vector TL_PUBLIC split_path (const std::string &p, bool keep_last = false); +/** + * @brief Gets the home directory path + */ +std::string TL_PUBLIC get_home_path (); + /** * @brief Gets the path of the currently running process */ diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 2fc9e1244..442a78122 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -666,15 +666,15 @@ TextInputStream::reset () InputFile::InputFile (const std::string &path) : m_fd (-1) { - m_source = path; + m_source = tl::absolute_file_path (path);; #if defined(_WIN32) - int fd = _wopen (tl::to_wstring (path).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL); + int fd = _wopen (tl::to_wstring (m_source).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL); if (fd < 0) { throw FileOpenErrorException (m_source, errno); } m_fd = fd; #else - int fd = open (path.c_str (), O_RDONLY); + int fd = open (m_source.c_str (), O_RDONLY); if (fd < 0) { throw FileOpenErrorException (m_source, errno); } @@ -747,15 +747,15 @@ InputFile::filename () const InputZLibFile::InputZLibFile (const std::string &path) : mp_d (new ZLibFilePrivate ()) { - m_source = path; + m_source = tl::absolute_file_path (path); #if defined(_WIN32) - int fd = _wopen (tl::to_wstring (path).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL); + int fd = _wopen (tl::to_wstring (m_source).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL); if (fd < 0) { throw FileOpenErrorException (m_source, errno); } mp_d->zs = gzdopen (fd, "rb"); #else - mp_d->zs = gzopen (tl::string_to_system (path).c_str (), "rb"); + mp_d->zs = gzopen (tl::string_to_system (m_source).c_str (), "rb"); #endif if (mp_d->zs == NULL) { throw FileOpenErrorException (m_source, errno); @@ -1035,10 +1035,10 @@ OutputStream::seek (size_t pos) // OutputFileBase implementation OutputFileBase::OutputFileBase (const std::string &path, int keep_backups) - : m_keep_backups (keep_backups), m_path (path), m_has_error (false) + : m_keep_backups (keep_backups), m_path (tl::absolute_file_path (path)), m_has_error (false) { - if (tl::file_exists (path)) { - m_backup_path = path + ".~backup"; + if (tl::file_exists (m_path)) { + m_backup_path = m_path + ".~backup"; if (tl::file_exists (m_backup_path)) { if (! tl::rm_file (m_backup_path)) { tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to remove existing file '%s'")), m_backup_path); @@ -1046,8 +1046,8 @@ OutputFileBase::OutputFileBase (const std::string &path, int keep_backups) } } if (! m_backup_path.empty ()) { - if (! tl::rename_file (path, tl::filename (m_backup_path))) { - tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to rename original file '%s' to backup file")), path, m_backup_path); + if (! tl::rename_file (m_path, tl::filename (m_backup_path))) { + tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to rename original file '%s' to backup file")), m_path, m_backup_path); m_backup_path = std::string (); } } diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index bf3c32225..90758df1c 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -210,6 +210,22 @@ inline bool safe_isspace (char c) return c != 0 && static_cast (c) < 0x80 && isspace (c); } +// ------------------------------------------------------------------------- +// Utility: skip a newline + +bool skip_newline (const char *&cp) +{ + if (*cp == '\012' || *cp == '\015') { + if (*cp == '\015' && cp[1] == '\012') { + ++cp; + } + ++cp; + return true; + } else { + return false; + } +} + // ------------------------------------------------------------------------- // Utility: a strtod version that is independent of the locale diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index aa2f18490..33bc0413b 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -987,6 +987,37 @@ TL_PUBLIC uint32_t utf32_upcase (uint32_t c32); */ TL_PUBLIC uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0); +/** + * @brief Checks if the next characters are CR, LF or CR+LF and skips them + * + * This function returns true, if a line separated was found and skipped + */ +TL_PUBLIC bool skip_newline (const char *&cp); + +/** + * @brief checks if the given character is a CR character + */ +inline bool is_cr (char c) +{ + return c == '\015'; +} + +/** + * @brief checks if the given character is a LF character + */ +inline bool is_lf (char c) +{ + return c == '\012'; +} + +/** + * @brief checks if the given character is a CR or LF character + */ +inline bool is_newline (char c) +{ + return is_cr (c) || is_lf (c); +} + } // namespace tl #endif diff --git a/src/tl/tl/tlUri.cc b/src/tl/tl/tlUri.cc index dbd3fc9e6..c416e1e8d 100644 --- a/src/tl/tl/tlUri.cc +++ b/src/tl/tl/tlUri.cc @@ -111,12 +111,9 @@ URI::URI (const std::string &uri) } m_scheme = unescape (m_scheme); - bool prefer_authority = true; - if (m_scheme == "file") { - prefer_authority = false; - // other schemes? - } else if (m_scheme.empty ()) { - prefer_authority = false; + bool prefer_authority = false; + if (m_scheme == "http" || m_scheme == "https") { + prefer_authority = true; } ex0 = ex; diff --git a/src/tl/unit_tests/tlFileUtilsTests.cc b/src/tl/unit_tests/tlFileUtilsTests.cc index 146c91923..f328b28b1 100644 --- a/src/tl/unit_tests/tlFileUtilsTests.cc +++ b/src/tl/unit_tests/tlFileUtilsTests.cc @@ -401,6 +401,8 @@ TEST (10) EXPECT_EQ (tl::extension_last ("\\hello\\.world.gz"), "gz"); EXPECT_EQ (tl::extension_last ("/hello//world/"), ""); + EXPECT_EQ (tl::is_absolute ("~/world"), true); + EXPECT_EQ (tl::is_absolute ("~"), true); EXPECT_EQ (tl::is_absolute ("world"), false); EXPECT_EQ (tl::is_absolute ("world/"), false); EXPECT_EQ (tl::is_absolute ("hello//world/"), false); @@ -796,3 +798,21 @@ TEST (18) EXPECT_EQ (is.read_all (), "hello, world!\n"); } } + +// get_home_path +TEST (19) +{ + std::string home = tl::get_home_path (); + // no specific value, just something ... + EXPECT_EQ (home.size () > 5, true); +#if defined(HAVE_QT) + EXPECT_EQ (tl::replaced (home, "\\", "/"), tl::replaced (tl::to_string (QDir::homePath ()), "\\", "/")); +#endif +} + +// absolute path with "~" expansion +TEST (20) +{ + EXPECT_EQ (tl::absolute_file_path ("~"), tl::get_home_path ()); + EXPECT_EQ (tl::absolute_file_path (tl::combine_path ("~", "test")), tl::combine_path (tl::get_home_path (), "test")); +} diff --git a/testdata/algo/hlp18.oas b/testdata/algo/hlp18.oas new file mode 100644 index 000000000..2c45f1226 Binary files /dev/null and b/testdata/algo/hlp18.oas differ diff --git a/testdata/algo/nreader19b.cir b/testdata/algo/nreader19b.cir new file mode 100644 index 000000000..5e2585fe8 --- /dev/null +++ b/testdata/algo/nreader19b.cir @@ -0,0 +1,29 @@ +* Test + +.options scale=1e-6 + +.model sky130_fd_pr__pfet_01v8 NMOS level=8 version=3.3.0 +.model sky130_fd_pr__nfet_01v8 NMOS level=8 version=3.3.0 + +XXpmos Q I VDD VDD pmos4_standard v=1.5 l=0.15 nf=4 +XXnmos Q I VSS VSS nmos4_standard v=1.5 l=0.15 nf=4 +XXDUMMY0 VSS VSS VSS VSS nmos4_standard v=1.5 l=0.15 nf=2 +XXDUMMY1 VSS VSS VSS VSS nmos4_standard v=1.5 l=0.15 nf=2 +XXDUMMY2 VDD VDD VDD VDD pmos4_standard v=1.5 l=0.15 nf=2 +XXDUMMY3 VDD VDD VDD VDD pmos4_standard v=1.5 l=0.15 nf=2 + +* NOTE: "W" in the "ad" formula uses the previously computed parameter "W" +.subckt pmos4_standard D G S B v=0.1 l=0.018 nf=4 +MM1 D G S B sky130_fd_pr__pfet_01v8 L=l W='v * nf ' ad='int((nf+1)/2) * W/nf**2 * 0.29' as='int((nf+2)/2) * W/nf**2 * 0.29' ++ pd='2*int((nf+1)/2) * (W/nf**2 + 0.29)' ps='2*int((nf+2)/2) * (W/nf**2 + 0.29)' nrd='0.29 / W' nrs='0.29 / W' ++ m=1 +.ends + +* NOTE: "W" in the "ad" formula uses the previously computed parameter "W" +.subckt nmos4_standard D G S B v=0.1 l=0.018 nf=4 +MM1 D G S B sky130_fd_pr__nfet_01v8 L=l W='v * nf ' ad='int((nf+1)/2) * W/nf**2 * 0.29' as='int((nf+2)/2) * W/nf**2 * 0.29' ++ pd='2*int((nf+1)/2) * (W/nf**2 + 0.29)' ps='2*int((nf+2)/2) * (W/nf**2 + 0.29)' nrd='0.29 / W' nrs='0.29 / W' ++ m=1 +.ends + +.end diff --git a/testdata/algo/nreader20.cir b/testdata/algo/nreader20.cir new file mode 100644 index 000000000..7680c8468 --- /dev/null +++ b/testdata/algo/nreader20.cir @@ -0,0 +1,37 @@ + +.subckt test + +* Variations are: +* (1) Cname n1 n2 C=value [other params] +* (2) Cname n1 n2 value [params] +* (3) Cname n1 n2 model C=value [other params] +* Cname n1 n2 n3 C=value [other params] -> not supported, cannot tell from (3) without further analysis +* (4) Cname n1 n2 model value [params] +* Cname n1 n2 n3 value [params] -> not supported, cannot tell from (4) without further analysis +* (5) Cname n1 n2 n3 model C=value [other params] +* (6) Cname n1 n2 value model [params] +* (7) Cname n1 n2 n3 model value [params] +* (8) Cname n1 n2 n3 value model [params] + +* ngspice takes second C parameter (5p) +c1 a b M=1 l=50.000u w=50.000u c=2.5p c=5p +* ngspice ignores C parameter and uses 2.5p +c2a a b M=1 l=50.000u w=50.000u 2.5p c=5p +* ngspice ignores C parameter and uses 2.5p +c2b a b 2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores first C parameter and uses 5p +c3 a b cap_model1 c=2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores C parameter and uses 2.5p +c4 a b cap_model1 2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores first C parameter and uses 5p +c5 a b c cap_model2 M=1 l=50.000u w=50.000u c=2.5p c=5p +* C parameter must not be present for this to be recognized as value +c6 a b 2.5p cap_model1 M=1 l=50.000u w=50.000u +* ngspice ignores C parameter and uses 2.5p +c7a a b c cap_model2 2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores C parameter and uses 2.5p +c7b a b c cap_model2 M=1 l=50.000u w=50.000u 2.5p c=5p +* ngspice ignores C parameter and uses 2.5p +c8 a b c 2.5p cap_model2 M=1 l=50.000u w=50.000u c=5p +.ends + diff --git a/testdata/algo/nreader21.cir b/testdata/algo/nreader21.cir new file mode 100644 index 000000000..21e6d198b --- /dev/null +++ b/testdata/algo/nreader21.cir @@ -0,0 +1,6 @@ + +.lib nreader21_lib1.cir lib1 +.lib nreader21_lib1.cir lib2 + +C100 1 2 15p + diff --git a/testdata/algo/nreader21_inc.cir b/testdata/algo/nreader21_inc.cir new file mode 100644 index 000000000..4dc60c6b2 --- /dev/null +++ b/testdata/algo/nreader21_inc.cir @@ -0,0 +1,4 @@ + +C2a 1 2 101p +C2b 1 2 102p + diff --git a/testdata/algo/nreader21_lib1.cir b/testdata/algo/nreader21_lib1.cir new file mode 100644 index 000000000..9240aa460 --- /dev/null +++ b/testdata/algo/nreader21_lib1.cir @@ -0,0 +1,10 @@ + +.lib lib1 +.lib nreader21_lib2.cir lib3 +C1 1 2 100p +.endl + +.lib lib2 +.include nreader21_inc.cir +.endl + diff --git a/testdata/algo/nreader21_lib2.cir b/testdata/algo/nreader21_lib2.cir new file mode 100644 index 000000000..b2d48e21d --- /dev/null +++ b/testdata/algo/nreader21_lib2.cir @@ -0,0 +1,11 @@ + +.lib lib3 +C10 1 2 1p +.endl + +C11 1 2 1.5p + +.lib lib4 +C12 1 2 42p +.endl + diff --git a/testdata/bool/issue_1366.oas b/testdata/bool/issue_1366.oas new file mode 100644 index 000000000..ee3f5741d Binary files /dev/null and b/testdata/bool/issue_1366.oas differ diff --git a/testdata/bool/issue_1366_au.gds b/testdata/bool/issue_1366_au.gds new file mode 100644 index 000000000..fb136f5d7 Binary files /dev/null and b/testdata/bool/issue_1366_au.gds differ diff --git a/testdata/bool/special2_au1.oas b/testdata/bool/special2_au1.oas index c55543d9f..a5f502f42 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 86229ca34..3290873a0 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 9885b6e32..8b2e223d4 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 9ca99c96f..1e82c2993 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 7410b1c99..861bc779c 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 b2d93d569..8af391f06 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 0e2d0b2d6..e8580a4f1 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 381af30ae..8f595465e 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 045eac670..50fa6a864 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 6081a28b4..a374b17aa 100644 Binary files a/testdata/bool/special2_au5_tz.oas and b/testdata/bool/special2_au5_tz.oas differ diff --git a/testdata/buddies/buddies.rb b/testdata/buddies/buddies.rb index 0812e7f02..3577a1855 100644 --- a/testdata/buddies/buddies.rb +++ b/testdata/buddies/buddies.rb @@ -72,15 +72,15 @@ class Buddies_TestClass < TestBase else out_file = File.join($ut_testtmp, "out_" + bin) end - if File.exists?(out_file) + if File.exist?(out_file) File.unlink(out_file) end - assert_equal(File.exists?(out_file), false) + assert_equal(File.exist?(out_file), false) in_file = File.join(File.dirname(__FILE__), "test1.gds") log = bin + "\n" + `#{self.buddy_bin(bin)} #{in_file} #{out_file} 2>&1` - assert_equal(File.exists?(out_file), true) + assert_equal(File.exist?(out_file), true) assert_equal(log, bin + "\n") File.open(out_file, "rb") do |file| @@ -96,16 +96,16 @@ class Buddies_TestClass < TestBase def test_strmxor out_file = File.join($ut_testtmp, "out") - if File.exists?(out_file) + if File.exist?(out_file) File.unlink(out_file) end - assert_equal(File.exists?(out_file), false) + assert_equal(File.exist?(out_file), false) in_file1 = File.join(File.dirname(__FILE__), "test1.gds") in_file2 = File.join(File.dirname(__FILE__), "test2.gds") log = "strmxor\n" + `#{self.buddy_bin("strmxor")} #{in_file1} #{in_file2} #{out_file} 2>&1` - assert_equal(File.exists?(out_file), true) + assert_equal(File.exist?(out_file), true) assert_equal(log, <<"END") strmxor Warning: Layer 1/0 is not present in second layout, but in first @@ -141,15 +141,15 @@ END def test_strmclip out_file = File.join($ut_testtmp, "out") - if File.exists?(out_file) + if File.exist?(out_file) File.unlink(out_file) end - assert_equal(File.exists?(out_file), false) + assert_equal(File.exist?(out_file), false) in_file = File.join(File.dirname(__FILE__), "test1.gds") log = "strmclip\n" + `#{self.buddy_bin("strmclip")} #{in_file} #{out_file} 2>&1` - assert_equal(File.exists?(out_file), true) + assert_equal(File.exist?(out_file), true) assert_equal(log, "strmclip\n") end diff --git a/testdata/drc/drcSimpleTests_14b.drc b/testdata/drc/drcSimpleTests_14b.drc new file mode 100644 index 000000000..0d4daa56a --- /dev/null +++ b/testdata/drc/drcSimpleTests_14b.drc @@ -0,0 +1,24 @@ + +source($drc_test_source) + +target($drc_test_target) + +l1 = input(1, 0) +l2 = input(2, 0) + +tcopy = new_target($drc_test_target2) +rcopy = new_report("Report 2", $drc_test_report2) + +l1.output(tcopy, 101, 0) +l2.output(tcopy, 102, 0) + +l1.output(1, 0) +l1.space(1.0.um).output(100, 0) + +report("Report 1", $drc_test_report) + +l2.space(1.0.um).output("l2 space < 1µm") +l1.width(1.0.um).output("l1 width < 1µm") + +l1.sep(l2, 1.0.um).output(rcopy, "l1 sep l2 < 1µm") + diff --git a/testdata/drc/drcSimpleTests_14b.gds b/testdata/drc/drcSimpleTests_14b.gds new file mode 100644 index 000000000..c43c2fae9 Binary files /dev/null and b/testdata/drc/drcSimpleTests_14b.gds differ diff --git a/testdata/drc/drcSimpleTests_au14b.gds b/testdata/drc/drcSimpleTests_au14b.gds new file mode 100644 index 000000000..af3ebbcfb Binary files /dev/null and b/testdata/drc/drcSimpleTests_au14b.gds differ diff --git a/testdata/drc/drcSimpleTests_au14b.lyrdb b/testdata/drc/drcSimpleTests_au14b.lyrdb new file mode 100644 index 000000000..0b6c00a9d --- /dev/null +++ b/testdata/drc/drcSimpleTests_au14b.lyrdb @@ -0,0 +1,88 @@ + + + Report 1 + + drc: script='.drc' + TOP + + + + + l2 space < 1µm + + + + + + l1 width < 1µm + + + + + + + + TOP + + + + + + + + + 'l2 space < 1\302\265m' + TOP + false + 1 + + + edge-pair: (-0.2,0.7;1,0.7)|(1,1.1;-0.2,1.1) + + + + + 'l1 width < 1\302\265m' + TOP + false + 1 + + + edge-pair: (0,0;0,0.9)|(0.3,0.9;0.3,0) + + + + + 'l1 width < 1\302\265m' + TOP + false + 1 + + + edge-pair: (0.3,0;0,0)|(0,0.9;0.3,0.9) + + + + + 'l1 width < 1\302\265m' + TOP + false + 1 + + + edge-pair: (0.5,0;0.5,0.9)|(0.8,0.9;0.8,0) + + + + + 'l1 width < 1\302\265m' + TOP + false + 1 + + + edge-pair: (0.8,0;0.5,0)|(0.5,0.9;0.8,0.9) + + + + diff --git a/testdata/drc/drcSimpleTests_au14b_2.gds b/testdata/drc/drcSimpleTests_au14b_2.gds new file mode 100644 index 000000000..b3a512645 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au14b_2.gds differ diff --git a/testdata/drc/drcSimpleTests_au14b_2.lyrdb b/testdata/drc/drcSimpleTests_au14b_2.lyrdb new file mode 100644 index 000000000..f3430532f --- /dev/null +++ b/testdata/drc/drcSimpleTests_au14b_2.lyrdb @@ -0,0 +1,49 @@ + + + Report 2 + + drc: script='.drc' + TOP + + + + + l1 sep l2 < 1µm + + + + + + + + TOP + + + + + + + + + 'l1 sep l2 < 1\302\265m' + TOP + false + 1 + + + edge-pair: (0,0.9;0.3,0.9)/(1,1.1;-0.2,1.1) + + + + + 'l1 sep l2 < 1\302\265m' + TOP + false + 1 + + + edge-pair: (0.5,0.9;0.8,0.9)/(1,1.1;-0.2,1.1) + + + + diff --git a/testdata/gds/issue-1353.gds b/testdata/gds/issue-1353.gds new file mode 100644 index 000000000..31874eeef Binary files /dev/null and b/testdata/gds/issue-1353.gds differ diff --git a/testdata/klayout_main/main.rb b/testdata/klayout_main/main.rb index fff79b11e..279c20417 100644 --- a/testdata/klayout_main/main.rb +++ b/testdata/klayout_main/main.rb @@ -45,7 +45,7 @@ class KLayoutMain_TestClass < TestBase def klayout_bin # special location for MacOS file = File.join(RBA::Application::instance.inst_path, "klayout.app", "Contents", "MacOS", "klayout") - if !File.exists?(file) + if !File.exist?(file) file = File.join(RBA::Application::instance.inst_path, "klayout") end return file diff --git a/testdata/lefdef/issue-1345/au-nojoin.oas.gz b/testdata/lefdef/issue-1345/au-nojoin.oas.gz new file mode 100644 index 000000000..21816fed7 Binary files /dev/null and b/testdata/lefdef/issue-1345/au-nojoin.oas.gz differ diff --git a/testdata/lefdef/issue-1345/au.oas.gz b/testdata/lefdef/issue-1345/au.oas.gz new file mode 100644 index 000000000..92324dc5c Binary files /dev/null and b/testdata/lefdef/issue-1345/au.oas.gz differ diff --git a/testdata/lefdef/issue-1345/in.def b/testdata/lefdef/issue-1345/in.def new file mode 100644 index 000000000..423d5a4be --- /dev/null +++ b/testdata/lefdef/issue-1345/in.def @@ -0,0 +1,16 @@ +VERSION 5.6 ; +NAMESCASESENSITIVE ON ; +DIVIDERCHAR "/" ; +BUSBITCHARS "<>" ; +DESIGN SMALL ; +UNITS DISTANCE MICRONS 100 ; + +DIEAREA ( -30 -30 ) ( 1030 1030 ) ; + +NETS 1 ; +- TOP ++ ROUTED M1 ( 0 0 ) ( 1000 * ) ( * 1000 ) M2_M1 + NEW M2 ( 1000 1000 ) ( 0 * ) ( 0 0 ) ; +END NETS + +END DESIGN diff --git a/testdata/lefdef/issue-1345/in.lef b/testdata/lefdef/issue-1345/in.lef new file mode 100644 index 000000000..a0359f764 --- /dev/null +++ b/testdata/lefdef/issue-1345/in.lef @@ -0,0 +1,40 @@ +VERSION 5.7 ; +NAMESCASESENSITIVE ON ; +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +USEMINSPACING OBS ON ; +USEMINSPACING PIN OFF ; +CLEARANCEMEASURE EUCLIDEAN ; + +MANUFACTURINGGRID 0.05 ; + +LAYER M1 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + WIDTH 0.2 ; +END M1 + +LAYER V2 + TYPE CUT ; +END V2 + +LAYER M2 + TYPE ROUTING ; + DIRECTION VERTICAL ; + WIDTH 0.2 ; +END M2 + +VIA M2_M1 DEFAULT + LAYER M1 ; + RECT -0.300 -0.300 0.300 0.300 ; + LAYER V2 ; + RECT -0.200 -0.200 0.200 0.200 ; + LAYER M2 ; + RECT -0.300 -0.300 0.300 0.300 ; +END M2_M1 + +END LIBRARY diff --git a/testdata/pymod/import_db.py b/testdata/pymod/import_db.py index 2c1881260..154bd7198 100755 --- a/testdata/pymod/import_db.py +++ b/testdata/pymod/import_db.py @@ -41,6 +41,12 @@ class BasicTest(unittest.TestCase): v.read(os.path.join(os.path.dirname(__file__), "..", "gds", "t10.gds")) self.assertEqual(v.top_cell().name, "RINGO") + def test_4(self): + # gds2_text plugin loaded? (issue #1393) + v = db.Layout() + v.read(os.path.join(os.path.dirname(__file__), "..", "gds2_txt", "read.txt")) + self.assertEqual(v.top_cell().name, "RINGO") + # run unit tests if __name__ == '__main__': suite = unittest.TestSuite() diff --git a/testdata/pymod/issue1327.py b/testdata/pymod/issue1327.py new file mode 100755 index 000000000..b6a4fbed5 --- /dev/null +++ b/testdata/pymod/issue1327.py @@ -0,0 +1,28 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2023 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 + +import klayout.db as kdb + +ly = kdb.Layout() +cell = ly.create_cell("TOP") +parents = cell.each_parent_cell() + +# makes the unit test happy +print("OK") + +# was crashing now in finalization code .. + diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index 8e3ea9e22..ae3bcee8b 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -1156,6 +1156,35 @@ class DBLayoutTest(unittest.TestCase): self.assertEqual(shape.property(42), None) self.assertEqual(shape.property(42.0), None) + # Bug #1397 + def test_bug1397(self): + + testtmp = os.getenv("TESTTMP_WITH_NAME", os.getenv("TESTTMP", ".")) + tmp = os.path.join(testtmp, "tmp.gds") + + l = pya.Layout() + + c = l.create_cell("test_cell") + + li = pya.LayerInfo(1, 0) + t = pya.Trans.R180 + c.add_meta_info(pya.LayoutMetaInfo("kfactory:li", li, None, True)) + c.add_meta_info(pya.LayoutMetaInfo("kfactory:t", t, None, True)) + + l.write(tmp) + + l2 = pya.Layout() + l2.read(tmp) + + c2 = l2.cell("test_cell") + + li = c2.meta_info("kfactory:li").value + self.assertEqual(li.layer, 1) + self.assertEqual(li.datatype, 0) + + t = c2.meta_info("kfactory:t").value + self.assertEqual(str(t), "r180 0,0") + # run unit tests if __name__ == '__main__': diff --git a/testdata/python/dbPolygonTest.py b/testdata/python/dbPolygonTest.py index 5ed0fc95a..feab569e6 100644 --- a/testdata/python/dbPolygonTest.py +++ b/testdata/python/dbPolygonTest.py @@ -122,6 +122,8 @@ class DBPolygonTests(unittest.TestCase): b.assign_hole(1, [ pya.DPoint( 15, 25 ), pya.DPoint( 25, 25 ), pya.DPoint( 25, 65 ) ]) self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" ) b.insert_hole( [ pya.DPoint( 1, 2 ), pya.DPoint( 2, 2 ), pya.DPoint( 2, 6 ) ] ) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60/1,2;2,2;2,6)" ) + b.sort_holes() self.assertEqual( str(b), "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" ) b.assign_hole(0, [ pya.DPoint( 15, 25 ), pya.DPoint( 25, 25 ), pya.DPoint( 25, 65 ) ]) self.assertEqual( str(b), "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" ) @@ -234,6 +236,8 @@ class DBPolygonTests(unittest.TestCase): b.assign_hole(1, [ pya.Point( 15, 25 ), pya.Point( 25, 25 ), pya.Point( 25, 65 ) ]) self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" ) b.insert_hole( [ pya.Point( 1, 2 ), pya.Point( 2, 2 ), pya.Point( 2, 6 ) ] ) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60/1,2;2,2;2,6)" ) + b.sort_holes() self.assertEqual( str(b), "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" ) b.assign_hole(0, [ pya.Point( 15, 25 ), pya.Point( 25, 25 ), pya.Point( 25, 65 ) ]) self.assertEqual( str(b), "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" ) diff --git a/testdata/ruby/dbLayoutTests1.rb b/testdata/ruby/dbLayoutTests1.rb index dca1b4b8b..fa7c0ebe6 100644 --- a/testdata/ruby/dbLayoutTests1.rb +++ b/testdata/ruby/dbLayoutTests1.rb @@ -25,6 +25,29 @@ load("test_prologue.rb") class DBLayoutTests1_TestClass < TestBase + def dump_layer(l, layer, cell_name) + s = [] + cell_index = l.cell_by_name(cell_name) + iter = l.begin_shapes(cell_index, layer) + while !iter.at_end + poly = iter.shape.polygon.transformed(iter.trans) + s.push(poly.is_box? ? poly.bbox.to_s : poly.to_s) + iter.next + end + return s.join("; ") + end + + def dump_layer_i(l, layer, cell_index) + s = [] + iter = l.begin_shapes(cell_index, layer) + while !iter.at_end + poly = iter.shape.polygon.transformed(iter.trans) + s.push(poly.is_box? ? poly.bbox.to_s : poly.to_s) + iter.next + end + return s.join("; ") + end + def test_1 # LayerInfo tests @@ -659,27 +682,6 @@ class DBLayoutTests1_TestClass < TestBase c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) - def dump_layer(l, layer, cell_name) - s = [] - cell_index = l.cell_by_name(cell_name) - iter = l.begin_shapes(cell_index, layer) - while !iter.at_end - s.push(iter.shape.box.transformed(iter.trans).to_s) - iter.next - end - return s.join("; ") - end - - def dump_layer_i(l, layer, cell_index) - s = [] - iter = l.begin_shapes(cell_index, layer) - while !iter.at_end - s.push(iter.shape.box.transformed(iter.trans).to_s) - iter.next - end - return s.join("; ") - end - assert_equal(dump_layer(l, 0, "c0"), "(0,100;1000,1200); (0,100;1000,1200); (100,0;1100,1100); (1200,0;2200,1100); (-1200,0;-100,1000)") assert_equal(dump_layer(l, 1, "c0"), "") @@ -854,31 +856,11 @@ class DBLayoutTests1_TestClass < TestBase def test_7 - def dump_layer(l, layer, cell_name) - s = [] - cell_index = l.cell_by_name(cell_name) - iter = l.begin_shapes(cell_index, layer) - while !iter.at_end - s.push(iter.shape.box.transformed(iter.trans).to_s) - iter.next - end - return s.join("; ") - end - - def dump_layer_i(l, layer, cell_index) - s = [] - iter = l.begin_shapes(cell_index, layer) - while !iter.at_end - s.push(iter.shape.box.transformed(iter.trans).to_s) - iter.next - end - return s.join("; ") - end - # clip tests l = RBA::Layout.new l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) c0 = l.cell(l.add_cell("c0")) c1 = l.cell(l.add_cell("c1")) c2 = l.cell(l.add_cell("c2")) @@ -890,14 +872,25 @@ class DBLayoutTests1_TestClass < TestBase c2.shapes(0).insert(b) c3.shapes(0).insert(b) + bh = (RBA::Region.new(RBA::Box.new(0, 100, 1000, 1200)) - RBA::Region.new(RBA::Box.new(100, 200, 900, 1100)))[0] + c0.shapes(1).insert(bh) + c1.shapes(1).insert(bh) + c2.shapes(1).insert(bh) + c3.shapes(1).insert(bh) + tt = RBA::Trans.new c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) + ci = l.clip(c0.cell_index, RBA::Box.new(0, 0, 1000, 1200)) + assert_equal(dump_layer_i(l, 0, ci), "(0,100;1000,1200); (0,100;1000,1200); (100,0;1000,1100)") + assert_equal(dump_layer_i(l, 1, ci), "(0,100;0,1200;1000,1200;1000,100/100,200;900,200;900,1100;100,1100); (0,100;0,1200;1000,1200;1000,100/100,200;900,200;900,1100;100,1100); (100,0;100,1100;1000,1100;1000,1000;200,1000;200,100;1000,100;1000,0)") + ci = l.clip(c0.cell_index, RBA::Box.new(0, 0, 200, 200)) assert_equal(dump_layer_i(l, 0, ci), "(0,100;200,200); (0,100;200,200); (100,0;200,200)") + assert_equal(dump_layer_i(l, 1, ci), "(0,100;200,200); (0,100;200,200); (100,0;200,200)") cic = l.clip(c0, RBA::Box.new(0, 0, 200, 200)) assert_equal(dump_layer_i(l, 0, cic.cell_index), "(0,100;200,200); (0,100;200,200); (100,0;200,200)") @@ -911,6 +904,8 @@ class DBLayoutTests1_TestClass < TestBase ci = l.multi_clip(c0.cell_index, [RBA::Box.new(0, 0, 200, 200),RBA::Box.new(1000, 0, 1300, 200)]) assert_equal(dump_layer_i(l, 0, ci[0]), "(0,100;200,200); (0,100;200,200); (100,0;200,200)") assert_equal(dump_layer_i(l, 0, ci[1]), "(1000,0;1100,200); (1200,0;1300,200)") + assert_equal(dump_layer_i(l, 1, ci[0]), "(0,100;200,200); (0,100;200,200); (100,0;200,200)") + assert_equal(dump_layer_i(l, 1, ci[1]), "(1000,0;1100,200); (1200,0;1300,200)") cic = l.multi_clip(c0, [RBA::Box.new(0, 0, 200, 200),RBA::Box.new(1000, 0, 1300, 200)]) assert_equal(dump_layer_i(l, 0, cic[0].cell_index), "(0,100;200,200); (0,100;200,200); (100,0;200,200)") @@ -1971,6 +1966,66 @@ class DBLayoutTests1_TestClass < TestBase end + def test_23 + + # layer operations with shape types + + m = RBA::Manager::new + + l = RBA::Layout.new(m) + l1 = l.insert_layer(RBA::LayerInfo.new(1, 0)) + l2 = l.insert_layer(RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + + c0.shapes(l1).insert(RBA::Box::new(1, 2, 3, 4)) + c1.shapes(l1).insert(RBA::Polygon::new(RBA::Box::new(1, 2, 3, 4))) + + str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)") + + m.transaction("T") + l.clear_layer(l1, RBA::Shapes::SPolygons) + m.commit + + str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str1, "c0:box (1,2;3,4)\nc1:") + + m.undo + + str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)") + + m.transaction("T") + l.move_layer(l1, l2, RBA::Shapes::SPolygons) + m.commit + + str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str1, "c0:box (1,2;3,4)\nc1:") + + str2 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l2).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str2, "c0:\nc1:polygon (1,2;1,4;3,4;3,2)") + + m.undo + + str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)") + + str2 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l2).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str2, "c0:\nc1:") + + m.transaction("T") + l.copy_layer(l1, l2, RBA::Shapes::SPolygons) + m.commit + + str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)") + + str2 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l2).each.collect { |sh| sh.to_s }.join(";") }.join("\n") + assert_equal(str2, "c0:\nc1:polygon (1,2;1,4;3,4;3,2)") + + end + # Iterating while flatten def test_issue200 diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index e204b2e3c..cde153ac3 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -1044,50 +1044,115 @@ class DBLayoutTests2_TestClass < TestBase end # Meta information - def test_12 + def test_12a mi = RBA::LayoutMetaInfo::new("myinfo", "a") assert_equal(mi.name, "myinfo") assert_equal(mi.description, "") assert_equal(mi.value, "a") + assert_equal(mi.is_persisted?, false) mi.name = "x" mi.description = "y" mi.value = "z" + mi.persisted = true assert_equal(mi.name, "x") assert_equal(mi.description, "y") assert_equal(mi.value, "z") + assert_equal(mi.is_persisted?, true) ly = RBA::Layout::new ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "a")) - ly.add_meta_info(RBA::LayoutMetaInfo::new("another", "42", "description")) + ly.add_meta_info(RBA::LayoutMetaInfo::new("another", 42, "description", true)) assert_equal(ly.meta_info_value("myinfo"), "a") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) a = [] ly.each_meta_info { |mi| a << mi.name } assert_equal(a.join(","), "myinfo,another") a = [] - ly.each_meta_info { |mi| a << mi.value } + ly.each_meta_info { |mi| a << mi.value.to_s } assert_equal(a.join(","), "a,42") a = [] ly.each_meta_info { |mi| a << mi.description } assert_equal(a.join(","), ",description") + a = [] + ly.each_meta_info { |mi| a << mi.is_persisted?.to_s } + assert_equal(a.join(","), "false,true") ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "b")) assert_equal(ly.meta_info_value("myinfo"), "b") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) + + ly.remove_meta_info("doesnotexist") # should not fail ly.remove_meta_info("myinfo") - assert_equal(ly.meta_info_value("myinfo"), "") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("myinfo"), nil) + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) + + assert_equal(ly.meta_info("doesnotexist"), nil) + assert_equal(ly.meta_info("another").value, 42) + assert_equal(ly.meta_info("another").description, "description") + + ly.clear_meta_info + assert_equal(ly.meta_info_value("another"), nil) + assert_equal(ly.meta_info("another"), nil) + + # cellwise + + c1 = ly.create_cell("X") + c2 = ly.create_cell("U") + + c1.add_meta_info(RBA::LayoutMetaInfo::new("a", true)) + c1.add_meta_info(RBA::LayoutMetaInfo::new("b", [ 1, 17, 42 ], "description", true)) + + assert_equal(c2.meta_info("a"), nil) + assert_equal(c2.meta_info_value("a"), nil) + + a = [] + c2.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "") + + assert_equal(c1.meta_info("a").value, true) + assert_equal(c1.meta_info("b").value, [ 1, 17, 42 ]) + assert_equal(c1.meta_info_value("b"), [ 1, 17, 42 ]) + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true,[1, 17, 42]") + + a = [] + c1.each_meta_info { |mi| a << mi.description } + assert_equal(a.join(","), ",description") + + a = [] + c1.each_meta_info { |mi| a << mi.is_persisted?.to_s } + assert_equal(a.join(","), "false,true") + + c1.remove_meta_info("doesnotexist") # should not fail + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true,[1, 17, 42]") + + c1.remove_meta_info("b") + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true") + + c1.clear_meta_info + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "") end diff --git a/testdata/ruby/dbNetlistReaderTests.rb b/testdata/ruby/dbNetlistReaderTests.rb index 3632c72f7..5080d9373 100644 --- a/testdata/ruby/dbNetlistReaderTests.rb +++ b/testdata/ruby/dbNetlistReaderTests.rb @@ -167,6 +167,7 @@ END nl = RBA::Netlist::new input = File.join($ut_testsrc, "testdata", "algo", "nreader6.cir") + input = File.absolute_path(input) mydelegate = MyNetlistSpiceReaderDelegate::new @@ -185,6 +186,14 @@ END rescue Exception => ex msg = ex.message end + + # normalize paths for Windows + msg = msg.gsub("\\", "/") + input = input.gsub("\\", "/") + + puts "INFO: msg is '#{msg}'" + puts "INFO: input is '#{input}'" + assert_equal(msg.sub(input, ""), "Nothing implemented in , line 22 in Netlist::read") assert_equal(nl.description, "Read by MyDelegate") diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 97cdafc76..5d6911b73 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -128,6 +128,8 @@ class DBPolygon_TestClass < TestBase b.assign_hole(1, [ RBA::DPoint::new( 15, 25 ), RBA::DPoint::new( 25, 25 ), RBA::DPoint::new( 25, 65 ) ]) assert_equal( b.to_s, "(0,1;1,5;1,1/10,20;20,20;20,60)" ) b.insert_hole( [ RBA::DPoint::new( 1, 2 ), RBA::DPoint::new( 2, 2 ), RBA::DPoint::new( 2, 6 ) ] ) + assert_equal( b.to_s, "(0,1;1,5;1,1/10,20;20,20;20,60/1,2;2,2;2,6)" ) + b.sort_holes assert_equal( b.to_s, "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" ) b.assign_hole(0, [ RBA::DPoint::new( 15, 25 ), RBA::DPoint::new( 25, 25 ), RBA::DPoint::new( 25, 65 ) ]) assert_equal( b.to_s, "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" ) @@ -251,6 +253,8 @@ class DBPolygon_TestClass < TestBase b.assign_hole(1, [ RBA::Point::new( 15, 25 ), RBA::Point::new( 25, 25 ), RBA::Point::new( 25, 65 ) ]) assert_equal( b.to_s, "(0,1;1,5;1,1/10,20;20,20;20,60)" ) b.insert_hole( [ RBA::Point::new( 1, 2 ), RBA::Point::new( 2, 2 ), RBA::Point::new( 2, 6 ) ] ) + assert_equal( b.to_s, "(0,1;1,5;1,1/10,20;20,20;20,60/1,2;2,2;2,6)" ) + b.sort_holes assert_equal( b.to_s, "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" ) b.assign_hole(0, [ RBA::Point::new( 15, 25 ), RBA::Point::new( 25, 25 ), RBA::Point::new( 25, 65 ) ]) assert_equal( b.to_s, "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" ) diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 76bb1df85..a55ffa4f8 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -1595,6 +1595,34 @@ class DBShapes_TestClass < TestBase end + # Shapes with shape-type specific insert and clear + def test_11 + + s = RBA::Shapes::new + s.insert(RBA::Box::new(1, 2, 3, 4)) + s.insert(RBA::Polygon::new(RBA::Box::new(1, 2, 3, 4))) + + assert_equal(s.each.collect { |sh| sh.to_s }.join("; "), "polygon (1,2;1,4;3,4;3,2); box (1,2;3,4)") + + s2 = RBA::Shapes::new + s2.insert(s) + + assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "polygon (1,2;1,4;3,4;3,2); box (1,2;3,4)") + + s2.clear(RBA::Shapes::SPolygons) + + assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "box (1,2;3,4)") + + s2.clear + + assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "") + + s2.insert(s, RBA::Shapes::SPolygons) + + assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "polygon (1,2;1,4;3,4;3,2)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/extNetTracer.rb b/testdata/ruby/extNetTracer.rb index f95a106b9..82ed2c4f9 100644 --- a/testdata/ruby/extNetTracer.rb +++ b/testdata/ruby/extNetTracer.rb @@ -158,6 +158,39 @@ class EXT_TestClass < TestBase end + # Technology component + def test_4 + + c1 = RBA::NetTracerConnectivity::new + c1.connection("1/0", "3/0") + c1.name = "1to3" + + c2 = RBA::NetTracerConnectivity::new + c2.connection("2/0", "3/0") + c2.name = "2to3" + + tc = RBA::NetTracerTechnologyComponent::new + names = tc.each.collect { |c| c.name }.join(";") + assert_equal(names, "") + + tc.add(c1) + names = tc.each.collect { |c| c.name }.join(";") + assert_equal(names, "1to3") + + tc.add(c2) + names = tc.each.collect { |c| c.name }.join(";") + assert_equal(names, "1to3;2to3") + + tc.clear + names = tc.each.collect { |c| c.name }.join(";") + assert_equal(names, "") + + tc.add(c2) + names = tc.each.collect { |c| c.name }.join(";") + assert_equal(names, "2to3") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/layLayoutView.rb b/testdata/ruby/layLayoutView.rb index 11f194205..9b1a75806 100644 --- a/testdata/ruby/layLayoutView.rb +++ b/testdata/ruby/layLayoutView.rb @@ -187,7 +187,7 @@ class LAYLayoutView_TestClass < TestBase view.set_config("search-range", "0") view.select_from(RBA::DBox::new(-2.5, -2.5, 2.5, 2.5)) assert_equal(selection_changed, 1) - assert_equal(view.selection_size, 4) + assert_equal(view.selection_size, 2) assert_equal(view.has_selection?, true) view.select_from(RBA::DPoint::new(0, 0), RBA::LayoutView::Invert) diff --git a/testdata/ruby/qtbinding.rb b/testdata/ruby/qtbinding.rb index 36004d706..16fda8932 100644 --- a/testdata/ruby/qtbinding.rb +++ b/testdata/ruby/qtbinding.rb @@ -851,6 +851,33 @@ class QtBinding_TestClass < TestBase assert_equal(h[RBA::Qt::MouseButton::RightButton], "right") assert_equal(h[RBA::Qt::MouseButton::NoButton], nil) + def test_60 + + # findChild, findChildren + + w = RBA::QWidget::new + w.objectName = "w" + w1 = RBA::QWidget::new(w) + w1.objectName = "w1" + w2 = RBA::QWidget::new(w1) + w2.objectName = "w2" + + assert_equal(w.findChild.objectName, "w1") + assert_equal(w.findChild("w2").objectName, "w2") + + assert_equal(w.findChildren().collect { |c| c.objectName }.join(","), "w1,w2") + assert_equal(w.findChildren("w2").collect { |c| c.objectName }.join(","), "w2") + + begin + # Qt5++ + re_cls = RBA::QRegularExpression + rescue => ex + # Qt4 + re_cls = RBA::QRegExp + end + assert_equal(w.findChildren(re_cls::new("^.2$")).collect { |c| c.objectName }.join(","), "w2") + assert_equal(w.findChildren(re_cls::new("^w.$")).collect { |c| c.objectName }.join(","), "w1,w2") + end end diff --git a/version.sh b/version.sh index 52820156f..37539b8c7 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.28.6" +KLAYOUT_VERSION="0.28.10" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.28.6" +KLAYOUT_PYPI_VERSION="0.28.10" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")