Merge branch 'master' into wip

This commit is contained in:
Matthias Koefferlein 2023-07-15 22:48:53 +02:00
commit e965f87f58
238 changed files with 9840 additions and 7111 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -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"

View File

@ -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 }}

35
.github/workflows/cache-maintenance.yml vendored Normal file
View File

@ -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 }}

View File

@ -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

View File

@ -1,3 +1,31 @@
klayout (0.28.10-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> 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 <matthias@koefferlein.de> 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 <matthias@koefferlein.de> 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 <matthias@koefferlein.de> Sat, 22 Apr 2023 15:18:27 +0200
klayout (0.28.6-1) unstable; urgency=low
* New features and bugfixes

View File

@ -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

View File

@ -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

View File

@ -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'
}

View File

@ -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}"

View File

@ -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

View File

@ -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.
# <repo-root>/build-debug. It needs to have a "pymod" installation in
# <repo-root>/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."

View File

@ -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<QObject *> (name); }
#if QT_VERSION < 0x50000
#include <QRegExp>
QObject *find_child_impl (QObject *object, const QString &name)
{
return object->findChild<QObject *> (name);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name)
{
return object->findChildren<QObject *> (name);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegExp &re)
{
return object->findChildren<QObject *> (re);
}
#else
#include <QRegularExpression>
QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChild<QObject *> (name, options);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (name, options);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (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

17
scripts/regenerate_stubs.sh Executable file
View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -185,6 +185,7 @@ private:
std::vector<std::string> 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<std::string> m_lefdef_lef_layout_files;

View File

@ -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;
}

View File

@ -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 ());

View File

@ -124,6 +124,7 @@ SOURCES = \
gsiDeclDbLibrary.cc \
gsiDeclDbManager.cc \
gsiDeclDbMatrix.cc \
gsiDeclDbMetaInfo.cc \
gsiDeclDbPath.cc \
gsiDeclDbPoint.cc \
gsiDeclDbPolygon.cc \

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -151,10 +151,6 @@ public:
}
std::set<db::cell_index_type> 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<db::cell_index_type, db::ICplxTrans>::const_iterator r = m_repr.find (cell_b);
@ -164,7 +160,11 @@ public:
return false;
}
}
std::set<db::cell_index_type> 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);

View File

@ -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

View File

