Merge branch 'v0.8.0' into ifcmax/initial-refresh

This commit is contained in:
Josef Wienerroither
2026-04-12 08:22:23 +02:00
61 changed files with 1343 additions and 385 deletions
+6 -1
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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("")
+3 -10
View File
@@ -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`
+232
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -10
View File
@@ -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"},
) )
+4 -4
View File
@@ -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
+2 -2
View File
@@ -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
+21 -21
View File
@@ -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="-",
) )
+4 -4
View File
@@ -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()
+27 -27
View File
@@ -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
+1 -1
View File
@@ -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],
) )
+3 -3
View File
@@ -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
+3 -3
View File
@@ -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,
) )
+6 -6
View File
@@ -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
+2 -2
View File
@@ -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")
+1 -1
View File
@@ -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.",
) )
+3 -3
View File
@@ -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 = {}
+2 -3
View File
@@ -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``
+1 -1
View File
@@ -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()) {
+12 -2
View File
@@ -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):
+10 -1
View File
@@ -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+."),
+4 -4
View File
@@ -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": {
+1 -1
View File
@@ -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",
+714 -92
View File
@@ -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);
+7
View File
@@ -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
+3
View File
@@ -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.;
}; };