mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-19 19:54:07 +00:00
Merge branch 'v0.8.0' into ifcmax/initial-refresh
This commit is contained in:
@@ -24,7 +24,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
pyver: [py311, py312]
|
pyver: [py311, py312, py313]
|
||||||
config:
|
config:
|
||||||
- {
|
- {
|
||||||
name: "Windows Build",
|
name: "Windows Build",
|
||||||
@@ -42,6 +42,11 @@ jobs:
|
|||||||
name: "MacOS ARM Build",
|
name: "MacOS ARM Build",
|
||||||
short_name: macosm1,
|
short_name: macosm1,
|
||||||
}
|
}
|
||||||
|
exclude:
|
||||||
|
# Python 3.13 is needed for Blender 5.1+ and Blender dropped Intel Mac support in 5.0.
|
||||||
|
- pyver: py313
|
||||||
|
config:
|
||||||
|
short_name: macos
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
|
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: ci-black-formatting
|
name: ci-lint
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -30,6 +30,7 @@ jobs:
|
|||||||
uv tool install ruff
|
uv tool install ruff
|
||||||
uv tool install black
|
uv tool install black
|
||||||
uv tool install poethepoet
|
uv tool install poethepoet
|
||||||
|
uv tool install ty
|
||||||
|
|
||||||
# black doesn't catch all syntax errors, so we check them explicitly.
|
# black doesn't catch all syntax errors, so we check them explicitly.
|
||||||
- name: Check syntax errors
|
- name: Check syntax errors
|
||||||
@@ -57,6 +58,13 @@ jobs:
|
|||||||
black --diff --check . | black-codeclimate | python .github/workflows/black_to_github_annotations.py
|
black --diff --check . | black-codeclimate | python .github/workflows/black_to_github_annotations.py
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: ty check
|
||||||
|
id: ty
|
||||||
|
run: |
|
||||||
|
poe ty-venv
|
||||||
|
poe ty
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Ruff check
|
- name: Ruff check
|
||||||
id: ruff
|
id: ruff
|
||||||
run: |
|
run: |
|
||||||
@@ -105,4 +113,7 @@ jobs:
|
|||||||
if [ "${{ steps.ruff.outcome }}" != "success" ]; then
|
if [ "${{ steps.ruff.outcome }}" != "success" ]; then
|
||||||
echo "::error::Ruff check failed, see Summary or 'ruff' step for the details." && ERROR=1
|
echo "::error::Ruff check failed, see Summary or 'ruff' step for the details." && ERROR=1
|
||||||
fi
|
fi
|
||||||
|
if [ "${{ steps.ty.outcome }}" != "success" ]; then
|
||||||
|
echo "::error::ty check failed, see 'ty check' step for the details." && ERROR=1
|
||||||
|
fi
|
||||||
exit $ERROR
|
exit $ERROR
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: Release Pyodide WASM Wheel
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout IfcOpenShell
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
|
|
||||||
|
- name: Build wheel
|
||||||
|
working-directory: pyodide
|
||||||
|
run: uv run pack_wheel.py --build
|
||||||
|
|
||||||
|
- name: Find wheel
|
||||||
|
id: wheel
|
||||||
|
run: |
|
||||||
|
WHEEL=$(ls pyodide/dist/ifcopenshell-*.whl)
|
||||||
|
echo "path=$WHEEL" >> $GITHUB_OUTPUT
|
||||||
|
echo "name=$(basename $WHEEL)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout wasm-wheels
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: IfcOpenShell/wasm-wheels
|
||||||
|
path: wasm-wheels
|
||||||
|
token: ${{ secrets.WASM_WHEELS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Commit and push wheel to wasm-wheels
|
||||||
|
run: |
|
||||||
|
WHEEL_NAME="${{ steps.wheel.outputs.name }}"
|
||||||
|
cp "${{ steps.wheel.outputs.path }}" "wasm-wheels/$WHEEL_NAME"
|
||||||
|
cd wasm-wheels
|
||||||
|
git config user.name "IfcOpenBot"
|
||||||
|
git config user.email "ifcopenbot@ifcopenshell.org"
|
||||||
|
git add "$WHEEL_NAME"
|
||||||
|
git commit -m "Add $WHEEL_NAME"
|
||||||
|
git push origin main
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Checkout intermediate Pages repo
|
- name: Checkout intermediate Pages repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: IfcOpenShell/aichat_ifcopenshell_org_static_html
|
repository: IfcOpenShell/aichat_ifcopenshell_org_static_html
|
||||||
ref: gh-pages
|
ref: gh-pages
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rsync -av --delete --exclude='.git/' src/ifcchat/ output/
|
rsync -av --delete --exclude='.git/' src/ifcchat/ output/
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Download wheels
|
- name: Download wheels
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Checkout intermediate Pages repo
|
- name: Checkout intermediate Pages repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: IfcOpenShell/wasm_ifcopenshell_org_static_html
|
repository: IfcOpenShell/wasm_ifcopenshell_org_static_html
|
||||||
ref: gh-pages
|
ref: gh-pages
|
||||||
|
|||||||
+1
-1
@@ -1515,7 +1515,7 @@ if "IfcOpenShell-Python" in targets:
|
|||||||
)
|
)
|
||||||
# Copy setup.py where pyodide build system expects it.
|
# Copy setup.py where pyodide build system expects it.
|
||||||
shutil.copy(REPO_PATH / "pyodide" / "setup.py", REPO_PATH)
|
shutil.copy(REPO_PATH / "pyodide" / "setup.py", REPO_PATH)
|
||||||
# Empty pyproject so it's contents won't affect the resulting wheelthe the
|
# Empty pyproject so it's contents won't affect the resulting wheel
|
||||||
# otherwise the wheel will use version and dependencies from toml, not setup.py.
|
# otherwise the wheel will use version and dependencies from toml, not setup.py.
|
||||||
(REPO_PATH / "pyproject.toml").write_text("")
|
(REPO_PATH / "pyproject.toml").write_text("")
|
||||||
|
|
||||||
|
|||||||
@@ -14,18 +14,11 @@ source .venv/bin/activate
|
|||||||
uv pip install pyodide-build
|
uv pip install pyodide-build
|
||||||
# `uv run` is required, so xbuildenv would skip using `pip`.
|
# `uv run` is required, so xbuildenv would skip using `pip`.
|
||||||
uv run pyodide xbuildenv install
|
uv run pyodide xbuildenv install
|
||||||
|
uv run pyodide xbuildenv install-emscripten
|
||||||
|
|
||||||
# Emscripten doesn't come with xbuildenv.
|
EMSDK_ROOT=$(pyodide config get emscripten_dir)
|
||||||
if [ ! -d emsdk ]; then
|
source ${EMSDK_ROOT}/emsdk_env.sh
|
||||||
git clone https://github.com/emscripten-core/emsdk
|
|
||||||
fi
|
|
||||||
pushd emsdk
|
|
||||||
PYODIDE_EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
|
|
||||||
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
|
|
||||||
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
|
|
||||||
source emsdk_env.sh
|
|
||||||
which emcc
|
which emcc
|
||||||
popd
|
|
||||||
|
|
||||||
mkdir -p packages/ifcopenshell
|
mkdir -p packages/ifcopenshell
|
||||||
VERSION=`cat IfcOpenShell/VERSION`
|
VERSION=`cat IfcOpenShell/VERSION`
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
#
|
||||||
|
# /// script
|
||||||
|
# # Latest Pyodide build env versions are listed here:
|
||||||
|
# # https://pyodide.github.io/pyodide/api/pyodide-cross-build-environments.json
|
||||||
|
# # https://github.com/pyodide/pyodide-build/blob/main/pyodide_build/xbuildenv_releases.py
|
||||||
|
# requires-python = "==3.13.2"
|
||||||
|
# dependencies = [
|
||||||
|
# "requests",
|
||||||
|
# "setuptools",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
"""
|
||||||
|
Pack an IfcOpenShell WASM wheel using Pyodide build system.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
uv run make_wheel.py # Show this help
|
||||||
|
uv run make_wheel.py --build # Build wheel
|
||||||
|
uv run make_wheel.py --clean # Clean build artifacts and exit
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Get repo root (parent of this script's parent directory)
|
||||||
|
REPO_ROOT = Path(__file__).parent.parent
|
||||||
|
PYODIDE_DIR = REPO_ROOT / "pyodide"
|
||||||
|
BUILD_DIR = PYODIDE_DIR / "build"
|
||||||
|
|
||||||
|
# Hardcoded path (Windows packing workaround with --dev flag)
|
||||||
|
PYODIDE_BUILD = Path(r"L:\Projects\Github\pyodide-build")
|
||||||
|
|
||||||
|
# Wheel platform tag (from PYODIDE_EMSCRIPTEN_VERSION in pyodide-build/Makefile.envs)
|
||||||
|
WHEEL_PLATFORM_TAG = "emscripten_4_0_9_wasm32"
|
||||||
|
|
||||||
|
# Location where ifcopenshell will be extracted
|
||||||
|
IFCOPENSHELL_DIR = PYODIDE_DIR / "ifcopenshell"
|
||||||
|
|
||||||
|
|
||||||
|
class WheelBuilder:
|
||||||
|
@staticmethod
|
||||||
|
def extract_ifcopenshell_from_git(dst: Path) -> None:
|
||||||
|
"""Extract ifcopenshell directory from git repo into destination."""
|
||||||
|
Tools.rmrf(dst)
|
||||||
|
|
||||||
|
print(f"Extracting ifcopenshell from git to {dst}...")
|
||||||
|
# Use git ls-files piped to git checkout-index to avoid copying
|
||||||
|
# untracked or ignored files from the actual repo.
|
||||||
|
ls_proc = subprocess.Popen(
|
||||||
|
["git", "ls-files", "-z", "src/ifcopenshell-python/ifcopenshell"],
|
||||||
|
cwd=REPO_ROOT,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
checkout_proc = subprocess.Popen(
|
||||||
|
["git", "checkout-index", "-z", "--prefix", "pyodide/", "--stdin"],
|
||||||
|
cwd=REPO_ROOT,
|
||||||
|
stdin=ls_proc.stdout,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
assert ls_proc.stdout is not None
|
||||||
|
ls_proc.stdout.close()
|
||||||
|
checkout_proc.communicate()
|
||||||
|
|
||||||
|
if checkout_proc.returncode != 0:
|
||||||
|
assert checkout_proc.stderr is not None
|
||||||
|
raise RuntimeError(f"Failed to extract: {checkout_proc.stderr.decode()}")
|
||||||
|
|
||||||
|
# Move src/ifcopenshell-python/ifcopenshell to ifcopenshell.
|
||||||
|
temp_src = PYODIDE_DIR / "src" / "ifcopenshell-python" / "ifcopenshell"
|
||||||
|
shutil.move(temp_src, dst)
|
||||||
|
|
||||||
|
# Clean up temporary src directory.
|
||||||
|
Tools.rmrf(PYODIDE_DIR / "src")
|
||||||
|
|
||||||
|
print("✓ Extracted ifcopenshell from git")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_wheel_url(makefile_path: Path) -> str:
|
||||||
|
"""Get S3 wheel URL based on BINARY_VERSION and BUILD_COMMIT from Makefile."""
|
||||||
|
|
||||||
|
def parse_makefile_vars() -> dict[str, str]:
|
||||||
|
content = makefile_path.read_text()
|
||||||
|
vars: dict[str, str] = {}
|
||||||
|
for match in re.finditer(r"^(BINARY_VERSION|BUILD_COMMIT):=(.+)$", content, re.MULTILINE):
|
||||||
|
vars[match.group(1)] = match.group(2).strip()
|
||||||
|
return vars
|
||||||
|
|
||||||
|
vars: dict[str, str] = parse_makefile_vars()
|
||||||
|
binary_version = vars["BINARY_VERSION"]
|
||||||
|
build_commit = vars["BUILD_COMMIT"]
|
||||||
|
filename = f"ifcopenshell-{binary_version}+{build_commit}-cp313-cp313-pyodide_2025_0_wasm32.whl"
|
||||||
|
encoded_filename = quote(filename, safe="")
|
||||||
|
return f"https://s3.amazonaws.com/ifcopenshell-builds/{encoded_filename}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def download_and_extract_so(url: str, build_dir: Path) -> tuple[Path, Path]:
|
||||||
|
"""Download wheel from URL and extract .so and .py files."""
|
||||||
|
py_wrapper_filename = "ifcopenshell_wrapper.py"
|
||||||
|
build_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
wheel_path = build_dir / url.rsplit("/", 1)[-1]
|
||||||
|
|
||||||
|
if wheel_path.exists():
|
||||||
|
print(f"Using cached wheel: {wheel_path}")
|
||||||
|
else:
|
||||||
|
print(f"Downloading {url}...")
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
wheel_path.write_bytes(response.content)
|
||||||
|
|
||||||
|
print("Extracting _ifcopenshell_wrapper files...")
|
||||||
|
with zipfile.ZipFile(wheel_path) as zf:
|
||||||
|
so_files = [f for f in zf.namelist() if f.endswith(".so")]
|
||||||
|
py_files = [f for f in zf.namelist() if f.endswith(py_wrapper_filename)]
|
||||||
|
|
||||||
|
assert so_files, "No .so file found in wheel"
|
||||||
|
assert py_files, f"No {py_wrapper_filename} file found in wheel"
|
||||||
|
|
||||||
|
so_file = so_files[0]
|
||||||
|
so_dst = build_dir / Path(so_file).name
|
||||||
|
so_dst.write_bytes(zf.read(so_file))
|
||||||
|
|
||||||
|
py_file = py_files[0]
|
||||||
|
py_dst = build_dir / Path(py_file).name
|
||||||
|
py_dst.write_bytes(zf.read(py_file))
|
||||||
|
|
||||||
|
return so_dst, py_dst
|
||||||
|
|
||||||
|
|
||||||
|
class Tools:
|
||||||
|
@staticmethod
|
||||||
|
def run(
|
||||||
|
cmd: list[str],
|
||||||
|
cwd: Path | None = None,
|
||||||
|
) -> None:
|
||||||
|
print(f"$ {' '.join(cmd)}")
|
||||||
|
subprocess.check_call(cmd, cwd=cwd)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_symlink(dst: Path, src: Path) -> None:
|
||||||
|
Tools.rmrf(dst)
|
||||||
|
dst.symlink_to(src)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def rmrf(path: Path) -> None:
|
||||||
|
if path.exists() or path.is_symlink():
|
||||||
|
if path.is_dir() and not path.is_symlink():
|
||||||
|
shutil.rmtree(path)
|
||||||
|
else:
|
||||||
|
path.unlink()
|
||||||
|
|
||||||
|
|
||||||
|
def clean() -> None:
|
||||||
|
"""Remove build artifacts."""
|
||||||
|
paths_to_remove = (
|
||||||
|
BUILD_DIR,
|
||||||
|
PYODIDE_DIR / ".pyodide_build",
|
||||||
|
PYODIDE_DIR / "dist",
|
||||||
|
PYODIDE_DIR / "ifcopenshell.egg-info",
|
||||||
|
PYODIDE_DIR / "src",
|
||||||
|
IFCOPENSHELL_DIR,
|
||||||
|
)
|
||||||
|
for path in paths_to_remove:
|
||||||
|
if path.exists() or path.is_symlink():
|
||||||
|
print(f"Removing {path}...")
|
||||||
|
Tools.rmrf(path)
|
||||||
|
print("✓ Clean complete")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__, add_help=False)
|
||||||
|
parser.add_argument("--build", action="store_true", help="Build the wheel")
|
||||||
|
parser.add_argument("--clean", action="store_true", help="Clean build folder")
|
||||||
|
parser.add_argument(
|
||||||
|
"--dev",
|
||||||
|
action="store_true",
|
||||||
|
help="Use editable pyodide-build from hardcoded path (Windows packing workaround)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not args.build and not args.clean:
|
||||||
|
print(__doc__)
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.clean:
|
||||||
|
clean()
|
||||||
|
return
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
WheelBuilder.extract_ifcopenshell_from_git(IFCOPENSHELL_DIR)
|
||||||
|
|
||||||
|
print("Downloading and extracting _ifcopenshell_wrapper files...")
|
||||||
|
makefile = REPO_ROOT / "src" / "ifcopenshell-python" / "Makefile"
|
||||||
|
wheel_url = WheelBuilder.get_wheel_url(makefile)
|
||||||
|
so_file, py_file = WheelBuilder.download_and_extract_so(wheel_url, BUILD_DIR)
|
||||||
|
|
||||||
|
Tools.create_symlink(IFCOPENSHELL_DIR / Path(so_file).name, so_file)
|
||||||
|
Tools.create_symlink(IFCOPENSHELL_DIR / Path(py_file).name, py_file)
|
||||||
|
|
||||||
|
print("Installing pyodide-build...")
|
||||||
|
if args.dev:
|
||||||
|
Tools.run(["uv", "pip", "install", "-e", str(PYODIDE_BUILD)])
|
||||||
|
else:
|
||||||
|
Tools.run(["uv", "pip", "install", "pyodide-build"])
|
||||||
|
|
||||||
|
print("Building with pyodide...")
|
||||||
|
# Use --no-isolation due to pyodide-build Windows support issues:
|
||||||
|
# symlink_unisolated_packages fails with missing `_sysconfigdata_$(CPYTHON_ABI_FLAGS)_emscripten_wasm32-emscripten.py`.
|
||||||
|
# Hardcode platform name since pyodide doesn't yet support overriding wheel tags on Windows.
|
||||||
|
#
|
||||||
|
# Use `LEGACY_PLATFORM` since pyodide 0.34.1 introduced new tag for wheels `pyemscripten`,
|
||||||
|
# which doesn't work with pyodide itself yet - https://github.com/pyodide/pyodide/issues/6177.
|
||||||
|
os.environ["USE_LEGACY_PLATFORM"] = "1"
|
||||||
|
Tools.run(["pyodide", "build", f"-C--build-option=--plat-name={WHEEL_PLATFORM_TAG}"])
|
||||||
|
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
print(f"\n✓ Done! ({elapsed:.1f}s)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+39
-1
@@ -2,12 +2,16 @@
|
|||||||
# because `tool.setuptools.ext-modules` is still experimental in pyproject.toml
|
# because `tool.setuptools.ext-modules` is still experimental in pyproject.toml
|
||||||
# and we need it to get the wheel suffix right.
|
# and we need it to get the wheel suffix right.
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import tomllib
|
import tomllib
|
||||||
from setuptools import Extension, find_packages, setup
|
from setuptools import Extension, find_packages, setup
|
||||||
|
from setuptools.command.build_ext import build_ext
|
||||||
|
|
||||||
REPO_FOLDER = Path(__file__).parent
|
# Detect repo folder: if setup.py is in pyodide folder, go to parent
|
||||||
|
SETUP_DIR = Path(__file__).parent
|
||||||
|
REPO_FOLDER = SETUP_DIR.parent if SETUP_DIR.name == "pyodide" else SETUP_DIR
|
||||||
|
|
||||||
|
|
||||||
def get_version() -> str:
|
def get_version() -> str:
|
||||||
@@ -25,6 +29,39 @@ def get_dependencies() -> list[str]:
|
|||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
|
|
||||||
|
class UnixBuildExt(build_ext):
|
||||||
|
"""Customize ``build_ext`` to support packing on Windows."""
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
from distutils import sysconfig
|
||||||
|
|
||||||
|
super().finalize_options()
|
||||||
|
if sys.platform == "win32":
|
||||||
|
self.compiler = "unix"
|
||||||
|
|
||||||
|
# Configure sysconfig for Windows builds
|
||||||
|
# CCSHARED is the only variable that's not customizable with env vars.
|
||||||
|
# Basically avoiding this:
|
||||||
|
# File ".venv\Lib\site-packages\setuptools\_distutils\sysconfig.py", line 366, in customize_compiler
|
||||||
|
# compiler_so=cc_cmd + ' ' + ccshared,
|
||||||
|
# ~~~~~~~~~~~~~^~~~~~~~~~
|
||||||
|
# TypeError: can only concatenate str (not "NoneType") to str
|
||||||
|
sysconfig.get_config_vars() # Initialize config cache
|
||||||
|
if sysconfig._config_vars.get("CCSHARED") is None:
|
||||||
|
sysconfig._config_vars["CCSHARED"] = "-fPIC"
|
||||||
|
# Override compiler type before it's instantiated
|
||||||
|
|
||||||
|
# Set Emscripten compiler environment variables
|
||||||
|
os.environ["CC"] = "emcc"
|
||||||
|
os.environ["CXX"] = "em++"
|
||||||
|
os.environ["CFLAGS"] = ""
|
||||||
|
os.environ["CXXFLAGS"] = ""
|
||||||
|
os.environ["LDSHARED"] = "emcc -shared"
|
||||||
|
os.environ["AR"] = "emar"
|
||||||
|
os.environ["ARFLAGS"] = "rcs"
|
||||||
|
os.environ["SETUPTOOLS_EXT_SUFFIX"] = ".cpython-313-wasm32-emscripten.so"
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="ifcopenshell",
|
name="ifcopenshell",
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
@@ -44,4 +81,5 @@ setup(
|
|||||||
},
|
},
|
||||||
# Has to provide extension to get the correct wheel suffix.
|
# Has to provide extension to get the correct wheel suffix.
|
||||||
ext_modules=[Extension("ifcopenshell._ifcopenshell_wrapper", sources=[])],
|
ext_modules=[Extension("ifcopenshell._ifcopenshell_wrapper", sources=[])],
|
||||||
|
cmdclass={"build_ext": UnixBuildExt},
|
||||||
)
|
)
|
||||||
|
|||||||
+6
-4
@@ -3,8 +3,9 @@ name = "IfcOpenShell"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"black==26.3.1",
|
"black==26.3.1",
|
||||||
"ruff==0.15.8",
|
"ruff==0.15.9",
|
||||||
"poethepoet",
|
"poethepoet",
|
||||||
|
"ty==0.0.29",
|
||||||
"gersemi==0.26.1",
|
"gersemi==0.26.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -28,6 +29,9 @@ extend-exclude = '''
|
|||||||
reportInvalidTypeForm = false
|
reportInvalidTypeForm = false
|
||||||
disableBytesTypePromotions = true
|
disableBytesTypePromotions = true
|
||||||
reportUnnecessaryTypeIgnoreComment = true
|
reportUnnecessaryTypeIgnoreComment = true
|
||||||
|
reportRedeclaration = false
|
||||||
|
# Ignore warnings from bpy stubs missing actual source files.
|
||||||
|
reportMissingModuleSource = false
|
||||||
# Pylance doesn't respect gitignore, so we have to exclude files manually here
|
# Pylance doesn't respect gitignore, so we have to exclude files manually here
|
||||||
# to avoid VS Code slowing down.
|
# to avoid VS Code slowing down.
|
||||||
# https://github.com/microsoft/pylance-release/issues/5169
|
# https://github.com/microsoft/pylance-release/issues/5169
|
||||||
@@ -84,7 +88,6 @@ all = "ignore"
|
|||||||
# Structural rules (no deep type inference needed, easier to adapt).
|
# Structural rules (no deep type inference needed, easier to adapt).
|
||||||
abstract-method-in-final-class = "error"
|
abstract-method-in-final-class = "error"
|
||||||
ambiguous-protocol-member = "error"
|
ambiguous-protocol-member = "error"
|
||||||
byte-string-type-annotation = "error"
|
|
||||||
conflicting-declarations = "error"
|
conflicting-declarations = "error"
|
||||||
conflicting-metaclass = "error"
|
conflicting-metaclass = "error"
|
||||||
cyclic-class-definition = "error"
|
cyclic-class-definition = "error"
|
||||||
@@ -96,7 +99,6 @@ empty-body = "error"
|
|||||||
escape-character-in-forward-annotation = "error"
|
escape-character-in-forward-annotation = "error"
|
||||||
final-on-non-method = "error"
|
final-on-non-method = "error"
|
||||||
final-without-value = "error"
|
final-without-value = "error"
|
||||||
fstring-type-annotation = "error"
|
|
||||||
ignore-comment-unknown-rule = "error"
|
ignore-comment-unknown-rule = "error"
|
||||||
implicit-concatenated-string-type-annotation = "error"
|
implicit-concatenated-string-type-annotation = "error"
|
||||||
inconsistent-mro = "error"
|
inconsistent-mro = "error"
|
||||||
@@ -224,7 +226,7 @@ ty.sequence = ["ty-bonsai", "ty-ios"]
|
|||||||
ty.help = "Run ty type checker. Requires ty-venv to be set up first."
|
ty.help = "Run ty type checker. Requires ty-venv to be set up first."
|
||||||
ty-bonsai = "ty check src/bonsai --python=src/bonsai/.venv"
|
ty-bonsai = "ty check src/bonsai --python=src/bonsai/.venv"
|
||||||
|
|
||||||
ty-venv.sequence = ["ty-venv-bonsai", "ty-venv-ios"]
|
ty-venv.sequence = ["bonsai-deps", "ty-venv-bonsai", "ty-venv-ios"]
|
||||||
|
|
||||||
ty-venv-bonsai.sequence = [
|
ty-venv-bonsai.sequence = [
|
||||||
{cmd = "uv venv src/bonsai/.venv --python=3.11 --allow-existing"},
|
{cmd = "uv venv src/bonsai/.venv --python=3.11 --allow-existing"},
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class AggregateDecorator:
|
|||||||
cls.is_installed = False
|
cls.is_installed = False
|
||||||
|
|
||||||
def dotted_line_shader(self):
|
def dotted_line_shader(self):
|
||||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||||
vert_out.smooth("FLOAT", "v_ArcLength")
|
vert_out.smooth("FLOAT", "v_ArcLength")
|
||||||
|
|
||||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||||
|
|||||||
@@ -295,13 +295,13 @@ class ExplorerShowUIPopup(bpy.types.Operator):
|
|||||||
bl_description = "Show Explorer UI to select element as attribute value or edit it."
|
bl_description = "Show Explorer UI to select element as attribute value or edit it."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
ifc_class: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
ifc_class: bpy.props.StringProperty()
|
||||||
"""Element IFC class."""
|
"""Element IFC class."""
|
||||||
attribute_name: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
attribute_name: bpy.props.StringProperty()
|
||||||
"""IFC class attribute name."""
|
"""IFC class attribute name."""
|
||||||
data_path: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
data_path: bpy.props.StringProperty()
|
||||||
"""Full data path"""
|
"""Full data path"""
|
||||||
preselect_ifc_id: bpy.props.IntProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
preselect_ifc_id: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||||
"""IFC id to preselect in the popup."""
|
"""IFC id to preselect in the popup."""
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class BIMAttributeProperties(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class ExplorerEntity(PropertyGroup):
|
class ExplorerEntity(PropertyGroup):
|
||||||
ifc_definition_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
ifc_definition_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
ifc_definition_id: int
|
ifc_definition_id: int
|
||||||
@@ -60,7 +60,7 @@ class BIMExplorerProperties(PropertyGroup):
|
|||||||
self.property_unset("editing_entity_id")
|
self.property_unset("editing_entity_id")
|
||||||
self.entity_attributes.clear()
|
self.entity_attributes.clear()
|
||||||
|
|
||||||
is_loaded: BoolProperty( # pyright: ignore[reportRedeclaration]
|
is_loaded: BoolProperty(
|
||||||
name="Toggle Explorer UI",
|
name="Toggle Explorer UI",
|
||||||
update=update_is_loaded,
|
update=update_is_loaded,
|
||||||
)
|
)
|
||||||
@@ -76,15 +76,15 @@ class BIMExplorerProperties(PropertyGroup):
|
|||||||
def update_ifc_class(self, context: object) -> None:
|
def update_ifc_class(self, context: object) -> None:
|
||||||
tool.Attribute.refresh_uilist_entities()
|
tool.Attribute.refresh_uilist_entities()
|
||||||
|
|
||||||
ifc_class: EnumProperty( # pyright: ignore[reportRedeclaration]
|
ifc_class: EnumProperty(
|
||||||
name="IFC Class To Search",
|
name="IFC Class To Search",
|
||||||
items=get_ifc_class,
|
items=get_ifc_class,
|
||||||
update=update_ifc_class,
|
update=update_ifc_class,
|
||||||
)
|
)
|
||||||
entities: CollectionProperty(type=ExplorerEntity) # pyright: ignore[reportRedeclaration]
|
entities: CollectionProperty(type=ExplorerEntity)
|
||||||
active_entity_index: IntProperty() # pyright: ignore[reportRedeclaration]
|
active_entity_index: IntProperty()
|
||||||
editing_entity_id: IntProperty() # pyright: ignore[reportRedeclaration]
|
editing_entity_id: IntProperty()
|
||||||
entity_attributes: CollectionProperty(type=Attribute) # pyright: ignore[reportRedeclaration]
|
entity_attributes: CollectionProperty(type=Attribute)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
is_loaded: bool
|
is_loaded: bool
|
||||||
|
|||||||
@@ -201,16 +201,10 @@ class ExecuteIfcClash(bpy.types.Operator, ExportHelper):
|
|||||||
"ALT+click to run a quick clash without selecting a file to save."
|
"ALT+click to run a quick clash without selecting a file to save."
|
||||||
)
|
)
|
||||||
|
|
||||||
filter_glob: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
filter_glob: bpy.props.StringProperty(default="*.bcf;*.json", options={"HIDDEN"})
|
||||||
default="*.bcf;*.json", options={"HIDDEN"}
|
format: bpy.props.EnumProperty(name="Format", items=[(i, i, "") for i in ("bcf", "json")])
|
||||||
)
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE"})
|
||||||
format: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
quick_clash: bpy.props.BoolProperty(
|
||||||
name="Format", items=[(i, i, "") for i in ("bcf", "json")]
|
|
||||||
)
|
|
||||||
filepath: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
|
||||||
subtype="FILE_PATH", options={"SKIP_SAVE"}
|
|
||||||
)
|
|
||||||
quick_clash: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,12 @@ from bonsai.bim.prop import BIMFilterGroup, StrProperty
|
|||||||
|
|
||||||
|
|
||||||
class ClashSource(PropertyGroup):
|
class ClashSource(PropertyGroup):
|
||||||
name: StringProperty( # pyright: ignore[reportRedeclaration]
|
name: StringProperty(
|
||||||
name="File",
|
name="File",
|
||||||
description="Absolute filepath to existing .ifc file to use as a clash source.",
|
description="Absolute filepath to existing .ifc file to use as a clash source.",
|
||||||
)
|
)
|
||||||
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups") # pyright: ignore[reportRedeclaration]
|
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
|
||||||
mode: EnumProperty( # pyright: ignore[reportRedeclaration]
|
mode: EnumProperty(
|
||||||
items=[
|
items=[
|
||||||
("a", "All Elements", "All elements will be used for clashing"),
|
("a", "All Elements", "All elements will be used for clashing"),
|
||||||
("i", "Include", "Only the selected elements are included for clashing"),
|
("i", "Include", "Only the selected elements are included for clashing"),
|
||||||
@@ -62,7 +62,7 @@ class Clash(PropertyGroup):
|
|||||||
b_global_id: StringProperty(name="B")
|
b_global_id: StringProperty(name="B")
|
||||||
a_name: StringProperty(name="A Name")
|
a_name: StringProperty(name="A Name")
|
||||||
b_name: StringProperty(name="B Name")
|
b_name: StringProperty(name="B Name")
|
||||||
clash_type: EnumProperty( # pyright: ignore[reportRedeclaration]
|
clash_type: EnumProperty(
|
||||||
name="Clash Type",
|
name="Clash Type",
|
||||||
items=tuple((i, i, "") for i in CLASH_TYPE_ITEMS),
|
items=tuple((i, i, "") for i in CLASH_TYPE_ITEMS),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class CopyCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Copy Cost Schedule"
|
bl_label = "Copy Cost Schedule"
|
||||||
bl_description = "Create a duplicate of the provided cost schedule."
|
bl_description = "Create a duplicate of the provided cost schedule."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
cost_schedule: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
cost_schedule: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
cost_schedule: int
|
cost_schedule: int
|
||||||
|
|||||||
@@ -260,14 +260,14 @@ class CreateAllShapes(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
geometry_library: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
geometry_library: bpy.props.EnumProperty(
|
||||||
name="Geometry Library",
|
name="Geometry Library",
|
||||||
description="Geometry library to use for testing shape creation.",
|
description="Geometry library to use for testing shape creation.",
|
||||||
items=[(i, i, "") for i in get_args(ifcopenshell.geom.GEOMETRY_LIBRARY)],
|
items=[(i, i, "") for i in get_args(ifcopenshell.geom.GEOMETRY_LIBRARY)],
|
||||||
# By default use the same library as used for importing ifc project.
|
# By default use the same library as used for importing ifc project.
|
||||||
default="hybrid-cgal-simple-opencascade",
|
default="hybrid-cgal-simple-opencascade",
|
||||||
)
|
)
|
||||||
custom_geometry_library: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
custom_geometry_library: bpy.props.StringProperty(
|
||||||
name="Custom Geometry Library",
|
name="Custom Geometry Library",
|
||||||
description="Provide a custom geometry library name, will override the 'geometry library' property.",
|
description="Provide a custom geometry library name, will override the 'geometry library' property.",
|
||||||
)
|
)
|
||||||
@@ -781,7 +781,7 @@ class PurgeUnusedObjects(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Purge Unused Objects"
|
bl_label = "Purge Unused Objects"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
object_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
object_type: bpy.props.EnumProperty(
|
||||||
name="Object Type",
|
name="Object Type",
|
||||||
items=((s, s.capitalize(), "") for s in get_args(tool.Debug.PurgeMergeObjectType)),
|
items=((s, s.capitalize(), "") for s in get_args(tool.Debug.PurgeMergeObjectType)),
|
||||||
)
|
)
|
||||||
@@ -827,7 +827,7 @@ class MergeIdenticalObjects(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
)
|
)
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
object_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
object_type: bpy.props.EnumProperty(
|
||||||
name="Object Type",
|
name="Object Type",
|
||||||
items=((s, s.capitalize(), "") for s in get_args(tool.Debug.PurgeMergeObjectType)),
|
items=((s, s.capitalize(), "") for s in get_args(tool.Debug.PurgeMergeObjectType)),
|
||||||
)
|
)
|
||||||
@@ -1073,7 +1073,7 @@ class ChangeLogLevel(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
bl_description = "Change general log level across all Python code in Blender"
|
bl_description = "Change general log level across all Python code in Blender"
|
||||||
|
|
||||||
log_level: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
log_level: bpy.props.EnumProperty(
|
||||||
name="Log Level",
|
name="Log Level",
|
||||||
items=[(i, i, "") for i in get_args(LogLevelType)],
|
items=[(i, i, "") for i in get_args(LogLevelType)],
|
||||||
default="WARNING",
|
default="WARNING",
|
||||||
|
|||||||
@@ -246,17 +246,17 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
+ "Add the CTRL modifier to optionally open drawings to view them as\n"
|
+ "Add the CTRL modifier to optionally open drawings to view them as\n"
|
||||||
+ "they are created"
|
+ "they are created"
|
||||||
)
|
)
|
||||||
print_all: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
print_all: bpy.props.BoolProperty(
|
||||||
name="Print All",
|
name="Print All",
|
||||||
default=False,
|
default=False,
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
)
|
)
|
||||||
open_viewer: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
open_viewer: bpy.props.BoolProperty(
|
||||||
name="Open in Viewer",
|
name="Open in Viewer",
|
||||||
default=False,
|
default=False,
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
)
|
)
|
||||||
sync: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
sync: bpy.props.BoolProperty(
|
||||||
name="Sync Before Creating Drawing",
|
name="Sync Before Creating Drawing",
|
||||||
description="Could save some time if you're sure IFC and current Blender session are already in sync",
|
description="Could save some time if you're sure IFC and current Blender session are already in sync",
|
||||||
default=True,
|
default=True,
|
||||||
@@ -2322,14 +2322,14 @@ class ActivateDrawingBase(tool.Ifc.Operator):
|
|||||||
+ "SHIFT+CLICK to load a quick preview of the drawing view"
|
+ "SHIFT+CLICK to load a quick preview of the drawing view"
|
||||||
)
|
)
|
||||||
|
|
||||||
drawing: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
drawing: bpy.props.IntProperty()
|
||||||
should_view_from_camera: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
should_view_from_camera: bpy.props.BoolProperty(
|
||||||
name="Should View From Camera",
|
name="Should View From Camera",
|
||||||
description="Move view to the activated drawing's camera position.",
|
description="Move view to the activated drawing's camera position.",
|
||||||
default=True,
|
default=True,
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
)
|
)
|
||||||
use_quick_preview: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
use_quick_preview: bpy.props.BoolProperty(
|
||||||
name="Use Quick Preview",
|
name="Use Quick Preview",
|
||||||
description="Just move the camera to the drawing view, without loading anything else.",
|
description="Just move the camera to the drawing view, without loading anything else.",
|
||||||
default=False,
|
default=False,
|
||||||
@@ -3635,14 +3635,12 @@ class ToggleTargetView(bpy.types.Operator):
|
|||||||
bl_label = "Toggle Target View"
|
bl_label = "Toggle Target View"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
target_view: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
target_view: bpy.props.StringProperty()
|
||||||
toggle_all: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
toggle_all: bpy.props.BoolProperty(
|
||||||
default=False,
|
default=False,
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
)
|
)
|
||||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
option: bpy.props.EnumProperty(items=[(i, i, "") for i in get_args(ToggleOption)])
|
||||||
items=[(i, i, "") for i in get_args(ToggleOption)]
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
target_view: str
|
target_view: str
|
||||||
|
|||||||
@@ -860,13 +860,13 @@ class BIMTextProperties(PropertyGroup):
|
|||||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||||
literals: CollectionProperty(name="Literals", type=LiteralProps)
|
literals: CollectionProperty(name="Literals", type=LiteralProps)
|
||||||
newline_at: IntProperty(name="Newline At")
|
newline_at: IntProperty(name="Newline At")
|
||||||
symbol: EnumProperty( # pyright: ignore[reportRedeclaration]
|
symbol: EnumProperty(
|
||||||
name="Symbol",
|
name="Symbol",
|
||||||
description="Symbol from symbols.svg to use for this text.",
|
description="Symbol from symbols.svg to use for this text.",
|
||||||
items=[(s, s, "") for s in ["NO SYMBOL", "CUSTOM SYMBOL"] + tool.Drawing.DEFAULT_SYMBOLS],
|
items=[(s, s, "") for s in ["NO SYMBOL", "CUSTOM SYMBOL"] + tool.Drawing.DEFAULT_SYMBOLS],
|
||||||
default="NO SYMBOL",
|
default="NO SYMBOL",
|
||||||
)
|
)
|
||||||
custom_symbol: StringProperty( # pyright: ignore[reportRedeclaration]
|
custom_symbol: StringProperty(
|
||||||
name="Custom Symbol",
|
name="Custom Symbol",
|
||||||
description="Non-default symbol to use for this text.",
|
description="Non-default symbol to use for this text.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class EditObjectPlacement(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.override_mesh_separate"
|
bl_idname = "bim.override_mesh_separate"
|
||||||
bl_label = "IFC Mesh Separate"
|
bl_label = "IFC Mesh Separate"
|
||||||
blender_op = bpy.ops.mesh.separate.get_rna_type()
|
blender_op = bpy.ops.mesh.separate.get_rna_type() # ty: ignore[missing-argument]
|
||||||
bl_description = blender_op.description + ".\nAlso makes sure changes are in sync with IFC."
|
bl_description = blender_op.description + ".\nAlso makes sure changes are in sync with IFC."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
blender_type_prop = blender_op.properties["type"]
|
blender_type_prop = blender_op.properties["type"]
|
||||||
@@ -246,7 +246,7 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
class OverrideOriginSet(bpy.types.Operator, tool.Ifc.Operator):
|
class OverrideOriginSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.override_origin_set"
|
bl_idname = "bim.override_origin_set"
|
||||||
blender_op = bpy.ops.object.origin_set.get_rna_type()
|
blender_op = bpy.ops.object.origin_set.get_rna_type() # ty: ignore[missing-argument]
|
||||||
bl_label = "IFC Origin Set"
|
bl_label = "IFC Origin Set"
|
||||||
bl_description = (
|
bl_description = (
|
||||||
blender_op.description + ".\nAlso makes sure changes are in sync with IFC (operator works only on IFC objects)"
|
blender_op.description + ".\nAlso makes sure changes are in sync with IFC (operator works only on IFC objects)"
|
||||||
@@ -801,7 +801,7 @@ def calc_delete_is_batch(ifc_file: ifcopenshell.file, context: bpy.types.Context
|
|||||||
class OverrideDelete(bpy.types.Operator):
|
class OverrideDelete(bpy.types.Operator):
|
||||||
bl_idname = "bim.override_object_delete"
|
bl_idname = "bim.override_object_delete"
|
||||||
bl_label = "IFC Delete"
|
bl_label = "IFC Delete"
|
||||||
blender_op = bpy.ops.object.delete.get_rna_type()
|
blender_op = bpy.ops.object.delete.get_rna_type() # ty: ignore[missing-argument]
|
||||||
bl_description = (
|
bl_description = (
|
||||||
blender_op.description
|
blender_op.description
|
||||||
+ ".\nAlso makes sure changes in sync with IFC."
|
+ ".\nAlso makes sure changes in sync with IFC."
|
||||||
@@ -821,7 +821,7 @@ class OverrideDelete(bpy.types.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
# Match `object.delete` poll for consistency.
|
# Match `object.delete` poll for consistency.
|
||||||
# `object.delete` poll just checks for OBJECT mode.
|
# `object.delete` poll just checks for OBJECT mode.
|
||||||
poll = bpy.ops.object.delete.poll()
|
poll = bpy.ops.object.delete.poll() # ty: ignore[missing-argument]
|
||||||
if poll:
|
if poll:
|
||||||
return True
|
return True
|
||||||
cls.poll_message_set("Only available in OBJECT mode")
|
cls.poll_message_set("Only available in OBJECT mode")
|
||||||
@@ -1045,7 +1045,7 @@ class SelectedIdsData(NamedTuple):
|
|||||||
class OverrideOutlinerDelete(bpy.types.Operator, tool.Ifc.Operator):
|
class OverrideOutlinerDelete(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.override_outliner_delete"
|
bl_idname = "bim.override_outliner_delete"
|
||||||
bl_label = "IFC Delete"
|
bl_label = "IFC Delete"
|
||||||
blender_op = bpy.ops.outliner.delete.get_rna_type()
|
blender_op = bpy.ops.outliner.delete.get_rna_type() # ty: ignore[missing-argument]
|
||||||
bl_description = (
|
bl_description = (
|
||||||
blender_op.description
|
blender_op.description
|
||||||
+ ".\nAlso makes sure changes in sync with IFC."
|
+ ".\nAlso makes sure changes in sync with IFC."
|
||||||
@@ -1060,7 +1060,7 @@ class OverrideOutlinerDelete(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context) -> bool:
|
def poll(cls, context) -> bool:
|
||||||
# Match `outliner.delete` poll for consistency.
|
# Match `outliner.delete` poll for consistency.
|
||||||
# `outliner.delete` just checks `area.type` == `OUTLINER`.
|
# `outliner.delete` just checks `area.type` == `OUTLINER`.
|
||||||
poll = bpy.ops.outliner.delete.poll()
|
poll = bpy.ops.outliner.delete.poll() # ty: ignore[missing-argument]
|
||||||
if poll:
|
if poll:
|
||||||
return True
|
return True
|
||||||
cls.poll_message_set("Only available from Outliner.")
|
cls.poll_message_set("Only available from Outliner.")
|
||||||
@@ -1164,7 +1164,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
|||||||
def poll(cls, context) -> bool:
|
def poll(cls, context) -> bool:
|
||||||
# Match `object.duplicate_move` poll for consistency.
|
# Match `object.duplicate_move` poll for consistency.
|
||||||
# `object.duplicate_move` poll checks for OBJECT mode.
|
# `object.duplicate_move` poll checks for OBJECT mode.
|
||||||
poll = bpy.ops.object.duplicate_move.poll()
|
poll = bpy.ops.object.duplicate_move.poll() # ty: ignore[missing-argument]
|
||||||
if poll:
|
if poll:
|
||||||
return True
|
return True
|
||||||
cls.poll_message_set("Only available in OBJECT mode")
|
cls.poll_message_set("Only available in OBJECT mode")
|
||||||
@@ -1908,7 +1908,7 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.override_object_join"
|
bl_idname = "bim.override_object_join"
|
||||||
bl_label = "IFC Join"
|
bl_label = "IFC Join"
|
||||||
blender_op = bpy.ops.mesh.separate.get_rna_type()
|
blender_op = bpy.ops.mesh.separate.get_rna_type() # ty: ignore[missing-argument]
|
||||||
bl_description = (
|
bl_description = (
|
||||||
blender_op.description
|
blender_op.description
|
||||||
+ ".\nAlso makes sure changes are in sync with IFC."
|
+ ".\nAlso makes sure changes are in sync with IFC."
|
||||||
@@ -1926,7 +1926,7 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.ops.object.join.poll():
|
if not bpy.ops.object.join.poll(): # ty: ignore[missing-argument]
|
||||||
cls.poll_message_set("Active object is not EDITable.")
|
cls.poll_message_set("Active object is not EDITable.")
|
||||||
return False
|
return False
|
||||||
if not context.selected_editable_objects:
|
if not context.selected_editable_objects:
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ class ToggleGroup(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Toggle Group"
|
bl_label = "Toggle Group"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
ifc_definition_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
ifc_definition_id: bpy.props.IntProperty()
|
||||||
group_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
group_type: bpy.props.EnumProperty(
|
||||||
items=[(i, i, "") for i in get_args(tool.Group.GroupType)],
|
items=[(i, i, "") for i in get_args(tool.Group.GroupType)],
|
||||||
)
|
)
|
||||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
option: bpy.props.EnumProperty(
|
||||||
items=[(i, i, "") for i in get_args(tool.Group.ToggleOption)],
|
items=[(i, i, "") for i in get_args(tool.Group.ToggleOption)],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ class SelectConflictEntity(bpy.types.Operator):
|
|||||||
bl_idname = "ifcgit.select_conflict_entity"
|
bl_idname = "ifcgit.select_conflict_entity"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
step_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
step_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
step_id: int
|
step_id: int
|
||||||
@@ -515,7 +515,7 @@ class RunGitDiff(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
bl_options = set()
|
bl_options = set()
|
||||||
|
|
||||||
save_to_temp: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
save_to_temp: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
save_to_temp: bool
|
save_to_temp: bool
|
||||||
@@ -547,7 +547,7 @@ class RenameBranch(bpy.types.Operator):
|
|||||||
bl_idname = "ifcgit.rename_branch"
|
bl_idname = "ifcgit.rename_branch"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
new_name: bpy.props.StringProperty(name="New name") # pyright: ignore[reportRedeclaration]
|
new_name: bpy.props.StringProperty(name="New name")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
new_name: str
|
new_name: str
|
||||||
|
|||||||
@@ -272,21 +272,21 @@ class RadianceRender(bpy.types.Operator):
|
|||||||
+ '''" map_u map_v
|
+ '''" map_u map_v
|
||||||
0
|
0
|
||||||
1 0.5
|
1 0.5
|
||||||
|
|
||||||
# This is a multiplier to colour balance the env map
|
# This is a multiplier to colour balance the env map
|
||||||
# In this case, it provides a rough ground luminance from 3k-5k
|
# In this case, it provides a rough ground luminance from 3k-5k
|
||||||
env_map colorfunc env_colour
|
env_map colorfunc env_colour
|
||||||
4 100 100 100 .
|
4 100 100 100 .
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
|
|
||||||
# .37 .57 1.5 is measured from a HDRI image
|
# .37 .57 1.5 is measured from a HDRI image
|
||||||
# It is multiplied by a factor such that grey(r,g,b) = 1
|
# It is multiplied by a factor such that grey(r,g,b) = 1
|
||||||
skyfunc colorfunc sky_colour
|
skyfunc colorfunc sky_colour
|
||||||
4 .64 .99 2.6 .
|
4 .64 .99 2.6 .
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
|
|
||||||
void mixpict composite
|
void mixpict composite
|
||||||
7 env_colour sky_colour grey "'''
|
7 env_colour sky_colour grey "'''
|
||||||
+ hdr_mask_path
|
+ hdr_mask_path
|
||||||
@@ -295,22 +295,22 @@ void mixpict composite
|
|||||||
+ """" map_u map_v
|
+ """" map_u map_v
|
||||||
0
|
0
|
||||||
2 0.5 1
|
2 0.5 1
|
||||||
|
|
||||||
composite glow env_map_glow
|
composite glow env_map_glow
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
4 1 1 1 0
|
4 1 1 1 0
|
||||||
|
|
||||||
env_map_glow source sky
|
env_map_glow source sky
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
4 0 0 1 180
|
4 0 0 1 180
|
||||||
|
|
||||||
env_colour glow ground_glow
|
env_colour glow ground_glow
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
4 1 1 1 0
|
4 1 1 1 0
|
||||||
|
|
||||||
ground_glow source ground
|
ground_glow source ground
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
@@ -566,7 +566,7 @@ class LightPickCoordinates(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
use_current_location: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
use_current_location: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
use_current_location: bool
|
use_current_location: bool
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
"Will unassign element from a type if type has a representation."
|
"Will unassign element from a type if type has a representation."
|
||||||
)
|
)
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
mode: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
mode: bpy.props.EnumProperty(
|
||||||
default="BOOLEAN",
|
default="BOOLEAN",
|
||||||
items=tuple((i, i, "") for i in get_args(SplitAlongEdgeMode)),
|
items=tuple((i, i, "") for i in get_args(SplitAlongEdgeMode)),
|
||||||
)
|
)
|
||||||
@@ -359,7 +359,7 @@ class ConfirmQuickFavoriteOperator(bpy.types.Operator):
|
|||||||
bl_idname = "bim.confirm_quick_favorite_operator"
|
bl_idname = "bim.confirm_quick_favorite_operator"
|
||||||
bl_label = "Confirm Operator"
|
bl_label = "Confirm Operator"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
index: int
|
index: int
|
||||||
@@ -452,10 +452,8 @@ class MoveQuickFavoritesItem(bpy.types.Operator):
|
|||||||
bl_idname = "bim.move_quick_favorites_item"
|
bl_idname = "bim.move_quick_favorites_item"
|
||||||
bl_label = "Move Quick Favorites Item"
|
bl_label = "Move Quick Favorites Item"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
index: bpy.props.IntProperty()
|
||||||
direction: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
direction: bpy.props.EnumProperty(items=[("UP", "Up", ""), ("DOWN", "Down", "")])
|
||||||
items=[("UP", "Up", ""), ("DOWN", "Down", "")]
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
index: int
|
index: int
|
||||||
@@ -474,7 +472,7 @@ class RemoveQuickFavoritesItem(bpy.types.Operator):
|
|||||||
bl_idname = "bim.remove_quick_favorites_item"
|
bl_idname = "bim.remove_quick_favorites_item"
|
||||||
bl_label = "Remove Quick Favorites Item"
|
bl_label = "Remove Quick Favorites Item"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
index: int
|
index: int
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ QuickFavoriteValueType = Literal["float_value", "bool_value", "int_value", "stri
|
|||||||
|
|
||||||
|
|
||||||
class QuickFavoriteEnumItem(PropertyGroup):
|
class QuickFavoriteEnumItem(PropertyGroup):
|
||||||
name: StringProperty(name="Name", default="") # pyright: ignore[reportRedeclaration]
|
name: StringProperty(name="Name", default="")
|
||||||
display_name: StringProperty(name="Display Name", default="") # pyright: ignore[reportRedeclaration]
|
display_name: StringProperty(name="Display Name", default="")
|
||||||
description: StringProperty(name="Description", default="") # pyright: ignore[reportRedeclaration]
|
description: StringProperty(name="Description", default="")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
name: str
|
name: str
|
||||||
@@ -51,19 +51,19 @@ def get_enum_items(self: "QuickFavoriteProperty", context: bpy.types.Context | N
|
|||||||
|
|
||||||
|
|
||||||
class QuickFavoriteProperty(PropertyGroup):
|
class QuickFavoriteProperty(PropertyGroup):
|
||||||
name: StringProperty(name="Name", default="") # pyright: ignore[reportRedeclaration]
|
name: StringProperty(name="Name", default="")
|
||||||
display_name: StringProperty(name="Display Name", default="") # pyright: ignore[reportRedeclaration]
|
display_name: StringProperty(name="Display Name", default="")
|
||||||
value_prop: EnumProperty( # pyright: ignore[reportRedeclaration]
|
value_prop: EnumProperty(
|
||||||
name="Value Prop",
|
name="Value Prop",
|
||||||
items=tuple((v, v, "") for v in get_args(QuickFavoriteValueType)),
|
items=tuple((v, v, "") for v in get_args(QuickFavoriteValueType)),
|
||||||
)
|
)
|
||||||
string_value: StringProperty(name="String Value", default="") # pyright: ignore[reportRedeclaration]
|
string_value: StringProperty(name="String Value", default="")
|
||||||
float_value: FloatProperty(name="Float Value", default=0.0) # pyright: ignore[reportRedeclaration]
|
float_value: FloatProperty(name="Float Value", default=0.0)
|
||||||
int_value: IntProperty(name="Int Value", default=0) # pyright: ignore[reportRedeclaration]
|
int_value: IntProperty(name="Int Value", default=0)
|
||||||
bool_value: BoolProperty(name="Bool Value", default=False) # pyright: ignore[reportRedeclaration]
|
bool_value: BoolProperty(name="Bool Value", default=False)
|
||||||
enum_value: EnumProperty(name="Enum Value", items=get_enum_items) # pyright: ignore[reportRedeclaration]
|
enum_value: EnumProperty(name="Enum Value", items=get_enum_items)
|
||||||
enum_items: CollectionProperty(type=QuickFavoriteEnumItem) # pyright: ignore[reportRedeclaration]
|
enum_items: CollectionProperty(type=QuickFavoriteEnumItem)
|
||||||
is_active: BoolProperty( # pyright: ignore[reportRedeclaration]
|
is_active: BoolProperty(
|
||||||
name="Is Active",
|
name="Is Active",
|
||||||
description="Only active properties will be added to the operator when invoked from Quick Favorites",
|
description="Only active properties will be added to the operator when invoked from Quick Favorites",
|
||||||
default=False,
|
default=False,
|
||||||
@@ -100,20 +100,20 @@ def get_operator_suggestions(self: "QuickFavoritesItem", context: bpy.types.Cont
|
|||||||
|
|
||||||
|
|
||||||
class QuickFavoritesItem(PropertyGroup):
|
class QuickFavoritesItem(PropertyGroup):
|
||||||
is_expanded: BoolProperty(name="Is Expanded", default=False) # pyright: ignore[reportRedeclaration]
|
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||||
search: StringProperty( # pyright: ignore[reportRedeclaration]
|
search: StringProperty(
|
||||||
name="Search",
|
name="Search",
|
||||||
default="",
|
default="",
|
||||||
search=get_operator_suggestions,
|
search=get_operator_suggestions,
|
||||||
# Resetting `search_options`, allowing users only to use suggestions.
|
# Resetting `search_options`, allowing users only to use suggestions.
|
||||||
search_options=set(),
|
search_options=set(),
|
||||||
)
|
)
|
||||||
properties: CollectionProperty(type=QuickFavoriteProperty) # pyright: ignore[reportRedeclaration]
|
properties: CollectionProperty(type=QuickFavoriteProperty)
|
||||||
operator_id: StringProperty( # pyright: ignore[reportRedeclaration]
|
operator_id: StringProperty(
|
||||||
name="Operator ID",
|
name="Operator ID",
|
||||||
default="",
|
default="",
|
||||||
)
|
)
|
||||||
label: StringProperty( # pyright: ignore[reportRedeclaration]
|
label: StringProperty(
|
||||||
name="Label",
|
name="Label",
|
||||||
description="Label that will be used in Quick Favorites for this operator",
|
description="Label that will be used in Quick Favorites for this operator",
|
||||||
default="",
|
default="",
|
||||||
@@ -139,15 +139,15 @@ class QuickFavoritesItem(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class BIMMiscProperties(PropertyGroup):
|
class BIMMiscProperties(PropertyGroup):
|
||||||
total_storeys: IntProperty( # pyright: ignore[reportRedeclaration]
|
total_storeys: IntProperty(
|
||||||
name="Total Storeys",
|
name="Total Storeys",
|
||||||
description="Number of storeys above object's storey to take into account for resizing",
|
description="Number of storeys above object's storey to take into account for resizing",
|
||||||
default=1,
|
default=1,
|
||||||
)
|
)
|
||||||
override_colour: FloatVectorProperty( # pyright: ignore[reportRedeclaration]
|
override_colour: FloatVectorProperty(
|
||||||
name="Override Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
|
name="Override Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
|
||||||
)
|
)
|
||||||
quick_favorites: CollectionProperty(type=QuickFavoritesItem) # pyright: ignore[reportRedeclaration]
|
quick_favorites: CollectionProperty(type=QuickFavoritesItem)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
total_storeys: int
|
total_storeys: int
|
||||||
|
|||||||
@@ -545,7 +545,7 @@ class ChangeTypePage(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.change_type_page"
|
bl_idname = "bim.change_type_page"
|
||||||
bl_label = "Change Type Page"
|
bl_label = "Change Type Page"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
page: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
page: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
page: int
|
page: int
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ class ExtendProfile(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.extend_profile"
|
bl_idname = "bim.extend_profile"
|
||||||
bl_label = "Extend Profile"
|
bl_label = "Extend Profile"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
join_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
join_type: bpy.props.EnumProperty(
|
||||||
items=[("-", "Unjoin", ""), ("L", "L", ""), ("V", "V", ""), ("T", "T", "")],
|
items=[("-", "Unjoin", ""), ("L", "L", ""), ("V", "V", ""), ("T", "T", "")],
|
||||||
default="-",
|
default="-",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1729,20 +1729,20 @@ def poll_sverchok_nodes(self: "BIMExternalParametricGeometryProperties", node_tr
|
|||||||
|
|
||||||
|
|
||||||
class BIMExternalParametricGeometryProperties(bpy.types.PropertyGroup):
|
class BIMExternalParametricGeometryProperties(bpy.types.PropertyGroup):
|
||||||
is_editing: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
is_editing: bpy.props.BoolProperty(
|
||||||
name="Is Editing Paramteric Geometry",
|
name="Is Editing Paramteric Geometry",
|
||||||
description="Toggle editing parametric geometry.",
|
description="Toggle editing parametric geometry.",
|
||||||
default=False,
|
default=False,
|
||||||
update=update_is_editing,
|
update=update_is_editing,
|
||||||
)
|
)
|
||||||
geometry_source: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
geometry_source: bpy.props.EnumProperty(
|
||||||
name="Geometry Source",
|
name="Geometry Source",
|
||||||
items=[
|
items=[
|
||||||
("GEONODES", "Geometry Nodes", ""),
|
("GEONODES", "Geometry Nodes", ""),
|
||||||
("IFCSVERCHOK", "IFC Sverchok", ""),
|
("IFCSVERCHOK", "IFC Sverchok", ""),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
geo_nodes: bpy.props.PointerProperty( # pyright: ignore[reportRedeclaration]
|
geo_nodes: bpy.props.PointerProperty(
|
||||||
name="Geometry Nodes",
|
name="Geometry Nodes",
|
||||||
description="Geometry nodes tree to use as a source for representation.",
|
description="Geometry nodes tree to use as a source for representation.",
|
||||||
type=bpy.types.GeometryNodeTree,
|
type=bpy.types.GeometryNodeTree,
|
||||||
@@ -1750,7 +1750,7 @@ class BIMExternalParametricGeometryProperties(bpy.types.PropertyGroup):
|
|||||||
poll=lambda self, node_tree: not node_tree.name.startswith("BBIM_EPG"),
|
poll=lambda self, node_tree: not node_tree.name.startswith("BBIM_EPG"),
|
||||||
)
|
)
|
||||||
|
|
||||||
sverchok_nodes: bpy.props.PointerProperty( # pyright: ignore[reportRedeclaration]
|
sverchok_nodes: bpy.props.PointerProperty(
|
||||||
name="Sverchok Nodes",
|
name="Sverchok Nodes",
|
||||||
description="Sverchok node tree to use as a source for representation.",
|
description="Sverchok node tree to use as a source for representation.",
|
||||||
type=bpy.types.NodeTree,
|
type=bpy.types.NodeTree,
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class NestDecorator:
|
|||||||
cls.is_installed = False
|
cls.is_installed = False
|
||||||
|
|
||||||
def dotted_line_shader(self):
|
def dotted_line_shader(self):
|
||||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||||
vert_out.smooth("FLOAT", "v_ArcLength")
|
vert_out.smooth("FLOAT", "v_ArcLength")
|
||||||
|
|
||||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class EnableEditingPerson(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_person"
|
bl_idname = "bim.enable_editing_person"
|
||||||
bl_label = "Enable Editing Person"
|
bl_label = "Enable Editing Person"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
person: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
person: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
person: int
|
person: int
|
||||||
@@ -75,7 +75,7 @@ class RemovePerson(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.remove_person"
|
bl_idname = "bim.remove_person"
|
||||||
bl_label = "Remove Person"
|
bl_label = "Remove Person"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
person: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
person: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
person: int
|
person: int
|
||||||
@@ -88,7 +88,7 @@ class AddPersonAttribute(bpy.types.Operator):
|
|||||||
bl_idname = "bim.add_person_attribute"
|
bl_idname = "bim.add_person_attribute"
|
||||||
bl_label = "Add Person Attribute"
|
bl_label = "Add Person Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
name: bpy.props.EnumProperty(
|
||||||
items=tuple((i, i, "") for i in get_args(tool.Owner.PersonAttributeType)),
|
items=tuple((i, i, "") for i in get_args(tool.Owner.PersonAttributeType)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -104,10 +104,10 @@ class RemovePersonAttribute(bpy.types.Operator):
|
|||||||
bl_idname = "bim.remove_person_attribute"
|
bl_idname = "bim.remove_person_attribute"
|
||||||
bl_label = "Remove Person Attribute"
|
bl_label = "Remove Person Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
name: bpy.props.EnumProperty(
|
||||||
items=tuple((i, i, "") for i in get_args(tool.Owner.PersonAttributeType)),
|
items=tuple((i, i, "") for i in get_args(tool.Owner.PersonAttributeType)),
|
||||||
)
|
)
|
||||||
id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
name: tool.Owner.PersonAttributeType # pyright: ignore[reportIncompatibleVariableOverride]
|
name: tool.Owner.PersonAttributeType # pyright: ignore[reportIncompatibleVariableOverride]
|
||||||
@@ -122,7 +122,7 @@ class EnableEditingRole(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_role"
|
bl_idname = "bim.enable_editing_role"
|
||||||
bl_label = "Enable Editing Role"
|
bl_label = "Enable Editing Role"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
role: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
role: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
role: int
|
role: int
|
||||||
@@ -146,7 +146,7 @@ class AddRole(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.add_role"
|
bl_idname = "bim.add_role"
|
||||||
bl_label = "Add Role"
|
bl_label = "Add Role"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
parent: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
parent: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
parent: int
|
parent: int
|
||||||
@@ -168,7 +168,7 @@ class RemoveRole(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.remove_role"
|
bl_idname = "bim.remove_role"
|
||||||
bl_label = "Remove Role"
|
bl_label = "Remove Role"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
role: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
role: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
role: int
|
role: int
|
||||||
@@ -181,8 +181,8 @@ class AddAddress(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.add_address"
|
bl_idname = "bim.add_address"
|
||||||
bl_label = "Add Address"
|
bl_label = "Add Address"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
parent: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
parent: bpy.props.IntProperty()
|
||||||
ifc_class: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
ifc_class: bpy.props.EnumProperty(
|
||||||
items=tuple((i, i, "") for i in get_args(ADDRESS_TYPE)),
|
items=tuple((i, i, "") for i in get_args(ADDRESS_TYPE)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ class AddAddressAttribute(bpy.types.Operator):
|
|||||||
bl_idname = "bim.add_address_attribute"
|
bl_idname = "bim.add_address_attribute"
|
||||||
bl_label = "Add Address Attribute"
|
bl_label = "Add Address Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
name: bpy.props.EnumProperty(
|
||||||
items=tuple((i, i, "") for i in get_args(tool.Owner.AddressAttributeType)),
|
items=tuple((i, i, "") for i in get_args(tool.Owner.AddressAttributeType)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -214,10 +214,10 @@ class RemoveAddressAttribute(bpy.types.Operator):
|
|||||||
bl_idname = "bim.remove_address_attribute"
|
bl_idname = "bim.remove_address_attribute"
|
||||||
bl_label = "Remove Address Attribute"
|
bl_label = "Remove Address Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
name: bpy.props.EnumProperty(
|
||||||
items=tuple((i, i, "") for i in get_args(tool.Owner.AddressAttributeType)),
|
items=tuple((i, i, "") for i in get_args(tool.Owner.AddressAttributeType)),
|
||||||
)
|
)
|
||||||
id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
name: tool.Owner.AddressAttributeType # pyright: ignore[reportIncompatibleVariableOverride]
|
name: tool.Owner.AddressAttributeType # pyright: ignore[reportIncompatibleVariableOverride]
|
||||||
@@ -232,7 +232,7 @@ class EnableEditingAddress(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_address"
|
bl_idname = "bim.enable_editing_address"
|
||||||
bl_label = "Enable Editing Address"
|
bl_label = "Enable Editing Address"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
address: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
address: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
address: int
|
address: int
|
||||||
@@ -265,7 +265,7 @@ class RemoveAddress(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.remove_address"
|
bl_idname = "bim.remove_address"
|
||||||
bl_label = "Remove Address"
|
bl_label = "Remove Address"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
address: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
address: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
address: int
|
address: int
|
||||||
@@ -278,7 +278,7 @@ class EnableEditingOrganisation(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_organisation"
|
bl_idname = "bim.enable_editing_organisation"
|
||||||
bl_label = "Enable Editing Organisation"
|
bl_label = "Enable Editing Organisation"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
organisation: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
organisation: int
|
organisation: int
|
||||||
@@ -320,7 +320,7 @@ class RemoveOrganisation(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.remove_organisation"
|
bl_idname = "bim.remove_organisation"
|
||||||
bl_label = "Remove Organisation"
|
bl_label = "Remove Organisation"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
organisation: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
organisation: int
|
organisation: int
|
||||||
@@ -333,8 +333,8 @@ class AddPersonAndOrganisation(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.add_person_and_organisation"
|
bl_idname = "bim.add_person_and_organisation"
|
||||||
bl_label = "Add Person And Organisation"
|
bl_label = "Add Person And Organisation"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
person: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
person: bpy.props.IntProperty()
|
||||||
organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
organisation: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
person: int
|
person: int
|
||||||
@@ -350,7 +350,7 @@ class RemovePersonAndOrganisation(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.remove_person_and_organisation"
|
bl_idname = "bim.remove_person_and_organisation"
|
||||||
bl_label = "Remove Person And Organisation"
|
bl_label = "Remove Person And Organisation"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
person_and_organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
person_and_organisation: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
person_and_organisation: int
|
person_and_organisation: int
|
||||||
@@ -365,7 +365,7 @@ class SetUser(bpy.types.Operator):
|
|||||||
bl_idname = "bim.set_user"
|
bl_idname = "bim.set_user"
|
||||||
bl_label = "Set User"
|
bl_label = "Set User"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
user: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
user: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
user: int
|
user: int
|
||||||
@@ -401,7 +401,7 @@ class EnableEditingActor(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_actor"
|
bl_idname = "bim.enable_editing_actor"
|
||||||
bl_label = "Enable Editing Actor"
|
bl_label = "Enable Editing Actor"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
actor: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
actor: int
|
actor: int
|
||||||
@@ -434,7 +434,7 @@ class RemoveActor(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.remove_actor"
|
bl_idname = "bim.remove_actor"
|
||||||
bl_label = "Remove Actor"
|
bl_label = "Remove Actor"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
actor: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
actor: int
|
actor: int
|
||||||
@@ -447,7 +447,7 @@ class AssignActor(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.assign_actor"
|
bl_idname = "bim.assign_actor"
|
||||||
bl_label = "Assign Actor"
|
bl_label = "Assign Actor"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
actor: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
actor: int
|
actor: int
|
||||||
@@ -462,7 +462,7 @@ class UnassignActor(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.unassign_actor"
|
bl_idname = "bim.unassign_actor"
|
||||||
bl_label = "Unassign Actor"
|
bl_label = "Unassign Actor"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
actor: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
actor: int
|
actor: int
|
||||||
@@ -481,7 +481,7 @@ class RemoveApplication(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
"Remove provided IfcApplication."
|
"Remove provided IfcApplication."
|
||||||
"\n\nFor safety will only work on applications without inverses (they are typically marked as '(unused)'."
|
"\n\nFor safety will only work on applications without inverses (they are typically marked as '(unused)'."
|
||||||
)
|
)
|
||||||
application_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
application_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
application_id: int
|
application_id: int
|
||||||
@@ -525,7 +525,7 @@ class EnableEditingApplication(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_application"
|
bl_idname = "bim.enable_editing_application"
|
||||||
bl_label = "Enable Editing Application"
|
bl_label = "Enable Editing Application"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
application_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
application_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
application_id: int
|
application_id: int
|
||||||
|
|||||||
@@ -86,9 +86,7 @@ class NewProject(bpy.types.Operator):
|
|||||||
bl_label = "New Project"
|
bl_label = "New Project"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Start a new IFC project in a fresh session"
|
bl_description = "Start a new IFC project in a fresh session"
|
||||||
preset: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
preset: bpy.props.EnumProperty(items=[(i, i, "") for i in get_args(PresetType)])
|
||||||
items=[(i, i, "") for i in get_args(PresetType)]
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
preset: PresetType
|
preset: PresetType
|
||||||
@@ -178,13 +176,9 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|||||||
bl_description = (
|
bl_description = (
|
||||||
"Select an IFC file that can be used as a library.\n\nALT+click to reload the current loaded library file."
|
"Select an IFC file that can be used as a library.\n\nALT+click to reload the current loaded library file."
|
||||||
)
|
)
|
||||||
filter_glob: bpy.props.StringProperty(
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||||
default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}
|
append_all: bpy.props.BoolProperty(default=False)
|
||||||
) # pyright: ignore[reportRedeclaration]
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||||
append_all: bpy.props.BoolProperty(default=False) # pyright: ignore[reportRedeclaration]
|
|
||||||
use_relative_path: bpy.props.BoolProperty(
|
|
||||||
name="Use Relative Path", default=False
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
filter_glob: str
|
filter_glob: str
|
||||||
@@ -568,7 +562,7 @@ class AppendLibraryElementByQuery(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.append_library_element_by_query"
|
bl_idname = "bim.append_library_element_by_query"
|
||||||
bl_label = "Append Library Element By Query"
|
bl_label = "Append Library Element By Query"
|
||||||
|
|
||||||
query: bpy.props.StringProperty(name="Query") # pyright: ignore[reportRedeclaration]
|
query: bpy.props.StringProperty(name="Query")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
query: str
|
query: str
|
||||||
@@ -600,11 +594,9 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
"Append element to the current project.\n\n"
|
"Append element to the current project.\n\n"
|
||||||
"ALT+CLICK to skip reusing materials, profiles, styles based on their name (may result in duplicates)"
|
"ALT+CLICK to skip reusing materials, profiles, styles based on their name (may result in duplicates)"
|
||||||
)
|
)
|
||||||
definition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
definition: bpy.props.IntProperty()
|
||||||
prop_index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
prop_index: bpy.props.IntProperty()
|
||||||
assume_unique_by_name: bpy.props.BoolProperty(
|
assume_unique_by_name: bpy.props.BoolProperty(name="Assume Unique By Name", default=True, options={"SKIP_SAVE"})
|
||||||
name="Assume Unique By Name", default=True, options={"SKIP_SAVE"}
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
definition: int
|
definition: int
|
||||||
@@ -959,28 +951,24 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|||||||
bl_label = "Load Project"
|
bl_label = "Load Project"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Load an existing IFC project"
|
bl_description = "Load an existing IFC project"
|
||||||
filepath: bpy.props.StringProperty(
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE"})
|
||||||
subtype="FILE_PATH", options={"SKIP_SAVE"}
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
|
||||||
) # pyright: ignore[reportRedeclaration]
|
is_advanced: bpy.props.BoolProperty(
|
||||||
filter_glob: bpy.props.StringProperty(
|
|
||||||
default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"}
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
is_advanced: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
|
||||||
name="Enable Advanced Mode",
|
name="Enable Advanced Mode",
|
||||||
description="Load IFC file with advanced settings. Checking this option will skip loading IFC file and will open advanced load settings",
|
description="Load IFC file with advanced settings. Checking this option will skip loading IFC file and will open advanced load settings",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_relative_path: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
use_relative_path: bpy.props.BoolProperty(
|
||||||
name="Use Relative Path",
|
name="Use Relative Path",
|
||||||
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
should_start_fresh_session: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
should_start_fresh_session: bpy.props.BoolProperty(
|
||||||
name="Should Start Fresh Session",
|
name="Should Start Fresh Session",
|
||||||
description="Clear current Blender session before loading IFC. Not supported with 'Use Relative Path' option",
|
description="Clear current Blender session before loading IFC. Not supported with 'Use Relative Path' option",
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
import_without_ifc_data: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
import_without_ifc_data: bpy.props.BoolProperty(
|
||||||
name="Import Without IFC Data",
|
name="Import Without IFC Data",
|
||||||
description=(
|
description=(
|
||||||
"Import IFC objects as Blender objects without any IFC metadata and authoring capabilities."
|
"Import IFC objects as Blender objects without any IFC metadata and authoring capabilities."
|
||||||
@@ -988,9 +976,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|||||||
),
|
),
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_detailed_tooltip: bpy.props.BoolProperty(
|
use_detailed_tooltip: bpy.props.BoolProperty(default=False, options={"HIDDEN"})
|
||||||
default=False, options={"HIDDEN"}
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -1300,7 +1286,7 @@ class ToggleFilterCategories(bpy.types.Operator):
|
|||||||
bl_idname = "bim.toggle_filter_categories"
|
bl_idname = "bim.toggle_filter_categories"
|
||||||
bl_label = "Toggle Filter Categories"
|
bl_label = "Toggle Filter Categories"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
should_select: bpy.props.BoolProperty(name="Should Select", default=True) # pyright: ignore[reportRedeclaration]
|
should_select: bpy.props.BoolProperty(name="Should Select", default=True)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
should_select: bool
|
should_select: bool
|
||||||
@@ -1327,7 +1313,7 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
||||||
query: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
query: bpy.props.StringProperty(
|
||||||
name="Query",
|
name="Query",
|
||||||
description=(
|
description=(
|
||||||
"Custom selector query to use to load element from a linked model. E.g. 'IfcElement'.\n\n"
|
"Custom selector query to use to load element from a linked model. E.g. 'IfcElement'.\n\n"
|
||||||
@@ -1404,7 +1390,7 @@ class UnlinkIfc(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Remove the selected file from the link list"
|
bl_description = "Remove the selected file from the link list"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
@@ -1428,7 +1414,7 @@ class UnloadLink(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Unload the selected linked file"
|
bl_description = "Unload the selected linked file"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
@@ -1454,9 +1440,9 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Load the selected file"
|
bl_description = "Load the selected file"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) # pyright: ignore[reportRedeclaration]
|
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
||||||
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
query: bpy.props.StringProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
@@ -1631,7 +1617,7 @@ class ReloadLink(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Reload the selected file"
|
bl_description = "Reload the selected file"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
@@ -1647,7 +1633,7 @@ class ToggleLinkSelectability(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Toggle selectability"
|
bl_description = "Toggle selectability"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
@@ -1679,8 +1665,8 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Toggle visibility between SOLID and WIREFRAME"
|
bl_description = "Toggle visibility between SOLID and WIREFRAME"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
mode: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
mode: bpy.props.EnumProperty(
|
||||||
name="Visibility Mode",
|
name="Visibility Mode",
|
||||||
items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")),
|
items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")),
|
||||||
)
|
)
|
||||||
@@ -1821,7 +1807,7 @@ class SelectLinkHandle(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Select link empty object handle"
|
bl_description = "Select link empty object handle"
|
||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
@@ -1843,7 +1829,7 @@ class SelectLinkedModelElement(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
bl_description = "Select an element in the currently selected linked model by providing GlobalId."
|
bl_description = "Select an element in the currently selected linked model by providing GlobalId."
|
||||||
|
|
||||||
guid: bpy.props.StringProperty(name="GlobalId") # pyright: ignore[reportRedeclaration]
|
guid: bpy.props.StringProperty(name="GlobalId")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
guid: str
|
guid: str
|
||||||
@@ -1882,21 +1868,11 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
supported_filexts = (".ifc", ".ifczip", ".ifcjson")
|
supported_filexts = (".ifc", ".ifczip", ".ifcjson")
|
||||||
filter_glob: bpy.props.StringProperty(
|
filter_glob: bpy.props.StringProperty(default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"})
|
||||||
default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"}
|
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
|
||||||
) # pyright: ignore[reportRedeclaration]
|
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
|
||||||
json_version: bpy.props.EnumProperty(
|
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
|
||||||
items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version"
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
json_compact: bpy.props.BoolProperty(
|
|
||||||
name="Export Compact IFCJSON", default=False
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
should_save_as: bpy.props.BoolProperty(
|
|
||||||
name="Should Save As", default=False, options={"HIDDEN"}
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
use_relative_path: bpy.props.BoolProperty(
|
|
||||||
name="Use Relative Path", default=False
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
filter_glob: str
|
filter_glob: str
|
||||||
@@ -2053,7 +2029,7 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
|
|||||||
bl_description = "Operator is used to load a project .cache.blend to then link it to the IFC file."
|
bl_description = "Operator is used to load a project .cache.blend to then link it to the IFC file."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
query: bpy.props.StringProperty()
|
||||||
"""See ``bim.link_ifc``."""
|
"""See ``bim.link_ifc``."""
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -2443,8 +2419,8 @@ class HideQueriedLinkedElement(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
unhide_all: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
unhide_all: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||||
hide_all_except: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
hide_all_except: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
unhide_all: bool
|
unhide_all: bool
|
||||||
@@ -2918,12 +2894,8 @@ class IFCFileHandlerOperator(bpy.types.Operator):
|
|||||||
bl_label = "Import .ifc file"
|
bl_label = "Import .ifc file"
|
||||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||||
|
|
||||||
directory: bpy.props.StringProperty(
|
directory: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"})
|
||||||
subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"}
|
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"})
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
files: bpy.props.CollectionProperty(
|
|
||||||
type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"}
|
|
||||||
) # pyright: ignore[reportRedeclaration]
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
directory: str
|
directory: str
|
||||||
@@ -2978,7 +2950,7 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
|
|||||||
bl_label = "Measure Tool"
|
bl_label = "Measure Tool"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
measure_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
measure_type: str
|
measure_type: str
|
||||||
@@ -3077,7 +3049,7 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
|
|||||||
bl_label = "Measure Face Area Tool"
|
bl_label = "Measure Face Area Tool"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
measure_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
measure_type: str
|
measure_type: str
|
||||||
@@ -3379,7 +3351,7 @@ class LoadBlendMetadataAndIFC(bpy.types.Operator):
|
|||||||
bl_idname = "bim.load_blend_metadata_and_ifc"
|
bl_idname = "bim.load_blend_metadata_and_ifc"
|
||||||
bl_label = "Load Blend Metadata and IFC"
|
bl_label = "Load Blend Metadata and IFC"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(name="IFC File Path", default="") # pyright: ignore[reportRedeclaration]
|
filepath: bpy.props.StringProperty(name="IFC File Path", default="")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
filepath: str
|
filepath: str
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ class BIMProjectProperties(PropertyGroup):
|
|||||||
),
|
),
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
should_cache: BoolProperty( # pyright: ignore[reportRedeclaration]
|
should_cache: BoolProperty(
|
||||||
name="Cache",
|
name="Cache",
|
||||||
description=(
|
description=(
|
||||||
"Cache loaded geometry to .h5 file in your cache directory (see in preferences) "
|
"Cache loaded geometry to .h5 file in your cache directory (see in preferences) "
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ class CopyPropertyToSelection(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Copy Property To Selection"
|
bl_label = "Copy Property To Selection"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
name: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
name: bpy.props.StringProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
name: str
|
name: str
|
||||||
@@ -280,10 +280,10 @@ class BIM_OT_add_property_to_edit(bpy.types.Operator):
|
|||||||
bl_label = "Add Property to Edit"
|
bl_label = "Add Property to Edit"
|
||||||
bl_idname = "bim.add_property_to_edit"
|
bl_idname = "bim.add_property_to_edit"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
option: bpy.props.EnumProperty(
|
||||||
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
||||||
)
|
)
|
||||||
index: bpy.props.IntProperty(default=-1) # pyright: ignore[reportRedeclaration]
|
index: bpy.props.IntProperty(default=-1)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
option: tool.Pset.BulkOperationType
|
option: tool.Pset.BulkOperationType
|
||||||
@@ -307,9 +307,9 @@ class BIM_OT_remove_property_to_edit(bpy.types.Operator):
|
|||||||
bl_label = "Remove Property from Editing"
|
bl_label = "Remove Property from Editing"
|
||||||
bl_idname = "bim.remove_property_to_edit"
|
bl_idname = "bim.remove_property_to_edit"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
index: bpy.props.IntProperty()
|
||||||
index2: bpy.props.IntProperty(default=-1) # pyright: ignore[reportRedeclaration]
|
index2: bpy.props.IntProperty(default=-1)
|
||||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
option: bpy.props.EnumProperty(
|
||||||
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -336,7 +336,7 @@ class BIM_OT_bulk_edit_clear_list(bpy.types.Operator):
|
|||||||
bl_label = "Clear List of Properties"
|
bl_label = "Clear List of Properties"
|
||||||
bl_idname = "bim.pset_bulk_edit_clear_list"
|
bl_idname = "bim.pset_bulk_edit_clear_list"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
option: bpy.props.EnumProperty(
|
||||||
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -368,9 +368,9 @@ class GlobalPsetProperties(PropertyGroup):
|
|||||||
qto_filter: StringProperty(name="Qto Filter", options={"TEXTEDIT_UPDATE"})
|
qto_filter: StringProperty(name="Qto Filter", options={"TEXTEDIT_UPDATE"})
|
||||||
|
|
||||||
# Bulk operations.
|
# Bulk operations.
|
||||||
psets_to_delete: CollectionProperty(type=DeletePsetEntry) # pyright: ignore[reportRedeclaration]
|
psets_to_delete: CollectionProperty(type=DeletePsetEntry)
|
||||||
psets_to_rename: CollectionProperty(type=RenamePropertyEntry) # pyright: ignore[reportRedeclaration]
|
psets_to_rename: CollectionProperty(type=RenamePropertyEntry)
|
||||||
psets_to_add_edit: CollectionProperty(type=AddEditPropertyEntry) # pyright: ignore[reportRedeclaration]
|
psets_to_add_edit: CollectionProperty(type=AddEditPropertyEntry)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
pset_filter: str
|
pset_filter: str
|
||||||
|
|||||||
@@ -799,7 +799,7 @@ class SelectQueryElements(Operator):
|
|||||||
bl_description = "Select elements matching an provided selector query"
|
bl_description = "Select elements matching an provided selector query"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
query: StringProperty(name="Query") # pyright: ignore[reportRedeclaration]
|
query: StringProperty(name="Query")
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
query: str
|
query: str
|
||||||
@@ -829,12 +829,12 @@ class SaveSearch(Operator, tool.Ifc.Operator):
|
|||||||
# Extra item so it will be easy to select current text.
|
# Extra item so it will be easy to select current text.
|
||||||
return [text] + SaveSearch.name_search_items
|
return [text] + SaveSearch.name_search_items
|
||||||
|
|
||||||
name: StringProperty( # pyright: ignore[reportRedeclaration]
|
name: StringProperty(
|
||||||
name="Name",
|
name="Name",
|
||||||
search=get_name_search_items,
|
search=get_name_search_items,
|
||||||
search_options={"SORT"},
|
search_options={"SORT"},
|
||||||
)
|
)
|
||||||
module: StringProperty() # pyright: ignore[reportRedeclaration]
|
module: StringProperty()
|
||||||
|
|
||||||
def update_use_all_ifcgroups(self, context: object = None) -> None:
|
def update_use_all_ifcgroups(self, context: object = None) -> None:
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
@@ -845,7 +845,7 @@ class SaveSearch(Operator, tool.Ifc.Operator):
|
|||||||
}
|
}
|
||||||
self.name_search_items[:] = natsorted(groups)
|
self.name_search_items[:] = natsorted(groups)
|
||||||
|
|
||||||
use_all_ifcgroups: BoolProperty( # pyright: ignore[reportRedeclaration]
|
use_all_ifcgroups: BoolProperty(
|
||||||
name="Use Any IfcGroup",
|
name="Use Any IfcGroup",
|
||||||
description=(
|
description=(
|
||||||
"By default we're targeting only IfcGroups with SEARCH ObjectType "
|
"By default we're targeting only IfcGroups with SEARCH ObjectType "
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class ActivateStatusFilters(bpy.types.Operator):
|
|||||||
bl_description = "Filter and display objects based on currently selected IFC statuses"
|
bl_description = "Filter and display objects based on currently selected IFC statuses"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
only_if_enabled: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
only_if_enabled: bpy.props.BoolProperty(
|
||||||
name="Only If Filters are Enabled",
|
name="Only If Filters are Enabled",
|
||||||
description="Activate status filters only in case if they were enabled from the UI before.",
|
description="Activate status filters only in case if they were enabled from the UI before.",
|
||||||
default=False,
|
default=False,
|
||||||
@@ -137,7 +137,7 @@ class SelectStatusFilter(bpy.types.Operator):
|
|||||||
bl_description = "Select elements with currently selected status"
|
bl_description = "Select elements with currently selected status"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
status: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
status: bpy.props.StringProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
status: tool.Sequence.ElementStatusUI
|
status: tool.Sequence.ElementStatusUI
|
||||||
@@ -156,7 +156,7 @@ class AssignStatus(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_description = "Assign status to the selected elements.\n\nAlt+CLICK to unassign the status."
|
bl_description = "Assign status to the selected elements.\n\nAlt+CLICK to unassign the status."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
should_override_previous_status: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
should_override_previous_status: bpy.props.BoolProperty(
|
||||||
name="Override Previous Status",
|
name="Override Previous Status",
|
||||||
description=(
|
description=(
|
||||||
"Whether assigning new status should override previous one.\n\n"
|
"Whether assigning new status should override previous one.\n\n"
|
||||||
@@ -165,8 +165,8 @@ class AssignStatus(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
),
|
),
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
status: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
status: bpy.props.StringProperty()
|
||||||
should_unassign_status: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
should_unassign_status: bpy.props.BoolProperty(
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -415,7 +415,7 @@ class CopyWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Copy Work Schedule"
|
bl_label = "Copy Work Schedule"
|
||||||
bl_description = "Create a duplicate of the provided work schedule."
|
bl_description = "Create a duplicate of the provided work schedule."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
work_schedule: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
work_schedule: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
work_schedule: int
|
work_schedule: int
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ WorkPlanEditingType = Literal["-", "ATTRIBUTES", "SCHEDULES", "WORK_SCHEDULE", "
|
|||||||
|
|
||||||
class BIMWorkPlanProperties(PropertyGroup):
|
class BIMWorkPlanProperties(PropertyGroup):
|
||||||
work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute)
|
work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute)
|
||||||
editing_type: EnumProperty( # pyright: ignore[reportRedeclaration]
|
editing_type: EnumProperty(
|
||||||
items=[(i, i, "") for i in get_args(WorkPlanEditingType)],
|
items=[(i, i, "") for i in get_args(WorkPlanEditingType)],
|
||||||
)
|
)
|
||||||
work_plans: CollectionProperty(name="Work Plans", type=WorkPlan)
|
work_plans: CollectionProperty(name="Work Plans", type=WorkPlan)
|
||||||
@@ -430,8 +430,8 @@ class BIMWorkPlanProperties(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class IFCStatus(PropertyGroup):
|
class IFCStatus(PropertyGroup):
|
||||||
name: StringProperty() # pyright: ignore[reportRedeclaration]
|
name: StringProperty()
|
||||||
is_visible: BoolProperty( # pyright: ignore[reportRedeclaration]
|
is_visible: BoolProperty(
|
||||||
name="Is Visible", default=True, update=lambda x, y: (None, bpy.ops.bim.activate_status_filters())[0]
|
name="Is Visible", default=True, update=lambda x, y: (None, bpy.ops.bim.activate_status_filters())[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ class CopyToContainer(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Copy to Container"
|
bl_label = "Copy to Container"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
container: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
container: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
container: int
|
container: int
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_structural_boundary_condition"
|
bl_idname = "bim.enable_editing_structural_boundary_condition"
|
||||||
bl_label = "Enable Editing Structural Boundary Condition"
|
bl_label = "Enable Editing Structural Boundary Condition"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
boundary_condition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
boundary_condition: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
boundary_condition: int
|
boundary_condition: int
|
||||||
@@ -186,7 +186,7 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.edit_structural_boundary_condition"
|
bl_idname = "bim.edit_structural_boundary_condition"
|
||||||
bl_label = "Edit Structural Boundary Condition"
|
bl_label = "Edit Structural Boundary Condition"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
connection: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
connection: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
connection: int
|
connection: int
|
||||||
@@ -917,7 +917,7 @@ class EnableEditingBoundaryCondition(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_boundary_condition"
|
bl_idname = "bim.enable_editing_boundary_condition"
|
||||||
bl_label = "Enable Editing Boundary Condition"
|
bl_label = "Enable Editing Boundary Condition"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
boundary_condition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
boundary_condition: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
boundary_condition: int
|
boundary_condition: int
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class DecorationShader:
|
|||||||
PARALLEL DISTRIBUTED FORCE,
|
PARALLEL DISTRIBUTED FORCE,
|
||||||
DISTRIBUTED MOMENT,
|
DISTRIBUTED MOMENT,
|
||||||
"""
|
"""
|
||||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||||
vert_out.smooth("VEC3", "forces")
|
vert_out.smooth("VEC3", "forces")
|
||||||
vert_out.smooth("VEC3", "co")
|
vert_out.smooth("VEC3", "co")
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ class DecorationShader:
|
|||||||
"""param: pattern: type of pattern
|
"""param: pattern: type of pattern
|
||||||
SINGLE FORCE,
|
SINGLE FORCE,
|
||||||
SINGLE MOMENT"""
|
SINGLE MOMENT"""
|
||||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty: ignore[too-many-positional-arguments]
|
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||||
vert_out.smooth("VEC3", "co")
|
vert_out.smooth("VEC3", "co")
|
||||||
|
|
||||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||||
@@ -253,7 +253,7 @@ class DecorationShader:
|
|||||||
|
|
||||||
def get_planar_shader(self) -> gpu.types.GPUShader:
|
def get_planar_shader(self) -> gpu.types.GPUShader:
|
||||||
"""shader for planar loads"""
|
"""shader for planar loads"""
|
||||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty: ignore[too-many-positional-arguments]
|
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||||
vert_out.smooth("VEC3", "co")
|
vert_out.smooth("VEC3", "co")
|
||||||
|
|
||||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class AddSystem(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Add System"
|
bl_label = "Add System"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
parent_system_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
parent_system_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
parent_system_id: int
|
parent_system_id: int
|
||||||
|
|||||||
@@ -26,16 +26,16 @@ from bpy.types import PropertyGroup
|
|||||||
|
|
||||||
|
|
||||||
class WebProperties(PropertyGroup):
|
class WebProperties(PropertyGroup):
|
||||||
webserver_port: IntProperty( # pyright: ignore[reportRedeclaration]
|
webserver_port: IntProperty(
|
||||||
name="Webserver Port",
|
name="Webserver Port",
|
||||||
min=0,
|
min=0,
|
||||||
max=65535,
|
max=65535,
|
||||||
)
|
)
|
||||||
is_running: BoolProperty( # pyright: ignore[reportRedeclaration]
|
is_running: BoolProperty(
|
||||||
name="Webserver Running Status",
|
name="Webserver Running Status",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
is_connected: BoolProperty( # pyright: ignore[reportRedeclaration]
|
is_connected: BoolProperty(
|
||||||
name="Connection Status",
|
name="Connection Status",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -159,9 +159,9 @@ class SelectURIAttribute(bpy.types.Operator, ImportHelper):
|
|||||||
bl_label = "Select URI Attribute"
|
bl_label = "Select URI Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Select a local file"
|
bl_description = "Select a local file"
|
||||||
attribute_data_path: bpy.props.StringProperty(name="Data Path") # pyright: ignore[reportRedeclaration]
|
attribute_data_path: bpy.props.StringProperty(name="Data Path")
|
||||||
"""Full data path to `Attribute`/string property."""
|
"""Full data path to `Attribute`/string property."""
|
||||||
use_relative_path: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
use_relative_path: bpy.props.BoolProperty(
|
||||||
name="Use Relative Path",
|
name="Use Relative Path",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
@@ -601,7 +601,7 @@ class CreateMacBonsaiApp(bpy.types.Operator):
|
|||||||
"ALT+click to uninstall Bonsai app if it was installed previously."
|
"ALT+click to uninstall Bonsai app if it was installed previously."
|
||||||
)
|
)
|
||||||
|
|
||||||
uninstall: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
uninstall: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
uninstall: bool
|
uninstall: bool
|
||||||
@@ -1667,7 +1667,7 @@ class BIM_OT_attribute_add_subitem(bpy.types.Operator):
|
|||||||
bl_description = "Add subitem to the current attribute"
|
bl_description = "Add subitem to the current attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
data_path: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
data_path: bpy.props.StringProperty()
|
||||||
"""Full data path."""
|
"""Full data path."""
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -1691,9 +1691,9 @@ class BIM_OT_attribute_remove_subitem(bpy.types.Operator):
|
|||||||
bl_description = "Add subitem to the current attribute"
|
bl_description = "Add subitem to the current attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
data_path: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
data_path: bpy.props.StringProperty()
|
||||||
"""Full data path."""
|
"""Full data path."""
|
||||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
data_path: str
|
data_path: str
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ class Attribute(PropertyGroup):
|
|||||||
filter_glob: StringProperty()
|
filter_glob: StringProperty()
|
||||||
is_null: BoolProperty(name="Is Null", update=update_is_null)
|
is_null: BoolProperty(name="Is Null", update=update_is_null)
|
||||||
is_selected: BoolProperty(name="Is Selected", default=False)
|
is_selected: BoolProperty(name="Is Selected", default=False)
|
||||||
subitems_values: CollectionProperty(type=StrProperty) # pyright: ignore[reportRedeclaration]
|
subitems_values: CollectionProperty(type=StrProperty)
|
||||||
|
|
||||||
# Attribute parameters.
|
# Attribute parameters.
|
||||||
is_optional: BoolProperty(name="Is Optional")
|
is_optional: BoolProperty(name="Is Optional")
|
||||||
@@ -342,7 +342,7 @@ class Attribute(PropertyGroup):
|
|||||||
value_max: FloatProperty(description="This is used to validate int_value and float_value")
|
value_max: FloatProperty(description="This is used to validate int_value and float_value")
|
||||||
value_max_constraint: BoolProperty(default=False, description="True if the numerical value has an upper bound")
|
value_max_constraint: BoolProperty(default=False, description="True if the numerical value has an upper bound")
|
||||||
special_type: StringProperty(name="Special Value Type", default="")
|
special_type: StringProperty(name="Special Value Type", default="")
|
||||||
use_explorer_ui: BoolProperty() # pyright: ignore[reportRedeclaration]
|
use_explorer_ui: BoolProperty()
|
||||||
metadata: StringProperty(name="Metadata", description="For storing some additional information about the attribute")
|
metadata: StringProperty(name="Metadata", description="For storing some additional information about the attribute")
|
||||||
update: StringProperty(name="Update", description="Custom update function to be executed")
|
update: StringProperty(name="Update", description="Custom update function to be executed")
|
||||||
|
|
||||||
|
|||||||
@@ -665,7 +665,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|||||||
name="Disable Undo When Saving (Faster saves, no undo for you!)", default=False
|
name="Disable Undo When Saving (Faster saves, no undo for you!)", default=False
|
||||||
)
|
)
|
||||||
should_stream: BoolProperty(name="Stream Data From IFC-SPF (Only for advanced users)", default=False)
|
should_stream: BoolProperty(name="Stream Data From IFC-SPF (Only for advanced users)", default=False)
|
||||||
should_always_cache: BoolProperty( # pyright: ignore[reportRedeclaration]
|
should_always_cache: BoolProperty(
|
||||||
name="Always Cache Geometry",
|
name="Always Cache Geometry",
|
||||||
description="Whether to always cache geometry regardless of 'Cache' setting during Advanced Project Load.",
|
description="Whether to always cache geometry regardless of 'Cache' setting during Advanced Project Load.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -286,11 +286,11 @@ class IfcGit:
|
|||||||
if re.match("^Ifc", obj.name):
|
if re.match("^Ifc", obj.name):
|
||||||
bpy.data.objects.remove(obj, do_unlink=True)
|
bpy.data.objects.remove(obj, do_unlink=True)
|
||||||
|
|
||||||
bpy.data.orphans_purge(do_recursive=True) # ty:ignore[unknown-argument]
|
bpy.data.orphans_purge(do_recursive=True)
|
||||||
|
|
||||||
from bonsai.bim.module.root.data import IfcClassData
|
|
||||||
from bonsai.bim.module.model.data import AuthoringData
|
|
||||||
import bonsai.bim.handler
|
import bonsai.bim.handler
|
||||||
|
from bonsai.bim.module.model.data import AuthoringData
|
||||||
|
from bonsai.bim.module.root.data import IfcClassData
|
||||||
|
|
||||||
AuthoringData.type_thumbnails = {}
|
AuthoringData.type_thumbnails = {}
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ import bmesh
|
|||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from mathutils import Vector
|
|
||||||
|
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from bpy_extras import view3d_utils
|
|
||||||
|
|
||||||
class Raycast(bonsai.core.tool.Raycast):
|
class Raycast(bonsai.core.tool.Raycast):
|
||||||
offset = 10
|
offset = 10
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Python code formatters
|
|||||||
For Python code formatting, we use `Black code formatter <https://pypi.org/project/black/>`__,
|
For Python code formatting, we use `Black code formatter <https://pypi.org/project/black/>`__,
|
||||||
black settings are stored in the repository's pyproject.toml.
|
black settings are stored in the repository's pyproject.toml.
|
||||||
|
|
||||||
We have GitHub workflow `ci-black-formatting` to maintain black formatting across the repository.
|
We have GitHub workflow `ci-lint` to maintain black formatting across the repository.
|
||||||
|
|
||||||
``black`` can be installed using ``pip install black`` and files can be formatted with the following example command:
|
``black`` can be installed using ``pip install black`` and files can be formatted with the following example command:
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ When adding or removing a supported Python version, update the following:
|
|||||||
|
|
||||||
* - File
|
* - File
|
||||||
- What to update
|
- What to update
|
||||||
* - ``.github/workflows/ci-black-formatting.yaml``
|
* - ``.github/workflows/ci-lint.yaml``
|
||||||
- ``MIN_IOS_PY_VERSION``
|
- ``MIN_IOS_PY_VERSION``
|
||||||
* - ``.github/workflows/ci-ifcopenshell-python-pypi.yml``
|
* - ``.github/workflows/ci-ifcopenshell-python-pypi.yml``
|
||||||
- ``pyver`` matrix
|
- ``pyver`` matrix
|
||||||
@@ -44,6 +44,8 @@ When a new Blender version is released and supported:
|
|||||||
|
|
||||||
* - File
|
* - File
|
||||||
- What to update
|
- What to update
|
||||||
|
* - ``.github/workflows/ci-bonsai.yml``
|
||||||
|
- ``pyver`` matrix
|
||||||
* - ``.github/workflows/ci-bonsai-daily.yml``
|
* - ``.github/workflows/ci-bonsai-daily.yml``
|
||||||
- Blender download URL
|
- Blender download URL
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ When Blender ships with a new Python version:
|
|||||||
|
|
||||||
* - File
|
* - File
|
||||||
- What to update
|
- What to update
|
||||||
* - ``.github/workflows/ci-black-formatting.yaml``
|
* - ``.github/workflows/ci-lint.yaml``
|
||||||
- ``MIN_BLENDER_PY_VERSION``
|
- ``MIN_BLENDER_PY_VERSION``
|
||||||
* - ``src/bonsai/Makefile``
|
* - ``src/bonsai/Makefile``
|
||||||
- ``SUPPORTED_PYVERSIONS``
|
- ``SUPPORTED_PYVERSIONS``
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import bonsai.core.ifcgit as subject
|
import bonsai.core.ifcgit as subject
|
||||||
from test.core.bootstrap import ifcgit, ifc
|
from test.core.bootstrap import ifc, ifcgit
|
||||||
|
|
||||||
|
|
||||||
class MockOperator:
|
class MockOperator:
|
||||||
|
|||||||
@@ -52,15 +52,6 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* i
|
|||||||
if (inst->OffsetVertical().has_value()) {
|
if (inst->OffsetVertical().has_value()) {
|
||||||
auto offset_vertical = inst->OffsetVertical().get() * length_unit_;
|
auto offset_vertical = inst->OffsetVertical().get() * length_unit_;
|
||||||
o += offset_vertical * z;
|
o += offset_vertical * z;
|
||||||
|
|
||||||
auto tmp1 = (z * offset_vertical).eval();
|
|
||||||
auto tmp2 = (Eigen::Vector3d(0, 0, 1) * offset_vertical).eval();
|
|
||||||
auto tmp3 = (tmp1 - tmp2).eval();
|
|
||||||
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "local z: " << z.x() << "," << z.y() << "," << z.z() << "; delta: " << tmp3.x() << "," << tmp3.y() << "," << tmp3.z();
|
|
||||||
auto osss = oss.str();
|
|
||||||
std::wcout << osss.c_str() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->OffsetLongitudinal().has_value()) {
|
if (inst->OffsetLongitudinal().has_value()) {
|
||||||
|
|||||||
@@ -278,7 +278,12 @@ class IfcSession:
|
|||||||
return info.info(model, element)
|
return info.info(model, element)
|
||||||
|
|
||||||
def ifc_select(self, query: str) -> list[dict[str, Any]]:
|
def ifc_select(self, query: str) -> list[dict[str, Any]]:
|
||||||
"""Filter elements using ifcopenshell selector syntax (e.g. 'IfcWall', 'IfcWindow')."""
|
"""Filter elements using ifcopenshell selector syntax.
|
||||||
|
|
||||||
|
Examples: ``IfcWall``, ``IfcWall, IfcColumn``, ``! IfcWall``,
|
||||||
|
``IfcWall, Name = "My Wall"``, ``type = "Concrete Wall"``,
|
||||||
|
``material = "Concrete"``.
|
||||||
|
"""
|
||||||
return select.select(self._require_model(), query)
|
return select.select(self._require_model(), query)
|
||||||
|
|
||||||
def ifc_relations(self, element_id: int, traverse: str = "") -> dict[str, Any] | list[dict[str, Any]]:
|
def ifc_relations(self, element_id: int, traverse: str = "") -> dict[str, Any] | list[dict[str, Any]]:
|
||||||
@@ -536,7 +541,12 @@ class IfcSession:
|
|||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"name": "ifc_select",
|
"name": "ifc_select",
|
||||||
"description": "Select elements using ifcopenshell selector syntax (e.g. 'IfcWall').",
|
"description": (
|
||||||
|
"Select elements using ifcopenshell selector syntax. "
|
||||||
|
"Examples: 'IfcWall', 'IfcWall, IfcColumn', '! IfcWall', "
|
||||||
|
"'IfcWall, Name = \"My Wall\"', 'type = \"Concrete Wall\"', "
|
||||||
|
"'material = \"Concrete\"'."
|
||||||
|
),
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {"query": {"type": "string"}},
|
"properties": {"query": {"type": "string"}},
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ def get_properties(
|
|||||||
del data["HasProperties"]
|
del data["HasProperties"]
|
||||||
results[prop_name] = data
|
results[prop_name] = data
|
||||||
if verbose:
|
if verbose:
|
||||||
results[prop_name] = {"id": data["id"], "class": data["class"], "value": results[prop_name]}
|
results[prop_name] = {"id": data["id"], "class": data["type"], "value": results[prop_name]}
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,35 @@ class TestGetPropertiesIFC4(test.bootstrap.IFC4):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def test_getting_complex_properties_verbose(self):
|
||||||
|
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.pset.add_pset(self.file, product=element, name="pset")
|
||||||
|
complex_property = self.file.create_entity("IfcComplexProperty", Name="prop", UsageName="usage_name")
|
||||||
|
ifcopenshell.api.pset.edit_pset(self.file, pset=complex_property, properties={"a": "b"})
|
||||||
|
pset.HasProperties = [complex_property]
|
||||||
|
properties = subject.get_properties(pset.HasProperties, verbose=True)
|
||||||
|
prop_value = properties["prop"]["value"]
|
||||||
|
nested_prop = prop_value["properties"]["a"]
|
||||||
|
assert properties == {
|
||||||
|
"prop": {
|
||||||
|
"id": complex_property.id(),
|
||||||
|
"class": "IfcComplexProperty",
|
||||||
|
"value": {
|
||||||
|
"UsageName": "usage_name",
|
||||||
|
"id": complex_property.id(),
|
||||||
|
"type": "IfcComplexProperty",
|
||||||
|
"properties": {
|
||||||
|
"a": {
|
||||||
|
"id": nested_prop["id"],
|
||||||
|
"class": "IfcPropertySingleValue",
|
||||||
|
"value": "b",
|
||||||
|
"value_type": "IfcLabel",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestGetElementsUsingPset(test.bootstrap.IFC4):
|
class TestGetElementsUsingPset(test.bootstrap.IFC4):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
|
|||||||
@@ -26,7 +26,16 @@ import ifcopenshell.util.selector
|
|||||||
|
|
||||||
|
|
||||||
def select(model: ifcopenshell.file, query: str) -> list[dict[str, Any]]:
|
def select(model: ifcopenshell.file, query: str) -> list[dict[str, Any]]:
|
||||||
"""Filter elements using selector syntax and return matching element summaries."""
|
"""Filter elements using ifcopenshell selector syntax and return matching element summaries.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- ``IfcWall`` — all walls
|
||||||
|
- ``IfcWall, IfcColumn`` — walls and columns
|
||||||
|
- ``! IfcWall`` — everything except walls
|
||||||
|
- ``IfcWall, Name = "My Wall"`` — walls with a specific name attribute
|
||||||
|
- ``type = "Concrete Wall"`` — elements assigned that type product
|
||||||
|
- ``material = "Concrete"`` — elements with that material
|
||||||
|
"""
|
||||||
elements = ifcopenshell.util.selector.filter_elements(model, query)
|
elements = ifcopenshell.util.selector.filter_elements(model, query)
|
||||||
results = []
|
results = []
|
||||||
for element in sorted(elements, key=lambda e: e.id()):
|
for element in sorted(elements, key=lambda e: e.id()):
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class SvIfcSbExtrude(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.Sv
|
|||||||
bl_idname = "SvIfcSbExtrude"
|
bl_idname = "SvIfcSbExtrude"
|
||||||
bl_label = "IFC Extrude"
|
bl_label = "IFC Extrude"
|
||||||
|
|
||||||
extrude_axis: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
extrude_axis: bpy.props.EnumProperty(
|
||||||
default="Z",
|
default="Z",
|
||||||
items=[
|
items=[
|
||||||
("X", "X", "Interpret curve as in XY plane and extrude along X+."),
|
("X", "X", "Interpret curve as in XY plane and extrude along X+."),
|
||||||
|
|||||||
Generated
+4
-4
@@ -33,7 +33,7 @@
|
|||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"tw-animate-css": "^1.3.2",
|
"tw-animate-css": "^1.3.2",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^6.4.1"
|
"vite": "^6.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -3264,9 +3264,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.4.1",
|
"version": "6.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
|
||||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
"integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"tw-animate-css": "^1.3.2",
|
"tw-animate-css": "^1.3.2",
|
||||||
"vite": "^6.4.1"
|
"vite": "^6.4.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
|
|||||||
@@ -615,9 +615,6 @@ void eliminate_overlaps(DebugWriter& debug_writer, double OVERLAP_RESOLUTION_DIS
|
|||||||
std::swap(poly1, poly2);
|
std::swap(poly1, poly2);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "processing: " << edge.first << " " << edge.second << std::endl;
|
|
||||||
std::cerr << "area before: " << poly1->area() << " " << poly2->area() << std::endl;
|
|
||||||
|
|
||||||
bool is_ = edge == std::make_pair<size_t, size_t>(25, 27);
|
bool is_ = edge == std::make_pair<size_t, size_t>(25, 27);
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
@@ -657,8 +654,6 @@ void eliminate_overlaps(DebugWriter& debug_writer, double OVERLAP_RESOLUTION_DIS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "area after: " << poly1->area() << " " << poly2->area() << std::endl;
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
eliminated_polies.insert(swap ? edge.first : edge.second);
|
eliminated_polies.insert(swap ? edge.first : edge.second);
|
||||||
continue;
|
continue;
|
||||||
@@ -911,6 +906,681 @@ build_line_graph(const std::vector<Polygon_2>& input_polygons, SegmentLookup& se
|
|||||||
return {line_graph, midpoint_to_segment, segment_to_input_facet, midpoint_to_edge_length};
|
return {line_graph, midpoint_to_segment, segment_to_input_facet, midpoint_to_edge_length};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using DPoint = CGAL::Simple_cartesian<double>::Point_2;
|
||||||
|
using DDir = CGAL::Simple_cartesian<double>::Vector_2;
|
||||||
|
using DBox = std::array<DPoint, 2>;
|
||||||
|
|
||||||
|
struct CenterLineGraphData {
|
||||||
|
std::vector<Point_2> points;
|
||||||
|
std::vector<DPoint> points_double;
|
||||||
|
std::vector<double> widths;
|
||||||
|
std::vector<std::pair<size_t, size_t>> edges;
|
||||||
|
std::vector<std::vector<size_t>> incident_edges;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LineRun {
|
||||||
|
Point_2 start_exact;
|
||||||
|
Point_2 end_exact;
|
||||||
|
DPoint start;
|
||||||
|
DPoint end;
|
||||||
|
DDir direction;
|
||||||
|
double avg_width;
|
||||||
|
double length;
|
||||||
|
size_t vertex_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RunBoxRecord {
|
||||||
|
size_t run_index;
|
||||||
|
DPoint start;
|
||||||
|
DPoint end;
|
||||||
|
DDir direction;
|
||||||
|
double width;
|
||||||
|
double length;
|
||||||
|
std::array<DPoint, 4> corners;
|
||||||
|
DBox bbox;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MergedBoxRecord {
|
||||||
|
DPoint start;
|
||||||
|
DPoint end;
|
||||||
|
DDir direction;
|
||||||
|
DDir normal;
|
||||||
|
double avg_width;
|
||||||
|
double length;
|
||||||
|
size_t member_count;
|
||||||
|
std::vector<size_t> members;
|
||||||
|
std::array<DPoint, 4> corners;
|
||||||
|
DBox bbox;
|
||||||
|
Point_2 exact_start;
|
||||||
|
Point_2 exact_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoxCluster {
|
||||||
|
std::vector<size_t> members;
|
||||||
|
MergedBoxRecord box;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SnapCandidate {
|
||||||
|
size_t box_index;
|
||||||
|
double box_distance;
|
||||||
|
double line_distance;
|
||||||
|
Point_2 projection;
|
||||||
|
};
|
||||||
|
|
||||||
|
DDir unit(const DDir& a) {
|
||||||
|
auto n = std::sqrt(a.squared_length());
|
||||||
|
if (n < 1.e-9) {
|
||||||
|
return {0., 0.};
|
||||||
|
}
|
||||||
|
return a / n;
|
||||||
|
}
|
||||||
|
|
||||||
|
DDir perpendicular(const DDir& a) {
|
||||||
|
return DDir(-a.y(), a.x());
|
||||||
|
}
|
||||||
|
|
||||||
|
DDir canonicalize_like(const DDir& a, const DDir& ref) {
|
||||||
|
return (a * ref) < 0. ? -a : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPoint to_double_point(const Point_2& p) {
|
||||||
|
return {CGAL::to_double(p.x()), CGAL::to_double(p.y())};
|
||||||
|
}
|
||||||
|
|
||||||
|
Point_2 to_exact_point(const DPoint& p) {
|
||||||
|
return Point_2(p.x(), p.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
double point_line_distance(const DPoint& p, const DPoint& line_point, const DDir& line_dir) {
|
||||||
|
auto u = unit(line_dir);
|
||||||
|
auto delta = (p - line_point);
|
||||||
|
if (u.squared_length() < 1.e-18) {
|
||||||
|
return std::sqrt(delta.squared_length());
|
||||||
|
}
|
||||||
|
return std::abs(CGAL::determinant(u.x(), u.y(), delta.x(), delta.y()));
|
||||||
|
}
|
||||||
|
|
||||||
|
double angle_between_dirs_deg(const DDir& a, const DDir& b) {
|
||||||
|
auto u = unit(a);
|
||||||
|
auto v = unit(b);
|
||||||
|
auto c = std::abs(u * v);
|
||||||
|
if (c > 1.) {
|
||||||
|
c = 1.;
|
||||||
|
}
|
||||||
|
return std::acos(c) * 180. / 3.14159265358979323846;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<DPoint, 4> rectangle_corners(const DPoint& start, const DPoint& end, double width) {
|
||||||
|
auto u = unit(end - start);
|
||||||
|
if (u.squared_length() < 1.e-18) {
|
||||||
|
u = {1., 0.};
|
||||||
|
}
|
||||||
|
auto n = perpendicular(u);
|
||||||
|
auto ext = width;
|
||||||
|
auto p0 = start - u * ext;
|
||||||
|
auto p1 = end + u * ext;
|
||||||
|
auto w = n * (width / 2.);
|
||||||
|
return {p0 + w, p1 + w, p1 - w, p0 - w};
|
||||||
|
}
|
||||||
|
|
||||||
|
DBox aabb_from_points(const std::array<DPoint, 4>& corners) {
|
||||||
|
DBox bbox{corners[0], corners[0]};
|
||||||
|
for (auto& p : corners) {
|
||||||
|
bbox[0] = {std::min(bbox[0].x(), p.x()), std::min(bbox[0].y(), p.y())};
|
||||||
|
bbox[1] = {std::max(bbox[1].x(), p.x()), std::max(bbox[1].y(), p.y())};
|
||||||
|
}
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aabb_overlap(const DBox& a, const DBox& b, double eps = 1.e-9) {
|
||||||
|
return a[0].x() <= b[1].x() + eps &&
|
||||||
|
a[1].x() + eps >= b[0].x() &&
|
||||||
|
a[0].y() <= b[1].y() + eps &&
|
||||||
|
a[1].y() + eps >= b[0].y();
|
||||||
|
}
|
||||||
|
|
||||||
|
CenterLineGraphData make_center_line_graph_data(
|
||||||
|
const std::map<Point_2, std::vector<Point_2>>& line_graph,
|
||||||
|
const std::map<Point_2, double>& midpoint_to_edge_length)
|
||||||
|
{
|
||||||
|
CenterLineGraphData graph;
|
||||||
|
std::map<Point_2, size_t> point_to_index;
|
||||||
|
|
||||||
|
auto ensure_point = [&](const Point_2& p) {
|
||||||
|
auto it = point_to_index.find(p);
|
||||||
|
if (it != point_to_index.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
auto i = graph.points.size();
|
||||||
|
point_to_index[p] = i;
|
||||||
|
graph.points.push_back(p);
|
||||||
|
graph.points_double.push_back(to_double_point(p));
|
||||||
|
auto wt = midpoint_to_edge_length.find(p);
|
||||||
|
graph.widths.push_back(wt == midpoint_to_edge_length.end() ? 0. : wt->second);
|
||||||
|
graph.incident_edges.emplace_back();
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& p : line_graph) {
|
||||||
|
ensure_point(p.first);
|
||||||
|
for (auto& q : p.second) {
|
||||||
|
ensure_point(q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::pair<size_t, size_t>> seen_edges;
|
||||||
|
for (auto& p : line_graph) {
|
||||||
|
auto i = ensure_point(p.first);
|
||||||
|
for (auto& q : p.second) {
|
||||||
|
auto j = ensure_point(q);
|
||||||
|
if (i == j) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto e = i < j ? std::make_pair(i, j) : std::make_pair(j, i);
|
||||||
|
if (seen_edges.insert(e).second) {
|
||||||
|
auto k = graph.edges.size();
|
||||||
|
graph.edges.push_back(e);
|
||||||
|
graph.incident_edges[e.first].push_back(k);
|
||||||
|
graph.incident_edges[e.second].push_back(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
double segment_width(const CenterLineGraphData& graph, const std::pair<size_t, size_t>& edge) {
|
||||||
|
return 0.5 * (graph.widths[edge.first] + graph.widths[edge.second]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool edge_supports_same_line(
|
||||||
|
const DPoint& seed_a,
|
||||||
|
const DPoint& seed_b,
|
||||||
|
const DPoint& test_a,
|
||||||
|
const DPoint& test_b,
|
||||||
|
double angle_tol_deg = 3.,
|
||||||
|
double line_dist_tol = 0.15)
|
||||||
|
{
|
||||||
|
auto d_seed = seed_b - seed_a;
|
||||||
|
auto d_test = test_b - test_a;
|
||||||
|
if (d_seed.squared_length() < 1.e-18 || d_test.squared_length() < 1.e-18) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (angle_between_dirs_deg(d_seed, d_test) > angle_tol_deg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return
|
||||||
|
point_line_distance(test_a, seed_a, d_seed) <= line_dist_tol &&
|
||||||
|
point_line_distance(test_b, seed_a, d_seed) <= line_dist_tol;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LineRun> runs_from_graph(const CenterLineGraphData& graph, double angle_tol_deg = 3., double line_dist_tol = 0.15) {
|
||||||
|
std::vector<bool> visited(graph.edges.size(), false);
|
||||||
|
std::vector<LineRun> runs;
|
||||||
|
|
||||||
|
for (size_t seed_ei = 0; seed_ei < graph.edges.size(); ++seed_ei) {
|
||||||
|
if (visited[seed_ei]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& seed_edge = graph.edges[seed_ei];
|
||||||
|
auto seed_a = graph.points_double[seed_edge.first];
|
||||||
|
auto seed_b = graph.points_double[seed_edge.second];
|
||||||
|
auto seed_dir = seed_b - seed_a;
|
||||||
|
if (seed_dir.squared_length() < 1.e-18) {
|
||||||
|
visited[seed_ei] = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> queue = {seed_ei};
|
||||||
|
std::set<size_t> component_edges;
|
||||||
|
|
||||||
|
while (!queue.empty()) {
|
||||||
|
auto ei = queue.back();
|
||||||
|
queue.pop_back();
|
||||||
|
if (!component_edges.insert(ei).second) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& edge = graph.edges[ei];
|
||||||
|
std::array<size_t, 2> vertices = {edge.first, edge.second};
|
||||||
|
for (auto v : vertices) {
|
||||||
|
for (auto ej : graph.incident_edges[v]) {
|
||||||
|
if (ej == ei || visited[ej] || component_edges.count(ej)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& candidate = graph.edges[ej];
|
||||||
|
auto test_a = graph.points_double[candidate.first];
|
||||||
|
auto test_b = graph.points_double[candidate.second];
|
||||||
|
if (edge_supports_same_line(seed_a, seed_b, test_a, test_b, angle_tol_deg, line_dist_tol)) {
|
||||||
|
queue.push_back(ej);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto ei : component_edges) {
|
||||||
|
visited[ei] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<size_t> component_vertices;
|
||||||
|
auto ref = unit(seed_dir);
|
||||||
|
DDir direction_sum{0., 0.};
|
||||||
|
double total_length = 0.;
|
||||||
|
double weighted_width_sum = 0.;
|
||||||
|
|
||||||
|
for (auto ei : component_edges) {
|
||||||
|
const auto& edge = graph.edges[ei];
|
||||||
|
component_vertices.insert(edge.first);
|
||||||
|
component_vertices.insert(edge.second);
|
||||||
|
|
||||||
|
auto d = graph.points_double[edge.second] - graph.points_double[edge.first];
|
||||||
|
auto u = canonicalize_like(unit(d), ref);
|
||||||
|
direction_sum = direction_sum + u;
|
||||||
|
|
||||||
|
auto len = std::sqrt(d.squared_length());
|
||||||
|
total_length += len;
|
||||||
|
weighted_width_sum += len * segment_width(graph, edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto run_direction = direction_sum.squared_length() < 1.e-18 ? ref : unit(direction_sum);
|
||||||
|
|
||||||
|
double min_t = std::numeric_limits<double>::infinity();
|
||||||
|
double max_t = -std::numeric_limits<double>::infinity();
|
||||||
|
size_t start_index = *component_vertices.begin();
|
||||||
|
size_t end_index = start_index;
|
||||||
|
for (auto vi : component_vertices) {
|
||||||
|
auto t = (graph.points_double[vi] - CGAL::ORIGIN) * run_direction;
|
||||||
|
if (t < min_t) {
|
||||||
|
min_t = t;
|
||||||
|
start_index = vi;
|
||||||
|
}
|
||||||
|
if (t > max_t) {
|
||||||
|
max_t = t;
|
||||||
|
end_index = vi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto avg_width = total_length < 1.e-9 ? segment_width(graph, seed_edge) : weighted_width_sum / total_length;
|
||||||
|
|
||||||
|
runs.push_back({
|
||||||
|
graph.points[start_index],
|
||||||
|
graph.points[end_index],
|
||||||
|
graph.points_double[start_index],
|
||||||
|
graph.points_double[end_index],
|
||||||
|
run_direction,
|
||||||
|
avg_width,
|
||||||
|
std::sqrt((graph.points_double[end_index] - graph.points_double[start_index]).squared_length()),
|
||||||
|
component_vertices.size()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<RunBoxRecord> build_run_box_records(const std::vector<LineRun>& runs) {
|
||||||
|
std::vector<RunBoxRecord> records;
|
||||||
|
records.reserve(runs.size());
|
||||||
|
for (size_t i = 0; i < runs.size(); ++i) {
|
||||||
|
auto corners = rectangle_corners(runs[i].start, runs[i].end, runs[i].avg_width);
|
||||||
|
records.push_back({
|
||||||
|
i,
|
||||||
|
runs[i].start,
|
||||||
|
runs[i].end,
|
||||||
|
unit(runs[i].end - runs[i].start),
|
||||||
|
runs[i].avg_width,
|
||||||
|
runs[i].length,
|
||||||
|
corners,
|
||||||
|
aabb_from_points(corners)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::pair<double, double> projected_interval_on_axis(const T& box, const DDir& axis_u) {
|
||||||
|
auto u = unit(axis_u);
|
||||||
|
auto ta = (box.start - CGAL::ORIGIN) * u;
|
||||||
|
auto tb = (box.end - CGAL::ORIGIN) * u;
|
||||||
|
return {std::min(ta, tb), std::max(ta, tb)};
|
||||||
|
}
|
||||||
|
|
||||||
|
double interval_overlap_length(const std::pair<double, double>& a, const std::pair<double, double>& b) {
|
||||||
|
return std::max(0., std::min(a.second, b.second) - std::max(a.first, b.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double boxes_overlap_along_merge_axis(const T& a, const T& b) {
|
||||||
|
auto d1 = unit(a.end - a.start);
|
||||||
|
auto d2 = unit(b.end - b.start);
|
||||||
|
if (d1 * d2 < 0.) {
|
||||||
|
d2 = {-d2.x(), -d2.y()};
|
||||||
|
}
|
||||||
|
auto merge_axis = unit(d1 + d2);
|
||||||
|
if (merge_axis.squared_length() < 1.e-18) {
|
||||||
|
merge_axis = d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto i1 = projected_interval_on_axis(a, merge_axis);
|
||||||
|
auto i2 = projected_interval_on_axis(b, merge_axis);
|
||||||
|
auto overlap = interval_overlap_length(i1, i2);
|
||||||
|
auto small_length = std::min(i1.second - i1.first, i2.second - i2.first);
|
||||||
|
if (small_length < 1.e-9) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return overlap / small_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
MergedBoxRecord merge_cluster_to_box(const std::vector<size_t>& member_indices, const std::vector<RunBoxRecord>& records) {
|
||||||
|
auto ref = records[member_indices.front()].direction;
|
||||||
|
DDir direction_sum{0., 0.};
|
||||||
|
for (auto i : member_indices) {
|
||||||
|
auto u = canonicalize_like(records[i].direction, ref);
|
||||||
|
direction_sum = direction_sum + u * std::max(records[i].length, 1.e-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto u = direction_sum.squared_length() < 1.e-18 ? ref : unit(direction_sum);
|
||||||
|
auto n = perpendicular(u);
|
||||||
|
|
||||||
|
double tmin = std::numeric_limits<double>::infinity();
|
||||||
|
double tmax = -std::numeric_limits<double>::infinity();
|
||||||
|
double smin = std::numeric_limits<double>::infinity();
|
||||||
|
double smax = -std::numeric_limits<double>::infinity();
|
||||||
|
|
||||||
|
for (auto i : member_indices) {
|
||||||
|
for (auto& corner : records[i].corners) {
|
||||||
|
auto t = (corner - CGAL::ORIGIN) * u;
|
||||||
|
auto s = (corner - CGAL::ORIGIN) * n;
|
||||||
|
tmin = std::min(tmin, t);
|
||||||
|
tmax = std::max(tmax, t);
|
||||||
|
smin = std::min(smin, s);
|
||||||
|
smax = std::max(smax, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto width = smax - smin;
|
||||||
|
auto sc = (smin + smax) / 2.;
|
||||||
|
auto start = u * tmin + n * sc;
|
||||||
|
auto end = u * tmax + n * sc;
|
||||||
|
auto corners = rectangle_corners(CGAL::ORIGIN + start, CGAL::ORIGIN + end, width);
|
||||||
|
|
||||||
|
MergedBoxRecord box{
|
||||||
|
CGAL::ORIGIN + start,
|
||||||
|
CGAL::ORIGIN + end,
|
||||||
|
u,
|
||||||
|
n,
|
||||||
|
width,
|
||||||
|
std::sqrt((end - start).squared_length()),
|
||||||
|
member_indices.size(),
|
||||||
|
member_indices,
|
||||||
|
corners,
|
||||||
|
aabb_from_points(corners),
|
||||||
|
to_exact_point(CGAL::ORIGIN + start),
|
||||||
|
to_exact_point(CGAL::ORIGIN + end)
|
||||||
|
};
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<double, double> merge_score(const MergedBoxRecord& a, const MergedBoxRecord& b) {
|
||||||
|
auto ang = angle_between_dirs_deg(a.direction, b.direction);
|
||||||
|
auto center_a = ((a.start - CGAL::ORIGIN) + (a.end - CGAL::ORIGIN)) / 2.;
|
||||||
|
auto center_b = ((b.start - CGAL::ORIGIN) + (b.end - CGAL::ORIGIN)) / 2.;
|
||||||
|
return {ang, std::sqrt((center_b - center_a).squared_length())};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clusters_can_merge(const BoxCluster& a, const BoxCluster& b, double angle_tol_deg = 5., double axis_overlap_ratio_limit = 0.5) {
|
||||||
|
if (!aabb_overlap(a.box.bbox, b.box.bbox)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (angle_between_dirs_deg(a.box.direction, b.box.direction) > angle_tol_deg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (boxes_overlap_along_merge_axis(a.box, b.box) > axis_overlap_ratio_limit) {
|
||||||
|
auto a_center = CGAL::ORIGIN + ((a.box.start - CGAL::ORIGIN) + (a.box.end - CGAL::ORIGIN)) / 2.;
|
||||||
|
auto b_center = CGAL::ORIGIN + ((b.box.start - CGAL::ORIGIN) + (b.box.end - CGAL::ORIGIN)) / 2.;
|
||||||
|
auto a_dir = a.box.direction;
|
||||||
|
auto b_dir = b.box.direction;
|
||||||
|
auto dist = a.box.length < b.box.length ? point_line_distance(a_center, b_center, b_dir) : point_line_distance(b_center, a_center, a_dir);
|
||||||
|
auto ref = a.box.length < b.box.length ? a.box.avg_width : b.box.avg_width;
|
||||||
|
return dist < (ref / 4.);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<MergedBoxRecord> merge_intersecting_parallel_boxes_iterative(const std::vector<LineRun>& runs) {
|
||||||
|
auto records = build_run_box_records(runs);
|
||||||
|
std::vector<BoxCluster> clusters;
|
||||||
|
clusters.reserve(records.size());
|
||||||
|
for (size_t i = 0; i < records.size(); ++i) {
|
||||||
|
clusters.push_back({{i}, merge_cluster_to_box({i}, records)});
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::optional<std::pair<size_t, size_t>> best_pair;
|
||||||
|
std::pair<double, double> best_score;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < clusters.size(); ++i) {
|
||||||
|
for (size_t j = i + 1; j < clusters.size(); ++j) {
|
||||||
|
if (!clusters_can_merge(clusters[i], clusters[j])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto score = merge_score(clusters[i].box, clusters[j].box);
|
||||||
|
if (!best_pair || score < best_score) {
|
||||||
|
best_pair = std::make_pair(i, j);
|
||||||
|
best_score = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!best_pair) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto i = best_pair->first;
|
||||||
|
auto j = best_pair->second;
|
||||||
|
std::vector<size_t> members = clusters[i].members;
|
||||||
|
members.insert(members.end(), clusters[j].members.begin(), clusters[j].members.end());
|
||||||
|
auto merged = BoxCluster{members, merge_cluster_to_box(members, records)};
|
||||||
|
|
||||||
|
std::vector<BoxCluster> next_clusters;
|
||||||
|
next_clusters.reserve(clusters.size() - 1);
|
||||||
|
for (size_t k = 0; k < clusters.size(); ++k) {
|
||||||
|
if (k != i && k != j) {
|
||||||
|
next_clusters.push_back(std::move(clusters[k]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_clusters.push_back(std::move(merged));
|
||||||
|
clusters = std::move(next_clusters);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<MergedBoxRecord> merged_boxes;
|
||||||
|
merged_boxes.reserve(clusters.size());
|
||||||
|
for (auto& cluster : clusters) {
|
||||||
|
merged_boxes.push_back(cluster.box);
|
||||||
|
}
|
||||||
|
return merged_boxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point_2 project_point_to_line_exact(const Point_2& p, const MergedBoxRecord& box) {
|
||||||
|
auto d = box.exact_end - box.exact_start;
|
||||||
|
if (d.squared_length() == 0) {
|
||||||
|
return box.exact_start;
|
||||||
|
}
|
||||||
|
auto t = ((p - box.exact_start) * d) / d.squared_length();
|
||||||
|
return box.exact_start + d * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<Point_2> intersect_infinite_lines_exact(const MergedBoxRecord& a, const MergedBoxRecord& b) {
|
||||||
|
if (a.exact_start == a.exact_end || b.exact_start == b.exact_end) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
auto x = CGAL::intersection(CGAL::Line_2<K>(a.exact_start, a.exact_end), CGAL::Line_2<K>(b.exact_start, b.exact_end));
|
||||||
|
if (!x) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
if (auto* xp = variant_get<Point_2>(&*x)) {
|
||||||
|
return *xp;
|
||||||
|
}
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
double point_to_oriented_box_distance(const DPoint& p, const MergedBoxRecord& box) {
|
||||||
|
auto d = box.end - box.start;
|
||||||
|
auto L = std::sqrt(d.squared_length());
|
||||||
|
if (L < 1.e-9) {
|
||||||
|
return std::sqrt((p - box.start).squared_length());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto u = d / L;
|
||||||
|
auto n = perpendicular(u);
|
||||||
|
auto rel = p - box.start;
|
||||||
|
auto t = rel * u;
|
||||||
|
auto s = rel * n;
|
||||||
|
|
||||||
|
auto tmin = -box.avg_width / 2.;
|
||||||
|
auto tmax = L + box.avg_width / 2.;
|
||||||
|
auto smin = -box.avg_width / 2.;
|
||||||
|
auto smax = box.avg_width / 2.;
|
||||||
|
|
||||||
|
double dt = 0.;
|
||||||
|
if (t < tmin) {
|
||||||
|
dt = tmin - t;
|
||||||
|
} else if (t > tmax) {
|
||||||
|
dt = t - tmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ds = 0.;
|
||||||
|
if (s < smin) {
|
||||||
|
ds = smin - s;
|
||||||
|
} else if (s > smax) {
|
||||||
|
ds = s - smax;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::hypot(dt, ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<Point_2, std::vector<Point_2>> snap_points_to_box_axes(
|
||||||
|
const CenterLineGraphData& graph,
|
||||||
|
const std::vector<MergedBoxRecord>& boxes)
|
||||||
|
{
|
||||||
|
std::vector<Point_2> snapped_points(graph.points.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < graph.points.size(); ++i) {
|
||||||
|
if (boxes.empty()) {
|
||||||
|
snapped_points[i] = graph.points[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SnapCandidate> candidates;
|
||||||
|
candidates.reserve(boxes.size());
|
||||||
|
for (size_t j = 0; j < boxes.size(); ++j) {
|
||||||
|
candidates.push_back({
|
||||||
|
j,
|
||||||
|
point_to_oriented_box_distance(graph.points_double[i], boxes[j]),
|
||||||
|
point_line_distance(graph.points_double[i], boxes[j].start, boxes[j].direction),
|
||||||
|
project_point_to_line_exact(graph.points[i], boxes[j])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SnapCandidate> containing;
|
||||||
|
for (auto& candidate : candidates) {
|
||||||
|
if (candidate.box_distance <= 1.e-9) {
|
||||||
|
containing.push_back(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto less = [](const SnapCandidate& a, const SnapCandidate& b) {
|
||||||
|
if (a.line_distance != b.line_distance) {
|
||||||
|
return a.line_distance < b.line_distance;
|
||||||
|
}
|
||||||
|
return a.box_distance < b.box_distance;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (containing.size() >= 2) {
|
||||||
|
std::sort(containing.begin(), containing.end(), less);
|
||||||
|
auto& c1 = containing[0];
|
||||||
|
auto& c2 = containing[1];
|
||||||
|
if (angle_between_dirs_deg(boxes[c1.box_index].direction, boxes[c2.box_index].direction) > 8.) {
|
||||||
|
if (auto x = intersect_infinite_lines_exact(boxes[c1.box_index], boxes[c2.box_index])) {
|
||||||
|
snapped_points[i] = *x;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snapped_points[i] = c1.projection;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containing.size() == 1) {
|
||||||
|
snapped_points[i] = containing[0].projection;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto best = *std::min_element(candidates.begin(), candidates.end(), [](const SnapCandidate& a, const SnapCandidate& b) {
|
||||||
|
if (a.box_distance != b.box_distance) {
|
||||||
|
return a.box_distance < b.box_distance;
|
||||||
|
}
|
||||||
|
return a.line_distance < b.line_distance;
|
||||||
|
});
|
||||||
|
snapped_points[i] = best.projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<Point_2, std::set<Point_2>> adjacency;
|
||||||
|
for (auto& edge : graph.edges) {
|
||||||
|
auto a = snapped_points[edge.first];
|
||||||
|
auto b = snapped_points[edge.second];
|
||||||
|
if (a == b) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
adjacency[a].insert(b);
|
||||||
|
adjacency[b].insert(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<Point_2, std::vector<Point_2>> snapped_graph;
|
||||||
|
for (auto& p : adjacency) {
|
||||||
|
snapped_graph[p.first] = {p.second.begin(), p.second.end()};
|
||||||
|
}
|
||||||
|
return snapped_graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
Graph2D<K> join_segment_runs(
|
||||||
|
DebugWriter& debug,
|
||||||
|
const std::map<Point_2, std::vector<Point_2>>& line_graph,
|
||||||
|
const std::map<Point_2, double>& midpoint_to_edge_length)
|
||||||
|
{
|
||||||
|
auto graph = make_center_line_graph_data(line_graph, midpoint_to_edge_length);
|
||||||
|
auto runs = runs_from_graph(graph);
|
||||||
|
runs.erase(std::remove_if(runs.begin(), runs.end(), [](const LineRun& run) {
|
||||||
|
return run.vertex_count <= 5;
|
||||||
|
}), runs.end());
|
||||||
|
|
||||||
|
std::vector<Polygon_2> run_polygons;
|
||||||
|
for (auto& r : runs) {
|
||||||
|
auto ps = rectangle_corners(r.start, r.end, r.avg_width);
|
||||||
|
std::array<Point_2, 4> exact_corners;
|
||||||
|
std::transform(ps.begin(), ps.end(), exact_corners.begin(), [](const DPoint& p) {
|
||||||
|
return to_exact_point(p);
|
||||||
|
});
|
||||||
|
run_polygons.emplace_back(exact_corners.begin(), exact_corners.end());
|
||||||
|
}
|
||||||
|
debug.write_polygons(run_polygons, "initial_runs");
|
||||||
|
run_polygons.clear();
|
||||||
|
|
||||||
|
auto boxes = merge_intersecting_parallel_boxes_iterative(runs);
|
||||||
|
|
||||||
|
for (auto& r : boxes) {
|
||||||
|
auto ps = rectangle_corners(r.start, r.end, r.avg_width);
|
||||||
|
std::array<Point_2, 4> exact_corners;
|
||||||
|
std::transform(ps.begin(), ps.end(), exact_corners.begin(), [](const DPoint& p) {
|
||||||
|
return to_exact_point(p);
|
||||||
|
});
|
||||||
|
run_polygons.emplace_back(exact_corners.begin(), exact_corners.end());
|
||||||
|
}
|
||||||
|
debug.write_polygons(run_polygons, "merged_boxes");
|
||||||
|
|
||||||
|
auto snapped_graph = snap_points_to_box_axes(graph, boxes);
|
||||||
|
return Graph2D<K>(snapped_graph);
|
||||||
|
}
|
||||||
|
|
||||||
std::set<Triangle<K>> find_triangles(const std::map<Point_2, std::vector<Point_2>>& line_graph) {
|
std::set<Triangle<K>> find_triangles(const std::map<Point_2, std::vector<Point_2>>& line_graph) {
|
||||||
// Find triangles in this network often occuring at junctions in the corridor mesh
|
// Find triangles in this network often occuring at junctions in the corridor mesh
|
||||||
std::set<Triangle<K>> triangles;
|
std::set<Triangle<K>> triangles;
|
||||||
@@ -1249,8 +1919,6 @@ std::list<std::pair<Point_2, Point_2>> extend_end_vertices_based_on_input(
|
|||||||
// create ray incoming -> M
|
// create ray incoming -> M
|
||||||
CGAL::Ray_2<K> ray(incoming, M - incoming);
|
CGAL::Ray_2<K> ray(incoming, M - incoming);
|
||||||
|
|
||||||
std::cerr << "Extending end vertex " << M << " along ray " << ray << " to boundary of input polygon" << std::endl;
|
|
||||||
|
|
||||||
// intersect ray with boundary
|
// intersect ray with boundary
|
||||||
boost::optional<CGAL::Segment_2<K>> closest_segment;
|
boost::optional<CGAL::Segment_2<K>> closest_segment;
|
||||||
boost::optional<CGAL::Point_2<K>> closest_intersection_point;
|
boost::optional<CGAL::Point_2<K>> closest_intersection_point;
|
||||||
@@ -1261,7 +1929,6 @@ std::list<std::pair<Point_2, Point_2>> extend_end_vertices_based_on_input(
|
|||||||
if (x) {
|
if (x) {
|
||||||
if (auto* xp = variant_get<CGAL::Point_2<K>>(&*x)) {
|
if (auto* xp = variant_get<CGAL::Point_2<K>>(&*x)) {
|
||||||
auto dist = ((*xp) - M).squared_length();
|
auto dist = ((*xp) - M).squared_length();
|
||||||
std::cerr << " - found " << *xp << " on segment " << seg << " with distance " << std::sqrt(CGAL::to_double(dist)) << std::endl;
|
|
||||||
if (dist < sq_distance_along_ray) {
|
if (dist < sq_distance_along_ray) {
|
||||||
if (dist < (max_projection_distance * max_projection_distance)) {
|
if (dist < (max_projection_distance * max_projection_distance)) {
|
||||||
closest_segment = seg;
|
closest_segment = seg;
|
||||||
@@ -1329,9 +1996,11 @@ std::list<std::pair<Point_2, Point_2>> extend_end_vertices_based_on_input(
|
|||||||
auto Pp = seg.supporting_line().projection(M);
|
auto Pp = seg.supporting_line().projection(M);
|
||||||
if (seg.has_on(Pp)) {
|
if (seg.has_on(Pp)) {
|
||||||
auto d = CGAL::squared_distance(Pp, M);
|
auto d = CGAL::squared_distance(Pp, M);
|
||||||
if (d < closest_distance) {
|
if (d < (max_projection_distance * max_projection_distance)) {
|
||||||
closest_distance = d;
|
if (d < closest_distance) {
|
||||||
closest_point = Pp;
|
closest_distance = d;
|
||||||
|
closest_point = Pp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1651,13 +2320,10 @@ void clean_noisy_paths(DebugWriter& debug_output, Arrangement_2& arr, SegmentLoo
|
|||||||
return (best + 0.01) / own_length;
|
return (best + 0.01) / own_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::cerr << "badnesses:";
|
|
||||||
std::map<Segment_2, double, Segment_2_less> badnesses;
|
std::map<Segment_2, double, Segment_2_less> badnesses;
|
||||||
for (auto& e : edges) {
|
for (auto& e : edges) {
|
||||||
badnesses[e] = edge_badness(e);
|
badnesses[e] = edge_badness(e);
|
||||||
std::cerr << " (" << e.source().x() << "," << e.source().y() << ") - (" << e.target().x() << "," << e.target().y() << "): " << badnesses[e] << ";";
|
|
||||||
}
|
}
|
||||||
std::cerr << std::endl;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::vector<double> tmp;
|
std::vector<double> tmp;
|
||||||
@@ -1670,8 +2336,6 @@ void clean_noisy_paths(DebugWriter& debug_output, Arrangement_2& arr, SegmentLoo
|
|||||||
threshold = 4.0 * med;
|
threshold = 4.0 * med;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "badness threshold: " << threshold << std::endl;
|
|
||||||
|
|
||||||
std::set<Segment_2, Segment_2_less> bad_edges;
|
std::set<Segment_2, Segment_2_less> bad_edges;
|
||||||
for (auto& p : badnesses) {
|
for (auto& p : badnesses) {
|
||||||
if (p.second > threshold) {
|
if (p.second > threshold) {
|
||||||
@@ -1835,14 +2499,6 @@ void clean_noisy_paths(DebugWriter& debug_output, Arrangement_2& arr, SegmentLoo
|
|||||||
decltype(to_remove) to_remove_this_path;
|
decltype(to_remove) to_remove_this_path;
|
||||||
decltype(to_insert) to_insert_this_path;
|
decltype(to_insert) to_insert_this_path;
|
||||||
|
|
||||||
std::cerr << "Processing bad path:";
|
|
||||||
for (size_t i = 0; i < path.size() - 1; ++i) {
|
|
||||||
auto& a = path[i];
|
|
||||||
auto& b = path[i + 1];
|
|
||||||
std::cerr << " (" << a.x() << "," << a.y() << ") - (" << b.x() << "," << b.y() << ");";
|
|
||||||
}
|
|
||||||
std::cerr << std::endl;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < path.size() - 1; ++i) {
|
for (size_t i = 0; i < path.size() - 1; ++i) {
|
||||||
auto& a = path[i];
|
auto& a = path[i];
|
||||||
auto& b = path[i + 1];
|
auto& b = path[i + 1];
|
||||||
@@ -1852,7 +2508,7 @@ void clean_noisy_paths(DebugWriter& debug_output, Arrangement_2& arr, SegmentLoo
|
|||||||
|
|
||||||
auto x = collapse_path(path);
|
auto x = collapse_path(path);
|
||||||
if (!x) {
|
if (!x) {
|
||||||
std::cerr << "Unable to collapse path, skipping" << std::endl;
|
// std::cerr << "Unable to collapse path, skipping" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1866,16 +2522,14 @@ void clean_noisy_paths(DebugWriter& debug_output, Arrangement_2& arr, SegmentLoo
|
|||||||
double new_length = std::sqrt(CGAL::to_double((path.front() - *x).squared_length())) + std::sqrt(CGAL::to_double((path.back() - *x).squared_length()));
|
double new_length = std::sqrt(CGAL::to_double((path.front() - *x).squared_length())) + std::sqrt(CGAL::to_double((path.back() - *x).squared_length()));
|
||||||
|
|
||||||
if (new_length > orig_length * 2 || orig_length > new_length * 2) {
|
if (new_length > orig_length * 2 || orig_length > new_length * 2) {
|
||||||
std::cerr << "Collapsing path would increase length too much, skipping" << std::endl;
|
// std::cerr << "Collapsing path would increase length too much, skipping" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "new_length: " << new_length << " orig_length: " << orig_length << std::endl;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < path.size(); ++i) {
|
for (size_t i = 0; i < path.size(); ++i) {
|
||||||
auto& v = path[i];
|
auto& v = path[i];
|
||||||
if (CGAL::squared_distance(v, *x) < 1.e-5) {
|
if (CGAL::squared_distance(v, *x) < 1.e-5) {
|
||||||
std::cerr << "Collapsing path would create near-duplicate vert to previous path, skipping" << std::endl;
|
// std::cerr << "Collapsing path would create near-duplicate vert to previous path, skipping" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2059,7 +2713,6 @@ void clean_noisy_bounds(DebugWriter& debug_output, Arrangement_2& arr, SegmentLo
|
|||||||
|
|
||||||
size_t facet_index = 0;
|
size_t facet_index = 0;
|
||||||
for (auto it = arr.faces_begin(); it != arr.faces_end(); ++it, ++facet_index) {
|
for (auto it = arr.faces_begin(); it != arr.faces_end(); ++it, ++facet_index) {
|
||||||
std::cout << "facet_index " << facet_index << std::endl;
|
|
||||||
if (!it->is_unbounded()) {
|
if (!it->is_unbounded()) {
|
||||||
std::set<std::pair<Point_2, Point_2>> to_remove;
|
std::set<std::pair<Point_2, Point_2>> to_remove;
|
||||||
std::vector<std::pair<Point_2, Point_2>> to_insert;
|
std::vector<std::pair<Point_2, Point_2>> to_insert;
|
||||||
@@ -2078,17 +2731,14 @@ void clean_noisy_bounds(DebugWriter& debug_output, Arrangement_2& arr, SegmentLo
|
|||||||
++circ;
|
++circ;
|
||||||
} while (circ != it->outer_ccb());
|
} while (circ != it->outer_ccb());
|
||||||
|
|
||||||
std::cerr << "badnesses:";
|
|
||||||
std::vector<double> badnesses;
|
std::vector<double> badnesses;
|
||||||
for (auto& e : segs) {
|
for (auto& e : segs) {
|
||||||
badnesses.push_back(edge_badness(e));
|
badnesses.push_back(edge_badness(e));
|
||||||
std::cerr << " (" << e.source().x() << "," << e.source().y() << ") - (" << e.target().x() << "," << e.target().y() << "): " << badnesses.back() << ";";
|
|
||||||
}
|
}
|
||||||
std::cerr << std::endl;
|
|
||||||
|
|
||||||
auto bit = std::min_element(badnesses.begin(), badnesses.end());
|
auto bit = std::min_element(badnesses.begin(), badnesses.end());
|
||||||
if (*bit > threshold) {
|
if (*bit > threshold) {
|
||||||
std::cerr << "All edges are good, skipping" << std::endl;
|
// std::cerr << "All edges are good, skipping" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2096,19 +2746,15 @@ void clean_noisy_bounds(DebugWriter& debug_output, Arrangement_2& arr, SegmentLo
|
|||||||
auto N = circular_distance(it_pair.first, it_pair.second, badnesses);
|
auto N = circular_distance(it_pair.first, it_pair.second, badnesses);
|
||||||
|
|
||||||
if (N == 0) {
|
if (N == 0) {
|
||||||
std::cerr << "Unable to find run of bad edges, skipping" << std::endl;
|
// std::cerr << "Unable to find run of bad edges, skipping" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<Point_2>> incoming_paths;
|
std::vector<std::vector<Point_2>> incoming_paths;
|
||||||
|
|
||||||
std::cout << "range " << std::distance(badnesses.cbegin(), it_pair.first) << " to " << std::distance(badnesses.cbegin(), it_pair.second) << " length " << N << std::endl;
|
|
||||||
|
|
||||||
auto jt = it_pair.first;
|
auto jt = it_pair.first;
|
||||||
for (std::size_t k = 0; k < N; ++k, next_circular(jt, badnesses)) {
|
for (std::size_t k = 0; k < N; ++k, next_circular(jt, badnesses)) {
|
||||||
|
|
||||||
std::cout << " at " << std::distance(badnesses.cbegin(), jt) << " badness: " << *jt << std::endl;
|
|
||||||
|
|
||||||
auto he = halfedges[std::distance(badnesses.cbegin(), jt)];
|
auto he = halfedges[std::distance(badnesses.cbegin(), jt)];
|
||||||
to_remove.insert({he->source()->point(), he->target()->point()});
|
to_remove.insert({he->source()->point(), he->target()->point()});
|
||||||
debug_output.write_segment(he->source()->point(), he->target()->point(), "arr_bad_bound facet_" + std::to_string(facet_index));
|
debug_output.write_segment(he->source()->point(), he->target()->point(), "arr_bad_bound facet_" + std::to_string(facet_index));
|
||||||
@@ -2175,12 +2821,9 @@ void clean_noisy_bounds(DebugWriter& debug_output, Arrangement_2& arr, SegmentLo
|
|||||||
CGAL::Ray_2<K> r1((*a)->point(), (*b)->point());
|
CGAL::Ray_2<K> r1((*a)->point(), (*b)->point());
|
||||||
CGAL::Ray_2<K> r2((*d)->point(), (*c)->point());
|
CGAL::Ray_2<K> r2((*d)->point(), (*c)->point());
|
||||||
|
|
||||||
std::cout << "a: (" << (*a)->point().x() << "," << (*a)->point().y() << ") b: (" << (*b)->point().x() << "," << (*b)->point().y() << ") c: (" << (*c)->point().x() << "," << (*c)->point().y() << ") d: (" << (*d)->point().x() << "," << (*d)->point().y() << ")" << std::endl;
|
|
||||||
|
|
||||||
auto x = CGAL::intersection(r1, r2);
|
auto x = CGAL::intersection(r1, r2);
|
||||||
if (x) {
|
if (x) {
|
||||||
if (auto* xp = variant_get<CGAL::Point_2<K>>(&*x)) {
|
if (auto* xp = variant_get<CGAL::Point_2<K>>(&*x)) {
|
||||||
std::cout << "ray xp: (" << xp->x() << "," << xp->y() << ")" << std::endl;
|
|
||||||
to_insert.emplace_back((*b)->point(), *xp);
|
to_insert.emplace_back((*b)->point(), *xp);
|
||||||
to_insert.emplace_back((*c)->point(), *xp);
|
to_insert.emplace_back((*c)->point(), *xp);
|
||||||
|
|
||||||
@@ -2194,7 +2837,6 @@ void clean_noisy_bounds(DebugWriter& debug_output, Arrangement_2& arr, SegmentLo
|
|||||||
auto x = CGAL::intersection(r1, r2);
|
auto x = CGAL::intersection(r1, r2);
|
||||||
if (x) {
|
if (x) {
|
||||||
if (auto* xp = variant_get<CGAL::Point_2<K>>(&*x)) {
|
if (auto* xp = variant_get<CGAL::Point_2<K>>(&*x)) {
|
||||||
std::cout << "line xp: (" << xp->x() << "," << xp->y() << ")" << std::endl;
|
|
||||||
to_insert.emplace_back((*b)->point(), *xp);
|
to_insert.emplace_back((*b)->point(), *xp);
|
||||||
to_insert.emplace_back((*c)->point(), *xp);
|
to_insert.emplace_back((*c)->point(), *xp);
|
||||||
|
|
||||||
@@ -2495,64 +3137,44 @@ void arrange_cgal_polygons(svgfill::arrange_polygon_settings settings, const std
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a JSON structure with center line topology with the midpoint_to_edge_length as per-point data
|
|
||||||
{
|
|
||||||
std::ofstream ofs("center_line_topology.json");
|
|
||||||
ofs << "{\n";
|
|
||||||
ofs << " \"vertices\": [\n";
|
|
||||||
bool first_vertex = true;
|
|
||||||
for (auto& p : line_graph) {
|
|
||||||
if (!first_vertex) {
|
|
||||||
ofs << ",\n";
|
|
||||||
}
|
|
||||||
first_vertex = false;
|
|
||||||
ofs << " {\n";
|
|
||||||
ofs << " \"point\": [" << p.first.x() << ", " << p.first.y() << "],\n";
|
|
||||||
ofs << " \"width\":" << midpoint_to_edge_length.find(p.first)->second << ",\n";
|
|
||||||
ofs << " \"connected_to\": [\n";
|
|
||||||
bool first_connected = true;
|
|
||||||
for (auto& q : p.second) {
|
|
||||||
if (!first_connected) {
|
|
||||||
ofs << ",\n";
|
|
||||||
}
|
|
||||||
first_connected = false;
|
|
||||||
ofs << " [" << q.x() << ", " << q.y() << "]";
|
|
||||||
}
|
|
||||||
ofs << "\n ]\n";
|
|
||||||
ofs << " }";
|
|
||||||
}
|
|
||||||
ofs << "\n ]\n";
|
|
||||||
ofs << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
t0.stop();
|
t0.stop();
|
||||||
|
|
||||||
t0 = timer.start("center line cleaning");
|
t0 = timer.start("center line cleaning");
|
||||||
|
|
||||||
auto triangles = find_triangles(line_graph);
|
|
||||||
|
|
||||||
// For every triangle found in the network we eliminate one edge to break the cycle
|
Graph2D<K> G;
|
||||||
// The edge we eliminate is the edge with the greatest angle with any of it's neighbours
|
if (settings.line_cleaning_algo == 0) {
|
||||||
auto eliminated_segments = eliminate_triangles(line_graph);
|
G = join_segment_runs(debug_output, line_graph, midpoint_to_edge_length);
|
||||||
|
Arrangement_2 arr;
|
||||||
|
G.to_arrangement(arr);
|
||||||
|
Graph2D<K> G2;
|
||||||
|
G2.from_arrangement(arr);
|
||||||
|
eliminate_colinear_vertices(G2);
|
||||||
|
G = G2;
|
||||||
|
for (auto it = G.edges_begin(); it != G.edges_end(); ++it) {
|
||||||
|
debug_output.write_segment(it->first, it->second, "network_2");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto eliminated_segments = eliminate_triangles(line_graph);
|
||||||
|
|
||||||
Graph2D<K> G2(line_graph);
|
Graph2D<K> G2(line_graph);
|
||||||
for (auto& e : eliminated_segments) {
|
for (auto& e : eliminated_segments) {
|
||||||
debug_output.write_segment(e.first, e.second, "eliminated");
|
debug_output.write_segment(e.first, e.second, "eliminated");
|
||||||
G2.remove_edge(e.first, e.second);
|
G2.remove_edge(e.first, e.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto G = G2.weld_vertices();
|
G = G2.weld_vertices();
|
||||||
|
|
||||||
for (auto it = G.edges_begin(); it != G.edges_end(); ++it) {
|
for (auto it = G.edges_begin(); it != G.edges_end(); ++it) {
|
||||||
debug_output.write_segment(it->first, it->second, "network_2");
|
debug_output.write_segment(it->first, it->second, "network_2");
|
||||||
}
|
}
|
||||||
|
|
||||||
eliminate_colinear_vertices(G);
|
eliminate_colinear_vertices(G);
|
||||||
|
|
||||||
edge_slide(G);
|
edge_slide(G);
|
||||||
|
|
||||||
for (auto it = G.edges_begin(); it != G.edges_end(); ++it) {
|
for (auto it = G.edges_begin(); it != G.edges_end(); ++it) {
|
||||||
debug_output.write_segment(it->first, it->second, "network_3");
|
debug_output.write_segment(it->first, it->second, "network_3");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t0.stop();
|
t0.stop();
|
||||||
@@ -2629,7 +3251,7 @@ void arrange_cgal_polygons(svgfill::arrange_polygon_settings settings, const std
|
|||||||
fuse_corridor_halves_with_input(arr, G, segment_lookup, input_polygons, debug_output);
|
fuse_corridor_halves_with_input(arr, G, segment_lookup, input_polygons, debug_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.perform_cleanup) {
|
if (settings.perform_cleanup && settings.line_cleaning_algo != 0) {
|
||||||
remove_colinear_vertices(arr);
|
remove_colinear_vertices(arr);
|
||||||
double threshold;
|
double threshold;
|
||||||
clean_noisy_paths(debug_output, arr, segment_lookup, threshold);
|
clean_noisy_paths(debug_output, arr, segment_lookup, threshold);
|
||||||
|
|||||||
@@ -346,6 +346,13 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void from_arrangement(T& arr) {
|
||||||
|
for (auto it = arr.edges_begin(); it != arr.edges_end(); ++it) {
|
||||||
|
insert(it->source()->point(), it->target()->point());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void assert_symmetric() {
|
void assert_symmetric() {
|
||||||
#ifdef SVGFILL_DEBUG
|
#ifdef SVGFILL_DEBUG
|
||||||
#if 0
|
#if 0
|
||||||
|
|||||||
@@ -126,6 +126,9 @@ namespace svgfill {
|
|||||||
// 0: outer perimiter and corridor center lines
|
// 0: outer perimiter and corridor center lines
|
||||||
// 1: input polygons, corridor center lines and segments connecting corridor center lines to input polygons
|
// 1: input polygons, corridor center lines and segments connecting corridor center lines to input polygons
|
||||||
int topology_reconstruction_algo = 0;
|
int topology_reconstruction_algo = 0;
|
||||||
|
// 0: join segment runs
|
||||||
|
// 1: local badness reduction
|
||||||
|
int line_cleaning_algo = 0;
|
||||||
bool perform_cleanup = true;
|
bool perform_cleanup = true;
|
||||||
double subdivision_factor = 16.;
|
double subdivision_factor = 16.;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user