@ -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 <db::SimplePolygon> clipped_polygons;
if (! poly.box ().inside (clip_box)) {
std::vector <db::SimplePolygon> clipped_polygons;
clip_poly (poly, clip_box, clipped_polygons);
for (std::vector <db::SimplePolygon>::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 <db::Polygon> clipped_polygons;
clip_poly (poly, clip_box, clipped_polygons);
for (std::vector <db::Polygon>::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 <std::pair <db::cell_index_type, db::Box>, 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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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<DeepRegion> 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;
}

View File

@ -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);

View File

@ -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<DeepShapeStoreState> m_state_stack;
bool m_keep_layouts;
bool m_wants_all_cells;
tl::Mutex m_lock;
struct DeliveryMappingCacheKey

View File

@ -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> &cutpoints)
add_hparallel_cutpoints (WorkEdge &e1, WorkEdge &e2, const db::Box &cell, std::vector <CutPoints> &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> &cutpoints, std::vector <
std::vector <WorkEdge>::iterator f = current;
for (std::vector <WorkEdge>::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 <db::Coord>::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 <db::Coord>::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> &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 <bool, db::Point> 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> &cutpoints, std::vector <
c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) {
std::pair <bool, db::Point> 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> &cutpoints, std::vector <WorkEdge>::iterator current, std::vector <WorkEdge>::iterator future, db::Coord y, db::Coord yy, bool with_h)
{
std::vector <WorkEdge *> p1_weak; // holds weak interactions of edge endpoints with other edges
std::vector <WorkEdge *> ip_weak;
double dy = y - 0.5;
double dyy = yy + 0.5;
std::vector <std::pair<const WorkEdge *, WorkEdge *> > p1_weak; // holds weak interactions of edge endpoints with other edges
std::sort (current, future, edge_xmin_at_yinterval_double_compare<db::Coord> (dy, dyy));
@ -1356,14 +1371,28 @@ get_intersections_per_band_any (std::vector <CutPoints> &cutpoints, std::vector
std::vector <WorkEdge>::iterator f = current;
for (std::vector <WorkEdge>::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 <db::Coord>::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 <db::Coord>::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> &cutpoints, std::vector
db::Box cell (x, y, xx, yy);
for (std::vector <WorkEdge>::iterator c1 = c; c1 != f; ++c1) {
std::set<db::Point> weak_points; // holds points that need to go in all other edges
p1_weak.clear ();
p1_weak.clear ();
for (std::vector <WorkEdge>::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> &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 <bool, db::Point> 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 <WorkEdge>::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 <WorkEdge *>::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> &cutpoints, std::vector
c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) {
std::pair <bool, db::Point> 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 <WorkEdge>::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 <WorkEdge *>::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> &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> &cutpoints, std::vector
}
if (! p1_weak.empty ()) {
}
bool strong = false;
for (std::vector<WorkEdge *>::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<WorkEdge *>::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 <WorkEdge>::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::vector<std::pair<db::EdgeSink *, db::
progress->set (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<std::pair<db::EdgeSink *, db::
yy = std::numeric_limits <db::Coord>::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;

View File

@ -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) {

View File

@ -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<db::Polygon, db::unstable_layer_tag> ().begin (), other_flat->raw_polygons ().get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer<db::PolygonWithProperties, db::unstable_layer_tag> ().begin (), other_flat->raw_polygons ().get_layer<db::PolygonWithProperties, db::unstable_layer_tag> ().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<db::Polygon, db::unstable_layer_tag> ().begin (), other_flat->raw_polygons ().get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
polygons.insert (other_flat->raw_polygons ().get_layer<db::PolygonWithProperties, db::unstable_layer_tag> ().begin (), other_flat->raw_polygons ().get_layer<db::PolygonWithProperties, db::unstable_layer_tag> ().end ());
} else {

View File

@ -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<db::DPoint> &linestarts)
void
hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector<db::DPoint> &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<db::DPoint> &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 ();
}
}

View File

@ -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<db::DPoint> &linestarts);
DB_PUBLIC void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector<db::DPoint> &linestarts, double &left, double &bottom);
DB_PUBLIC std::vector<std::string> 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<db::DPoint> 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<C> bbox () const
db::DBox bbox () const
{
db::DBox b = hershey_text_box (m_string, m_font);
db::point<C> p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale));
db::point<C> p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale));
return box<C> (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 <db::DPoint> m_linestarts;
double m_left, m_bottom;
};
/**

View File

@ -64,6 +64,11 @@ cronology::events::event_collection<event_compute_results, event_compute_local_c
namespace db
{
// Heuristic parameter to control the recursion of the cell-to-cell intruder
// detection: do not recurse if the intruder cell's bounding box is smaller
// than the overlap box times this factor.
const double area_ratio_for_recursion = 3.0;
// ---------------------------------------------------------------------------------------------
// Shape reference translator
@ -370,6 +375,32 @@ db::Box safe_box_enlarged (const db::Box &box, db::Coord dx, db::Coord dy)
}
}
// ---------------------------------------------------------------------------------------------
// Debugging utility: dump the cell contexts
template <class TS, class TI, class TR>
static void dump_cell_contexts (local_processor_contexts<TS, TI, TR> &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 <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &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

View File

@ -152,13 +152,13 @@ static std::pair<bool, std::set<db::Box> > 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);
}

View File

@ -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 &region, 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<std::pair<bool, std::vector<db::Cell *> > > m_cell_stack;
db::Cell *mp_initial_cell;

View File

@ -258,12 +258,12 @@ private:
// -----------------------------------------------------------------
// Implementation of the ProxyContextInfo class
ProxyContextInfo
ProxyContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
LayoutOrCellContextInfo
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
{
ProxyContextInfo info;
LayoutOrCellContextInfo info;
for (std::vector<std::string>::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<std::string>::const_iterator from, st
info.cell_name = ex.skip ();
} else if (ex.test ("META(")) {
std::pair<std::string, std::pair<tl::Variant, std::string> > 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<std::string>::const_iterator from, st
}
void
ProxyContextInfo::serialize (std::vector<std::string> &strings)
LayoutOrCellContextInfo::serialize (std::vector<std::string> &strings)
{
if (! lib_name.empty ()) {
strings.push_back ("LIB=" + lib_name);
}
for (std::map<std::string, tl::Variant> ::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<std::string> &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<meta_info_name_id_type>::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::vector<tl
}
bool
Layout::has_context_info () const
{
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
if (i->second.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 <std::string> &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<tl::Variant, std::string> &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 <std::string>::const_iterator from, std::vector <std::string>::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 <std::string> &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 <std::string>
}
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<tl::Variant, std::string> &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 <const db::ColdProxy *> (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 <std::string>::const_iterator from, std::vector <std::string>::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 <std::string>:
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 <std::string>::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);

View File

@ -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<std::string, tl::Variant> pcell_parameters;
std::map<std::string, std::pair<tl::Variant, std::string> > meta_info;
static ProxyContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
void serialize (std::vector<std::string> &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<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
typedef LayerIterator layer_iterator;
typedef std::vector<MetaInfo> meta_info;
typedef meta_info::const_iterator meta_info_iterator;
typedef size_t meta_info_name_id_type;
typedef std::map<meta_info_name_id_type, MetaInfo> 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 <std::string> &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 <std::string>::const_iterator from, std::vector <std::string>::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 <std::string> &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 <std::string>::const_iterator from, std::vector <std::string>::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 <class I>
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 <class I>
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<std::string, meta_info_name_id_type> m_meta_info_name_map;
std::vector<std::string> m_meta_info_names;
meta_info_map m_meta_info;
std::map<db::cell_index_type, meta_info_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<db::Layout *> (other.mp_layout.get ()), other.m_no_update);
return *this;
}
private:
db::Layout *mp_layout;
tl::weak_ptr<db::Layout> 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 ();
}
}

View File

@ -69,8 +69,10 @@ collect_cells (const db::Layout &l, const db::Cell *top, std::map <std::string,
}
static void
collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell, unsigned int /*flags*/, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts)
collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell, unsigned int /*flags*/, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts, bool no_duplicates)
{
size_t n_before = insts.size ();
for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) {
std::map <db::cell_index_type, db::cell_index_type>::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 <db::CellInstArrayWithProperties> &insts, unsi
}
static void
collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flags, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts, PropertyMapper &pn)
collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flags, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &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 <class X, class Op>
void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate)
void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate, bool no_duplicates)
{
do {
@ -196,12 +209,29 @@ void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate)
while (ra != a.end () && rb != b.end ()) {
if (op (*ra, *rb)) {
*wa++ = *ra++;
typename std::vector<X>::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<X>::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<X>::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<X> &a, std::vector<X> &b, Op op, bool iterate)
if (ra != wa) {
while (ra != a.end ()) {
*wa++ = *ra++;
typename std::vector<X>::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<X>::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 <db::CellInstArrayWithProperties> 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 <db::CellInstArrayWithProperties> 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;

View File

@ -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;
}
/**

View File

@ -218,7 +218,7 @@ merge_layouts (db::Layout &target,
std::map<db::cell_index_type, db::cell_index_type> new_cell_mapping;
for (std::set<db::cell_index_type>::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<db::cell_index_type>::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<db::cell_index_type>::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) {

View File

@ -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);

View File

@ -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);
}

View File

@ -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<std::string> m_paths;
std::map<std::string, int> m_file_id_per_path;
std::list<SpiceReaderStream> m_streams;
std::list<std::string> m_in_lib;
SpiceReaderStream m_stream;
int m_file_id;
std::map<std::string, SpiceCachedCircuit *> m_cached_circuits;
@ -393,7 +404,7 @@ private:
std::set<std::string> m_global_net_names;
std::vector<std::string> 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<db::Net *> &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;
}

View File

@ -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<std::string, tl::Variant> params = pv;
std::vector<size_t> terminal_order;
size_t defp = std::numeric_limits<size_t>::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<std::s
return parser.read (ex);
}
tl::Variant
NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables1, const std::map<std::string, tl::Variant> &variables2)
{
NetlistSpiceReaderExpressionParser parser (&variables1, &variables2);
return parser.read (ex);
}
bool
NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &v, const std::map<std::string, tl::Variant> &variables)
{

View File

@ -154,6 +154,11 @@ public:
*/
static tl::Variant read_value(tl::Extractor &ex, const std::map<std::string, tl::Variant> &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<std::string, tl::Variant> &variables1, const std::map<std::string, tl::Variant> &variables2);
/**
* @brief Tries to read a value from the extractor (with formula evaluation)
*/

View File

@ -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);
}

View File

@ -46,6 +46,7 @@ public:
typedef std::map<std::string, tl::Variant> 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;

View File

@ -1044,6 +1044,19 @@ private:
}
};
} // namespace db
namespace std
{
// injecting a global std::swap for polygons into the
// std namespace
template <class C>
void swap (db::polygon_contour<C> &a, db::polygon_contour<C> &b)
{
a.swap (b);
}
}
namespace db
@ -2047,7 +2060,7 @@ public:
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*/
template <class I>
void insert_hole (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false)
void insert_hole (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false)
{
insert_hole (start, end, db::unit_trans<C> (), 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 <class C>
void swap (db::polygon<C> &a, db::polygon<C> &b)

View File

@ -865,6 +865,8 @@ PolygonGenerator::produce_poly (const PGPolyContour &c)
}
m_poly.sort_holes ();
}
mp_psink->put (m_poly);

View File

@ -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<C> &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<db::point<C> > *) 0, false)) {
@ -1542,6 +1548,8 @@ do_compute_rounded (const db::polygon<C> &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<db::Coord>::area_type atot = 0;
db::coord_traits<db::Coord>::distance_type min_edge = std::numeric_limits<db::coord_traits<db::Coord>::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<db::Point> 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<db::Coord>::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;
}

View File

@ -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;
}
}

View File

@ -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 ();

View File

@ -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
*/

View File

@ -81,6 +81,12 @@ inline bool needs_translate (object_tag<Sh> /*tag*/)
return tl::is_equal_type<typename shape_traits<Sh>::can_deref, tl::True> () || tl::is_equal_type<typename shape_traits<Sh>::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<LayerBase *>::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<LayerBase *>::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<LayerBase *>::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<LayerBase *>::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<LayerBase *> new_layers;
for (tl::vector<LayerBase *>::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);

View File

@ -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

View File

@ -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<db::Cell> 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 "

View File

@ -111,6 +111,21 @@ Class<db::DeepShapeStore> 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"

View File

@ -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<db::MetaInfo> 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<db::Layout> 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<void (db::Layout::*) ()> (&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<void (db::Layout::*) (const std::string &name)> (&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<db::Layout> 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<void (db::Layout::*) (unsigned int, unsigned int)> (&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<void (db::Layout::*) (unsigned int, unsigned int, unsigned int)> (&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<void (db::Layout::*) (unsigned int, unsigned int)> (&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<void (db::Layout::*) (unsigned int, unsigned int, unsigned int)> (&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<void (db::Layout::*) (unsigned int)> (&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<db::Layout> 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<void (db::Layout::*) (unsigned int, unsigned int)> (&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"

View File

@ -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<LayoutDiff> 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 "

View File

@ -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<MetaInfo> 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."
);
}

View File

@ -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 <string>
#include <iterator>
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<db::Layout *> (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<db::Layout> mp_layout;
db::Layout::meta_info_iterator m_b, m_e;
};
}
#endif

View File

@ -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"

View File

@ -108,47 +108,47 @@ static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin (const db
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_all (const db::Shapes *s)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_all (const db::Shapes *s)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_overlapping_all (const db::Shapes *s, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping_all (const db::Shapes *s, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (region, db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_doverlapping_all (const db::Shapes *s, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_doverlapping_all (const db::Shapes *s, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_touching (const db::Shapes *s, unsigned int flags, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching (const db::Shapes *s, unsigned int flags, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_touching_all (const db::Shapes *s, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching_all (const db::Shapes *s, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (region, db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_dtouching_all (const db::Shapes *s, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_dtouching_all (const db::Shapes *s, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (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<db::Shapes> 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<void (db::Shapes::*) ()> (&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<void (db::Shapes::*) (unsigned int)> (&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<db::Shapes> 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<db::Shapes> 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<bool> ("detailed", false),
"@hide"

View File

@ -938,6 +938,7 @@ TEST(two_1)
db::box_convert<db::Box> bc1;
db::box_convert<db::SimplePolygon> 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<db::Box> bc1;
db::box_convert<db::SimplePolygon> 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<db::Box> bc1;
db::box_convert<db::SimplePolygon> 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<db::Box> bc1;
db::box_convert<db::SimplePolygon> 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<db::Box> 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<db::SimplePolygon> 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<db::SimplePolygon> 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*/);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -66,6 +66,9 @@ public:
void begin_edge_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b);
void end_edge_differences ();
void begin_edge_pair_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &b);
void end_edge_pair_differences ();
void begin_text_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &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 <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &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, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
// c5->c1
c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
// c3->c5 (3x)
c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
// c4->c3
c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
// c4->c1
c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
// c2->c1 (2x)
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
// c2->c4 (2x)
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
c2.insert (db::array <db::CellInst, db::Trans> (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, db::Trans> (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
h.cell(c4i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t));
h.cell(c4i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (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, db::Trans> (db::CellInst (c6i), t));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (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"
);
}

View File

@ -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");
}

View File

@ -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<std::string, tl::Variant> vars;

View File

@ -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));

View File

@ -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 <vector>
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<db::cell_index_type> 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<db::cell_index_type> 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"
);
}

View File

@ -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;

View File

@ -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 ()) {

View File

@ -1182,6 +1182,36 @@ See <a href="/about/drc_ref_netter.xml#netlist">Netter#netlist</a> for a descrip
<p>
See <a href="/about/drc_ref_netter.xml">Netter</a> for more details
</p>
<a name="new_report"/><h2>"new_report" - Creates a new report database object for use in "output"</h2>
<keyword name="new_report"/>
<p>Usage:</p>
<ul>
<li><tt>new_report(description [, filename [, cellname ] ])</tt></li>
</ul>
<p>
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.
</p><p>
Arguments are the same than for <a href="/about/drc_ref_global.xml#report">report</a>.
</p><p>
See <a href="/about/drc_ref_layer.xml#output">Layer#output</a> for details about this feature.
</p>
<a name="new_target"/><h2>"new_target" - Creates a new layout target object for use in "output"</h2>
<keyword name="new_target"/>
<p>Usage:</p>
<ul>
<li><tt>new_target(what [, cellname])</tt></li>
</ul>
<p>
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.
</p><p>
Arguments are the same than for <a href="/about/drc_ref_global.xml#target">target</a>.
</p><p>
See <a href="/about/drc_ref_layer.xml#output">Layer#output</a> for details about this feature.
</p>
<a name="no_borders"/><h2>"no_borders" - Reset the tile borders</h2>
<keyword name="no_borders"/>
<p>Usage:</p>
@ -1413,6 +1443,21 @@ See <a href="/about/drc_ref_source.xml#polygons">Source#polygons</a> for a descr
The primary input of the universal DRC function is the layer the <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> function
is called on.
</p>
<a name="profile"/><h2>"profile" - Profiles the script and provides a runtime + memory statistics</h2>
<keyword name="profile"/>
<p>Usage:</p>
<ul>
<li><tt>profile</tt></li>
<li><tt>profile(n)</tt></li>
</ul>
<p>
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.
</p>
<a name="props_copy"/><h2>"props_copy" - Specifies "copy properties" on operations supporting user properties constraints</h2>
<keyword name="props_copy"/>
<p>

View File

@ -2217,6 +2217,26 @@ a single <class_doc href="LayerInfo">LayerInfo</class_doc> object.
</p><p>
See <a href="/about/drc_ref_global.xml#report">report</a> and <a href="/about/drc_ref_global.xml#target">target</a> on how to configure output to a target layout
or report database.
</p><p>
See also <a href="/about/drc_ref_global.xml#new_target">new_target</a> and <a href="/about/drc_ref_global.xml#new_report">new_report</a> 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.
</p><p>
Example:
</p><p>
<pre>
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")
</pre>
</p>
<a name="outside"/><h2>"outside" - Selects edges or polygons of self which are outside edges or polygons from the other layer</h2>
<keyword name="outside"/>

View File

@ -185,7 +185,7 @@ l.output("OUT")
l.output(17, 0, "OUT")</pre>
<p>
Output can be sent to other layouts using the "target" function:
Output can be sent to other layouts using the <a href="/about/drc_ref_global.xml#target">target</a> function:
</p>
<pre>
@ -196,7 +196,7 @@ target("@2")
target("out.gds", "OUT_TOP")</pre>
<p>
Output can also be sent to a report database:
Output can also be sent to a report database using the <a href="/about/drc_ref_global.xml#report">report</a> function:
</p>
<pre>
@ -234,6 +234,38 @@ l.output("check1", "The first check")</pre>
unpredictable.
</p>
<p>
It is possible to open "side" reports and targets and send layers to these
outputs without closing the default output.
</p>
<p>
To open a "side report", use <a href="/about/drc_ref_global.xml#new_report">new_report</a>
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:
</p>
<pre>
# 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")</pre>
<p>
In the same way, "side targets" can be opened using <a href="/about/drc_ref_global.xml#new_target">new_target</a>.
Such side targets open a way to write certain layers to other layout files.
This is very handy for debugging:
</p>
<pre>
# 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)</pre>
<h2>Dimension specifications</h2>
@ -710,12 +742,12 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
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)</pre>
# 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)</pre>
<p>
The following two images show the effect of raw and clean mode:
@ -792,20 +824,18 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
drc_w = input(1, 0).width(0.2)
...
</pre>
...
drc_w = input(1, 0).width(0.2)
...</pre>
<p>
can be written as:
</p>
<pre>
...
drc_w = input(1, 0).drc(width &lt; 0.2)
...
</pre>
...
drc_w = input(1, 0).drc(width &lt; 0.2)
...</pre>
<p>
The <a href="/about/drc_ref_layer.xml#drc">drc</a> method is the "universal DRC" method.
@ -845,12 +875,11 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
l1 = input(1, 0)
l2 = input(2, 0)
drc_sep = l1.drc(separation(l2) &lt;= 0.5)
...
</pre>
...
l1 = input(1, 0)
l2 = input(2, 0)
drc_sep = l1.drc(separation(l2) &lt;= 0.5)
...</pre>
<p>
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)</pre>
</p>
<pre>
...
drc_w = input(1, 0).drc(width(projection) &lt; 0.2)
...
</pre>
...
drc_w = input(1, 0).drc(width(projection) &lt; 0.2)
...</pre>
<p>
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)</pre>
</p>
<pre>
...
drc_ws = input(1, 0).drc((width &lt; 0.2) &amp; (space &lt; 0.3))
...
</pre>
...
drc_ws = input(1, 0).drc((width &lt; 0.2) &amp; (space &lt; 0.3))
...</pre>
<p>
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)</pre>
</p>
<pre>
...
drc_ws1 = input(1, 0).width(0.2).edges
drc_ws2 = input(1, 0).space(0.3).edges
drc_ws = drc_ws1 &amp; drc_ws2
...
</pre>
...
drc_ws1 = input(1, 0).width(0.2).edges
drc_ws2 = input(1, 0).space(0.3).edges
drc_ws = drc_ws1 &amp; drc_ws2
...</pre>
<p>
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)</pre>
</p>
<pre>
...
drc_w = input(1, 0).width(0.2)
log("Number of width violations: #{drc_w.data.size}")
...
</pre>
...
drc_w = input(1, 0).width(0.2)
log("Number of width violations: #{drc_w.data.size}")
...</pre>
<p>
The <a href="/about/drc_ref_global.xml#error">error</a> function can be used to output error messages
@ -946,11 +971,23 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
log_file("drc_log.txt")
verbose(true)
info("This message will be sent to the log file")
...
</pre>
log_file("drc_log.txt")
verbose(true)
info("This message will be sent to the log file")
...</pre>
<p>
The <a href="/about/drc_ref_global.xml#profile">profile</a> 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.
</p>
<pre>
# enables profiling
profile
...</pre>
<h2>The tiling option</h2>
@ -1003,8 +1040,7 @@ threads(4)
# Disable tiling
flat
... non-tiled operations ...
</pre>
... non-tiled operations ...</pre>
<p>
Some operations implicitly specify a tile border. If the tile border is known (see length example above), explicit borders

View File

@ -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
<p>
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).
</p>
<ul>
<li>Parametrized circuits are not permitted except for device subcircuits
(with a delegate)</li>
<li>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).</li>
</ul>
<p>
As for the SPICE reader, a delegate can be provided to customize the reader.
For doing so, subclass the <class_doc href="NetlistSpiceReaderDelegate"/>

View File

@ -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

View File

@ -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

View File

@ -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 ();

View File

@ -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<db::Layout *> 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::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::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<lay::ObjectInstPath> new_selection;
for (std::vector<edt::Service *>::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<lay::ObjectInstPath> 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<lay::ObjectInstPath>::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::Service *> edt_services = view ()->get_plugins <edt::Service> ();
@ -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<edt::Service::obj_iterator> to_delete;
std::vector<lay::ObjectInstPath> 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<edt::Service *>::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<edt::Service *>::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<edt::Service *>::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<edt::Service *>::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")));

View File

@ -243,9 +243,14 @@ insert_point_path (const db::Path &p, const std::set<EdgeWithIndex> &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 <bool, db::Point> projected = db::Edge (p1, p2).projected (ins);
db::Edge e (p1, p2);
std::pair <bool, db::Point> 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<EdgeWithIndex> &sel, db::Po
}
static void
remove_redundant_points (std::vector <db::Point> &ctr)
remove_redundant_points (std::vector <db::Point> &ctr, bool cyclic)
{
// compress contour (remove redundant points)
// and assign to path
std::vector<db::Point>::iterator wp = ctr.begin ();
std::vector<db::Point>::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<EdgeWithIndex> &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 <PointWithIndex, db::Point> &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<EdgeWithIndex> &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 <bool, db::Point> 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<EdgeWithIndex> &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<EdgeWithIndex> &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<int>::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<int>::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 <EdgeWithIndex> 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 ();

View File

@ -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<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ());
if (tv_list != 0) {
for (std::vector<db::DCplxTrans>::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 ());
}
}
}

View File

@ -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"

View File

@ -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<db::Shape>::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 ());

View File

@ -51,7 +51,40 @@ static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, g
ret.write<const QMetaObject &> (QObject::staticMetaObject);
}
QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild<QObject *> (name); }
#if QT_VERSION < 0x50000
#include <QRegExp>
QObject *find_child_impl (QObject *object, const QString &name)
{
return object->findChild<QObject *> (name);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name)
{
return object->findChildren<QObject *> (name);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegExp &re)
{
return object->findChildren<QObject *> (re);
}
#else
#include <QRegularExpression>
QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChild<QObject *> (name, options);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (name, options);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (re, options);
}
#endif
// bool QObject::blockSignals(bool b)
@ -757,7 +790,15 @@ static gsi::Methods methods_QObject () {
}
qt_gsi::QtNativeClass<QObject> 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");

View File

@ -52,7 +52,40 @@ static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, g
ret.write<const QMetaObject &> (QObject::staticMetaObject);
}
QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild<QObject *> (name); }
#if QT_VERSION < 0x50000
#include <QRegExp>
QObject *find_child_impl (QObject *object, const QString &name)
{
return object->findChild<QObject *> (name);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name)
{
return object->findChildren<QObject *> (name);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegExp &re)
{
return object->findChildren<QObject *> (re);
}
#else
#include <QRegularExpression>
QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChild<QObject *> (name, options);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (name, options);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (re, options);
}
#endif
// bool QObject::blockSignals(bool b)
@ -847,7 +880,15 @@ static gsi::Methods methods_QObject () {
}
qt_gsi::QtNativeClass<QObject> 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");

View File

@ -52,7 +52,40 @@ static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, g
ret.write<const QMetaObject &> (QObject::staticMetaObject);
}
QObject *find_child_impl (QObject *object, const QString &name) { return object->findChild<QObject *> (name); }
#if QT_VERSION < 0x50000
#include <QRegExp>
QObject *find_child_impl (QObject *object, const QString &name)
{
return object->findChild<QObject *> (name);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name)
{
return object->findChildren<QObject *> (name);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegExp &re)
{
return object->findChildren<QObject *> (re);
}
#else
#include <QRegularExpression>
QObject *find_child_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChild<QObject *> (name, options);
}
QList<QObject *> find_children_impl (QObject *object, const QString &name, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (name, options);
}
QList<QObject *> find_children_impl2 (QObject *object, const QRegularExpression &re, Qt::FindChildOptions options)
{
return object->findChildren<QObject *> (re, options);
}
#endif
// bool QObject::blockSignals(bool b)
@ -771,7 +804,15 @@ static gsi::Methods methods_QObject () {
}
qt_gsi::QtNativeClass<QObject> 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");

View File

@ -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;

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ClipDialog</class>
<widget class="QDialog" name="ClipDialog" >
<property name="geometry" >
<widget class="QDialog" name="ClipDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,49 +10,47 @@
<height>421</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Clip Tool</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>9</number>
</property>
<item>
<widget class="QFrame" name="frame_3" >
<property name="frameShape" >
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_13" >
<property name="text" >
<widget class="QLabel" name="label_13">
<property name="text">
<string>Clip result cell name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_cell_name" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<widget class="QLineEdit" name="le_cell_name">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<property name="text">
<string>CLIP</string>
</property>
</widget>
@ -61,13 +60,13 @@
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
@ -76,48 +75,48 @@
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="title" >
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Clip Box Specification</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="2" column="0" >
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<item row="2" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="rb_box2" >
<property name="text" >
<widget class="QRadioButton" name="rb_box2">
<property name="text">
<string>Single box with ...</string>
</property>
<property name="autoExclusive" >
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@ -128,40 +127,40 @@
</layout>
</widget>
</item>
<item row="1" column="0" >
<widget class="QFrame" name="frame_2" >
<property name="frameShape" >
<item row="1" column="0">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="rb_box1" >
<property name="text" >
<widget class="QRadioButton" name="rb_box1">
<property name="text">
<string>Single box with ...</string>
</property>
<property name="checked" >
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive" >
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@ -172,12 +171,12 @@
</layout>
</widget>
</item>
<item row="0" column="2" >
<item row="0" column="2">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>231</width>
<height>20</height>
@ -185,176 +184,180 @@
</property>
</spacer>
</item>
<item row="0" column="0" >
<widget class="QRadioButton" name="rb_shapes" >
<property name="text" >
<item row="0" column="0">
<widget class="QRadioButton" name="rb_shapes">
<property name="text">
<string>Shapes on layer (multi clip)</string>
</property>
<property name="autoExclusive" >
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2" >
<widget class="QGroupBox" name="grp_box1" >
<property name="title" >
<item row="1" column="1" colspan="2">
<widget class="QGroupBox" name="grp_box1">
<property name="title">
<string>Box Boundaries</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="3" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<item row="1" column="3">
<widget class="QLabel" name="label_6">
<property name="text">
<string>y =</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>2nd corner</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<item row="1" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>x =</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>1st corner</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>x =</string>
</property>
</widget>
</item>
<item row="0" column="3" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="0" column="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>y =</string>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLineEdit" name="le_y2" />
<item row="1" column="4">
<widget class="QLineEdit" name="le_y2"/>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="le_x2" />
<item row="1" column="2">
<widget class="QLineEdit" name="le_x2"/>
</item>
<item row="0" column="4" >
<widget class="QLineEdit" name="le_y1" />
<item row="0" column="4">
<widget class="QLineEdit" name="le_y1"/>
</item>
<item row="0" column="2" >
<widget class="QLineEdit" name="le_x1" />
<item row="0" column="2">
<widget class="QLineEdit" name="le_x1"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="1" colspan="2" >
<widget class="QGroupBox" name="grp_box2" >
<property name="title" >
<item row="2" column="1" colspan="2">
<widget class="QGroupBox" name="grp_box2">
<property name="title">
<string>Box Center And Dimensions</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="3" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<item row="1" column="3">
<widget class="QLabel" name="label_7">
<property name="text">
<string>h =</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Dimensions</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="label_9" >
<property name="text" >
<item row="1" column="1">
<widget class="QLabel" name="label_9">
<property name="text">
<string>w =</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_10" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Center</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLabel" name="label_11" >
<property name="text" >
<item row="0" column="1">
<widget class="QLabel" name="label_11">
<property name="text">
<string>x =</string>
</property>
</widget>
</item>
<item row="0" column="3" >
<widget class="QLabel" name="label_12" >
<property name="text" >
<item row="0" column="3">
<widget class="QLabel" name="label_12">
<property name="text">
<string>y =</string>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLineEdit" name="le_h" />
<item row="1" column="4">
<widget class="QLineEdit" name="le_h"/>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="le_w" />
<item row="1" column="2">
<widget class="QLineEdit" name="le_w"/>
</item>
<item row="0" column="4" >
<widget class="QLineEdit" name="le_y" />
<item row="0" column="4">
<widget class="QLineEdit" name="le_y"/>
</item>
<item row="0" column="2" >
<widget class="QLineEdit" name="le_x" />
<item row="0" column="2">
<widget class="QLineEdit" name="le_x"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="0" colspan="2" >
<widget class="QRadioButton" name="rb_rulers" >
<property name="text" >
<item row="3" column="0" colspan="2">
<widget class="QRadioButton" name="rb_rulers">
<property name="text">
<string>From ruler start and end points (multi clip)</string>
</property>
<property name="autoExclusive" >
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="lay::LayerSelectionComboBox" name="cb_layer" />
<item row="0" column="1">
<widget class="lay::LayerSelectionComboBox" name="cb_layer">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@ -363,12 +366,12 @@
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="button_box" >
<property name="orientation" >
<widget class="QDialogButtonBox" name="button_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@ -406,11 +409,11 @@
<receiver>ClipDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@ -422,11 +425,11 @@
<receiver>ClipDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>

View File

@ -49,7 +49,7 @@
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -70,8 +70,30 @@
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QFrame" name="frame_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
@ -131,10 +153,13 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>20</height>
</size>
</property>
</spacer>
@ -142,20 +167,7 @@
</layout>
</widget>
</item>
<item row="0" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="3">
<item row="2" column="0" colspan="3">
<widget class="QFrame" name="frame_6">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@ -203,19 +215,6 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_37">
<property name="text">
@ -251,6 +250,12 @@
</item>
<item row="0" column="2">
<widget class="QStackedWidget" name="fill_area_stack">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
</property>
@ -277,10 +282,13 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>241</width>
<height>40</height>
<height>20</height>
</size>
</property>
</spacer>
@ -303,6 +311,9 @@
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
@ -409,6 +420,22 @@
</widget>
</widget>
</item>
<item row="1" column="0" colspan="3">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>

View File

@ -94,10 +94,7 @@
<item>
<widget class="QToolButton" name="newFolderButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;New folder&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>New folder</string>
</property>
<property name="text">
<string>...</string>
@ -121,10 +118,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="addButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;New&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>New</string>
</property>
<property name="text">
<string>...</string>
@ -141,10 +135,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="deleteButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;Delete&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Delete</string>
</property>
<property name="text">
<string>...</string>
@ -161,10 +152,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="renameButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Rename&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Rename</string>
</property>
<property name="text">
<string>Rename</string>
@ -181,10 +169,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="importButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Import file&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Import file</string>
</property>
<property name="text">
<string>Import</string>
@ -208,7 +193,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="saveAllButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save all files (Ctrl+Shift+S)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Save all files (Ctrl+Shift+S)</string>
</property>
<property name="text">
<string>...</string>
@ -228,7 +213,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="saveButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save current file (Ctrl+S)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Save current file (Ctrl+S)</string>
</property>
<property name="text">
<string>...</string>
@ -333,8 +318,8 @@ p, li { white-space: pre-wrap; }
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/back_16.png</normaloff>:/back_16.png</iconset>
<iconset resource="../../icons/icons.qrc">
<normaloff>:/back_16px.png</normaloff>:/back_16px.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
@ -347,8 +332,8 @@ p, li { white-space: pre-wrap; }
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/forward_16.png</normaloff>:/forward_16.png</iconset>
<iconset resource="../../icons/icons.qrc">
<normaloff>:/forward_16px.png</normaloff>:/forward_16px.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
@ -365,10 +350,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="closeButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Close tab&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Close tab</string>
</property>
<property name="text">
<string>close</string>
@ -432,10 +414,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="stopButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Stop script&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Stop script</string>
</property>
<property name="text">
<string>...</string>
@ -452,10 +431,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="pauseButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Pause script (Ctrl+F5)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Pause script (Ctrl+F5)</string>
</property>
<property name="text">
<string>...</string>
@ -482,10 +458,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="breakpointButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Set breakpoint (F9)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Set breakpoint (F9)</string>
</property>
<property name="text">
<string>...</string>
@ -505,10 +478,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="clearBreakpointsButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Clear all breakpoints (Ctrl+Shift+F9)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Clear all breakpoints (Ctrl+Shift+F9)</string>
</property>
<property name="text">
<string>...</string>
@ -535,10 +505,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="singleStepButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Step into procedure (F11)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Step into procedure (F11)</string>
</property>
<property name="text">
<string>S</string>
@ -558,10 +525,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="nextStepButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Step over procedure or block (F10)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Step over procedure or block (F10)</string>
</property>
<property name="text">
<string>N</string>
@ -588,10 +552,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="dbgOn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Enable or disable debugging&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Enable or disable debugging</string>
</property>
<property name="text">
<string>DBG</string>
@ -618,10 +579,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="propertiesButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Edit properties of macro&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Edit properties of macro</string>
</property>
<property name="text">
<string>P</string>
@ -638,10 +596,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="setupButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Setup colors, formats, debugger&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Setup colors, formats, debugger</string>
</property>
<property name="text">
<string>prop</string>
@ -674,10 +629,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="findNextButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Find next&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Find next (Ctrl+F)</string>
</property>
<property name="text">
<string>N</string>
@ -687,7 +639,7 @@ p, li { white-space: pre-wrap; }
<normaloff>:/find_16px.png</normaloff>:/find_16px.png</iconset>
</property>
<property name="shortcut">
<string>F3</string>
<string>Ctrl+F</string>
</property>
<property name="autoRaise">
<bool>true</bool>
@ -709,10 +661,7 @@ p, li { white-space: pre-wrap; }
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Show replace mode&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Show replace mode</string>
</property>
<property name="text">
<string>...</string>
@ -764,10 +713,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="replaceNextButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Replace and find next&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Replace and find next (Ctrl+R)</string>
</property>
<property name="text">
<string>RN</string>
@ -777,7 +723,7 @@ p, li { white-space: pre-wrap; }
<normaloff>:/replace_16px.png</normaloff>:/replace_16px.png</iconset>
</property>
<property name="shortcut">
<string/>
<string>Ctrl+R</string>
</property>
<property name="autoRaise">
<bool>true</bool>
@ -787,10 +733,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QToolButton" name="replaceAllButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Replace all&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Replace all (Ctrl+Shift+R)</string>
</property>
<property name="text">
<string>All</string>
@ -799,6 +742,9 @@ p, li { white-space: pre-wrap; }
<iconset resource="../../icons/icons.qrc">
<normaloff>:/replace_all_16px.png</normaloff>:/replace_all_16px.png</iconset>
</property>
<property name="shortcut">
<string>Ctrl+Shift+R</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
@ -1497,14 +1443,6 @@ p, li { white-space: pre-wrap; }
<string>Case Sensitive</string>
</property>
</action>
<action name="actionSearchReplace">
<property name="text">
<string>Search &amp; Replace</string>
</property>
<property name="shortcut">
<string>Ctrl+F</string>
</property>
</action>
<action name="actionSaveAs">
<property name="text">
<string>Save As</string>

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReplacePropertiesBox</class>
<widget class="QWidget" name="ReplacePropertiesBox" >
<property name="geometry" >
<widget class="QWidget" name="ReplacePropertiesBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,32 +10,32 @@
<height>265</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="2" column="0" >
<widget class="QLabel" name="label_54" >
<property name="text" >
<item row="2" column="0">
<widget class="QLabel" name="label_54">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="2" column="2" >
<widget class="QLineEdit" name="box_height" />
<item row="2" column="2">
<widget class="QLineEdit" name="box_height"/>
</item>
<item row="3" column="0" colspan="3" >
<item row="3" column="0" colspan="3">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>200</width>
<height>40</height>
@ -42,69 +43,79 @@
</property>
</spacer>
</item>
<item row="2" column="1" >
<widget class="QLabel" name="label_60" >
<property name="text" >
<item row="2" column="1">
<widget class="QLabel" name="label_60">
<property name="text">
<string/>
</property>
<property name="pixmap" >
<pixmap resource="layResources.qrc" >:/right_16px@2x.png</pixmap>
<property name="pixmap">
<pixmap resource="../../icons/icons.qrc">:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLabel" name="label_58" >
<property name="text" >
<item row="0" column="1">
<widget class="QLabel" name="label_58">
<property name="text">
<string/>
</property>
<property name="pixmap" >
<pixmap resource="layResources.qrc" >:/right_16px@2x.png</pixmap>
<property name="pixmap">
<pixmap resource="../../icons/icons.qrc">:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="box_width" />
<item row="1" column="2">
<widget class="QLineEdit" name="box_width"/>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_55" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_55">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="label_59" >
<property name="text" >
<item row="1" column="1">
<widget class="QLabel" name="label_59">
<property name="text">
<string/>
</property>
<property name="pixmap" >
<pixmap resource="layResources.qrc" >:/right_16px@2x.png</pixmap>
<property name="pixmap">
<pixmap resource="../../icons/icons.qrc">:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_53" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label_53">
<property name="text">
<string>Layer</string>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="1" column="3">
<widget class="QLabel" name="label">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="2" column="3" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="2" column="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="lay::LayerSelectionComboBox" name="box_layer" />
<item row="0" column="2">
<widget class="lay::LayerSelectionComboBox" name="box_layer">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</widget>
@ -115,6 +126,8 @@
<header>layWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<resources>
<include location="../../icons/icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,37 +1,38 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReplacePropertiesPath</class>
<widget class="QWidget" name="ReplacePropertiesPath" >
<property name="geometry" >
<widget class="QWidget" name="ReplacePropertiesPath">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>239</width>
<width>246</width>
<height>241</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="0" >
<widget class="QLabel" name="label_55" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_55">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3" >
<item row="2" column="0" colspan="3">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>200</width>
<height>40</height>
@ -39,42 +40,52 @@
</property>
</spacer>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_54" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label_54">
<property name="text">
<string>Layer</string>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="lay::LayerSelectionComboBox" name="path_layer" />
</item>
<item row="1" column="1" >
<widget class="QLabel" name="label_61" >
<property name="text" >
<string/>
<item row="0" column="2">
<widget class="lay::LayerSelectionComboBox" name="path_layer">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="pixmap" >
<pixmap resource="layResources.qrc" >:/right_16px@2x.png</pixmap>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="path_width" />
</item>
<item row="0" column="1" >
<widget class="QLabel" name="label_62" >
<property name="text" >
<item row="1" column="1">
<widget class="QLabel" name="label_61">
<property name="text">
<string/>
</property>
<property name="pixmap" >
<pixmap resource="layResources.qrc" >:/right_16px@2x.png</pixmap>
<property name="pixmap">
<pixmap resource="../../icons/icons.qrc">:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="1" column="2">
<widget class="QLineEdit" name="path_width"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_62">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../icons/icons.qrc">:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label">
<property name="text">
<string>µm</string>
</property>
</widget>
@ -88,6 +99,8 @@
<header>layWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<resources>
<include location="../../icons/icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReplacePropertiesShape</class>
<widget class="QWidget" name="ReplacePropertiesShape" >
<property name="geometry" >
<widget class="QWidget" name="ReplacePropertiesShape">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,39 +10,19 @@
<height>187</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QGridLayout">
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="1" >
<widget class="QLabel" name="label_57" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="layResources.qrc" >:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_47" >
<property name="text" >
<string>Layer</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3" >
<item row="2" column="0" colspan="3">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>281</height>
@ -49,18 +30,49 @@
</property>
</spacer>
</item>
<item row="0" column="2" >
<widget class="lay::LayerSelectionComboBox" name="shape_layer" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<item row="0" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
<string>Layer</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="lay::LayerSelectionComboBox" name="shape_layer">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_57">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../icons/icons.qrc">:/right_16px@2x.png</pixmap>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
@ -70,6 +82,8 @@
<header>layWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<resources>
<include location="../../icons/icons.qrc"/>
</resources>
<connections/>
</ui>

Some files were not shown because too many files have changed in this diff Show More